Packages
noora
0.82.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/Select.js
import * as select from "@zag-js/select";
import {
getOption,
getPartSelector,
normalizeProps,
renderPart,
spreadProps,
} from "./util.js";
import { Component } from "./component.js";
import { VanillaMachine } from "./machine.js";
class Select extends Component {
initMachine(context) {
return new VanillaMachine(select.machine, context);
}
initApi() {
return select.connect(this.machine.service, normalizeProps);
}
render() {
const parts = [
"hidden-select",
"trigger",
"trigger:indicator",
"positioner",
"positioner:content",
];
for (const part of parts) renderPart(this.el, part, this.api);
this.renderItems();
this.renderTriggerLabel();
}
renderTriggerLabel() {
// Zag keeps the hidden <select> and item states in sync, but nothing
// updates the visible trigger label when the user picks an option.
// Mirror `api.valueAsString` into the trigger's label span so the
// displayed value tracks the selection.
const label = this.el.querySelector(
"[data-part='trigger'] [data-part='label-wrapper'] [data-part='label']",
);
if (!label) return;
const selectedLabel = this.api.valueAsString;
const placeholder = label.dataset.placeholder || label.textContent;
if (!label.dataset.placeholder) label.dataset.placeholder = placeholder;
label.textContent = selectedLabel || label.dataset.placeholder;
}
renderItems() {
for (const item of this.el.querySelectorAll(
getPartSelector("positioner:content:item"),
)) {
const value = item.dataset.value;
const label = item.dataset.label;
if (!value || !label) {
console.error(
"Missing `data-value` or `data-label` attribute on item.",
);
return;
}
spreadProps(item, this.api.getItemProps({ item: { value, label } }));
}
}
}
export default {
mounted() {
this.select = new Select(this.el, this.context());
this.select.init();
},
updated() {
this.select.render();
},
beforeDestroy() {
this.select.destroy();
},
context() {
return {
id: this.el.id,
collection: this.collection(),
name: getOption(this.el, "name"),
// Zag.js expects the controlled value to always be an array.
onValueChange: (details) => {
if (this.el.dataset.onValueChange) {
this.pushEvent(this.el.dataset.onValueChange, details);
}
},
};
},
items() {
return Array.from(this.el.querySelectorAll("[data-part='item']"))
.map((item) => {
const value = item.dataset.value;
const label = item.dataset.label;
if (!value || !label) {
console.error(
"Missing `data-value` or `data-label` attribute on item.",
);
return;
}
return { value, label };
})
.filter((value) => value !== undefined);
},
collection() {
return select.collection({
items: this.items(),
itemToString: (item) => item.label,
itemToValue: (item) => item.value,
});
},
};