Packages
noora
0.14.0
0.84.1
0.84.0
0.83.2
0.83.1
0.83.0
0.82.7
0.82.6
0.82.5
0.82.4
0.82.3
0.82.2
0.82.1
0.82.0
0.81.4
0.81.3
0.81.2
0.81.1
0.81.0
0.80.1
0.80.0
0.79.1
0.79.0
0.78.1
0.78.0
0.77.4
0.77.3
0.77.2
0.77.1
0.77.0
0.76.0
0.75.0
0.74.0
0.73.0
0.72.0
0.71.0
0.70.0
0.69.1
0.69.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.2
0.64.1
0.64.0
0.63.2
0.63.1
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.0
0.54.0
0.53.1
0.53.0
0.52.1
0.52.0
0.51.0
0.50.1
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.3
0.44.2
0.44.1
0.44.0
0.43.0
0.42.0
0.41.0
0.40.6
0.40.5
0.40.4
0.40.3
0.40.2
0.40.1
0.40.0
0.39.1
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.1
0.32.0
0.31.0
0.30.0
0.29.2
0.29.1
0.29.0
0.28.5
0.28.4
0.28.3
0.28.2
0.28.1
0.28.0
0.27.0
0.26.1
0.26.0
0.25.0
0.24.0
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.0
0.1.0
0.1.0-rc.2
0.1.0-rc.1
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
A component library for Phoenix LiveView applications
Current section
Files
Jump to
Current section
Files
js/Dropdown.js
import * as menu from "@zag-js/menu";
import {
normalizeProps,
spreadProps,
renderPart,
getBooleanOption,
getOption,
getPartSelector,
} from "./util.js";
import { Component } from "./component.js";
import { VanillaMachine } from "./machine.js";
class Menu extends Component {
initMachine(context) {
return new VanillaMachine(menu.machine, context);
}
initApi() {
return menu.connect(this.machine.service, normalizeProps);
}
render() {
const parts = [
"trigger",
"trigger:indicator",
"positioner",
"positioner:content",
];
for (const part of parts) renderPart(this.el, part, this.api);
this.renderItemGroupLabels();
this.renderItemGroups();
this.renderItems();
this.renderSeparators();
}
renderItemGroupLabels() {
for (const itemGroupLabel of this.el.querySelectorAll(
getPartSelector("positioner:content:item-group-label"),
)) {
const htmlFor = itemGroupLabel.getAttribute("for");
if (!htmlFor) {
console.error("Missing `for` attribute on item group label.");
return;
}
spreadProps(itemGroupLabel, this.api.getItemGroupLabelProps({ htmlFor }));
}
}
renderItemGroups() {
for (const itemGroup of this.el.querySelectorAll(
getPartSelector("positioner:content:item-group"),
)) {
const value = itemGroup.dataset.value;
if (!value) {
console.error("Missing `data-value` attribute on item group.");
return;
}
spreadProps(itemGroup, this.api.getItemGroupProps({ id: value }));
}
}
renderItems() {
for (const item of this.el.querySelectorAll(
getPartSelector("positioner:content:item"),
)) {
const value = item.dataset.value;
if (!value) {
console.error("Missing `data-value` attribute on item.");
return;
}
spreadProps(item, this.api.getItemProps({ value }));
}
}
renderSeparators() {
for (const separator of this.el.querySelectorAll(
getPartSelector("positioner:content:separator"),
)) {
spreadProps(separator, this.api.getSeparatorProps());
}
}
}
/**
* Phoenix LiveView Hook for Menu component
*/
export default {
mounted() {
this.metadata = {};
for (const key in this.el.dataset) {
if (key.startsWith("meta")) {
const metaKey = key.charAt(4).toLowerCase() + key.slice(5);
this.metadata[metaKey] = this.el.dataset[key];
}
}
this.context = {
id: this.el.id,
loopFocus: getBooleanOption(this.el, "loopFocus"),
closeOnSelect: getBooleanOption(this.el, "closeOnSelect"),
typeahead: getBooleanOption(this.el, "typeahead"),
positioning: {
offset: { mainAxis: getOption(this.el, "positioningOffsetMainAxis") },
},
onOpenChange: (details) => {
if (this.el.dataset.onOpenChange) {
this.pushEvent(this.el.dataset.onOpenChange, details);
}
},
onHighlightChange: (details) => {
if (this.el.dataset.onHighlightChange) {
this.pushEvent(this.el.dataset.onHighlightChange, details);
}
},
onSelect: (details) => {
if (this.el.dataset.onSelect) {
this.pushEvent(this.el.dataset.onSelect, {
...this.metadata,
...details,
});
}
},
onEscapeKeyDown: (details) => {
if (this.el.dataset.onEscapeKeyDown) {
this.pushEvent(this.el.dataset.onEscapeKeyDown, details);
}
},
onPointerDownOutside: (details) => {
if (this.el.dataset.onPointerDownOutside) {
this.pushEvent(this.el.dataset.onPointerDownOutside, details);
}
},
onFocusOutside: (details) => {
if (this.el.dataset.onFocusOutside) {
this.pushEvent(this.el.dataset.onFocusOutside, details);
}
},
onInteractOutside: (details) => {
if (this.el.dataset.onInteractOutside) {
this.pushEvent(this.el.dataset.onInteractOutside, details);
}
},
};
this.menu = new Menu(this.el, this.context);
this.menu.init();
this.handleOpenDropdown = (event) => {
if (event.detail.id == this.el.id) {
this.menu.api.setOpen(true);
}
};
this.handleCloseDropdown = (event) => {
if (event.detail.all || event.detail.id === this.el.id) {
this.menu.api.setOpen(false);
}
};
window.addEventListener("phx:open-dropdown", this.handleOpenDropdown);
window.addEventListener("phx:close-dropdown", this.handleCloseDropdown);
},
updated() {
this.menu.render();
},
beforeDestroy() {
this.menu.destroy();
},
destroyed() {
window.removeEventListener("phx:open-dropdown", this.handleOpenDropdown);
window.removeEventListener("phx:close-dropdown", this.handleCloseDropdown);
},
};