luxen-ui 0.21.2 → 0.21.4
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/cdn/custom-elements.json +296 -296
- package/cdn/elements/avatar/avatar.js +1 -1
- package/cdn/elements/avatar/avatar.js.map +1 -1
- package/cdn/elements/combobox/combobox.d.ts +15 -0
- package/cdn/elements/combobox/combobox.d.ts.map +1 -1
- package/cdn/elements/combobox/combobox.js +2 -2
- package/cdn/elements/combobox/combobox.js.map +1 -1
- package/cdn/elements/dialog/dialog.styles.js +1 -1
- package/cdn/elements/dialog/dialog.styles.js.map +1 -1
- package/cdn/elements/dropdown-label/dropdown-label.js +1 -1
- package/cdn/elements/dropdown-label/dropdown-label.js.map +1 -1
- package/cdn/elements/select/select.d.ts +10 -0
- package/cdn/elements/select/select.d.ts.map +1 -1
- package/cdn/elements/select/select.js +2 -2
- package/cdn/elements/select/select.js.map +1 -1
- package/cdn/elements/stories-viewer/stories-viewer.js +1 -1
- package/cdn/elements/stories-viewer/stories-viewer.js.map +1 -1
- package/cdn/shared/controllers/datalist-observer.d.ts +42 -0
- package/cdn/shared/controllers/datalist-observer.d.ts.map +1 -0
- package/cdn/shared/controllers/datalist-observer.js +2 -0
- package/cdn/shared/controllers/datalist-observer.js.map +1 -0
- package/cdn/standalone.css +9 -9
- package/cdn/styles/elements/disclosure.css +1 -1
- package/cdn/styles/elements/divider.css +1 -1
- package/cdn/styles/elements/kbd.css +1 -1
- package/cdn/styles/elements/prose-editor.css +1 -1
- package/cdn/styles/elements/segmented-control.css +1 -1
- package/cdn/styles/elements/select.css +1 -1
- package/cdn/styles/elements/story.css +1 -1
- package/cdn/styles/elements/tabs/enclosed.css +1 -1
- package/cdn/styles/elements/tabs/line.css +1 -1
- package/dist/css/elements/disclosure.css +1 -1
- package/dist/css/elements/divider.css +1 -1
- package/dist/css/elements/kbd.css +1 -1
- package/dist/css/elements/prose-editor.css +1 -1
- package/dist/css/elements/segmented-control.css +1 -1
- package/dist/css/elements/select.css +1 -1
- package/dist/css/elements/story.css +1 -1
- package/dist/css/elements/tabs/enclosed.css +1 -1
- package/dist/css/elements/tabs/line.css +1 -1
- package/dist/custom-elements.json +296 -296
- package/dist/elements/avatar/avatar.css +2 -2
- package/dist/elements/combobox/combobox.css +1 -1
- package/dist/elements/combobox/combobox.d.ts +15 -0
- package/dist/elements/combobox/combobox.d.ts.map +1 -1
- package/dist/elements/combobox/combobox.js +33 -1
- package/dist/elements/dialog/dialog.css +1 -1
- package/dist/elements/dropdown-label/dropdown-label.css +1 -1
- package/dist/elements/select/select.css +2 -2
- package/dist/elements/select/select.d.ts +10 -0
- package/dist/elements/select/select.d.ts.map +1 -1
- package/dist/elements/select/select.js +19 -0
- package/dist/elements/stories-viewer/stories-viewer.css +1 -1
- package/dist/metadata/index.json +1 -1
- package/dist/shared/controllers/datalist-observer.d.ts +42 -0
- package/dist/shared/controllers/datalist-observer.d.ts.map +1 -0
- package/dist/shared/controllers/datalist-observer.js +79 -0
- package/dist/templates/elements/combobox.md +11 -0
- package/dist/templates/elements/select.md +11 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.js","names":[],"sources":["../../../src/html/elements/avatar/avatar.css?inline","../../../src/html/elements/avatar/avatar.ts"],"sourcesContent":[":host {\n --size: 40px;\n --_font-size: calc(var(--size) * 0.4);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n width: var(--size);\n height: var(--size);\n border-radius: calc(var(--size) * 0.15);\n isolation: isolate;\n vertical-align: middle;\n flex-shrink: 0;\n color: var(--l-color-text-secondary);\n}\n\n:host([size='xs']) {\n --size: 24px;\n --_font-size: 0.75rem;\n}\n:host([size='sm']) {\n --size: 32px;\n --_font-size: 0.875rem;\n}\n:host([size='lg']) {\n --size: 48px;\n --_font-size: 1.25rem;\n}\n:host([size='xl']) {\n --size: 56px;\n --_font-size: 1.25rem;\n}\n\nimg {\n width: 100%;\n height: 100%;\n object-fit: cover;\n border-radius: inherit;\n}\n\n.initials {\n margin: auto;\n}\n\n.icon {\n width: 60%;\n height: 60%;\n color: currentColor;\n}\n\n.badge {\n position: absolute;\n bottom: 0;\n right: 0;\n z-index: 1;\n height: 12px;\n min-width: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding-inline: 2px;\n border-radius: 3px;\n box-shadow: 0 0 0 2px var(--l-color-surface);\n background-color: var(--l-color-surface);\n font-size: 0.625rem;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n color: var(--l-color-text-primary);\n overflow: hidden;\n}\n\n/*\n * Inset ring via ::after so it renders on top of the <img>.\n * A box-shadow on :host would be covered by the image since\n * it fills the entire element with object-fit: cover.\n */\n.base::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n box-shadow: inset 0 0 0 1px light-dark(oklch(0 0 0 / 7%), oklch(100% 0 0 / 10%));\n pointer-events: none;\n}\n\n.base {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n border: none;\n border-radius: calc(var(--size) * 0.15);\n background-color: var(--color, var(--l-color-bg-fill-neutral-soft));\n /*\n * Text over --color, decided by the background's own luminance — never the\n * document mode, since --color is consumer-set and ignores light/dark.\n * The contrast-color() block below is the primary path (Baseline 2026); this\n * --_on calc() is the fallback for browsers predating it (Chrome ≤146,\n * Safari <26, Firefox <146). It approximates WCAG relative luminance with\n * squared sRGB channels (a cheap linearization, Rec. 709 weights) and flips at\n * 0.2, just above the black/white crossover (~0.179) to offset squaring\n * underestimating mid-tones — holding AA across saturated brand colors where an\n * OKLCH-lightness threshold collapses to ≈1:1 (OKLCH L tracks hue, not\n * luminance: a mid red reads \"light\" yet needs white text). --text-color lets\n * consumers force a specific brand text color, overriding both paths.\n */\n --_on: calc(\n 255 *\n (\n 1 -\n sign(\n 0.2126 * (r / 255) * (r / 255) + 0.7152 * (g / 255) * (g / 255) + 0.0722 * (b / 255) *\n (b / 255) - 0.2\n )\n ) /\n 2\n );\n color: var(--text-color, rgb(from var(--color) var(--_on) var(--_on) var(--_on)));\n font-size: var(--_font-size);\n font-weight: 500;\n line-height: 1;\n text-transform: uppercase;\n padding: 0;\n cursor: inherit;\n}\n\n/* Primary path: native, guaranteed-contrast black/white. contrast-color() is\n * Baseline Newly Available (2026) — Safari 26+, Firefox 146+, Chrome/Edge 147+ —\n * and supersedes the luminance approximation above where supported. @supports (not\n * declaration order) is required: the value contains var(), so a non-supporting\n * browser would treat contrast-color() as invalid-at-computed-value-time and reset\n * to `inherit` rather than falling back to the calc() line. No bg fallback inside,\n * so a bare avatar (no --color) keeps inheriting --l-color-text-secondary.\n * --text-color still wins here, overriding contrast-color()'s choice.\n * (The earlier `… vs black, white to AA` extended syntax was dropped from the spec.) */\n@supports (color: contrast-color(red)) {\n .base {\n color: var(--text-color, contrast-color(var(--color)));\n }\n}\n\nbutton.base {\n cursor: pointer;\n}\n\nbutton.base:focus-visible {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 2px;\n}\n\nbutton.base:hover {\n box-shadow: inset 0 0 0 1.5px var(--l-color-border-interactive);\n}\n\n@container style(--appearance: circle) {\n .base {\n border-radius: 50%;\n }\n}\n","import { html, nothing, svg, unsafeCSS } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { html as staticHtml, literal } from 'lit/static-html.js';\nimport { property, state } from 'lit/decorators.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './avatar.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * Extracts up to two uppercase initials from a name: the first letter of the\n * first word, plus the first letter of the last word when there is more than one.\n *\n * Splits on whitespace rather than relying on a `\\b`/`\\w` regex, which only\n * recognises ASCII and mangles accented or non-ASCII names.\n *\n * @param name - The name to extract initials from. Defaults to an empty string.\n * @return The uppercased initials, or an empty string when `name` is blank.\n *\n * @example getInitials('John Doe') // 'JD'\n * @example getInitials('Markus Nösterer') // 'MN' (accents preserved)\n * @example getInitials('Cher') // 'C' (single word → one initial)\n * @example getInitials(' John Doe ') // 'JD' (extra whitespace ignored)\n * @example getInitials('') // ''\n */\nfunction getInitials(name: string = ''): string {\n const words = name.normalize('NFC').trim().split(/\\s+/).filter(Boolean);\n if (words.length === 0) return '';\n\n // Spreading iterates by code point, so an accented letter (or an astral\n // character like an emoji) is taken whole instead of a broken half.\n // oxlint-disable-next-line typescript/no-misused-spread -- code-point-safe first char is the intent\n const firstLetter = (word: string) => [...word][0];\n\n const first = firstLetter(words[0]);\n const last = words.length > 1 ? firstLetter(words[words.length - 1]) : '';\n return (first + last).toUpperCase();\n}\n\nconst defaultIcon = svg`<svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z\" />\n</svg>`;\n\n/**\n * @summary An avatar component for displaying user images, initials, or a default icon.\n * @customElement l-avatar\n *\n * @cssproperty [--size=40px] - Box size (any length). Set it inline (e.g. `style=\"--size: 20px\"`) for an arbitrary size beyond the `size` token scale; the font then follows proportionally. The `size` attribute sets it to the matching token.\n * @cssproperty --color - Background fill color for initials and the default icon.\n * @cssproperty --text-color - Initials/icon color over `--color`. Defaults to an auto-derived readable color; set it to enforce a specific brand color (overrides the automatic choice).\n * @cssproperty --appearance - Set to `circle` for a fully circular avatar (default is a rounded square).\n *\n * @csspart base - The avatar container that paints `--color`; style it (e.g. `color`) to override the auto-derived text color.\n *\n * @cssClass .l-avatar-group - Flex wrapper that overlaps a row of stacked avatars.\n */\nexport class Avatar extends LuxenElement {\n static styles = [hostStyles, styles];\n\n /** Image URL. Falls back to initials (then the default icon) if it fails to load. */\n @property()\n src = '';\n\n /** Name used as the accessible label and to derive the initials fallback. */\n @property()\n name = '';\n\n /** Avatar size: `xs`, `sm`, `md` (default), `lg`, or `xl`. */\n @property({ reflect: true })\n size = 'md';\n\n /** Count shown in the corner badge. `0` hides the badge. */\n @property({ type: Number })\n badge = 0;\n\n /** Render as a `<button>` with focus ring and hover states. */\n @property({ type: Boolean, reflect: true })\n interactive = false;\n\n @state() private _hasError = false;\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('src')) {\n this._hasError = false;\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.interactive && !this.hasAttribute('role')) {\n this.setAttribute('role', 'img');\n }\n }\n\n updated() {\n // An interactive avatar renders a focusable <button>, and a role=\"img\" node\n // must stay a leaf in the accessibility tree — so the host carries no role\n // or label and the inner button holds the accessible name (see render()).\n if (this.interactive) {\n if (this.getAttribute('role') === 'img') this.removeAttribute('role');\n this.removeAttribute('aria-label');\n } else {\n if (!this.hasAttribute('role')) this.setAttribute('role', 'img');\n if (this.name) this.setAttribute('aria-label', this.name);\n }\n\n const isCircle = getComputedStyle(this).getPropertyValue('--appearance').trim() === 'circle';\n this.style.borderRadius = isCircle ? '50%' : '';\n }\n\n private get _tag() {\n return this.interactive ? literal`button` : literal`div`;\n }\n\n render() {\n const content =\n this.src && !this._hasError\n ? html`<img\n src=${this.src}\n alt=${this.name || ''}\n @error=${this._onError}\n />`\n : this.name\n ? html`<span class=\"initials\">${getInitials(this.name)}</span>`\n : html`<slot>${defaultIcon}</slot>`;\n\n return staticHtml`\n <${this._tag}\n class=\"base\"\n part=\"base\"\n type=${this.interactive ? 'button' : nothing}\n aria-label=${this.interactive && this.name ? this.name : nothing}\n >\n ${content}\n </${this._tag}>\n ${\n this.badge\n ? html`<div\n class=\"badge\"\n aria-hidden=\"true\"\n >\n ${this.badge}\n </div>`\n : nothing\n }\n `;\n }\n\n private _onError = () => {\n this._hasError = true;\n };\n}\n"],"mappings":"oUCOA,IAAM,EAAS,EAAU,4jEAAS,EAkBlC,SAAS,EAAY,EAAe,GAAY,CAC9C,IAAM,EAAQ,EAAK,UAAU,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,EACtE,GAAI,EAAM,SAAW,EAAG,MAAO,GAK/B,IAAM,EAAe,GAAiB,CAAC,GAAG,CAAI,CAAC,CAAC,GAIhD,OAFc,EAAY,EAAM,EAExB,GADK,EAAM,OAAS,EAAI,EAAY,EAAM,EAAM,OAAS,EAAE,EAAI,IAAA,CACjD,YAAY,CACpC,CAEA,IAAM,EAAc,CAAG;;QAiBV,EAAb,cAA4B,CAAa,wCAKjC,aAIC,aAIA,gBAIC,mBAIM,kBAEe,qBAqEJ,CACvB,KAAK,UAAY,EACnB,qBA7FgB,CAAC,EAAY,CAAM,EAwBnC,WAAW,EAA+B,CACpC,EAAQ,IAAI,KAAK,IACnB,KAAK,UAAY,GAErB,CAEA,mBAAoB,CAClB,MAAM,kBAAkB,EACpB,CAAC,KAAK,aAAe,CAAC,KAAK,aAAa,MAAM,GAChD,KAAK,aAAa,OAAQ,KAAK,CAEnC,CAEA,SAAU,CAIJ,KAAK,aACH,KAAK,aAAa,MAAM,IAAM,OAAO,KAAK,gBAAgB,MAAM,EACpE,KAAK,gBAAgB,YAAY,IAE5B,KAAK,aAAa,MAAM,GAAG,KAAK,aAAa,OAAQ,KAAK,EAC3D,KAAK,MAAM,KAAK,aAAa,aAAc,KAAK,IAAI,GAG1D,IAAM,EAAW,iBAAiB,IAAI,CAAC,CAAC,iBAAiB,cAAc,CAAC,CAAC,KAAK,IAAM,SACpF,KAAK,MAAM,aAAe,EAAW,MAAQ,EAC/C,CAEA,IAAY,MAAO,CACjB,OAAO,KAAK,YAAc,CAAO,SAAW,CAAO,KACrD,CAEA,QAAS,CACP,IAAM,EACJ,KAAK,KAAO,CAAC,KAAK,UACd,CAAI;kBACI,KAAK,IAAI;kBACT,KAAK,MAAQ,GAAG;qBACb,KAAK,SAAS;cAEzB,KAAK,KACH,CAAI,0BAA0B,EAAY,KAAK,IAAI,EAAE,SACrD,CAAI,SAAS,EAAY,SAEjC,MAAO,EAAU;SACZ,KAAK,KAAK;;;eAGJ,KAAK,YAAc,SAAW,EAAQ;qBAChC,KAAK,aAAe,KAAK,KAAO,KAAK,KAAO,EAAQ;;UAE/D,EAAQ;UACR,KAAK,KAAK;QAEZ,KAAK,MACD,CAAI;;;;gBAIA,KAAK,MAAM;oBAEf,EACL;KAEL,CAKF,KA3FG,EAAS,CAAA,EAAA,EAAA,UAAA,MAAA,IAAA,EAAA,KAIT,EAAS,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAIT,EAAS,CAAE,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAI1B,EAAS,CAAE,KAAM,MAAO,CAAC,CAAA,EAAA,EAAA,UAAA,QAAA,IAAA,EAAA,KAIzB,EAAS,CAAE,KAAM,QAAS,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,cAAA,IAAA,EAAA,KAGzC,EAAM,CAAA,EAAA,EAAA,UAAA,YAAA,IAAA,EAAA"}
|
|
1
|
+
{"version":3,"file":"avatar.js","names":[],"sources":["../../../src/html/elements/avatar/avatar.css?inline","../../../src/html/elements/avatar/avatar.ts"],"sourcesContent":[":host {\n --size: 40px;\n --_font-size: calc(var(--size) * 0.4);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n width: var(--size);\n height: var(--size);\n border-radius: calc(var(--size) * 0.15);\n isolation: isolate;\n vertical-align: middle;\n flex-shrink: 0;\n color: var(--l-color-text-secondary);\n}\n\n:host([size='xs']) {\n --size: 24px;\n --_font-size: 0.75rem;\n}\n:host([size='sm']) {\n --size: 32px;\n --_font-size: 0.875rem;\n}\n:host([size='lg']) {\n --size: 48px;\n --_font-size: 1.25rem;\n}\n:host([size='xl']) {\n --size: 56px;\n --_font-size: 1.25rem;\n}\n\nimg {\n width: 100%;\n height: 100%;\n object-fit: cover;\n border-radius: inherit;\n}\n\n.initials {\n margin: auto;\n}\n\n.icon {\n width: 60%;\n height: 60%;\n color: currentColor;\n}\n\n.badge {\n position: absolute;\n bottom: 0;\n right: 0;\n z-index: 1;\n height: 12px;\n min-width: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding-inline: 2px;\n border-radius: 3px;\n box-shadow: 0 0 0 2px var(--l-color-surface);\n background-color: var(--l-color-surface);\n font-size: 0.625rem;\n font-weight: var(--l-font-weight-medium);\n font-variant-numeric: tabular-nums;\n line-height: 1;\n color: var(--l-color-text-primary);\n overflow: hidden;\n}\n\n/*\n * Inset ring via ::after so it renders on top of the <img>.\n * A box-shadow on :host would be covered by the image since\n * it fills the entire element with object-fit: cover.\n */\n.base::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n box-shadow: inset 0 0 0 1px light-dark(oklch(0 0 0 / 7%), oklch(100% 0 0 / 10%));\n pointer-events: none;\n}\n\n.base {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n border: none;\n border-radius: calc(var(--size) * 0.15);\n background-color: var(--color, var(--l-color-bg-fill-neutral-soft));\n /*\n * Text over --color, decided by the background's own luminance — never the\n * document mode, since --color is consumer-set and ignores light/dark.\n * The contrast-color() block below is the primary path (Baseline 2026); this\n * --_on calc() is the fallback for browsers predating it (Chrome ≤146,\n * Safari <26, Firefox <146). It approximates WCAG relative luminance with\n * squared sRGB channels (a cheap linearization, Rec. 709 weights) and flips at\n * 0.2, just above the black/white crossover (~0.179) to offset squaring\n * underestimating mid-tones — holding AA across saturated brand colors where an\n * OKLCH-lightness threshold collapses to ≈1:1 (OKLCH L tracks hue, not\n * luminance: a mid red reads \"light\" yet needs white text). --text-color lets\n * consumers force a specific brand text color, overriding both paths.\n */\n --_on: calc(\n 255 *\n (\n 1 -\n sign(\n 0.2126 * (r / 255) * (r / 255) + 0.7152 * (g / 255) * (g / 255) + 0.0722 * (b / 255) *\n (b / 255) - 0.2\n )\n ) /\n 2\n );\n color: var(--text-color, rgb(from var(--color) var(--_on) var(--_on) var(--_on)));\n font-size: var(--_font-size);\n font-weight: var(--l-font-weight-medium);\n line-height: 1;\n text-transform: uppercase;\n padding: 0;\n cursor: inherit;\n}\n\n/* Primary path: native, guaranteed-contrast black/white. contrast-color() is\n * Baseline Newly Available (2026) — Safari 26+, Firefox 146+, Chrome/Edge 147+ —\n * and supersedes the luminance approximation above where supported. @supports (not\n * declaration order) is required: the value contains var(), so a non-supporting\n * browser would treat contrast-color() as invalid-at-computed-value-time and reset\n * to `inherit` rather than falling back to the calc() line. No bg fallback inside,\n * so a bare avatar (no --color) keeps inheriting --l-color-text-secondary.\n * --text-color still wins here, overriding contrast-color()'s choice.\n * (The earlier `… vs black, white to AA` extended syntax was dropped from the spec.) */\n@supports (color: contrast-color(red)) {\n .base {\n color: var(--text-color, contrast-color(var(--color)));\n }\n}\n\nbutton.base {\n cursor: pointer;\n}\n\nbutton.base:focus-visible {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 2px;\n}\n\nbutton.base:hover {\n box-shadow: inset 0 0 0 1.5px var(--l-color-border-interactive);\n}\n\n@container style(--appearance: circle) {\n .base {\n border-radius: 50%;\n }\n}\n","import { html, nothing, svg, unsafeCSS } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { html as staticHtml, literal } from 'lit/static-html.js';\nimport { property, state } from 'lit/decorators.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './avatar.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * Extracts up to two uppercase initials from a name: the first letter of the\n * first word, plus the first letter of the last word when there is more than one.\n *\n * Splits on whitespace rather than relying on a `\\b`/`\\w` regex, which only\n * recognises ASCII and mangles accented or non-ASCII names.\n *\n * @param name - The name to extract initials from. Defaults to an empty string.\n * @return The uppercased initials, or an empty string when `name` is blank.\n *\n * @example getInitials('John Doe') // 'JD'\n * @example getInitials('Markus Nösterer') // 'MN' (accents preserved)\n * @example getInitials('Cher') // 'C' (single word → one initial)\n * @example getInitials(' John Doe ') // 'JD' (extra whitespace ignored)\n * @example getInitials('') // ''\n */\nfunction getInitials(name: string = ''): string {\n const words = name.normalize('NFC').trim().split(/\\s+/).filter(Boolean);\n if (words.length === 0) return '';\n\n // Spreading iterates by code point, so an accented letter (or an astral\n // character like an emoji) is taken whole instead of a broken half.\n // oxlint-disable-next-line typescript/no-misused-spread -- code-point-safe first char is the intent\n const firstLetter = (word: string) => [...word][0];\n\n const first = firstLetter(words[0]);\n const last = words.length > 1 ? firstLetter(words[words.length - 1]) : '';\n return (first + last).toUpperCase();\n}\n\nconst defaultIcon = svg`<svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z\" />\n</svg>`;\n\n/**\n * @summary An avatar component for displaying user images, initials, or a default icon.\n * @customElement l-avatar\n *\n * @cssproperty [--size=40px] - Box size (any length). Set it inline (e.g. `style=\"--size: 20px\"`) for an arbitrary size beyond the `size` token scale; the font then follows proportionally. The `size` attribute sets it to the matching token.\n * @cssproperty --color - Background fill color for initials and the default icon.\n * @cssproperty --text-color - Initials/icon color over `--color`. Defaults to an auto-derived readable color; set it to enforce a specific brand color (overrides the automatic choice).\n * @cssproperty --appearance - Set to `circle` for a fully circular avatar (default is a rounded square).\n *\n * @csspart base - The avatar container that paints `--color`; style it (e.g. `color`) to override the auto-derived text color.\n *\n * @cssClass .l-avatar-group - Flex wrapper that overlaps a row of stacked avatars.\n */\nexport class Avatar extends LuxenElement {\n static styles = [hostStyles, styles];\n\n /** Image URL. Falls back to initials (then the default icon) if it fails to load. */\n @property()\n src = '';\n\n /** Name used as the accessible label and to derive the initials fallback. */\n @property()\n name = '';\n\n /** Avatar size: `xs`, `sm`, `md` (default), `lg`, or `xl`. */\n @property({ reflect: true })\n size = 'md';\n\n /** Count shown in the corner badge. `0` hides the badge. */\n @property({ type: Number })\n badge = 0;\n\n /** Render as a `<button>` with focus ring and hover states. */\n @property({ type: Boolean, reflect: true })\n interactive = false;\n\n @state() private _hasError = false;\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('src')) {\n this._hasError = false;\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.interactive && !this.hasAttribute('role')) {\n this.setAttribute('role', 'img');\n }\n }\n\n updated() {\n // An interactive avatar renders a focusable <button>, and a role=\"img\" node\n // must stay a leaf in the accessibility tree — so the host carries no role\n // or label and the inner button holds the accessible name (see render()).\n if (this.interactive) {\n if (this.getAttribute('role') === 'img') this.removeAttribute('role');\n this.removeAttribute('aria-label');\n } else {\n if (!this.hasAttribute('role')) this.setAttribute('role', 'img');\n if (this.name) this.setAttribute('aria-label', this.name);\n }\n\n const isCircle = getComputedStyle(this).getPropertyValue('--appearance').trim() === 'circle';\n this.style.borderRadius = isCircle ? '50%' : '';\n }\n\n private get _tag() {\n return this.interactive ? literal`button` : literal`div`;\n }\n\n render() {\n const content =\n this.src && !this._hasError\n ? html`<img\n src=${this.src}\n alt=${this.name || ''}\n @error=${this._onError}\n />`\n : this.name\n ? html`<span class=\"initials\">${getInitials(this.name)}</span>`\n : html`<slot>${defaultIcon}</slot>`;\n\n return staticHtml`\n <${this._tag}\n class=\"base\"\n part=\"base\"\n type=${this.interactive ? 'button' : nothing}\n aria-label=${this.interactive && this.name ? this.name : nothing}\n >\n ${content}\n </${this._tag}>\n ${\n this.badge\n ? html`<div\n class=\"badge\"\n aria-hidden=\"true\"\n >\n ${this.badge}\n </div>`\n : nothing\n }\n `;\n }\n\n private _onError = () => {\n this._hasError = true;\n };\n}\n"],"mappings":"oUCOA,IAAM,EAAS,EAAU,4mEAAS,EAkBlC,SAAS,EAAY,EAAe,GAAY,CAC9C,IAAM,EAAQ,EAAK,UAAU,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,EACtE,GAAI,EAAM,SAAW,EAAG,MAAO,GAK/B,IAAM,EAAe,GAAiB,CAAC,GAAG,CAAI,CAAC,CAAC,GAIhD,OAFc,EAAY,EAAM,EAExB,GADK,EAAM,OAAS,EAAI,EAAY,EAAM,EAAM,OAAS,EAAE,EAAI,IAAA,CACjD,YAAY,CACpC,CAEA,IAAM,EAAc,CAAG;;QAiBV,EAAb,cAA4B,CAAa,wCAKjC,aAIC,aAIA,gBAIC,mBAIM,kBAEe,qBAqEJ,CACvB,KAAK,UAAY,EACnB,qBA7FgB,CAAC,EAAY,CAAM,EAwBnC,WAAW,EAA+B,CACpC,EAAQ,IAAI,KAAK,IACnB,KAAK,UAAY,GAErB,CAEA,mBAAoB,CAClB,MAAM,kBAAkB,EACpB,CAAC,KAAK,aAAe,CAAC,KAAK,aAAa,MAAM,GAChD,KAAK,aAAa,OAAQ,KAAK,CAEnC,CAEA,SAAU,CAIJ,KAAK,aACH,KAAK,aAAa,MAAM,IAAM,OAAO,KAAK,gBAAgB,MAAM,EACpE,KAAK,gBAAgB,YAAY,IAE5B,KAAK,aAAa,MAAM,GAAG,KAAK,aAAa,OAAQ,KAAK,EAC3D,KAAK,MAAM,KAAK,aAAa,aAAc,KAAK,IAAI,GAG1D,IAAM,EAAW,iBAAiB,IAAI,CAAC,CAAC,iBAAiB,cAAc,CAAC,CAAC,KAAK,IAAM,SACpF,KAAK,MAAM,aAAe,EAAW,MAAQ,EAC/C,CAEA,IAAY,MAAO,CACjB,OAAO,KAAK,YAAc,CAAO,SAAW,CAAO,KACrD,CAEA,QAAS,CACP,IAAM,EACJ,KAAK,KAAO,CAAC,KAAK,UACd,CAAI;kBACI,KAAK,IAAI;kBACT,KAAK,MAAQ,GAAG;qBACb,KAAK,SAAS;cAEzB,KAAK,KACH,CAAI,0BAA0B,EAAY,KAAK,IAAI,EAAE,SACrD,CAAI,SAAS,EAAY,SAEjC,MAAO,EAAU;SACZ,KAAK,KAAK;;;eAGJ,KAAK,YAAc,SAAW,EAAQ;qBAChC,KAAK,aAAe,KAAK,KAAO,KAAK,KAAO,EAAQ;;UAE/D,EAAQ;UACR,KAAK,KAAK;QAEZ,KAAK,MACD,CAAI;;;;gBAIA,KAAK,MAAM;oBAEf,EACL;KAEL,CAKF,KA3FG,EAAS,CAAA,EAAA,EAAA,UAAA,MAAA,IAAA,EAAA,KAIT,EAAS,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAIT,EAAS,CAAE,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAI1B,EAAS,CAAE,KAAM,MAAO,CAAC,CAAA,EAAA,EAAA,UAAA,QAAA,IAAA,EAAA,KAIzB,EAAS,CAAE,KAAM,QAAS,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,cAAA,IAAA,EAAA,KAGzC,EAAM,CAAA,EAAA,EAAA,UAAA,YAAA,IAAA,EAAA"}
|
|
@@ -70,6 +70,7 @@ export declare class Combobox extends LuxenFormAssociatedElement {
|
|
|
70
70
|
private readonly _listId;
|
|
71
71
|
private _floating;
|
|
72
72
|
private _nav;
|
|
73
|
+
private _options;
|
|
73
74
|
/** Selected value (the chosen option's `value`). */
|
|
74
75
|
accessor value: string;
|
|
75
76
|
/** Placeholder text in the input. */
|
|
@@ -99,6 +100,20 @@ export declare class Combobox extends LuxenFormAssociatedElement {
|
|
|
99
100
|
formResetCallback(): void;
|
|
100
101
|
formStateRestoreCallback(state: string): void;
|
|
101
102
|
private _readItems;
|
|
103
|
+
/**
|
|
104
|
+
* Re-read the options after the light DOM changed underneath us.
|
|
105
|
+
*
|
|
106
|
+
* The input carries its own text rather than a render of `_items`, so it has
|
|
107
|
+
* to be refreshed by hand — but only when it is still showing the label we put
|
|
108
|
+
* there. Anything else in it is the user's (a query typed while the panel
|
|
109
|
+
* stayed closed, because a consumer vetoed `show`) and must survive. A closed
|
|
110
|
+
* panel is not on its own proof that nobody is typing.
|
|
111
|
+
*
|
|
112
|
+
* Open, the input is theirs by definition; what needs care there is the active
|
|
113
|
+
* option, addressed by position in `_filtered` — re-anchor it on the option the
|
|
114
|
+
* user had highlighted, or a list that grows or shrinks commits a different one.
|
|
115
|
+
*/
|
|
116
|
+
private _syncItems;
|
|
102
117
|
private _labelForValue;
|
|
103
118
|
private get _filtered();
|
|
104
119
|
private _resetDisplay;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../../src/html/elements/combobox/combobox.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAG1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;
|
|
1
|
+
{"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../../src/html/elements/combobox/combobox.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{cls as e,uniqueId as t}from"../../registry.js";import{i as n}from"../../chunks/lit.js";import{i as r,r as i,t as a}from"../../chunks/decorate.js";import{a as o,t as s}from"../../chunks/lit-html.js";import{LocalizeController as c}from"../../shared/localize.js";import l from"../../shared/styles/host.styles.js";import{t as u}from"../../chunks/unsafe-html.js";import{LuxenFormAssociatedElement as d}from"../../shared/luxen-form-associated-element.js";import{PopoverController as f}from"../../shared/controllers/popover.js";import{ListboxNavController as p}from"../../shared/controllers/listbox-nav.js";var m=n(`: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}.control{block-size:var(--height);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);cursor:text;align-items:center;gap:.25rem;display:flex}@media (hover:hover){.control:hover{border-color:var(--l-form-control-border-color-hover)}}.control:has(.input:focus-visible){border-color:var(--l-focus-ring);box-shadow:0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent)}.input{min-inline-size:0;color:var(--l-form-control-value-color);font:inherit;background:0 0;border:0;outline:none;flex:1}.input::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}.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;flex:none;padding:0}@media (hover:hover){.clear:hover{color:var(--l-form-control-value-color)}}.control:has(.input[aria-expanded=true]) .chevron{rotate:180deg}.panel{border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius);background-color:var(--background);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;inset:auto;overflow:hidden;scale:.98}.panel:popover-open{opacity:1;scale:1}@starting-style{.panel:popover-open{opacity:0;scale:.98}}.listbox{overscroll-behavior:contain;max-block-size:16rem;margin:0;padding:.25rem;list-style:none;overflow-y:auto}.option{min-block-size:2rem;padding-block:.25rem;padding-inline:.5rem var(--height);border-radius:calc(var(--border-radius) - 2px);color:var(--l-color-text-primary);cursor:pointer;align-items:center;gap:.5rem;display:flex;position:relative}.option[aria-selected=true]{background-color:var(--l-color-bg-state-hover)}.option[aria-disabled=true]{opacity:.4;cursor:not-allowed}.option[data-current]:after{content:"";background-color:var(--l-form-control-activated-color);block-size:1em;inline-size:1em;-webkit-mask:var(--_check-icon) center / contain no-repeat;mask:var(--_check-icon) center / contain no-repeat;position:absolute;inset-inline-end:.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:center;gap:.5rem;min-inline-size:0;display:flex}.option-rich .l-select-item-media{flex:none;align-items:center;font-size:1.25em;display:inline-flex}.option-rich .l-select-item-text{flex-direction:column;min-inline-size:0;display:flex}.option-rich .l-select-item-title{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.option-rich .l-select-item-description{color:var(--l-color-text-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:.85em;overflow:hidden}.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 h(e){return e.normalize(`NFD`).replace(/[̀-ͯ]/g,``).toLowerCase()}var g=(e,t)=>{if(!t)return!0;let n=h(e.label);return h(t).split(/\s+/).filter(Boolean).every(e=>n.includes(e))},_=class extends Event{constructor(e){super(`change`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},v=class extends Event{constructor(e){super(`input`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},y=class extends d{constructor(...e){super(...e),this._localize=new c(this),this._listId=t(`combobox-list`),this._floating=new f(this,{getTriggerElement:()=>this._controlEl,getFloatingElement:()=>this._panelEl,getArrowElement:()=>null}),this._nav=new p(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=``,this.#r=`md`,this.#i=!1,this.#a=!1,this.#o=!0,this.#s=`bottom-start`,this.filter=g,this.#c=!1,this.#l=``,this.#u=[],this._onKeyDown=e=>{if(!this._nav.onKeyDown(e))switch(e.key){case`Enter`:this._open&&(e.preventDefault(),this._commitActiveOrCustom());break;case`Escape`:this._open?(e.preventDefault(),this.hide()):(this.value||this._inputEl?.value)&&this._clear();break;case`Tab`:this._open&&this._nav.activeIndex>=0&&this._commitActiveOrCustom();break;default:break}},this._onInput=e=>{this._query=e.target.value,this._nav.reset(),this._open||this.show(),this.dispatchEvent(new v(this._query))},this._onControlPointerDown=e=>{e.target.closest(`.clear`)||this._open||this.show()},this._onFocusOut=e=>{let t=e.relatedTarget;t&&this.shadowRoot?.contains(t)||(this.hasInteracted=!0,this._updateValidity(),this.hide())}}static{this.styles=[l,m]}#e;get value(){return this.#e}set value(e){this.#e=e}#t;get placeholder(){return this.#t}set placeholder(e){this.#t=e}#n;get label(){return this.#n}set label(e){this.#n=e}#r;get size(){return this.#r}set size(e){this.#r=e}#i;get withClear(){return this.#i}set withClear(e){this.#i=e}#a;get allowCustom(){return this.#a}set allowCustom(e){this.#a=e}#o;get highlight(){return this.#o}set highlight(e){this.#o=e}#s;get placement(){return this.#s}set placement(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 _controlEl(){return this.shadowRoot?.querySelector(`.control`)??null}get _panelEl(){return this.shadowRoot?.querySelector(`.panel`)??null}get _inputEl(){return this.shadowRoot?.querySelector(`.input`)??null}connectedCallback(){super.connectedCallback(),this._readItems(),this._defaultFormValue=this.value,this._syncFormValue(this.value)}updated(e){e.has(`value`)&&(this._syncFormValue(this.value),this._updateValidity(),this._open||this._resetDisplay())}formResetCallback(){this.value=this._defaultFormValue,this._query=``,this._nav.reset(),super.formResetCallback(),this._resetDisplay()}formStateRestoreCallback(e){this.value=e,this._syncFormValue(e)}_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,html:n?t.innerHTML:void 0}})}_labelForValue(e){return this._items.find(t=>t.value===e)?.label??(this.allowCustom?e:``)}get _filtered(){return this._items.filter(e=>this.filter(e,this._query))}_resetDisplay(){let e=this._inputEl;e&&(e.value=this._labelForValue(this.value))}_updateValidity(){this.required&&!this.value?this.setValidity({valueMissing:!0},this._localize.term(`selectOption`),this._inputEl??void 0):this.setValidity({})}show(){this._open||this.disabled||(this._readItems(),this.dispatchEvent(new Event(`show`,{cancelable:!0}))&&(this._open=!0,this.value&&!this._query&&this._nav.setActive(this._filtered.findIndex(e=>e.value===this.value)),this.updateComplete.then(()=>{this._panelEl?.showPopover?.(),this._floating.startPositioning({placement:this.placement,distance:4,minWidth:`trigger`})})))}hide(){if(!this._open||!this.dispatchEvent(new Event(`hide`,{cancelable:!0})))return;this._open=!1,this._nav.reset();let e=this._panelEl;e?.matches?.(`:popover-open`)&&e.hidePopover?.(),this._floating.stopPositioning(),this._resetDisplay()}_commit(e){if(e.disabled)return;this.value=e.value,this._query=``,this.hasInteracted=!0;let t=this._inputEl;t&&(t.value=e.label),this._syncFormValue(this.value),this._updateValidity(),this.dispatchEvent(new _(this.value)),this.hide(),t?.focus()}_commitActiveOrCustom(){let e=this._filtered,t=this._nav.activeIndex;if(t>=0&&e[t]){this._commit(e[t]);return}let n=this._inputEl;this.allowCustom&&n&&(this.value=n.value,this._syncFormValue(this.value),this._updateValidity(),this.dispatchEvent(new _(this.value)),this.hide())}_clear(){this.value=``,this._query=``,this._nav.reset(),this._inputEl&&(this._inputEl.value=``),this._syncFormValue(``),this._updateValidity(),this.dispatchEvent(new _(``)),this._inputEl?.focus()}_highlight(e){let t=this._query.trim();if(!this.highlight||!t)return e;let n=h(e).indexOf(h(t).split(/\s+/)[0]);return n<0?e:o`${e.slice(0,n)}<mark>${e.slice(n,n+t.length)}</mark>${e.slice(n+t.length)}`}render(){let e=this._filtered,t=this._nav.activeDescendant,n=this._nav.activeIndex,r=this.required&&!this.value&&this.hasInteracted,i=this._open?e.length===0?this._localize.term(`noResults`):`${e.length} ${this._localize.term(`suggestions`)}`:``;return o`
|
|
1
|
+
import{cls as e,uniqueId as t}from"../../registry.js";import{i as n}from"../../chunks/lit.js";import{i as r,r as i,t as a}from"../../chunks/decorate.js";import{a as o,t as s}from"../../chunks/lit-html.js";import{LocalizeController as c}from"../../shared/localize.js";import l from"../../shared/styles/host.styles.js";import{t as u}from"../../chunks/unsafe-html.js";import{LuxenFormAssociatedElement as d}from"../../shared/luxen-form-associated-element.js";import{PopoverController as f}from"../../shared/controllers/popover.js";import{ListboxNavController as p}from"../../shared/controllers/listbox-nav.js";import{DatalistObserverController as m}from"../../shared/controllers/datalist-observer.js";var h=n(`: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}.control{block-size:var(--height);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);cursor:text;align-items:center;gap:.25rem;display:flex}@media (hover:hover){.control:hover{border-color:var(--l-form-control-border-color-hover)}}.control:has(.input:focus-visible){border-color:var(--l-focus-ring);box-shadow:0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent)}.input{min-inline-size:0;color:var(--l-form-control-value-color);font:inherit;background:0 0;border:0;outline:none;flex:1}.input::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}.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;flex:none;padding:0}@media (hover:hover){.clear:hover{color:var(--l-form-control-value-color)}}.control:has(.input[aria-expanded=true]) .chevron{rotate:180deg}.panel{border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius);background-color:var(--background);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;inset:auto;overflow:hidden;scale:.98}.panel:popover-open{opacity:1;scale:1}@starting-style{.panel:popover-open{opacity:0;scale:.98}}.listbox{overscroll-behavior:contain;max-block-size:16rem;margin:0;padding:.25rem;list-style:none;overflow-y:auto}.option{min-block-size:2rem;padding-block:.25rem;padding-inline:.5rem var(--height);border-radius:calc(var(--border-radius) - 2px);color:var(--l-color-text-primary);cursor:pointer;align-items:center;gap:.5rem;display:flex;position:relative}.option[aria-selected=true]{background-color:var(--l-color-bg-state-hover)}.option[aria-disabled=true]{opacity:.4;cursor:not-allowed}.option[data-current]:after{content:"";background-color:var(--l-form-control-activated-color);block-size:1em;inline-size:1em;-webkit-mask:var(--_check-icon) center / contain no-repeat;mask:var(--_check-icon) center / contain no-repeat;position:absolute;inset-inline-end:.5rem}.option-label{text-overflow:ellipsis;white-space:nowrap;flex:1;min-inline-size:0;overflow:hidden}.option-label mark{color:inherit;font-weight:var(--l-font-weight-semibold);background:0 0}.option-rich{flex:1;align-items:center;gap:.5rem;min-inline-size:0;display:flex}.option-rich .l-select-item-media{flex:none;align-items:center;font-size:1.25em;display:inline-flex}.option-rich .l-select-item-text{flex-direction:column;min-inline-size:0;display:flex}.option-rich .l-select-item-title{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.option-rich .l-select-item-description{color:var(--l-color-text-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:.85em;overflow:hidden}.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 g(e){return e.normalize(`NFD`).replace(/[̀-ͯ]/g,``).toLowerCase()}var _=(e,t)=>{if(!t)return!0;let n=g(e.label);return g(t).split(/\s+/).filter(Boolean).every(e=>n.includes(e))},v=class extends Event{constructor(e){super(`change`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},y=class extends Event{constructor(e){super(`input`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},b=class extends d{constructor(...e){super(...e),this._localize=new c(this),this._listId=t(`combobox-list`),this._floating=new f(this,{getTriggerElement:()=>this._controlEl,getFloatingElement:()=>this._panelEl,getArrowElement:()=>null}),this._nav=new p(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._options=new m(this,()=>this._syncItems()),this.#e=``,this.#t=``,this.#n=``,this.#r=`md`,this.#i=!1,this.#a=!1,this.#o=!0,this.#s=`bottom-start`,this.filter=_,this.#c=!1,this.#l=``,this.#u=[],this._onKeyDown=e=>{if(!this._nav.onKeyDown(e))switch(e.key){case`Enter`:this._open&&(e.preventDefault(),this._commitActiveOrCustom());break;case`Escape`:this._open?(e.preventDefault(),this.hide()):(this.value||this._inputEl?.value)&&this._clear();break;case`Tab`:this._open&&this._nav.activeIndex>=0&&this._commitActiveOrCustom();break;default:break}},this._onInput=e=>{this._query=e.target.value,this._nav.reset(),this._open||this.show(),this.dispatchEvent(new y(this._query))},this._onControlPointerDown=e=>{e.target.closest(`.clear`)||this._open||this.show()},this._onFocusOut=e=>{let t=e.relatedTarget;t&&this.shadowRoot?.contains(t)||(this.hasInteracted=!0,this._updateValidity(),this.hide())}}static{this.styles=[l,h]}#e;get value(){return this.#e}set value(e){this.#e=e}#t;get placeholder(){return this.#t}set placeholder(e){this.#t=e}#n;get label(){return this.#n}set label(e){this.#n=e}#r;get size(){return this.#r}set size(e){this.#r=e}#i;get withClear(){return this.#i}set withClear(e){this.#i=e}#a;get allowCustom(){return this.#a}set allowCustom(e){this.#a=e}#o;get highlight(){return this.#o}set highlight(e){this.#o=e}#s;get placement(){return this.#s}set placement(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 _controlEl(){return this.shadowRoot?.querySelector(`.control`)??null}get _panelEl(){return this.shadowRoot?.querySelector(`.panel`)??null}get _inputEl(){return this.shadowRoot?.querySelector(`.input`)??null}connectedCallback(){super.connectedCallback(),this._syncItems(),this._defaultFormValue=this.value,this._syncFormValue(this.value)}updated(e){e.has(`value`)&&(this._syncFormValue(this.value),this._updateValidity(),this._open||this._resetDisplay())}formResetCallback(){this.value=this._defaultFormValue,this._query=``,this._nav.reset(),super.formResetCallback(),this._resetDisplay()}formStateRestoreCallback(e){this.value=e,this._syncFormValue(e)}_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,html:n?t.innerHTML:void 0}})}_syncItems(){let e=this._open?this._filtered[this._nav.activeIndex]:void 0,t=this._inputEl?.value,n=t===void 0||t===this._labelForValue(this.value);if(this._readItems(),!this._open){n&&this._resetDisplay();return}this._nav.setActive(e?this._filtered.findIndex(t=>t.value===e.value):-1)}_labelForValue(e){return this._items.find(t=>t.value===e)?.label??(this.allowCustom?e:``)}get _filtered(){return this._items.filter(e=>this.filter(e,this._query))}_resetDisplay(){let e=this._inputEl;e&&(e.value=this._labelForValue(this.value))}_updateValidity(){this.required&&!this.value?this.setValidity({valueMissing:!0},this._localize.term(`selectOption`),this._inputEl??void 0):this.setValidity({})}show(){this._open||this.disabled||(this._readItems(),this.dispatchEvent(new Event(`show`,{cancelable:!0}))&&(this._open=!0,this.value&&!this._query&&this._nav.setActive(this._filtered.findIndex(e=>e.value===this.value)),this.updateComplete.then(()=>{this._panelEl?.showPopover?.(),this._floating.startPositioning({placement:this.placement,distance:4,minWidth:`trigger`})})))}hide(){if(!this._open||!this.dispatchEvent(new Event(`hide`,{cancelable:!0})))return;this._open=!1,this._nav.reset();let e=this._panelEl;e?.matches?.(`:popover-open`)&&e.hidePopover?.(),this._floating.stopPositioning(),this._resetDisplay()}_commit(e){if(e.disabled)return;this.value=e.value,this._query=``,this.hasInteracted=!0;let t=this._inputEl;t&&(t.value=e.label),this._syncFormValue(this.value),this._updateValidity(),this.dispatchEvent(new v(this.value)),this.hide(),t?.focus()}_commitActiveOrCustom(){let e=this._filtered,t=this._nav.activeIndex;if(t>=0&&e[t]){this._commit(e[t]);return}let n=this._inputEl;this.allowCustom&&n&&(this.value=n.value,this._syncFormValue(this.value),this._updateValidity(),this.dispatchEvent(new v(this.value)),this.hide())}_clear(){this.value=``,this._query=``,this._nav.reset(),this._inputEl&&(this._inputEl.value=``),this._syncFormValue(``),this._updateValidity(),this.dispatchEvent(new v(``)),this._inputEl?.focus()}_highlight(e){let t=this._query.trim();if(!this.highlight||!t)return e;let n=g(e).indexOf(g(t).split(/\s+/)[0]);return n<0?e:o`${e.slice(0,n)}<mark>${e.slice(n,n+t.length)}</mark>${e.slice(n+t.length)}`}render(){let e=this._filtered,t=this._nav.activeDescendant,n=this._nav.activeIndex,r=this.required&&!this.value&&this.hasInteracted,i=this._open?e.length===0?this._localize.term(`noResults`):`${e.length} ${this._localize.term(`suggestions`)}`:``;return o`
|
|
2
2
|
<div
|
|
3
3
|
class="base"
|
|
4
4
|
part="base"
|
|
@@ -84,5 +84,5 @@ import{cls as e,uniqueId as t}from"../../registry.js";import{i as n}from"../../c
|
|
|
84
84
|
${i}
|
|
85
85
|
</div>
|
|
86
86
|
</div>
|
|
87
|
-
`}};a([r({reflect:!0})],
|
|
87
|
+
`}};a([r({reflect:!0})],b.prototype,`value`,null),a([r()],b.prototype,`placeholder`,null),a([r()],b.prototype,`label`,null),a([r({reflect:!0})],b.prototype,`size`,null),a([r({type:Boolean,reflect:!0,attribute:`with-clear`})],b.prototype,`withClear`,null),a([r({type:Boolean,reflect:!0,attribute:`allow-custom`})],b.prototype,`allowCustom`,null),a([r({type:Boolean})],b.prototype,`highlight`,null),a([r()],b.prototype,`placement`,null),a([i()],b.prototype,`_open`,null),a([i()],b.prototype,`_query`,null),a([i()],b.prototype,`_items`,null);export{b as Combobox,v as ComboboxChangeEvent,y as ComboboxInputEvent};
|
|
88
88
|
//# sourceMappingURL=combobox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.js","names":[],"sources":["../../../src/html/elements/combobox/combobox.css?inline","../../../src/html/elements/combobox/combobox.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 🅲🅾🅽🆃🆁🅾🅻 (trigger)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.control {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n block-size: var(--height);\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 cursor: text;\n}\n\n@media (hover: hover) {\n .control:hover {\n border-color: var(--l-form-control-border-color-hover);\n }\n}\n\n.control:has(.input:focus-visible) {\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.input {\n flex: 1;\n min-inline-size: 0;\n border: 0;\n background: transparent;\n color: var(--l-form-control-value-color);\n font: inherit;\n outline: none;\n}\n\n.input::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/* ≥24px hit area (WCAG 2.5.8) with a 1em icon centred inside. */\n.clear {\n flex: none;\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.control:has(.input[aria-expanded='true']) .chevron {\n rotate: 180deg;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅿🅰🅽🅴🅻 (popover 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 box-shadow: var(--l-shadow-md);\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.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.option {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n min-block-size: 2rem;\n padding-block: 0.25rem;\n padding-inline: 0.5rem var(--height);\n position: relative;\n border-radius: calc(var(--border-radius) - 2px);\n color: var(--l-color-text-primary);\n cursor: pointer;\n}\n\n.option[aria-selected='true'] {\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/* Checkmark on the currently committed value. */\n.option[data-current]::after {\n content: '';\n position: absolute;\n inset-inline-end: 0.5rem;\n inline-size: 1em;\n block-size: 1em;\n background-color: var(--l-form-control-activated-color);\n mask: var(--_check-icon) center / contain 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/* Rich options — same authoring classes as l-select, styled inside the shadow. */\n.option-rich {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex: 1;\n min-inline-size: 0;\n}\n\n.option-rich .l-select-item-media {\n flex: none;\n display: inline-flex;\n align-items: center;\n font-size: 1.25em;\n}\n\n.option-rich .l-select-item-text {\n display: flex;\n flex-direction: column;\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}\n\n.option-rich .l-select-item-description {\n font-size: 0.85em;\n color: var(--l-color-text-tertiary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\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 { 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, uniqueId } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './combobox.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type ComboboxSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\n/** A single option read from the `<datalist>`. */\nexport interface ComboboxItem {\n value: string;\n /** Plain text used for filtering and the input display. */\n label: string;\n disabled: 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 ComboboxFilter = (item: ComboboxItem, query: string) => boolean;\n\nfunction normalize(s: string): string {\n return s\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase();\n}\n\nconst defaultFilter: ComboboxFilter = (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 an option is committed (selected). Bubbles; not composed. */\nexport class ComboboxChangeEvent extends Event {\n readonly value: string;\n constructor(value: string) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\n/** Fired as the user types (filter query changes). Bubbles; not composed. */\nexport class ComboboxInputEvent 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 ComboboxEventMap {\n change: ComboboxChangeEvent;\n input: ComboboxInputEvent;\n}\n\n/**\n * A text input paired with a filterable list of options (the ARIA combobox\n * pattern). Options are authored as a native `<datalist>` of `<option>` —\n * the same authoring surface as `l-select`. Form-associated.\n *\n * @summary A searchable text input with a filtered list of options.\n *\n * @example\n * ```html\n * <l-combobox label=\"Country\" name=\"country\" placeholder=\"Search…\">\n * <datalist>\n * <option value=\"us\">United States</option>\n * <option value=\"fr\">France</option>\n * </datalist>\n * </l-combobox>\n * ```\n *\n * @event change - Fired when an option is selected. Bubbles. Not cancelable. Properties: `value: string`.\n * @event input - Fired as the user types. Bubbles. Not cancelable. Properties: `value: string` (the query).\n * @event show - Fired before the list opens. Cancelable.\n * @event hide - Fired before the list closes. Cancelable.\n *\n * @csspart base - The host wrapper.\n * @csspart control - The input control row (input + clear + chevron).\n * @csspart input - The text input.\n * @csspart panel - The floating popover panel.\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] - Control + panel radius.\n * @cssproperty [--background] - Panel background.\n *\n * @customElement l-combobox\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Combobox extends LuxenFormAssociatedElement {\n static override styles = [hostStyles, styles];\n\n private _localize = new LocalizeController(this);\n private readonly _listId = uniqueId('combobox-list');\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._controlEl,\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 /** Selected value (the chosen option's `value`). */\n @property({ reflect: true })\n accessor value = '';\n\n /** Placeholder text in the input. */\n @property()\n accessor placeholder = '';\n\n /** Accessible label for the input. */\n @property()\n accessor label = '';\n\n /** Control size. */\n @property({ reflect: true })\n accessor size: ComboboxSize = 'md';\n\n /** Show a button to clear the value. */\n @property({ type: Boolean, reflect: true, attribute: 'with-clear' })\n accessor withClear = false;\n\n /** Accept a typed value that matches no option. */\n @property({ type: Boolean, reflect: true, attribute: 'allow-custom' })\n accessor allowCustom = false;\n\n /** Wrap the matching substring of each option in `<mark>`. */\n @property({ type: Boolean })\n accessor highlight = true;\n\n /** Panel placement relative to the control. */\n @property()\n accessor placement: Placement = 'bottom-start';\n\n /** Override the option filter. `(item, query) => boolean`. */\n filter: ComboboxFilter = defaultFilter;\n\n @state() private accessor _open = false;\n @state() private accessor _query = '';\n @state() private accessor _items: ComboboxItem[] = [];\n\n private get _controlEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.control') ?? null;\n }\n private get _panelEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.panel') ?? null;\n }\n private get _inputEl() {\n return this.shadowRoot?.querySelector<HTMLInputElement>('.input') ?? null;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this._readItems();\n this._defaultFormValue = this.value;\n this._syncFormValue(this.value);\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('value')) {\n this._syncFormValue(this.value);\n this._updateValidity();\n if (!this._open) this._resetDisplay();\n }\n }\n\n override formResetCallback() {\n this.value = this._defaultFormValue;\n this._query = '';\n this._nav.reset();\n super.formResetCallback();\n this._resetDisplay();\n }\n\n override formStateRestoreCallback(state: string) {\n this.value = state;\n this._syncFormValue(state);\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 // Filter/display label: `label` attribute → rich title → flattened text.\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 { value: o.value, label, disabled: o.disabled, html: rich ? o.innerHTML : undefined };\n });\n }\n\n private _labelForValue(value: string): string {\n return this._items.find((i) => i.value === value)?.label ?? (this.allowCustom ? value : '');\n }\n\n private get _filtered(): ComboboxItem[] {\n return this._items.filter((i) => this.filter(i, this._query));\n }\n\n private _resetDisplay() {\n const input = this._inputEl;\n if (input) input.value = this._labelForValue(this.value);\n }\n\n // --- Validity ---\n\n private _updateValidity() {\n if (this.required && !this.value) {\n this.setValidity(\n { valueMissing: true },\n this._localize.term('selectOption'),\n this._inputEl ?? 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 // Pre-activate the chosen option so a screen reader announces the current\n // selection when the list opens (only when not actively filtering).\n if (this.value && !this._query) {\n this._nav.setActive(this._filtered.findIndex((i) => i.value === this.value));\n }\n 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 });\n }\n\n hide() {\n if (!this._open) return;\n if (!this.dispatchEvent(new Event('hide', { cancelable: true }))) return;\n this._open = false;\n this._nav.reset();\n const panel = this._panelEl as (HTMLElement & { hidePopover?: () => void }) | null;\n if (panel?.matches?.(':popover-open')) (panel as { hidePopover?: () => void }).hidePopover?.();\n this._floating.stopPositioning();\n this._resetDisplay();\n }\n\n // --- Selection ---\n\n private _commit(item: ComboboxItem) {\n if (item.disabled) return;\n this.value = item.value;\n this._query = '';\n this.hasInteracted = true;\n const input = this._inputEl;\n if (input) input.value = item.label;\n this._syncFormValue(this.value);\n this._updateValidity();\n this.dispatchEvent(new ComboboxChangeEvent(this.value));\n this.hide();\n input?.focus();\n }\n\n private _commitActiveOrCustom() {\n const filtered = this._filtered;\n const active = this._nav.activeIndex;\n if (active >= 0 && filtered[active]) {\n this._commit(filtered[active]);\n return;\n }\n const input = this._inputEl;\n if (this.allowCustom && input) {\n this.value = input.value;\n this._syncFormValue(this.value);\n this._updateValidity();\n this.dispatchEvent(new ComboboxChangeEvent(this.value));\n this.hide();\n }\n }\n\n private _clear() {\n this.value = '';\n this._query = '';\n this._nav.reset();\n if (this._inputEl) this._inputEl.value = '';\n this._syncFormValue('');\n this._updateValidity();\n this.dispatchEvent(new ComboboxChangeEvent(''));\n this._inputEl?.focus();\n }\n\n // --- Keyboard (navigation delegated to ListboxNavController) ---\n\n private _onKeyDown = (e: KeyboardEvent) => {\n // Arrow Up/Down move aria-activedescendant. Home/End are left to the input's\n // text cursor (APG editable combobox).\n if (this._nav.onKeyDown(e)) return;\n switch (e.key) {\n case 'Enter':\n if (this._open) {\n e.preventDefault();\n this._commitActiveOrCustom();\n }\n break;\n case 'Escape':\n if (this._open) {\n e.preventDefault();\n this.hide();\n } else if (this.value || this._inputEl?.value) {\n this._clear();\n }\n break;\n case 'Tab':\n if (this._open && this._nav.activeIndex >= 0) this._commitActiveOrCustom();\n break;\n default:\n break;\n }\n };\n\n private _onInput = (e: Event) => {\n this._query = (e.target as HTMLInputElement).value;\n this._nav.reset();\n if (!this._open) this.show();\n this.dispatchEvent(new ComboboxInputEvent(this._query));\n };\n\n private _onControlPointerDown = (e: PointerEvent) => {\n // Toggle on clicking the control chrome (not while typing).\n if ((e.target as HTMLElement).closest('.clear')) return;\n if (this._open) return;\n this.show();\n };\n\n private _onFocusOut = (e: FocusEvent) => {\n const next = e.relatedTarget as Node | null;\n if (next && this.shadowRoot?.contains(next)) return;\n this.hasInteracted = true;\n this._updateValidity();\n this.hide();\n };\n\n // --- Render ---\n\n private _highlight(label: string): TemplateResult | string {\n const q = this._query.trim();\n if (!this.highlight || !q) return label;\n const idx = normalize(label).indexOf(normalize(q).split(/\\s+/)[0]);\n if (idx < 0) return label;\n const before = label.slice(0, idx);\n const match = label.slice(idx, idx + q.length);\n const after = label.slice(idx + q.length);\n return html`${before}<mark>${match}</mark>${after}`;\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.value && this.hasInteracted;\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 @focusout=${this._onFocusOut}\n >\n <div\n class=\"control\"\n part=\"control\"\n @pointerdown=${this._onControlPointerDown}\n >\n <slot name=\"prefix\"></slot>\n <input\n class=\"input\"\n part=\"input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n .placeholder=${this.placeholder}\n ?disabled=${this.disabled}\n aria-label=${this.label || nothing}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-activedescendant=${activeId ?? nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @input=${this._onInput}\n @keydown=${this._onKeyDown}\n />\n ${this.withClear && (this.value || this._query)\n ? html`<button\n class=\"clear\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=${this._localize.term('clear')}\n @click=${this._clear}\n ></button>`\n : nothing}\n <span\n class=\"chevron\"\n aria-hidden=\"true\"\n ></span>\n </div>\n\n <div\n class=\"panel\"\n part=\"panel\"\n popover=\"manual\"\n >\n <ul\n class=\"listbox\"\n part=\"listbox\"\n role=\"listbox\"\n id=${this._listId}\n aria-label=${this.label || this._localize.term('suggestions')}\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=${i === activeIndex ? 'true' : 'false'}\n aria-disabled=${item.disabled ? 'true' : nothing}\n data-current=${item.value === this.value ? '' : nothing}\n @pointerdown=${(e: Event) => e.preventDefault()}\n @click=${() => this._commit(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 Combobox {\n addEventListener<K extends keyof ComboboxEventMap>(\n type: K,\n listener: (this: Combobox, ev: ComboboxEventMap[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 ComboboxEventMap>(\n type: K,\n listener: (this: Combobox, ev: ComboboxEventMap[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":"combobox.js","names":[],"sources":["../../../src/html/elements/combobox/combobox.css?inline","../../../src/html/elements/combobox/combobox.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 🅲🅾🅽🆃🆁🅾🅻 (trigger)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.control {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n block-size: var(--height);\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 cursor: text;\n}\n\n@media (hover: hover) {\n .control:hover {\n border-color: var(--l-form-control-border-color-hover);\n }\n}\n\n.control:has(.input:focus-visible) {\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.input {\n flex: 1;\n min-inline-size: 0;\n border: 0;\n background: transparent;\n color: var(--l-form-control-value-color);\n font: inherit;\n outline: none;\n}\n\n.input::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/* ≥24px hit area (WCAG 2.5.8) with a 1em icon centred inside. */\n.clear {\n flex: none;\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.control:has(.input[aria-expanded='true']) .chevron {\n rotate: 180deg;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅿🅰🅽🅴🅻 (popover 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 box-shadow: var(--l-shadow-md);\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.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.option {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n min-block-size: 2rem;\n padding-block: 0.25rem;\n padding-inline: 0.5rem var(--height);\n position: relative;\n border-radius: calc(var(--border-radius) - 2px);\n color: var(--l-color-text-primary);\n cursor: pointer;\n}\n\n.option[aria-selected='true'] {\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/* Checkmark on the currently committed value. */\n.option[data-current]::after {\n content: '';\n position: absolute;\n inset-inline-end: 0.5rem;\n inline-size: 1em;\n block-size: 1em;\n background-color: var(--l-form-control-activated-color);\n mask: var(--_check-icon) center / contain 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: var(--l-font-weight-semibold);\n}\n\n/* Rich options — same authoring classes as l-select, styled inside the shadow. */\n.option-rich {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex: 1;\n min-inline-size: 0;\n}\n\n.option-rich .l-select-item-media {\n flex: none;\n display: inline-flex;\n align-items: center;\n font-size: 1.25em;\n}\n\n.option-rich .l-select-item-text {\n display: flex;\n flex-direction: column;\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}\n\n.option-rich .l-select-item-description {\n font-size: 0.85em;\n color: var(--l-color-text-tertiary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\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 { 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 { DatalistObserverController } from '../../shared/controllers/datalist-observer.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { cls, uniqueId } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './combobox.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type ComboboxSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\n/** A single option read from the `<datalist>`. */\nexport interface ComboboxItem {\n value: string;\n /** Plain text used for filtering and the input display. */\n label: string;\n disabled: 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 ComboboxFilter = (item: ComboboxItem, query: string) => boolean;\n\nfunction normalize(s: string): string {\n return s\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase();\n}\n\nconst defaultFilter: ComboboxFilter = (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 an option is committed (selected). Bubbles; not composed. */\nexport class ComboboxChangeEvent extends Event {\n readonly value: string;\n constructor(value: string) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\n/** Fired as the user types (filter query changes). Bubbles; not composed. */\nexport class ComboboxInputEvent 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 ComboboxEventMap {\n change: ComboboxChangeEvent;\n input: ComboboxInputEvent;\n}\n\n/**\n * A text input paired with a filterable list of options (the ARIA combobox\n * pattern). Options are authored as a native `<datalist>` of `<option>` —\n * the same authoring surface as `l-select`. Form-associated.\n *\n * @summary A searchable text input with a filtered list of options.\n *\n * @example\n * ```html\n * <l-combobox label=\"Country\" name=\"country\" placeholder=\"Search…\">\n * <datalist>\n * <option value=\"us\">United States</option>\n * <option value=\"fr\">France</option>\n * </datalist>\n * </l-combobox>\n * ```\n *\n * @event change - Fired when an option is selected. Bubbles. Not cancelable. Properties: `value: string`.\n * @event input - Fired as the user types. Bubbles. Not cancelable. Properties: `value: string` (the query).\n * @event show - Fired before the list opens. Cancelable.\n * @event hide - Fired before the list closes. Cancelable.\n *\n * @csspart base - The host wrapper.\n * @csspart control - The input control row (input + clear + chevron).\n * @csspart input - The text input.\n * @csspart panel - The floating popover panel.\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] - Control + panel radius.\n * @cssproperty [--background] - Panel background.\n *\n * @customElement l-combobox\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Combobox extends LuxenFormAssociatedElement {\n static override styles = [hostStyles, styles];\n\n private _localize = new LocalizeController(this);\n private readonly _listId = uniqueId('combobox-list');\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._controlEl,\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 // The `<datalist>` is light DOM the consumer owns and rewrites in place;\n // re-read it so the input tracks the options it was given.\n private _options = new DatalistObserverController(this, () => this._syncItems());\n\n /** Selected value (the chosen option's `value`). */\n @property({ reflect: true })\n accessor value = '';\n\n /** Placeholder text in the input. */\n @property()\n accessor placeholder = '';\n\n /** Accessible label for the input. */\n @property()\n accessor label = '';\n\n /** Control size. */\n @property({ reflect: true })\n accessor size: ComboboxSize = 'md';\n\n /** Show a button to clear the value. */\n @property({ type: Boolean, reflect: true, attribute: 'with-clear' })\n accessor withClear = false;\n\n /** Accept a typed value that matches no option. */\n @property({ type: Boolean, reflect: true, attribute: 'allow-custom' })\n accessor allowCustom = false;\n\n /** Wrap the matching substring of each option in `<mark>`. */\n @property({ type: Boolean })\n accessor highlight = true;\n\n /** Panel placement relative to the control. */\n @property()\n accessor placement: Placement = 'bottom-start';\n\n /** Override the option filter. `(item, query) => boolean`. */\n filter: ComboboxFilter = defaultFilter;\n\n @state() private accessor _open = false;\n @state() private accessor _query = '';\n @state() private accessor _items: ComboboxItem[] = [];\n\n private get _controlEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.control') ?? null;\n }\n private get _panelEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.panel') ?? null;\n }\n private get _inputEl() {\n return this.shadowRoot?.querySelector<HTMLInputElement>('.input') ?? null;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n // Sync rather than plain-read: the observer is disconnected while detached,\n // so options may have been re-labelled behind our back and the input — which\n // holds its own text, not a render of `_items` — would stay on the old one.\n this._syncItems();\n this._defaultFormValue = this.value;\n this._syncFormValue(this.value);\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('value')) {\n this._syncFormValue(this.value);\n this._updateValidity();\n if (!this._open) this._resetDisplay();\n }\n }\n\n override formResetCallback() {\n this.value = this._defaultFormValue;\n this._query = '';\n this._nav.reset();\n super.formResetCallback();\n this._resetDisplay();\n }\n\n override formStateRestoreCallback(state: string) {\n this.value = state;\n this._syncFormValue(state);\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 // Filter/display label: `label` attribute → rich title → flattened text.\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 { value: o.value, label, disabled: o.disabled, html: rich ? o.innerHTML : undefined };\n });\n }\n\n /**\n * Re-read the options after the light DOM changed underneath us.\n *\n * The input carries its own text rather than a render of `_items`, so it has\n * to be refreshed by hand — but only when it is still showing the label we put\n * there. Anything else in it is the user's (a query typed while the panel\n * stayed closed, because a consumer vetoed `show`) and must survive. A closed\n * panel is not on its own proof that nobody is typing.\n *\n * Open, the input is theirs by definition; what needs care there is the active\n * option, addressed by position in `_filtered` — re-anchor it on the option the\n * user had highlighted, or a list that grows or shrinks commits a different one.\n */\n private _syncItems() {\n const active = this._open ? this._filtered[this._nav.activeIndex] : undefined;\n const displayed = this._inputEl?.value;\n const untouched = displayed === undefined || displayed === this._labelForValue(this.value);\n this._readItems();\n if (!this._open) {\n if (untouched) this._resetDisplay();\n return;\n }\n this._nav.setActive(active ? this._filtered.findIndex((i) => i.value === active.value) : -1);\n }\n\n private _labelForValue(value: string): string {\n return this._items.find((i) => i.value === value)?.label ?? (this.allowCustom ? value : '');\n }\n\n private get _filtered(): ComboboxItem[] {\n return this._items.filter((i) => this.filter(i, this._query));\n }\n\n private _resetDisplay() {\n const input = this._inputEl;\n if (input) input.value = this._labelForValue(this.value);\n }\n\n // --- Validity ---\n\n private _updateValidity() {\n if (this.required && !this.value) {\n this.setValidity(\n { valueMissing: true },\n this._localize.term('selectOption'),\n this._inputEl ?? 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 // Pre-activate the chosen option so a screen reader announces the current\n // selection when the list opens (only when not actively filtering).\n if (this.value && !this._query) {\n this._nav.setActive(this._filtered.findIndex((i) => i.value === this.value));\n }\n 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 });\n }\n\n hide() {\n if (!this._open) return;\n if (!this.dispatchEvent(new Event('hide', { cancelable: true }))) return;\n this._open = false;\n this._nav.reset();\n const panel = this._panelEl as (HTMLElement & { hidePopover?: () => void }) | null;\n if (panel?.matches?.(':popover-open')) (panel as { hidePopover?: () => void }).hidePopover?.();\n this._floating.stopPositioning();\n this._resetDisplay();\n }\n\n // --- Selection ---\n\n private _commit(item: ComboboxItem) {\n if (item.disabled) return;\n this.value = item.value;\n this._query = '';\n this.hasInteracted = true;\n const input = this._inputEl;\n if (input) input.value = item.label;\n this._syncFormValue(this.value);\n this._updateValidity();\n this.dispatchEvent(new ComboboxChangeEvent(this.value));\n this.hide();\n input?.focus();\n }\n\n private _commitActiveOrCustom() {\n const filtered = this._filtered;\n const active = this._nav.activeIndex;\n if (active >= 0 && filtered[active]) {\n this._commit(filtered[active]);\n return;\n }\n const input = this._inputEl;\n if (this.allowCustom && input) {\n this.value = input.value;\n this._syncFormValue(this.value);\n this._updateValidity();\n this.dispatchEvent(new ComboboxChangeEvent(this.value));\n this.hide();\n }\n }\n\n private _clear() {\n this.value = '';\n this._query = '';\n this._nav.reset();\n if (this._inputEl) this._inputEl.value = '';\n this._syncFormValue('');\n this._updateValidity();\n this.dispatchEvent(new ComboboxChangeEvent(''));\n this._inputEl?.focus();\n }\n\n // --- Keyboard (navigation delegated to ListboxNavController) ---\n\n private _onKeyDown = (e: KeyboardEvent) => {\n // Arrow Up/Down move aria-activedescendant. Home/End are left to the input's\n // text cursor (APG editable combobox).\n if (this._nav.onKeyDown(e)) return;\n switch (e.key) {\n case 'Enter':\n if (this._open) {\n e.preventDefault();\n this._commitActiveOrCustom();\n }\n break;\n case 'Escape':\n if (this._open) {\n e.preventDefault();\n this.hide();\n } else if (this.value || this._inputEl?.value) {\n this._clear();\n }\n break;\n case 'Tab':\n if (this._open && this._nav.activeIndex >= 0) this._commitActiveOrCustom();\n break;\n default:\n break;\n }\n };\n\n private _onInput = (e: Event) => {\n this._query = (e.target as HTMLInputElement).value;\n this._nav.reset();\n if (!this._open) this.show();\n this.dispatchEvent(new ComboboxInputEvent(this._query));\n };\n\n private _onControlPointerDown = (e: PointerEvent) => {\n // Toggle on clicking the control chrome (not while typing).\n if ((e.target as HTMLElement).closest('.clear')) return;\n if (this._open) return;\n this.show();\n };\n\n private _onFocusOut = (e: FocusEvent) => {\n const next = e.relatedTarget as Node | null;\n if (next && this.shadowRoot?.contains(next)) return;\n this.hasInteracted = true;\n this._updateValidity();\n this.hide();\n };\n\n // --- Render ---\n\n private _highlight(label: string): TemplateResult | string {\n const q = this._query.trim();\n if (!this.highlight || !q) return label;\n const idx = normalize(label).indexOf(normalize(q).split(/\\s+/)[0]);\n if (idx < 0) return label;\n const before = label.slice(0, idx);\n const match = label.slice(idx, idx + q.length);\n const after = label.slice(idx + q.length);\n return html`${before}<mark>${match}</mark>${after}`;\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.value && this.hasInteracted;\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 @focusout=${this._onFocusOut}\n >\n <div\n class=\"control\"\n part=\"control\"\n @pointerdown=${this._onControlPointerDown}\n >\n <slot name=\"prefix\"></slot>\n <input\n class=\"input\"\n part=\"input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n .placeholder=${this.placeholder}\n ?disabled=${this.disabled}\n aria-label=${this.label || nothing}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-activedescendant=${activeId ?? nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @input=${this._onInput}\n @keydown=${this._onKeyDown}\n />\n ${this.withClear && (this.value || this._query)\n ? html`<button\n class=\"clear\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=${this._localize.term('clear')}\n @click=${this._clear}\n ></button>`\n : nothing}\n <span\n class=\"chevron\"\n aria-hidden=\"true\"\n ></span>\n </div>\n\n <div\n class=\"panel\"\n part=\"panel\"\n popover=\"manual\"\n >\n <ul\n class=\"listbox\"\n part=\"listbox\"\n role=\"listbox\"\n id=${this._listId}\n aria-label=${this.label || this._localize.term('suggestions')}\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=${i === activeIndex ? 'true' : 'false'}\n aria-disabled=${item.disabled ? 'true' : nothing}\n data-current=${item.value === this.value ? '' : nothing}\n @pointerdown=${(e: Event) => e.preventDefault()}\n @click=${() => this._commit(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 Combobox {\n addEventListener<K extends keyof ComboboxEventMap>(\n type: K,\n listener: (this: Combobox, ev: ComboboxEventMap[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 ComboboxEventMap>(\n type: K,\n listener: (this: Combobox, ev: ComboboxEventMap[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,2 +1,2 @@
|
|
|
1
|
-
import{i as e}from"../../chunks/lit.js";var t=e(`:host{--width:31rem;--border-radius:6px;--padding:1.5rem;--header-padding:var(--padding);--footer-padding:var(--padding);--show-duration:.2s;--hide-duration:.2s;--backdrop:var(--l-backdrop);--backdrop-blur:0;display:contents}dialog{box-sizing:border-box;width:var(--width);max-inline-size:min(90vw, var(--width));border-radius:var(--border-radius);background-color:var(--l-color-surface-overlay);max-block-size:min(80dvb,100%);color:var(--l-color-text-primary);opacity:0;transition-property:opacity,display,overlay;transition-duration:var(--hide-duration);transition-behavior:allow-discrete;border:0;margin:auto;padding:0;position:fixed;inset:0}dialog::backdrop{background:var(--backdrop);-webkit-backdrop-filter:blur(var(--backdrop-blur));backdrop-filter:blur(var(--backdrop-blur))}dialog[open]{opacity:1;transition-duration:var(--show-duration);grid-template-rows:auto minmax(0,1fr) auto;display:grid}@starting-style{dialog[open]{opacity:0}}[part=header]{padding:var(--header-padding);justify-content:space-between;align-items:center;gap:1rem;display:flex}[part=title]{
|
|
1
|
+
import{i as e}from"../../chunks/lit.js";var t=e(`:host{--width:31rem;--border-radius:6px;--padding:1.5rem;--header-padding:var(--padding);--footer-padding:var(--padding);--show-duration:.2s;--hide-duration:.2s;--backdrop:var(--l-backdrop);--backdrop-blur:0;display:contents}dialog{box-sizing:border-box;width:var(--width);max-inline-size:min(90vw, var(--width));border-radius:var(--border-radius);background-color:var(--l-color-surface-overlay);max-block-size:min(80dvb,100%);color:var(--l-color-text-primary);opacity:0;transition-property:opacity,display,overlay;transition-duration:var(--hide-duration);transition-behavior:allow-discrete;border:0;margin:auto;padding:0;position:fixed;inset:0}dialog::backdrop{background:var(--backdrop);-webkit-backdrop-filter:blur(var(--backdrop-blur));backdrop-filter:blur(var(--backdrop-blur))}dialog[open]{opacity:1;transition-duration:var(--show-duration);grid-template-rows:auto minmax(0,1fr) auto;display:grid}@starting-style{dialog[open]{opacity:0}}[part=header]{padding:var(--header-padding);justify-content:space-between;align-items:center;gap:1rem;display:flex}[part=title]{font-size:1.125rem;font-weight:var(--l-font-weight-semibold);margin:0;line-height:1.4}[part=body]{padding-inline:var(--padding);grid-row:2;overflow-y:auto}[part=footer]{padding:var(--footer-padding);grid-row:3;place-content:end;gap:.5rem;display:flex}[part=footer][data-empty]{display:none}[part=body]:has(~[part=footer][data-empty]){padding-block-end:var(--padding)}:host([without-header]) [part=body]{padding-block-start:var(--padding)}::slotted(menu[slot=footer]){display:contents}@media (prefers-reduced-motion:reduce){:host{--show-duration:0s;--hide-duration:0s}}`);export{t as default};
|
|
2
2
|
//# sourceMappingURL=dialog.styles.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.styles.js","names":[],"sources":["../../../src/html/elements/dialog/dialog.css?inline","../../../src/html/elements/dialog/dialog.styles.ts"],"sourcesContent":[":host {\n --width: 31rem;\n --border-radius: 6px;\n --padding: 1.5rem;\n --header-padding: var(--padding);\n --footer-padding: var(--padding);\n --show-duration: 200ms;\n --hide-duration: 200ms;\n --backdrop: var(--l-backdrop);\n --backdrop-blur: 0;\n\n display: contents;\n}\n\ndialog {\n position: fixed;\n inset: 0;\n box-sizing: border-box;\n width: var(--width);\n max-inline-size: min(90vw, var(--width));\n max-block-size: min(80dvb, 100%);\n margin: auto;\n padding: 0;\n border: 0;\n border-radius: var(--border-radius);\n background-color: var(--l-color-surface-overlay);\n color: var(--l-color-text-primary);\n\n /* EXIT STATE */\n opacity: 0;\n\n transition-property: opacity, display, overlay;\n transition-duration: var(--hide-duration);\n transition-behavior: allow-discrete;\n\n &::backdrop {\n background: var(--backdrop);\n backdrop-filter: blur(var(--backdrop-blur));\n }\n\n /* OPEN STATE */\n /* grid layout pins header/footer; the middle row (body) scrolls via\n overflow on [part='body'] and minmax(0, 1fr) allowing it to shrink. */\n &[open] {\n display: grid;\n grid-template-rows: auto minmax(0, 1fr) auto;\n opacity: 1;\n transition-duration: var(--show-duration);\n }\n\n /* BEFORE-OPEN STATE */\n @starting-style {\n &[open] {\n opacity: 0;\n }\n }\n}\n\n[part='header'] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 1rem;\n padding: var(--header-padding);\n}\n\n[part='title'] {\n margin: 0;\n font-size: 1.125rem;\n font-weight:
|
|
1
|
+
{"version":3,"file":"dialog.styles.js","names":[],"sources":["../../../src/html/elements/dialog/dialog.css?inline","../../../src/html/elements/dialog/dialog.styles.ts"],"sourcesContent":[":host {\n --width: 31rem;\n --border-radius: 6px;\n --padding: 1.5rem;\n --header-padding: var(--padding);\n --footer-padding: var(--padding);\n --show-duration: 200ms;\n --hide-duration: 200ms;\n --backdrop: var(--l-backdrop);\n --backdrop-blur: 0;\n\n display: contents;\n}\n\ndialog {\n position: fixed;\n inset: 0;\n box-sizing: border-box;\n width: var(--width);\n max-inline-size: min(90vw, var(--width));\n max-block-size: min(80dvb, 100%);\n margin: auto;\n padding: 0;\n border: 0;\n border-radius: var(--border-radius);\n background-color: var(--l-color-surface-overlay);\n color: var(--l-color-text-primary);\n\n /* EXIT STATE */\n opacity: 0;\n\n transition-property: opacity, display, overlay;\n transition-duration: var(--hide-duration);\n transition-behavior: allow-discrete;\n\n &::backdrop {\n background: var(--backdrop);\n backdrop-filter: blur(var(--backdrop-blur));\n }\n\n /* OPEN STATE */\n /* grid layout pins header/footer; the middle row (body) scrolls via\n overflow on [part='body'] and minmax(0, 1fr) allowing it to shrink. */\n &[open] {\n display: grid;\n grid-template-rows: auto minmax(0, 1fr) auto;\n opacity: 1;\n transition-duration: var(--show-duration);\n }\n\n /* BEFORE-OPEN STATE */\n @starting-style {\n &[open] {\n opacity: 0;\n }\n }\n}\n\n[part='header'] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 1rem;\n padding: var(--header-padding);\n}\n\n[part='title'] {\n margin: 0;\n font-size: 1.125rem;\n font-weight: var(--l-font-weight-semibold);\n line-height: 1.4;\n}\n\n/* Pin body and footer so the layout stays correct when [without-header]\n removes the header and only two children auto-place into the grid. */\n[part='body'] {\n grid-row: 2;\n padding-inline: var(--padding);\n overflow-y: auto;\n}\n\n[part='footer'] {\n grid-row: 3;\n display: flex;\n place-content: end;\n gap: 0.5rem;\n padding: var(--footer-padding);\n}\n\n/* Collapse the footer row when nothing is slotted into it, so an empty\n dialog/drawer reserves no space at the bottom. `data-empty` is toggled by the\n HasSlotController in dialog.ts: `:host(:has(...))` can't be used because\n `:has()` is invalid inside `:host()` and the whole rule is dropped by the\n browser. */\n[part='footer'][data-empty] {\n display: none;\n}\n\n/* With the footer collapsed, the body has to provide its own block-end padding\n (normally inherited visually from the footer padding below it). */\n[part='body']:has(~ [part='footer'][data-empty]) {\n padding-block-end: var(--padding);\n}\n\n/* Without a header, the body has to provide its own block-start padding\n (normally inherited visually from the header padding above it). */\n:host([without-header]) [part='body'] {\n padding-block-start: var(--padding);\n}\n\n::slotted(menu[slot='footer']) {\n display: contents;\n}\n\n@media (prefers-reduced-motion: reduce) {\n :host {\n --show-duration: 0ms;\n --hide-duration: 0ms;\n }\n}\n","import { unsafeCSS } from 'lit';\nimport raw from './dialog.css?inline';\n\n/**\n * Wrapper module: imported by both `dialog.ts` and `drawer.ts`.\n * `unsafeCSS()` is called once here so both importers share the same\n * `CSSResult` instance (one constructed `CSSStyleSheet`, not two).\n */\nexport default unsafeCSS(raw);\n"],"mappings":"wCCQA,IAAA,EAAe,EAAU,ymDAAG"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{i as e}from"../../chunks/lit.js";import{a as t}from"../../chunks/lit-html.js";import{LuxenElement as n}from"../../shared/luxen-element.js";import r from"../../shared/styles/host.styles.js";var i=e(`:host{--color:var(--l-color-text-tertiary);display:block}.label{letter-spacing:.03em;text-transform:uppercase;color:var(--color);white-space:nowrap;-webkit-user-select:none;user-select:none;padding:.375rem .5rem .125rem;
|
|
1
|
+
import{i as e}from"../../chunks/lit.js";import{a as t}from"../../chunks/lit-html.js";import{LuxenElement as n}from"../../shared/luxen-element.js";import r from"../../shared/styles/host.styles.js";var i=e(`:host{--color:var(--l-color-text-tertiary);display:block}.label{font-size:.6875rem;font-weight:var(--l-font-weight-semibold);letter-spacing:.03em;text-transform:uppercase;color:var(--color);white-space:nowrap;-webkit-user-select:none;user-select:none;padding:.375rem .5rem .125rem;line-height:1.4}`),a=class extends n{static{this.styles=[r,i]}connectedCallback(){super.connectedCallback(),this.setAttribute(`role`,`presentation`)}render(){return t`<div class="label">
|
|
2
2
|
<slot></slot>
|
|
3
3
|
</div>`}};export{a as DropdownLabel};
|
|
4
4
|
//# sourceMappingURL=dropdown-label.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-label.js","names":[],"sources":["../../../src/html/elements/dropdown-label/dropdown-label.css?inline","../../../src/html/elements/dropdown-label/dropdown-label.ts"],"sourcesContent":[":host {\n display: block;\n --color: var(--l-color-text-tertiary);\n}\n\n.label {\n /* Inline padding aligns with dropdown-item's `0.375rem 0.5rem` so labels and\n items line up; extra top padding adds breathing room above each section. */\n padding: 0.375rem 0.5rem 0.125rem;\n font-size: 0.6875rem; /* 11px */\n font-weight:
|
|
1
|
+
{"version":3,"file":"dropdown-label.js","names":[],"sources":["../../../src/html/elements/dropdown-label/dropdown-label.css?inline","../../../src/html/elements/dropdown-label/dropdown-label.ts"],"sourcesContent":[":host {\n display: block;\n --color: var(--l-color-text-tertiary);\n}\n\n.label {\n /* Inline padding aligns with dropdown-item's `0.375rem 0.5rem` so labels and\n items line up; extra top padding adds breathing room above each section. */\n padding: 0.375rem 0.5rem 0.125rem;\n font-size: 0.6875rem; /* 11px */\n font-weight: var(--l-font-weight-semibold);\n letter-spacing: 0.03em;\n text-transform: uppercase;\n line-height: 1.4;\n color: var(--color);\n white-space: nowrap;\n user-select: none;\n}\n","import { html, unsafeCSS, type CSSResultGroup } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './dropdown-label.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * A non-interactive section label for grouping items inside `<l-dropdown>`.\n *\n * @slot - Label text.\n *\n * @cssproperty --color - Text color. Defaults to `var(--l-color-text-tertiary)`.\n *\n * @customElement l-dropdown-label\n */\nexport class DropdownLabel extends LuxenElement {\n static override styles: CSSResultGroup = [hostStyles, styles];\n\n override connectedCallback() {\n super.connectedCallback();\n // Set the role on the host — that's the node that lands in the\n // `role=\"menu\"` light-DOM subtree (mirrors `l-divider`'s `role=\"separator\"`).\n // A role on the shadow-DOM `.label` <div> would not change how the host is\n // announced. `presentation` keeps the label out of menu-item navigation\n // while its text is still read as plain content.\n this.setAttribute('role', 'presentation');\n }\n\n override render() {\n return html`<div class=\"label\">\n <slot></slot>\n </div>`;\n }\n}\n"],"mappings":"oMCKA,IAAM,EAAS,EAAU,2SAAS,EAWrB,EAAb,cAAmC,CAAa,oBACL,CAAC,EAAY,CAAM,EAE5D,mBAA6B,CAC3B,MAAM,kBAAkB,EAMxB,KAAK,aAAa,OAAQ,cAAc,CAC1C,CAEA,QAAkB,CAChB,MAAO,EAAI;;WAGb,CACF"}
|
|
@@ -77,6 +77,7 @@ export declare class Select extends LuxenFormAssociatedElement {
|
|
|
77
77
|
private readonly _listId;
|
|
78
78
|
private _floating;
|
|
79
79
|
private _nav;
|
|
80
|
+
private _options;
|
|
80
81
|
/** Placeholder shown in the trigger when nothing is selected. */
|
|
81
82
|
accessor placeholder: string;
|
|
82
83
|
/** Accessible label for the trigger. */
|
|
@@ -110,6 +111,15 @@ export declare class Select extends LuxenFormAssociatedElement {
|
|
|
110
111
|
formResetCallback(): void;
|
|
111
112
|
formStateRestoreCallback(state: string): void;
|
|
112
113
|
private _readItems;
|
|
114
|
+
/**
|
|
115
|
+
* Re-read the options after the light DOM changed underneath us. The active
|
|
116
|
+
* option is addressed by position in `_filtered`, so a list that grows or
|
|
117
|
+
* shrinks while the listbox is open would silently move the keyboard
|
|
118
|
+
* selection onto a different row (or off the end, leaving Enter dead).
|
|
119
|
+
* Re-anchor it on the option the user had highlighted, and drop it when that
|
|
120
|
+
* option is gone.
|
|
121
|
+
*/
|
|
122
|
+
private _syncItems;
|
|
113
123
|
private _itemFor;
|
|
114
124
|
private get _filtered();
|
|
115
125
|
/** Push the current selection to the form (single value, or one entry per value). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../src/html/elements/select/select.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAI1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;
|
|
1
|
+
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../src/html/elements/select/select.ts"],"names":[],"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}[part=tag]{--height:initial;--border-radius: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}
|
|
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";import{DatalistObserverController as _}from"../../shared/controllers/datalist-observer.js";var v=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}.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;font-weight:var(--l-font-weight-semibold);background:0 0}.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:var(--l-font-weight-semibold);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 y(e){return e.normalize(`NFD`).replace(/[̀-ͯ]/g,``).toLowerCase()}var b=(e,t)=>{if(!t)return!0;let n=y(e.label);return y(t).split(/\s+/).filter(Boolean).every(e=>n.includes(e))},x=class extends Event{constructor(e){super(`change`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},S=class extends Event{constructor(e){super(`input`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},C=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._options=new _(this,()=>this._syncItems()),this.#e=``,this.#t=``,this.#n=`md`,this.#r=!1,this.#i=!1,this.#a=`bottom-start`,this.#o=!1,this.filter=b,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 S(this._query))}}static{this.styles=[f,v]}#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}})}_syncItems(){let e=this._open?this._filtered[this._nav.activeIndex]:void 0;this._readItems(),this._open&&this._nav.setActive(e?this._filtered.findIndex(t=>t.value===e.value):-1)}_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 x(this.value)),this._searchEl?.focus()):(this._values=[e.value],this.dispatchEvent(new x(this.value)),this.hide()))}_removeValue(e){this._values=this._values.filter(t=>t!==e),this.hasInteracted=!0,this.dispatchEvent(new x(this.value))}_clear(e){e?.stopPropagation(),this._values=[],this.hasInteracted=!0,this.dispatchEvent(new x(this.value)),this._triggerEl?.focus()}_highlight(e){let t=this._query.trim();if(!t)return e;let n=y(e).indexOf(y(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"
|
|
@@ -120,5 +120,5 @@ import{cls as e,tagName as t,uniqueId as n}from"../../registry.js";import{i as r
|
|
|
120
120
|
${a}
|
|
121
121
|
</div>
|
|
122
122
|
</div>
|
|
123
|
-
`}};o([i()],
|
|
123
|
+
`}};o([i()],C.prototype,`placeholder`,null),o([i()],C.prototype,`label`,null),o([i({reflect:!0})],C.prototype,`size`,null),o([i({type:Boolean,reflect:!0})],C.prototype,`searchable`,null),o([i({type:Boolean,reflect:!0,attribute:`with-clear`})],C.prototype,`withClear`,null),o([i()],C.prototype,`placement`,null),o([i({type:Boolean,reflect:!0})],C.prototype,`multiple`,null),o([a()],C.prototype,`_values`,null),o([a()],C.prototype,`_open`,null),o([a()],C.prototype,`_query`,null),o([a()],C.prototype,`_items`,null);export{C as Select,x as SelectChangeEvent,S as SelectInputEvent};
|
|
124
124
|
//# sourceMappingURL=select.js.map
|