@vielzeug/refine 2.1.3 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"select.cjs","names":[],"sources":["../src/inputs/select/select.ts"],"sourcesContent":["import { bind, define, getHost, html, onCleanup, onMounted, prop, useEmit, useField, useSlots } from '@vielzeug/ore';\nimport { computed, effect, signal, watch } from '@vielzeug/ripple';\n\nimport type { OverlayOpenChangeDetail, OverlayOpenReason } from '../../core';\nimport { createChoiceField, createListboxDropdown, lifecycleSignal } from '../../core';\nimport type { SelectableFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\nimport '../../feedback/chip/chip';\nimport '../../content/icon/icon';\nimport '../input/input';\nimport { disablableBundle, loadableBundle, roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, reducedMotionMixin, roundedVariantMixin } from '../../styles';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport componentStyles from './select.css?inline';\n\n// ── Types ─────────────────────────────────────────────────────────────\n\ntype OptionItem = {\n disabled: boolean;\n group?: string;\n label: string;\n value: string;\n};\n\nexport type OreSelectOptionInput = {\n disabled?: boolean;\n group?: string;\n label?: string;\n value: string;\n};\n\ntype FlatRow =\n | {\n idx: number;\n opt: OptionItem;\n type: 'option';\n }\n | {\n label: string;\n type: 'group';\n };\n\n// ── Styles ─────────────────────────────────────────────────────────────\n\n// ── Component Props ─────────────────────────────────────────────────────────────\n\n/** Select component properties */\n\nexport type OreSelectEvents = {\n change: Event;\n input: Event;\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreSelectProps = SelectableFieldProps<Exclude<VisualVariant, 'text' | 'frost'>> & {\n /** Show loading state in dropdown */\n loading?: boolean;\n /** Allow selecting multiple options */\n multiple?: boolean;\n /** JS options array (alternative to slotted <option> elements) */\n options?: OreSelectOptionInput[];\n /** Mark the field as required */\n required?: boolean;\n};\n\n/**\n * A fully custom form-associated select dropdown with keyboard navigation and ARIA support.\n * Reads `<option>` and `<optgroup>` children from the default slot.\n *\n * @element ore-select\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current selected value(s) (comma-separated for multiple)\n * @attr {string} placeholder - Placeholder when no option selected\n * @attr {string} name - Form field name\n * @attr {boolean} multiple - Enable multi-select\n * @attr {boolean} disabled - Disable the select\n * @attr {boolean} required - Required field\n * @attr {string} helper - Helper text below the select\n * @attr {string} error - Error message\n * @attr {boolean} success - Show an inline success check icon (suppressed while `error` is set)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {boolean} fullwidth - Expand to full width\n *\n * @fires input - Fired when selection changes.\n * @fires change - Fired when selection changes.\n * @fires open-change - Fired when the dropdown state changes. detail: { open, reason }\n *\n * @slot - `<option>` and `<optgroup>` elements\n *\n * @cssprop --select-bg - Background\n * @cssprop --select-border-color - Border color\n * @cssprop --select-radius - Border radius\n * @cssprop --select-padding - Padding\n * @cssprop --select-font-size - Font size\n * @cssprop --select-placeholder-color - Placeholder text color\n *\n * @part trigger - The `ore-input` trigger element that opens the dropdown.\n * @part dropdown - The dropdown listbox panel container.\n *\n * @example\n * ```html\n * <ore-select label=\"Role\" value=\"admin\">\n * <option value=\"admin\">Administrator</option>\n * <option value=\"editor\">Editor</option>\n * <option value=\"viewer\">Viewer</option>\n * </ore-select>\n *\n * <ore-select label=\"Tags\" multiple color=\"primary\">\n * <optgroup label=\"Frontend\">\n * <option value=\"react\">React</option>\n * <option value=\"vue\">Vue</option>\n * </optgroup>\n * <optgroup label=\"Backend\">\n * <option value=\"node\">Node.js</option>\n * </optgroup>\n * </ore-select>\n * ```\n */\nexport const SELECT_TAG = 'ore-select' as const;\ndefine<OreSelectProps>(SELECT_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...loadableBundle,\n ...roundableBundle,\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n multiple: prop.bool(false),\n name: prop.string(),\n options: prop.json(undefined as OreSelectOptionInput[] | undefined),\n placeholder: prop.string(),\n required: prop.bool(false),\n success: prop.bool(false),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreSelectEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n // ────────────────────────────────────────────────────────────────\n // State & Context\n // ────────────────────────────────────────────────────────────────\n\n const slottedOptions = signal<OptionItem[]>([]);\n const isLoading = computed(() => Boolean(props.loading.value));\n\n // Merged options: explicit prop value overrides slotted options\n function normalizeOption(option: OreSelectOptionInput): OptionItem {\n return {\n disabled: Boolean(option.disabled),\n group: option.group,\n label: option.label ?? option.value,\n value: option.value,\n };\n }\n\n const options = computed(() => {\n const explicitOptions = props.options.value;\n\n return Array.isArray(explicitOptions) ? explicitOptions.map(normalizeOption) : slottedOptions.value;\n });\n let triggerEl: HTMLElement | null = null;\n let dropdownEl: HTMLElement | null = null;\n\n const abortSignal = lifecycleSignal(onCleanup);\n const choice = createChoiceField({\n disabled: props.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n multiple: props.multiple,\n prefix: 'select',\n required: props.required,\n signal: abortSignal,\n value: props.value,\n });\n\n choice.attachFormField(\n useField<string>({\n disabled: choice.disabled,\n onReset: choice.reset,\n toFormValue: (v) => v,\n validationMessage: choice.validationMessage,\n validity: choice.validity,\n value: choice.formValue,\n }),\n );\n\n const optionList = createListboxDropdown<OptionItem>({\n getBoundary: () => el,\n getFocusedOptionElement: () => dropdownEl?.querySelector<HTMLElement>('[data-focused]') ?? null,\n getItems: () => options.value,\n getPanel: () => dropdownEl,\n getReference: () => triggerEl,\n getTrigger: () => triggerEl,\n isDisabled: () => choice.disabled.value,\n onClose: (reason) => {\n emit('open-change', { open: false, reason });\n choice.triggerValidation('blur');\n },\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n signal: abortSignal,\n });\n\n const { triggerValidation } = choice;\n const selectedValues = choice.selectedValues;\n const isDisabled = choice.disabled;\n\n defineFieldValue(\n el,\n () => choice.formValue.value,\n (value) => {\n choice.setValues(value ? value.split(',') : []);\n },\n );\n\n const { fieldId: selectId } = choice;\n const listboxId = `listbox-${selectId}`;\n const { focusedIndex, isOpen } = optionList;\n\n // Sync host attributes for CSS hooks\n bind({\n attr: {\n 'has-error': () => (props.error.value ? true : undefined),\n open: () => (isOpen.value ? true : undefined),\n size: props.size,\n variant: props.variant,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Option Reading from Slot\n // ────────────────────────────────────────────────────────────────\n\n function readOptions() {\n const slot = shadowRoot?.querySelector<HTMLSlotElement>('slot');\n\n if (!slot) return;\n\n const assigned = slot.assignedElements({ flatten: true });\n const items: OptionItem[] = [];\n\n for (const el of assigned) {\n if (el.tagName === 'OPTION') {\n const opt = el as HTMLOptionElement;\n\n items.push({ disabled: opt.disabled, label: opt.text || opt.value, value: opt.value });\n } else if (el.tagName === 'OPTGROUP') {\n const group = el as HTMLOptGroupElement;\n const groupLabel = group.label;\n\n for (const child of Array.from(group.querySelectorAll('option'))) {\n const opt = child as HTMLOptionElement;\n\n items.push({ disabled: opt.disabled, group: groupLabel, label: opt.text || opt.value, value: opt.value });\n }\n }\n }\n\n slottedOptions.value = items;\n }\n\n const displayLabel = computed(() => {\n if (selectedValues.value.length === 0) return '';\n\n if (props.multiple.value && selectedValues.value.length > 1) {\n return `${selectedValues.value.length} selected`;\n }\n\n const first = selectedValues.value[0];\n\n return options.value.find((o) => o.value === first)?.label ?? first;\n });\n const selectedChipItems = computed(() => {\n if (!props.multiple.value) return [];\n\n return selectedValues.value.map((value) => ({\n label: options.value.find((o) => o.value === value)?.label ?? value,\n value,\n }));\n });\n const showChips = computed(() => props.multiple.value && selectedValues.value.length > 0);\n const triggerText = computed(() => displayLabel.value || props.placeholder.value || '');\n\n function buildFlatList(opts: OptionItem[]): FlatRow[] {\n const flat: FlatRow[] = [];\n const groups = new Map<string | undefined, OptionItem[]>();\n\n for (const opt of opts) {\n const key = opt.group;\n\n let group = groups.get(key);\n\n if (!group) {\n group = [];\n groups.set(key, group);\n }\n\n group.push(opt);\n }\n\n let globalIdx = 0;\n\n for (const [groupLabel, groupOpts] of groups) {\n if (groupLabel !== undefined) flat.push({ label: groupLabel, type: 'group' });\n\n for (const opt of groupOpts) flat.push({ idx: globalIdx++, opt, type: 'option' });\n }\n\n return flat;\n }\n\n const flatRows = computed(() => buildFlatList(options.value));\n\n function emitChange(): void {\n setFieldValue(el, choice.formValue.value);\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n }\n\n function removeChip(event: Event): void {\n event.stopPropagation();\n\n const value = (event as CustomEvent<{ value?: string }>).detail?.value;\n\n if (value === undefined) return;\n\n choice.removeValue(value);\n emitChange();\n triggerValidation('change');\n }\n\n function openPopup(reason: OverlayOpenReason = 'programmatic') {\n optionList.open(reason);\n\n requestAnimationFrame(() => {\n const selectedIndex =\n selectedValues.value.length > 0\n ? options.value.findIndex((option) => option.value === selectedValues.value[0])\n : -1;\n\n if (selectedIndex >= 0) {\n optionList.set(selectedIndex);\n\n return;\n }\n\n optionList.navigate('first');\n });\n }\n\n // Selection\n\n function selectOption(opt: OptionItem, _e?: Event) {\n if (opt.disabled) return;\n\n if (props.multiple.value) {\n choice.toggleValue(opt.value);\n } else {\n choice.selectValue(opt.value);\n optionList.close();\n }\n\n emitChange();\n triggerValidation('change');\n }\n\n // Keyboard navigation\n\n function handleTriggerKeydown(e: KeyboardEvent) {\n if (isDisabled.value) return;\n\n // Let optionList handle arrow key navigation\n if (optionList.handleKeydown(e)) return;\n\n switch (e.key) {\n case ' ':\n case 'Enter':\n e.preventDefault();\n\n if (isOpen.value) {\n const idx = focusedIndex.value;\n const opts = options.value;\n\n if (idx >= 0 && idx < opts.length) selectOption(opts[idx], e);\n } else {\n openPopup('keyboard');\n }\n\n break;\n case 'Tab':\n optionList.close();\n break;\n }\n }\n\n // ore-input prop helpers\n const inputValue = () => triggerText.value;\n const inputLabel = () => props.label.value ?? '';\n const inputPlaceholder = () => props.placeholder.value ?? '';\n const inputLabelPlacement = () => props['label-placement'].value ?? 'inset';\n const inputColor = () => props.color?.value ?? undefined;\n const inputSize = () => props.size?.value ?? undefined;\n const inputVariant = () => props.variant?.value ?? undefined;\n const inputRounded = () => props.rounded?.value ?? undefined;\n const inputHelper = () => props.helper.value ?? '';\n const inputError = () => props.error.value ?? '';\n const inputDisabled = () => (isDisabled.value ? true : undefined);\n const inputRequired = () => (props.required.value ? true : undefined);\n const inputFullwidth = () => (props.fullwidth.value ? true : undefined);\n const tabIndexAttr = () => (isDisabled.value ? '-1' : '0');\n\n watch(slots.elements(), () => readOptions(), { immediate: true });\n\n const stopPopoverSync = effect(() => {\n if (isOpen.value) {\n if (dropdownEl && 'showPopover' in dropdownEl && !dropdownEl.matches(':popover-open')) dropdownEl.showPopover();\n } else {\n if (dropdownEl && 'hidePopover' in dropdownEl && dropdownEl.matches(':popover-open')) dropdownEl.hidePopover();\n }\n });\n\n onCleanup(() => stopPopoverSync.dispose());\n\n onMounted(() => {\n let onTriggerClick: ((event: MouseEvent) => void) | null = null;\n let onTriggerKeydown: ((event: KeyboardEvent) => void) | null = null;\n\n if (triggerEl) {\n onTriggerClick = (event: MouseEvent) => {\n event.stopPropagation();\n\n if (isDisabled.value) return;\n\n if (isOpen.value) optionList.close('trigger');\n else openPopup('click');\n };\n onTriggerKeydown = (event: KeyboardEvent) => {\n handleTriggerKeydown(event);\n };\n\n triggerEl.addEventListener('click', onTriggerClick);\n triggerEl.addEventListener('keydown', onTriggerKeydown);\n }\n\n return () => {\n if (triggerEl && onTriggerClick) triggerEl.removeEventListener('click', onTriggerClick);\n\n if (triggerEl && onTriggerKeydown) triggerEl.removeEventListener('keydown', onTriggerKeydown);\n };\n });\n\n return html`\n <slot style=\"display:none\"></slot>\n <ore-input\n class=\"trigger\"\n part=\"trigger\"\n readonly\n tabindex=\"${tabIndexAttr}\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-controls=\"${listboxId}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n aria-expanded=\"${() => String(isOpen.value)}\"\n aria-invalid=\"${() => (props.error.value ? 'true' : null)}\"\n value=\"${inputValue}\"\n label=\"${inputLabel}\"\n placeholder=\"${inputPlaceholder}\"\n label-placement=\"${inputLabelPlacement}\"\n color=\"${inputColor}\"\n size=\"${inputSize}\"\n variant=\"${inputVariant}\"\n rounded=\"${inputRounded}\"\n helper=\"${inputHelper}\"\n error=\"${inputError}\"\n ?disabled=\"${inputDisabled}\"\n ?required=\"${inputRequired}\"\n ?fullwidth=\"${inputFullwidth}\"\n ?success=\"${() => props.success.value}\"\n ref=\"${(el: HTMLElement) => {\n triggerEl = el;\n }}\">\n <div slot=\"prefix\" class=\"chips-row\" ?hidden=\"${() => !showChips.value}\">\n ${() =>\n selectedChipItems.value.map(\n (item) => html`\n <ore-chip\n value=\"${item.value}\"\n label=\"${item.label}\"\n mode=\"removable\"\n variant=\"flat\"\n size=\"sm\"\n color=\"${props.color}\"\n @remove=\"${removeChip}\">\n ${item.label}\n </ore-chip>\n `,\n )}\n </div>\n <span slot=\"suffix\" class=\"trigger-suffix\" aria-hidden=\"true\">\n <span class=\"loader\"></span>\n <span class=\"trigger-icon\">\n <ore-icon name=\"chevron-down\" size=\"14\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n </span>\n </span>\n </ore-input>\n <div\n class=\"dropdown\"\n part=\"dropdown\"\n popover=\"manual\"\n ?data-open=\"${isOpen}\"\n role=\"listbox\"\n id=\"${listboxId}\"\n aria-label=\"Options\"\n ref=\"${(el: HTMLElement) => {\n dropdownEl = el;\n }}\">\n <div class=\"dropdown-loading\" ?hidden=\"${() => !isLoading.value}\">Loading…</div>\n <div class=\"dropdown-empty\" ?hidden=\"${() => isLoading.value || options.value.length > 0}\">No options</div>\n <div class=\"options-list\" ?hidden=\"${() => isLoading.value || options.value.length === 0}\">\n ${() =>\n flatRows.value.map((row) =>\n row.type === 'group'\n ? html`\n <div class=\"optgroup-label\" role=\"presentation\">${row.label}</div>\n `\n : html`\n <div\n class=\"option\"\n role=\"option\"\n id=\"${`${selectId}-opt-${row.idx}`}\"\n data-option-index=\"${String(row.idx)}\"\n data-option-value=\"${row.opt.value}\"\n aria-selected=\"${() => String(selectedValues.value.includes(row.opt.value))}\"\n aria-disabled=\"${() => String(row.opt.disabled)}\"\n ?data-focused=\"${() => focusedIndex.value === row.idx}\"\n ?data-selected=\"${() => selectedValues.value.includes(row.opt.value)}\"\n ?data-disabled=\"${() => row.opt.disabled}\"\n @click=\"${(e: MouseEvent) => {\n e.stopPropagation();\n selectOption(row.opt, e);\n }}\"\n @pointerenter=\"${() => {\n optionList.set(row.idx);\n }}\">\n <span>${row.opt.label}</span>\n <span class=\"option-check\" aria-hidden=\"true\">\n <ore-icon name=\"check\" size=\"14\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </span>\n </div>\n `,\n )}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, reducedMotionMixin, roundedVariantMixin, componentStyles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"select.cjs","names":[],"sources":["../src/inputs/select/select.ts"],"sourcesContent":["import { bind, define, getHost, html, onCleanup, onMounted, prop, useEmit, useField, useSlots } from '@vielzeug/ore';\nimport { computed, effect, signal, watch } from '@vielzeug/ripple';\n\nimport type { OverlayOpenChangeDetail, OverlayOpenReason } from '../../core';\nimport { createChoiceField, createListboxDropdown, lifecycleSignal } from '../../core';\nimport type { SelectableFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\nimport '../../feedback/chip/chip';\nimport '../../content/icon/icon';\nimport '../input/input';\nimport { disablableBundle, loadableBundle, roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, reducedMotionMixin, roundedVariantMixin } from '../../styles';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport componentStyles from './select.css?inline';\n\n// ── Types ─────────────────────────────────────────────────────────────\n\ntype OptionItem = {\n disabled: boolean;\n group?: string;\n label: string;\n value: string;\n};\n\nexport type OreSelectOptionInput = {\n disabled?: boolean;\n group?: string;\n label?: string;\n value: string;\n};\n\ntype FlatRow =\n | {\n idx: number;\n opt: OptionItem;\n type: 'option';\n }\n | {\n label: string;\n type: 'group';\n };\n\n// ── Styles ─────────────────────────────────────────────────────────────\n\n// ── Component Props ─────────────────────────────────────────────────────────────\n\n/** Select component properties */\n\nexport type OreSelectEvents = {\n change: Event;\n input: Event;\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreSelectProps = SelectableFieldProps<Exclude<VisualVariant, 'frost'>> & {\n /** Show loading state in dropdown */\n loading?: boolean;\n /** Allow selecting multiple options */\n multiple?: boolean;\n /** JS options array (alternative to slotted <option> elements) */\n options?: OreSelectOptionInput[];\n /** Mark the field as required */\n required?: boolean;\n};\n\n/**\n * A fully custom form-associated select dropdown with keyboard navigation and ARIA support.\n * Reads `<option>` and `<optgroup>` children from the default slot.\n *\n * @element ore-select\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current selected value(s) (comma-separated for multiple)\n * @attr {string} placeholder - Placeholder when no option selected\n * @attr {string} name - Form field name\n * @attr {boolean} multiple - Enable multi-select\n * @attr {boolean} disabled - Disable the select\n * @attr {boolean} required - Required field\n * @attr {string} helper - Helper text below the select\n * @attr {string} error - Error message\n * @attr {boolean} success - Show an inline success check icon (suppressed while `error` is set)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {boolean} fullwidth - Expand to full width\n *\n * @fires input - Fired when selection changes.\n * @fires change - Fired when selection changes.\n * @fires open-change - Fired when the dropdown state changes. detail: { open, reason }\n *\n * @slot - `<option>` and `<optgroup>` elements\n *\n * @cssprop --select-bg - Background\n * @cssprop --select-border-color - Border color\n * @cssprop --select-radius - Border radius\n * @cssprop --select-padding - Padding\n * @cssprop --select-font-size - Font size\n * @cssprop --select-placeholder-color - Placeholder text color\n *\n * @part trigger - The `ore-input` trigger element that opens the dropdown.\n * @part dropdown - The dropdown listbox panel container.\n *\n * @example\n * ```html\n * <ore-select label=\"Role\" value=\"admin\">\n * <option value=\"admin\">Administrator</option>\n * <option value=\"editor\">Editor</option>\n * <option value=\"viewer\">Viewer</option>\n * </ore-select>\n *\n * <ore-select label=\"Tags\" multiple color=\"primary\">\n * <optgroup label=\"Frontend\">\n * <option value=\"react\">React</option>\n * <option value=\"vue\">Vue</option>\n * </optgroup>\n * <optgroup label=\"Backend\">\n * <option value=\"node\">Node.js</option>\n * </optgroup>\n * </ore-select>\n * ```\n */\nexport const SELECT_TAG = 'ore-select' as const;\ndefine<OreSelectProps>(SELECT_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...loadableBundle,\n ...roundableBundle,\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n multiple: prop.bool(false),\n name: prop.string(),\n options: prop.json(undefined as OreSelectOptionInput[] | undefined),\n placeholder: prop.string(),\n required: prop.bool(false),\n success: prop.bool(false),\n value: prop.string(),\n variant: prop.string<Exclude<VisualVariant, 'frost'>>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreSelectEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n // ────────────────────────────────────────────────────────────────\n // State & Context\n // ────────────────────────────────────────────────────────────────\n\n const slottedOptions = signal<OptionItem[]>([]);\n const isLoading = computed(() => Boolean(props.loading.value));\n\n // Merged options: explicit prop value overrides slotted options\n function normalizeOption(option: OreSelectOptionInput): OptionItem {\n return {\n disabled: Boolean(option.disabled),\n group: option.group,\n label: option.label ?? option.value,\n value: option.value,\n };\n }\n\n const options = computed(() => {\n const explicitOptions = props.options.value;\n\n return Array.isArray(explicitOptions) ? explicitOptions.map(normalizeOption) : slottedOptions.value;\n });\n let triggerEl: HTMLElement | null = null;\n let dropdownEl: HTMLElement | null = null;\n\n const abortSignal = lifecycleSignal(onCleanup);\n const choice = createChoiceField({\n disabled: props.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n multiple: props.multiple,\n prefix: 'select',\n required: props.required,\n signal: abortSignal,\n value: props.value,\n });\n\n choice.attachFormField(\n useField<string>({\n disabled: choice.disabled,\n onReset: choice.reset,\n toFormValue: (v) => v,\n validationMessage: choice.validationMessage,\n validity: choice.validity,\n value: choice.formValue,\n }),\n );\n\n const optionList = createListboxDropdown<OptionItem>({\n getBoundary: () => el,\n getFocusedOptionElement: () => dropdownEl?.querySelector<HTMLElement>('[data-focused]') ?? null,\n getItems: () => options.value,\n getPanel: () => dropdownEl,\n getReference: () => triggerEl,\n getTrigger: () => triggerEl,\n isDisabled: () => choice.disabled.value,\n onClose: (reason) => {\n emit('open-change', { open: false, reason });\n choice.triggerValidation('blur');\n },\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n signal: abortSignal,\n });\n\n const { triggerValidation } = choice;\n const selectedValues = choice.selectedValues;\n const isDisabled = choice.disabled;\n\n defineFieldValue(\n el,\n () => choice.formValue.value,\n (value) => {\n choice.setValues(value ? value.split(',') : []);\n },\n );\n\n const { fieldId: selectId } = choice;\n const listboxId = `listbox-${selectId}`;\n const { focusedIndex, isOpen } = optionList;\n\n // Sync host attributes for CSS hooks\n bind({\n attr: {\n 'has-error': () => (props.error.value ? true : undefined),\n open: () => (isOpen.value ? true : undefined),\n size: props.size,\n variant: props.variant,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Option Reading from Slot\n // ────────────────────────────────────────────────────────────────\n\n function readOptions() {\n const slot = shadowRoot?.querySelector<HTMLSlotElement>('slot');\n\n if (!slot) return;\n\n const assigned = slot.assignedElements({ flatten: true });\n const items: OptionItem[] = [];\n\n for (const el of assigned) {\n if (el.tagName === 'OPTION') {\n const opt = el as HTMLOptionElement;\n\n items.push({ disabled: opt.disabled, label: opt.text || opt.value, value: opt.value });\n } else if (el.tagName === 'OPTGROUP') {\n const group = el as HTMLOptGroupElement;\n const groupLabel = group.label;\n\n for (const child of Array.from(group.querySelectorAll('option'))) {\n const opt = child as HTMLOptionElement;\n\n items.push({ disabled: opt.disabled, group: groupLabel, label: opt.text || opt.value, value: opt.value });\n }\n }\n }\n\n slottedOptions.value = items;\n }\n\n const displayLabel = computed(() => {\n if (selectedValues.value.length === 0) return '';\n\n if (props.multiple.value && selectedValues.value.length > 1) {\n return `${selectedValues.value.length} selected`;\n }\n\n const first = selectedValues.value[0];\n\n return options.value.find((o) => o.value === first)?.label ?? first;\n });\n const selectedChipItems = computed(() => {\n if (!props.multiple.value) return [];\n\n return selectedValues.value.map((value) => ({\n label: options.value.find((o) => o.value === value)?.label ?? value,\n value,\n }));\n });\n const showChips = computed(() => props.multiple.value && selectedValues.value.length > 0);\n const triggerText = computed(() => displayLabel.value || props.placeholder.value || '');\n\n function buildFlatList(opts: OptionItem[]): FlatRow[] {\n const flat: FlatRow[] = [];\n const groups = new Map<string | undefined, OptionItem[]>();\n\n for (const opt of opts) {\n const key = opt.group;\n\n let group = groups.get(key);\n\n if (!group) {\n group = [];\n groups.set(key, group);\n }\n\n group.push(opt);\n }\n\n let globalIdx = 0;\n\n for (const [groupLabel, groupOpts] of groups) {\n if (groupLabel !== undefined) flat.push({ label: groupLabel, type: 'group' });\n\n for (const opt of groupOpts) flat.push({ idx: globalIdx++, opt, type: 'option' });\n }\n\n return flat;\n }\n\n const flatRows = computed(() => buildFlatList(options.value));\n\n function emitChange(): void {\n setFieldValue(el, choice.formValue.value);\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n }\n\n function removeChip(event: Event): void {\n event.stopPropagation();\n\n const value = (event as CustomEvent<{ value?: string }>).detail?.value;\n\n if (value === undefined) return;\n\n choice.removeValue(value);\n emitChange();\n triggerValidation('change');\n }\n\n function openPopup(reason: OverlayOpenReason = 'programmatic') {\n optionList.open(reason);\n\n requestAnimationFrame(() => {\n const selectedIndex =\n selectedValues.value.length > 0\n ? options.value.findIndex((option) => option.value === selectedValues.value[0])\n : -1;\n\n if (selectedIndex >= 0) {\n optionList.set(selectedIndex);\n\n return;\n }\n\n optionList.navigate('first');\n });\n }\n\n // Selection\n\n function selectOption(opt: OptionItem, _e?: Event) {\n if (opt.disabled) return;\n\n if (props.multiple.value) {\n choice.toggleValue(opt.value);\n } else {\n choice.selectValue(opt.value);\n optionList.close();\n }\n\n emitChange();\n triggerValidation('change');\n }\n\n // Keyboard navigation\n\n function handleTriggerKeydown(e: KeyboardEvent) {\n if (isDisabled.value) return;\n\n // Let optionList handle arrow key navigation\n if (optionList.handleKeydown(e)) return;\n\n switch (e.key) {\n case ' ':\n case 'Enter':\n e.preventDefault();\n\n if (isOpen.value) {\n const idx = focusedIndex.value;\n const opts = options.value;\n\n if (idx >= 0 && idx < opts.length) selectOption(opts[idx], e);\n } else {\n openPopup('keyboard');\n }\n\n break;\n case 'Tab':\n optionList.close();\n break;\n }\n }\n\n // ore-input prop helpers\n const inputValue = () => triggerText.value;\n const inputLabel = () => props.label.value ?? '';\n const inputPlaceholder = () => props.placeholder.value ?? '';\n const inputLabelPlacement = () => props['label-placement'].value ?? 'inset';\n const inputColor = () => props.color?.value ?? undefined;\n const inputSize = () => props.size?.value ?? undefined;\n const inputVariant = () => props.variant?.value ?? undefined;\n const inputRounded = () => props.rounded?.value ?? undefined;\n const inputHelper = () => props.helper.value ?? '';\n const inputError = () => props.error.value ?? '';\n const inputDisabled = () => (isDisabled.value ? true : undefined);\n const inputRequired = () => (props.required.value ? true : undefined);\n const inputFullwidth = () => (props.fullwidth.value ? true : undefined);\n const tabIndexAttr = () => (isDisabled.value ? '-1' : '0');\n\n watch(slots.elements(), () => readOptions(), { immediate: true });\n\n const stopPopoverSync = effect(() => {\n if (isOpen.value) {\n if (dropdownEl && 'showPopover' in dropdownEl && !dropdownEl.matches(':popover-open')) dropdownEl.showPopover();\n } else {\n if (dropdownEl && 'hidePopover' in dropdownEl && dropdownEl.matches(':popover-open')) dropdownEl.hidePopover();\n }\n });\n\n onCleanup(() => stopPopoverSync.dispose());\n\n onMounted(() => {\n let onTriggerClick: ((event: MouseEvent) => void) | null = null;\n let onTriggerKeydown: ((event: KeyboardEvent) => void) | null = null;\n\n if (triggerEl) {\n onTriggerClick = (event: MouseEvent) => {\n event.stopPropagation();\n\n if (isDisabled.value) return;\n\n if (isOpen.value) optionList.close('trigger');\n else openPopup('click');\n };\n onTriggerKeydown = (event: KeyboardEvent) => {\n handleTriggerKeydown(event);\n };\n\n triggerEl.addEventListener('click', onTriggerClick);\n triggerEl.addEventListener('keydown', onTriggerKeydown);\n }\n\n return () => {\n if (triggerEl && onTriggerClick) triggerEl.removeEventListener('click', onTriggerClick);\n\n if (triggerEl && onTriggerKeydown) triggerEl.removeEventListener('keydown', onTriggerKeydown);\n };\n });\n\n return html`\n <slot style=\"display:none\"></slot>\n <ore-input\n class=\"trigger\"\n part=\"trigger\"\n readonly\n tabindex=\"${tabIndexAttr}\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-controls=\"${listboxId}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n aria-expanded=\"${() => String(isOpen.value)}\"\n aria-invalid=\"${() => (props.error.value ? 'true' : null)}\"\n value=\"${inputValue}\"\n label=\"${inputLabel}\"\n placeholder=\"${inputPlaceholder}\"\n label-placement=\"${inputLabelPlacement}\"\n color=\"${inputColor}\"\n size=\"${inputSize}\"\n variant=\"${inputVariant}\"\n rounded=\"${inputRounded}\"\n helper=\"${inputHelper}\"\n error=\"${inputError}\"\n ?disabled=\"${inputDisabled}\"\n ?required=\"${inputRequired}\"\n ?fullwidth=\"${inputFullwidth}\"\n ?success=\"${() => props.success.value}\"\n ref=\"${(el: HTMLElement) => {\n triggerEl = el;\n }}\">\n <div slot=\"prefix\" class=\"chips-row\" ?hidden=\"${() => !showChips.value}\">\n ${() =>\n selectedChipItems.value.map(\n (item) => html`\n <ore-chip\n value=\"${item.value}\"\n label=\"${item.label}\"\n mode=\"removable\"\n variant=\"flat\"\n size=\"sm\"\n color=\"${props.color}\"\n @remove=\"${removeChip}\">\n ${item.label}\n </ore-chip>\n `,\n )}\n </div>\n <span slot=\"suffix\" class=\"trigger-suffix\" aria-hidden=\"true\">\n <span class=\"loader\"></span>\n <span class=\"trigger-icon\">\n <ore-icon name=\"chevron-down\" size=\"14\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n </span>\n </span>\n </ore-input>\n <div\n class=\"dropdown\"\n part=\"dropdown\"\n popover=\"manual\"\n ?data-open=\"${isOpen}\"\n role=\"listbox\"\n id=\"${listboxId}\"\n aria-label=\"Options\"\n ref=\"${(el: HTMLElement) => {\n dropdownEl = el;\n }}\">\n <div class=\"dropdown-loading\" ?hidden=\"${() => !isLoading.value}\">Loading…</div>\n <div class=\"dropdown-empty\" ?hidden=\"${() => isLoading.value || options.value.length > 0}\">No options</div>\n <div class=\"options-list\" ?hidden=\"${() => isLoading.value || options.value.length === 0}\">\n ${() =>\n flatRows.value.map((row) =>\n row.type === 'group'\n ? html`\n <div class=\"optgroup-label\" role=\"presentation\">${row.label}</div>\n `\n : html`\n <div\n class=\"option\"\n role=\"option\"\n id=\"${`${selectId}-opt-${row.idx}`}\"\n data-option-index=\"${String(row.idx)}\"\n data-option-value=\"${row.opt.value}\"\n aria-selected=\"${() => String(selectedValues.value.includes(row.opt.value))}\"\n aria-disabled=\"${() => String(row.opt.disabled)}\"\n ?data-focused=\"${() => focusedIndex.value === row.idx}\"\n ?data-selected=\"${() => selectedValues.value.includes(row.opt.value)}\"\n ?data-disabled=\"${() => row.opt.disabled}\"\n @click=\"${(e: MouseEvent) => {\n e.stopPropagation();\n selectOption(row.opt, e);\n }}\"\n @pointerenter=\"${() => {\n optionList.set(row.idx);\n }}\">\n <span>${row.opt.label}</span>\n <span class=\"option-check\" aria-hidden=\"true\">\n <ore-icon name=\"check\" size=\"14\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </span>\n </div>\n `,\n )}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, reducedMotionMixin, roundedVariantMixin, componentStyles],\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","names":[],"sources":["../src/inputs/select/select.ts"],"sourcesContent":["import { bind, define, getHost, html, onCleanup, onMounted, prop, useEmit, useField, useSlots } from '@vielzeug/ore';\nimport { computed, effect, signal, watch } from '@vielzeug/ripple';\n\nimport type { OverlayOpenChangeDetail, OverlayOpenReason } from '../../core';\nimport { createChoiceField, createListboxDropdown, lifecycleSignal } from '../../core';\nimport type { SelectableFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\nimport '../../feedback/chip/chip';\nimport '../../content/icon/icon';\nimport '../input/input';\nimport { disablableBundle, loadableBundle, roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, reducedMotionMixin, roundedVariantMixin } from '../../styles';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport componentStyles from './select.css?inline';\n\n// ── Types ─────────────────────────────────────────────────────────────\n\ntype OptionItem = {\n disabled: boolean;\n group?: string;\n label: string;\n value: string;\n};\n\nexport type OreSelectOptionInput = {\n disabled?: boolean;\n group?: string;\n label?: string;\n value: string;\n};\n\ntype FlatRow =\n | {\n idx: number;\n opt: OptionItem;\n type: 'option';\n }\n | {\n label: string;\n type: 'group';\n };\n\n// ── Styles ─────────────────────────────────────────────────────────────\n\n// ── Component Props ─────────────────────────────────────────────────────────────\n\n/** Select component properties */\n\nexport type OreSelectEvents = {\n change: Event;\n input: Event;\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreSelectProps = SelectableFieldProps<Exclude<VisualVariant, 'text' | 'frost'>> & {\n /** Show loading state in dropdown */\n loading?: boolean;\n /** Allow selecting multiple options */\n multiple?: boolean;\n /** JS options array (alternative to slotted <option> elements) */\n options?: OreSelectOptionInput[];\n /** Mark the field as required */\n required?: boolean;\n};\n\n/**\n * A fully custom form-associated select dropdown with keyboard navigation and ARIA support.\n * Reads `<option>` and `<optgroup>` children from the default slot.\n *\n * @element ore-select\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current selected value(s) (comma-separated for multiple)\n * @attr {string} placeholder - Placeholder when no option selected\n * @attr {string} name - Form field name\n * @attr {boolean} multiple - Enable multi-select\n * @attr {boolean} disabled - Disable the select\n * @attr {boolean} required - Required field\n * @attr {string} helper - Helper text below the select\n * @attr {string} error - Error message\n * @attr {boolean} success - Show an inline success check icon (suppressed while `error` is set)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {boolean} fullwidth - Expand to full width\n *\n * @fires input - Fired when selection changes.\n * @fires change - Fired when selection changes.\n * @fires open-change - Fired when the dropdown state changes. detail: { open, reason }\n *\n * @slot - `<option>` and `<optgroup>` elements\n *\n * @cssprop --select-bg - Background\n * @cssprop --select-border-color - Border color\n * @cssprop --select-radius - Border radius\n * @cssprop --select-padding - Padding\n * @cssprop --select-font-size - Font size\n * @cssprop --select-placeholder-color - Placeholder text color\n *\n * @part trigger - The `ore-input` trigger element that opens the dropdown.\n * @part dropdown - The dropdown listbox panel container.\n *\n * @example\n * ```html\n * <ore-select label=\"Role\" value=\"admin\">\n * <option value=\"admin\">Administrator</option>\n * <option value=\"editor\">Editor</option>\n * <option value=\"viewer\">Viewer</option>\n * </ore-select>\n *\n * <ore-select label=\"Tags\" multiple color=\"primary\">\n * <optgroup label=\"Frontend\">\n * <option value=\"react\">React</option>\n * <option value=\"vue\">Vue</option>\n * </optgroup>\n * <optgroup label=\"Backend\">\n * <option value=\"node\">Node.js</option>\n * </optgroup>\n * </ore-select>\n * ```\n */\nexport const SELECT_TAG = 'ore-select' as const;\ndefine<OreSelectProps>(SELECT_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...loadableBundle,\n ...roundableBundle,\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n multiple: prop.bool(false),\n name: prop.string(),\n options: prop.json(undefined as OreSelectOptionInput[] | undefined),\n placeholder: prop.string(),\n required: prop.bool(false),\n success: prop.bool(false),\n value: prop.string(),\n variant: prop.string<'flat' | 'solid' | 'bordered' | 'outline' | 'ghost'>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreSelectEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n // ────────────────────────────────────────────────────────────────\n // State & Context\n // ────────────────────────────────────────────────────────────────\n\n const slottedOptions = signal<OptionItem[]>([]);\n const isLoading = computed(() => Boolean(props.loading.value));\n\n // Merged options: explicit prop value overrides slotted options\n function normalizeOption(option: OreSelectOptionInput): OptionItem {\n return {\n disabled: Boolean(option.disabled),\n group: option.group,\n label: option.label ?? option.value,\n value: option.value,\n };\n }\n\n const options = computed(() => {\n const explicitOptions = props.options.value;\n\n return Array.isArray(explicitOptions) ? explicitOptions.map(normalizeOption) : slottedOptions.value;\n });\n let triggerEl: HTMLElement | null = null;\n let dropdownEl: HTMLElement | null = null;\n\n const abortSignal = lifecycleSignal(onCleanup);\n const choice = createChoiceField({\n disabled: props.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n multiple: props.multiple,\n prefix: 'select',\n required: props.required,\n signal: abortSignal,\n value: props.value,\n });\n\n choice.attachFormField(\n useField<string>({\n disabled: choice.disabled,\n onReset: choice.reset,\n toFormValue: (v) => v,\n validationMessage: choice.validationMessage,\n validity: choice.validity,\n value: choice.formValue,\n }),\n );\n\n const optionList = createListboxDropdown<OptionItem>({\n getBoundary: () => el,\n getFocusedOptionElement: () => dropdownEl?.querySelector<HTMLElement>('[data-focused]') ?? null,\n getItems: () => options.value,\n getPanel: () => dropdownEl,\n getReference: () => triggerEl,\n getTrigger: () => triggerEl,\n isDisabled: () => choice.disabled.value,\n onClose: (reason) => {\n emit('open-change', { open: false, reason });\n choice.triggerValidation('blur');\n },\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n signal: abortSignal,\n });\n\n const { triggerValidation } = choice;\n const selectedValues = choice.selectedValues;\n const isDisabled = choice.disabled;\n\n defineFieldValue(\n el,\n () => choice.formValue.value,\n (value) => {\n choice.setValues(value ? value.split(',') : []);\n },\n );\n\n const { fieldId: selectId } = choice;\n const listboxId = `listbox-${selectId}`;\n const { focusedIndex, isOpen } = optionList;\n\n // Sync host attributes for CSS hooks\n bind({\n attr: {\n 'has-error': () => (props.error.value ? true : undefined),\n open: () => (isOpen.value ? true : undefined),\n size: props.size,\n variant: props.variant,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Option Reading from Slot\n // ────────────────────────────────────────────────────────────────\n\n function readOptions() {\n const slot = shadowRoot?.querySelector<HTMLSlotElement>('slot');\n\n if (!slot) return;\n\n const assigned = slot.assignedElements({ flatten: true });\n const items: OptionItem[] = [];\n\n for (const el of assigned) {\n if (el.tagName === 'OPTION') {\n const opt = el as HTMLOptionElement;\n\n items.push({ disabled: opt.disabled, label: opt.text || opt.value, value: opt.value });\n } else if (el.tagName === 'OPTGROUP') {\n const group = el as HTMLOptGroupElement;\n const groupLabel = group.label;\n\n for (const child of Array.from(group.querySelectorAll('option'))) {\n const opt = child as HTMLOptionElement;\n\n items.push({ disabled: opt.disabled, group: groupLabel, label: opt.text || opt.value, value: opt.value });\n }\n }\n }\n\n slottedOptions.value = items;\n }\n\n const displayLabel = computed(() => {\n if (selectedValues.value.length === 0) return '';\n\n if (props.multiple.value && selectedValues.value.length > 1) {\n return `${selectedValues.value.length} selected`;\n }\n\n const first = selectedValues.value[0];\n\n return options.value.find((o) => o.value === first)?.label ?? first;\n });\n const selectedChipItems = computed(() => {\n if (!props.multiple.value) return [];\n\n return selectedValues.value.map((value) => ({\n label: options.value.find((o) => o.value === value)?.label ?? value,\n value,\n }));\n });\n const showChips = computed(() => props.multiple.value && selectedValues.value.length > 0);\n const triggerText = computed(() => displayLabel.value || props.placeholder.value || '');\n\n function buildFlatList(opts: OptionItem[]): FlatRow[] {\n const flat: FlatRow[] = [];\n const groups = new Map<string | undefined, OptionItem[]>();\n\n for (const opt of opts) {\n const key = opt.group;\n\n let group = groups.get(key);\n\n if (!group) {\n group = [];\n groups.set(key, group);\n }\n\n group.push(opt);\n }\n\n let globalIdx = 0;\n\n for (const [groupLabel, groupOpts] of groups) {\n if (groupLabel !== undefined) flat.push({ label: groupLabel, type: 'group' });\n\n for (const opt of groupOpts) flat.push({ idx: globalIdx++, opt, type: 'option' });\n }\n\n return flat;\n }\n\n const flatRows = computed(() => buildFlatList(options.value));\n\n function emitChange(): void {\n setFieldValue(el, choice.formValue.value);\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n }\n\n function removeChip(event: Event): void {\n event.stopPropagation();\n\n const value = (event as CustomEvent<{ value?: string }>).detail?.value;\n\n if (value === undefined) return;\n\n choice.removeValue(value);\n emitChange();\n triggerValidation('change');\n }\n\n function openPopup(reason: OverlayOpenReason = 'programmatic') {\n optionList.open(reason);\n\n requestAnimationFrame(() => {\n const selectedIndex =\n selectedValues.value.length > 0\n ? options.value.findIndex((option) => option.value === selectedValues.value[0])\n : -1;\n\n if (selectedIndex >= 0) {\n optionList.set(selectedIndex);\n\n return;\n }\n\n optionList.navigate('first');\n });\n }\n\n // Selection\n\n function selectOption(opt: OptionItem, _e?: Event) {\n if (opt.disabled) return;\n\n if (props.multiple.value) {\n choice.toggleValue(opt.value);\n } else {\n choice.selectValue(opt.value);\n optionList.close();\n }\n\n emitChange();\n triggerValidation('change');\n }\n\n // Keyboard navigation\n\n function handleTriggerKeydown(e: KeyboardEvent) {\n if (isDisabled.value) return;\n\n // Let optionList handle arrow key navigation\n if (optionList.handleKeydown(e)) return;\n\n switch (e.key) {\n case ' ':\n case 'Enter':\n e.preventDefault();\n\n if (isOpen.value) {\n const idx = focusedIndex.value;\n const opts = options.value;\n\n if (idx >= 0 && idx < opts.length) selectOption(opts[idx], e);\n } else {\n openPopup('keyboard');\n }\n\n break;\n case 'Tab':\n optionList.close();\n break;\n }\n }\n\n // ore-input prop helpers\n const inputValue = () => triggerText.value;\n const inputLabel = () => props.label.value ?? '';\n const inputPlaceholder = () => props.placeholder.value ?? '';\n const inputLabelPlacement = () => props['label-placement'].value ?? 'inset';\n const inputColor = () => props.color?.value ?? undefined;\n const inputSize = () => props.size?.value ?? undefined;\n const inputVariant = () => props.variant?.value ?? undefined;\n const inputRounded = () => props.rounded?.value ?? undefined;\n const inputHelper = () => props.helper.value ?? '';\n const inputError = () => props.error.value ?? '';\n const inputDisabled = () => (isDisabled.value ? true : undefined);\n const inputRequired = () => (props.required.value ? true : undefined);\n const inputFullwidth = () => (props.fullwidth.value ? true : undefined);\n const tabIndexAttr = () => (isDisabled.value ? '-1' : '0');\n\n watch(slots.elements(), () => readOptions(), { immediate: true });\n\n const stopPopoverSync = effect(() => {\n if (isOpen.value) {\n if (dropdownEl && 'showPopover' in dropdownEl && !dropdownEl.matches(':popover-open')) dropdownEl.showPopover();\n } else {\n if (dropdownEl && 'hidePopover' in dropdownEl && dropdownEl.matches(':popover-open')) dropdownEl.hidePopover();\n }\n });\n\n onCleanup(() => stopPopoverSync.dispose());\n\n onMounted(() => {\n let onTriggerClick: ((event: MouseEvent) => void) | null = null;\n let onTriggerKeydown: ((event: KeyboardEvent) => void) | null = null;\n\n if (triggerEl) {\n onTriggerClick = (event: MouseEvent) => {\n event.stopPropagation();\n\n if (isDisabled.value) return;\n\n if (isOpen.value) optionList.close('trigger');\n else openPopup('click');\n };\n onTriggerKeydown = (event: KeyboardEvent) => {\n handleTriggerKeydown(event);\n };\n\n triggerEl.addEventListener('click', onTriggerClick);\n triggerEl.addEventListener('keydown', onTriggerKeydown);\n }\n\n return () => {\n if (triggerEl && onTriggerClick) triggerEl.removeEventListener('click', onTriggerClick);\n\n if (triggerEl && onTriggerKeydown) triggerEl.removeEventListener('keydown', onTriggerKeydown);\n };\n });\n\n return html`\n <slot style=\"display:none\"></slot>\n <ore-input\n class=\"trigger\"\n part=\"trigger\"\n readonly\n tabindex=\"${tabIndexAttr}\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-controls=\"${listboxId}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n aria-expanded=\"${() => String(isOpen.value)}\"\n aria-invalid=\"${() => (props.error.value ? 'true' : null)}\"\n value=\"${inputValue}\"\n label=\"${inputLabel}\"\n placeholder=\"${inputPlaceholder}\"\n label-placement=\"${inputLabelPlacement}\"\n color=\"${inputColor}\"\n size=\"${inputSize}\"\n variant=\"${inputVariant}\"\n rounded=\"${inputRounded}\"\n helper=\"${inputHelper}\"\n error=\"${inputError}\"\n ?disabled=\"${inputDisabled}\"\n ?required=\"${inputRequired}\"\n ?fullwidth=\"${inputFullwidth}\"\n ?success=\"${() => props.success.value}\"\n ref=\"${(el: HTMLElement) => {\n triggerEl = el;\n }}\">\n <div slot=\"prefix\" class=\"chips-row\" ?hidden=\"${() => !showChips.value}\">\n ${() =>\n selectedChipItems.value.map(\n (item) => html`\n <ore-chip\n value=\"${item.value}\"\n label=\"${item.label}\"\n mode=\"removable\"\n variant=\"flat\"\n size=\"sm\"\n color=\"${props.color}\"\n @remove=\"${removeChip}\">\n ${item.label}\n </ore-chip>\n `,\n )}\n </div>\n <span slot=\"suffix\" class=\"trigger-suffix\" aria-hidden=\"true\">\n <span class=\"loader\"></span>\n <span class=\"trigger-icon\">\n <ore-icon name=\"chevron-down\" size=\"14\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n </span>\n </span>\n </ore-input>\n <div\n class=\"dropdown\"\n part=\"dropdown\"\n popover=\"manual\"\n ?data-open=\"${isOpen}\"\n role=\"listbox\"\n id=\"${listboxId}\"\n aria-label=\"Options\"\n ref=\"${(el: HTMLElement) => {\n dropdownEl = el;\n }}\">\n <div class=\"dropdown-loading\" ?hidden=\"${() => !isLoading.value}\">Loading…</div>\n <div class=\"dropdown-empty\" ?hidden=\"${() => isLoading.value || options.value.length > 0}\">No options</div>\n <div class=\"options-list\" ?hidden=\"${() => isLoading.value || options.value.length === 0}\">\n ${() =>\n flatRows.value.map((row) =>\n row.type === 'group'\n ? html`\n <div class=\"optgroup-label\" role=\"presentation\">${row.label}</div>\n `\n : html`\n <div\n class=\"option\"\n role=\"option\"\n id=\"${`${selectId}-opt-${row.idx}`}\"\n data-option-index=\"${String(row.idx)}\"\n data-option-value=\"${row.opt.value}\"\n aria-selected=\"${() => String(selectedValues.value.includes(row.opt.value))}\"\n aria-disabled=\"${() => String(row.opt.disabled)}\"\n ?data-focused=\"${() => focusedIndex.value === row.idx}\"\n ?data-selected=\"${() => selectedValues.value.includes(row.opt.value)}\"\n ?data-disabled=\"${() => row.opt.disabled}\"\n @click=\"${(e: MouseEvent) => {\n e.stopPropagation();\n selectOption(row.opt, e);\n }}\"\n @pointerenter=\"${() => {\n optionList.set(row.idx);\n }}\">\n <span>${row.opt.label}</span>\n <span class=\"option-check\" aria-hidden=\"true\">\n <ore-icon name=\"check\" size=\"14\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </span>\n </div>\n `,\n )}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, reducedMotionMixin, roundedVariantMixin, componentStyles],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"select.js","names":[],"sources":["../src/inputs/select/select.ts"],"sourcesContent":["import { bind, define, getHost, html, onCleanup, onMounted, prop, useEmit, useField, useSlots } from '@vielzeug/ore';\nimport { computed, effect, signal, watch } from '@vielzeug/ripple';\n\nimport type { OverlayOpenChangeDetail, OverlayOpenReason } from '../../core';\nimport { createChoiceField, createListboxDropdown, lifecycleSignal } from '../../core';\nimport type { SelectableFieldProps } from '../../shared';\nimport type { VisualVariant } from '../../types';\nimport '../../feedback/chip/chip';\nimport '../../content/icon/icon';\nimport '../input/input';\nimport { disablableBundle, loadableBundle, roundableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, reducedMotionMixin, roundedVariantMixin } from '../../styles';\nimport { defineFieldValue, dispatchNativeFieldEvent, setFieldValue } from '../shared/native-field-event';\nimport componentStyles from './select.css?inline';\n\n// ── Types ─────────────────────────────────────────────────────────────\n\ntype OptionItem = {\n disabled: boolean;\n group?: string;\n label: string;\n value: string;\n};\n\nexport type OreSelectOptionInput = {\n disabled?: boolean;\n group?: string;\n label?: string;\n value: string;\n};\n\ntype FlatRow =\n | {\n idx: number;\n opt: OptionItem;\n type: 'option';\n }\n | {\n label: string;\n type: 'group';\n };\n\n// ── Styles ─────────────────────────────────────────────────────────────\n\n// ── Component Props ─────────────────────────────────────────────────────────────\n\n/** Select component properties */\n\nexport type OreSelectEvents = {\n change: Event;\n input: Event;\n 'open-change': OverlayOpenChangeDetail;\n};\n\nexport type OreSelectProps = SelectableFieldProps<Exclude<VisualVariant, 'frost'>> & {\n /** Show loading state in dropdown */\n loading?: boolean;\n /** Allow selecting multiple options */\n multiple?: boolean;\n /** JS options array (alternative to slotted <option> elements) */\n options?: OreSelectOptionInput[];\n /** Mark the field as required */\n required?: boolean;\n};\n\n/**\n * A fully custom form-associated select dropdown with keyboard navigation and ARIA support.\n * Reads `<option>` and `<optgroup>` children from the default slot.\n *\n * @element ore-select\n *\n * @attr {string} label - Label text\n * @attr {string} label-placement - 'inset' | 'outside'\n * @attr {string} value - Current selected value(s) (comma-separated for multiple)\n * @attr {string} placeholder - Placeholder when no option selected\n * @attr {string} name - Form field name\n * @attr {boolean} multiple - Enable multi-select\n * @attr {boolean} disabled - Disable the select\n * @attr {boolean} required - Required field\n * @attr {string} helper - Helper text below the select\n * @attr {string} error - Error message\n * @attr {boolean} success - Show an inline success check icon (suppressed while `error` is set)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {boolean} fullwidth - Expand to full width\n *\n * @fires input - Fired when selection changes.\n * @fires change - Fired when selection changes.\n * @fires open-change - Fired when the dropdown state changes. detail: { open, reason }\n *\n * @slot - `<option>` and `<optgroup>` elements\n *\n * @cssprop --select-bg - Background\n * @cssprop --select-border-color - Border color\n * @cssprop --select-radius - Border radius\n * @cssprop --select-padding - Padding\n * @cssprop --select-font-size - Font size\n * @cssprop --select-placeholder-color - Placeholder text color\n *\n * @part trigger - The `ore-input` trigger element that opens the dropdown.\n * @part dropdown - The dropdown listbox panel container.\n *\n * @example\n * ```html\n * <ore-select label=\"Role\" value=\"admin\">\n * <option value=\"admin\">Administrator</option>\n * <option value=\"editor\">Editor</option>\n * <option value=\"viewer\">Viewer</option>\n * </ore-select>\n *\n * <ore-select label=\"Tags\" multiple color=\"primary\">\n * <optgroup label=\"Frontend\">\n * <option value=\"react\">React</option>\n * <option value=\"vue\">Vue</option>\n * </optgroup>\n * <optgroup label=\"Backend\">\n * <option value=\"node\">Node.js</option>\n * </optgroup>\n * </ore-select>\n * ```\n */\nexport const SELECT_TAG = 'ore-select' as const;\ndefine<OreSelectProps>(SELECT_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n ...loadableBundle,\n ...roundableBundle,\n error: prop.string(),\n fullwidth: prop.bool(false),\n helper: prop.string(),\n label: prop.string(),\n 'label-placement': prop.oneOf(['inset', 'outside'] as const, 'inset'),\n multiple: prop.bool(false),\n name: prop.string(),\n options: prop.json(undefined as OreSelectOptionInput[] | undefined),\n placeholder: prop.string(),\n required: prop.bool(false),\n success: prop.bool(false),\n value: prop.string(),\n variant: prop.string<Exclude<VisualVariant, 'frost'>>(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreSelectEvents>();\n const slots = useSlots();\n\n const shadowRoot = el.shadowRoot;\n // ────────────────────────────────────────────────────────────────\n // State & Context\n // ────────────────────────────────────────────────────────────────\n\n const slottedOptions = signal<OptionItem[]>([]);\n const isLoading = computed(() => Boolean(props.loading.value));\n\n // Merged options: explicit prop value overrides slotted options\n function normalizeOption(option: OreSelectOptionInput): OptionItem {\n return {\n disabled: Boolean(option.disabled),\n group: option.group,\n label: option.label ?? option.value,\n value: option.value,\n };\n }\n\n const options = computed(() => {\n const explicitOptions = props.options.value;\n\n return Array.isArray(explicitOptions) ? explicitOptions.map(normalizeOption) : slottedOptions.value;\n });\n let triggerEl: HTMLElement | null = null;\n let dropdownEl: HTMLElement | null = null;\n\n const abortSignal = lifecycleSignal(onCleanup);\n const choice = createChoiceField({\n disabled: props.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n labelPlacement: props['label-placement'],\n multiple: props.multiple,\n prefix: 'select',\n required: props.required,\n signal: abortSignal,\n value: props.value,\n });\n\n choice.attachFormField(\n useField<string>({\n disabled: choice.disabled,\n onReset: choice.reset,\n toFormValue: (v) => v,\n validationMessage: choice.validationMessage,\n validity: choice.validity,\n value: choice.formValue,\n }),\n );\n\n const optionList = createListboxDropdown<OptionItem>({\n getBoundary: () => el,\n getFocusedOptionElement: () => dropdownEl?.querySelector<HTMLElement>('[data-focused]') ?? null,\n getItems: () => options.value,\n getPanel: () => dropdownEl,\n getReference: () => triggerEl,\n getTrigger: () => triggerEl,\n isDisabled: () => choice.disabled.value,\n onClose: (reason) => {\n emit('open-change', { open: false, reason });\n choice.triggerValidation('blur');\n },\n onOpen: (reason) => emit('open-change', { open: true, reason }),\n signal: abortSignal,\n });\n\n const { triggerValidation } = choice;\n const selectedValues = choice.selectedValues;\n const isDisabled = choice.disabled;\n\n defineFieldValue(\n el,\n () => choice.formValue.value,\n (value) => {\n choice.setValues(value ? value.split(',') : []);\n },\n );\n\n const { fieldId: selectId } = choice;\n const listboxId = `listbox-${selectId}`;\n const { focusedIndex, isOpen } = optionList;\n\n // Sync host attributes for CSS hooks\n bind({\n attr: {\n 'has-error': () => (props.error.value ? true : undefined),\n open: () => (isOpen.value ? true : undefined),\n size: props.size,\n variant: props.variant,\n },\n });\n\n // ────────────────────────────────────────────────────────────────\n // Option Reading from Slot\n // ────────────────────────────────────────────────────────────────\n\n function readOptions() {\n const slot = shadowRoot?.querySelector<HTMLSlotElement>('slot');\n\n if (!slot) return;\n\n const assigned = slot.assignedElements({ flatten: true });\n const items: OptionItem[] = [];\n\n for (const el of assigned) {\n if (el.tagName === 'OPTION') {\n const opt = el as HTMLOptionElement;\n\n items.push({ disabled: opt.disabled, label: opt.text || opt.value, value: opt.value });\n } else if (el.tagName === 'OPTGROUP') {\n const group = el as HTMLOptGroupElement;\n const groupLabel = group.label;\n\n for (const child of Array.from(group.querySelectorAll('option'))) {\n const opt = child as HTMLOptionElement;\n\n items.push({ disabled: opt.disabled, group: groupLabel, label: opt.text || opt.value, value: opt.value });\n }\n }\n }\n\n slottedOptions.value = items;\n }\n\n const displayLabel = computed(() => {\n if (selectedValues.value.length === 0) return '';\n\n if (props.multiple.value && selectedValues.value.length > 1) {\n return `${selectedValues.value.length} selected`;\n }\n\n const first = selectedValues.value[0];\n\n return options.value.find((o) => o.value === first)?.label ?? first;\n });\n const selectedChipItems = computed(() => {\n if (!props.multiple.value) return [];\n\n return selectedValues.value.map((value) => ({\n label: options.value.find((o) => o.value === value)?.label ?? value,\n value,\n }));\n });\n const showChips = computed(() => props.multiple.value && selectedValues.value.length > 0);\n const triggerText = computed(() => displayLabel.value || props.placeholder.value || '');\n\n function buildFlatList(opts: OptionItem[]): FlatRow[] {\n const flat: FlatRow[] = [];\n const groups = new Map<string | undefined, OptionItem[]>();\n\n for (const opt of opts) {\n const key = opt.group;\n\n let group = groups.get(key);\n\n if (!group) {\n group = [];\n groups.set(key, group);\n }\n\n group.push(opt);\n }\n\n let globalIdx = 0;\n\n for (const [groupLabel, groupOpts] of groups) {\n if (groupLabel !== undefined) flat.push({ label: groupLabel, type: 'group' });\n\n for (const opt of groupOpts) flat.push({ idx: globalIdx++, opt, type: 'option' });\n }\n\n return flat;\n }\n\n const flatRows = computed(() => buildFlatList(options.value));\n\n function emitChange(): void {\n setFieldValue(el, choice.formValue.value);\n dispatchNativeFieldEvent(el, 'input');\n dispatchNativeFieldEvent(el, 'change');\n }\n\n function removeChip(event: Event): void {\n event.stopPropagation();\n\n const value = (event as CustomEvent<{ value?: string }>).detail?.value;\n\n if (value === undefined) return;\n\n choice.removeValue(value);\n emitChange();\n triggerValidation('change');\n }\n\n function openPopup(reason: OverlayOpenReason = 'programmatic') {\n optionList.open(reason);\n\n requestAnimationFrame(() => {\n const selectedIndex =\n selectedValues.value.length > 0\n ? options.value.findIndex((option) => option.value === selectedValues.value[0])\n : -1;\n\n if (selectedIndex >= 0) {\n optionList.set(selectedIndex);\n\n return;\n }\n\n optionList.navigate('first');\n });\n }\n\n // Selection\n\n function selectOption(opt: OptionItem, _e?: Event) {\n if (opt.disabled) return;\n\n if (props.multiple.value) {\n choice.toggleValue(opt.value);\n } else {\n choice.selectValue(opt.value);\n optionList.close();\n }\n\n emitChange();\n triggerValidation('change');\n }\n\n // Keyboard navigation\n\n function handleTriggerKeydown(e: KeyboardEvent) {\n if (isDisabled.value) return;\n\n // Let optionList handle arrow key navigation\n if (optionList.handleKeydown(e)) return;\n\n switch (e.key) {\n case ' ':\n case 'Enter':\n e.preventDefault();\n\n if (isOpen.value) {\n const idx = focusedIndex.value;\n const opts = options.value;\n\n if (idx >= 0 && idx < opts.length) selectOption(opts[idx], e);\n } else {\n openPopup('keyboard');\n }\n\n break;\n case 'Tab':\n optionList.close();\n break;\n }\n }\n\n // ore-input prop helpers\n const inputValue = () => triggerText.value;\n const inputLabel = () => props.label.value ?? '';\n const inputPlaceholder = () => props.placeholder.value ?? '';\n const inputLabelPlacement = () => props['label-placement'].value ?? 'inset';\n const inputColor = () => props.color?.value ?? undefined;\n const inputSize = () => props.size?.value ?? undefined;\n const inputVariant = () => props.variant?.value ?? undefined;\n const inputRounded = () => props.rounded?.value ?? undefined;\n const inputHelper = () => props.helper.value ?? '';\n const inputError = () => props.error.value ?? '';\n const inputDisabled = () => (isDisabled.value ? true : undefined);\n const inputRequired = () => (props.required.value ? true : undefined);\n const inputFullwidth = () => (props.fullwidth.value ? true : undefined);\n const tabIndexAttr = () => (isDisabled.value ? '-1' : '0');\n\n watch(slots.elements(), () => readOptions(), { immediate: true });\n\n const stopPopoverSync = effect(() => {\n if (isOpen.value) {\n if (dropdownEl && 'showPopover' in dropdownEl && !dropdownEl.matches(':popover-open')) dropdownEl.showPopover();\n } else {\n if (dropdownEl && 'hidePopover' in dropdownEl && dropdownEl.matches(':popover-open')) dropdownEl.hidePopover();\n }\n });\n\n onCleanup(() => stopPopoverSync.dispose());\n\n onMounted(() => {\n let onTriggerClick: ((event: MouseEvent) => void) | null = null;\n let onTriggerKeydown: ((event: KeyboardEvent) => void) | null = null;\n\n if (triggerEl) {\n onTriggerClick = (event: MouseEvent) => {\n event.stopPropagation();\n\n if (isDisabled.value) return;\n\n if (isOpen.value) optionList.close('trigger');\n else openPopup('click');\n };\n onTriggerKeydown = (event: KeyboardEvent) => {\n handleTriggerKeydown(event);\n };\n\n triggerEl.addEventListener('click', onTriggerClick);\n triggerEl.addEventListener('keydown', onTriggerKeydown);\n }\n\n return () => {\n if (triggerEl && onTriggerClick) triggerEl.removeEventListener('click', onTriggerClick);\n\n if (triggerEl && onTriggerKeydown) triggerEl.removeEventListener('keydown', onTriggerKeydown);\n };\n });\n\n return html`\n <slot style=\"display:none\"></slot>\n <ore-input\n class=\"trigger\"\n part=\"trigger\"\n readonly\n tabindex=\"${tabIndexAttr}\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-controls=\"${listboxId}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n aria-expanded=\"${() => String(isOpen.value)}\"\n aria-invalid=\"${() => (props.error.value ? 'true' : null)}\"\n value=\"${inputValue}\"\n label=\"${inputLabel}\"\n placeholder=\"${inputPlaceholder}\"\n label-placement=\"${inputLabelPlacement}\"\n color=\"${inputColor}\"\n size=\"${inputSize}\"\n variant=\"${inputVariant}\"\n rounded=\"${inputRounded}\"\n helper=\"${inputHelper}\"\n error=\"${inputError}\"\n ?disabled=\"${inputDisabled}\"\n ?required=\"${inputRequired}\"\n ?fullwidth=\"${inputFullwidth}\"\n ?success=\"${() => props.success.value}\"\n ref=\"${(el: HTMLElement) => {\n triggerEl = el;\n }}\">\n <div slot=\"prefix\" class=\"chips-row\" ?hidden=\"${() => !showChips.value}\">\n ${() =>\n selectedChipItems.value.map(\n (item) => html`\n <ore-chip\n value=\"${item.value}\"\n label=\"${item.label}\"\n mode=\"removable\"\n variant=\"flat\"\n size=\"sm\"\n color=\"${props.color}\"\n @remove=\"${removeChip}\">\n ${item.label}\n </ore-chip>\n `,\n )}\n </div>\n <span slot=\"suffix\" class=\"trigger-suffix\" aria-hidden=\"true\">\n <span class=\"loader\"></span>\n <span class=\"trigger-icon\">\n <ore-icon name=\"chevron-down\" size=\"14\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n </span>\n </span>\n </ore-input>\n <div\n class=\"dropdown\"\n part=\"dropdown\"\n popover=\"manual\"\n ?data-open=\"${isOpen}\"\n role=\"listbox\"\n id=\"${listboxId}\"\n aria-label=\"Options\"\n ref=\"${(el: HTMLElement) => {\n dropdownEl = el;\n }}\">\n <div class=\"dropdown-loading\" ?hidden=\"${() => !isLoading.value}\">Loading…</div>\n <div class=\"dropdown-empty\" ?hidden=\"${() => isLoading.value || options.value.length > 0}\">No options</div>\n <div class=\"options-list\" ?hidden=\"${() => isLoading.value || options.value.length === 0}\">\n ${() =>\n flatRows.value.map((row) =>\n row.type === 'group'\n ? html`\n <div class=\"optgroup-label\" role=\"presentation\">${row.label}</div>\n `\n : html`\n <div\n class=\"option\"\n role=\"option\"\n id=\"${`${selectId}-opt-${row.idx}`}\"\n data-option-index=\"${String(row.idx)}\"\n data-option-value=\"${row.opt.value}\"\n aria-selected=\"${() => String(selectedValues.value.includes(row.opt.value))}\"\n aria-disabled=\"${() => String(row.opt.disabled)}\"\n ?data-focused=\"${() => focusedIndex.value === row.idx}\"\n ?data-selected=\"${() => selectedValues.value.includes(row.opt.value)}\"\n ?data-disabled=\"${() => row.opt.disabled}\"\n @click=\"${(e: MouseEvent) => {\n e.stopPropagation();\n selectOption(row.opt, e);\n }}\"\n @pointerenter=\"${() => {\n optionList.set(row.idx);\n }}\">\n <span>${row.opt.label}</span>\n <span class=\"option-check\" aria-hidden=\"true\">\n <ore-icon name=\"check\" size=\"14\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </span>\n </div>\n `,\n )}\n </div>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, reducedMotionMixin, roundedVariantMixin, componentStyles],\n});\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ @layer refine.reset {
2
+ /*
3
+ * Suppress flash of unstyled content (FOUC) for custom elements.
4
+ *
5
+ * :not(:defined) only matches custom elements that have not yet been
6
+ * upgraded — standard HTML elements are always :defined, so this rule is
7
+ * safe globally. Once customElements.define() runs and the element
8
+ * upgrades, :defined matches and this rule no longer applies.
9
+ *
10
+ * Import this file in your application's CSS bundle (not via JS injection)
11
+ * so the rule is available at first paint:
12
+ *
13
+ * import '@vielzeug/refine/fouc.css';
14
+ *
15
+ * `visibility: hidden` is used instead of `display: none` to preserve
16
+ * layout space — the element occupies its inline size while invisible,
17
+ * reducing layout shift when the shadow DOM attaches.
18
+ */
19
+ :not(:defined) {
20
+ visibility: hidden;
21
+ }
22
+ }
@@ -1,14 +1,6 @@
1
- @layer refine.reset {
2
- /*
3
- * Suppress flash of unstyled content (FOUC) for custom elements.
4
- * :not(:defined) only matches custom elements that have not yet been upgraded —
5
- * standard HTML elements are always :defined, so this rule is safe globally.
6
- * The rule is removed automatically once customElements.define() runs.
7
- */
8
- :not(:defined) {
9
- visibility: hidden;
10
- }
1
+ @import url('./fouc.css');
11
2
 
3
+ @layer refine.reset {
12
4
  *,
13
5
  ::after,
14
6
  ::before,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/refine",
3
- "version": "2.1.3",
3
+ "version": "2.2.3",
4
4
  "description": "Accessible, themeable web components built on Ore custom element primitives",
5
5
  "keywords": [
6
6
  "web-components",
@@ -15,9 +15,9 @@
15
15
  ],
16
16
  "homepage": "https://vielzeug.dev/refine/",
17
17
  "repository": {
18
- "directory": "packages/refine",
19
18
  "type": "git",
20
- "url": "https://github.com/helmuthdu/vielzeug"
19
+ "url": "https://github.com/helmuthdu/vielzeug",
20
+ "directory": "packages/refine"
21
21
  },
22
22
  "bugs": {
23
23
  "url": "https://github.com/helmuthdu/vielzeug/issues"
@@ -323,6 +323,10 @@
323
323
  "default": "./dist/styles/layer.css",
324
324
  "import": "./dist/styles/layer.css"
325
325
  },
326
+ "./fouc.css": {
327
+ "default": "./dist/styles/fouc.css",
328
+ "import": "./dist/styles/fouc.css"
329
+ },
326
330
  "./styles/preflight.css": {
327
331
  "default": "./dist/styles/preflight.css",
328
332
  "import": "./dist/styles/preflight.css"
@@ -624,14 +628,14 @@
624
628
  "test:e2e": "playwright test"
625
629
  },
626
630
  "dependencies": {
627
- "@vielzeug/arsenal": "2.0.1",
628
- "@vielzeug/assay": "2.0.1",
631
+ "@vielzeug/arsenal": "2.1.0",
632
+ "@vielzeug/assay": "2.1.0",
629
633
  "@vielzeug/dnd": "2.0.0",
630
- "@vielzeug/keymap": "2.0.0",
631
- "@vielzeug/orbit": "2.0.2",
632
- "@vielzeug/ore": "2.0.6",
633
- "@vielzeug/ripple": "2.2.0",
634
- "@vielzeug/tempo": "2.0.0",
634
+ "@vielzeug/keymap": "2.1.0",
635
+ "@vielzeug/orbit": "2.1.1",
636
+ "@vielzeug/ore": "2.0.10",
637
+ "@vielzeug/ripple": "2.2.2",
638
+ "@vielzeug/tempo": "2.1.0",
635
639
  "lucide": "^1.31.0"
636
640
  },
637
641
  "devDependencies": {