Packages
pui
1.0.0-alpha.12
1.0.0-beta.18
1.0.0-beta.17
1.0.0-beta.16
1.0.0-beta.15
1.0.0-beta.14
1.0.0-beta.13
1.0.0-beta.12
1.0.0-beta.11
1.0.0-beta.10
1.0.0-beta.9
1.0.0-beta.8
1.0.0-beta.7
1.0.0-beta.6
1.0.0-beta.5
1.0.0-beta.4
1.0.0-beta.3
1.0.0-beta.1
1.0.0-alpha.32
1.0.0-alpha.31
1.0.0-alpha.30
1.0.0-alpha.29
1.0.0-alpha.28
1.0.0-alpha.27
1.0.0-alpha.26
1.0.0-alpha.25
1.0.0-alpha.24
1.0.0-alpha.23
1.0.0-alpha.22
1.0.0-alpha.21
1.0.0-alpha.19
1.0.0-alpha.18
1.0.0-alpha.17
1.0.0-alpha.16
1.0.0-alpha.15
1.0.0-alpha.14
1.0.0-alpha.13
1.0.0-alpha.12
1.0.0-alpha.11
A Phoenix LiveView UI toolkit
Current section
Files
Jump to
Current section
Files
assets/js/select.js
import Popover from "./popover";
export default class Select extends Popover {
placement = "bottom-start";
expand_popover = true;
focus_selected = false;
#searchInputHandler;
mounted() {
this.name = "select";
super.mounted();
this.search = this.el.querySelector("input[type='text'][role='combobox']");
this.hiddenInput = this.el.querySelector("input[type='hidden']");
// Search input handler
// this.search?.addEventListener("input", this.handleSearchInput.bind(this));
this.popup.addEventListener("click", this.handlePopupClick.bind(this));
this.#searchInputHandler = this.handleSearchInput.bind(this);
this.selectDefaultValue();
}
updated() {
super.updated();
this.syncValueFromDataset();
}
syncValueFromDataset() {
const serverValue = this.el.dataset.value;
if (serverValue && this.hiddenInput.value !== serverValue) {
this.hiddenInput.value = serverValue;
this.updatePlaceholder();
}
}
selectDefaultValue() {
const defaultValue = this.el.dataset.value;
if (defaultValue) {
const defaultItem = Array.from(this.items).find(
(item) => item.dataset.value === defaultValue,
);
if (defaultItem) {
this.selectItem(defaultItem);
}
}
}
selectItem(itemEl) {
if (this.hiddenInput) {
const newValue = itemEl.dataset.value;
if (newValue && this.hiddenInput.value !== newValue) {
this.hiddenInput.value = newValue;
this.hiddenInput.dispatchEvent(new Event("change", { bubbles: true }));
}
}
this.updatePlaceholder();
this.closePopover();
}
updatePlaceholder() {
const items = Array.from(this.items).find(
(item) => item.dataset.value === this.hiddenInput.value,
);
this.placeholder = items ? items.textContent : "";
const label = this.el.querySelector(`[data-pui="selected-label"]`);
if (label) {
label.textContent = items ? items.textContent : "";
}
}
handlePopupClick(event) {
// handle item click
const item = event.target?.closest("[role='menuitem']");
if (item && this.popup.contains(item)) {
this.selectItem(item);
}
}
handleKeyEnter(event) {
event.preventDefault();
if (this.currentIndex >= 0) {
const visibleItems = Array.from(this.items).filter(
(item) => item.getAttribute("aria-hidden") !== "true",
);
const currentItem = visibleItems[this.currentIndex];
if (currentItem) {
this.selectItem(currentItem);
}
}
}
handleSearchInput(event) {
// Prevent liveview form trigger phx-change
// event.preventDefault();
event.stopPropagation();
const query = event.target.value.toLowerCase();
this.currentIndex = -1;
this.items.forEach((item) => {
const itemText = (item.dataset.label || item.textContent)
.trim()
.toLowerCase();
const matches = itemText.includes(query);
item.setAttribute("aria-hidden", String(!matches));
});
}
clearSearch() {
this.search.value = "";
this.items.forEach((item) => {
item.removeAttribute("aria-hidden");
});
}
// @override
onPopupClosed() {
super.onPopupClosed();
if (this.search) {
this.clearSearch();
}
}
// @override
openPopover() {
super.openPopover();
if (this.search) {
this.search.focus();
this.search.addEventListener("input", this.#searchInputHandler);
}
}
// @override
closePopover() {
super.closePopover();
if (this.search) {
this.search.removeEventListener("input", this.#searchInputHandler);
}
}
}