Skip to content

Commit

Permalink
Merge branch 'release/2024.01'
Browse files Browse the repository at this point in the history
  • Loading branch information
lurnid committed Dec 9, 2024
2 parents 0d298bf + 7318fea commit d9cdd31
Show file tree
Hide file tree
Showing 27 changed files with 1,360 additions and 222 deletions.
2 changes: 2 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import Uploaders from "./uploaders"
import {Combobox, ComboboxOption} from "./combobox"
import LiveSelect from "./live_select"

let Hooks = {}
Hooks.Combobox = Combobox
Hooks.ComboboxOption = ComboboxOption
Hooks.LiveSelect = LiveSelect

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
Expand Down
151 changes: 151 additions & 0 deletions assets/js/live_select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
function debounce(func, msec) {
let timer;
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, msec)
}
}

const LiveSelect = {
textInput() {
return this.el.querySelector("input[type=text]")
},

debounceMsec() {
return parseInt(this.el.dataset["debounce"])
},

updateMinLen() {
return parseInt(this.el.dataset["updateMinLen"])
},

maybeStyleClearButton() {
const clear_button = this.el.querySelector('button[phx-click=clear]')
if (clear_button) {
this.textInput().parentElement.style.position = 'relative'
clear_button.style.position = 'absolute'
clear_button.style.top = '0px'
clear_button.style.bottom = '0px'
clear_button.style.right = '5px'
clear_button.style.display = 'block'
}
},

pushEventToParent(event, payload) {
const target = this.el.dataset['phxTarget'];
if (target) {
this.pushEventTo(target, event, payload)
} else {
this.pushEvent(event, payload)
}
},

attachDomEventHandlers() {
const textInput = this.textInput()
textInput.onkeydown = (event) => {
if (event.code === "Enter") {
event.preventDefault()
}
this.pushEventTo(this.el, 'keydown', {key: event.code})
}

const changeEvents = debounce((id, field, text) => {
this.pushEventTo(this.el, "change", {text})
this.pushEventToParent("live_select_change", {id: this.el.id, field, text})
}, this.debounceMsec())

textInput.oninput = (event) => {
const text = event.target.value.trim()
const field = this.el.dataset['field']
if (text.length >= this.updateMinLen()) {
changeEvents(this.el.id, field, text)
} else {
this.pushEventTo(this.el, "options_clear", {})
}
}

const dropdown = this.el.querySelector("ul")
if (dropdown) {
dropdown.onmousedown = (event) => {
const option = event.target.closest('div[data-idx]')
if (option) {
this.pushEventTo(this.el, 'option_click', {idx: option.dataset.idx})
event.preventDefault()
}
}
}

this.el.querySelectorAll("button[data-idx]").forEach(button => {
button.onclick = (event) => {
this.pushEventTo(this.el, 'option_remove', {idx: button.dataset.idx})
}
})
},

setInputValue(value) {
this.textInput().value = value
},

inputEvent(selection, mode) {
const selector = mode === "single"
? "input.single-mode"
: (selection.length === 0
? "input[data-live-select-empty]"
: "input[type=hidden]")
this.el.querySelector(selector).dispatchEvent(new Event('input', {bubbles: true}))
},

mounted() {
this.maybeStyleClearButton()

this.handleEvent("parent_event", ({id, event, payload}) => {
if (this.el.id === id) {
this.pushEventToParent(event, payload)
}
})

this.handleEvent("select", ({id, selection, mode, input_event, parent_event}) => {
if (this.el.id === id) {
this.selection = selection
if (mode === "single") {
const label = selection.length > 0 ? selection[0].label : null
this.setInputValue(label)
} else {
this.setInputValue(null)
}
if (input_event) {
this.inputEvent(selection, mode)
}
if (parent_event) {
this.pushEventToParent(parent_event, {id})
}
}
})

this.handleEvent("active", ({id, idx}) => {
if (this.el.id === id) {
const option = this.el.querySelector(`div[data-idx="${idx}"]`)
if (option) {
option.scrollIntoView({block: "nearest"})
}
}
})

this.attachDomEventHandlers()
},

updated() {
this.maybeStyleClearButton()
this.attachDomEventHandlers()
},

reconnected() {
if (this.selection && this.selection.length > 0) {
this.pushEventTo(this.el, "selection_recovery", this.selection)
}
}
}

export default LiveSelect
4 changes: 3 additions & 1 deletion assets/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ module.exports = {
content: [
"./js/**/*.js",
"../lib/maker_passport_web.ex",
"../lib/maker_passport_web/**/*.*ex"
"../lib/maker_passport_web/**/*.*ex",
'../deps/live_select/lib/live_select/component.*ex',

],
theme: {
extend: {
Expand Down
Loading

0 comments on commit d9cdd31

Please sign in to comment.