Packages
corex
0.1.2
0.1.2
0.1.1
0.1.0
0.1.0-rc.1
0.1.0-rc.0
0.1.0-beta.5
0.1.0-beta.4
0.1.0-beta.3
0.1.0-beta.2
0.1.0-beta.1
0.1.0-alpha.33
0.1.0-alpha.32
0.1.0-alpha.31
0.1.0-alpha.30
0.1.0-alpha.29
0.1.0-alpha.28
0.1.0-alpha.27
0.1.0-alpha.26
0.1.0-alpha.25
0.1.0-alpha.24
0.1.0-alpha.23
0.1.0-alpha.22
0.1.0-alpha.21
0.1.0-alpha.20
0.1.0-alpha.19
0.1.0-alpha.18
0.1.0-alpha.17
0.1.0-alpha.16
0.1.0-alpha.15
0.1.0-alpha.14
0.1.0-alpha.13
0.1.0-alpha.12
0.1.0-alpha.11
0.1.0-alpha.10
0.1.0-alpha.9
0.1.0-alpha.8
0.1.0-alpha.7
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
Accessible and unstyled UI components library written in Elixir and TypeScript that integrates Zag.js state machines into the Phoenix Framework.
Current section
Files
Jump to
Current section
Files
priv/static/select.mjs
import {
getPlacement,
getPlacementStyles
} from "./chunks/chunk-RK6266HP.mjs";
import {
trackDismissableElement
} from "./chunks/chunk-WJDVLJMP.mjs";
import "./chunks/chunk-B5L2AGOH.mjs";
import {
queueLiveViewFormInputSync,
reapplyLiveViewValueInputUsage
} from "./chunks/chunk-VMKNATWC.mjs";
import {
readPositioningOptions
} from "./chunks/chunk-CNPBJL2G.mjs";
import {
itemValue,
zagListCollectionConfig
} from "./chunks/chunk-NICWUGGL.mjs";
import {
ListCollection,
createSelectedItemMap,
deriveSelectionState,
resolveSelectedItems
} from "./chunks/chunk-FVGYE2AE.mjs";
import {
performRedirect,
readDomItemRedirect
} from "./chunks/chunk-HZLPIQBD.mjs";
import {
getInteractionModality,
setInteractionModality,
trackFocusVisible
} from "./chunks/chunk-VDUSDBJS.mjs";
import {
mountStringListBinding,
readUpdatedServerStringList
} from "./chunks/chunk-I2HPUDHJ.mjs";
import {
createDomEventRegistry,
createHookHandleEventRegistry
} from "./chunks/chunk-77HPO22C.mjs";
import {
idMatches,
notifyChange,
readPayloadId
} from "./chunks/chunk-2WCNJX5P.mjs";
import {
Component,
VanillaMachine,
addOrRemove,
ariaAttr,
canPushEvent,
contains,
createAnatomy,
createGuards,
createMachine,
dataAttr,
ensure,
getBoolean,
getByTypeahead,
getDir,
getEventKey,
getEventTarget,
getInitialFocus,
getNativeEvent,
getString,
getStringList,
isEditableElement,
isEqual,
isInternalChangeEvent,
isValidTabEvent,
markAsInternalChangeEvent,
observeAttributes,
raf,
scrollIntoView,
syncInputFormAssociation,
trackFormControl,
visuallyHiddenStyle
} from "./chunks/chunk-2GQRP3FN.mjs";
// ../node_modules/.pnpm/@zag-js+select@1.40.0/node_modules/@zag-js/select/dist/select.anatomy.mjs
var anatomy = createAnatomy("select").parts(
"label",
"positioner",
"trigger",
"indicator",
"clearTrigger",
"item",
"itemText",
"itemIndicator",
"itemGroup",
"itemGroupLabel",
"list",
"content",
"root",
"control",
"valueText"
);
var parts = anatomy.build();
// ../node_modules/.pnpm/@zag-js+select@1.40.0/node_modules/@zag-js/select/dist/select.collection.mjs
var collection = (options) => {
return new ListCollection(options);
};
collection.empty = () => {
return new ListCollection({ items: [] });
};
// ../node_modules/.pnpm/@zag-js+select@1.40.0/node_modules/@zag-js/select/dist/select.dom.mjs
var getRootId = (ctx) => ctx.ids?.root ?? `select:${ctx.id}`;
var getContentId = (ctx) => ctx.ids?.content ?? `select:${ctx.id}:content`;
var getTriggerId = (ctx) => ctx.ids?.trigger ?? `select:${ctx.id}:trigger`;
var getClearTriggerId = (ctx) => ctx.ids?.clearTrigger ?? `select:${ctx.id}:clear-trigger`;
var getLabelId = (ctx) => ctx.ids?.label ?? `select:${ctx.id}:label`;
var getControlId = (ctx) => ctx.ids?.control ?? `select:${ctx.id}:control`;
var getItemId = (ctx, id) => ctx.ids?.item?.(id) ?? `select:${ctx.id}:option:${id}`;
var getHiddenSelectId = (ctx) => ctx.ids?.hiddenSelect ?? `select:${ctx.id}:select`;
var getPositionerId = (ctx) => ctx.ids?.positioner ?? `select:${ctx.id}:positioner`;
var getItemGroupId = (ctx, id) => ctx.ids?.itemGroup?.(id) ?? `select:${ctx.id}:optgroup:${id}`;
var getItemGroupLabelId = (ctx, id) => ctx.ids?.itemGroupLabel?.(id) ?? `select:${ctx.id}:optgroup-label:${id}`;
var getHiddenSelectEl = (ctx) => ctx.getById(getHiddenSelectId(ctx));
var getContentEl = (ctx) => ctx.getById(getContentId(ctx));
var getTriggerEl = (ctx) => ctx.getById(getTriggerId(ctx));
var getClearTriggerEl = (ctx) => ctx.getById(getClearTriggerId(ctx));
var getPositionerEl = (ctx) => ctx.getById(getPositionerId(ctx));
var getItemEl = (ctx, id) => {
if (id == null) return null;
return ctx.getById(getItemId(ctx, id));
};
// ../node_modules/.pnpm/@zag-js+select@1.40.0/node_modules/@zag-js/select/dist/select.connect.mjs
function connect(service, normalize) {
const { context, prop, scope, state, computed, send } = service;
const translations = prop("translations");
const disabled = prop("disabled") || context.get("fieldsetDisabled");
const invalid = !!prop("invalid");
const required = !!prop("required");
const readOnly = !!prop("readOnly");
const composite = prop("composite");
const collection2 = prop("collection");
const open = state.hasTag("open");
const focused = state.matches("focused");
const highlightedValue = context.get("highlightedValue");
const highlightedItem = context.get("highlightedItem");
const selectedItems = computed("selectedItems");
const currentPlacement = context.get("currentPlacement");
const isTypingAhead = computed("isTypingAhead");
const interactive = computed("isInteractive");
const ariaActiveDescendant = highlightedValue ? getItemId(scope, highlightedValue) : void 0;
function getItemState(props) {
const _disabled = collection2.getItemDisabled(props.item);
const value = collection2.getItemValue(props.item);
ensure(value, () => `[zag-js] No value found for item ${JSON.stringify(props.item)}`);
return {
value,
disabled: Boolean(disabled || _disabled),
highlighted: highlightedValue === value,
selected: context.get("value").includes(value)
};
}
const popperStyles = getPlacementStyles({
...prop("positioning"),
placement: currentPlacement
});
return {
open,
focused,
empty: context.get("value").length === 0,
highlightedItem,
highlightedValue,
selectedItems,
hasSelectedItems: computed("hasSelectedItems"),
value: context.get("value"),
valueAsString: computed("valueAsString"),
collection: collection2,
multiple: !!prop("multiple"),
disabled: !!disabled,
reposition(options = {}) {
send({ type: "POSITIONING.SET", options });
},
focus() {
getTriggerEl(scope)?.focus({ preventScroll: true });
},
setOpen(nextOpen) {
const open2 = state.hasTag("open");
if (open2 === nextOpen) return;
send({ type: nextOpen ? "OPEN" : "CLOSE" });
},
selectValue(value) {
send({ type: "ITEM.SELECT", value });
},
setValue(value) {
send({ type: "VALUE.SET", value });
},
selectAll() {
send({ type: "VALUE.SET", value: collection2.getValues() });
},
setHighlightValue(value) {
send({ type: "HIGHLIGHTED_VALUE.SET", value });
},
clearHighlightValue() {
send({ type: "HIGHLIGHTED_VALUE.CLEAR" });
},
clearValue(value) {
if (value) {
send({ type: "ITEM.CLEAR", value });
} else {
send({ type: "VALUE.CLEAR" });
}
},
getItemState,
getRootProps() {
return normalize.element({
...parts.root.attrs,
dir: prop("dir"),
id: getRootId(scope),
"data-invalid": dataAttr(invalid),
"data-readonly": dataAttr(readOnly)
});
},
getLabelProps() {
return normalize.label({
dir: prop("dir"),
id: getLabelId(scope),
...parts.label.attrs,
"data-disabled": dataAttr(disabled),
"data-invalid": dataAttr(invalid),
"data-readonly": dataAttr(readOnly),
"data-required": dataAttr(required),
htmlFor: getHiddenSelectId(scope),
onClick(event) {
if (event.defaultPrevented) return;
if (disabled) return;
getTriggerEl(scope)?.focus({ preventScroll: true });
}
});
},
getControlProps() {
return normalize.element({
...parts.control.attrs,
dir: prop("dir"),
id: getControlId(scope),
"data-state": open ? "open" : "closed",
"data-focus": dataAttr(focused),
"data-disabled": dataAttr(disabled),
"data-invalid": dataAttr(invalid)
});
},
getValueTextProps() {
return normalize.element({
...parts.valueText.attrs,
dir: prop("dir"),
"data-disabled": dataAttr(disabled),
"data-invalid": dataAttr(invalid),
"data-focus": dataAttr(focused)
});
},
getTriggerProps() {
return normalize.button({
id: getTriggerId(scope),
disabled,
dir: prop("dir"),
type: "button",
role: "combobox",
"aria-controls": getContentId(scope),
"aria-expanded": open,
"aria-haspopup": "listbox",
"data-state": open ? "open" : "closed",
"aria-invalid": invalid,
"aria-required": required,
"aria-labelledby": getLabelId(scope),
...parts.trigger.attrs,
"data-disabled": dataAttr(disabled),
"data-invalid": dataAttr(invalid),
"data-readonly": dataAttr(readOnly),
"data-placement": currentPlacement,
"data-placeholder-shown": dataAttr(!computed("hasSelectedItems")),
onClick(event) {
if (!interactive) return;
if (event.defaultPrevented) return;
send({ type: "TRIGGER.CLICK" });
},
onFocus() {
send({ type: "TRIGGER.FOCUS" });
},
onBlur() {
send({ type: "TRIGGER.BLUR" });
},
onKeyDown(event) {
if (event.defaultPrevented) return;
if (!interactive) return;
const keyMap = {
ArrowUp() {
send({ type: "TRIGGER.ARROW_UP" });
},
ArrowDown(event2) {
send({ type: event2.altKey ? "OPEN" : "TRIGGER.ARROW_DOWN" });
},
ArrowLeft() {
send({ type: "TRIGGER.ARROW_LEFT" });
},
ArrowRight() {
send({ type: "TRIGGER.ARROW_RIGHT" });
},
Home() {
send({ type: "TRIGGER.HOME" });
},
End() {
send({ type: "TRIGGER.END" });
},
Enter() {
send({ type: "TRIGGER.ENTER" });
},
Space(event2) {
if (isTypingAhead) {
send({ type: "TRIGGER.TYPEAHEAD", key: event2.key });
} else {
send({ type: "TRIGGER.ENTER" });
}
}
};
const exec = keyMap[getEventKey(event, {
dir: prop("dir"),
orientation: "vertical"
})];
if (exec) {
exec(event);
event.preventDefault();
return;
}
if (getByTypeahead.isValidEvent(event)) {
send({ type: "TRIGGER.TYPEAHEAD", key: event.key });
event.preventDefault();
}
}
});
},
getIndicatorProps() {
return normalize.element({
...parts.indicator.attrs,
dir: prop("dir"),
"aria-hidden": true,
"data-state": open ? "open" : "closed",
"data-disabled": dataAttr(disabled),
"data-invalid": dataAttr(invalid),
"data-readonly": dataAttr(readOnly)
});
},
getItemProps(props) {
const itemState = getItemState(props);
return normalize.element({
id: getItemId(scope, itemState.value),
role: "option",
...parts.item.attrs,
dir: prop("dir"),
"data-value": itemState.value,
"aria-selected": itemState.selected,
"data-state": itemState.selected ? "checked" : "unchecked",
"data-highlighted": dataAttr(itemState.highlighted),
"data-disabled": dataAttr(itemState.disabled),
"aria-disabled": ariaAttr(itemState.disabled),
onPointerMove(event) {
if (itemState.disabled || event.pointerType !== "mouse") return;
if (itemState.value === highlightedValue) return;
send({ type: "ITEM.POINTER_MOVE", value: itemState.value });
},
onClick(event) {
if (event.defaultPrevented) return;
if (itemState.disabled) return;
send({ type: "ITEM.CLICK", src: "pointerup", value: itemState.value });
},
onPointerLeave(event) {
if (itemState.disabled) return;
if (props.persistFocus) return;
if (event.pointerType !== "mouse") return;
const pointerMoved = service.event.previous()?.type.includes("POINTER");
if (!pointerMoved) return;
send({ type: "ITEM.POINTER_LEAVE" });
}
});
},
getItemTextProps(props) {
const itemState = getItemState(props);
return normalize.element({
...parts.itemText.attrs,
"data-state": itemState.selected ? "checked" : "unchecked",
"data-disabled": dataAttr(itemState.disabled),
"data-highlighted": dataAttr(itemState.highlighted)
});
},
getItemIndicatorProps(props) {
const itemState = getItemState(props);
return normalize.element({
"aria-hidden": true,
...parts.itemIndicator.attrs,
"data-state": itemState.selected ? "checked" : "unchecked",
hidden: !itemState.selected
});
},
getItemGroupLabelProps(props) {
const { htmlFor } = props;
return normalize.element({
...parts.itemGroupLabel.attrs,
id: getItemGroupLabelId(scope, htmlFor),
dir: prop("dir"),
role: "presentation"
});
},
getItemGroupProps(props) {
const { id } = props;
return normalize.element({
...parts.itemGroup.attrs,
"data-disabled": dataAttr(disabled),
id: getItemGroupId(scope, id),
"aria-labelledby": getItemGroupLabelId(scope, id),
role: "group",
dir: prop("dir")
});
},
getClearTriggerProps() {
return normalize.button({
...parts.clearTrigger.attrs,
id: getClearTriggerId(scope),
type: "button",
"aria-label": translations.clearTriggerLabel,
"data-invalid": dataAttr(invalid),
disabled,
hidden: !computed("hasSelectedItems"),
dir: prop("dir"),
onClick(event) {
if (event.defaultPrevented) return;
send({ type: "CLEAR.CLICK" });
}
});
},
getHiddenSelectProps() {
const value = context.get("value");
const defaultValue = prop("multiple") ? value : value?.[0];
const handleChange = (e) => {
const evt = getNativeEvent(e);
if (isInternalChangeEvent(evt)) return;
send({ type: "VALUE.SET", value: getSelectedValues(e.currentTarget) });
};
return normalize.select({
name: prop("name"),
form: prop("form"),
disabled,
multiple: prop("multiple"),
required: prop("required"),
"aria-hidden": true,
id: getHiddenSelectId(scope),
defaultValue,
style: visuallyHiddenStyle,
tabIndex: -1,
autoComplete: prop("autoComplete"),
onChange: handleChange,
onInput: handleChange,
// Some browser extensions will focus the hidden select.
// Let's forward the focus to the trigger.
onFocus() {
getTriggerEl(scope)?.focus({ preventScroll: true });
},
"aria-labelledby": getLabelId(scope)
});
},
getPositionerProps() {
return normalize.element({
...parts.positioner.attrs,
dir: prop("dir"),
id: getPositionerId(scope),
style: popperStyles.floating
});
},
getContentProps() {
return normalize.element({
hidden: !open,
dir: prop("dir"),
id: getContentId(scope),
role: composite ? "listbox" : "dialog",
...parts.content.attrs,
"data-state": open ? "open" : "closed",
"data-placement": currentPlacement,
"data-activedescendant": ariaActiveDescendant,
"aria-activedescendant": composite ? ariaActiveDescendant : void 0,
"aria-multiselectable": prop("multiple") && composite ? true : void 0,
"aria-labelledby": getLabelId(scope),
tabIndex: 0,
onKeyDown(event) {
if (!interactive) return;
if (!contains(event.currentTarget, getEventTarget(event))) return;
if (event.key === "Tab") {
const valid = isValidTabEvent(event);
if (!valid) {
event.preventDefault();
return;
}
}
const keyMap = {
ArrowUp() {
send({ type: "CONTENT.ARROW_UP" });
},
ArrowDown() {
send({ type: "CONTENT.ARROW_DOWN" });
},
Home() {
send({ type: "CONTENT.HOME" });
},
End() {
send({ type: "CONTENT.END" });
},
Enter() {
send({ type: "ITEM.CLICK", src: "keydown.enter" });
},
Space(event2) {
if (isTypingAhead) {
send({ type: "CONTENT.TYPEAHEAD", key: event2.key });
} else {
keyMap.Enter?.(event2);
}
}
};
const exec = keyMap[getEventKey(event)];
if (exec) {
exec(event);
event.preventDefault();
return;
}
const target = getEventTarget(event);
if (isEditableElement(target)) {
return;
}
if (getByTypeahead.isValidEvent(event)) {
send({ type: "CONTENT.TYPEAHEAD", key: event.key });
event.preventDefault();
}
}
});
},
getListProps() {
return normalize.element({
...parts.list.attrs,
tabIndex: 0,
role: !composite ? "listbox" : void 0,
"aria-labelledby": getTriggerId(scope),
"aria-activedescendant": !composite ? ariaActiveDescendant : void 0,
"aria-multiselectable": !composite && prop("multiple") ? true : void 0
});
}
};
}
var getSelectedValues = (el) => {
return el.multiple ? Array.from(el.selectedOptions, (o) => o.value) : el.value ? [el.value] : [];
};
// ../node_modules/.pnpm/@zag-js+select@1.40.0/node_modules/@zag-js/select/dist/select.machine.mjs
var { and, not, or } = createGuards();
var machine = createMachine({
props({ props }) {
return {
loopFocus: false,
closeOnSelect: !props.multiple,
composite: true,
defaultValue: [],
...props,
collection: props.collection ?? collection.empty(),
translations: {
clearTriggerLabel: "Clear value",
...props.translations
},
positioning: {
placement: "bottom-start",
gutter: 8,
...props.positioning
}
};
},
context({ prop, bindable, getContext }) {
const initialValue = prop("value") ?? prop("defaultValue") ?? [];
const initialSelectedItems = prop("collection").findMany(initialValue);
return {
value: bindable(() => ({
defaultValue: prop("defaultValue"),
value: prop("value"),
isEqual,
onChange(value) {
const context = getContext();
const collection2 = prop("collection");
const selectedItemMap = context.get("selectedItemMap");
const proposed = deriveSelectionState({
values: value,
collection: collection2,
selectedItemMap
});
const effectiveValue = prop("value") ?? value;
const effective = effectiveValue === value ? proposed : deriveSelectionState({
values: effectiveValue,
collection: collection2,
selectedItemMap: proposed.nextSelectedItemMap
});
context.set("selectedItemMap", effective.nextSelectedItemMap);
return prop("onValueChange")?.({ value, items: proposed.selectedItems });
}
})),
highlightedValue: bindable(() => ({
defaultValue: prop("defaultHighlightedValue") || null,
value: prop("highlightedValue"),
onChange(value) {
prop("onHighlightChange")?.({
highlightedValue: value,
highlightedItem: prop("collection").find(value),
highlightedIndex: prop("collection").indexOf(value)
});
}
})),
currentPlacement: bindable(() => ({
defaultValue: void 0
})),
fieldsetDisabled: bindable(() => ({
defaultValue: false
})),
highlightedItem: bindable(() => ({
defaultValue: null
})),
selectedItemMap: bindable(() => {
return {
defaultValue: createSelectedItemMap({
selectedItems: initialSelectedItems,
collection: prop("collection")
})
};
})
};
},
refs() {
return {
typeahead: { ...getByTypeahead.defaultOptions }
};
},
computed: {
hasSelectedItems: ({ context }) => context.get("value").length > 0,
isTypingAhead: ({ refs }) => refs.get("typeahead").keysSoFar !== "",
isDisabled: ({ prop, context }) => !!prop("disabled") || !!context.get("fieldsetDisabled"),
isInteractive: ({ prop }) => !(prop("disabled") || prop("readOnly")),
selectedItems: ({ context, prop }) => resolveSelectedItems({
values: context.get("value"),
collection: prop("collection"),
selectedItemMap: context.get("selectedItemMap")
}),
valueAsString: ({ computed, prop }) => prop("collection").stringifyItems(computed("selectedItems"))
},
initialState({ prop }) {
const open = prop("open") || prop("defaultOpen");
return open ? "open" : "idle";
},
entry: ["syncSelectElement"],
watch({ context, prop, track, action }) {
track([() => context.get("value").toString()], () => {
action(["syncSelectedItems", "syncSelectElement", "dispatchChangeEvent"]);
});
track([() => prop("open")], () => {
action(["toggleVisibility"]);
});
track([() => context.get("highlightedValue")], () => {
action(["syncHighlightedItem"]);
});
track([() => prop("collection").toString()], () => {
action(["syncCollection"]);
});
},
on: {
"HIGHLIGHTED_VALUE.SET": {
actions: ["setHighlightedItem"]
},
"HIGHLIGHTED_VALUE.CLEAR": {
actions: ["clearHighlightedItem"]
},
"ITEM.SELECT": {
actions: ["selectItem"]
},
"ITEM.CLEAR": {
actions: ["clearItem"]
},
"VALUE.SET": {
actions: ["setSelectedItems"]
},
"VALUE.CLEAR": {
actions: ["clearSelectedItems"]
},
"CLEAR.CLICK": {
actions: ["clearSelectedItems", "focusTriggerEl"]
}
},
effects: ["trackFormControlState"],
states: {
idle: {
tags: ["closed"],
on: {
"CONTROLLED.OPEN": [
{
guard: "isTriggerClickEvent",
target: "open",
actions: ["setInitialFocus", "highlightFirstSelectedItem"]
},
{
target: "open",
actions: ["setInitialFocus"]
}
],
"TRIGGER.CLICK": [
{
guard: "isOpenControlled",
actions: ["invokeOnOpen"]
},
{
target: "open",
actions: ["invokeOnOpen", "setInitialFocus", "highlightFirstSelectedItem"]
}
],
"TRIGGER.FOCUS": {
target: "focused"
},
OPEN: [
{
guard: "isOpenControlled",
actions: ["invokeOnOpen"]
},
{
target: "open",
actions: ["setInitialFocus", "invokeOnOpen"]
}
]
}
},
focused: {
tags: ["closed"],
on: {
"CONTROLLED.OPEN": [
{
guard: "isTriggerClickEvent",
target: "open",
actions: ["setInitialFocus", "highlightFirstSelectedItem"]
},
{
guard: "isTriggerArrowUpEvent",
target: "open",
actions: ["setInitialFocus", "highlightComputedLastItem"]
},
{
guard: or("isTriggerArrowDownEvent", "isTriggerEnterEvent"),
target: "open",
actions: ["setInitialFocus", "highlightComputedFirstItem"]
},
{
target: "open",
actions: ["setInitialFocus"]
}
],
OPEN: [
{
guard: "isOpenControlled",
actions: ["invokeOnOpen"]
},
{
target: "open",
actions: ["setInitialFocus", "invokeOnOpen"]
}
],
"TRIGGER.BLUR": {
target: "idle"
},
"TRIGGER.CLICK": [
{
guard: "isOpenControlled",
actions: ["invokeOnOpen"]
},
{
target: "open",
actions: ["setInitialFocus", "invokeOnOpen", "highlightFirstSelectedItem"]
}
],
"TRIGGER.ENTER": [
{
guard: "isOpenControlled",
actions: ["invokeOnOpen"]
},
{
target: "open",
actions: ["setInitialFocus", "invokeOnOpen", "highlightComputedFirstItem"]
}
],
"TRIGGER.ARROW_UP": [
{
guard: "isOpenControlled",
actions: ["invokeOnOpen"]
},
{
target: "open",
actions: ["setInitialFocus", "invokeOnOpen", "highlightComputedLastItem"]
}
],
"TRIGGER.ARROW_DOWN": [
{
guard: "isOpenControlled",
actions: ["invokeOnOpen"]
},
{
target: "open",
actions: ["setInitialFocus", "invokeOnOpen", "highlightComputedFirstItem"]
}
],
"TRIGGER.ARROW_LEFT": [
{
guard: and(not("multiple"), "hasSelectedItems"),
actions: ["selectPreviousItem"]
},
{
guard: not("multiple"),
actions: ["selectLastItem"]
}
],
"TRIGGER.ARROW_RIGHT": [
{
guard: and(not("multiple"), "hasSelectedItems"),
actions: ["selectNextItem"]
},
{
guard: not("multiple"),
actions: ["selectFirstItem"]
}
],
"TRIGGER.HOME": {
guard: not("multiple"),
actions: ["selectFirstItem"]
},
"TRIGGER.END": {
guard: not("multiple"),
actions: ["selectLastItem"]
},
"TRIGGER.TYPEAHEAD": {
guard: not("multiple"),
actions: ["selectMatchingItem"]
}
}
},
open: {
tags: ["open"],
exit: ["scrollContentToTop"],
effects: ["trackDismissableElement", "trackFocusVisible", "computePlacement", "scrollToHighlightedItem"],
on: {
"CONTROLLED.CLOSE": [
{
guard: "restoreFocus",
target: "focused",
actions: ["focusTriggerEl", "clearHighlightedItem"]
},
{
target: "idle",
actions: ["clearHighlightedItem"]
}
],
CLOSE: [
{
guard: "isOpenControlled",
actions: ["invokeOnClose"]
},
{
guard: "restoreFocus",
target: "focused",
actions: ["invokeOnClose", "focusTriggerEl", "clearHighlightedItem"]
},
{
target: "idle",
actions: ["invokeOnClose", "clearHighlightedItem"]
}
],
"TRIGGER.CLICK": [
{
guard: "isOpenControlled",
actions: ["invokeOnClose"]
},
{
target: "focused",
actions: ["invokeOnClose", "clearHighlightedItem"]
}
],
"ITEM.CLICK": [
{
guard: and("closeOnSelect", "isOpenControlled"),
actions: ["selectHighlightedItem", "invokeOnClose"]
},
{
guard: "closeOnSelect",
target: "focused",
actions: ["selectHighlightedItem", "invokeOnClose", "focusTriggerEl", "clearHighlightedItem"]
},
{
actions: ["selectHighlightedItem"]
}
],
"CONTENT.HOME": {
actions: ["highlightFirstItem"]
},
"CONTENT.END": {
actions: ["highlightLastItem"]
},
"CONTENT.ARROW_DOWN": [
{
guard: and("hasHighlightedItem", "loop", "isLastItemHighlighted"),
actions: ["highlightFirstItem"]
},
{
guard: "hasHighlightedItem",
actions: ["highlightNextItem"]
},
{
actions: ["highlightFirstItem"]
}
],
"CONTENT.ARROW_UP": [
{
guard: and("hasHighlightedItem", "loop", "isFirstItemHighlighted"),
actions: ["highlightLastItem"]
},
{
guard: "hasHighlightedItem",
actions: ["highlightPreviousItem"]
},
{
actions: ["highlightLastItem"]
}
],
"CONTENT.TYPEAHEAD": {
actions: ["highlightMatchingItem"]
},
"ITEM.POINTER_MOVE": {
actions: ["highlightItem"]
},
"ITEM.POINTER_LEAVE": {
actions: ["clearHighlightedItem"]
},
"POSITIONING.SET": {
actions: ["reposition"]
}
}
}
},
implementations: {
guards: {
loop: ({ prop }) => !!prop("loopFocus"),
multiple: ({ prop }) => !!prop("multiple"),
hasSelectedItems: ({ computed }) => !!computed("hasSelectedItems"),
hasHighlightedItem: ({ context }) => context.get("highlightedValue") != null,
isFirstItemHighlighted: ({ context, prop }) => context.get("highlightedValue") === prop("collection").firstValue,
isLastItemHighlighted: ({ context, prop }) => context.get("highlightedValue") === prop("collection").lastValue,
closeOnSelect: ({ prop, event }) => !!(event.closeOnSelect ?? prop("closeOnSelect")),
restoreFocus: ({ event }) => restoreFocusFn(event),
// guard assertions (for controlled mode)
isOpenControlled: ({ prop }) => prop("open") !== void 0,
isTriggerClickEvent: ({ event }) => event.previousEvent?.type === "TRIGGER.CLICK",
isTriggerEnterEvent: ({ event }) => event.previousEvent?.type === "TRIGGER.ENTER",
isTriggerArrowUpEvent: ({ event }) => event.previousEvent?.type === "TRIGGER.ARROW_UP",
isTriggerArrowDownEvent: ({ event }) => event.previousEvent?.type === "TRIGGER.ARROW_DOWN"
},
effects: {
trackFocusVisible({ scope }) {
return trackFocusVisible({ root: scope.getRootNode?.() });
},
trackFormControlState({ context, scope }) {
return trackFormControl(getHiddenSelectEl(scope), {
onFieldsetDisabledChange(disabled) {
context.set("fieldsetDisabled", disabled);
},
onFormReset() {
const value = context.initial("value");
context.set("value", value);
}
});
},
trackDismissableElement({ scope, send, prop }) {
const contentEl = () => getContentEl(scope);
let restoreFocus = true;
return trackDismissableElement(contentEl, {
type: "listbox",
defer: true,
exclude: [getTriggerEl(scope), getClearTriggerEl(scope)],
onFocusOutside: prop("onFocusOutside"),
onPointerDownOutside: prop("onPointerDownOutside"),
onInteractOutside(event) {
prop("onInteractOutside")?.(event);
restoreFocus = !(event.detail.focusable || event.detail.contextmenu);
},
onDismiss() {
send({ type: "CLOSE", src: "interact-outside", restoreFocus });
}
});
},
computePlacement({ context, prop, scope }) {
const positioning = prop("positioning");
context.set("currentPlacement", positioning.placement);
const triggerEl = () => getTriggerEl(scope);
const positionerEl = () => getPositionerEl(scope);
return getPlacement(triggerEl, positionerEl, {
defer: true,
...positioning,
onComplete(data) {
context.set("currentPlacement", data.placement);
}
});
},
scrollToHighlightedItem({ context, prop, scope }) {
const exec = (immediate) => {
const highlightedValue = context.get("highlightedValue");
if (highlightedValue == null) return;
const modality = getInteractionModality();
if (modality === "pointer") return;
const contentEl2 = getContentEl(scope);
const scrollToIndexFn = prop("scrollToIndexFn");
if (scrollToIndexFn) {
const highlightedIndex = prop("collection").indexOf(highlightedValue);
scrollToIndexFn?.({
index: highlightedIndex,
immediate,
getElement: () => getItemEl(scope, highlightedValue)
});
return;
}
const itemEl = getItemEl(scope, highlightedValue);
scrollIntoView(itemEl, { rootEl: contentEl2, block: "nearest" });
};
raf(() => {
setInteractionModality("virtual");
exec(true);
});
const contentEl = () => getContentEl(scope);
return observeAttributes(contentEl, {
defer: true,
attributes: ["data-activedescendant"],
callback() {
exec(false);
}
});
}
},
actions: {
reposition({ context, prop, scope, event }) {
const positionerEl = () => getPositionerEl(scope);
getPlacement(getTriggerEl(scope), positionerEl, {
...prop("positioning"),
...event.options,
defer: true,
listeners: false,
onComplete(data) {
context.set("currentPlacement", data.placement);
}
});
},
toggleVisibility({ send, prop, event }) {
send({ type: prop("open") ? "CONTROLLED.OPEN" : "CONTROLLED.CLOSE", previousEvent: event });
},
highlightPreviousItem({ context, prop }) {
const highlightedValue = context.get("highlightedValue");
if (highlightedValue == null) return;
const value = prop("collection").getPreviousValue(highlightedValue, 1, prop("loopFocus"));
if (value == null) return;
context.set("highlightedValue", value);
},
highlightNextItem({ context, prop }) {
const highlightedValue = context.get("highlightedValue");
if (highlightedValue == null) return;
const value = prop("collection").getNextValue(highlightedValue, 1, prop("loopFocus"));
if (value == null) return;
context.set("highlightedValue", value);
},
highlightFirstItem({ context, prop }) {
const value = prop("collection").firstValue;
context.set("highlightedValue", value);
},
highlightLastItem({ context, prop }) {
const value = prop("collection").lastValue;
context.set("highlightedValue", value);
},
setInitialFocus({ scope }) {
raf(() => {
const element = getInitialFocus({
root: getContentEl(scope)
});
element?.focus({ preventScroll: true });
});
},
focusTriggerEl({ event, scope }) {
if (!restoreFocusFn(event)) return;
raf(() => {
const element = getTriggerEl(scope);
element?.focus({ preventScroll: true });
});
},
selectHighlightedItem({ context, prop, event }) {
let value = event.value ?? context.get("highlightedValue");
if (value == null || !prop("collection").has(value)) return;
prop("onSelect")?.({ value });
const nullable = prop("deselectable") && !prop("multiple") && context.get("value").includes(value);
value = nullable ? null : value;
context.set("value", (prev) => {
if (value == null) return [];
if (prop("multiple")) return addOrRemove(prev, value);
return [value];
});
},
highlightComputedFirstItem({ context, prop, computed }) {
const collection2 = prop("collection");
const value = computed("hasSelectedItems") ? collection2.sort(context.get("value"))[0] : collection2.firstValue;
context.set("highlightedValue", value);
},
highlightComputedLastItem({ context, prop, computed }) {
const collection2 = prop("collection");
const value = computed("hasSelectedItems") ? collection2.sort(context.get("value"))[0] : collection2.lastValue;
context.set("highlightedValue", value);
},
highlightFirstSelectedItem({ context, prop, computed }) {
if (!computed("hasSelectedItems")) return;
const value = prop("collection").sort(context.get("value"))[0];
context.set("highlightedValue", value);
},
highlightItem({ context, event }) {
context.set("highlightedValue", event.value);
},
highlightMatchingItem({ context, prop, event, refs }) {
const value = prop("collection").search(event.key, {
state: refs.get("typeahead"),
currentValue: context.get("highlightedValue")
});
if (value == null) return;
context.set("highlightedValue", value);
},
setHighlightedItem({ context, event }) {
context.set("highlightedValue", event.value);
},
clearHighlightedItem({ context }) {
context.set("highlightedValue", null);
},
selectItem({ context, prop, event }) {
prop("onSelect")?.({ value: event.value });
const nullable = prop("deselectable") && !prop("multiple") && context.get("value").includes(event.value);
const value = nullable ? null : event.value;
context.set("value", (prev) => {
if (value == null) return [];
if (prop("multiple")) return addOrRemove(prev, value);
return [value];
});
},
clearItem({ context, event }) {
context.set("value", (prev) => prev.filter((v) => v !== event.value));
},
setSelectedItems({ context, event }) {
context.set("value", event.value);
},
clearSelectedItems({ context }) {
context.set("value", []);
},
selectPreviousItem({ context, prop }) {
const [firstItem] = context.get("value");
const value = prop("collection").getPreviousValue(firstItem);
if (value) context.set("value", [value]);
},
selectNextItem({ context, prop }) {
const [firstItem] = context.get("value");
const value = prop("collection").getNextValue(firstItem);
if (value) context.set("value", [value]);
},
selectFirstItem({ context, prop }) {
const value = prop("collection").firstValue;
if (value) context.set("value", [value]);
},
selectLastItem({ context, prop }) {
const value = prop("collection").lastValue;
if (value) context.set("value", [value]);
},
selectMatchingItem({ context, prop, event, refs }) {
const value = prop("collection").search(event.key, {
state: refs.get("typeahead"),
currentValue: context.get("value")[0]
});
if (value == null) return;
context.set("value", [value]);
},
scrollContentToTop({ prop, scope }) {
if (prop("scrollToIndexFn")) {
const firstValue = prop("collection").firstValue;
prop("scrollToIndexFn")?.({
index: 0,
immediate: true,
getElement: () => getItemEl(scope, firstValue)
});
} else {
getContentEl(scope)?.scrollTo(0, 0);
}
},
invokeOnOpen({ prop, context }) {
prop("onOpenChange")?.({ open: true, value: context.get("value") });
},
invokeOnClose({ prop, context }) {
prop("onOpenChange")?.({ open: false, value: context.get("value") });
},
syncSelectElement({ context, prop, scope }) {
const selectEl = getHiddenSelectEl(scope);
if (!selectEl) return;
if (context.get("value").length === 0 && !prop("multiple")) {
selectEl.selectedIndex = -1;
return;
}
for (const option of selectEl.options) {
option.selected = context.get("value").includes(option.value);
}
},
syncCollection({ context, prop }) {
const collection2 = prop("collection");
const highlightedItem = collection2.find(context.get("highlightedValue"));
if (highlightedItem) context.set("highlightedItem", highlightedItem);
const next = deriveSelectionState({
values: context.get("value"),
collection: collection2,
selectedItemMap: context.get("selectedItemMap")
});
context.set("selectedItemMap", next.nextSelectedItemMap);
},
syncSelectedItems({ context, prop }) {
const next = deriveSelectionState({
values: context.get("value"),
collection: prop("collection"),
selectedItemMap: context.get("selectedItemMap")
});
context.set("selectedItemMap", next.nextSelectedItemMap);
},
syncHighlightedItem({ context, prop }) {
const collection2 = prop("collection");
const highlightedValue = context.get("highlightedValue");
const highlightedItem = highlightedValue ? collection2.find(highlightedValue) : null;
context.set("highlightedItem", highlightedItem);
},
dispatchChangeEvent({ scope }) {
queueMicrotask(() => {
const node = getHiddenSelectEl(scope);
if (!node) return;
const win = scope.getWin();
const evt = new win.Event("change", { bubbles: true, composed: true });
node.dispatchEvent(markAsInternalChangeEvent(evt));
});
}
}
}
});
function restoreFocusFn(event) {
const v = event.restoreFocus ?? event.previousEvent?.restoreFocus;
return v == null || !!v;
}
// components/select.ts
var Select = class extends Component {
_options = [];
hasGroups = false;
placeholder = "";
constructor(el, props) {
super(el, props);
const collectionFromProps = props.collection;
this._options = collectionFromProps?.items ?? [];
this.placeholder = getString(this.el, "placeholder") || "";
}
get options() {
return Array.isArray(this._options) ? this._options : [];
}
setOptions(options) {
this._options = Array.isArray(options) ? options : [];
}
getCollection() {
return collection(zagListCollectionConfig(this.options, this.hasGroups));
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
initMachine(props) {
const getCollection = this.getCollection.bind(this);
return new VanillaMachine(machine, {
...props,
get collection() {
return getCollection();
}
});
}
initApi() {
return this.zagConnect(connect);
}
applyItemProps() {
const contentEl = this.el.querySelector(
'[data-scope="select"][data-part="content"]'
);
if (!contentEl) return;
const isOwnedByContent = (el) => el.closest('[data-scope="select"][data-part="content"]') === contentEl;
contentEl.querySelectorAll('[data-scope="select"][data-part="item-group"]').forEach((groupEl) => {
if (!isOwnedByContent(groupEl)) return;
const groupId = groupEl.dataset.id ?? "";
this.spreadProps(groupEl, this.api.getItemGroupProps({ id: groupId }));
const labelEl = groupEl.querySelector(
'[data-scope="select"][data-part="item-group-label"]'
);
if (labelEl) {
this.spreadProps(labelEl, this.api.getItemGroupLabelProps({ htmlFor: groupId }));
}
});
contentEl.querySelectorAll('[data-scope="select"][data-part="item"]').forEach((itemEl) => {
if (!isOwnedByContent(itemEl)) return;
const value = itemEl.dataset.value ?? "";
if (!value) return;
const item = this.options.find((i) => String(itemValue(i)) === String(value));
if (!item) return;
this.spreadProps(itemEl, this.api.getItemProps({ item }));
const textEl = itemEl.querySelector(
'[data-scope="select"][data-part="item-text"]'
);
if (textEl) {
this.spreadProps(textEl, this.api.getItemTextProps({ item }));
}
const indicatorEl = itemEl.querySelector(
'[data-scope="select"][data-part="item-indicator"]'
);
if (indicatorEl) {
this.spreadProps(indicatorEl, this.api.getItemIndicatorProps({ item }));
}
});
}
render() {
const root = this.el.querySelector('[data-scope="select"][data-part="root"]') ?? this.el;
this.spreadProps(root, this.api.getRootProps());
const valueInput = this.el.querySelector(
'[data-scope="select"][data-part="value-input"]'
);
const formArrayName = getString(this.el, "hiddenSelectName");
if (valueInput) {
syncInputFormAssociation(valueInput, this.el);
if (valueInput.name && !formArrayName) {
const valueStr = this.api.value?.length ? this.api.value.map(String).join(",") : "";
valueInput.value = valueStr;
}
}
const hiddenSelect = this.el.querySelector(
'[data-scope="select"][data-part="hidden-select"]'
);
if (hiddenSelect) {
this.spreadProps(hiddenSelect, this.api.getHiddenSelectProps());
if (formArrayName) {
hiddenSelect.name = formArrayName;
hiddenSelect.disabled = false;
const valueSet = new Set((this.api.value ?? []).map(String));
Array.from(hiddenSelect.options).forEach((option) => {
if (option.value === "") {
option.selected = false;
return;
}
option.selected = valueSet.has(option.value);
});
if (valueInput) valueInput.removeAttribute("name");
} else if (hiddenSelect.name) {
const valueSet = new Set((this.api.value ?? []).map(String));
Array.from(hiddenSelect.options).forEach((option) => {
if (option.value === "") {
option.selected = false;
return;
}
option.selected = valueSet.has(option.value);
});
} else {
hiddenSelect.disabled = true;
hiddenSelect.removeAttribute("name");
}
syncInputFormAssociation(hiddenSelect, this.el);
}
["label", "control", "trigger", "indicator", "clear-trigger", "positioner"].forEach((part) => {
const el = this.el.querySelector(`[data-scope="select"][data-part="${part}"]`);
if (!el) return;
const method = "get" + part.split("-").map((s) => s[0].toUpperCase() + s.slice(1)).join("") + "Props";
this.spreadProps(el, this.api[method]());
});
const valueText = this.el.querySelector(
'[data-scope="select"][data-part="item-text"]'
);
if (valueText && this.el.dataset.updateTrigger !== "false") {
const valueAsString = this.api.valueAsString;
if (this.api.value && this.api.value.length > 0 && !valueAsString) {
const selectedValue = this.api.value[0];
const selectedItem = this.options.find((item) => {
return String(itemValue(item)) === String(selectedValue);
});
valueText.textContent = selectedItem?.label || this.placeholder;
} else {
valueText.textContent = valueAsString || this.placeholder;
}
}
const contentEl = this.el.querySelector(
'[data-scope="select"][data-part="content"]'
);
if (contentEl) {
this.spreadProps(contentEl, this.api.getContentProps());
this.applyItemProps();
}
}
};
// hooks/select.ts
function selectHiddenSelectForForm(el) {
const hiddenSelect = el.querySelector(
'[data-scope="select"][data-part="hidden-select"]'
);
if (!hiddenSelect) return null;
const formArrayName = getString(el, "hiddenSelectName");
if (formArrayName) {
hiddenSelect.name = formArrayName;
hiddenSelect.disabled = false;
return hiddenSelect;
}
if (!hiddenSelect.name) return null;
return hiddenSelect;
}
function formatSelectHiddenValue(el, values) {
const list = values.map((v) => String(v));
if (list.length === 0) return "";
if (getBoolean(el, "multiple") && selectHiddenSelectForForm(el)) return "";
return getBoolean(el, "multiple") ? list.join(",") : list[0] ?? "";
}
function syncSelectHiddenSelectForPhoenix(hiddenSelect, values, onTouched) {
const valueSet = new Set(values.map(String));
Array.from(hiddenSelect.options).forEach((option) => {
if (option.value === "") {
option.selected = false;
return;
}
option.selected = valueSet.has(option.value);
});
queueMicrotask(() => {
onTouched?.();
hiddenSelect.dispatchEvent(new Event("input", { bubbles: true }));
hiddenSelect.dispatchEvent(new Event("change", { bubbles: true }));
});
}
function syncSelectHiddenInputForPhoenix(el, values, onTouched) {
const hiddenSelect = selectHiddenSelectForForm(el);
if (hiddenSelect && getBoolean(el, "multiple")) {
syncSelectHiddenSelectForPhoenix(hiddenSelect, values, onTouched);
return;
}
const valueInput = el.querySelector(
'[data-scope="select"][data-part="value-input"]'
);
if (!valueInput) return;
queueLiveViewFormInputSync(valueInput, () => formatSelectHiddenValue(el, values), onTouched);
}
function buildCollection(items, hasGroups) {
return collection(zagListCollectionConfig(items, hasGroups));
}
function selectZagPropsBase(el, liveSocket, pushEvent, canPush, markFieldTouched) {
const redirectOn = getBoolean(el, "redirect");
return {
id: el.id,
disabled: getBoolean(el, "disabled"),
closeOnSelect: getBoolean(el, "closeOnSelect"),
dir: getDir(el),
loopFocus: getBoolean(el, "loopFocus"),
multiple: redirectOn ? false : getBoolean(el, "multiple"),
invalid: getBoolean(el, "invalid"),
name: getString(el, "name"),
form: getString(el, "form"),
readOnly: getBoolean(el, "readonly"),
required: getBoolean(el, "required"),
deselectable: getBoolean(el, "deselectable"),
positioning: readPositioningOptions(el),
onValueChange: (details) => {
const firstValue = details.value.length > 0 ? String(details.value[0]) : null;
if (getBoolean(el, "redirect") && firstValue) {
const itemEl = el.querySelector(
`[data-scope="select"][data-part="item"][data-value="${CSS.escape(firstValue)}"]`
);
performRedirect(readDomItemRedirect(itemEl, firstValue), { liveSocket });
}
syncSelectHiddenInputForPhoenix(el, details.value, markFieldTouched);
notifyChange({
el,
canPushServer: canPush(),
pushEvent,
payload: {
id: el.id,
value: details.value,
items: details.items
},
serverEventName: getString(el, "onValueChange"),
clientEventName: getString(el, "onValueChangeClient")
});
}
};
}
function reapplySelectInteractiveState(el) {
el.removeAttribute("data-loading");
if (getBoolean(el, "disabled") || getBoolean(el, "readonly")) return;
const trigger = el.querySelector('[data-scope="select"][data-part="trigger"]');
if (!trigger || getBoolean(trigger, "disabled")) return;
trigger.disabled = false;
trigger.removeAttribute("disabled");
}
var SelectHook = {
mounted() {
const el = this.el;
const pushEvent = this.pushEvent.bind(this);
const canPush = () => canPushEvent(this.liveSocket);
const hook = this;
hook.fieldTouched = false;
const markFieldTouched = () => {
hook.fieldTouched = true;
};
const defaultValues = getStringList(el, "defaultValue") ?? [];
if (defaultValues.length > 0) {
hook.fieldTouched = true;
queueMicrotask(() => {
const hiddenSelect = selectHiddenSelectForForm(el);
if (hiddenSelect && getBoolean(el, "multiple")) {
syncSelectHiddenSelectForPhoenix(hiddenSelect, defaultValues);
return;
}
const valueInput = el.querySelector(
'[data-scope="select"][data-part="value-input"]'
);
if (valueInput) reapplyLiveViewValueInputUsage(valueInput);
});
}
const allItems = JSON.parse(el.dataset.items || "[]");
const hasGroups = allItems.some((item) => Boolean(item.group));
const selectComponent = new Select(el, {
...selectZagPropsBase(el, this.liveSocket, pushEvent, canPush, markFieldTouched),
collection: buildCollection(allItems, hasGroups),
...mountStringListBinding(el)
});
selectComponent.hasGroups = hasGroups;
selectComponent.setOptions(allItems);
selectComponent.init();
this.select = selectComponent;
this.handlers = [];
const domRegistry = createDomEventRegistry(el);
this.domRegistry = domRegistry;
domRegistry.add("corex:select:set-value", (event) => {
selectComponent.api.setValue(event.detail.value);
});
domRegistry.add("corex:select:set-open", (event) => {
selectComponent.api.setOpen(event.detail.open);
});
const registry = createHookHandleEventRegistry(this);
this.handleRegistry = registry;
registry.add("select_set_value", (payload) => {
if (!idMatches(el.id, readPayloadId(payload))) return;
selectComponent.api.setValue(payload.value);
});
registry.add("select_set_open", (payload) => {
if (!idMatches(el.id, readPayloadId(payload))) return;
if (typeof payload.open !== "boolean") return;
selectComponent.api.setOpen(payload.open);
});
},
updated() {
if (!this.select) return;
const valuePatch = readUpdatedServerStringList(this.el);
const newItems = JSON.parse(this.el.dataset.items || "[]");
const hasGroups = newItems.some((item) => Boolean(item.group));
this.select.hasGroups = hasGroups;
this.select.setOptions(newItems);
const pushEvent = this.pushEvent.bind(this);
const canPush = () => canPushEvent(this.liveSocket);
this.select.updateProps({
...selectZagPropsBase(this.el, this.liveSocket, pushEvent, canPush, () => {
this.fieldTouched = true;
}),
collection: this.select.getCollection(),
...valuePatch
});
queueMicrotask(() => {
reapplySelectInteractiveState(this.el);
if (!("value" in valuePatch) || !this.select) return;
const values = valuePatch.value;
const hiddenSelect = selectHiddenSelectForForm(this.el);
if (hiddenSelect && getBoolean(this.el, "multiple")) {
syncSelectHiddenSelectForPhoenix(hiddenSelect, values);
return;
}
const valueInput = this.el.querySelector(
'[data-scope="select"][data-part="value-input"]'
);
if (!valueInput) return;
const v = formatSelectHiddenValue(this.el, values);
if (valueInput.value !== v) valueInput.value = v;
reapplyLiveViewValueInputUsage(valueInput);
});
},
destroyed() {
if (this.handlers) {
for (const handler of this.handlers) {
this.removeHandleEvent(handler);
}
}
this.domRegistry?.teardown();
this.handleRegistry?.teardown();
this.select?.destroy();
}
};
export {
SelectHook as Select,
buildCollection,
formatSelectHiddenValue,
reapplySelectInteractiveState,
syncSelectHiddenInputForPhoenix,
syncSelectHiddenSelectForPhoenix
};