luxen-ui 0.19.0 → 0.20.0
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.
- package/README.md +1 -0
- package/cdn/custom-elements.json +99 -5
- package/cdn/elements/button-group/button-group.d.ts +12 -7
- package/cdn/elements/button-group/button-group.d.ts.map +1 -1
- package/cdn/elements/button-group/button-group.js.map +1 -1
- package/cdn/elements/input-stepper/input-stepper.d.ts +1 -1
- package/cdn/elements/input-stepper/input-stepper.js.map +1 -1
- package/cdn/elements/popover/popover.d.ts +15 -0
- package/cdn/elements/popover/popover.d.ts.map +1 -1
- package/cdn/elements/popover/popover.js +1 -1
- package/cdn/elements/popover/popover.js.map +1 -1
- package/cdn/elements/radio-group/radio-group.meta.d.ts +41 -0
- package/cdn/elements/radio-group/radio-group.meta.d.ts.map +1 -0
- package/cdn/elements/radio-group/radio-group.meta.js +0 -0
- package/cdn/elements/segmented-control/segmented-control.d.ts +8 -4
- package/cdn/elements/segmented-control/segmented-control.d.ts.map +1 -1
- package/cdn/elements/segmented-control/segmented-control.js.map +1 -1
- package/cdn/elements/select/select.js +1 -1
- package/cdn/elements/select/select.js.map +1 -1
- package/cdn/elements/tag/tag.d.ts +3 -1
- package/cdn/elements/tag/tag.d.ts.map +1 -1
- package/cdn/elements/tag/tag.js +1 -1
- package/cdn/elements/tag/tag.js.map +1 -1
- package/cdn/elements/tooltip/tooltip.d.ts +15 -0
- package/cdn/elements/tooltip/tooltip.d.ts.map +1 -1
- package/cdn/elements/tooltip/tooltip.js +1 -1
- package/cdn/elements/tooltip/tooltip.js.map +1 -1
- package/cdn/shared/controllers/popover.d.ts +40 -0
- package/cdn/shared/controllers/popover.d.ts.map +1 -1
- package/cdn/shared/controllers/popover.js +1 -1
- package/cdn/shared/controllers/popover.js.map +1 -1
- package/cdn/shared/styles/button-core.styles.js +1 -1
- package/cdn/shared/styles/button-core.styles.js.map +1 -1
- package/cdn/standalone.css +774 -28
- package/cdn/styles/elements/button-group.css +4 -3
- package/cdn/styles/elements/button.css +28 -1
- package/cdn/styles/elements/disclosure.css +3 -3
- package/cdn/styles/elements/input-stepper/default.css +29 -2
- package/cdn/styles/elements/input-stepper/pill.css +420 -0
- package/cdn/styles/elements/input-stepper/rounded.css +69 -8
- package/cdn/styles/elements/kbd.css +2 -2
- package/cdn/styles/elements/radio-group.css +206 -0
- package/cdn/styles/elements/segmented-control.css +3 -3
- package/cdn/styles/elements/tabs/enclosed.css +4 -4
- package/cdn/styles/elements/tabs/line.css +2 -2
- package/dist/css/elements/button-group.css +4 -3
- package/dist/css/elements/button.css +28 -1
- package/dist/css/elements/disclosure.css +3 -3
- package/dist/css/elements/input-stepper/default.css +29 -2
- package/dist/css/elements/input-stepper/pill.css +420 -0
- package/dist/css/elements/input-stepper/rounded.css +69 -8
- package/dist/css/elements/kbd.css +2 -2
- package/dist/css/elements/radio-group.css +206 -0
- package/dist/css/elements/segmented-control.css +3 -3
- package/dist/css/elements/tabs/enclosed.css +4 -4
- package/dist/css/elements/tabs/line.css +2 -2
- package/dist/custom-elements.json +99 -5
- package/dist/elements/button-group/button-group.d.ts +12 -7
- package/dist/elements/button-group/button-group.d.ts.map +1 -1
- package/dist/elements/button-group/button-group.js +12 -7
- package/dist/elements/input-stepper/input-stepper.d.ts +1 -1
- package/dist/elements/input-stepper/input-stepper.js +1 -1
- package/dist/elements/popover/popover.d.ts +15 -0
- package/dist/elements/popover/popover.d.ts.map +1 -1
- package/dist/elements/popover/popover.js +61 -14
- package/dist/elements/radio-group/radio-group.meta.d.ts +41 -0
- package/dist/elements/radio-group/radio-group.meta.d.ts.map +1 -0
- package/dist/elements/radio-group/radio-group.meta.js +51 -0
- package/dist/elements/segmented-control/segmented-control.d.ts +8 -4
- package/dist/elements/segmented-control/segmented-control.d.ts.map +1 -1
- package/dist/elements/segmented-control/segmented-control.js +8 -4
- package/dist/elements/select/select.css +14 -0
- package/dist/elements/tag/tag.css +128 -49
- package/dist/elements/tag/tag.d.ts +3 -1
- package/dist/elements/tag/tag.d.ts.map +1 -1
- package/dist/elements/tag/tag.js +3 -1
- package/dist/elements/tooltip/tooltip.d.ts +15 -0
- package/dist/elements/tooltip/tooltip.d.ts.map +1 -1
- package/dist/elements/tooltip/tooltip.js +61 -14
- package/dist/metadata/button-group.json +2 -2
- package/dist/metadata/index.json +95 -7
- package/dist/metadata/input-stepper.json +3 -2
- package/dist/metadata/popover.json +6 -0
- package/dist/metadata/radio-group.json +65 -0
- package/dist/metadata/segmented-control.json +1 -1
- package/dist/metadata/tag.json +11 -1
- package/dist/metadata/tooltip.json +6 -0
- package/dist/shared/controllers/popover.d.ts +40 -0
- package/dist/shared/controllers/popover.d.ts.map +1 -1
- package/dist/shared/controllers/popover.js +69 -0
- package/dist/shared/styles/button-core.css +28 -1
- package/dist/templates/elements/button-group.md +159 -41
- package/dist/templates/elements/button.md +1 -1
- package/dist/templates/elements/input-stepper.md +220 -58
- package/dist/templates/elements/popover.md +3 -0
- package/dist/templates/elements/radio-group.md +351 -0
- package/dist/templates/elements/radio.md +34 -30
- package/dist/templates/elements/segmented-control.md +5 -63
- package/dist/templates/elements/tag.md +72 -3
- package/dist/templates/elements/tooltip.md +3 -0
- package/elements.json +10 -1
- package/package.json +1 -1
- package/vite-plugin.ts +44 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"segmented-control.js","names":[],"sources":["../../../src/html/elements/segmented-control/segmented-control.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenFormAssociatedElement } from '../../shared/luxen-form-associated-element.js';\n\nexport type SegmentedControlSize = 'sm' | 'md' | 'lg' | 'xl';\n\n/** Fired when the selected segment changes. Bubbles; not composed. */\nexport class SegmentChangeEvent extends Event {\n readonly value: string;\n readonly index: number;\n constructor(value: string, index: number) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n this.index = index;\n }\n}\n\ninterface SegmentedControlEventMap {\n change: SegmentChangeEvent;\n}\n\n/**\n * @summary A single-select segmented control: a compact, mutually-exclusive\n * switch between a few options (radio-group semantics with a sliding pill).\n * Progressively enhances light-DOM `<button>`s so it works before JS runs.\n * Form-associated: give it a `name` and its selected `value` is submitted with\n * the form (and restored on reset), like a native radio group.\n *\n * @example\n * Mark the initially-selected segment with `aria-checked=\"true\"` (or set\n * `value` to its `value` attribute) so it is styled before the script upgrades\n * the element.\n * ```html\n * <l-segmented-control label=\"Metric\" value=\"cost\">\n * <button value=\"volume\">Volume</button>\n * <button value=\"cost\" aria-checked=\"true\">Cost</button>\n * </l-segmented-control>\n * ```\n *\n * @event change - Fired when the selected segment changes. Not cancelable. Bubbles. Properties: `value: string`, `index: number`.\n *\n * @cssproperty [--indicator-color=var(--l-color-surface)] - Background of the sliding pill behind the selected segment.\n * @cssproperty [--border-radius=var(--l-radius-lg)] - Corner radius of the track.\n *\n * @customElement l-segmented-control\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class SegmentedControl extends LuxenFormAssociatedElement {\n override createRenderRoot() {\n return this;\n }\n\n private _initialized = false;\n private _setupTimer = 0;\n private _buttons: HTMLButtonElement[] = [];\n private _resizeObserver: ResizeObserver | null = null;\n /** Segments the consumer authored `aria-disabled` on — never ours to clear. */\n private _authoredDisabled = new WeakSet<HTMLButtonElement>();\n\n /** Value of the selected segment (its `value` attribute, else its index). */\n @property({ reflect: true })\n value = '';\n\n /** Control height, aligned with buttons and form controls at the same size. */\n @property({ reflect: true })\n size: SegmentedControlSize = 'md';\n\n /** Accessible label announced for the group. Not displayed on screen. */\n @property({ reflect: true })\n label?: string;\n\n /** Stretch segments to fill the container width. */\n @property({ type: Boolean, reflect: true, attribute: 'full-width' })\n fullWidth = false;\n\n // --- Lifecycle ---\n\n override connectedCallback() {\n super.connectedCallback();\n // Children may not be parsed yet when the element upgrades mid-parse: try\n // synchronously, then retry once on a macrotask. setTimeout, not rAF — rAF\n // is suspended in hidden documents, so the element would stay inert there.\n if (!this._trySetup()) {\n this._setupTimer = window.setTimeout(() => this._trySetup(), 0);\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n clearTimeout(this._setupTimer);\n this._teardown();\n }\n\n /** @returns true when setup ran or was already done; false to schedule a retry. */\n private _trySetup(): boolean {\n if (this._initialized || !this.isConnected) return true;\n if (this.querySelectorAll('button').length < 2) return false;\n this._setup();\n return true;\n }\n\n override updated(changed: Map<string, unknown>) {\n if (!this._initialized) return;\n if (changed.has('value')) {\n this._select(this._resolveActiveIndex(), false);\n }\n if (changed.has('size')) {\n this._updateIndicator();\n }\n if (changed.has('label')) {\n if (this.label) {\n this.setAttribute('aria-label', this.label);\n } else {\n this.removeAttribute('aria-label');\n }\n }\n if (changed.has('disabled')) {\n this._applyDisabledState();\n }\n }\n\n // --- Form lifecycle ---\n\n override formResetCallback() {\n // Restoring `value` re-selects the default segment via `updated()`.\n this.value = this._defaultFormValue;\n super.formResetCallback();\n }\n\n override formStateRestoreCallback(state: string, mode: 'restore' | 'autocomplete') {\n this.value = state;\n super.formStateRestoreCallback(state, mode);\n }\n\n // --- Setup / Teardown ---\n\n private _setup() {\n this._buttons = Array.from(this.querySelectorAll('button'));\n if (this._buttons.length < 2) return;\n this._initialized = true;\n\n this.setAttribute('role', 'radiogroup');\n if (this.label) this.setAttribute('aria-label', this.label);\n\n const activeIndex = this._resolveActiveIndex();\n\n for (let i = 0; i < this._buttons.length; i++) {\n const btn = this._buttons[i];\n // Force type=button so a segment never submits the enclosing form.\n btn.type = 'button';\n btn.setAttribute('role', 'radio');\n btn.setAttribute('aria-checked', String(i === activeIndex));\n btn.setAttribute('tabindex', i === activeIndex ? '0' : '-1');\n\n // Record an authored `aria-disabled` before the host-level state is\n // mirrored onto the segments: `_applyDisabledState()` clears that mirror\n // whenever the control is enabled, and must not take this one with it.\n if (btn.getAttribute('aria-disabled') === 'true') this._authoredDisabled.add(btn);\n\n // A segment with no visible text is icon-only: square it and require an\n // accessible name (the icon itself is decorative to assistive tech).\n if ((btn.textContent ?? '').trim() === '') {\n btn.setAttribute('data-icon-only', '');\n if (!btn.hasAttribute('aria-label') && !btn.hasAttribute('aria-labelledby')) {\n // eslint-disable-next-line no-console\n console.warn(\n `<${this.localName}>: icon-only segment #${i} has no accessible name; add aria-label.`,\n );\n }\n }\n }\n\n this.addEventListener('click', this._onClick);\n this.addEventListener('keydown', this._onKeyDown);\n\n // Reflect a whole-control `disabled` onto the segments (aria + tab order).\n this._applyDisabledState();\n\n // Canonicalize `value` to the active segment now that roles are set, and\n // seed the form value (the value a form reset restores to).\n this._syncValueFromIndex(activeIndex);\n this._defaultFormValue = this.value;\n this._syncFormValue(this.value);\n\n // Initial pill position. Synchronous first (offset reads force layout),\n // then a ResizeObserver keeps it correct across hidden→visible transitions\n // and size changes — rAF is suspended in hidden documents, so it must not\n // be the only scheduling mechanism.\n this._updateIndicator();\n this._resizeObserver = new ResizeObserver(() => this._updateIndicator());\n this._resizeObserver.observe(this);\n }\n\n private _teardown() {\n this.removeEventListener('click', this._onClick);\n this.removeEventListener('keydown', this._onKeyDown);\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n this._initialized = false;\n }\n\n // --- Selection ---\n\n /** Resolve the selected index from `value`, else an authored `aria-checked`, else 0. */\n private _resolveActiveIndex(): number {\n const { _buttons } = this;\n if (this.value !== '') {\n const byValue = _buttons.findIndex((b) => (b.getAttribute('value') ?? '') === this.value);\n if (byValue >= 0) return byValue;\n const asIndex = Number(this.value);\n if (Number.isInteger(asIndex) && asIndex >= 0 && asIndex < _buttons.length) return asIndex;\n }\n const checked = _buttons.findIndex((b) => b.getAttribute('aria-checked') === 'true');\n if (checked >= 0) return checked;\n // No explicit selection: default to the first enabled segment (else 0).\n const firstEnabled = _buttons.findIndex((b) => !this._isDisabled(b));\n return firstEnabled >= 0 ? firstEnabled : 0;\n }\n\n private _syncValueFromIndex(index: number) {\n this.value = this._buttons[index]?.getAttribute('value') ?? String(index);\n }\n\n private _select(index: number, emitEvent = true) {\n if (index < 0 || index >= this._buttons.length) return;\n // Whether this is an actual change — re-selecting the current segment must\n // not re-fire `change` (matches native radio / `<select>`).\n const changed = this._buttons[index].getAttribute('aria-checked') !== 'true';\n\n for (let i = 0; i < this._buttons.length; i++) {\n const isActive = i === index;\n this._buttons[i].setAttribute('aria-checked', String(isActive));\n // Disabled control keeps every segment out of the tab order.\n this._buttons[i].setAttribute('tabindex', isActive && !this.disabled ? '0' : '-1');\n }\n\n this._syncValueFromIndex(index);\n this._syncFormValue(this.value);\n this._updateIndicator();\n\n if (emitEvent && changed) {\n this.hasInteracted = true;\n this.dispatchEvent(new SegmentChangeEvent(this.value, index));\n }\n }\n\n /** Index of the next non-disabled segment from `from`, moving by `dir`, wrapping. */\n private _nextEnabled(from: number, dir: 1 | -1): number {\n const len = this._buttons.length;\n for (let step = 1; step <= len; step++) {\n const i = (from + dir * step + len * step) % len;\n if (!this._isDisabled(this._buttons[i])) return i;\n }\n return from;\n }\n\n private _isDisabled(btn: HTMLButtonElement): boolean {\n return btn.disabled || btn.getAttribute('aria-disabled') === 'true';\n }\n\n /** Reflect the host `disabled` state onto the segments (aria + tab order). */\n private _applyDisabledState() {\n for (const btn of this._buttons) {\n if (this.disabled) {\n btn.setAttribute('aria-disabled', 'true');\n btn.setAttribute('tabindex', '-1');\n } else {\n // Only clear the mirror this method set: a segment the consumer marked\n // `aria-disabled` stays disabled when the control is re-enabled.\n if (!this._authoredDisabled.has(btn)) btn.removeAttribute('aria-disabled');\n // Restore the roving tabindex (only the selected segment is tabbable).\n btn.setAttribute('tabindex', btn.getAttribute('aria-checked') === 'true' ? '0' : '-1');\n }\n }\n }\n\n // --- Indicator ---\n\n private _updateIndicator() {\n const active = this._buttons.find((b) => b.getAttribute('aria-checked') === 'true');\n if (!active) return;\n this.style.setProperty('--_indicator-left', `${active.offsetLeft}px`);\n this.style.setProperty('--_indicator-width', `${active.offsetWidth}px`);\n }\n\n // --- Event handlers ---\n\n private _onClick = (e: Event) => {\n if (this.disabled) return;\n const btn = (e.target as HTMLElement).closest<HTMLButtonElement>('button');\n if (!btn) return;\n const index = this._buttons.indexOf(btn);\n if (index < 0 || this._isDisabled(btn)) return;\n this._select(index);\n btn.focus();\n };\n\n // Typed `Event` (not `KeyboardEvent`): the listener is attached to `this`,\n // whose merged addEventListener overloads (see the interface below) fall back\n // to the platform `EventListener` signature for non-map events like `keydown`.\n private _onKeyDown = (e: Event) => {\n if (this.disabled) return;\n const ke = e as KeyboardEvent;\n const target = e.target as HTMLElement;\n if (target.getAttribute('role') !== 'radio') return;\n\n const current = this._buttons.indexOf(target as HTMLButtonElement);\n if (current < 0) return;\n\n let next = current;\n switch (ke.key) {\n case 'ArrowRight':\n case 'ArrowDown':\n next = this._nextEnabled(current, 1);\n break;\n case 'ArrowLeft':\n case 'ArrowUp':\n next = this._nextEnabled(current, -1);\n break;\n case 'Home':\n next = this._nextEnabled(-1, 1);\n break;\n case 'End':\n next = this._nextEnabled(this._buttons.length, -1);\n break;\n case ' ':\n // Space (re)selects the focused segment (APG radio group).\n break;\n default:\n return;\n }\n e.preventDefault();\n this._select(next);\n this._buttons[next].focus();\n };\n}\n\n// Type `addEventListener('change', …)` so listeners receive a `SegmentChangeEvent`\n// (with `.value`/`.index`) instead of a bare `Event`. `change` collides with\n// lib.dom's `GlobalEventHandlersEventMap`, so it can't be augmented globally —\n// the overloads are declaration-merged onto this element's interface instead.\n// The interface MUST come after the class (the CEM analyzer dedups on the first\n// declaration of the name). See `tabs.ts` for the fully-annotated reference.\nexport interface SegmentedControl {\n addEventListener<K extends keyof SegmentedControlEventMap>(\n type: K,\n listener: (this: SegmentedControl, ev: SegmentedControlEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener<K extends keyof SegmentedControlEventMap>(\n type: K,\n listener: (this: SegmentedControl, ev: SegmentedControlEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"segmented-control.js","names":[],"sources":["../../../src/html/elements/segmented-control/segmented-control.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenFormAssociatedElement } from '../../shared/luxen-form-associated-element.js';\n\nexport type SegmentedControlSize = 'sm' | 'md' | 'lg' | 'xl';\n\n/** Fired when the selected segment changes. Bubbles; not composed. */\nexport class SegmentChangeEvent extends Event {\n readonly value: string;\n readonly index: number;\n constructor(value: string, index: number) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n this.index = index;\n }\n}\n\ninterface SegmentedControlEventMap {\n change: SegmentChangeEvent;\n}\n\n/**\n * @summary A hybrid between a button group, radio buttons, and tabs: pick one\n * of a few closely related options or views, and the selection applies\n * immediately (radio-group semantics with a sliding pill).\n * Progressively enhances light-DOM `<button>`s so it works before JS runs.\n *\n * The immediate effect is the point — act on `change`. The control is\n * form-associated as well (`name` submits the selected `value`, reset restores\n * it), but a value the user confirms by submitting is better served by native\n * radios, which get keyboard, submission and validation from the platform.\n *\n * @example\n * Mark the initially-selected segment with `aria-checked=\"true\"` (or set\n * `value` to its `value` attribute) so it is styled before the script upgrades\n * the element.\n * ```html\n * <l-segmented-control label=\"Metric\" value=\"cost\">\n * <button value=\"volume\">Volume</button>\n * <button value=\"cost\" aria-checked=\"true\">Cost</button>\n * </l-segmented-control>\n * ```\n *\n * @event change - Fired when the selected segment changes. Not cancelable. Bubbles. Properties: `value: string`, `index: number`.\n *\n * @cssproperty [--indicator-color=var(--l-color-surface)] - Background of the sliding pill behind the selected segment.\n * @cssproperty [--border-radius=var(--l-radius-lg)] - Corner radius of the track.\n *\n * @customElement l-segmented-control\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class SegmentedControl extends LuxenFormAssociatedElement {\n override createRenderRoot() {\n return this;\n }\n\n private _initialized = false;\n private _setupTimer = 0;\n private _buttons: HTMLButtonElement[] = [];\n private _resizeObserver: ResizeObserver | null = null;\n /** Segments the consumer authored `aria-disabled` on — never ours to clear. */\n private _authoredDisabled = new WeakSet<HTMLButtonElement>();\n\n /** Value of the selected segment (its `value` attribute, else its index). */\n @property({ reflect: true })\n value = '';\n\n /** Control height, aligned with buttons and form controls at the same size. */\n @property({ reflect: true })\n size: SegmentedControlSize = 'md';\n\n /** Accessible label announced for the group. Not displayed on screen. */\n @property({ reflect: true })\n label?: string;\n\n /** Stretch segments to fill the container width. */\n @property({ type: Boolean, reflect: true, attribute: 'full-width' })\n fullWidth = false;\n\n // --- Lifecycle ---\n\n override connectedCallback() {\n super.connectedCallback();\n // Children may not be parsed yet when the element upgrades mid-parse: try\n // synchronously, then retry once on a macrotask. setTimeout, not rAF — rAF\n // is suspended in hidden documents, so the element would stay inert there.\n if (!this._trySetup()) {\n this._setupTimer = window.setTimeout(() => this._trySetup(), 0);\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n clearTimeout(this._setupTimer);\n this._teardown();\n }\n\n /** @returns true when setup ran or was already done; false to schedule a retry. */\n private _trySetup(): boolean {\n if (this._initialized || !this.isConnected) return true;\n if (this.querySelectorAll('button').length < 2) return false;\n this._setup();\n return true;\n }\n\n override updated(changed: Map<string, unknown>) {\n if (!this._initialized) return;\n if (changed.has('value')) {\n this._select(this._resolveActiveIndex(), false);\n }\n if (changed.has('size')) {\n this._updateIndicator();\n }\n if (changed.has('label')) {\n if (this.label) {\n this.setAttribute('aria-label', this.label);\n } else {\n this.removeAttribute('aria-label');\n }\n }\n if (changed.has('disabled')) {\n this._applyDisabledState();\n }\n }\n\n // --- Form lifecycle ---\n\n override formResetCallback() {\n // Restoring `value` re-selects the default segment via `updated()`.\n this.value = this._defaultFormValue;\n super.formResetCallback();\n }\n\n override formStateRestoreCallback(state: string, mode: 'restore' | 'autocomplete') {\n this.value = state;\n super.formStateRestoreCallback(state, mode);\n }\n\n // --- Setup / Teardown ---\n\n private _setup() {\n this._buttons = Array.from(this.querySelectorAll('button'));\n if (this._buttons.length < 2) return;\n this._initialized = true;\n\n this.setAttribute('role', 'radiogroup');\n if (this.label) this.setAttribute('aria-label', this.label);\n\n const activeIndex = this._resolveActiveIndex();\n\n for (let i = 0; i < this._buttons.length; i++) {\n const btn = this._buttons[i];\n // Force type=button so a segment never submits the enclosing form.\n btn.type = 'button';\n btn.setAttribute('role', 'radio');\n btn.setAttribute('aria-checked', String(i === activeIndex));\n btn.setAttribute('tabindex', i === activeIndex ? '0' : '-1');\n\n // Record an authored `aria-disabled` before the host-level state is\n // mirrored onto the segments: `_applyDisabledState()` clears that mirror\n // whenever the control is enabled, and must not take this one with it.\n if (btn.getAttribute('aria-disabled') === 'true') this._authoredDisabled.add(btn);\n\n // A segment with no visible text is icon-only: square it and require an\n // accessible name (the icon itself is decorative to assistive tech).\n if ((btn.textContent ?? '').trim() === '') {\n btn.setAttribute('data-icon-only', '');\n if (!btn.hasAttribute('aria-label') && !btn.hasAttribute('aria-labelledby')) {\n // eslint-disable-next-line no-console\n console.warn(\n `<${this.localName}>: icon-only segment #${i} has no accessible name; add aria-label.`,\n );\n }\n }\n }\n\n this.addEventListener('click', this._onClick);\n this.addEventListener('keydown', this._onKeyDown);\n\n // Reflect a whole-control `disabled` onto the segments (aria + tab order).\n this._applyDisabledState();\n\n // Canonicalize `value` to the active segment now that roles are set, and\n // seed the form value (the value a form reset restores to).\n this._syncValueFromIndex(activeIndex);\n this._defaultFormValue = this.value;\n this._syncFormValue(this.value);\n\n // Initial pill position. Synchronous first (offset reads force layout),\n // then a ResizeObserver keeps it correct across hidden→visible transitions\n // and size changes — rAF is suspended in hidden documents, so it must not\n // be the only scheduling mechanism.\n this._updateIndicator();\n this._resizeObserver = new ResizeObserver(() => this._updateIndicator());\n this._resizeObserver.observe(this);\n }\n\n private _teardown() {\n this.removeEventListener('click', this._onClick);\n this.removeEventListener('keydown', this._onKeyDown);\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n this._initialized = false;\n }\n\n // --- Selection ---\n\n /** Resolve the selected index from `value`, else an authored `aria-checked`, else 0. */\n private _resolveActiveIndex(): number {\n const { _buttons } = this;\n if (this.value !== '') {\n const byValue = _buttons.findIndex((b) => (b.getAttribute('value') ?? '') === this.value);\n if (byValue >= 0) return byValue;\n const asIndex = Number(this.value);\n if (Number.isInteger(asIndex) && asIndex >= 0 && asIndex < _buttons.length) return asIndex;\n }\n const checked = _buttons.findIndex((b) => b.getAttribute('aria-checked') === 'true');\n if (checked >= 0) return checked;\n // No explicit selection: default to the first enabled segment (else 0).\n const firstEnabled = _buttons.findIndex((b) => !this._isDisabled(b));\n return firstEnabled >= 0 ? firstEnabled : 0;\n }\n\n private _syncValueFromIndex(index: number) {\n this.value = this._buttons[index]?.getAttribute('value') ?? String(index);\n }\n\n private _select(index: number, emitEvent = true) {\n if (index < 0 || index >= this._buttons.length) return;\n // Whether this is an actual change — re-selecting the current segment must\n // not re-fire `change` (matches native radio / `<select>`).\n const changed = this._buttons[index].getAttribute('aria-checked') !== 'true';\n\n for (let i = 0; i < this._buttons.length; i++) {\n const isActive = i === index;\n this._buttons[i].setAttribute('aria-checked', String(isActive));\n // Disabled control keeps every segment out of the tab order.\n this._buttons[i].setAttribute('tabindex', isActive && !this.disabled ? '0' : '-1');\n }\n\n this._syncValueFromIndex(index);\n this._syncFormValue(this.value);\n this._updateIndicator();\n\n if (emitEvent && changed) {\n this.hasInteracted = true;\n this.dispatchEvent(new SegmentChangeEvent(this.value, index));\n }\n }\n\n /** Index of the next non-disabled segment from `from`, moving by `dir`, wrapping. */\n private _nextEnabled(from: number, dir: 1 | -1): number {\n const len = this._buttons.length;\n for (let step = 1; step <= len; step++) {\n const i = (from + dir * step + len * step) % len;\n if (!this._isDisabled(this._buttons[i])) return i;\n }\n return from;\n }\n\n private _isDisabled(btn: HTMLButtonElement): boolean {\n return btn.disabled || btn.getAttribute('aria-disabled') === 'true';\n }\n\n /** Reflect the host `disabled` state onto the segments (aria + tab order). */\n private _applyDisabledState() {\n for (const btn of this._buttons) {\n if (this.disabled) {\n btn.setAttribute('aria-disabled', 'true');\n btn.setAttribute('tabindex', '-1');\n } else {\n // Only clear the mirror this method set: a segment the consumer marked\n // `aria-disabled` stays disabled when the control is re-enabled.\n if (!this._authoredDisabled.has(btn)) btn.removeAttribute('aria-disabled');\n // Restore the roving tabindex (only the selected segment is tabbable).\n btn.setAttribute('tabindex', btn.getAttribute('aria-checked') === 'true' ? '0' : '-1');\n }\n }\n }\n\n // --- Indicator ---\n\n private _updateIndicator() {\n const active = this._buttons.find((b) => b.getAttribute('aria-checked') === 'true');\n if (!active) return;\n this.style.setProperty('--_indicator-left', `${active.offsetLeft}px`);\n this.style.setProperty('--_indicator-width', `${active.offsetWidth}px`);\n }\n\n // --- Event handlers ---\n\n private _onClick = (e: Event) => {\n if (this.disabled) return;\n const btn = (e.target as HTMLElement).closest<HTMLButtonElement>('button');\n if (!btn) return;\n const index = this._buttons.indexOf(btn);\n if (index < 0 || this._isDisabled(btn)) return;\n this._select(index);\n btn.focus();\n };\n\n // Typed `Event` (not `KeyboardEvent`): the listener is attached to `this`,\n // whose merged addEventListener overloads (see the interface below) fall back\n // to the platform `EventListener` signature for non-map events like `keydown`.\n private _onKeyDown = (e: Event) => {\n if (this.disabled) return;\n const ke = e as KeyboardEvent;\n const target = e.target as HTMLElement;\n if (target.getAttribute('role') !== 'radio') return;\n\n const current = this._buttons.indexOf(target as HTMLButtonElement);\n if (current < 0) return;\n\n let next = current;\n switch (ke.key) {\n case 'ArrowRight':\n case 'ArrowDown':\n next = this._nextEnabled(current, 1);\n break;\n case 'ArrowLeft':\n case 'ArrowUp':\n next = this._nextEnabled(current, -1);\n break;\n case 'Home':\n next = this._nextEnabled(-1, 1);\n break;\n case 'End':\n next = this._nextEnabled(this._buttons.length, -1);\n break;\n case ' ':\n // Space (re)selects the focused segment (APG radio group).\n break;\n default:\n return;\n }\n e.preventDefault();\n this._select(next);\n this._buttons[next].focus();\n };\n}\n\n// Type `addEventListener('change', …)` so listeners receive a `SegmentChangeEvent`\n// (with `.value`/`.index`) instead of a bare `Event`. `change` collides with\n// lib.dom's `GlobalEventHandlersEventMap`, so it can't be augmented globally —\n// the overloads are declaration-merged onto this element's interface instead.\n// The interface MUST come after the class (the CEM analyzer dedups on the first\n// declaration of the name). See `tabs.ts` for the fully-annotated reference.\nexport interface SegmentedControl {\n addEventListener<K extends keyof SegmentedControlEventMap>(\n type: K,\n listener: (this: SegmentedControl, ev: SegmentedControlEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener<K extends keyof SegmentedControlEventMap>(\n type: K,\n listener: (this: SegmentedControl, ev: SegmentedControlEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{cls as e,tagName as t,uniqueId as n}from"../../registry.js";import{i as r}from"../../chunks/lit.js";import{i,r as a,t as o}from"../../chunks/decorate.js";import{a as s,t as c}from"../../chunks/lit-html.js";import{LocalizeController as l}from"../../shared/localize.js";import{n as u,r as d}from"../../chunks/static-html.js";import f from"../../shared/styles/host.styles.js";import{t as p}from"../../chunks/unsafe-html.js";import{LuxenFormAssociatedElement as m}from"../../shared/luxen-form-associated-element.js";import{PopoverController as h}from"../../shared/controllers/popover.js";import{ListboxNavController as g}from"../../shared/controllers/listbox-nav.js";var _=r(`:host{--height:var(--l-form-control-height);--border-radius:var(--l-form-control-border-radius);--background:var(--l-color-surface-overlay);inline-size:100%;display:block}:host([size=xs]){--height:var(--l-size-control-xs)}:host([size=sm]){--height:var(--l-size-control-sm)}:host([size=lg]){--height:var(--l-size-control-lg)}:host([size=xl]){--height:var(--l-size-control-xl)}:host([disabled]){opacity:.5;pointer-events:none}.base{position:relative}.trigger{inline-size:100%;min-block-size:var(--height);padding-block:.25rem;padding-inline:var(--l-form-control-padding-inline);border:var(--l-form-control-border-width) solid var(--l-form-control-border-color);border-radius:var(--border-radius);background-color:var(--l-form-control-background-color);color:var(--l-form-control-value-color);font:inherit;text-align:start;cursor:pointer;align-items:center;gap:.5rem;display:flex}:host([multiple]) .trigger{padding-inline-start:.375rem}@media (hover:hover){.trigger:hover{border-color:var(--l-form-control-border-color-hover)}}.trigger:focus-visible{border-color:var(--l-focus-ring);box-shadow:0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent);outline:none}.value{flex-wrap:wrap;flex:1;align-items:center;gap:.25rem;min-inline-size:0;display:flex;overflow:hidden}.value-text{text-overflow:ellipsis;white-space:nowrap;min-inline-size:0;overflow:hidden}.placeholder{color:var(--l-form-control-placeholder-color)}.chevron{block-size:1em;inline-size:1em;color:var(--l-form-control-placeholder-color);-webkit-mask:var(--_chevron-icon) center / contain no-repeat;mask:var(--_chevron-icon) center / contain no-repeat;background-color:currentColor;flex:none;transition:rotate .15s}.trigger[aria-expanded=true] .chevron{rotate:180deg}.clear{cursor:pointer;block-size:1.5rem;inline-size:1.5rem;color:var(--l-form-control-placeholder-color);-webkit-mask:var(--_x-icon) center / 1em no-repeat;mask:var(--_x-icon) center / 1em no-repeat;background-color:currentColor;border:0;margin-block:auto;padding:0;position:absolute;inset-block:0;inset-inline-end:calc(var(--l-form-control-padding-inline) + 1.25em)}@media (hover:hover){.clear:hover{color:var(--l-form-control-value-color)}}.clear:focus-visible{outline:2px solid var(--l-focus-ring);outline-offset:2px}.value .l-select-item-media{flex:none;align-items:center;font-size:1.25em;display:inline-flex}.value .l-select-item-text{flex-direction:column;min-inline-size:0;display:flex}.value .l-select-item-title{font-weight:inherit;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.value .l-select-item-description{display:none}.panel{border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius);background-color:var(--background);color:var(--l-color-text-primary);box-shadow:var(--l-shadow-md);opacity:0;transition:opacity .12s ease, scale .12s ease, display .12s allow-discrete, overlay .12s allow-discrete;margin:0;padding:0;font-size:.875rem;line-height:1.5;inset:auto;overflow:hidden;scale:.98}.panel:popover-open{opacity:1;scale:1}@starting-style{.panel:popover-open{opacity:0;scale:.98}}.search-row{border-block-end:1px solid var(--l-color-border-overlay);padding:.375rem}.search{inline-size:100%;block-size:var(--l-size-control-sm);border:var(--l-form-control-border-width) solid var(--l-form-control-border-color);border-radius:calc(var(--border-radius) - 2px);background-color:var(--l-form-control-background-color);color:var(--l-form-control-value-color);font:inherit;padding-inline:.5rem}.search:focus-visible{border-color:var(--l-focus-ring);outline:none}.search::placeholder{color:var(--l-form-control-placeholder-color)}.listbox{overscroll-behavior:contain;max-block-size:16rem;margin:0;padding:.25rem;list-style:none;overflow-y:auto}.option{min-block-size:36px;color:var(--l-color-text-primary);cursor:pointer;border-radius:4px;align-items:start;gap:.5rem;padding-block:.5rem;padding-inline:1.75rem .5rem;display:flex;position:relative}@media (hover:hover){.option:hover{background-color:var(--l-color-bg-state-hover)}}.option[data-active]{background-color:var(--l-color-bg-state-hover)}.option[aria-disabled=true]{opacity:.4;cursor:not-allowed}.option[data-current]:after{content:"";block-size:1.3125rem;inline-size:1rem;-webkit-mask:var(--_check-icon) center / 1rem no-repeat;mask:var(--_check-icon) center / 1rem no-repeat;background-color:currentColor;position:absolute;inset-block-start:.5rem;inset-inline-start:.5rem}.option-label{text-overflow:ellipsis;white-space:nowrap;flex:1;min-inline-size:0;overflow:hidden}.option-label mark{color:inherit;background:0 0;font-weight:600}.option-rich{flex:1;align-items:start;gap:.5rem;min-inline-size:0;display:flex}.option-rich .l-select-item-media{object-fit:cover;border-radius:4px;flex:none;align-items:center;block-size:1.5rem;inline-size:1.5rem;font-size:1.25em;display:inline-flex}.option-rich .l-select-item-text{flex-direction:column;flex:auto;min-inline-size:0;display:flex}.option-rich .l-select-item-title{text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.option-rich .l-select-item-description{color:var(--l-color-text-tertiary);white-space:normal;text-wrap:pretty;font-size:.75rem;line-height:1.3}.empty{color:var(--l-color-text-tertiary);text-align:center;padding:.5rem}.visually-hidden{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}:host{--_chevron-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");--_x-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");--_check-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E")}@media (prefers-reduced-motion:reduce){.panel,.chevron{transition-duration:0s}}`);function v(e){return e.normalize(`NFD`).replace(/[̀-ͯ]/g,``).toLowerCase()}var y=(e,t)=>{if(!t)return!0;let n=v(e.label);return v(t).split(/\s+/).filter(Boolean).every(e=>n.includes(e))},b=class extends Event{constructor(e){super(`change`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},x=class extends Event{constructor(e){super(`input`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},S=class extends m{constructor(...e){super(...e),this._localize=new l(this),this._listId=n(`select-list`),this._floating=new h(this,{getTriggerElement:()=>this._triggerEl,getFloatingElement:()=>this._panelEl,getArrowElement:()=>null}),this._nav=new g(this,{listboxId:this._listId,getCount:()=>this._filtered.length,isDisabled:e=>this._filtered[e]?.disabled??!1,isOpen:()=>this._open,open:()=>this.show(),getOptionElements:()=>this.shadowRoot?.querySelectorAll(`.option`)}),this.#e=``,this.#t=``,this.#n=`md`,this.#r=!1,this.#i=!1,this.#a=`bottom-start`,this.#o=!1,this.filter=y,this.#s=[],this.#c=!1,this.#l=``,this.#u=[],this._defaultValues=[],this._onDocumentPointerDown=e=>{e.composedPath().includes(this)||(this.hasInteracted=!0,this._updateValidity(),this.hide({focusTrigger:!1}))},this._onTriggerKeyDown=e=>{this._open||(e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`||e.key===` `)&&(e.preventDefault(),this.show())},this._onPanelKeyDown=e=>{if(!this._nav.onKeyDown(e))switch(e.key){case`Enter`:{e.preventDefault();let t=this._filtered[this._nav.activeIndex];t&&this._select(t);break}case`Escape`:e.preventDefault(),this.hide();break;case`Tab`:this.hide({focusTrigger:!1});break;default:break}},this._onSearchInput=e=>{this._query=e.target.value,this._nav.reset(),this.dispatchEvent(new x(this._query))}}static{this.styles=[f,_]}#e;get placeholder(){return this.#e}set placeholder(e){this.#e=e}#t;get label(){return this.#t}set label(e){this.#t=e}#n;get size(){return this.#n}set size(e){this.#n=e}#r;get searchable(){return this.#r}set searchable(e){this.#r=e}#i;get withClear(){return this.#i}set withClear(e){this.#i=e}#a;get placement(){return this.#a}set placement(e){this.#a=e}#o;get multiple(){return this.#o}set multiple(e){this.#o=e}#s;get _values(){return this.#s}set _values(e){this.#s=e}#c;get _open(){return this.#c}set _open(e){this.#c=e}#l;get _query(){return this.#l}set _query(e){this.#l=e}#u;get _items(){return this.#u}set _items(e){this.#u=e}get value(){return this.multiple?[...this._values]:this._values[0]??``}set value(e){this._values=Array.isArray(e)?[...e]:e?[e]:[]}get _triggerEl(){return this.shadowRoot?.querySelector(`.trigger`)??null}get _panelEl(){return this.shadowRoot?.querySelector(`.panel`)??null}get _searchEl(){return this.shadowRoot?.querySelector(`.search`)??null}get validationTarget(){return this._triggerEl??void 0}connectedCallback(){if(super.connectedCallback(),this._readItems(),this._values.length===0){let e=this.getAttribute(`value`);if(e)this._values=[e];else{let e=this._items.filter(e=>e.selected).map(e=>e.value);this._values=this.multiple?e:e.slice(0,1)}}this._defaultValues=[...this._values],this._syncForm()}updated(e){(e.has(`_values`)||e.has(`multiple`))&&(this._syncForm(),this._updateValidity())}formResetCallback(){this._values=[...this._defaultValues],this._query=``,this._nav.reset(),super.formResetCallback(),this._syncForm()}formStateRestoreCallback(e){this.value=e,this._syncForm()}_readItems(){let t=this.querySelector(`datalist`),n=t?Array.from(t.options):[];this._items=n.map(t=>{let n=t.childElementCount>0,r=t.querySelector(`.${e(`select-item-title`)}`)?.textContent?.trim(),i=(t.getAttribute(`label`)||r||t.textContent||t.value).trim();return{value:t.value,label:i,disabled:t.disabled,selected:t.hasAttribute(`selected`),html:n?t.innerHTML:void 0}})}_itemFor(e){return this._items.find(t=>t.value===e)}get _filtered(){return this._items.filter(e=>this.filter(e,this._query))}_syncForm(){if(this.multiple){let e=new FormData;for(let t of this._values)e.append(this.name,t);this._internals.setFormValue(e)}else this._internals.setFormValue(this._values[0]??``)}_updateValidity(){this.required&&this._values.length===0?this.setValidity({valueMissing:!0},this._localize.term(`selectOption`),this._triggerEl??void 0):this.setValidity({})}show(){this._open||this.disabled||(this._readItems(),this.dispatchEvent(new Event(`show`,{cancelable:!0}))&&(this._open=!0,this._query=``,this._values.length&&this._nav.setActive(this._filtered.findIndex(e=>e.value===this._values[0])),this.updateComplete.then(()=>{this._panelEl?.showPopover?.(),this._floating.startPositioning({placement:this.placement,distance:4,minWidth:`trigger`}),(this._searchEl??this._panelEl?.querySelector(`.listbox`))?.focus(),document.addEventListener(`pointerdown`,this._onDocumentPointerDown,!0)})))}hide({focusTrigger:e=!0}={}){if(!this._open||!this.dispatchEvent(new Event(`hide`,{cancelable:!0})))return;this._open=!1,this._query=``,this._nav.reset();let t=this._panelEl;t?.matches?.(`:popover-open`)&&t.hidePopover?.(),this._floating.stopPositioning(),document.removeEventListener(`pointerdown`,this._onDocumentPointerDown,!0),e&&this._triggerEl?.focus()}_select(e){e.disabled||(this.hasInteracted=!0,this.multiple?(this._values=this._values.includes(e.value)?this._values.filter(t=>t!==e.value):[...this._values,e.value],this.dispatchEvent(new b(this.value)),this._searchEl?.focus()):(this._values=[e.value],this.dispatchEvent(new b(this.value)),this.hide()))}_removeValue(e){this._values=this._values.filter(t=>t!==e),this.hasInteracted=!0,this.dispatchEvent(new b(this.value))}_clear(e){e?.stopPropagation(),this._values=[],this.hasInteracted=!0,this.dispatchEvent(new b(this.value)),this._triggerEl?.focus()}_highlight(e){let t=this._query.trim();if(!t)return e;let n=v(e).indexOf(v(t).split(/\s+/)[0]);return n<0?e:s`${e.slice(0,n)}<mark>${e.slice(n,n+t.length)}</mark>${e.slice(n+t.length)}`}_renderSingleValue(){let e=this._itemFor(this._values[0]);return e?e.html?s`${p(e.html)}`:s`<span class="value-text">${e.label}</span>`:s`<span class="value-text placeholder">${this.placeholder}</span>`}_renderChips(){if(this._values.length===0)return s`<span class="value-text placeholder">${this.placeholder}</span>`;let e=u(t(`tag`));return this._values.map(t=>d`<${e}
|
|
1
|
+
import{cls as e,tagName as t,uniqueId as n}from"../../registry.js";import{i as r}from"../../chunks/lit.js";import{i,r as a,t as o}from"../../chunks/decorate.js";import{a as s,t as c}from"../../chunks/lit-html.js";import{LocalizeController as l}from"../../shared/localize.js";import{n as u,r as d}from"../../chunks/static-html.js";import f from"../../shared/styles/host.styles.js";import{t as p}from"../../chunks/unsafe-html.js";import{LuxenFormAssociatedElement as m}from"../../shared/luxen-form-associated-element.js";import{PopoverController as h}from"../../shared/controllers/popover.js";import{ListboxNavController as g}from"../../shared/controllers/listbox-nav.js";var _=r(`:host{--height:var(--l-form-control-height);--border-radius:var(--l-form-control-border-radius);--background:var(--l-color-surface-overlay);inline-size:100%;display:block}:host([size=xs]){--height:var(--l-size-control-xs)}:host([size=sm]){--height:var(--l-size-control-sm)}:host([size=lg]){--height:var(--l-size-control-lg)}:host([size=xl]){--height:var(--l-size-control-xl)}:host([disabled]){opacity:.5;pointer-events:none}.base{position:relative}.trigger{inline-size:100%;min-block-size:var(--height);padding-block:.25rem;padding-inline:var(--l-form-control-padding-inline);border:var(--l-form-control-border-width) solid var(--l-form-control-border-color);border-radius:var(--border-radius);background-color:var(--l-form-control-background-color);color:var(--l-form-control-value-color);font:inherit;text-align:start;cursor:pointer;align-items:center;gap:.5rem;display:flex}:host([multiple]) .trigger{padding-inline-start:.375rem}@media (hover:hover){.trigger:hover{border-color:var(--l-form-control-border-color-hover)}}.trigger:focus-visible{border-color:var(--l-focus-ring);box-shadow:0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent);outline:none}.value{flex-wrap:wrap;flex:1;align-items:center;gap:.25rem;min-inline-size:0;display:flex;overflow:hidden}.value-text{text-overflow:ellipsis;white-space:nowrap;min-inline-size:0;overflow:hidden}[part=tag]{--height:initial;--border-radius:initial;--background:initial}.placeholder{color:var(--l-form-control-placeholder-color)}.chevron{block-size:1em;inline-size:1em;color:var(--l-form-control-placeholder-color);-webkit-mask:var(--_chevron-icon) center / contain no-repeat;mask:var(--_chevron-icon) center / contain no-repeat;background-color:currentColor;flex:none;transition:rotate .15s}.trigger[aria-expanded=true] .chevron{rotate:180deg}.clear{cursor:pointer;block-size:1.5rem;inline-size:1.5rem;color:var(--l-form-control-placeholder-color);-webkit-mask:var(--_x-icon) center / 1em no-repeat;mask:var(--_x-icon) center / 1em no-repeat;background-color:currentColor;border:0;margin-block:auto;padding:0;position:absolute;inset-block:0;inset-inline-end:calc(var(--l-form-control-padding-inline) + 1.25em)}@media (hover:hover){.clear:hover{color:var(--l-form-control-value-color)}}.clear:focus-visible{outline:2px solid var(--l-focus-ring);outline-offset:2px}.value .l-select-item-media{flex:none;align-items:center;font-size:1.25em;display:inline-flex}.value .l-select-item-text{flex-direction:column;min-inline-size:0;display:flex}.value .l-select-item-title{font-weight:inherit;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.value .l-select-item-description{display:none}.panel{border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius);background-color:var(--background);color:var(--l-color-text-primary);box-shadow:var(--l-shadow-md);opacity:0;transition:opacity .12s ease, scale .12s ease, display .12s allow-discrete, overlay .12s allow-discrete;margin:0;padding:0;font-size:.875rem;line-height:1.5;inset:auto;overflow:hidden;scale:.98}.panel:popover-open{opacity:1;scale:1}@starting-style{.panel:popover-open{opacity:0;scale:.98}}.search-row{border-block-end:1px solid var(--l-color-border-overlay);padding:.375rem}.search{inline-size:100%;block-size:var(--l-size-control-sm);border:var(--l-form-control-border-width) solid var(--l-form-control-border-color);border-radius:calc(var(--border-radius) - 2px);background-color:var(--l-form-control-background-color);color:var(--l-form-control-value-color);font:inherit;padding-inline:.5rem}.search:focus-visible{border-color:var(--l-focus-ring);outline:none}.search::placeholder{color:var(--l-form-control-placeholder-color)}.listbox{overscroll-behavior:contain;max-block-size:16rem;margin:0;padding:.25rem;list-style:none;overflow-y:auto}.option{min-block-size:36px;color:var(--l-color-text-primary);cursor:pointer;border-radius:4px;align-items:start;gap:.5rem;padding-block:.5rem;padding-inline:1.75rem .5rem;display:flex;position:relative}@media (hover:hover){.option:hover{background-color:var(--l-color-bg-state-hover)}}.option[data-active]{background-color:var(--l-color-bg-state-hover)}.option[aria-disabled=true]{opacity:.4;cursor:not-allowed}.option[data-current]:after{content:"";block-size:1.3125rem;inline-size:1rem;-webkit-mask:var(--_check-icon) center / 1rem no-repeat;mask:var(--_check-icon) center / 1rem no-repeat;background-color:currentColor;position:absolute;inset-block-start:.5rem;inset-inline-start:.5rem}.option-label{text-overflow:ellipsis;white-space:nowrap;flex:1;min-inline-size:0;overflow:hidden}.option-label mark{color:inherit;background:0 0;font-weight:600}.option-rich{flex:1;align-items:start;gap:.5rem;min-inline-size:0;display:flex}.option-rich .l-select-item-media{object-fit:cover;border-radius:4px;flex:none;align-items:center;block-size:1.5rem;inline-size:1.5rem;font-size:1.25em;display:inline-flex}.option-rich .l-select-item-text{flex-direction:column;flex:auto;min-inline-size:0;display:flex}.option-rich .l-select-item-title{text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.option-rich .l-select-item-description{color:var(--l-color-text-tertiary);white-space:normal;text-wrap:pretty;font-size:.75rem;line-height:1.3}.empty{color:var(--l-color-text-tertiary);text-align:center;padding:.5rem}.visually-hidden{clip-path:inset(50%);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}:host{--_chevron-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");--_x-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");--_check-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E")}@media (prefers-reduced-motion:reduce){.panel,.chevron{transition-duration:0s}}`);function v(e){return e.normalize(`NFD`).replace(/[̀-ͯ]/g,``).toLowerCase()}var y=(e,t)=>{if(!t)return!0;let n=v(e.label);return v(t).split(/\s+/).filter(Boolean).every(e=>n.includes(e))},b=class extends Event{constructor(e){super(`change`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},x=class extends Event{constructor(e){super(`input`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},S=class extends m{constructor(...e){super(...e),this._localize=new l(this),this._listId=n(`select-list`),this._floating=new h(this,{getTriggerElement:()=>this._triggerEl,getFloatingElement:()=>this._panelEl,getArrowElement:()=>null}),this._nav=new g(this,{listboxId:this._listId,getCount:()=>this._filtered.length,isDisabled:e=>this._filtered[e]?.disabled??!1,isOpen:()=>this._open,open:()=>this.show(),getOptionElements:()=>this.shadowRoot?.querySelectorAll(`.option`)}),this.#e=``,this.#t=``,this.#n=`md`,this.#r=!1,this.#i=!1,this.#a=`bottom-start`,this.#o=!1,this.filter=y,this.#s=[],this.#c=!1,this.#l=``,this.#u=[],this._defaultValues=[],this._onDocumentPointerDown=e=>{e.composedPath().includes(this)||(this.hasInteracted=!0,this._updateValidity(),this.hide({focusTrigger:!1}))},this._onTriggerKeyDown=e=>{this._open||(e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`||e.key===` `)&&(e.preventDefault(),this.show())},this._onPanelKeyDown=e=>{if(!this._nav.onKeyDown(e))switch(e.key){case`Enter`:{e.preventDefault();let t=this._filtered[this._nav.activeIndex];t&&this._select(t);break}case`Escape`:e.preventDefault(),this.hide();break;case`Tab`:this.hide({focusTrigger:!1});break;default:break}},this._onSearchInput=e=>{this._query=e.target.value,this._nav.reset(),this.dispatchEvent(new x(this._query))}}static{this.styles=[f,_]}#e;get placeholder(){return this.#e}set placeholder(e){this.#e=e}#t;get label(){return this.#t}set label(e){this.#t=e}#n;get size(){return this.#n}set size(e){this.#n=e}#r;get searchable(){return this.#r}set searchable(e){this.#r=e}#i;get withClear(){return this.#i}set withClear(e){this.#i=e}#a;get placement(){return this.#a}set placement(e){this.#a=e}#o;get multiple(){return this.#o}set multiple(e){this.#o=e}#s;get _values(){return this.#s}set _values(e){this.#s=e}#c;get _open(){return this.#c}set _open(e){this.#c=e}#l;get _query(){return this.#l}set _query(e){this.#l=e}#u;get _items(){return this.#u}set _items(e){this.#u=e}get value(){return this.multiple?[...this._values]:this._values[0]??``}set value(e){this._values=Array.isArray(e)?[...e]:e?[e]:[]}get _triggerEl(){return this.shadowRoot?.querySelector(`.trigger`)??null}get _panelEl(){return this.shadowRoot?.querySelector(`.panel`)??null}get _searchEl(){return this.shadowRoot?.querySelector(`.search`)??null}get validationTarget(){return this._triggerEl??void 0}connectedCallback(){if(super.connectedCallback(),this._readItems(),this._values.length===0){let e=this.getAttribute(`value`);if(e)this._values=[e];else{let e=this._items.filter(e=>e.selected).map(e=>e.value);this._values=this.multiple?e:e.slice(0,1)}}this._defaultValues=[...this._values],this._syncForm()}updated(e){(e.has(`_values`)||e.has(`multiple`))&&(this._syncForm(),this._updateValidity())}formResetCallback(){this._values=[...this._defaultValues],this._query=``,this._nav.reset(),super.formResetCallback(),this._syncForm()}formStateRestoreCallback(e){this.value=e,this._syncForm()}_readItems(){let t=this.querySelector(`datalist`),n=t?Array.from(t.options):[];this._items=n.map(t=>{let n=t.childElementCount>0,r=t.querySelector(`.${e(`select-item-title`)}`)?.textContent?.trim(),i=(t.getAttribute(`label`)||r||t.textContent||t.value).trim();return{value:t.value,label:i,disabled:t.disabled,selected:t.hasAttribute(`selected`),html:n?t.innerHTML:void 0}})}_itemFor(e){return this._items.find(t=>t.value===e)}get _filtered(){return this._items.filter(e=>this.filter(e,this._query))}_syncForm(){if(this.multiple){let e=new FormData;for(let t of this._values)e.append(this.name,t);this._internals.setFormValue(e)}else this._internals.setFormValue(this._values[0]??``)}_updateValidity(){this.required&&this._values.length===0?this.setValidity({valueMissing:!0},this._localize.term(`selectOption`),this._triggerEl??void 0):this.setValidity({})}show(){this._open||this.disabled||(this._readItems(),this.dispatchEvent(new Event(`show`,{cancelable:!0}))&&(this._open=!0,this._query=``,this._values.length&&this._nav.setActive(this._filtered.findIndex(e=>e.value===this._values[0])),this.updateComplete.then(()=>{this._panelEl?.showPopover?.(),this._floating.startPositioning({placement:this.placement,distance:4,minWidth:`trigger`}),(this._searchEl??this._panelEl?.querySelector(`.listbox`))?.focus(),document.addEventListener(`pointerdown`,this._onDocumentPointerDown,!0)})))}hide({focusTrigger:e=!0}={}){if(!this._open||!this.dispatchEvent(new Event(`hide`,{cancelable:!0})))return;this._open=!1,this._query=``,this._nav.reset();let t=this._panelEl;t?.matches?.(`:popover-open`)&&t.hidePopover?.(),this._floating.stopPositioning(),document.removeEventListener(`pointerdown`,this._onDocumentPointerDown,!0),e&&this._triggerEl?.focus()}_select(e){e.disabled||(this.hasInteracted=!0,this.multiple?(this._values=this._values.includes(e.value)?this._values.filter(t=>t!==e.value):[...this._values,e.value],this.dispatchEvent(new b(this.value)),this._searchEl?.focus()):(this._values=[e.value],this.dispatchEvent(new b(this.value)),this.hide()))}_removeValue(e){this._values=this._values.filter(t=>t!==e),this.hasInteracted=!0,this.dispatchEvent(new b(this.value))}_clear(e){e?.stopPropagation(),this._values=[],this.hasInteracted=!0,this.dispatchEvent(new b(this.value)),this._triggerEl?.focus()}_highlight(e){let t=this._query.trim();if(!t)return e;let n=v(e).indexOf(v(t).split(/\s+/)[0]);return n<0?e:s`${e.slice(0,n)}<mark>${e.slice(n,n+t.length)}</mark>${e.slice(n+t.length)}`}_renderSingleValue(){let e=this._itemFor(this._values[0]);return e?e.html?s`${p(e.html)}`:s`<span class="value-text">${e.label}</span>`:s`<span class="value-text placeholder">${this.placeholder}</span>`}_renderChips(){if(this._values.length===0)return s`<span class="value-text placeholder">${this.placeholder}</span>`;let e=u(t(`tag`));return this._values.map(t=>d`<${e}
|
|
2
2
|
size="sm"
|
|
3
3
|
removable
|
|
4
4
|
part="tag"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.js","names":[],"sources":["../../../src/html/elements/select/select.css?inline","../../../src/html/elements/select/select.ts"],"sourcesContent":[":host {\n --height: var(--l-form-control-height);\n --border-radius: var(--l-form-control-border-radius);\n --background: var(--l-color-surface-overlay);\n\n display: block;\n inline-size: 100%;\n}\n\n:host([size='xs']) {\n --height: var(--l-size-control-xs);\n}\n:host([size='sm']) {\n --height: var(--l-size-control-sm);\n}\n:host([size='lg']) {\n --height: var(--l-size-control-lg);\n}\n:host([size='xl']) {\n --height: var(--l-size-control-xl);\n}\n\n:host([disabled]) {\n opacity: 0.5;\n pointer-events: none;\n}\n\n.base {\n position: relative;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆃🆁🅸🅶🅶🅴🆁 (button, opens the listbox popover)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.trigger {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n inline-size: 100%;\n min-block-size: var(--height);\n padding-block: 0.25rem;\n padding-inline: var(--l-form-control-padding-inline);\n border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);\n border-radius: var(--border-radius);\n background-color: var(--l-form-control-background-color);\n color: var(--l-form-control-value-color);\n font: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n/* The form-control inline padding is tuned for text; before a chip row it reads\n as too much, so tighten the start edge in multiple mode (the chips bring their\n own spacing). */\n:host([multiple]) .trigger {\n padding-inline-start: 0.375rem;\n}\n\n@media (hover: hover) {\n .trigger:hover {\n border-color: var(--l-form-control-border-color-hover);\n }\n}\n\n.trigger:focus-visible {\n outline: none;\n border-color: var(--l-focus-ring);\n box-shadow: 0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent);\n}\n\n/* Selection display fills the trigger; the chevron (and overlaid clear) sit at the end. */\n.value {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 0.25rem;\n overflow: hidden;\n}\n\n.value-text {\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.placeholder {\n color: var(--l-form-control-placeholder-color);\n}\n\n.chevron {\n flex: none;\n inline-size: 1em;\n block-size: 1em;\n background-color: currentColor;\n color: var(--l-form-control-placeholder-color);\n mask: var(--_chevron-icon) center / contain no-repeat;\n transition: rotate 0.15s ease;\n}\n\n.trigger[aria-expanded='true'] .chevron {\n rotate: 180deg;\n}\n\n/* Clear is a SIBLING of the trigger (not a child — no nested buttons), overlaid\n * over the trigger's end, before the chevron. ≥24px hit area (WCAG 2.5.8). */\n.clear {\n position: absolute;\n inset-block: 0;\n inset-inline-end: calc(var(--l-form-control-padding-inline) + 1.25em);\n margin-block: auto;\n inline-size: 1.5rem;\n block-size: 1.5rem;\n border: 0;\n padding: 0;\n cursor: pointer;\n background-color: currentColor;\n color: var(--l-form-control-placeholder-color);\n mask: var(--_x-icon) center / 1em no-repeat;\n}\n\n@media (hover: hover) {\n .clear:hover {\n color: var(--l-form-control-value-color);\n }\n}\n\n.clear:focus-visible {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 2px;\n}\n\n/* Rich content rendered in the trigger mirrors the option rows. */\n.value .l-select-item-media {\n flex: none;\n display: inline-flex;\n align-items: center;\n font-size: 1.25em;\n}\n.value .l-select-item-text {\n display: flex;\n flex-direction: column;\n min-inline-size: 0;\n}\n/* Mirror the native trigger: show only the title, drop the secondary description\n so the selected value stays compact and consistent across options. */\n.value .l-select-item-title {\n font-weight: inherit;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.value .l-select-item-description {\n display: none;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅿🅰🅽🅴🅻 (popover: search + listbox)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.panel {\n inset: auto;\n margin: 0;\n padding: 0;\n border: 1px solid var(--l-color-border-overlay);\n border-radius: var(--border-radius);\n background-color: var(--background);\n color: var(--l-color-text-primary);\n box-shadow: var(--l-shadow-md);\n font-size: 0.875rem;\n line-height: 1.5;\n overflow: hidden;\n opacity: 0;\n scale: 0.98;\n transition:\n opacity 0.12s ease,\n scale 0.12s ease,\n display 0.12s allow-discrete,\n overlay 0.12s allow-discrete;\n}\n\n.panel:popover-open {\n opacity: 1;\n scale: 1;\n}\n\n@starting-style {\n .panel:popover-open {\n opacity: 0;\n scale: 0.98;\n }\n}\n\n.search-row {\n padding: 0.375rem;\n border-block-end: 1px solid var(--l-color-border-overlay);\n}\n\n.search {\n inline-size: 100%;\n block-size: var(--l-size-control-sm);\n padding-inline: 0.5rem;\n border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);\n border-radius: calc(var(--border-radius) - 2px);\n background-color: var(--l-form-control-background-color);\n color: var(--l-form-control-value-color);\n font: inherit;\n}\n\n.search:focus-visible {\n outline: none;\n border-color: var(--l-focus-ring);\n}\n\n.search::placeholder {\n color: var(--l-form-control-placeholder-color);\n}\n\n.listbox {\n margin: 0;\n padding: 0.25rem;\n list-style: none;\n max-block-size: 16rem;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n/* Mirrors the native `.l-select` picker (itself calé on l-dropdown): the\n * checkmark lives in a reserved inline-start column, only the hovered/active row\n * takes the tint, and a selected row carries no background (checkmark only). */\n.option {\n display: flex;\n position: relative;\n align-items: start;\n gap: 0.5rem;\n min-block-size: 36px;\n padding-block: 0.5rem;\n padding-inline: 1.75rem 0.5rem;\n border-radius: 4px;\n color: var(--l-color-text-primary);\n cursor: pointer;\n}\n\n@media (hover: hover) {\n .option:hover {\n background-color: var(--l-color-bg-state-hover);\n }\n}\n\n/* Keyboard-active row (moves with Arrow keys) — same tint as hover. */\n.option[data-active] {\n background-color: var(--l-color-bg-state-hover);\n}\n\n.option[aria-disabled='true'] {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n/* Selected indicator in the reserved inline-start column, pinned to the first\n * line so a wrapping description never shifts it (matches the native picker). */\n.option[data-current]::after {\n content: '';\n position: absolute;\n inset-inline-start: 0.5rem;\n inset-block-start: 0.5rem;\n inline-size: 1rem;\n block-size: 1.3125rem;\n background-color: currentColor;\n mask: var(--_check-icon) center / 1rem no-repeat;\n}\n\n.option-label {\n flex: 1;\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.option-label mark {\n background: transparent;\n color: inherit;\n font-weight: 600;\n}\n\n.option-rich {\n display: flex;\n align-items: start;\n gap: 0.5rem;\n flex: 1;\n min-inline-size: 0;\n}\n\n.option-rich .l-select-item-media {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n inline-size: 1.5rem;\n block-size: 1.5rem;\n border-radius: 4px;\n object-fit: cover;\n font-size: 1.25em;\n}\n\n.option-rich .l-select-item-text {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-inline-size: 0;\n}\n\n.option-rich .l-select-item-title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n}\n\n.option-rich .l-select-item-description {\n color: var(--l-color-text-tertiary);\n font-size: 0.75rem;\n line-height: 1.3;\n white-space: normal;\n text-wrap: pretty;\n}\n\n.empty {\n padding: 0.5rem;\n color: var(--l-color-text-tertiary);\n text-align: center;\n}\n\n.visually-hidden {\n position: absolute;\n inline-size: 1px;\n block-size: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅸🅲🅾🅽🆂\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n:host {\n --_chevron-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\");\n --_x-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E\");\n --_check-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E\");\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel,\n .chevron {\n transition-duration: 0ms;\n }\n}\n","import { html, nothing, unsafeCSS } from 'lit';\nimport type { TemplateResult } from 'lit';\nimport { html as staticHtml, unsafeStatic } from 'lit/static-html.js';\nimport { property, state } from 'lit/decorators.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { LuxenFormAssociatedElement } from '../../shared/luxen-form-associated-element.js';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { ListboxNavController } from '../../shared/controllers/listbox-nav.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { cls, tagName, uniqueId } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './select.css?inline';\n// Registers the chip element used to render selected values in multiple mode.\nimport '../tag/index.js';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type SelectSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\n/** A single option read from the `<datalist>`. */\nexport interface SelectItem {\n value: string;\n /** Plain text used for filtering and the trigger display. */\n label: string;\n disabled: boolean;\n /** Authored `selected` attribute — seeds the initial value. */\n selected: boolean;\n /** Rich inner markup (from a non-empty `<option>`), projected into the row. */\n html?: string;\n}\n\n/** Default filter: accent/case-insensitive, every space-separated keyword must match. */\nexport type SelectFilter = (item: SelectItem, query: string) => boolean;\n\nfunction normalize(s: string): string {\n return s\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase();\n}\n\nconst defaultFilter: SelectFilter = (item, query) => {\n if (!query) return true;\n const label = normalize(item.label);\n return normalize(query)\n .split(/\\s+/)\n .filter(Boolean)\n .every((kw) => label.includes(kw));\n};\n\n/** Fired when the selection changes. Bubbles; not composed. */\nexport class SelectChangeEvent extends Event {\n /** The selected value (single mode) or values (multiple mode). */\n readonly value: string | string[];\n constructor(value: string | string[]) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\n/** Fired as the user types in the search box. Bubbles; not composed. */\nexport class SelectInputEvent extends Event {\n /** The current query text. */\n readonly value: string;\n constructor(value: string) {\n super('input', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\ninterface SelectEventMap {\n change: SelectChangeEvent;\n input: SelectInputEvent;\n}\n\n/**\n * A button-triggered, searchable select. A trigger shows the current selection,\n * opening a popover with a search box and a `role=\"listbox\"` of options authored\n * as a native `<datalist>` of `<option>` — the same authoring surface as\n * `l-combobox` and the native `.l-select`. Form-associated.\n *\n * @summary A searchable select with a button trigger and a popover listbox.\n *\n * @example\n * ```html\n * <l-select label=\"Country\" name=\"country\" placeholder=\"Select a country…\">\n * <datalist>\n * <option value=\"us\">United States</option>\n * <option value=\"fr\" selected>France</option>\n * </datalist>\n * </l-select>\n * ```\n *\n * @event change - Fired when the selection changes. Bubbles. Not cancelable. Properties: `value: string | string[]`.\n * @event input - Fired as the user types in the search box. Bubbles. Not cancelable. Properties: `value: string` (the query).\n * @event show - Fired before the listbox opens. Cancelable.\n * @event hide - Fired before the listbox closes. Cancelable.\n *\n * @csspart base - The host wrapper.\n * @csspart trigger - The button that opens the listbox.\n * @csspart value - The selection display inside the trigger.\n * @csspart chevron - The trigger chevron.\n * @csspart clear - The clear button.\n * @csspart panel - The floating popover panel.\n * @csspart search - The search input.\n * @csspart listbox - The options container.\n * @csspart option - Each option row.\n * @csspart empty - The \"no results\" message.\n *\n * @cssproperty [--height] - Control height. Defaults to the form-control height.\n * @cssproperty [--border-radius] - Trigger + panel radius.\n * @cssproperty [--background] - Panel background.\n *\n * @customElement l-select\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Select extends LuxenFormAssociatedElement {\n static override styles = [hostStyles, styles];\n\n private _localize = new LocalizeController(this);\n private readonly _listId = uniqueId('select-list');\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._triggerEl,\n getFloatingElement: () => this._panelEl,\n getArrowElement: () => null,\n });\n\n private _nav = new ListboxNavController(this, {\n listboxId: this._listId,\n getCount: () => this._filtered.length,\n isDisabled: (i) => this._filtered[i]?.disabled ?? false,\n isOpen: () => this._open,\n open: () => this.show(),\n getOptionElements: () => this.shadowRoot?.querySelectorAll<HTMLElement>('.option'),\n });\n\n /** Placeholder shown in the trigger when nothing is selected. */\n @property()\n accessor placeholder = '';\n\n /** Accessible label for the trigger. */\n @property()\n accessor label = '';\n\n /** Control size. */\n @property({ reflect: true })\n accessor size: SelectSize = 'md';\n\n /** Show a filter box inside the popover (opt-in, for long lists). */\n @property({ type: Boolean, reflect: true })\n accessor searchable = false;\n\n /** Show a button to clear the value. */\n @property({ type: Boolean, reflect: true, attribute: 'with-clear' })\n accessor withClear = false;\n\n /** Panel placement relative to the trigger. */\n @property()\n accessor placement: Placement = 'bottom-start';\n\n /** Allow selecting multiple values — renders chips and submits one entry per value. */\n @property({ type: Boolean, reflect: true })\n accessor multiple = false;\n\n /** Override the option filter. `(item, query) => boolean`. */\n filter: SelectFilter = defaultFilter;\n\n @state() private accessor _values: string[] = [];\n @state() private accessor _open = false;\n @state() private accessor _query = '';\n @state() private accessor _items: SelectItem[] = [];\n\n private _defaultValues: string[] = [];\n\n /** The selected value (single mode) or array of values (multiple mode). */\n get value(): string | string[] {\n return this.multiple ? [...this._values] : (this._values[0] ?? '');\n }\n set value(v: string | string[]) {\n this._values = Array.isArray(v) ? [...v] : v ? [v] : [];\n }\n\n private get _triggerEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.trigger') ?? null;\n }\n private get _panelEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.panel') ?? null;\n }\n private get _searchEl() {\n return this.shadowRoot?.querySelector<HTMLInputElement>('.search') ?? null;\n }\n\n override get validationTarget() {\n return this._triggerEl ?? undefined;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this._readItems();\n // Seed the initial selection: an explicit `value` attribute wins, else the\n // authored `<option selected>` (declarative pre-selection — markup-driven).\n if (this._values.length === 0) {\n const attr = this.getAttribute('value');\n if (attr) {\n this._values = [attr];\n } else {\n const selected = this._items.filter((i) => i.selected).map((i) => i.value);\n this._values = this.multiple ? selected : selected.slice(0, 1);\n }\n }\n this._defaultValues = [...this._values];\n this._syncForm();\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_values') || changed.has('multiple')) {\n this._syncForm();\n this._updateValidity();\n }\n }\n\n override formResetCallback() {\n this._values = [...this._defaultValues];\n this._query = '';\n this._nav.reset();\n super.formResetCallback();\n this._syncForm();\n }\n\n override formStateRestoreCallback(state: string) {\n this.value = state;\n this._syncForm();\n }\n\n // --- Items (read from the light-DOM <datalist>) ---\n\n private _readItems() {\n const list = this.querySelector('datalist');\n const opts = list ? Array.from(list.options) : [];\n this._items = opts.map((o) => {\n const rich = o.childElementCount > 0;\n const title = o.querySelector(`.${cls('select-item-title')}`)?.textContent?.trim();\n const label = (o.getAttribute('label') || title || o.textContent || o.value).trim();\n return {\n value: o.value,\n label,\n disabled: o.disabled,\n selected: o.hasAttribute('selected'),\n html: rich ? o.innerHTML : undefined,\n };\n });\n }\n\n private _itemFor(value: string): SelectItem | undefined {\n return this._items.find((i) => i.value === value);\n }\n\n private get _filtered(): SelectItem[] {\n return this._items.filter((i) => this.filter(i, this._query));\n }\n\n // --- Validity ---\n\n /** Push the current selection to the form (single value, or one entry per value). */\n private _syncForm() {\n if (this.multiple) {\n const data = new FormData();\n for (const v of this._values) data.append(this.name, v);\n this._internals.setFormValue(data);\n } else {\n this._internals.setFormValue(this._values[0] ?? '');\n }\n }\n\n private _updateValidity() {\n if (this.required && this._values.length === 0) {\n this.setValidity(\n { valueMissing: true },\n this._localize.term('selectOption'),\n this._triggerEl ?? undefined,\n );\n } else {\n this.setValidity({});\n }\n }\n\n // --- Open / close ---\n\n show() {\n if (this._open || this.disabled) return;\n this._readItems();\n if (!this.dispatchEvent(new Event('show', { cancelable: true }))) return;\n this._open = true;\n this._query = '';\n // Pre-activate the (first) chosen option so a screen reader announces it on open.\n if (this._values.length) {\n this._nav.setActive(this._filtered.findIndex((i) => i.value === this._values[0]));\n }\n void this.updateComplete.then(() => {\n const panel = this._panelEl as (HTMLElement & { showPopover?: () => void }) | null;\n panel?.showPopover?.();\n this._floating.startPositioning({\n placement: this.placement,\n distance: 4,\n minWidth: 'trigger',\n });\n // Move focus into the search box (or the listbox when not searchable).\n (this._searchEl ?? this._panelEl?.querySelector<HTMLElement>('.listbox'))?.focus();\n document.addEventListener('pointerdown', this._onDocumentPointerDown, true);\n });\n }\n\n hide({ focusTrigger = true } = {}) {\n if (!this._open) return;\n if (!this.dispatchEvent(new Event('hide', { cancelable: true }))) return;\n this._open = false;\n this._query = '';\n this._nav.reset();\n const panel = this._panelEl as (HTMLElement & { hidePopover?: () => void }) | null;\n if (panel?.matches?.(':popover-open')) panel.hidePopover?.();\n this._floating.stopPositioning();\n document.removeEventListener('pointerdown', this._onDocumentPointerDown, true);\n if (focusTrigger) this._triggerEl?.focus();\n }\n\n private _onDocumentPointerDown = (e: PointerEvent) => {\n // popover=\"manual\" does not light-dismiss; do it ourselves.\n const path = e.composedPath();\n if (path.includes(this)) return;\n this.hasInteracted = true;\n this._updateValidity();\n this.hide({ focusTrigger: false });\n };\n\n // --- Selection ---\n\n /** Single: set the value and close. Multiple: toggle the value and stay open. */\n private _select(item: SelectItem) {\n if (item.disabled) return;\n this.hasInteracted = true;\n if (this.multiple) {\n this._values = this._values.includes(item.value)\n ? this._values.filter((v) => v !== item.value)\n : [...this._values, item.value];\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this._searchEl?.focus();\n } else {\n this._values = [item.value];\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this.hide();\n }\n }\n\n /** Deselect a single value (chip remove, multiple mode). */\n private _removeValue(value: string) {\n this._values = this._values.filter((v) => v !== value);\n this.hasInteracted = true;\n this.dispatchEvent(new SelectChangeEvent(this.value));\n }\n\n private _clear(e?: Event) {\n e?.stopPropagation();\n this._values = [];\n this.hasInteracted = true;\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this._triggerEl?.focus();\n }\n\n // --- Keyboard ---\n\n private _onTriggerKeyDown = (e: KeyboardEvent) => {\n if (this._open) return;\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.show();\n }\n };\n\n private _onPanelKeyDown = (e: KeyboardEvent) => {\n if (this._nav.onKeyDown(e)) return;\n switch (e.key) {\n case 'Enter': {\n e.preventDefault();\n const active = this._filtered[this._nav.activeIndex];\n if (active) this._select(active);\n break;\n }\n case 'Escape':\n e.preventDefault();\n this.hide();\n break;\n case 'Tab':\n this.hide({ focusTrigger: false });\n break;\n default:\n break;\n }\n };\n\n private _onSearchInput = (e: Event) => {\n this._query = (e.target as HTMLInputElement).value;\n this._nav.reset();\n this.dispatchEvent(new SelectInputEvent(this._query));\n };\n\n // --- Render ---\n\n private _highlight(label: string): TemplateResult | string {\n const q = this._query.trim();\n if (!q) return label;\n const idx = normalize(label).indexOf(normalize(q).split(/\\s+/)[0]);\n if (idx < 0) return label;\n return html`${label.slice(0, idx)}<mark>${label.slice(idx, idx + q.length)}</mark>${label.slice(\n idx + q.length,\n )}`;\n }\n\n private _renderSingleValue() {\n const selected = this._itemFor(this._values[0]);\n if (!selected) {\n return html`<span class=\"value-text placeholder\">${this.placeholder}</span>`;\n }\n return selected.html\n ? html`${unsafeHTML(selected.html)}`\n : html`<span class=\"value-text\">${selected.label}</span>`;\n }\n\n private _renderChips() {\n if (this._values.length === 0) {\n return html`<span class=\"value-text placeholder\">${this.placeholder}</span>`;\n }\n // The chip tag is the renameable `l-tag`; it's a Shadow-DOM element so its\n // remove button renders correctly inside this element's shadow root.\n const chip = unsafeStatic(tagName('tag'));\n return this._values.map((value) => {\n const item = this._itemFor(value);\n return staticHtml`<${chip}\n size=\"sm\"\n removable\n part=\"tag\"\n @remove=${(e: Event) => {\n e.preventDefault();\n this._removeValue(value);\n }}\n @click=${(e: Event) => e.stopPropagation()}\n >${item?.label ?? value}</${chip}\n >`;\n });\n }\n\n private _renderTrigger(invalid: boolean) {\n const inner = html`\n <span\n class=\"value\"\n part=\"value\"\n >${this.multiple ? this._renderChips() : this._renderSingleValue()}</span\n >\n <span\n class=\"chevron\"\n aria-hidden=\"true\"\n ></span>\n `;\n const onClick = () => (this._open ? this.hide() : this.show());\n // Multiple mode uses a focusable div (role=\"combobox\") so the chips' remove\n // buttons are valid interactive children — a <button> trigger can't nest them.\n return this.multiple\n ? html`<div\n class=\"trigger\"\n part=\"trigger\"\n role=\"combobox\"\n tabindex=${this.disabled ? -1 : 0}\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-label=${this.label || nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @click=${onClick}\n @keydown=${this._onTriggerKeyDown}\n >\n ${inner}\n </div>`\n : html`<button\n class=\"trigger\"\n part=\"trigger\"\n type=\"button\"\n ?disabled=${this.disabled}\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-label=${this.label || nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @click=${onClick}\n @keydown=${this._onTriggerKeyDown}\n >\n ${inner}\n </button>`;\n }\n\n override render() {\n const filtered = this._filtered;\n const activeId = this._nav.activeDescendant;\n const activeIndex = this._nav.activeIndex;\n const invalid = this.required && this._values.length === 0 && this.hasInteracted;\n const hasValue = this._values.length > 0;\n const status = !this._open\n ? ''\n : filtered.length === 0\n ? this._localize.term('noResults')\n : `${filtered.length} ${this._localize.term('suggestions')}`;\n\n return html`\n <div\n class=\"base\"\n part=\"base\"\n >\n ${this._renderTrigger(invalid)}\n ${this.withClear && hasValue\n ? html`<button\n class=\"clear\"\n part=\"clear\"\n type=\"button\"\n aria-label=${this._localize.term('clear')}\n @click=${this._clear}\n ></button>`\n : nothing}\n\n <div\n class=\"panel\"\n part=\"panel\"\n popover=\"manual\"\n @keydown=${this._onPanelKeyDown}\n >\n ${this.searchable\n ? html`<div class=\"search-row\">\n <input\n class=\"search\"\n part=\"search\"\n type=\"text\"\n role=\"searchbox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n .placeholder=${this._localize.term('search') ?? ''}\n aria-controls=${this._listId}\n aria-activedescendant=${activeId ?? nothing}\n @input=${this._onSearchInput}\n />\n </div>`\n : nothing}\n\n <ul\n class=\"listbox\"\n part=\"listbox\"\n role=\"listbox\"\n id=${this._listId}\n tabindex=${this.searchable ? nothing : '0'}\n aria-label=${this.label || this._localize.term('suggestions')}\n aria-multiselectable=${this.multiple ? 'true' : nothing}\n aria-activedescendant=${!this.searchable ? (activeId ?? nothing) : nothing}\n >\n ${filtered.map(\n (item, i) => html`<li\n class=\"option\"\n part=\"option\"\n id=${this._nav.optionId(i)}\n role=\"option\"\n aria-selected=${this._values.includes(item.value) ? 'true' : 'false'}\n aria-disabled=${item.disabled ? 'true' : nothing}\n data-active=${i === activeIndex ? '' : nothing}\n data-current=${this._values.includes(item.value) ? '' : nothing}\n @pointerdown=${(e: Event) => e.preventDefault()}\n @click=${() => this._select(item)}\n >\n ${item.html\n ? html`<span class=\"option-rich\">${unsafeHTML(item.html)}</span>`\n : html`<span class=\"option-label\">${this._highlight(item.label)}</span>`}\n </li>`,\n )}\n </ul>\n ${filtered.length === 0\n ? html`<div\n class=\"empty\"\n part=\"empty\"\n >\n ${this._localize.term('noResults')}\n </div>`\n : nothing}\n </div>\n\n <div\n class=\"visually-hidden\"\n aria-live=\"polite\"\n >\n ${status}\n </div>\n </div>\n `;\n }\n}\n\n// Typed addEventListener for the colliding `change` / `input` names.\nexport interface Select {\n addEventListener<K extends keyof SelectEventMap>(\n type: K,\n listener: (this: Select, ev: SelectEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener<K extends keyof SelectEventMap>(\n type: K,\n listener: (this: Select, ev: SelectEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"select.js","names":[],"sources":["../../../src/html/elements/select/select.css?inline","../../../src/html/elements/select/select.ts"],"sourcesContent":[":host {\n --height: var(--l-form-control-height);\n --border-radius: var(--l-form-control-border-radius);\n --background: var(--l-color-surface-overlay);\n\n display: block;\n inline-size: 100%;\n}\n\n:host([size='xs']) {\n --height: var(--l-size-control-xs);\n}\n:host([size='sm']) {\n --height: var(--l-size-control-sm);\n}\n:host([size='lg']) {\n --height: var(--l-size-control-lg);\n}\n:host([size='xl']) {\n --height: var(--l-size-control-xl);\n}\n\n:host([disabled]) {\n opacity: 0.5;\n pointer-events: none;\n}\n\n.base {\n position: relative;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆃🆁🅸🅶🅶🅴🆁 (button, opens the listbox popover)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.trigger {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n inline-size: 100%;\n min-block-size: var(--height);\n padding-block: 0.25rem;\n padding-inline: var(--l-form-control-padding-inline);\n border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);\n border-radius: var(--border-radius);\n background-color: var(--l-form-control-background-color);\n color: var(--l-form-control-value-color);\n font: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n/* The form-control inline padding is tuned for text; before a chip row it reads\n as too much, so tighten the start edge in multiple mode (the chips bring their\n own spacing). */\n:host([multiple]) .trigger {\n padding-inline-start: 0.375rem;\n}\n\n@media (hover: hover) {\n .trigger:hover {\n border-color: var(--l-form-control-border-color-hover);\n }\n}\n\n.trigger:focus-visible {\n outline: none;\n border-color: var(--l-focus-ring);\n box-shadow: 0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent);\n}\n\n/* Selection display fills the trigger; the chevron (and overlaid clear) sit at the end. */\n.value {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 0.25rem;\n overflow: hidden;\n}\n\n.value-text {\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* This element's public knobs and the chip's share three names — `--height`,\n `--border-radius`, `--background` — and custom properties inherit straight\n through a shadow boundary. Left alone, every chip in the trigger renders at\n the *select's* height, corner and fill rather than its own: a 36px chip with\n a 6px corner on a white ground, when `size=\"sm\"` asked for a 24px pill on the\n neutral fill. `initial` is the guaranteed-invalid value, so each `var(--x, …)`\n inside the chip falls back to its own default. Matched on the part, not on\n the tag name, so a renamed `elementPrefix` can't miss it. */\n[part='tag'] {\n --height: initial;\n --border-radius: initial;\n --background: initial;\n}\n\n.placeholder {\n color: var(--l-form-control-placeholder-color);\n}\n\n.chevron {\n flex: none;\n inline-size: 1em;\n block-size: 1em;\n background-color: currentColor;\n color: var(--l-form-control-placeholder-color);\n mask: var(--_chevron-icon) center / contain no-repeat;\n transition: rotate 0.15s ease;\n}\n\n.trigger[aria-expanded='true'] .chevron {\n rotate: 180deg;\n}\n\n/* Clear is a SIBLING of the trigger (not a child — no nested buttons), overlaid\n * over the trigger's end, before the chevron. ≥24px hit area (WCAG 2.5.8). */\n.clear {\n position: absolute;\n inset-block: 0;\n inset-inline-end: calc(var(--l-form-control-padding-inline) + 1.25em);\n margin-block: auto;\n inline-size: 1.5rem;\n block-size: 1.5rem;\n border: 0;\n padding: 0;\n cursor: pointer;\n background-color: currentColor;\n color: var(--l-form-control-placeholder-color);\n mask: var(--_x-icon) center / 1em no-repeat;\n}\n\n@media (hover: hover) {\n .clear:hover {\n color: var(--l-form-control-value-color);\n }\n}\n\n.clear:focus-visible {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 2px;\n}\n\n/* Rich content rendered in the trigger mirrors the option rows. */\n.value .l-select-item-media {\n flex: none;\n display: inline-flex;\n align-items: center;\n font-size: 1.25em;\n}\n.value .l-select-item-text {\n display: flex;\n flex-direction: column;\n min-inline-size: 0;\n}\n/* Mirror the native trigger: show only the title, drop the secondary description\n so the selected value stays compact and consistent across options. */\n.value .l-select-item-title {\n font-weight: inherit;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.value .l-select-item-description {\n display: none;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅿🅰🅽🅴🅻 (popover: search + listbox)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.panel {\n inset: auto;\n margin: 0;\n padding: 0;\n border: 1px solid var(--l-color-border-overlay);\n border-radius: var(--border-radius);\n background-color: var(--background);\n color: var(--l-color-text-primary);\n box-shadow: var(--l-shadow-md);\n font-size: 0.875rem;\n line-height: 1.5;\n overflow: hidden;\n opacity: 0;\n scale: 0.98;\n transition:\n opacity 0.12s ease,\n scale 0.12s ease,\n display 0.12s allow-discrete,\n overlay 0.12s allow-discrete;\n}\n\n.panel:popover-open {\n opacity: 1;\n scale: 1;\n}\n\n@starting-style {\n .panel:popover-open {\n opacity: 0;\n scale: 0.98;\n }\n}\n\n.search-row {\n padding: 0.375rem;\n border-block-end: 1px solid var(--l-color-border-overlay);\n}\n\n.search {\n inline-size: 100%;\n block-size: var(--l-size-control-sm);\n padding-inline: 0.5rem;\n border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);\n border-radius: calc(var(--border-radius) - 2px);\n background-color: var(--l-form-control-background-color);\n color: var(--l-form-control-value-color);\n font: inherit;\n}\n\n.search:focus-visible {\n outline: none;\n border-color: var(--l-focus-ring);\n}\n\n.search::placeholder {\n color: var(--l-form-control-placeholder-color);\n}\n\n.listbox {\n margin: 0;\n padding: 0.25rem;\n list-style: none;\n max-block-size: 16rem;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n/* Mirrors the native `.l-select` picker (itself calé on l-dropdown): the\n * checkmark lives in a reserved inline-start column, only the hovered/active row\n * takes the tint, and a selected row carries no background (checkmark only). */\n.option {\n display: flex;\n position: relative;\n align-items: start;\n gap: 0.5rem;\n min-block-size: 36px;\n padding-block: 0.5rem;\n padding-inline: 1.75rem 0.5rem;\n border-radius: 4px;\n color: var(--l-color-text-primary);\n cursor: pointer;\n}\n\n@media (hover: hover) {\n .option:hover {\n background-color: var(--l-color-bg-state-hover);\n }\n}\n\n/* Keyboard-active row (moves with Arrow keys) — same tint as hover. */\n.option[data-active] {\n background-color: var(--l-color-bg-state-hover);\n}\n\n.option[aria-disabled='true'] {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n/* Selected indicator in the reserved inline-start column, pinned to the first\n * line so a wrapping description never shifts it (matches the native picker). */\n.option[data-current]::after {\n content: '';\n position: absolute;\n inset-inline-start: 0.5rem;\n inset-block-start: 0.5rem;\n inline-size: 1rem;\n block-size: 1.3125rem;\n background-color: currentColor;\n mask: var(--_check-icon) center / 1rem no-repeat;\n}\n\n.option-label {\n flex: 1;\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.option-label mark {\n background: transparent;\n color: inherit;\n font-weight: 600;\n}\n\n.option-rich {\n display: flex;\n align-items: start;\n gap: 0.5rem;\n flex: 1;\n min-inline-size: 0;\n}\n\n.option-rich .l-select-item-media {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n inline-size: 1.5rem;\n block-size: 1.5rem;\n border-radius: 4px;\n object-fit: cover;\n font-size: 1.25em;\n}\n\n.option-rich .l-select-item-text {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-inline-size: 0;\n}\n\n.option-rich .l-select-item-title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n}\n\n.option-rich .l-select-item-description {\n color: var(--l-color-text-tertiary);\n font-size: 0.75rem;\n line-height: 1.3;\n white-space: normal;\n text-wrap: pretty;\n}\n\n.empty {\n padding: 0.5rem;\n color: var(--l-color-text-tertiary);\n text-align: center;\n}\n\n.visually-hidden {\n position: absolute;\n inline-size: 1px;\n block-size: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅸🅲🅾🅽🆂\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n:host {\n --_chevron-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\");\n --_x-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E\");\n --_check-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E\");\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel,\n .chevron {\n transition-duration: 0ms;\n }\n}\n","import { html, nothing, unsafeCSS } from 'lit';\nimport type { TemplateResult } from 'lit';\nimport { html as staticHtml, unsafeStatic } from 'lit/static-html.js';\nimport { property, state } from 'lit/decorators.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { LuxenFormAssociatedElement } from '../../shared/luxen-form-associated-element.js';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { ListboxNavController } from '../../shared/controllers/listbox-nav.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { cls, tagName, uniqueId } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './select.css?inline';\n// Registers the chip element used to render selected values in multiple mode.\nimport '../tag/index.js';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type SelectSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\n/** A single option read from the `<datalist>`. */\nexport interface SelectItem {\n value: string;\n /** Plain text used for filtering and the trigger display. */\n label: string;\n disabled: boolean;\n /** Authored `selected` attribute — seeds the initial value. */\n selected: boolean;\n /** Rich inner markup (from a non-empty `<option>`), projected into the row. */\n html?: string;\n}\n\n/** Default filter: accent/case-insensitive, every space-separated keyword must match. */\nexport type SelectFilter = (item: SelectItem, query: string) => boolean;\n\nfunction normalize(s: string): string {\n return s\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase();\n}\n\nconst defaultFilter: SelectFilter = (item, query) => {\n if (!query) return true;\n const label = normalize(item.label);\n return normalize(query)\n .split(/\\s+/)\n .filter(Boolean)\n .every((kw) => label.includes(kw));\n};\n\n/** Fired when the selection changes. Bubbles; not composed. */\nexport class SelectChangeEvent extends Event {\n /** The selected value (single mode) or values (multiple mode). */\n readonly value: string | string[];\n constructor(value: string | string[]) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\n/** Fired as the user types in the search box. Bubbles; not composed. */\nexport class SelectInputEvent extends Event {\n /** The current query text. */\n readonly value: string;\n constructor(value: string) {\n super('input', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\ninterface SelectEventMap {\n change: SelectChangeEvent;\n input: SelectInputEvent;\n}\n\n/**\n * A button-triggered, searchable select. A trigger shows the current selection,\n * opening a popover with a search box and a `role=\"listbox\"` of options authored\n * as a native `<datalist>` of `<option>` — the same authoring surface as\n * `l-combobox` and the native `.l-select`. Form-associated.\n *\n * @summary A searchable select with a button trigger and a popover listbox.\n *\n * @example\n * ```html\n * <l-select label=\"Country\" name=\"country\" placeholder=\"Select a country…\">\n * <datalist>\n * <option value=\"us\">United States</option>\n * <option value=\"fr\" selected>France</option>\n * </datalist>\n * </l-select>\n * ```\n *\n * @event change - Fired when the selection changes. Bubbles. Not cancelable. Properties: `value: string | string[]`.\n * @event input - Fired as the user types in the search box. Bubbles. Not cancelable. Properties: `value: string` (the query).\n * @event show - Fired before the listbox opens. Cancelable.\n * @event hide - Fired before the listbox closes. Cancelable.\n *\n * @csspart base - The host wrapper.\n * @csspart trigger - The button that opens the listbox.\n * @csspart value - The selection display inside the trigger.\n * @csspart chevron - The trigger chevron.\n * @csspart clear - The clear button.\n * @csspart panel - The floating popover panel.\n * @csspart search - The search input.\n * @csspart listbox - The options container.\n * @csspart option - Each option row.\n * @csspart empty - The \"no results\" message.\n *\n * @cssproperty [--height] - Control height. Defaults to the form-control height.\n * @cssproperty [--border-radius] - Trigger + panel radius.\n * @cssproperty [--background] - Panel background.\n *\n * @customElement l-select\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Select extends LuxenFormAssociatedElement {\n static override styles = [hostStyles, styles];\n\n private _localize = new LocalizeController(this);\n private readonly _listId = uniqueId('select-list');\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._triggerEl,\n getFloatingElement: () => this._panelEl,\n getArrowElement: () => null,\n });\n\n private _nav = new ListboxNavController(this, {\n listboxId: this._listId,\n getCount: () => this._filtered.length,\n isDisabled: (i) => this._filtered[i]?.disabled ?? false,\n isOpen: () => this._open,\n open: () => this.show(),\n getOptionElements: () => this.shadowRoot?.querySelectorAll<HTMLElement>('.option'),\n });\n\n /** Placeholder shown in the trigger when nothing is selected. */\n @property()\n accessor placeholder = '';\n\n /** Accessible label for the trigger. */\n @property()\n accessor label = '';\n\n /** Control size. */\n @property({ reflect: true })\n accessor size: SelectSize = 'md';\n\n /** Show a filter box inside the popover (opt-in, for long lists). */\n @property({ type: Boolean, reflect: true })\n accessor searchable = false;\n\n /** Show a button to clear the value. */\n @property({ type: Boolean, reflect: true, attribute: 'with-clear' })\n accessor withClear = false;\n\n /** Panel placement relative to the trigger. */\n @property()\n accessor placement: Placement = 'bottom-start';\n\n /** Allow selecting multiple values — renders chips and submits one entry per value. */\n @property({ type: Boolean, reflect: true })\n accessor multiple = false;\n\n /** Override the option filter. `(item, query) => boolean`. */\n filter: SelectFilter = defaultFilter;\n\n @state() private accessor _values: string[] = [];\n @state() private accessor _open = false;\n @state() private accessor _query = '';\n @state() private accessor _items: SelectItem[] = [];\n\n private _defaultValues: string[] = [];\n\n /** The selected value (single mode) or array of values (multiple mode). */\n get value(): string | string[] {\n return this.multiple ? [...this._values] : (this._values[0] ?? '');\n }\n set value(v: string | string[]) {\n this._values = Array.isArray(v) ? [...v] : v ? [v] : [];\n }\n\n private get _triggerEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.trigger') ?? null;\n }\n private get _panelEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.panel') ?? null;\n }\n private get _searchEl() {\n return this.shadowRoot?.querySelector<HTMLInputElement>('.search') ?? null;\n }\n\n override get validationTarget() {\n return this._triggerEl ?? undefined;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this._readItems();\n // Seed the initial selection: an explicit `value` attribute wins, else the\n // authored `<option selected>` (declarative pre-selection — markup-driven).\n if (this._values.length === 0) {\n const attr = this.getAttribute('value');\n if (attr) {\n this._values = [attr];\n } else {\n const selected = this._items.filter((i) => i.selected).map((i) => i.value);\n this._values = this.multiple ? selected : selected.slice(0, 1);\n }\n }\n this._defaultValues = [...this._values];\n this._syncForm();\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_values') || changed.has('multiple')) {\n this._syncForm();\n this._updateValidity();\n }\n }\n\n override formResetCallback() {\n this._values = [...this._defaultValues];\n this._query = '';\n this._nav.reset();\n super.formResetCallback();\n this._syncForm();\n }\n\n override formStateRestoreCallback(state: string) {\n this.value = state;\n this._syncForm();\n }\n\n // --- Items (read from the light-DOM <datalist>) ---\n\n private _readItems() {\n const list = this.querySelector('datalist');\n const opts = list ? Array.from(list.options) : [];\n this._items = opts.map((o) => {\n const rich = o.childElementCount > 0;\n const title = o.querySelector(`.${cls('select-item-title')}`)?.textContent?.trim();\n const label = (o.getAttribute('label') || title || o.textContent || o.value).trim();\n return {\n value: o.value,\n label,\n disabled: o.disabled,\n selected: o.hasAttribute('selected'),\n html: rich ? o.innerHTML : undefined,\n };\n });\n }\n\n private _itemFor(value: string): SelectItem | undefined {\n return this._items.find((i) => i.value === value);\n }\n\n private get _filtered(): SelectItem[] {\n return this._items.filter((i) => this.filter(i, this._query));\n }\n\n // --- Validity ---\n\n /** Push the current selection to the form (single value, or one entry per value). */\n private _syncForm() {\n if (this.multiple) {\n const data = new FormData();\n for (const v of this._values) data.append(this.name, v);\n this._internals.setFormValue(data);\n } else {\n this._internals.setFormValue(this._values[0] ?? '');\n }\n }\n\n private _updateValidity() {\n if (this.required && this._values.length === 0) {\n this.setValidity(\n { valueMissing: true },\n this._localize.term('selectOption'),\n this._triggerEl ?? undefined,\n );\n } else {\n this.setValidity({});\n }\n }\n\n // --- Open / close ---\n\n show() {\n if (this._open || this.disabled) return;\n this._readItems();\n if (!this.dispatchEvent(new Event('show', { cancelable: true }))) return;\n this._open = true;\n this._query = '';\n // Pre-activate the (first) chosen option so a screen reader announces it on open.\n if (this._values.length) {\n this._nav.setActive(this._filtered.findIndex((i) => i.value === this._values[0]));\n }\n void this.updateComplete.then(() => {\n const panel = this._panelEl as (HTMLElement & { showPopover?: () => void }) | null;\n panel?.showPopover?.();\n this._floating.startPositioning({\n placement: this.placement,\n distance: 4,\n minWidth: 'trigger',\n });\n // Move focus into the search box (or the listbox when not searchable).\n (this._searchEl ?? this._panelEl?.querySelector<HTMLElement>('.listbox'))?.focus();\n document.addEventListener('pointerdown', this._onDocumentPointerDown, true);\n });\n }\n\n hide({ focusTrigger = true } = {}) {\n if (!this._open) return;\n if (!this.dispatchEvent(new Event('hide', { cancelable: true }))) return;\n this._open = false;\n this._query = '';\n this._nav.reset();\n const panel = this._panelEl as (HTMLElement & { hidePopover?: () => void }) | null;\n if (panel?.matches?.(':popover-open')) panel.hidePopover?.();\n this._floating.stopPositioning();\n document.removeEventListener('pointerdown', this._onDocumentPointerDown, true);\n if (focusTrigger) this._triggerEl?.focus();\n }\n\n private _onDocumentPointerDown = (e: PointerEvent) => {\n // popover=\"manual\" does not light-dismiss; do it ourselves.\n const path = e.composedPath();\n if (path.includes(this)) return;\n this.hasInteracted = true;\n this._updateValidity();\n this.hide({ focusTrigger: false });\n };\n\n // --- Selection ---\n\n /** Single: set the value and close. Multiple: toggle the value and stay open. */\n private _select(item: SelectItem) {\n if (item.disabled) return;\n this.hasInteracted = true;\n if (this.multiple) {\n this._values = this._values.includes(item.value)\n ? this._values.filter((v) => v !== item.value)\n : [...this._values, item.value];\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this._searchEl?.focus();\n } else {\n this._values = [item.value];\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this.hide();\n }\n }\n\n /** Deselect a single value (chip remove, multiple mode). */\n private _removeValue(value: string) {\n this._values = this._values.filter((v) => v !== value);\n this.hasInteracted = true;\n this.dispatchEvent(new SelectChangeEvent(this.value));\n }\n\n private _clear(e?: Event) {\n e?.stopPropagation();\n this._values = [];\n this.hasInteracted = true;\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this._triggerEl?.focus();\n }\n\n // --- Keyboard ---\n\n private _onTriggerKeyDown = (e: KeyboardEvent) => {\n if (this._open) return;\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.show();\n }\n };\n\n private _onPanelKeyDown = (e: KeyboardEvent) => {\n if (this._nav.onKeyDown(e)) return;\n switch (e.key) {\n case 'Enter': {\n e.preventDefault();\n const active = this._filtered[this._nav.activeIndex];\n if (active) this._select(active);\n break;\n }\n case 'Escape':\n e.preventDefault();\n this.hide();\n break;\n case 'Tab':\n this.hide({ focusTrigger: false });\n break;\n default:\n break;\n }\n };\n\n private _onSearchInput = (e: Event) => {\n this._query = (e.target as HTMLInputElement).value;\n this._nav.reset();\n this.dispatchEvent(new SelectInputEvent(this._query));\n };\n\n // --- Render ---\n\n private _highlight(label: string): TemplateResult | string {\n const q = this._query.trim();\n if (!q) return label;\n const idx = normalize(label).indexOf(normalize(q).split(/\\s+/)[0]);\n if (idx < 0) return label;\n return html`${label.slice(0, idx)}<mark>${label.slice(idx, idx + q.length)}</mark>${label.slice(\n idx + q.length,\n )}`;\n }\n\n private _renderSingleValue() {\n const selected = this._itemFor(this._values[0]);\n if (!selected) {\n return html`<span class=\"value-text placeholder\">${this.placeholder}</span>`;\n }\n return selected.html\n ? html`${unsafeHTML(selected.html)}`\n : html`<span class=\"value-text\">${selected.label}</span>`;\n }\n\n private _renderChips() {\n if (this._values.length === 0) {\n return html`<span class=\"value-text placeholder\">${this.placeholder}</span>`;\n }\n // The chip tag is the renameable `l-tag`; it's a Shadow-DOM element so its\n // remove button renders correctly inside this element's shadow root.\n const chip = unsafeStatic(tagName('tag'));\n return this._values.map((value) => {\n const item = this._itemFor(value);\n return staticHtml`<${chip}\n size=\"sm\"\n removable\n part=\"tag\"\n @remove=${(e: Event) => {\n e.preventDefault();\n this._removeValue(value);\n }}\n @click=${(e: Event) => e.stopPropagation()}\n >${item?.label ?? value}</${chip}\n >`;\n });\n }\n\n private _renderTrigger(invalid: boolean) {\n const inner = html`\n <span\n class=\"value\"\n part=\"value\"\n >${this.multiple ? this._renderChips() : this._renderSingleValue()}</span\n >\n <span\n class=\"chevron\"\n aria-hidden=\"true\"\n ></span>\n `;\n const onClick = () => (this._open ? this.hide() : this.show());\n // Multiple mode uses a focusable div (role=\"combobox\") so the chips' remove\n // buttons are valid interactive children — a <button> trigger can't nest them.\n return this.multiple\n ? html`<div\n class=\"trigger\"\n part=\"trigger\"\n role=\"combobox\"\n tabindex=${this.disabled ? -1 : 0}\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-label=${this.label || nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @click=${onClick}\n @keydown=${this._onTriggerKeyDown}\n >\n ${inner}\n </div>`\n : html`<button\n class=\"trigger\"\n part=\"trigger\"\n type=\"button\"\n ?disabled=${this.disabled}\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-label=${this.label || nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @click=${onClick}\n @keydown=${this._onTriggerKeyDown}\n >\n ${inner}\n </button>`;\n }\n\n override render() {\n const filtered = this._filtered;\n const activeId = this._nav.activeDescendant;\n const activeIndex = this._nav.activeIndex;\n const invalid = this.required && this._values.length === 0 && this.hasInteracted;\n const hasValue = this._values.length > 0;\n const status = !this._open\n ? ''\n : filtered.length === 0\n ? this._localize.term('noResults')\n : `${filtered.length} ${this._localize.term('suggestions')}`;\n\n return html`\n <div\n class=\"base\"\n part=\"base\"\n >\n ${this._renderTrigger(invalid)}\n ${this.withClear && hasValue\n ? html`<button\n class=\"clear\"\n part=\"clear\"\n type=\"button\"\n aria-label=${this._localize.term('clear')}\n @click=${this._clear}\n ></button>`\n : nothing}\n\n <div\n class=\"panel\"\n part=\"panel\"\n popover=\"manual\"\n @keydown=${this._onPanelKeyDown}\n >\n ${this.searchable\n ? html`<div class=\"search-row\">\n <input\n class=\"search\"\n part=\"search\"\n type=\"text\"\n role=\"searchbox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n .placeholder=${this._localize.term('search') ?? ''}\n aria-controls=${this._listId}\n aria-activedescendant=${activeId ?? nothing}\n @input=${this._onSearchInput}\n />\n </div>`\n : nothing}\n\n <ul\n class=\"listbox\"\n part=\"listbox\"\n role=\"listbox\"\n id=${this._listId}\n tabindex=${this.searchable ? nothing : '0'}\n aria-label=${this.label || this._localize.term('suggestions')}\n aria-multiselectable=${this.multiple ? 'true' : nothing}\n aria-activedescendant=${!this.searchable ? (activeId ?? nothing) : nothing}\n >\n ${filtered.map(\n (item, i) => html`<li\n class=\"option\"\n part=\"option\"\n id=${this._nav.optionId(i)}\n role=\"option\"\n aria-selected=${this._values.includes(item.value) ? 'true' : 'false'}\n aria-disabled=${item.disabled ? 'true' : nothing}\n data-active=${i === activeIndex ? '' : nothing}\n data-current=${this._values.includes(item.value) ? '' : nothing}\n @pointerdown=${(e: Event) => e.preventDefault()}\n @click=${() => this._select(item)}\n >\n ${item.html\n ? html`<span class=\"option-rich\">${unsafeHTML(item.html)}</span>`\n : html`<span class=\"option-label\">${this._highlight(item.label)}</span>`}\n </li>`,\n )}\n </ul>\n ${filtered.length === 0\n ? html`<div\n class=\"empty\"\n part=\"empty\"\n >\n ${this._localize.term('noResults')}\n </div>`\n : nothing}\n </div>\n\n <div\n class=\"visually-hidden\"\n aria-live=\"polite\"\n >\n ${status}\n </div>\n </div>\n `;\n }\n}\n\n// Typed addEventListener for the colliding `change` / `input` names.\nexport interface Select {\n addEventListener<K extends keyof SelectEventMap>(\n type: K,\n listener: (this: Select, ev: SelectEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener<K extends keyof SelectEventMap>(\n type: K,\n listener: (this: Select, ev: SelectEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}
|
|
@@ -55,7 +55,7 @@ declare global {
|
|
|
55
55
|
* @csspart content - The label wrapper.
|
|
56
56
|
* @csspart toggle - The toggle button rendered by `selectable` (not with `control="checkbox"`).
|
|
57
57
|
* @csspart checkbox - The native checkbox rendered by `control="checkbox"`.
|
|
58
|
-
* @csspart remove - The remove button.
|
|
58
|
+
* @csspart remove - The remove button — a full-height segment at the end of the chip.
|
|
59
59
|
*
|
|
60
60
|
* @cssproperty [--border-radius] - Corner radius. Defaults to a full pill.
|
|
61
61
|
* @cssproperty [--height] - Chip height. Defaults to the `size` step (a selectable chip is taller, to keep a comfortable target).
|
|
@@ -63,8 +63,10 @@ declare global {
|
|
|
63
63
|
* @cssproperty [--padding-inline] - Horizontal padding. Defaults to the `size` step.
|
|
64
64
|
* @cssproperty [--background] - Chip background.
|
|
65
65
|
* @cssproperty [--color] - Text color.
|
|
66
|
+
* @cssproperty [--border-color] - Chip border, and the rule that splits off the remove button. Defaults to a tint of `--color`; setting it pins the line in every state, so a token-based theme keeps its own value on hover.
|
|
66
67
|
* @cssproperty [--selected-color] - Text, border, and checkbox accent when selected. Defaults to the library's form-control accent, lightened in dark mode.
|
|
67
68
|
* @cssproperty [--selected-background] - Chip background when selected. Defaults to a tint of `--selected-color`.
|
|
69
|
+
* @cssproperty [--selected-border-color] - Chip border when selected. Defaults to a 45% tint of `--selected-color`. Selection replaces the line the same way it replaces the fill and the ink, so a chip themed with `--border-color` still gets its emphasis step; set this to theme that step too.
|
|
68
70
|
*
|
|
69
71
|
* @customElement l-tag
|
|
70
72
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../src/html/elements/tag/tag.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAS7D,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AAE7C;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,KAAK;;CAIxC;AAED,uEAAuE;AACvE,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;gBACf,QAAQ,EAAE,OAAO;CAI9B;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,2BAA2B;QACnC,MAAM,EAAE,cAAc,CAAC;KACxB;CACF;AAED
|
|
1
|
+
{"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../src/html/elements/tag/tag.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAS7D,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AAE7C;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,KAAK;;CAIxC;AAED,uEAAuE;AACvE,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;gBACf,QAAQ,EAAE,OAAO;CAI9B;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,2BAA2B;QACnC,MAAM,EAAE,cAAc,CAAC;KACxB;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,qBAAa,GAAI,SAAQ,YAAY;IACnC,OAAgB,MAAM,4BAA4C;IAElE,OAAO,CAAC,SAAS,CAAgC;IAEjD,+CAA+C;IAE/C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAQ;IAE9B,kEAAkE;IAElE,QAAQ,CAAC,SAAS,UAAS;IAE3B,oEAAoE;IAEpE,QAAQ,CAAC,UAAU,UAAS;IAE5B,4EAA4E;IAE5E,QAAQ,CAAC,QAAQ,UAAS;IAE1B;;;;OAIG;IAEH,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAU;IAEtC,kEAAkE;IAElE,QAAQ,CAAC,QAAQ,UAAS;;IASjB,UAAU;IAOV,OAAO;IAWhB,OAAO,CAAC,cAAc,CAKpB;IAEF,OAAO,CAAC,UAAU,CAMhB;IAEF,OAAO,CAAC,OAAO,CAKb;IAEF;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,YAAY,CAMlB;IAEF,OAAO,CAAC,iBAAiB,CAGvB;IAEF,OAAO,CAAC,MAAM;IAad,OAAO,CAAC,aAAa;IAaZ,MAAM;CAqDhB;AAQD,MAAM,WAAW,GAAG;IAClB,gBAAgB,CAAC,CAAC,SAAS,MAAM,WAAW,EAC1C,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,EACjD,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;IACR,gBAAgB,CACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;IACR,mBAAmB,CAAC,CAAC,SAAS,MAAM,WAAW,EAC7C,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,EACjD,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GACvC,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GACvC,IAAI,CAAC;CACT"}
|
package/cdn/elements/tag/tag.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{cls as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,t as r}from"../../chunks/decorate.js";import{a as i,t as a}from"../../chunks/lit-html.js";import{LuxenElement as o}from"../../shared/luxen-element.js";import{LocalizeController as s}from"../../shared/localize.js";import c from"../../shared/styles/host.styles.js";import l from"../../shared/styles/checkbox-appearance.styles.js";var u=t(`@layer components{:host{--_height:var(--height,1.5rem);--_font-size:var(--font-size,var(--l-text-xs));--_padding-inline:var(--padding-inline,.5rem);--_gap:.25rem;--_background:var(--background,var(--l-color-bg-fill-neutral-subtle));--_color:var(--color,var(--l-color-text-neutral));--_border-strength:14
|
|
1
|
+
import{cls as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,t as r}from"../../chunks/decorate.js";import{a as i,t as a}from"../../chunks/lit-html.js";import{LuxenElement as o}from"../../shared/luxen-element.js";import{LocalizeController as s}from"../../shared/localize.js";import c from"../../shared/styles/host.styles.js";import l from"../../shared/styles/checkbox-appearance.styles.js";var u=t(`@layer components{:host{--_height:var(--height,1.5rem);--_font-size:var(--font-size,var(--l-text-xs));--_padding-inline:var(--padding-inline,.5rem);--_gap:.25rem;--_background:var(--background,var(--l-color-bg-fill-neutral-subtle));--_color:var(--color,var(--l-color-text-neutral));--_border-strength:14%;--_border-color:var(--border-color,color-mix(in oklab, currentColor var(--_border-strength), transparent));--_border-color-hover:var(--border-color,color-mix(in oklab, currentColor calc(var(--_border-strength) + 12%), transparent));--_border-radius:var(--border-radius,var(--l-radius-full));--_remove-glyph:.75rem;vertical-align:middle;max-inline-size:100%;display:inline-flex}:host([size=sm]){--_padding-inline:var(--padding-inline,.375rem);--_gap:.125rem}:host([size=lg]){--_height:var(--height,1.75rem);--_font-size:var(--font-size,var(--l-text-sm));--_padding-inline:var(--padding-inline,.625rem);--_remove-glyph:.875rem}:host([disabled]) .base{cursor:not-allowed}.base{align-items:center;gap:var(--_gap);min-block-size:var(--_height);padding-inline:var(--_padding-inline);border:1px solid var(--_border-color);border-radius:var(--_border-radius);background-color:var(--_background);color:var(--_color);font-family:var(--l-font-neo-grotesque);font-size:var(--_font-size);font-weight:var(--l-font-weight-medium);min-inline-size:0;line-height:1;transition:background-color .12s,border-color .12s;display:inline-flex}:host([removable]) .base{min-block-size:max(1.5rem, var(--_height))}@media (hover:hover){:host([selectable]:hover:not([disabled])) .base{background-color:color-mix(in oklab, var(--_background), currentColor 7%);border-color:var(--_border-color-hover)}}.content{overflow-clip-margin:.2em;text-overflow:ellipsis;white-space:nowrap;align-items:center;min-inline-size:0;display:inline-flex;overflow:clip}::slotted([slot=prefix]){flex:none;display:inline-flex}::slotted([slot=suffix]){font-weight:var(--l-font-weight-normal);font-variant-numeric:tabular-nums;flex:none}:host([selectable]){--_height:var(--height,1.75rem);--_accent:var(--l-form-control-activated-color);--_selected-color:var(--selected-color,var(--lightningcss-light,color-mix(in oklab, var(--_accent), black 14%))var(--lightningcss-dark,color-mix(in oklab, var(--_accent), white 45%)));--_selected-background:var(--selected-background,color-mix(in oklab, var(--_selected-color) 10%, var(--l-color-surface)));--_selected-border-color:var(--selected-border-color,color-mix(in oklab, var(--_selected-color) 45%, transparent))}:host([selectable][size=lg]){--_height:var(--height,2rem)}:host([selectable][selected]){--_background:var(--_selected-background);--_color:var(--_selected-color);--_border-color:var(--_selected-border-color);--_border-color-hover:var(--selected-border-color,color-mix(in oklab, var(--_selected-color) 57%, transparent))}:host([selectable]) .base{cursor:pointer;-webkit-user-select:none;user-select:none}:host([selectable][disabled]) .base{cursor:not-allowed}.toggle{align-self:stretch;align-items:center;gap:var(--_gap);min-inline-size:0;margin-inline:calc(var(--_padding-inline) * -1);padding-inline:var(--_padding-inline);color:inherit;font:inherit;text-align:start;cursor:inherit;background:0 0;border:0;flex:auto;display:inline-flex}:host([removable]) .toggle{margin-inline-end:0;padding-inline-end:0}:host([selectable]) .toggle:focus-visible,:host([selectable]) .checkbox:focus-visible{outline:none}.base:has(:is(.toggle,.checkbox):focus-visible){outline:2px solid var(--l-focus-ring);outline-offset:2px}.checkbox{--accent:var(--selected-color,var(--l-form-control-activated-color));--_halo:var(--l-form-control-border-color-hover);font:inherit;flex:none;transition-property:background-color,border-color,box-shadow}.checkbox:checked,.checkbox:indeterminate{--_halo:var(--accent)}@media (hover:hover){:host([selectable]:not([disabled])) .base:hover .checkbox:not(:disabled){box-shadow:0 0 0 calc(var(--size) * var(--l-form-control-toggle-halo-ratio)) color-mix(in oklab, var(--_halo), transparent 84%)}}@media (forced-colors:active){:host([selectable][selected]) .base{border-color:highlight}}.remove{inline-size:max(1.5rem, var(--_height));margin-block:-1px;margin-inline:calc(var(--_padding-inline) - var(--_gap)) calc(var(--_padding-inline) * -1 - 1px);border:1px solid #0000;border-inline-start-color:var(--_border-color);color:inherit;cursor:pointer;--_press-scale:.86;background-color:#0000;background-clip:padding-box;border-start-end-radius:var(--_border-radius);border-end-end-radius:var(--_border-radius);flex:none;align-self:stretch;place-items:center;padding:0;transition:background-color .12s;display:grid}.remove:after{content:"";inline-size:var(--_remove-glyph);block-size:var(--_remove-glyph);background-color:currentColor;transition:scale .12s;-webkit-mask:url("data:image/svg+xml;utf8,<svg xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 24 24\\"><path d=\\"M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z\\" /></svg>") 50%/contain no-repeat;mask:url("data:image/svg+xml;utf8,<svg xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 24 24\\"><path d=\\"M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z\\" /></svg>") 50%/contain no-repeat}@media (hover:hover){.remove:hover:not(:disabled){background-color:color-mix(in oklab, transparent, currentColor 12%)}}.remove:active:not(:disabled){background-color:color-mix(in oklab, transparent, currentColor 22%)}.remove:active:not(:disabled):after{scale:var(--_press-scale)}.remove:focus-visible{outline:2px solid var(--l-focus-ring);outline-offset:-2px}.remove:disabled{cursor:not-allowed;opacity:.4}@media (forced-colors:active){.remove:after{background-color:buttontext}}@media (prefers-reduced-motion:reduce){.base,.checkbox,.remove,.remove:after{transition-duration:0s}.remove{--_press-scale:1}}}`),d=class extends Event{constructor(){super(`remove`,{bubbles:!1,composed:!1,cancelable:!0})}},f=class extends Event{constructor(e){super(`change`,{bubbles:!0,composed:!1,cancelable:!1}),this.selected=e}},p=class extends o{static{this.styles=[c,l,u]}#e;get size(){return this.#e}set size(e){this.#e=e}#t;get removable(){return this.#t}set removable(e){this.#t=e}#n;get selectable(){return this.#n}set selectable(e){this.#n=e}#r;get selected(){return this.#r}set selected(e){this.#r=e}#i;get control(){return this.#i}set control(e){this.#i=e}#a;get disabled(){return this.#a}set disabled(e){this.#a=e}constructor(){super(),this._localize=new s(this),this.#e=`md`,this.#t=!1,this.#n=!1,this.#r=!1,this.#i=`none`,this.#a=!1,this._requestRemove=()=>{if(this.disabled)return;let e=new d;this.dispatchEvent(e),e.defaultPrevented||this.remove()},this._onKeyDown=e=>{!this.removable||this.disabled||(e.key===`Backspace`||e.key===`Delete`)&&(e.preventDefault(),this._requestRemove())},this._toggle=()=>{this.disabled||(this.selected=!this.selected,this.dispatchEvent(new f(this.selected)))},this._onHostClick=e=>{!this.selectable||this.disabled||e.composedPath()[0]===this&&this._toggle()},this._onCheckboxChange=e=>{this.selected=e.target.checked,this.dispatchEvent(new f(this.selected))},this.addEventListener(`click`,this._onHostClick)}willUpdate(){this.control===`checkbox`&&(this.selectable=!0)}updated(){let e=this.renderRoot.querySelector(`.checkbox`);e&&(e.checked=this.selected)}_label(){return i`
|
|
2
2
|
<slot name="prefix"></slot>
|
|
3
3
|
<span
|
|
4
4
|
class="content"
|