luxen-ui 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/cdn/custom-elements.json +203 -170
  2. package/cdn/elements/combobox/combobox.d.ts +6 -1
  3. package/cdn/elements/combobox/combobox.d.ts.map +1 -1
  4. package/cdn/elements/combobox/combobox.js +19 -17
  5. package/cdn/elements/combobox/combobox.js.map +1 -1
  6. package/cdn/elements/dropdown/dropdown.d.ts +6 -7
  7. package/cdn/elements/dropdown/dropdown.d.ts.map +1 -1
  8. package/cdn/elements/dropdown/dropdown.js +2 -2
  9. package/cdn/elements/dropdown/dropdown.js.map +1 -1
  10. package/cdn/elements/dropdown-item/dropdown-item.d.ts +0 -1
  11. package/cdn/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
  12. package/cdn/elements/dropdown-item/dropdown-item.js +2 -2
  13. package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
  14. package/cdn/elements/dropdown-label/dropdown-label.js +1 -1
  15. package/cdn/elements/dropdown-label/dropdown-label.js.map +1 -1
  16. package/cdn/elements/form-field/form-field.d.ts +1 -1
  17. package/cdn/elements/form-field/form-field.js.map +1 -1
  18. package/cdn/elements/input/input.meta.d.ts +2 -0
  19. package/cdn/elements/input/input.meta.d.ts.map +1 -1
  20. package/cdn/elements/input-otp/input-otp.d.ts +3 -3
  21. package/cdn/elements/input-otp/input-otp.js.map +1 -1
  22. package/cdn/elements/popover/popover.d.ts +3 -4
  23. package/cdn/elements/popover/popover.d.ts.map +1 -1
  24. package/cdn/elements/popover/popover.js +1 -1
  25. package/cdn/elements/popover/popover.js.map +1 -1
  26. package/cdn/elements/select/select.d.ts +6 -1
  27. package/cdn/elements/select/select.d.ts.map +1 -1
  28. package/cdn/elements/select/select.js +8 -7
  29. package/cdn/elements/select/select.js.map +1 -1
  30. package/cdn/elements/stories-viewer/stories-viewer.d.ts.map +1 -1
  31. package/cdn/elements/stories-viewer/stories-viewer.js +4 -4
  32. package/cdn/elements/stories-viewer/stories-viewer.js.map +1 -1
  33. package/cdn/elements/tooltip/tooltip.d.ts +0 -1
  34. package/cdn/elements/tooltip/tooltip.d.ts.map +1 -1
  35. package/cdn/elements/tooltip/tooltip.js +1 -1
  36. package/cdn/elements/tooltip/tooltip.js.map +1 -1
  37. package/cdn/shared/controllers/listbox-nav.d.ts +15 -1
  38. package/cdn/shared/controllers/listbox-nav.d.ts.map +1 -1
  39. package/cdn/shared/controllers/listbox-nav.js +1 -1
  40. package/cdn/shared/controllers/listbox-nav.js.map +1 -1
  41. package/cdn/shared/controllers/popover.d.ts +10 -2
  42. package/cdn/shared/controllers/popover.d.ts.map +1 -1
  43. package/cdn/shared/controllers/popover.js +1 -1
  44. package/cdn/shared/controllers/popover.js.map +1 -1
  45. package/cdn/shared/styles/checkbox-appearance.styles.js +1 -1
  46. package/cdn/shared/styles/checkbox-appearance.styles.js.map +1 -1
  47. package/cdn/shared/styles/menu-panel.styles.d.ts +8 -0
  48. package/cdn/shared/styles/menu-panel.styles.d.ts.map +1 -0
  49. package/cdn/shared/styles/menu-panel.styles.js +2 -0
  50. package/cdn/shared/styles/menu-panel.styles.js.map +1 -0
  51. package/cdn/shared/styles/picker-core.styles.d.ts +9 -0
  52. package/cdn/shared/styles/picker-core.styles.d.ts.map +1 -0
  53. package/cdn/shared/styles/picker-core.styles.js +2 -0
  54. package/cdn/shared/styles/picker-core.styles.js.map +1 -0
  55. package/cdn/shared/styles/select-item-suffix.styles.js +1 -1
  56. package/cdn/shared/styles/select-item-suffix.styles.js.map +1 -1
  57. package/cdn/shared/styles/select-item.styles.d.ts +8 -0
  58. package/cdn/shared/styles/select-item.styles.d.ts.map +1 -0
  59. package/cdn/shared/styles/select-item.styles.js +2 -0
  60. package/cdn/shared/styles/select-item.styles.js.map +1 -0
  61. package/cdn/shared/styles/visually-hidden.styles.d.ts +8 -0
  62. package/cdn/shared/styles/visually-hidden.styles.d.ts.map +1 -0
  63. package/cdn/shared/styles/visually-hidden.styles.js +2 -0
  64. package/cdn/shared/styles/visually-hidden.styles.js.map +1 -0
  65. package/cdn/standalone.css +365 -196
  66. package/cdn/standalone.js +148 -75
  67. package/cdn/standalone.js.map +1 -1
  68. package/cdn/styles/base.css +30 -20
  69. package/cdn/styles/elements/checkbox.css +2 -2
  70. package/cdn/styles/elements/form-field.css +16 -9
  71. package/cdn/styles/elements/input-otp.css +16 -9
  72. package/cdn/styles/elements/input-stepper/default.css +4 -4
  73. package/cdn/styles/elements/input-stepper/pill.css +4 -4
  74. package/cdn/styles/elements/input-stepper/rounded.css +3 -3
  75. package/cdn/styles/elements/input.css +82 -27
  76. package/cdn/styles/elements/radio.css +2 -2
  77. package/cdn/styles/elements/select.css +137 -80
  78. package/cdn/styles/elements/switch.css +3 -3
  79. package/cdn/styles/elements/textarea.css +5 -5
  80. package/cdn/styles/preset.css +92 -49
  81. package/cdn/styles/tokens/forms.css +33 -30
  82. package/cdn/styles/tokens.css +62 -29
  83. package/dist/css/base.css +30 -20
  84. package/dist/css/elements/checkbox.css +2 -2
  85. package/dist/css/elements/form-field.css +16 -9
  86. package/dist/css/elements/input-otp.css +16 -9
  87. package/dist/css/elements/input-stepper/default.css +4 -4
  88. package/dist/css/elements/input-stepper/pill.css +4 -4
  89. package/dist/css/elements/input-stepper/rounded.css +3 -3
  90. package/dist/css/elements/input.css +82 -27
  91. package/dist/css/elements/radio.css +2 -2
  92. package/dist/css/elements/select.css +137 -80
  93. package/dist/css/elements/switch.css +3 -3
  94. package/dist/css/elements/textarea.css +5 -5
  95. package/dist/css/preset.css +92 -49
  96. package/dist/css/tokens/forms.css +33 -30
  97. package/dist/css/tokens.css +62 -29
  98. package/dist/custom-elements.json +203 -170
  99. package/dist/elements/combobox/combobox.css +30 -187
  100. package/dist/elements/combobox/combobox.d.ts +6 -1
  101. package/dist/elements/combobox/combobox.d.ts.map +1 -1
  102. package/dist/elements/combobox/combobox.js +48 -20
  103. package/dist/elements/dropdown/dropdown.css +7 -30
  104. package/dist/elements/dropdown/dropdown.d.ts +6 -7
  105. package/dist/elements/dropdown/dropdown.d.ts.map +1 -1
  106. package/dist/elements/dropdown/dropdown.js +17 -14
  107. package/dist/elements/dropdown-item/dropdown-item.css +7 -36
  108. package/dist/elements/dropdown-item/dropdown-item.d.ts +0 -1
  109. package/dist/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
  110. package/dist/elements/dropdown-item/dropdown-item.js +4 -7
  111. package/dist/elements/dropdown-label/dropdown-label.css +3 -3
  112. package/dist/elements/form-field/form-field.d.ts +1 -1
  113. package/dist/elements/form-field/form-field.js +2 -2
  114. package/dist/elements/input/input.meta.d.ts +2 -0
  115. package/dist/elements/input/input.meta.d.ts.map +1 -1
  116. package/dist/elements/input/input.meta.js +2 -0
  117. package/dist/elements/input-otp/input-otp.d.ts +3 -3
  118. package/dist/elements/input-otp/input-otp.js +3 -3
  119. package/dist/elements/popover/popover.css +4 -4
  120. package/dist/elements/popover/popover.d.ts +3 -4
  121. package/dist/elements/popover/popover.d.ts.map +1 -1
  122. package/dist/elements/popover/popover.js +5 -9
  123. package/dist/elements/select/select.css +24 -260
  124. package/dist/elements/select/select.d.ts +6 -1
  125. package/dist/elements/select/select.d.ts.map +1 -1
  126. package/dist/elements/select/select.js +27 -9
  127. package/dist/elements/stories-viewer/stories-viewer.css +0 -12
  128. package/dist/elements/stories-viewer/stories-viewer.d.ts.map +1 -1
  129. package/dist/elements/stories-viewer/stories-viewer.js +4 -3
  130. package/dist/elements/tooltip/tooltip.d.ts +0 -1
  131. package/dist/elements/tooltip/tooltip.d.ts.map +1 -1
  132. package/dist/elements/tooltip/tooltip.js +2 -6
  133. package/dist/metadata/combobox.json +16 -1
  134. package/dist/metadata/dropdown.json +10 -10
  135. package/dist/metadata/form-field.json +1 -1
  136. package/dist/metadata/index.json +57 -22
  137. package/dist/metadata/input-otp.json +3 -3
  138. package/dist/metadata/input.json +5 -0
  139. package/dist/metadata/popover.json +5 -5
  140. package/dist/metadata/select.json +16 -1
  141. package/dist/shared/controllers/listbox-nav.d.ts +15 -1
  142. package/dist/shared/controllers/listbox-nav.d.ts.map +1 -1
  143. package/dist/shared/controllers/listbox-nav.js +32 -1
  144. package/dist/shared/controllers/popover.d.ts +10 -2
  145. package/dist/shared/controllers/popover.d.ts.map +1 -1
  146. package/dist/shared/controllers/popover.js +16 -2
  147. package/dist/shared/styles/checkbox-appearance.css +2 -2
  148. package/dist/shared/styles/menu-panel.css +37 -0
  149. package/dist/shared/styles/menu-panel.styles.d.ts +8 -0
  150. package/dist/shared/styles/menu-panel.styles.d.ts.map +1 -0
  151. package/dist/shared/styles/menu-panel.styles.js +8 -0
  152. package/dist/shared/styles/picker-core.css +250 -0
  153. package/dist/shared/styles/picker-core.styles.d.ts +9 -0
  154. package/dist/shared/styles/picker-core.styles.d.ts.map +1 -0
  155. package/dist/shared/styles/picker-core.styles.js +9 -0
  156. package/dist/shared/styles/select-item-suffix.css +4 -4
  157. package/dist/shared/styles/select-item.css +98 -0
  158. package/dist/shared/styles/select-item.styles.d.ts +8 -0
  159. package/dist/shared/styles/select-item.styles.d.ts.map +1 -0
  160. package/dist/shared/styles/select-item.styles.js +8 -0
  161. package/dist/shared/styles/visually-hidden.css +27 -0
  162. package/dist/shared/styles/visually-hidden.styles.d.ts +8 -0
  163. package/dist/shared/styles/visually-hidden.styles.d.ts.map +1 -0
  164. package/dist/shared/styles/visually-hidden.styles.js +8 -0
  165. package/dist/templates/elements/combobox.md +26 -1
  166. package/dist/templates/elements/disclosure.md +2 -0
  167. package/dist/templates/elements/dropdown.md +6 -6
  168. package/dist/templates/elements/form-field.md +2 -2
  169. package/dist/templates/elements/input-otp.md +3 -3
  170. package/dist/templates/elements/input.md +1 -0
  171. package/dist/templates/elements/popover.md +3 -3
  172. package/dist/templates/elements/select.md +30 -1
  173. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","names":["unsafeCSS","rawStyles","hostStyles","suffixStyles","unsafeHTML","unsafeStatic","nothing","_decorate","property","state","r","s","u","r$1","n","__decorate","b","A","o","host_styles_default","select_item_suffix_styles_default","styles","defaultFilter","query","label","normalize","item","kw","SelectChangeEvent","value","SelectInputEvent","Select","LuxenFormAssociatedElement","_args","LocalizeController","uniqueId","RichHighlighter","PopoverController","ListboxNavController","i","DatalistObserverController","#_placeholder_accessor_storage","#_label_accessor_storage","#_size_accessor_storage","#_searchable_accessor_storage","#_withClear_accessor_storage","#_placement_accessor_storage","#_multiple_accessor_storage","#__values_accessor_storage","#__open_accessor_storage","#__query_accessor_storage","#__items_accessor_storage","active","v","attr","selected","changed","state","list","opts","rich","richOptionLabel","adoptRichContentStyles","data","panel","focusTrigger","chip","tagName","inner","onClick","invalid","filtered","activeId","activeIndex","hasValue","status","highlightText"],"sources":["../../../src/html/elements/select/select.css?inline","../../../src/html/elements/select/select.ts"],"sourcesContent":[":host {\n --height: var(--l-form-control-height);\n --border-radius: var(--l-form-control-border-radius);\n --background: var(--l-color-surface-overlay);\n\n display: block;\n inline-size: 100%;\n}\n\n:host([size='xs']) {\n --height: var(--l-size-control-xs);\n}\n:host([size='sm']) {\n --height: var(--l-size-control-sm);\n}\n:host([size='lg']) {\n --height: var(--l-size-control-lg);\n}\n:host([size='xl']) {\n --height: var(--l-size-control-xl);\n}\n\n:host([disabled]) {\n opacity: 0.5;\n pointer-events: none;\n}\n\n.base {\n position: relative;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆃🆁🅸🅶🅶🅴🆁 (button, opens the listbox popover)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.trigger {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n inline-size: 100%;\n min-block-size: var(--height);\n padding-block: 0.25rem;\n padding-inline: var(--l-form-control-padding-inline);\n border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);\n border-radius: var(--border-radius);\n background-color: var(--l-form-control-background-color);\n color: var(--l-form-control-value-color);\n font: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n/* The form-control inline padding is tuned for text; before a chip row it reads\n as too much, so tighten the start edge in multiple mode (the chips bring their\n own spacing). */\n:host([multiple]) .trigger {\n padding-inline-start: 0.375rem;\n}\n\n@media (hover: hover) {\n .trigger:hover {\n border-color: var(--l-form-control-border-color-hover);\n }\n}\n\n.trigger:focus-visible {\n outline: none;\n border-color: var(--l-focus-ring);\n box-shadow: 0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent);\n}\n\n/* Selection display fills the trigger; the chevron (and overlaid clear) sit at the end. */\n.value {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 0.25rem;\n overflow: hidden;\n}\n\n.value-text {\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* This element's public knobs and the chip's share two names — `--height` and\n `--border-radius` — and custom properties inherit straight through a shadow\n boundary. Left alone, every chip in the trigger renders at the *select's*\n height and corner rather than its own: a 36px chip with a 6px corner, when\n `size=\"sm\"` asked for a 24px pill. `initial` is the guaranteed-invalid value,\n so each `var(--x, …)` inside the chip falls back to its own default. Matched\n on the part, not on the tag name, so a renamed `elementPrefix` can't miss it.\n\n The fill used to collide too, back when the chip read `--background`; it now\n reads `--background-color`, which this element does not expose. */\n[part='tag'] {\n --height: initial;\n --border-radius: initial;\n}\n\n.placeholder {\n color: var(--l-form-control-placeholder-color);\n}\n\n.chevron {\n flex: none;\n inline-size: 1em;\n block-size: 1em;\n background-color: currentColor;\n color: var(--l-form-control-placeholder-color);\n mask: var(--_chevron-icon) center / contain no-repeat;\n transition: rotate 0.15s ease;\n}\n\n.trigger[aria-expanded='true'] .chevron {\n rotate: 180deg;\n}\n\n/* Clear is a SIBLING of the trigger (not a child — no nested buttons), overlaid\n * over the trigger's end, before the chevron. ≥24px hit area (WCAG 2.5.8). */\n.clear {\n position: absolute;\n inset-block: 0;\n inset-inline-end: calc(var(--l-form-control-padding-inline) + 1.25em);\n margin-block: auto;\n inline-size: 1.5rem;\n block-size: 1.5rem;\n border: 0;\n padding: 0;\n cursor: pointer;\n background-color: currentColor;\n color: var(--l-form-control-placeholder-color);\n mask: var(--_x-icon) center / 1em no-repeat;\n}\n\n@media (hover: hover) {\n .clear:hover {\n color: var(--l-form-control-value-color);\n }\n}\n\n.clear:focus-visible {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 2px;\n}\n\n/* Rich content rendered in the trigger mirrors the option rows. */\n.value .l-select-item-media {\n flex: none;\n display: inline-flex;\n align-items: center;\n font-size: 1.25em;\n}\n.value .l-select-item-text {\n display: flex;\n flex-direction: column;\n min-inline-size: 0;\n}\n/* Mirror the native trigger: show only the media and title, drop the secondary\n description and the trailing suffix so the selected value stays compact and\n consistent across options. */\n.value .l-select-item-title {\n font-weight: inherit;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.value .l-select-item-description,\n.value .l-select-item-suffix {\n display: none;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅿🅰🅽🅴🅻 (popover: search + listbox)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.panel {\n inset: auto;\n margin: 0;\n padding: 0;\n border: 1px solid var(--l-color-border-overlay);\n border-radius: var(--border-radius);\n background-color: var(--background);\n color: var(--l-color-text-primary);\n box-shadow: var(--l-shadow-md);\n font-size: 0.875rem;\n line-height: 1.5;\n overflow: hidden;\n opacity: 0;\n scale: 0.98;\n transition:\n opacity 0.12s ease,\n scale 0.12s ease,\n display 0.12s allow-discrete,\n overlay 0.12s allow-discrete;\n}\n\n.panel:popover-open {\n opacity: 1;\n scale: 1;\n}\n\n@starting-style {\n .panel:popover-open {\n opacity: 0;\n scale: 0.98;\n }\n}\n\n.search-row {\n padding: 0.375rem;\n border-block-end: 1px solid var(--l-color-border-overlay);\n}\n\n.search {\n inline-size: 100%;\n block-size: var(--l-size-control-sm);\n padding-inline: 0.5rem;\n border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);\n border-radius: calc(var(--border-radius) - 2px);\n background-color: var(--l-form-control-background-color);\n color: var(--l-form-control-value-color);\n font: inherit;\n}\n\n.search:focus-visible {\n outline: none;\n border-color: var(--l-focus-ring);\n}\n\n.search::placeholder {\n color: var(--l-form-control-placeholder-color);\n}\n\n.listbox {\n margin: 0;\n padding: 0.25rem;\n list-style: none;\n max-block-size: 16rem;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n/* Mirrors the native `.l-select` picker (itself calé on l-dropdown): the\n * checkmark lives in a reserved inline-start column, only the hovered/active row\n * takes the tint, and a selected row carries no background (checkmark only). */\n.option {\n display: flex;\n position: relative;\n align-items: start;\n gap: 0.5rem;\n min-block-size: 36px;\n padding-block: 0.5rem;\n padding-inline: 1.75rem 0.5rem;\n border-radius: 4px;\n color: var(--l-color-text-primary);\n cursor: pointer;\n}\n\n@media (hover: hover) {\n .option:hover {\n background-color: var(--l-color-bg-state-hover);\n }\n}\n\n/* Keyboard-active row (moves with Arrow keys) — same tint as hover. */\n.option[data-active] {\n background-color: var(--l-color-bg-state-hover);\n}\n\n.option[aria-disabled='true'] {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n/* Selected indicator in the reserved inline-start column, pinned to the first\n * line so a wrapping description never shifts it (matches the native picker). */\n.option[data-current]::after {\n content: '';\n position: absolute;\n inset-inline-start: 0.5rem;\n inset-block-start: 0.5rem;\n inline-size: 1rem;\n block-size: 1.3125rem;\n background-color: currentColor;\n mask: var(--_check-icon) center / 1rem no-repeat;\n}\n\n.option-label {\n flex: 1;\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* The part of an option that matches the search query. Scoped to the marks the\n * highlight adds, so a `<mark>` authored in a rich option keeps its own look. */\nmark.match {\n border-radius: 0.2em;\n background-color: var(--l-form-control-match-background-color);\n color: inherit;\n font-weight: var(--l-form-control-match-font-weight);\n box-decoration-break: clone;\n}\n\n.option-rich {\n display: flex;\n align-items: start;\n gap: 0.5rem;\n flex: 1;\n min-inline-size: 0;\n}\n\n.option-rich .l-select-item-media {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n inline-size: 1.5rem;\n block-size: 1.5rem;\n border-radius: 4px;\n object-fit: cover;\n font-size: 1.25em;\n}\n\n.option-rich .l-select-item-text {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-inline-size: 0;\n}\n\n.option-rich .l-select-item-title {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: var(--l-font-weight-semibold);\n}\n\n.option-rich .l-select-item-description {\n color: var(--l-color-text-tertiary);\n font-size: 0.75rem;\n line-height: 1.3;\n white-space: normal;\n text-wrap: pretty;\n}\n\n.empty {\n padding: 0.5rem;\n color: var(--l-color-text-tertiary);\n text-align: center;\n}\n\n.visually-hidden {\n position: absolute;\n inline-size: 1px;\n block-size: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅸🅲🅾🅽🆂\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n:host {\n --_chevron-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\");\n --_x-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E\");\n --_check-icon: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E\");\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel,\n .chevron {\n transition-duration: 0ms;\n }\n}\n","import { html, nothing, unsafeCSS } from 'lit';\nimport { html as staticHtml, unsafeStatic } from 'lit/static-html.js';\nimport { property, state } from 'lit/decorators.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { LuxenFormAssociatedElement } from '../../shared/luxen-form-associated-element.js';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { ListboxNavController } from '../../shared/controllers/listbox-nav.js';\nimport { DatalistObserverController } from '../../shared/controllers/datalist-observer.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { RichHighlighter, highlightText, normalize } from '../../shared/highlight.js';\nimport { tagName, uniqueId } from '../../registry.js';\nimport { adoptRichContentStyles, richOptionLabel } from '../../shared/rich-option.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport suffixStyles from '../../shared/styles/select-item-suffix.styles.js';\nimport rawStyles from './select.css?inline';\n// Registers the chip element used to render selected values in multiple mode.\nimport '../tag/index.js';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type SelectSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\n/** A single option read from the `<datalist>`. */\nexport interface SelectItem {\n value: string;\n /** Plain text used for filtering and the trigger display. */\n label: string;\n disabled: boolean;\n /** Authored `selected` attribute — seeds the initial value. */\n selected: boolean;\n /** Rich inner markup (from a non-empty `<option>`), projected into the row. */\n html?: string;\n}\n\n/** Default filter: accent/case-insensitive, every space-separated keyword must match. */\nexport type SelectFilter = (item: SelectItem, query: string) => boolean;\n\nconst defaultFilter: SelectFilter = (item, query) => {\n if (!query) return true;\n const label = normalize(item.label);\n return normalize(query)\n .split(/\\s+/)\n .filter(Boolean)\n .every((kw) => label.includes(kw));\n};\n\n/** Fired when the selection changes. Bubbles; not composed. */\nexport class SelectChangeEvent extends Event {\n /** The selected value (single mode) or values (multiple mode). */\n readonly value: string | string[];\n constructor(value: string | string[]) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\n/** Fired as the user types in the search box. Bubbles; not composed. */\nexport class SelectInputEvent extends Event {\n /** The current query text. */\n readonly value: string;\n constructor(value: string) {\n super('input', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\ninterface SelectEventMap {\n change: SelectChangeEvent;\n input: SelectInputEvent;\n}\n\n/**\n * A button-triggered, searchable select. A trigger shows the current selection,\n * opening a popover with a search box and a `role=\"listbox\"` of options authored\n * as a native `<datalist>` of `<option>` — the same authoring surface as\n * `l-combobox` and the native `.l-select`. Form-associated.\n *\n * @summary A searchable select with a button trigger and a popover listbox.\n *\n * @example\n * ```html\n * <l-select label=\"Country\" name=\"country\" placeholder=\"Select a country…\">\n * <datalist>\n * <option value=\"us\">United States</option>\n * <option value=\"fr\" selected>France</option>\n * </datalist>\n * </l-select>\n * ```\n *\n * @event change - Fired when the selection changes. Bubbles. Not cancelable. Properties: `value: string | string[]`.\n * @event input - Fired as the user types in the search box. Bubbles. Not cancelable. Properties: `value: string` (the query).\n * @event show - Fired before the listbox opens. Cancelable.\n * @event hide - Fired before the listbox closes. Cancelable.\n *\n * @csspart base - The host wrapper.\n * @csspart trigger - The button that opens the listbox.\n * @csspart value - The selection display inside the trigger.\n * @csspart chevron - The trigger chevron.\n * @csspart clear - The clear button.\n * @csspart panel - The floating popover panel.\n * @csspart search - The search input.\n * @csspart listbox - The options container.\n * @csspart option - Each option row.\n * @csspart empty - The \"no results\" message.\n *\n * @cssproperty [--height] - Control height. Defaults to the form-control height.\n * @cssproperty [--border-radius] - Trigger + panel radius.\n * @cssproperty [--background] - Panel background.\n *\n * @cssClass .l-select-item-media - Rich option: leading image or icon.\n * @cssClass .l-select-item-text - Rich option: column holding the title and the description.\n * @cssClass .l-select-item-title - Rich option: title, truncated with an ellipsis. Used as the label when the option has no `label`.\n * @cssClass .l-select-item-description - Rich option: secondary line, shown in the listbox only.\n * @cssClass .l-select-item-suffix - Rich option: trailing badge, count or status icon on the title's line. Kept out of the trigger, the chips and the search.\n *\n * @customElement l-select\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Select extends LuxenFormAssociatedElement {\n // Rich option markup is copied into the shadow root, out of reach of the\n // global stylesheets: suffixStyles lays out the `.l-select-item-suffix` zone,\n // and `_readItems` adopts the styles of the light-DOM elements (l-badge) the\n // options actually contain.\n static override styles = [hostStyles, suffixStyles, styles];\n\n private _localize = new LocalizeController(this);\n private readonly _listId = uniqueId('select-list');\n private readonly _richHighlighter = new RichHighlighter();\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._triggerEl,\n getFloatingElement: () => this._panelEl,\n getArrowElement: () => null,\n });\n\n private _nav = new ListboxNavController(this, {\n listboxId: this._listId,\n getCount: () => this._filtered.length,\n isDisabled: (i) => this._filtered[i]?.disabled ?? false,\n isOpen: () => this._open,\n open: () => this.show(),\n getOptionElements: () => this.shadowRoot?.querySelectorAll<HTMLElement>('.option'),\n });\n\n // The `<datalist>` is light DOM the consumer owns and rewrites in place;\n // re-read it so the closed trigger tracks the options it was given.\n private _options = new DatalistObserverController(this, () => this._syncItems());\n\n /** Placeholder shown in the trigger when nothing is selected. */\n @property()\n accessor placeholder = '';\n\n /** Accessible label for the trigger. */\n @property()\n accessor label = '';\n\n /** Control size. */\n @property({ reflect: true })\n accessor size: SelectSize = 'md';\n\n /** Show a filter box inside the popover (opt-in, for long lists). */\n @property({ type: Boolean, reflect: true })\n accessor searchable = false;\n\n /** Show a button to clear the value. */\n @property({ type: Boolean, reflect: true, attribute: 'with-clear' })\n accessor withClear = false;\n\n /** Panel placement relative to the trigger. */\n @property()\n accessor placement: Placement = 'bottom-start';\n\n /** Allow selecting multiple values — renders chips and submits one entry per value. */\n @property({ type: Boolean, reflect: true })\n accessor multiple = false;\n\n /** Override the option filter. `(item, query) => boolean`. */\n filter: SelectFilter = defaultFilter;\n\n @state() private accessor _values: string[] = [];\n @state() private accessor _open = false;\n @state() private accessor _query = '';\n @state() private accessor _items: SelectItem[] = [];\n\n private _defaultValues: string[] = [];\n\n /** The selected value (single mode) or array of values (multiple mode). */\n get value(): string | string[] {\n return this.multiple ? [...this._values] : (this._values[0] ?? '');\n }\n set value(v: string | string[]) {\n this._values = Array.isArray(v) ? [...v] : v ? [v] : [];\n }\n\n private get _triggerEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.trigger') ?? null;\n }\n private get _panelEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.panel') ?? null;\n }\n private get _searchEl() {\n return this.shadowRoot?.querySelector<HTMLInputElement>('.search') ?? null;\n }\n\n override get validationTarget() {\n return this._triggerEl ?? undefined;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this._readItems();\n // Seed the initial selection: an explicit `value` attribute wins, else the\n // authored `<option selected>` (declarative pre-selection — markup-driven).\n if (this._values.length === 0) {\n const attr = this.getAttribute('value');\n if (attr) {\n this._values = [attr];\n } else {\n const selected = this._items.filter((i) => i.selected).map((i) => i.value);\n this._values = this.multiple ? selected : selected.slice(0, 1);\n }\n }\n this._defaultValues = [...this._values];\n this._syncForm();\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_values') || changed.has('multiple')) {\n this._syncForm();\n this._updateValidity();\n }\n }\n\n override formResetCallback() {\n this._values = [...this._defaultValues];\n this._query = '';\n this._nav.reset();\n super.formResetCallback();\n this._syncForm();\n }\n\n override formStateRestoreCallback(state: string) {\n this.value = state;\n this._syncForm();\n }\n\n // --- Items (read from the light-DOM <datalist>) ---\n\n private _readItems() {\n const list = this.querySelector('datalist');\n const opts = list ? Array.from(list.options) : [];\n this._items = opts.map((o) => {\n const rich = o.childElementCount > 0;\n const label = richOptionLabel(o);\n return {\n value: o.value,\n label,\n disabled: o.disabled,\n selected: o.hasAttribute('selected'),\n html: rich ? o.innerHTML : undefined,\n };\n });\n adoptRichContentStyles(this.shadowRoot, this._items);\n }\n\n /**\n * Re-read the options after the light DOM changed underneath us. The active\n * option is addressed by position in `_filtered`, so a list that grows or\n * shrinks while the listbox is open would silently move the keyboard\n * selection onto a different row (or off the end, leaving Enter dead).\n * Re-anchor it on the option the user had highlighted, and drop it when that\n * option is gone.\n */\n private _syncItems() {\n const active = this._open ? this._filtered[this._nav.activeIndex] : undefined;\n this._readItems();\n if (!this._open) return;\n this._nav.setActive(active ? this._filtered.findIndex((i) => i.value === active.value) : -1);\n }\n\n private _itemFor(value: string): SelectItem | undefined {\n return this._items.find((i) => i.value === value);\n }\n\n private get _filtered(): SelectItem[] {\n return this._items.filter((i) => this.filter(i, this._query));\n }\n\n // --- Validity ---\n\n /** Push the current selection to the form (single value, or one entry per value). */\n private _syncForm() {\n if (this.multiple) {\n const data = new FormData();\n for (const v of this._values) data.append(this.name, v);\n this._internals.setFormValue(data);\n } else {\n this._internals.setFormValue(this._values[0] ?? '');\n }\n }\n\n private _updateValidity() {\n if (this.required && this._values.length === 0) {\n this.setValidity(\n { valueMissing: true },\n this._localize.term('selectOption'),\n this._triggerEl ?? undefined,\n );\n } else {\n this.setValidity({});\n }\n }\n\n // --- Open / close ---\n\n show() {\n if (this._open || this.disabled) return;\n this._readItems();\n if (!this.dispatchEvent(new Event('show', { cancelable: true }))) return;\n this._open = true;\n this._query = '';\n // Pre-activate the (first) chosen option so a screen reader announces it on open.\n if (this._values.length) {\n this._nav.setActive(this._filtered.findIndex((i) => i.value === this._values[0]));\n }\n void this.updateComplete.then(() => {\n const panel = this._panelEl;\n panel?.showPopover?.();\n this._floating.startPositioning({\n placement: this.placement,\n distance: 4,\n minWidth: 'trigger',\n });\n // Move focus into the search box (or the listbox when not searchable).\n (this._searchEl ?? this._panelEl?.querySelector<HTMLElement>('.listbox'))?.focus();\n document.addEventListener('pointerdown', this._onDocumentPointerDown, true);\n });\n }\n\n hide({ focusTrigger = true } = {}) {\n if (!this._open) return;\n if (!this.dispatchEvent(new Event('hide', { cancelable: true }))) return;\n this._open = false;\n this._query = '';\n this._nav.reset();\n const panel = this._panelEl;\n if (panel?.matches?.(':popover-open')) panel.hidePopover?.();\n this._floating.stopPositioning();\n document.removeEventListener('pointerdown', this._onDocumentPointerDown, true);\n if (focusTrigger) this._triggerEl?.focus();\n }\n\n private _onDocumentPointerDown = (e: PointerEvent) => {\n // popover=\"manual\" does not light-dismiss; do it ourselves.\n const path = e.composedPath();\n if (path.includes(this)) return;\n this.hasInteracted = true;\n this._updateValidity();\n this.hide({ focusTrigger: false });\n };\n\n // --- Selection ---\n\n /** Single: set the value and close. Multiple: toggle the value and stay open. */\n private _select(item: SelectItem) {\n if (item.disabled) return;\n this.hasInteracted = true;\n if (this.multiple) {\n this._values = this._values.includes(item.value)\n ? this._values.filter((v) => v !== item.value)\n : [...this._values, item.value];\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this._searchEl?.focus();\n } else {\n this._values = [item.value];\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this.hide();\n }\n }\n\n /** Deselect a single value (chip remove, multiple mode). */\n private _removeValue(value: string) {\n this._values = this._values.filter((v) => v !== value);\n this.hasInteracted = true;\n this.dispatchEvent(new SelectChangeEvent(this.value));\n }\n\n private _clear(e?: Event) {\n e?.stopPropagation();\n this._values = [];\n this.hasInteracted = true;\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this._triggerEl?.focus();\n }\n\n // --- Keyboard ---\n\n private _onTriggerKeyDown = (e: KeyboardEvent) => {\n if (this._open) return;\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.show();\n }\n };\n\n private _onPanelKeyDown = (e: KeyboardEvent) => {\n if (this._nav.onKeyDown(e)) return;\n switch (e.key) {\n case 'Enter': {\n e.preventDefault();\n const active = this._filtered[this._nav.activeIndex];\n if (active) this._select(active);\n break;\n }\n case 'Escape':\n e.preventDefault();\n this.hide();\n break;\n case 'Tab':\n this.hide({ focusTrigger: false });\n break;\n default:\n break;\n }\n };\n\n private _onSearchInput = (e: Event) => {\n this._query = (e.target as HTMLInputElement).value;\n this._nav.reset();\n this.dispatchEvent(new SelectInputEvent(this._query));\n };\n\n // --- Render ---\n\n private _renderSingleValue() {\n const selected = this._itemFor(this._values[0]);\n if (!selected) {\n return html`<span class=\"value-text placeholder\">${this.placeholder}</span>`;\n }\n return selected.html\n ? html`${unsafeHTML(selected.html)}`\n : html`<span class=\"value-text\">${selected.label}</span>`;\n }\n\n private _renderChips() {\n if (this._values.length === 0) {\n return html`<span class=\"value-text placeholder\">${this.placeholder}</span>`;\n }\n // The chip tag is the renameable `l-tag`; it's a Shadow-DOM element so its\n // remove button renders correctly inside this element's shadow root.\n const chip = unsafeStatic(tagName('tag'));\n return this._values.map((value) => {\n const item = this._itemFor(value);\n return staticHtml`<${chip}\n size=\"sm\"\n removable\n part=\"tag\"\n @remove=${(e: Event) => {\n e.preventDefault();\n this._removeValue(value);\n }}\n @click=${(e: Event) => e.stopPropagation()}\n >${item?.label ?? value}</${chip}\n >`;\n });\n }\n\n private _renderTrigger(invalid: boolean) {\n const inner = html`\n <span\n class=\"value\"\n part=\"value\"\n >${this.multiple ? this._renderChips() : this._renderSingleValue()}</span\n >\n <span\n class=\"chevron\"\n aria-hidden=\"true\"\n ></span>\n `;\n const onClick = () => (this._open ? this.hide() : this.show());\n // Both modes expose role=\"combobox\" (APG select-only combobox). A combobox\n // doesn't take its name from its content, so `label` stays the accessible\n // name while the shown value is announced as the value. A plain button would\n // let `aria-label` hide the chosen value from screen readers (WCAG 2.5.3).\n // Multiple mode uses a focusable div so the chips' remove buttons are valid\n // interactive children — a <button> trigger can't nest them.\n return this.multiple\n ? html`<div\n class=\"trigger\"\n part=\"trigger\"\n role=\"combobox\"\n tabindex=${this.disabled ? -1 : 0}\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-label=${this.label || nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @click=${onClick}\n @keydown=${this._onTriggerKeyDown}\n >\n ${inner}\n </div>`\n : html`<button\n class=\"trigger\"\n part=\"trigger\"\n type=\"button\"\n role=\"combobox\"\n ?disabled=${this.disabled}\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-label=${this.label || nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @click=${onClick}\n @keydown=${this._onTriggerKeyDown}\n >\n ${inner}\n </button>`;\n }\n\n override render() {\n const filtered = this._filtered;\n const activeId = this._nav.activeDescendant;\n const activeIndex = this._nav.activeIndex;\n const invalid = this.required && this._values.length === 0 && this.hasInteracted;\n const hasValue = this._values.length > 0;\n const status = !this._open\n ? ''\n : filtered.length === 0\n ? this._localize.term('noResults')\n : `${filtered.length} ${this._localize.term('suggestions')}`;\n\n return html`\n <div\n class=\"base\"\n part=\"base\"\n >\n ${this._renderTrigger(invalid)}\n ${\n this.withClear && hasValue\n ? html`<button\n class=\"clear\"\n part=\"clear\"\n type=\"button\"\n aria-label=${this._localize.term('clear')}\n @click=${this._clear}\n ></button>`\n : nothing\n }\n\n <div\n class=\"panel\"\n part=\"panel\"\n popover=\"manual\"\n @keydown=${this._onPanelKeyDown}\n >\n ${\n this.searchable\n ? html`<div class=\"search-row\">\n <input\n class=\"search\"\n part=\"search\"\n type=\"text\"\n role=\"searchbox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n .placeholder=${this._localize.term('search') ?? ''}\n aria-controls=${this._listId}\n aria-activedescendant=${activeId ?? nothing}\n @input=${this._onSearchInput}\n />\n </div>`\n : nothing\n }\n\n <ul\n class=\"listbox\"\n part=\"listbox\"\n role=\"listbox\"\n id=${this._listId}\n tabindex=${this.searchable ? nothing : '0'}\n aria-label=${this.label || this._localize.term('suggestions')}\n aria-multiselectable=${this.multiple ? 'true' : nothing}\n aria-activedescendant=${!this.searchable ? (activeId ?? nothing) : nothing}\n >\n ${filtered.map(\n (item, i) =>\n html`<li\n class=\"option\"\n part=\"option\"\n id=${this._nav.optionId(i)}\n role=\"option\"\n aria-selected=${this._values.includes(item.value) ? 'true' : 'false'}\n aria-disabled=${item.disabled ? 'true' : nothing}\n data-active=${i === activeIndex ? '' : nothing}\n data-current=${this._values.includes(item.value) ? '' : nothing}\n @pointerdown=${(e: Event) => e.preventDefault()}\n @click=${() => this._select(item)}\n >\n ${\n item.html\n ? html`<span class=\"option-rich\"\n >${unsafeHTML(this._richHighlighter.highlight(item.html, this._query))}</span\n >`\n : html`<span class=\"option-label\"\n >${highlightText(item.label, this._query)}</span\n >`\n }\n </li>`,\n )}\n </ul>\n ${\n filtered.length === 0\n ? html`<div\n class=\"empty\"\n part=\"empty\"\n >\n ${this._localize.term('noResults')}\n </div>`\n : nothing\n }\n </div>\n\n <div\n class=\"visually-hidden\"\n aria-live=\"polite\"\n >\n ${status}\n </div>\n </div>\n `;\n }\n}\n\n// Typed addEventListener for the colliding `change` / `input` names.\nexport interface Select {\n addEventListener<K extends keyof SelectEventMap>(\n type: K,\n listener: (this: Select, ev: SelectEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener<K extends keyof SelectEventMap>(\n type: K,\n listener: (this: Select, ev: SelectEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"A,6+BCmBA,IAAMqB,EAASrB,EAAUC,q0MAAS,EAmB5BqB,GAA+B,EAAM,IAAU,CACnD,GAAI,CAACC,EAAO,MAAO,GACnB,IAAMC,EAAQC,EAAUC,EAAK,KAAK,EAClC,OAAOD,EAAUF,CAAK,CAAC,CACpB,MAAM,KAAK,CAAC,CACZ,OAAO,OAAO,CAAC,CACf,MAAO,GAAOC,EAAM,SAASG,CAAE,CAAC,CACrC,EAGaC,EAAb,cAAuC,KAAM,CAG3C,YAAY,EAA0B,CACpC,MAAM,SAAU,CAAE,QAAS,GAAM,SAAU,GAAO,WAAY,EAAM,CAAC,EACrE,KAAK,MAAQC,CACf,CACF,EAGaC,EAAb,cAAsC,KAAM,CAG1C,YAAY,EAAe,CACzB,MAAM,QAAS,CAAE,QAAS,GAAM,SAAU,GAAO,WAAY,EAAM,CAAC,EACpE,KAAK,MAAQD,CACf,CACF,EAsDaE,EAAb,cAA4BC,CAA2B,C,8BAOjC,KAAA,UAAA,IAAIE,EAAmB,IAAI,EACpB,KAAA,QAAAC,EAAS,aAAa,EACb,KAAA,iBAAA,IAAIC,EAEpB,KAAA,UAAA,IAAIC,EAAkB,KAAM,CAC9C,sBAAyB,KAAK,WAC9B,uBAA0B,KAAK,SAC/B,oBAAuB,IACzB,CAAC,EAEc,KAAA,KAAA,IAAIC,EAAqB,KAAM,CAC5C,UAAW,KAAK,QAChB,aAAgB,KAAK,UAAU,OAC/B,WAAa,GAAM,KAAK,UAAUC,EAAE,EAAE,UAAY,GAClD,WAAc,KAAK,MACnB,SAAY,KAAK,KAAK,EACtB,sBAAyB,KAAK,YAAY,iBAA8B,SAAS,CACnF,CAAC,EAIkB,KAAA,SAAA,IAAIC,EAA2B,SAAY,KAAK,WAAW,CAAC,EAIxD,KAAAC,GAAA,GAIN,KAAAC,GAAA,GAIW,KAAAC,GAAA,KAIN,KAAAC,GAAA,GAID,KAAAC,GAAA,GAIW,KAAAC,GAAA,eAIZ,KAAAC,GAAA,GAGG,KAAA,OAAAzB,EAEuB,KAAA0B,GAAA,CAAC,EACb,KAAAC,GAAA,GACC,KAAAC,GAAA,GACc,KAAAC,GAAA,CAAC,EAEf,KAAA,eAAA,CAAC,EAwKF,KAAA,uBAAA,GAAoB,CAEvC,EAAE,aACX,CAAA,CAAK,SAAS,IAAI,IACtB,KAAK,cAAgB,GACrB,KAAK,gBAAgB,EACrB,KAAK,KAAK,CAAE,aAAc,EAAM,CAAC,EACnC,EAsC6B,KAAA,kBAAA,GAAqB,CAC5C,KAAK,QACL,EAAE,MAAQ,aAAe,EAAE,MAAQ,WAAa,EAAE,MAAQ,SAAW,EAAE,MAAQ,OACjF,EAAE,eAAe,EACjB,KAAK,KAAK,EAEd,EAE2B,KAAA,gBAAA,GAAqB,CAC9C,GAAI,MAAK,KAAK,UAAU,CAAC,EACzB,OAAQ,EAAE,IAAV,CACE,IAAK,QAAS,CACZ,EAAE,eAAe,EACjB,IAAMC,EAAS,KAAK,UAAU,KAAK,KAAK,aACpCA,GAAQ,KAAK,QAAQA,CAAM,EAC/B,KACF,CACA,IAAK,SACH,EAAE,eAAe,EACjB,KAAK,KAAK,EACV,MACF,IAAK,MACH,KAAK,KAAK,CAAE,aAAc,EAAM,CAAC,CAIrC,CACF,EAE0B,KAAA,eAAA,GAAa,CACrC,KAAK,OAAU,EAAE,OAA4B,MAC7C,KAAK,KAAK,MAAM,EAChB,KAAK,cAAc,IAAItB,EAAiB,KAAK,MAAM,CAAC,CACtD,C,QAnTyB,KAAA,OAAA,CAAC5B,EAAYC,EAAckB,CAAM,C,IA2BjD,IAAA,aAAA,C,+CAIA,IAAA,OAAA,C,yCAIA,IAAA,MAAA,C,wCAIA,IAAA,YAAA,C,8CAIA,IAAA,WAAA,C,6CAIA,IAAA,WAAA,C,6CAIA,IAAA,UAAA,C,4CAKiB,IAAA,SAAA,C,2CACA,IAAA,OAAA,C,yCACA,IAAA,QAAA,C,0CACA,IAAA,QAAA,C,uCAK1B,IAAI,OAA2B,CAC7B,OAAO,KAAK,SAAW,CAAC,GAAG,KAAK,OAAO,EAAK,KAAK,QAAQ,IAAM,EACjE,CACA,IAAI,MAAM,EAAsB,CAC9B,KAAK,QAAU,MAAM,QAAQgC,CAAC,EAAI,CAAC,GAAGA,CAAC,EAAIA,EAAI,CAACA,CAAC,EAAI,CAAC,CACxD,CAEA,IAAY,YAAa,CACvB,OAAO,KAAK,YAAY,cAA2B,UAAU,GAAK,IACpE,CACA,IAAY,UAAW,CACrB,OAAO,KAAK,YAAY,cAA2B,QAAQ,GAAK,IAClE,CACA,IAAY,WAAY,CACtB,OAAO,KAAK,YAAY,cAAgC,SAAS,GAAK,IACxE,CAEA,IAAa,kBAAmB,CAC9B,OAAO,KAAK,YAAc,IAAA,EAC5B,CAEA,mBAA6B,CAK3B,GAJA,MAAM,kBAAkB,EACxB,KAAK,WAAW,EAGZ,KAAK,QAAQ,SAAW,EAAG,CAC7B,IAAMC,EAAO,KAAK,aAAa,OAAO,EACtC,GAAIA,EACF,KAAK,QAAU,CAACA,CAAI,MACf,CACL,IAAMC,EAAW,KAAK,OAAO,OAAQ,GAAMhB,EAAE,QAAQ,CAAC,CAAC,IAAK,GAAMA,EAAE,KAAK,EACzE,KAAK,QAAU,KAAK,SAAWgB,EAAWA,EAAS,MAAM,EAAG,CAAC,CAC/D,CACF,CACA,KAAK,eAAiB,CAAC,GAAG,KAAK,OAAO,EACtC,KAAK,UAAU,CACjB,CAEA,QAAiB,EAA+B,EAC1CC,EAAQ,IAAI,SAAS,GAAKA,EAAQ,IAAI,UAAU,KAClD,KAAK,UAAU,EACf,KAAK,gBAAgB,EAEzB,CAEA,mBAA6B,CAC3B,KAAK,QAAU,CAAC,GAAG,KAAK,cAAc,EACtC,KAAK,OAAS,GACd,KAAK,KAAK,MAAM,EAChB,MAAM,kBAAkB,EACxB,KAAK,UAAU,CACjB,CAEA,yBAAkC,EAAe,CAC/C,KAAK,MAAQC,EACb,KAAK,UAAU,CACjB,CAIA,YAAqB,CACnB,IAAMC,EAAO,KAAK,cAAc,UAAU,EACpCC,EAAOD,EAAO,MAAM,KAAKA,EAAK,OAAO,EAAI,CAAC,EAChD,KAAK,OAASC,EAAK,IAAK,GAAM,CAC5B,IAAMC,EAAO1C,EAAE,kBAAoB,EAC7BM,EAAQqC,EAAgB3C,CAAC,EAC/B,MAAO,CACL,MAAOA,EAAE,MACT,QACA,SAAUA,EAAE,SACZ,SAAUA,EAAE,aAAa,UAAU,EACnC,KAAM0C,EAAO1C,EAAE,UAAY,IAAA,EAC7B,CACF,CAAC,EACD4C,EAAuB,KAAK,WAAY,KAAK,MAAM,CACrD,CAUA,YAAqB,CACnB,IAAMV,EAAS,KAAK,MAAQ,KAAK,UAAU,KAAK,KAAK,aAAe,IAAA,GACpE,KAAK,WAAW,EACX,KAAK,OACV,KAAK,KAAK,UAAUA,EAAS,KAAK,UAAU,UAAW,GAAMb,EAAE,QAAUa,EAAO,KAAK,EAAI,EAAE,CAC7F,CAEA,SAAiB,EAAuC,CACtD,OAAO,KAAK,OAAO,KAAM,GAAMb,EAAE,QAAUV,CAAK,CAClD,CAEA,IAAY,WAA0B,CACpC,OAAO,KAAK,OAAO,OAAQ,GAAM,KAAK,OAAOU,EAAG,KAAK,MAAM,CAAC,CAC9D,CAKA,WAAoB,CAClB,GAAI,KAAK,SAAU,CACjB,IAAMwB,EAAO,IAAI,SACjB,IAAK,IAAMV,KAAK,KAAK,QAASU,EAAK,OAAO,KAAK,KAAMV,CAAC,EACtD,KAAK,WAAW,aAAaU,CAAI,CACnC,MACE,KAAK,WAAW,aAAa,KAAK,QAAQ,IAAM,EAAE,CAEtD,CAEA,iBAA0B,CACpB,KAAK,UAAY,KAAK,QAAQ,SAAW,EAC3C,KAAK,YACH,CAAE,aAAc,EAAK,EACrB,KAAK,UAAU,KAAK,cAAc,EAClC,KAAK,YAAc,IAAA,EACrB,EAEA,KAAK,YAAY,CAAC,CAAC,CAEvB,CAIA,MAAO,CACD,KAAK,OAAS,KAAK,WACvB,KAAK,WAAW,EACX,KAAK,cAAc,IAAI,MAAM,OAAQ,CAAE,WAAY,EAAK,CAAC,CAAC,IAC/D,KAAK,MAAQ,GACb,KAAK,OAAS,GAEV,KAAK,QAAQ,QACf,KAAK,KAAK,UAAU,KAAK,UAAU,UAAW,GAAMxB,EAAE,QAAU,KAAK,QAAQ,EAAE,CAAC,EAElF,KAAU,eAAe,SAAW,CAElC,KADmB,UACZ,cAAc,EACrB,KAAK,UAAU,iBAAiB,CAC9B,UAAW,KAAK,UAChB,SAAU,EACV,SAAU,SACZ,CAAC,GAEA,KAAK,WAAa,KAAK,UAAU,cAA2B,UAAU,EAAA,EAAI,MAAM,EACjF,SAAS,iBAAiB,cAAe,KAAK,uBAAwB,EAAI,CAC5E,CAAC,GACH,CAEA,KAAK,CAAE,eAAe,IAAS,CAAC,EAAG,CAEjC,GADI,CAAC,KAAK,OACN,CAAC,KAAK,cAAc,IAAI,MAAM,OAAQ,CAAE,WAAY,EAAK,CAAC,CAAC,EAAG,OAClE,KAAK,MAAQ,GACb,KAAK,OAAS,GACd,KAAK,KAAK,MAAM,EAChB,IAAMyB,EAAQ,KAAK,SACfA,GAAO,UAAU,eAAe,GAAGA,EAAM,cAAc,EAC3D,KAAK,UAAU,gBAAgB,EAC/B,SAAS,oBAAoB,cAAe,KAAK,uBAAwB,EAAI,EACzEC,GAAc,KAAK,YAAY,MAAM,CAC3C,CAcA,QAAgB,EAAkB,CAC5BvC,EAAK,WACT,KAAK,cAAgB,GACjB,KAAK,UACP,KAAK,QAAU,KAAK,QAAQ,SAASA,EAAK,KAAK,EAC3C,KAAK,QAAQ,OAAQ,GAAM2B,IAAM3B,EAAK,KAAK,EAC3C,CAAC,GAAG,KAAK,QAASA,EAAK,KAAK,EAChC,KAAK,cAAc,IAAIE,EAAkB,KAAK,KAAK,CAAC,EACpD,KAAK,WAAW,MAAM,IAEtB,KAAK,QAAU,CAACF,EAAK,KAAK,EAC1B,KAAK,cAAc,IAAIE,EAAkB,KAAK,KAAK,CAAC,EACpD,KAAK,KAAK,GAEd,CAGA,aAAqB,EAAe,CAClC,KAAK,QAAU,KAAK,QAAQ,OAAQ,GAAMyB,IAAMxB,CAAK,EACrD,KAAK,cAAgB,GACrB,KAAK,cAAc,IAAID,EAAkB,KAAK,KAAK,CAAC,CACtD,CAEA,OAAe,EAAW,CACxB,GAAG,gBAAgB,EACnB,KAAK,QAAU,CAAC,EAChB,KAAK,cAAgB,GACrB,KAAK,cAAc,IAAIA,EAAkB,KAAK,KAAK,CAAC,EACpD,KAAK,YAAY,MAAM,CACzB,CAyCA,oBAA6B,CAC3B,IAAM2B,EAAW,KAAK,SAAS,KAAK,QAAQ,EAAE,EAI9C,OAHKA,EAGEA,EAAS,KACZ,CAAI,GAAGnD,EAAWmD,EAAS,IAAI,IAC/B,CAAI,4BAA4BA,EAAS,MAAM,SAJ1C,CAAI,wCAAwC,KAAK,YAAY,QAKxE,CAEA,cAAuB,CACrB,GAAI,KAAK,QAAQ,SAAW,EAC1B,MAAO,EAAI,wCAAwC,KAAK,YAAY,SAItE,IAAMW,EAAO7D,EAAa8D,EAAQ,KAAK,CAAC,EACxC,OAAO,KAAK,QAAQ,IAAK,GAAU,CACjC,IAAMzC,EAAO,KAAK,SAASG,CAAK,EAChC,MAAO,EAAU,IAAIqC,EAAK;;;;kBAIb,GAAa,CACtB,EAAE,eAAe,EACjB,KAAK,aAAarC,CAAK,CACzB,EAAE;iBACQ,GAAa,EAAE,gBAAgB,EAAE;WACxCH,GAAM,OAASG,EAAM,IAAIqC,EAAK;QAErC,CAAC,CACH,CAEA,eAAuB,EAAkB,CACvC,IAAME,EAAQ,CAAI;;;;WAIX,KAAK,SAAW,KAAK,aAAa,EAAI,KAAK,mBAAmB,EAAE;;;;;;MAOjEC,MAAiB,KAAK,MAAQ,KAAK,KAAK,EAAI,KAAK,KAAK,EAO5D,OAAO,KAAK,SACR,CAAI;;;;qBAIS,KAAK,SAAW,GAAK,EAAE;;0BAElB,KAAK,MAAQ,OAAS,QAAQ;0BAC9B,KAAK,QAAQ;uBAChB,KAAK,OAAS/D,EAAQ;0BACnB,KAAK,SAAW,OAASA,EAAQ;yBAClCgE,EAAU,OAAShE,EAAQ;mBACjC+D,EAAQ;qBACN,KAAK,kBAAkB;;YAEhCD,EAAM;gBAEV,CAAI;;;;;sBAKU,KAAK,SAAS;;0BAEV,KAAK,MAAQ,OAAS,QAAQ;0BAC9B,KAAK,QAAQ;uBAChB,KAAK,OAAS9D,EAAQ;0BACnB,KAAK,SAAW,OAASA,EAAQ;yBAClCgE,EAAU,OAAShE,EAAQ;mBACjC+D,EAAQ;qBACN,KAAK,kBAAkB;;YAEhCD,EAAM;kBAEhB,CAEA,QAAkB,CAChB,IAAMG,EAAW,KAAK,UAChBC,EAAW,KAAK,KAAK,iBACrBC,EAAc,KAAK,KAAK,YACxBH,EAAU,KAAK,UAAY,KAAK,QAAQ,SAAW,GAAK,KAAK,cAC7DI,EAAW,KAAK,QAAQ,OAAS,EACjCC,EAAU,KAAK,MAEjBJ,EAAS,SAAW,EAClB,KAAK,UAAU,KAAK,WAAW,EAC/B,GAAGA,EAAS,OAAO,GAAG,KAAK,UAAU,KAAK,aAAa,IAHzD,GAKJ,MAAO,EAAI;;;;;UAKL,KAAK,eAAeD,CAAO,EAAE;UAE7B,KAAK,WAAaI,EACd,CAAI;;;;6BAIW,KAAK,UAAU,KAAK,OAAO,EAAE;yBACjC,KAAK,OAAO;0BAEvBpE,EACL;;;;;;qBAMY,KAAK,gBAAgB;;YAG9B,KAAK,WACD,CAAI;;;;;;;;mCAQe,KAAK,UAAU,KAAK,QAAQ,GAAK,GAAG;oCACnC,KAAK,QAAQ;4CACLkE,GAAYlE,EAAQ;6BACnC,KAAK,eAAe;;wBAGjCA,EACL;;;;;;iBAMM,KAAK,QAAQ;uBACP,KAAK,WAAaA,EAAU,IAAI;yBAC9B,KAAK,OAAS,KAAK,UAAU,KAAK,aAAa,EAAE;mCACvC,KAAK,SAAW,OAASA,EAAQ;oCAC/B,KAAK,WAAqCA,EAAvBkE,GAAYlE,EAAmB;;cAEzEiE,EAAS,KACR,EAAM,IACL,CAAI;;;uBAGG,KAAK,KAAK,SAAShC,CAAC,EAAE;;kCAEX,KAAK,QAAQ,SAASb,EAAK,KAAK,EAAI,OAAS,QAAQ;kCACrDA,EAAK,SAAW,OAASpB,EAAQ;gCACnCiC,IAAMkC,EAAc,GAAKnE,EAAQ;iCAChC,KAAK,QAAQ,SAASoB,EAAK,KAAK,EAAI,GAAKpB,EAAQ;iCAChD,GAAa,EAAE,eAAe,EAAE;+BACjC,KAAK,QAAQoB,CAAI,EAAE;;oBAGhCA,EAAK,KACD,CAAI;6BACCtB,EAAW,KAAK,iBAAiB,UAAUsB,EAAK,KAAM,KAAK,MAAM,CAAC,EAAE;2BAEzE,CAAI;6BACCkD,EAAclD,EAAK,MAAO,KAAK,MAAM,EAAE;2BAEjD;sBAEP,EAAE;;YAGF6C,EAAS,SAAW,EAChB,CAAI;;;;oBAIA,KAAK,UAAU,KAAK,WAAW,EAAE;wBAErCjE,EACL;;;;;;;YAOCqE,EAAO;;;KAIjB,CACF,EAreGpE,EAAA,CAAAC,EAAS,CAAA,EAAAuB,EAAA,UAAA,cAAA,IAAA,EAITxB,EAAA,CAAAC,EAAS,CAAA,EAAAuB,EAAA,UAAA,QAAA,IAAA,EAITxB,EAAA,CAAAC,EAAS,CAAE,QAAS,EAAK,CAAC,CAAA,EAAAuB,EAAA,UAAA,OAAA,IAAA,EAI1BxB,EAAA,CAAAC,EAAS,CAAE,KAAM,QAAS,QAAS,EAAK,CAAC,CAAA,EAAAuB,EAAA,UAAA,aAAA,IAAA,EAIzCxB,EAAA,CAAAC,EAAS,CAAE,KAAM,QAAS,QAAS,GAAM,UAAW,YAAa,CAAC,CAAA,EAAAuB,EAAA,UAAA,YAAA,IAAA,EAIlExB,EAAA,CAAAC,EAAS,CAAA,EAAAuB,EAAA,UAAA,YAAA,IAAA,EAITxB,EAAA,CAAAC,EAAS,CAAE,KAAM,QAAS,QAAS,EAAK,CAAC,CAAA,EAAAuB,EAAA,UAAA,WAAA,IAAA,EAMzCxB,EAAA,CAAAE,EAAM,CAAA,EAAAsB,EAAA,UAAA,UAAA,IAAA,EACNxB,EAAA,CAAAE,EAAM,CAAA,EAAAsB,EAAA,UAAA,QAAA,IAAA,EACNxB,EAAA,CAAAE,EAAM,CAAA,EAAAsB,EAAA,UAAA,SAAA,IAAA,EACNxB,EAAA,CAAAE,EAAM,CAAA,EAAAsB,EAAA,UAAA,SAAA,IAAA,E"}
1
+ {"version":3,"file":"select.js","names":["unsafeCSS","rawStyles","hostStyles","visuallyHiddenStyles","suffixStyles","selectItemStyles","pickerCoreStyles","unsafeHTML","unsafeStatic","nothing","_decorate","property","state","r","s","u","r$1","n","__decorate","b","A","o","host_styles_default","visually_hidden_styles_default","select_item_suffix_styles_default","picker_core_styles_default","select_item_styles_default","styles","defaultFilter","query","label","normalize","item","kw","SelectChangeEvent","value","SelectInputEvent","Select","LuxenFormAssociatedElement","_args","LocalizeController","uniqueId","RichHighlighter","PopoverController","ListboxNavController","i","DatalistObserverController","#_placeholder_accessor_storage","#_label_accessor_storage","#_size_accessor_storage","#_searchable_accessor_storage","#_withClear_accessor_storage","#_placement_accessor_storage","#_multiple_accessor_storage","#__values_accessor_storage","#__open_accessor_storage","#__query_accessor_storage","#__items_accessor_storage","active","v","attr","selected","changed","state","list","opts","rich","richOptionLabel","adoptRichContentStyles","data","panel","focusTrigger","chip","tagName","inner","onClick","invalid","filtered","activeId","activeIndex","hasValue","status","highlightText","cls"],"sources":["../../../src/html/elements/select/select.css?inline","../../../src/html/elements/select/select.ts"],"sourcesContent":["/* Knobs, size scale, field frame, chevron, clear, listbox popover and the\n shared option bits come from shared/styles/picker-core.css; the option row\n and rich-option content from shared/styles/select-item.css (also used by the\n native `.l-select` picker). This file holds what is specific to l-select. */\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆃🆁🅸🅶🅶🅴🆁 (button, opens the listbox popover)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.trigger {\n gap: 0.5rem;\n inline-size: 100%;\n min-block-size: var(--height);\n padding-block: 0.25rem;\n color: var(--l-form-control-value-color);\n font: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n/* The form-control inline padding is tuned for text; before a chip row it reads\n as too much, so tighten the start edge in multiple mode (the chips bring their\n own spacing). */\n:host([multiple]) .trigger {\n padding-inline-start: 0.375rem;\n}\n\n.trigger:focus-visible {\n outline: none;\n}\n\n/* Selection display fills the trigger; the chevron (and overlaid clear) sit at the end. */\n.value {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 0.25rem;\n overflow: hidden;\n}\n\n.value-text {\n min-inline-size: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* This element's public knobs and the chip's share two names — `--height` and\n `--border-radius` — and custom properties inherit straight through a shadow\n boundary. Left alone, every chip in the trigger renders at the *select's*\n height and corner rather than its own: a 36px chip with a 6px corner, when\n `size=\"sm\"` asked for a 24px pill. `initial` is the guaranteed-invalid value,\n so each `var(--x, …)` inside the chip falls back to its own default. Matched\n on the part, not on the tag name, so a renamed `elementPrefix` can't miss it.\n\n The fill used to collide too, back when the chip read `--background`; it now\n reads `--background-color`, which this element does not expose. */\n[part='tag'] {\n --height: initial;\n --border-radius: initial;\n}\n\n.placeholder {\n color: var(--l-form-control-placeholder-color);\n}\n\n/* The clear button is a SIBLING of the trigger (not a child — no nested buttons),\n * overlaid on the trigger's end, square against the chevron's square — which\n * sits flush against the border (picker-core.css). */\n.clear-button {\n position: absolute;\n inset-block: 0;\n inset-inline-end: calc(var(--l-form-control-border-width) + var(--_action-size));\n margin-block: auto;\n}\n\n/* Keep the value (text or chips) clear of the overlaid button. */\n.base:has(> .clear-button) .chevron {\n margin-inline-start: var(--_action-size);\n}\n\n/* Rich content in the trigger takes the row styles (select-item.css), then\n mirrors the native trigger: only the media and title, at the value's weight —\n the secondary description and the trailing suffix are dropped so the selected\n value stays compact and consistent across options. */\n.value .l-select-item-title {\n font-weight: inherit;\n}\n.value .l-select-item-description,\n.value .l-select-item-suffix {\n display: none;\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅿🅰🅽🅴🅻 (popover: search + listbox)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.search-row {\n padding: 0.375rem;\n border-block-end: 1px solid var(--l-color-border-overlay);\n}\n\n.search {\n inline-size: 100%;\n block-size: var(--l-size-control-sm);\n padding-inline: 0.5rem;\n border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);\n border-radius: calc(var(--border-radius) - 2px);\n background-color: var(--l-form-control-background-color);\n color: var(--l-form-control-value-color);\n font: inherit;\n}\n\n.search:focus-visible {\n outline: none;\n border-color: var(--l-focus-ring);\n}\n\n.search::placeholder {\n color: var(--l-form-control-placeholder-color);\n}\n\n/* Keyboard-active row (moves with Arrow keys) — same tint as hover. */\n.option[data-active] {\n background-color: var(--l-color-bg-state-hover);\n}\n\n/* Selected indicator in the reserved inline-start column, pinned to the first\n * line so a wrapping description never shifts it (matches the native\n * `::checkmark` in src/css/elements/select.css). */\n.option[data-current]::after {\n content: '';\n position: absolute;\n inset-inline-start: 0.5rem;\n inset-block-start: var(--_row-padding-block);\n inline-size: 1rem;\n block-size: calc(var(--l-popover-font-size) * var(--l-popover-line-height));\n background-color: currentColor;\n mask: var(--_check-icon) center / 1rem no-repeat;\n}\n\n/* Rich content, rows and trigger alike: the shared layout (select-item.css)\n plus what only this tier needs — media may be an emoji or icon rather than an\n image, so it is centred and sized by font. */\n.l-select-item-media {\n display: inline-flex;\n align-items: center;\n font-size: 1.25em;\n}\n","import { html, nothing, unsafeCSS } from 'lit';\nimport { html as staticHtml, unsafeStatic } from 'lit/static-html.js';\nimport { property, state } from 'lit/decorators.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { LuxenFormAssociatedElement } from '../../shared/luxen-form-associated-element.js';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { ListboxNavController } from '../../shared/controllers/listbox-nav.js';\nimport { DatalistObserverController } from '../../shared/controllers/datalist-observer.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { RichHighlighter, highlightText, normalize } from '../../shared/highlight.js';\nimport { cls, tagName, uniqueId } from '../../registry.js';\nimport { adoptRichContentStyles, richOptionLabel } from '../../shared/rich-option.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport visuallyHiddenStyles from '../../shared/styles/visually-hidden.styles.js';\nimport suffixStyles from '../../shared/styles/select-item-suffix.styles.js';\nimport pickerCoreStyles from '../../shared/styles/picker-core.styles.js';\nimport selectItemStyles from '../../shared/styles/select-item.styles.js';\nimport rawStyles from './select.css?inline';\n// Registers the chip element used to render selected values in multiple mode.\nimport '../tag/index.js';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type SelectSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\n/** A single option read from the `<datalist>`. */\nexport interface SelectItem {\n value: string;\n /** Plain text used for filtering and the trigger display. */\n label: string;\n disabled: boolean;\n /** Authored `selected` attribute — seeds the initial value. */\n selected: boolean;\n /** Rich inner markup (from a non-empty `<option>`), projected into the row. */\n html?: string;\n}\n\n/** Default filter: accent/case-insensitive, every space-separated keyword must match. */\nexport type SelectFilter = (item: SelectItem, query: string) => boolean;\n\nconst defaultFilter: SelectFilter = (item, query) => {\n if (!query) return true;\n const label = normalize(item.label);\n return normalize(query)\n .split(/\\s+/)\n .filter(Boolean)\n .every((kw) => label.includes(kw));\n};\n\n/** Fired when the selection changes. Bubbles; not composed. */\nexport class SelectChangeEvent extends Event {\n /** The selected value (single mode) or values (multiple mode). */\n readonly value: string | string[];\n constructor(value: string | string[]) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\n/** Fired as the user types in the search box. Bubbles; not composed. */\nexport class SelectInputEvent extends Event {\n /** The current query text. */\n readonly value: string;\n constructor(value: string) {\n super('input', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\ninterface SelectEventMap {\n change: SelectChangeEvent;\n input: SelectInputEvent;\n}\n\n/**\n * A button-triggered, searchable select. A trigger shows the current selection,\n * opening a popover with a search box and a `role=\"listbox\"` of options authored\n * as a native `<datalist>` of `<option>` — the same authoring surface as\n * `l-combobox` and the native `.l-select`. Form-associated.\n *\n * @summary A searchable select with a button trigger and a popover listbox.\n *\n * @example\n * ```html\n * <l-select label=\"Country\" name=\"country\" placeholder=\"Select a country…\">\n * <datalist>\n * <option value=\"us\">United States</option>\n * <option value=\"fr\" selected>France</option>\n * </datalist>\n * </l-select>\n * ```\n *\n * @event change - Fired when the selection changes. Bubbles. Not cancelable. Properties: `value: string | string[]`.\n * @event input - Fired as the user types in the search box. Bubbles. Not cancelable. Properties: `value: string` (the query).\n * @event show - Fired before the listbox opens. Cancelable.\n * @event hide - Fired before the listbox closes. Cancelable.\n *\n * @csspart base - The host wrapper.\n * @csspart trigger - The button that opens the listbox.\n * @csspart value - The selection display inside the trigger.\n * @csspart chevron - The trigger chevron.\n * @csspart clear - The clear button.\n * @csspart panel - The floating popover panel.\n * @csspart search - The search input.\n * @csspart listbox - The options container.\n * @csspart option - Each option row.\n * @csspart empty - The \"no results\" message.\n *\n * @cssproperty [--height=var(--l-form-control-height)] - Control height. The clear button and the chevron\n * each take a square of this height (borders excluded), so their hit area and spacing follow it.\n * @cssproperty [--l-form-control-icon-size=1em] - Glyph size of the clear button and the chevron (global form-control\n * token; set it here or on any ancestor). Changes the glyph only — the hit area follows `--height`.\n * @cssproperty --chevron-icon - Chevron glyph as a `url()`. Masked, so its color comes from the control, not the image.\n * @cssproperty --clear-icon - Clear button glyph as a `url()`.\n * @cssproperty [--border-radius] - Trigger + panel radius.\n * @cssproperty [--background] - Panel background.\n *\n * @cssClass .l-select-item-media - Rich option: leading image or icon.\n * @cssClass .l-select-item-text - Rich option: column holding the title and the description.\n * @cssClass .l-select-item-title - Rich option: title, truncated with an ellipsis. Used as the label when the option has no `label`.\n * @cssClass .l-select-item-description - Rich option: secondary line, shown in the listbox only.\n * @cssClass .l-select-item-suffix - Rich option: trailing badge, count or status icon on the title's line. Kept out of the trigger, the chips and the search.\n *\n * @customElement l-select\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Select extends LuxenFormAssociatedElement {\n // Rich option markup is copied into the shadow root, out of reach of the\n // global stylesheets: suffixStyles lays out the `.l-select-item-suffix` zone,\n // and `_readItems` adopts the styles of the light-DOM elements (l-badge) the\n // options actually contain. selectItemStyles (row + rich content, shared with\n // the native picker) loads before pickerCoreStyles so the shared disabled-row\n // rule wins over the row's `cursor: pointer`.\n static override styles = [\n hostStyles,\n visuallyHiddenStyles,\n suffixStyles,\n selectItemStyles,\n pickerCoreStyles,\n styles,\n ];\n\n private _localize = new LocalizeController(this);\n private readonly _listId = uniqueId('select-list');\n private readonly _richHighlighter = new RichHighlighter();\n\n private _floating = new PopoverController(this, {\n getTriggerElement: () => this._triggerEl,\n getFloatingElement: () => this._panelEl,\n getArrowElement: () => null,\n });\n\n private _nav = new ListboxNavController(this, {\n listboxId: this._listId,\n getCount: () => this._filtered.length,\n isDisabled: (i) => this._filtered[i]?.disabled ?? false,\n isOpen: () => this._open,\n open: () => this.show(),\n getOptionElements: () => this.shadowRoot?.querySelectorAll<HTMLElement>('.option'),\n });\n\n // The `<datalist>` is light DOM the consumer owns and rewrites in place;\n // re-read it so the closed trigger tracks the options it was given.\n private _options = new DatalistObserverController(this, () => this._syncItems());\n\n /** Placeholder shown in the trigger when nothing is selected. */\n @property()\n accessor placeholder = '';\n\n /** Accessible label for the trigger. */\n @property()\n accessor label = '';\n\n /** Control size. */\n @property({ reflect: true })\n accessor size: SelectSize = 'md';\n\n /** Show a filter box inside the popover (opt-in, for long lists). */\n @property({ type: Boolean, reflect: true })\n accessor searchable = false;\n\n /** Show a button to clear the value. */\n @property({ type: Boolean, reflect: true, attribute: 'with-clear' })\n accessor withClear = false;\n\n /** Panel placement relative to the trigger. */\n @property()\n accessor placement: Placement = 'bottom-start';\n\n /** Allow selecting multiple values — renders chips and submits one entry per value. */\n @property({ type: Boolean, reflect: true })\n accessor multiple = false;\n\n /** Override the option filter. `(item, query) => boolean`. */\n filter: SelectFilter = defaultFilter;\n\n @state() private accessor _values: string[] = [];\n @state() private accessor _open = false;\n @state() private accessor _query = '';\n @state() private accessor _items: SelectItem[] = [];\n\n private _defaultValues: string[] = [];\n\n /** The selected value (single mode) or array of values (multiple mode). */\n get value(): string | string[] {\n return this.multiple ? [...this._values] : (this._values[0] ?? '');\n }\n set value(v: string | string[]) {\n this._values = Array.isArray(v) ? [...v] : v ? [v] : [];\n }\n\n private get _triggerEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.trigger') ?? null;\n }\n private get _panelEl() {\n return this.shadowRoot?.querySelector<HTMLElement>('.panel') ?? null;\n }\n private get _searchEl() {\n return this.shadowRoot?.querySelector<HTMLInputElement>('.search') ?? null;\n }\n\n override get validationTarget() {\n return this._triggerEl ?? undefined;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this._readItems();\n // Seed the initial selection: an explicit `value` attribute wins, else the\n // authored `<option selected>` (declarative pre-selection — markup-driven).\n if (this._values.length === 0) {\n const attr = this.getAttribute('value');\n if (attr) {\n this._values = [attr];\n } else {\n const selected = this._items.filter((i) => i.selected).map((i) => i.value);\n this._values = this.multiple ? selected : selected.slice(0, 1);\n }\n }\n this._defaultValues = [...this._values];\n this._syncForm();\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_values') || changed.has('multiple')) {\n this._syncForm();\n this._updateValidity();\n }\n }\n\n override formResetCallback() {\n this._values = [...this._defaultValues];\n this._query = '';\n this._nav.reset();\n super.formResetCallback();\n this._syncForm();\n }\n\n override formStateRestoreCallback(state: string) {\n this.value = state;\n this._syncForm();\n }\n\n // --- Items (read from the light-DOM <datalist>) ---\n\n private _readItems() {\n const list = this.querySelector('datalist');\n const opts = list ? Array.from(list.options) : [];\n this._items = opts.map((o) => {\n const rich = o.childElementCount > 0;\n const label = richOptionLabel(o);\n return {\n value: o.value,\n label,\n disabled: o.disabled,\n selected: o.hasAttribute('selected'),\n html: rich ? o.innerHTML : undefined,\n };\n });\n adoptRichContentStyles(this.shadowRoot, this._items);\n }\n\n /**\n * Re-read the options after the light DOM changed underneath us. The active\n * option is addressed by position in `_filtered`, so a list that grows or\n * shrinks while the listbox is open would silently move the keyboard\n * selection onto a different row (or off the end, leaving Enter dead).\n * Re-anchor it on the option the user had highlighted, and drop it when that\n * option is gone.\n */\n private _syncItems() {\n const active = this._open ? this._filtered[this._nav.activeIndex] : undefined;\n this._readItems();\n if (!this._open) return;\n this._nav.setActive(active ? this._filtered.findIndex((i) => i.value === active.value) : -1);\n }\n\n private _itemFor(value: string): SelectItem | undefined {\n return this._items.find((i) => i.value === value);\n }\n\n private get _filtered(): SelectItem[] {\n return this._items.filter((i) => this.filter(i, this._query));\n }\n\n // --- Validity ---\n\n /** Push the current selection to the form (single value, or one entry per value). */\n private _syncForm() {\n if (this.multiple) {\n const data = new FormData();\n for (const v of this._values) data.append(this.name, v);\n this._internals.setFormValue(data);\n } else {\n this._internals.setFormValue(this._values[0] ?? '');\n }\n }\n\n private _updateValidity() {\n if (this.required && this._values.length === 0) {\n this.setValidity(\n { valueMissing: true },\n this._localize.term('selectOption'),\n this._triggerEl ?? undefined,\n );\n } else {\n this.setValidity({});\n }\n }\n\n // --- Open / close ---\n\n show() {\n if (this._open || this.disabled) return;\n this._readItems();\n if (!this.dispatchEvent(new Event('show', { cancelable: true }))) return;\n this._open = true;\n this._query = '';\n // Pre-activate the (first) chosen option so a screen reader announces it on open.\n if (this._values.length) {\n this._nav.setActive(this._filtered.findIndex((i) => i.value === this._values[0]));\n }\n void this.updateComplete.then(() => {\n const panel = this._panelEl;\n panel?.showPopover?.();\n this._floating.startPositioning({\n placement: this.placement,\n distance: 4,\n minWidth: 'trigger',\n });\n // Move focus into the search box (or the listbox when not searchable).\n (this._searchEl ?? this._panelEl?.querySelector<HTMLElement>('.listbox'))?.focus();\n document.addEventListener('pointerdown', this._onDocumentPointerDown, true);\n });\n }\n\n hide({ focusTrigger = true } = {}) {\n if (!this._open) return;\n if (!this.dispatchEvent(new Event('hide', { cancelable: true }))) return;\n this._open = false;\n this._query = '';\n this._nav.reset();\n const panel = this._panelEl;\n if (panel?.matches?.(':popover-open')) panel.hidePopover?.();\n this._floating.stopPositioning();\n document.removeEventListener('pointerdown', this._onDocumentPointerDown, true);\n if (focusTrigger) this._triggerEl?.focus();\n }\n\n private _onDocumentPointerDown = (e: PointerEvent) => {\n // popover=\"manual\" does not light-dismiss; do it ourselves.\n const path = e.composedPath();\n if (path.includes(this)) return;\n this.hasInteracted = true;\n this._updateValidity();\n this.hide({ focusTrigger: false });\n };\n\n // --- Selection ---\n\n /** Single: set the value and close. Multiple: toggle the value and stay open. */\n private _select(item: SelectItem) {\n if (item.disabled) return;\n this.hasInteracted = true;\n if (this.multiple) {\n this._values = this._values.includes(item.value)\n ? this._values.filter((v) => v !== item.value)\n : [...this._values, item.value];\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this._searchEl?.focus();\n } else {\n this._values = [item.value];\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this.hide();\n }\n }\n\n /** Deselect a single value (chip remove, multiple mode). */\n private _removeValue(value: string) {\n this._values = this._values.filter((v) => v !== value);\n this.hasInteracted = true;\n this.dispatchEvent(new SelectChangeEvent(this.value));\n }\n\n private _clear(e?: Event) {\n e?.stopPropagation();\n this._values = [];\n this.hasInteracted = true;\n this.dispatchEvent(new SelectChangeEvent(this.value));\n this._triggerEl?.focus();\n }\n\n // --- Keyboard ---\n\n private _onTriggerKeyDown = (e: KeyboardEvent) => {\n if (this._open) return;\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.show();\n }\n };\n\n private _onPanelKeyDown = (e: KeyboardEvent) => {\n if (this._nav.onKeyDown(e)) return;\n switch (e.key) {\n case 'Enter': {\n e.preventDefault();\n const active = this._filtered[this._nav.activeIndex];\n if (active) this._select(active);\n break;\n }\n case 'Escape':\n e.preventDefault();\n this.hide();\n break;\n case 'Tab':\n this.hide({ focusTrigger: false });\n break;\n default:\n break;\n }\n };\n\n private _onSearchInput = (e: Event) => {\n this._query = (e.target as HTMLInputElement).value;\n this._nav.reset();\n this.dispatchEvent(new SelectInputEvent(this._query));\n };\n\n // --- Render ---\n\n private _renderSingleValue() {\n const selected = this._itemFor(this._values[0]);\n if (!selected) {\n return html`<span class=\"value-text placeholder\">${this.placeholder}</span>`;\n }\n return selected.html\n ? html`${unsafeHTML(selected.html)}`\n : html`<span class=\"value-text\">${selected.label}</span>`;\n }\n\n private _renderChips() {\n if (this._values.length === 0) {\n return html`<span class=\"value-text placeholder\">${this.placeholder}</span>`;\n }\n // The chip tag is the renameable `l-tag`; it's a Shadow-DOM element so its\n // remove button renders correctly inside this element's shadow root.\n const chip = unsafeStatic(tagName('tag'));\n return this._values.map((value) => {\n const item = this._itemFor(value);\n return staticHtml`<${chip}\n size=\"sm\"\n removable\n part=\"tag\"\n @remove=${(e: Event) => {\n e.preventDefault();\n this._removeValue(value);\n }}\n @click=${(e: Event) => e.stopPropagation()}\n >${item?.label ?? value}</${chip}\n >`;\n });\n }\n\n private _renderTrigger(invalid: boolean) {\n const inner = html`\n <span\n class=\"value\"\n part=\"value\"\n >${this.multiple ? this._renderChips() : this._renderSingleValue()}</span\n >\n <span\n class=\"chevron\"\n part=\"chevron\"\n aria-hidden=\"true\"\n ></span>\n `;\n const onClick = () => (this._open ? this.hide() : this.show());\n // Both modes expose role=\"combobox\" (APG select-only combobox). A combobox\n // doesn't take its name from its content, so `label` stays the accessible\n // name while the shown value is announced as the value. A plain button would\n // let `aria-label` hide the chosen value from screen readers (WCAG 2.5.3).\n // Multiple mode uses a focusable div so the chips' remove buttons are valid\n // interactive children — a <button> trigger can't nest them.\n return this.multiple\n ? html`<div\n class=\"trigger\"\n part=\"trigger\"\n role=\"combobox\"\n tabindex=${this.disabled ? -1 : 0}\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-label=${this.label || nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @click=${onClick}\n @keydown=${this._onTriggerKeyDown}\n >\n ${inner}\n </div>`\n : html`<button\n class=\"trigger\"\n part=\"trigger\"\n type=\"button\"\n role=\"combobox\"\n ?disabled=${this.disabled}\n aria-haspopup=\"listbox\"\n aria-expanded=${this._open ? 'true' : 'false'}\n aria-controls=${this._listId}\n aria-label=${this.label || nothing}\n aria-required=${this.required ? 'true' : nothing}\n aria-invalid=${invalid ? 'true' : nothing}\n @click=${onClick}\n @keydown=${this._onTriggerKeyDown}\n >\n ${inner}\n </button>`;\n }\n\n override render() {\n const filtered = this._filtered;\n const activeId = this._nav.activeDescendant;\n const activeIndex = this._nav.activeIndex;\n const invalid = this.required && this._values.length === 0 && this.hasInteracted;\n const hasValue = this._values.length > 0;\n const status = !this._open\n ? ''\n : filtered.length === 0\n ? this._localize.term('noResults')\n : `${filtered.length} ${this._localize.term('suggestions')}`;\n\n return html`\n <div\n class=\"base\"\n part=\"base\"\n >\n ${this._renderTrigger(invalid)}\n ${\n this.withClear && hasValue\n ? html`<button\n class=\"clear-button\"\n part=\"clear\"\n type=\"button\"\n aria-label=${this._localize.term('clear')}\n @click=${this._clear}\n ></button>`\n : nothing\n }\n\n <div\n class=\"panel\"\n part=\"panel\"\n popover=\"manual\"\n @keydown=${this._onPanelKeyDown}\n >\n ${\n this.searchable\n ? html`<div class=\"search-row\">\n <input\n class=\"search\"\n part=\"search\"\n type=\"text\"\n role=\"searchbox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n .placeholder=${this._localize.term('search') ?? ''}\n aria-controls=${this._listId}\n aria-activedescendant=${activeId ?? nothing}\n @input=${this._onSearchInput}\n />\n </div>`\n : nothing\n }\n\n <ul\n class=\"listbox\"\n part=\"listbox\"\n role=\"listbox\"\n id=${this._listId}\n tabindex=${this.searchable ? nothing : '0'}\n aria-label=${this.label || this._localize.term('suggestions')}\n aria-multiselectable=${this.multiple ? 'true' : nothing}\n aria-activedescendant=${!this.searchable ? (activeId ?? nothing) : nothing}\n @pointermove=${this._nav.onPointerMove}\n @pointerleave=${this._nav.onPointerLeave}\n >\n ${filtered.map(\n (item, i) =>\n html`<li\n class=\"option\"\n part=\"option\"\n id=${this._nav.optionId(i)}\n role=\"option\"\n aria-selected=${this._values.includes(item.value) ? 'true' : 'false'}\n aria-disabled=${item.disabled ? 'true' : nothing}\n data-active=${i === activeIndex ? '' : nothing}\n data-current=${this._values.includes(item.value) ? '' : nothing}\n @pointerdown=${(e: Event) => e.preventDefault()}\n @click=${() => this._select(item)}\n >\n ${\n item.html\n ? unsafeHTML(this._richHighlighter.highlight(item.html, this._query))\n : html`<span class=\"option-label\"\n >${highlightText(item.label, this._query)}</span\n >`\n }\n </li>`,\n )}\n </ul>\n ${\n filtered.length === 0\n ? html`<div\n class=\"empty\"\n part=\"empty\"\n >\n ${this._localize.term('noResults')}\n </div>`\n : nothing\n }\n </div>\n\n <div\n class=${cls('visually-hidden')}\n aria-live=\"polite\"\n >\n ${status}\n </div>\n </div>\n `;\n }\n}\n\n// Typed addEventListener for the colliding `change` / `input` names.\nexport interface Select {\n addEventListener<K extends keyof SelectEventMap>(\n type: K,\n listener: (this: Select, ev: SelectEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener<K extends keyof SelectEventMap>(\n type: K,\n listener: (this: Select, ev: SelectEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"stories-viewer.d.ts","sourceRoot":"","sources":["../../../src/html/elements/stories-viewer/stories-viewer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stories-viewer.d.ts","sourceRoot":"","sources":["../../../src/html/elements/stories-viewer/stories-viewer.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import{i as e}from"../../chunks/lit.js";import{r as t}from"../../chunks/static-html.js";import{n,r,t as i}from"../../chunks/decorate.js";import{t as a}from"../../chunks/query.js";import{t as o}from"../../chunks/lit-html.js";import{t as s}from"../../chunks/map.js";import{LuxenElement as c}from"../../shared/luxen-element.js";import{LocalizeController as l}from"../../shared/localize.js";import{tagName as u}from"../../registry.js";import{staticTag as d}from"../../static-tag.js";import{ShowEvent as f}from"../../events/show.js";import{AfterShowEvent as p}from"../../events/after-show.js";import{HideEvent as m}from"../../events/hide.js";import{AfterHideEvent as h}from"../../events/after-hide.js";import g from"../../shared/styles/host.styles.js";var _=e(`:host{--width:min(420px, 100vw);--aspect-ratio:9 / 16;--progress-color:white;--progress-bg:#ffffff59;--progress-gap:4px;--show-duration:.2s;--hide-duration:.2s;--backdrop:var(--l-backdrop-strong);display:contents}dialog{color:#fff;opacity:0;width:100vw;max-width:none;height:100dvh;max-height:none;transition-property:opacity,display,overlay;transition-duration:var(--hide-duration);transition-behavior:allow-discrete;background:0 0;border:0;place-items:center;margin:0;padding:0;position:fixed;inset:0;overflow:clip}dialog::backdrop{background:var(--backdrop)}dialog[open]{opacity:1;transition-duration:var(--show-duration);display:grid}@starting-style{dialog[open]{opacity:0}}.frame{width:var(--width);max-width:100vw;max-height:100dvh;aspect-ratio:var(--aspect-ratio);isolation:isolate;-webkit-user-select:none;user-select:none;touch-action:pan-y;background:#000;border-radius:.75rem;position:relative;overflow:clip}@media (width<=480px){.frame{aspect-ratio:auto;border-radius:0;width:100vw;height:100dvh}}.video{object-fit:cover;background:#000;width:100%;height:100%;position:absolute;inset:0}.spinner{z-index:1;color:#fff;pointer-events:none;opacity:0;width:2rem;height:2rem;margin:auto;transition:opacity .15s;position:absolute;inset:0}.spinner[data-state=loading]{opacity:1;transition-delay:.2s}@media (prefers-reduced-motion:reduce){.spinner{transition:none}}.progress{z-index:2;gap:var(--progress-gap);pointer-events:none;display:flex;position:absolute;top:.5rem;left:.5rem;right:.5rem}.progress-segment{background:var(--progress-bg);border-radius:999px;flex:1 1 0;height:2.5px;overflow:clip}.progress-fill{background:var(--progress-color);width:100%;height:100%;transform:scaleX(var(--p,0));transform-origin:0;transition:transform 80ms linear;display:block}.progress-segment[data-state=past] .progress-fill{transform:scaleX(1)}@media (prefers-reduced-motion:reduce){.progress-fill{transition:none}:host{--show-duration:0s;--hide-duration:0s}}.header{z-index:2;pointer-events:none;align-items:center;gap:.625rem;min-width:0;display:flex;position:absolute;top:1.25rem;left:.75rem;right:4rem}.header>::slotted(*){pointer-events:auto}.header-thumb{object-fit:cover;pointer-events:auto;border-radius:50%;flex-shrink:0;width:2rem;height:2rem}.header-text{pointer-events:auto;flex-direction:column;min-width:0;display:flex}.header-label{font-size:.875rem;font-weight:var(--l-font-weight-semibold);color:#fff;text-overflow:ellipsis;white-space:nowrap;line-height:1.2;overflow:hidden}.actions{z-index:2;pointer-events:none;flex-direction:column;gap:.75rem;display:flex;position:absolute;top:1.25rem;right:.75rem}.actions>::slotted(*),.actions .btn{pointer-events:auto}.btn{color:#fff;cursor:pointer;background:#00000040;border:0;border-radius:999px;justify-content:center;align-items:center;width:2rem;height:2rem;padding:0;font-size:1rem;transition:background .15s;display:inline-flex;position:relative}.btn:hover{background:#00000073}.btn:focus-visible{outline:2px solid var(--progress-color);outline-offset:2px}.btn:before{content:"";position:absolute;inset:-6px}.btn-nav{z-index:2;width:2.25rem;height:2.25rem;font-size:1.25rem;position:absolute}.btn-nav:before{inset:-4px}.btn-previous,.btn-next{top:50%;transform:translateY(-50%)}@media (width>=768px){.btn-previous{left:calc(50% - var(--width) / 2 - 3rem)}.btn-next{right:calc(50% - var(--width) / 2 - 3rem)}}@media (width<=767px){.btn-previous,.btn-next{display:none}}.tap-zone{z-index:0;position:absolute;top:0;bottom:0}.tap-previous{width:30%;left:0}.tap-next{left:30%;right:0}.overlay{z-index:1;pointer-events:none;padding:1rem;position:absolute;inset:auto 0 0}.overlay>::slotted(*){pointer-events:auto}.sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}`),v=class extends Event{constructor(e,t){super(`story-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.index=e,this.story=t}},y=class extends Event{constructor(e){super(`story-end`,{bubbles:!1,composed:!1,cancelable:!1}),this.index=e}},b=class extends Event{constructor(e,t){super(`chapter-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.chapter=e,this.story=t}},x=class extends Event{constructor(e){super(`mute-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.muted=e}},S=Symbol.for(`luxen-stories-viewer-scroll-lock`);if(typeof document<`u`&&!(S in document)){let e=new CSSStyleSheet;e.replaceSync(`html:has(${u(`stories-viewer`)}[data-modal]) { overflow: hidden; scrollbar-gutter: stable; }`),document.adoptedStyleSheets.push(e),Object.defineProperty(document,S,{value:e})}var C=class extends c{constructor(...e){super(...e),this._localize=new l(this),this.open=!1,this.index=0,this.muted=!0,this.loop=!1,this.autoAdvance=!0,this.lightDismiss=!0,this.chapter=0,this.stories=[],this.source=null,this._paused=!1,this._loading=!1,this._raf=0,this._holdTimer=0,this._pointerStart=null,this._segments=[],this._chapterStarts=[0],this._prefetchedSrc=null,this._onKeyDown=e=>{if(this.open)switch(e.key){case`ArrowLeft`:e.preventDefault(),this.previous();break;case`ArrowRight`:e.preventDefault(),this.next();break;case` `:e.preventDefault(),this._video.paused?this.play():this.pause();break;case`m`:case`M`:e.preventDefault(),this.muted=!this.muted,this.dispatchEvent(new x(this.muted))}},this._onFramePointerDown=e=>{e.target.closest(`button, a, [data-no-gesture]`)||(this._pointerStart={x:e.clientX,y:e.clientY,t:performance.now()},this._holdTimer=window.setTimeout(()=>this.pause(),200))},this._onFramePointerUp=e=>{clearTimeout(this._holdTimer);let t=this._pointerStart;if(this._pointerStart=null,!t)return;let n=e.clientX-t.x,r=e.clientY-t.y,i=performance.now()-t.t;i>=200&&Math.abs(n)<10&&Math.abs(r)<10?this._video.paused&&this.play():i<200&&Math.abs(n)<10&&Math.abs(r)<10?e.target.closest(`[data-tap]`)?.dataset.tap===`previous`?this.previous():this.next():Math.abs(n)>Math.abs(r)&&Math.abs(n)>50?n<0?this.nextStory():this.previousStory():r>50&&this.close()},this._tick=()=>{if(!this.open)return;let e=this._video,t=this._segments[this.chapter];if(e&&t){let n=this._chapterStarts[this.chapter]??0,r=this._chapterStarts[this.chapter+1]??e.duration,i=Math.max(0,r-n),a=i?Math.max(0,Math.min((e.currentTime-n)/i,1)):0;t.style.setProperty(`--p`,String(a)),a>=1&&this.chapter<this._lastChapter&&this._advance(1)}this._raf=requestAnimationFrame(this._tick)}}static{this.styles=[g,_]}connectedCallback(){super.connectedCallback(),this.addEventListener(`keydown`,this._onKeyDown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(`keydown`,this._onKeyDown),cancelAnimationFrame(this._raf),clearTimeout(this._holdTimer),this._clearPrefetchHints()}firstUpdated(){this._dialog.addEventListener(`cancel`,e=>this._onCancel(e)),this._dialog.addEventListener(`close`,()=>this._onNativeClose()),this._dialog.addEventListener(`click`,e=>this._onDialogClick(e)),this._video.addEventListener(`ended`,()=>this._onEnded()),this._video.addEventListener(`loadedmetadata`,()=>this._tick()),this._video.addEventListener(`play`,()=>this._paused=!1),this._video.addEventListener(`pause`,()=>this._paused=!0),this._video.addEventListener(`loadstart`,()=>this._loading=!0),this._video.addEventListener(`waiting`,()=>this._loading=!0),this._video.addEventListener(`playing`,()=>this._loading=!1),this._video.addEventListener(`canplay`,()=>this._loading=!1),this._video.addEventListener(`error`,()=>this._loading=!1),this._video.addEventListener(`timeupdate`,()=>this._maybePrefetchNext())}willUpdate(e){let t=e.has(`open`)&&this.open,n=e.has(`index`);if(t||n){let e=this.stories[this.index];e&&(this._chapterStarts=e.getChapterStarts(),this.chapter=0)}}updated(e){if(e.has(`open`)){if(this.open&&!this._dialog.open){if(!this.dispatchEvent(new f)){queueMicrotask(()=>{this.open=!1});return}this.toggleAttribute(`data-modal`,!0),this._dialog.showModal(),queueMicrotask(()=>this._loadCurrent()),this._emitAfter(`after-show`)}else if(!this.open&&this._dialog.open){if(!this.dispatchEvent(new m)){queueMicrotask(()=>{this.open=!0});return}this._dialog.close()}}if(e.has(`index`)&&this.open){queueMicrotask(()=>this._loadCurrent());let e=this.stories[this.index];e&&this.dispatchEvent(new v(this.index,e))}e.has(`chapter`)&&this.open&&!e.has(`index`)&&this._applyChapterSeek(),e.has(`muted`)&&this._video&&(this._video.muted=this.muted)}_onCancel(e){this.dispatchEvent(new m)||e.preventDefault()}_onNativeClose(){cancelAnimationFrame(this._raf),this._video.pause(),this._video.removeAttribute(`src`),this._video.load(),this._clearPrefetchHints();for(let e=0;e<=this.index&&e<this.stories.length;e++)this.stories[e].seen=!0;this._restoreFocus(),this.open=!1,this.removeAttribute(`data-modal`),this._emitAfter(`after-hide`)}_onDialogClick(e){this.lightDismiss&&e.target===this._dialog&&(this.open=!1)}_refreshSegmentRefs(){this._segments=Array.from(this.shadowRoot.querySelectorAll(`[part~="progress-fill"]`))}_resetSegmentFills(){this._refreshSegmentRefs();for(let e=0;e<this._segments.length;e++){let t=+(e<this.chapter);this._segments[e].style.setProperty(`--p`,String(t))}}_loadCurrent(){if(!this.open||!this._dialog.open)return;let e=this.stories[this.index];e&&(cancelAnimationFrame(this._raf),this._prefetchedSrc=null,this._syncSlots(e),this._video.muted=this.muted,this._video.loop=this.loop,this._video.src===e.src?this._video.currentTime=0:this._video.src=e.src,requestAnimationFrame(()=>this._resetSegmentFills()),this._video.play().catch(()=>{this.muted=!0,this._video.muted=!0,this._video.play().catch(()=>{})}))}_maybePrefetchNext(){let e=this._video;if(!e.duration||e.currentTime/e.duration<.5)return;let t=this.stories[this.index+1];if(!t?.src||this._prefetchedSrc===t.src)return;this._prefetchedSrc=t.src;let n=document.createElement(`link`);n.rel=`preload`,n.as=`video`,n.href=t.src,n.crossOrigin=`anonymous`,n.dataset.luxenStoriesPrefetch=``,document.head.appendChild(n)}_clearPrefetchHints(){let e=document.head.querySelectorAll(`link[data-luxen-stories-prefetch]`);for(let t of e)t.remove();this._prefetchedSrc=null}_applyChapterSeek(){let e=this._chapterStarts[this.chapter]??0;this._video.currentTime=e,this._resetSegmentFills();let t=this.stories[this.index];t&&this.dispatchEvent(new b(this.chapter,t))}get _lastChapter(){return Math.max(0,this._chapterStarts.length-1)}_syncSlots(e){for(let e of Array.from(this.children))e.dataset.storySlot!==void 0&&e.remove();for(let t of Array.from(e.children)){if(!t.getAttribute?.(`slot`))continue;let e=t.cloneNode(!0);e.dataset.storySlot=``,this.appendChild(e)}}_onEnded(){this.stories[this.index]&&this.dispatchEvent(new y(this.index)),this.loop?(this._video.currentTime=this._chapterStarts[0]??0,this.chapter=0,this._video.play().catch(()=>{})):this.autoAdvance&&(this.index<this.stories.length-1?this.index+=1:this.close())}_advance(e){if(e===1){this.chapter<this._lastChapter?this.chapter+=1:this.index<this.stories.length-1?this.index+=1:this.close();return}let t=this._chapterStarts[this.chapter]??0;if(this._video.currentTime>t+1)this._video.currentTime=t,this._resetSegmentFills();else if(this.chapter>0)--this.chapter;else if(this.index>0){let e=this.stories[this.index-1],t=Math.max(0,e.getChapterStarts().length-1);--this.index,queueMicrotask(()=>{this.chapter=t})}}openAt(e,t=0,n=null){this.stories=e,this.source=n,this.index=Math.max(0,Math.min(t,e.length-1)),this.open=!0}close(){this.open=!1}next(){this._advance(1)}previous(){this._advance(-1)}nextStory(){this.index<this.stories.length-1?this.index+=1:this.close()}previousStory(){this.index>0&&--this.index}play(){this._video?.play().catch(()=>{}),this._raf=requestAnimationFrame(this._tick)}pause(){this._video?.pause(),cancelAnimationFrame(this._raf)}async _emitAfter(e){await new Promise(e=>requestAnimationFrame(()=>e(null)));let t=this._dialog.getAnimations({subtree:!1});await Promise.all(t.map(e=>e.finished.catch(()=>{}))),e===`after-show`===this.open&&(this.dispatchEvent(e===`after-show`?new p:new h),e===`after-show`&&(this._raf=requestAnimationFrame(this._tick)))}_restoreFocus(){((this.source?.stories()??this.stories)[this.index]?.querySelector(`button[data-story-trigger]`))?.focus({preventScroll:!1})}render(){let e=this.stories.length,n=this.stories[this.index],r=this.index===0,i=this.index>=e-1,a=this._chapterStarts.length?this._chapterStarts:n?.getChapterStarts()??[0],c=d(`icon`),l=d(`spinner`);return t`
1
+ import{i as e}from"../../chunks/lit.js";import{r as t}from"../../chunks/static-html.js";import{n,r,t as i}from"../../chunks/decorate.js";import{t as a}from"../../chunks/query.js";import{t as o}from"../../chunks/lit-html.js";import{t as s}from"../../chunks/map.js";import{LuxenElement as c}from"../../shared/luxen-element.js";import{LocalizeController as l}from"../../shared/localize.js";import{cls as u,tagName as d}from"../../registry.js";import{staticTag as f}from"../../static-tag.js";import{ShowEvent as p}from"../../events/show.js";import{AfterShowEvent as m}from"../../events/after-show.js";import{HideEvent as h}from"../../events/hide.js";import{AfterHideEvent as g}from"../../events/after-hide.js";import _ from"../../shared/styles/host.styles.js";import v from"../../shared/styles/visually-hidden.styles.js";var y=e(`:host{--width:min(420px, 100vw);--aspect-ratio:9 / 16;--progress-color:white;--progress-bg:#ffffff59;--progress-gap:4px;--show-duration:.2s;--hide-duration:.2s;--backdrop:var(--l-backdrop-strong);display:contents}dialog{color:#fff;opacity:0;width:100vw;max-width:none;height:100dvh;max-height:none;transition-property:opacity,display,overlay;transition-duration:var(--hide-duration);transition-behavior:allow-discrete;background:0 0;border:0;place-items:center;margin:0;padding:0;position:fixed;inset:0;overflow:clip}dialog::backdrop{background:var(--backdrop)}dialog[open]{opacity:1;transition-duration:var(--show-duration);display:grid}@starting-style{dialog[open]{opacity:0}}.frame{width:var(--width);max-width:100vw;max-height:100dvh;aspect-ratio:var(--aspect-ratio);isolation:isolate;-webkit-user-select:none;user-select:none;touch-action:pan-y;background:#000;border-radius:.75rem;position:relative;overflow:clip}@media (width<=480px){.frame{aspect-ratio:auto;border-radius:0;width:100vw;height:100dvh}}.video{object-fit:cover;background:#000;width:100%;height:100%;position:absolute;inset:0}.spinner{z-index:1;color:#fff;pointer-events:none;opacity:0;width:2rem;height:2rem;margin:auto;transition:opacity .15s;position:absolute;inset:0}.spinner[data-state=loading]{opacity:1;transition-delay:.2s}@media (prefers-reduced-motion:reduce){.spinner{transition:none}}.progress{z-index:2;gap:var(--progress-gap);pointer-events:none;display:flex;position:absolute;top:.5rem;left:.5rem;right:.5rem}.progress-segment{background:var(--progress-bg);border-radius:999px;flex:1 1 0;height:2.5px;overflow:clip}.progress-fill{background:var(--progress-color);width:100%;height:100%;transform:scaleX(var(--p,0));transform-origin:0;transition:transform 80ms linear;display:block}.progress-segment[data-state=past] .progress-fill{transform:scaleX(1)}@media (prefers-reduced-motion:reduce){.progress-fill{transition:none}:host{--show-duration:0s;--hide-duration:0s}}.header{z-index:2;pointer-events:none;align-items:center;gap:.625rem;min-width:0;display:flex;position:absolute;top:1.25rem;left:.75rem;right:4rem}.header>::slotted(*){pointer-events:auto}.header-thumb{object-fit:cover;pointer-events:auto;border-radius:50%;flex-shrink:0;width:2rem;height:2rem}.header-text{pointer-events:auto;flex-direction:column;min-width:0;display:flex}.header-label{font-size:.875rem;font-weight:var(--l-font-weight-semibold);color:#fff;text-overflow:ellipsis;white-space:nowrap;line-height:1.2;overflow:hidden}.actions{z-index:2;pointer-events:none;flex-direction:column;gap:.75rem;display:flex;position:absolute;top:1.25rem;right:.75rem}.actions>::slotted(*),.actions .btn{pointer-events:auto}.btn{color:#fff;cursor:pointer;background:#00000040;border:0;border-radius:999px;justify-content:center;align-items:center;width:2rem;height:2rem;padding:0;font-size:1rem;transition:background .15s;display:inline-flex;position:relative}.btn:hover{background:#00000073}.btn:focus-visible{outline:2px solid var(--progress-color);outline-offset:2px}.btn:before{content:"";position:absolute;inset:-6px}.btn-nav{z-index:2;width:2.25rem;height:2.25rem;font-size:1.25rem;position:absolute}.btn-nav:before{inset:-4px}.btn-previous,.btn-next{top:50%;transform:translateY(-50%)}@media (width>=768px){.btn-previous{left:calc(50% - var(--width) / 2 - 3rem)}.btn-next{right:calc(50% - var(--width) / 2 - 3rem)}}@media (width<=767px){.btn-previous,.btn-next{display:none}}.tap-zone{z-index:0;position:absolute;top:0;bottom:0}.tap-previous{width:30%;left:0}.tap-next{left:30%;right:0}.overlay{z-index:1;pointer-events:none;padding:1rem;position:absolute;inset:auto 0 0}.overlay>::slotted(*){pointer-events:auto}`),b=class extends Event{constructor(e,t){super(`story-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.index=e,this.story=t}},x=class extends Event{constructor(e){super(`story-end`,{bubbles:!1,composed:!1,cancelable:!1}),this.index=e}},S=class extends Event{constructor(e,t){super(`chapter-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.chapter=e,this.story=t}},C=class extends Event{constructor(e){super(`mute-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.muted=e}},w=Symbol.for(`luxen-stories-viewer-scroll-lock`);if(typeof document<`u`&&!(w in document)){let e=new CSSStyleSheet;e.replaceSync(`html:has(${d(`stories-viewer`)}[data-modal]) { overflow: hidden; scrollbar-gutter: stable; }`),document.adoptedStyleSheets.push(e),Object.defineProperty(document,w,{value:e})}var T=class extends c{constructor(...e){super(...e),this._localize=new l(this),this.open=!1,this.index=0,this.muted=!0,this.loop=!1,this.autoAdvance=!0,this.lightDismiss=!0,this.chapter=0,this.stories=[],this.source=null,this._paused=!1,this._loading=!1,this._raf=0,this._holdTimer=0,this._pointerStart=null,this._segments=[],this._chapterStarts=[0],this._prefetchedSrc=null,this._onKeyDown=e=>{if(this.open)switch(e.key){case`ArrowLeft`:e.preventDefault(),this.previous();break;case`ArrowRight`:e.preventDefault(),this.next();break;case` `:e.preventDefault(),this._video.paused?this.play():this.pause();break;case`m`:case`M`:e.preventDefault(),this.muted=!this.muted,this.dispatchEvent(new C(this.muted))}},this._onFramePointerDown=e=>{e.target.closest(`button, a, [data-no-gesture]`)||(this._pointerStart={x:e.clientX,y:e.clientY,t:performance.now()},this._holdTimer=window.setTimeout(()=>this.pause(),200))},this._onFramePointerUp=e=>{clearTimeout(this._holdTimer);let t=this._pointerStart;if(this._pointerStart=null,!t)return;let n=e.clientX-t.x,r=e.clientY-t.y,i=performance.now()-t.t;i>=200&&Math.abs(n)<10&&Math.abs(r)<10?this._video.paused&&this.play():i<200&&Math.abs(n)<10&&Math.abs(r)<10?e.target.closest(`[data-tap]`)?.dataset.tap===`previous`?this.previous():this.next():Math.abs(n)>Math.abs(r)&&Math.abs(n)>50?n<0?this.nextStory():this.previousStory():r>50&&this.close()},this._tick=()=>{if(!this.open)return;let e=this._video,t=this._segments[this.chapter];if(e&&t){let n=this._chapterStarts[this.chapter]??0,r=this._chapterStarts[this.chapter+1]??e.duration,i=Math.max(0,r-n),a=i?Math.max(0,Math.min((e.currentTime-n)/i,1)):0;t.style.setProperty(`--p`,String(a)),a>=1&&this.chapter<this._lastChapter&&this._advance(1)}this._raf=requestAnimationFrame(this._tick)}}static{this.styles=[_,v,y]}connectedCallback(){super.connectedCallback(),this.addEventListener(`keydown`,this._onKeyDown)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(`keydown`,this._onKeyDown),cancelAnimationFrame(this._raf),clearTimeout(this._holdTimer),this._clearPrefetchHints()}firstUpdated(){this._dialog.addEventListener(`cancel`,e=>this._onCancel(e)),this._dialog.addEventListener(`close`,()=>this._onNativeClose()),this._dialog.addEventListener(`click`,e=>this._onDialogClick(e)),this._video.addEventListener(`ended`,()=>this._onEnded()),this._video.addEventListener(`loadedmetadata`,()=>this._tick()),this._video.addEventListener(`play`,()=>this._paused=!1),this._video.addEventListener(`pause`,()=>this._paused=!0),this._video.addEventListener(`loadstart`,()=>this._loading=!0),this._video.addEventListener(`waiting`,()=>this._loading=!0),this._video.addEventListener(`playing`,()=>this._loading=!1),this._video.addEventListener(`canplay`,()=>this._loading=!1),this._video.addEventListener(`error`,()=>this._loading=!1),this._video.addEventListener(`timeupdate`,()=>this._maybePrefetchNext())}willUpdate(e){let t=e.has(`open`)&&this.open,n=e.has(`index`);if(t||n){let e=this.stories[this.index];e&&(this._chapterStarts=e.getChapterStarts(),this.chapter=0)}}updated(e){if(e.has(`open`)){if(this.open&&!this._dialog.open){if(!this.dispatchEvent(new p)){queueMicrotask(()=>{this.open=!1});return}this.toggleAttribute(`data-modal`,!0),this._dialog.showModal(),queueMicrotask(()=>this._loadCurrent()),this._emitAfter(`after-show`)}else if(!this.open&&this._dialog.open){if(!this.dispatchEvent(new h)){queueMicrotask(()=>{this.open=!0});return}this._dialog.close()}}if(e.has(`index`)&&this.open){queueMicrotask(()=>this._loadCurrent());let e=this.stories[this.index];e&&this.dispatchEvent(new b(this.index,e))}e.has(`chapter`)&&this.open&&!e.has(`index`)&&this._applyChapterSeek(),e.has(`muted`)&&this._video&&(this._video.muted=this.muted)}_onCancel(e){this.dispatchEvent(new h)||e.preventDefault()}_onNativeClose(){cancelAnimationFrame(this._raf),this._video.pause(),this._video.removeAttribute(`src`),this._video.load(),this._clearPrefetchHints();for(let e=0;e<=this.index&&e<this.stories.length;e++)this.stories[e].seen=!0;this._restoreFocus(),this.open=!1,this.removeAttribute(`data-modal`),this._emitAfter(`after-hide`)}_onDialogClick(e){this.lightDismiss&&e.target===this._dialog&&(this.open=!1)}_refreshSegmentRefs(){this._segments=Array.from(this.shadowRoot.querySelectorAll(`[part~="progress-fill"]`))}_resetSegmentFills(){this._refreshSegmentRefs();for(let e=0;e<this._segments.length;e++){let t=+(e<this.chapter);this._segments[e].style.setProperty(`--p`,String(t))}}_loadCurrent(){if(!this.open||!this._dialog.open)return;let e=this.stories[this.index];e&&(cancelAnimationFrame(this._raf),this._prefetchedSrc=null,this._syncSlots(e),this._video.muted=this.muted,this._video.loop=this.loop,this._video.src===e.src?this._video.currentTime=0:this._video.src=e.src,requestAnimationFrame(()=>this._resetSegmentFills()),this._video.play().catch(()=>{this.muted=!0,this._video.muted=!0,this._video.play().catch(()=>{})}))}_maybePrefetchNext(){let e=this._video;if(!e.duration||e.currentTime/e.duration<.5)return;let t=this.stories[this.index+1];if(!t?.src||this._prefetchedSrc===t.src)return;this._prefetchedSrc=t.src;let n=document.createElement(`link`);n.rel=`preload`,n.as=`video`,n.href=t.src,n.crossOrigin=`anonymous`,n.dataset.luxenStoriesPrefetch=``,document.head.appendChild(n)}_clearPrefetchHints(){let e=document.head.querySelectorAll(`link[data-luxen-stories-prefetch]`);for(let t of e)t.remove();this._prefetchedSrc=null}_applyChapterSeek(){let e=this._chapterStarts[this.chapter]??0;this._video.currentTime=e,this._resetSegmentFills();let t=this.stories[this.index];t&&this.dispatchEvent(new S(this.chapter,t))}get _lastChapter(){return Math.max(0,this._chapterStarts.length-1)}_syncSlots(e){for(let e of Array.from(this.children))e.dataset.storySlot!==void 0&&e.remove();for(let t of Array.from(e.children)){if(!t.getAttribute?.(`slot`))continue;let e=t.cloneNode(!0);e.dataset.storySlot=``,this.appendChild(e)}}_onEnded(){this.stories[this.index]&&this.dispatchEvent(new x(this.index)),this.loop?(this._video.currentTime=this._chapterStarts[0]??0,this.chapter=0,this._video.play().catch(()=>{})):this.autoAdvance&&(this.index<this.stories.length-1?this.index+=1:this.close())}_advance(e){if(e===1){this.chapter<this._lastChapter?this.chapter+=1:this.index<this.stories.length-1?this.index+=1:this.close();return}let t=this._chapterStarts[this.chapter]??0;if(this._video.currentTime>t+1)this._video.currentTime=t,this._resetSegmentFills();else if(this.chapter>0)--this.chapter;else if(this.index>0){let e=this.stories[this.index-1],t=Math.max(0,e.getChapterStarts().length-1);--this.index,queueMicrotask(()=>{this.chapter=t})}}openAt(e,t=0,n=null){this.stories=e,this.source=n,this.index=Math.max(0,Math.min(t,e.length-1)),this.open=!0}close(){this.open=!1}next(){this._advance(1)}previous(){this._advance(-1)}nextStory(){this.index<this.stories.length-1?this.index+=1:this.close()}previousStory(){this.index>0&&--this.index}play(){this._video?.play().catch(()=>{}),this._raf=requestAnimationFrame(this._tick)}pause(){this._video?.pause(),cancelAnimationFrame(this._raf)}async _emitAfter(e){await new Promise(e=>requestAnimationFrame(()=>e(null)));let t=this._dialog.getAnimations({subtree:!1});await Promise.all(t.map(e=>e.finished.catch(()=>{}))),e===`after-show`===this.open&&(this.dispatchEvent(e===`after-show`?new m:new g),e===`after-show`&&(this._raf=requestAnimationFrame(this._tick)))}_restoreFocus(){((this.source?.stories()??this.stories)[this.index]?.querySelector(`button[data-story-trigger]`))?.focus({preventScroll:!1})}render(){let e=this.stories.length,n=this.stories[this.index],r=this.index===0,i=this.index>=e-1,a=this._chapterStarts.length?this._chapterStarts:n?.getChapterStarts()??[0],c=f(`icon`),l=f(`spinner`);return t`
2
2
  <dialog
3
3
  part="dialog"
4
4
  aria-label=${this._localize.term(`stories`)}
@@ -87,7 +87,7 @@ import{i as e}from"../../chunks/lit.js";import{r as t}from"../../chunks/static-h
87
87
  class="btn btn-mute"
88
88
  aria-label=${this.muted?this._localize.term(`unmute`):this._localize.term(`mute`)}
89
89
  aria-pressed=${this.muted?`false`:`true`}
90
- @click=${()=>{this.muted=!this.muted,this.dispatchEvent(new x(this.muted))}}
90
+ @click=${()=>{this.muted=!this.muted,this.dispatchEvent(new C(this.muted))}}
91
91
  >
92
92
  <${c} name=${this.muted?`mdi:volume-off`:`mdi:volume-high`}></${c}>
93
93
  </button>
@@ -131,7 +131,7 @@ import{i as e}from"../../chunks/lit.js";import{r as t}from"../../chunks/static-h
131
131
  </div>
132
132
 
133
133
  <div
134
- class="sr-only"
134
+ class=${u(`visually-hidden`)}
135
135
  aria-live="polite"
136
136
  >
137
137
  ${e?`Story ${this.index+1} of ${e}${this.stories[this.index]?.label?` — ${this.stories[this.index].label}`:``}${a.length>1?` — chapter ${this.chapter+1} of ${a.length}`:``}`:``}
@@ -157,5 +157,5 @@ import{i as e}from"../../chunks/lit.js";import{r as t}from"../../chunks/static-h
157
157
  <${c} name="mdi:chevron-right"></${c}>
158
158
  </button>`}
159
159
  </dialog>
160
- `}};i([r({type:Boolean,reflect:!0})],C.prototype,`open`,void 0),i([r({type:Number,reflect:!0})],C.prototype,`index`,void 0),i([r({type:Boolean,reflect:!0})],C.prototype,`muted`,void 0),i([r({type:Boolean,reflect:!0})],C.prototype,`loop`,void 0),i([r({type:Boolean,reflect:!0,attribute:`auto-advance`})],C.prototype,`autoAdvance`,void 0),i([r({type:Boolean,reflect:!0,attribute:`light-dismiss`})],C.prototype,`lightDismiss`,void 0),i([r({type:Number,reflect:!0})],C.prototype,`chapter`,void 0),i([a(`dialog`)],C.prototype,`_dialog`,void 0),i([a(`video`)],C.prototype,`_video`,void 0),i([n()],C.prototype,`_paused`,void 0),i([n()],C.prototype,`_loading`,void 0);export{b as ChapterChangeEvent,C as LuxenStoriesViewer,x as MuteChangeEvent,v as StoryChangeEvent,y as StoryEndEvent};
160
+ `}};i([r({type:Boolean,reflect:!0})],T.prototype,`open`,void 0),i([r({type:Number,reflect:!0})],T.prototype,`index`,void 0),i([r({type:Boolean,reflect:!0})],T.prototype,`muted`,void 0),i([r({type:Boolean,reflect:!0})],T.prototype,`loop`,void 0),i([r({type:Boolean,reflect:!0,attribute:`auto-advance`})],T.prototype,`autoAdvance`,void 0),i([r({type:Boolean,reflect:!0,attribute:`light-dismiss`})],T.prototype,`lightDismiss`,void 0),i([r({type:Number,reflect:!0})],T.prototype,`chapter`,void 0),i([a(`dialog`)],T.prototype,`_dialog`,void 0),i([a(`video`)],T.prototype,`_video`,void 0),i([n()],T.prototype,`_paused`,void 0),i([n()],T.prototype,`_loading`,void 0);export{S as ChapterChangeEvent,T as LuxenStoriesViewer,C as MuteChangeEvent,b as StoryChangeEvent,x as StoryEndEvent};
161
161
  //# sourceMappingURL=stories-viewer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stories-viewer.js","names":["unsafeCSS","rawStyles","hostStyles","map","nothing","_decorate","property","query","state","r","u","__decorate","e","A","o","host_styles_default","styles","StoryChangeEvent","index","story","StoryEndEvent","ChapterChangeEvent","chapter","MuteChangeEvent","muted","SCROLL_LOCK_SHEET","sheet","tagName","LuxenStoriesViewer","LuxenElement","_args","LocalizeController","start","dx","dy","elapsed","v","seg","end","span","p","openBecoming","changed","indexChanged","ShowEvent","HideEvent","i","nextStory","link","links","target","node","child","clone","direction","previousStory","previousLast","stories","source","anims","a","name","AfterShowEvent","AfterHideEvent","total","currentStory","isFirstStory","isLastStory","chapterStarts","iconTag","staticTag","spinnerTag","n","r$1"],"sources":["../../../src/html/elements/stories-viewer/stories-viewer.css?inline","../../../src/html/elements/stories-viewer/stories-viewer.ts"],"sourcesContent":[":host {\n --width: min(420px, 100vw);\n --aspect-ratio: 9 / 16;\n --progress-color: white;\n --progress-bg: rgb(255 255 255 / 35%);\n --progress-gap: 4px;\n --show-duration: 200ms;\n --hide-duration: 200ms;\n /* Semi-transparent backdrop so the underlying page (e.g. a product page) stays visible behind the player, but with enough darkness to focus attention on the frame. Override with `--backdrop: …` or set to `oklch(0% 0 0 / 90%)` for an opaque blackout. */\n --backdrop: var(--l-backdrop-strong);\n\n display: contents;\n}\n\ndialog {\n position: fixed;\n inset: 0;\n width: 100vw;\n height: 100dvh;\n max-width: none;\n max-height: none;\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n color: white;\n overflow: clip;\n\n /* Center the frame at all times. `place-items` is on the base rule so it survives the close transition (only `display` and `opacity` are transitioned with `allow-discrete`; non-transitioned properties revert immediately when `[open]` is removed and would un-center the frame to top-left for one frame). */\n place-items: center;\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 }\n\n /* OPEN STATE */\n &[open] {\n display: grid;\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.frame {\n position: relative;\n width: var(--width);\n max-width: 100vw;\n max-height: 100dvh;\n aspect-ratio: var(--aspect-ratio);\n background: black;\n border-radius: 0.75rem;\n overflow: clip;\n isolation: isolate;\n user-select: none;\n -webkit-user-select: none;\n touch-action: pan-y;\n}\n\n@media (max-width: 480px) {\n .frame {\n width: 100vw;\n height: 100dvh;\n aspect-ratio: auto;\n border-radius: 0;\n }\n}\n\n.video {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n background: black;\n}\n\n/* Loading spinner — centered over the video. Delayed fade-in so a fast buffer never visibly flashes the spinner. */\n.spinner {\n position: absolute;\n inset: 0;\n margin: auto;\n z-index: 1;\n width: 2rem;\n height: 2rem;\n color: white;\n pointer-events: none;\n opacity: 0;\n transition: opacity 150ms ease;\n}\n\n.spinner[data-state='loading'] {\n opacity: 1;\n /* Only paint the spinner if loading actually exceeds 200ms. */\n transition-delay: 200ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .spinner {\n transition: none;\n }\n}\n\n.progress {\n position: absolute;\n top: 0.5rem;\n left: 0.5rem;\n right: 0.5rem;\n z-index: 2;\n display: flex;\n gap: var(--progress-gap);\n pointer-events: none;\n}\n\n.progress-segment {\n flex: 1 1 0;\n height: 2.5px;\n border-radius: 999px;\n background: var(--progress-bg);\n overflow: clip;\n}\n\n.progress-fill {\n display: block;\n width: 100%;\n height: 100%;\n background: var(--progress-color);\n transform: scaleX(var(--p, 0));\n transform-origin: left;\n transition: transform 80ms linear;\n}\n\n.progress-segment[data-state='past'] .progress-fill {\n transform: scaleX(1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-fill {\n transition: none;\n }\n :host {\n --show-duration: 0ms;\n --hide-duration: 0ms;\n }\n}\n\n/* Top-left header — story thumbnail + label (slot fallback) or consumer-supplied content. */\n.header {\n position: absolute;\n top: 1.25rem;\n left: 0.75rem;\n right: 4rem; /* leave room for the right-side action stack */\n z-index: 2;\n display: flex;\n align-items: center;\n gap: 0.625rem;\n min-width: 0;\n pointer-events: none;\n}\n\n.header > ::slotted(*) {\n pointer-events: auto;\n}\n\n.header-thumb {\n width: 2rem;\n height: 2rem;\n border-radius: 50%;\n object-fit: cover;\n flex-shrink: 0;\n pointer-events: auto;\n}\n\n.header-text {\n display: flex;\n flex-direction: column;\n min-width: 0;\n pointer-events: auto;\n}\n\n.header-label {\n font-size: 0.875rem;\n font-weight: var(--l-font-weight-semibold);\n line-height: 1.2;\n color: white;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Top-right vertical action stack — close, play/pause, mute. */\n.actions {\n position: absolute;\n top: 1.25rem;\n right: 0.75rem;\n z-index: 2;\n display: flex;\n flex-direction: column;\n /* 12px gap leaves room for the 6px hit-area extension on each adjacent button (see `.btn::before` below). */\n gap: 0.75rem;\n pointer-events: none;\n}\n\n.actions > ::slotted(*),\n.actions .btn {\n pointer-events: auto;\n}\n\n.btn {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: rgb(0 0 0 / 25%);\n color: white;\n font-size: 1rem;\n cursor: pointer;\n transition: background 150ms ease;\n\n &:hover {\n background: rgb(0 0 0 / 45%);\n }\n\n &:focus-visible {\n outline: 2px solid var(--progress-color);\n outline-offset: 2px;\n }\n\n /* WCAG 2.1 SC 2.5.5 / RGAA 13.4 — 44×44 hit area without changing the visible 32×32 disc. The pseudo carries no paint, only hit testing. */\n &::before {\n content: '';\n position: absolute;\n inset: -6px;\n }\n}\n\n.btn-nav {\n position: absolute;\n z-index: 2;\n width: 2.25rem;\n height: 2.25rem;\n font-size: 1.25rem;\n\n /* Chevrons start at 36×36 — 4px each side reaches 44. */\n &::before {\n inset: -4px;\n }\n}\n\n.btn-previous,\n.btn-next {\n top: 50%;\n transform: translateY(-50%);\n}\n\n/* Desktop ≥ 768px: chevrons sit outside the frame, on the dim backdrop. */\n@media (min-width: 768px) {\n .btn-previous {\n left: calc(50% - var(--width) / 2 - 3rem);\n }\n .btn-next {\n right: calc(50% - var(--width) / 2 - 3rem);\n }\n}\n\n/* Mobile < 768px: hide chevrons entirely — tap zones drive nav. */\n@media (max-width: 767px) {\n .btn-previous,\n .btn-next {\n display: none;\n }\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆃🅰🅿 zones (invisible click/tap targets — left = previous, rest = next)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.tap-zone {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 0;\n}\n\n.tap-previous {\n left: 0;\n width: 30%;\n}\n\n.tap-next {\n left: 30%;\n right: 0;\n}\n\n.overlay {\n position: absolute;\n inset: auto 0 0 0;\n z-index: 1;\n padding: 1rem;\n pointer-events: none;\n}\n\n.overlay > ::slotted(*) {\n pointer-events: auto;\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n","import { nothing, unsafeCSS, type CSSResultGroup, type PropertyValues } from 'lit';\nimport { html } from 'lit/static-html.js';\nimport { property, query, state } from 'lit/decorators.js';\nimport { map } from 'lit/directives/map.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { tagName } from '../../registry.js';\nimport { staticTag } from '../../static-tag.js';\nimport { ShowEvent, AfterShowEvent, HideEvent, AfterHideEvent } from '../../events/index.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport type { LuxenStories } from '../stories/stories.js';\nimport type { LuxenStory } from '../story/story.js';\nimport rawStyles from './stories-viewer.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/** Fired when the active story changes. */\nexport class StoryChangeEvent extends Event {\n readonly index: number;\n readonly story: LuxenStory;\n constructor(index: number, story: LuxenStory) {\n super('story-change', { bubbles: false, composed: false, cancelable: false });\n this.index = index;\n this.story = story;\n }\n}\n\n/** Fired when the active story finishes playback. */\nexport class StoryEndEvent extends Event {\n readonly index: number;\n constructor(index: number) {\n super('story-end', { bubbles: false, composed: false, cancelable: false });\n this.index = index;\n }\n}\n\n/** Fired when the active chapter (within a story) changes. */\nexport class ChapterChangeEvent extends Event {\n readonly chapter: number;\n readonly story: LuxenStory;\n constructor(chapter: number, story: LuxenStory) {\n super('chapter-change', { bubbles: false, composed: false, cancelable: false });\n this.chapter = chapter;\n this.story = story;\n }\n}\n\n/** Fired when the mute state changes. */\nexport class MuteChangeEvent extends Event {\n readonly muted: boolean;\n constructor(muted: boolean) {\n super('mute-change', { bubbles: false, composed: false, cancelable: false });\n this.muted = muted;\n }\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n 'story-change': StoryChangeEvent;\n 'story-end': StoryEndEvent;\n 'chapter-change': ChapterChangeEvent;\n 'mute-change': MuteChangeEvent;\n }\n}\n\nconst SCROLL_LOCK_SHEET = Symbol.for('luxen-stories-viewer-scroll-lock');\nif (typeof document !== 'undefined' && !(SCROLL_LOCK_SHEET in document)) {\n const sheet = new CSSStyleSheet();\n // Runs at import time, after the consumer has called `setPrefix()` (or the\n // vite-plugin baked it into registry.js) — same ordering `define()` relies on.\n sheet.replaceSync(\n `html:has(${tagName('stories-viewer')}[data-modal]) { overflow: hidden; scrollbar-gutter: stable; }`,\n );\n document.adoptedStyleSheets.push(sheet);\n Object.defineProperty(document, SCROLL_LOCK_SHEET, { value: sheet });\n}\n\n/**\n * @summary Fullscreen modal that plays a sequence of `<l-story>` videos with an\n * Instagram-style segmented progress bar, previous/next navigation, mute toggle,\n * and auto-advance.\n *\n * Linked to one or more `<l-stories>` rows via matching `id` ↔ `for`. Open\n * programmatically via `open()` or by clicking a thumbnail in a linked row.\n *\n * @slot cta - Default CTA overlay (e.g. shoppable card). Per-story `slot=\"cta\"` inside `<l-story>` overrides this when that story is active.\n * @slot header - Default header overlay (e.g. avatar + author). Per-story override available the same way.\n * @slot close - Override the default close button.\n *\n * @csspart dialog - The native `<dialog>` element.\n * @csspart frame - The aspect-ratio video frame.\n * @csspart progress - The progress bar wrapper.\n * @csspart progress-segment - A single progress segment.\n * @csspart progress-fill - The fill element inside an active segment.\n * @csspart video - The `<video>` element.\n * @csspart overlay - The overlay wrapper that hosts CTA/header slots.\n * @csspart header - The top-left header area (story thumbnail + label fallback, or consumer-supplied content via the `header` slot).\n * @csspart header-label - The default story label inside the header.\n * @csspart actions - The top-right vertical button stack (close, play/pause, mute).\n * @csspart button-close - The close button.\n * @csspart button-pause - The play/pause toggle.\n * @csspart button-mute - The mute toggle.\n * @csspart button-previous - The previous story button.\n * @csspart button-next - The next story button.\n * @csspart spinner - The loading spinner shown while the current video is buffering.\n *\n * @cssproperty --width - Frame width. Default `min(420px, 100vw)`.\n * @cssproperty --progress-color - Active progress fill color. Default `white`.\n * @cssproperty --progress-bg - Inactive progress segment background. Default `rgb(255 255 255 / 35%)`.\n * @cssproperty --progress-gap - Gap between segments. Default `4px`.\n * @cssproperty --show-duration - Open transition duration. Default `200ms`.\n * @cssproperty --hide-duration - Close transition duration. Default `200ms`.\n * @cssproperty --backdrop - Backdrop color. Default `var(--l-backdrop-strong)` — darker than `--l-backdrop` to focus attention on the immersive frame, but still translucent so the page stays perceptible.\n *\n * @event show - Fired when the viewer is about to open. Cancelable — call `event.preventDefault()` to keep it closed.\n * @event after-show - Fired after the open transition completes. Not cancelable.\n * @event hide - Fired when the viewer is about to close. Cancelable.\n * @event after-hide - Fired after the close transition completes. Not cancelable.\n * @event story-change - Fired when the active story changes. Properties: `index: number`, `story: LuxenStory`.\n * @event story-end - Fired when the active story finishes playback. Properties: `index: number`.\n * @event chapter-change - Fired when the active chapter (within a story) changes. Properties: `chapter: number`, `story: LuxenStory`.\n * @event mute-change - Fired when the mute state changes. Properties: `muted: boolean`.\n *\n * @customElement l-stories-viewer\n */\nexport class LuxenStoriesViewer extends LuxenElement {\n static override styles: CSSResultGroup = [hostStyles, styles];\n\n private _localize = new LocalizeController(this);\n\n /** Whether the viewer is open. */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /** Active story index (0-based). */\n @property({ type: Number, reflect: true })\n index = 0;\n\n /** Whether playback is muted. Defaults to `true` so autoplay always succeeds across browsers. The user can unmute via the dedicated button or the `m` keyboard shortcut. */\n @property({ type: Boolean, reflect: true })\n muted = true;\n\n /** Loop the active story instead of advancing. */\n @property({ type: Boolean, reflect: true })\n loop = false;\n\n /** Move to the next story when the current one ends; close after the last story. */\n @property({ type: Boolean, reflect: true, attribute: 'auto-advance' })\n autoAdvance = true;\n\n /** Close when the backdrop is clicked. */\n @property({ type: Boolean, reflect: true, attribute: 'light-dismiss' })\n lightDismiss = true;\n\n /** Active chapter index within the current story. Reflected. */\n @property({ type: Number, reflect: true })\n chapter = 0;\n\n /** Internal: the playlist set by the source `<l-stories>`. */\n stories: LuxenStory[] = [];\n\n /** Internal: the source `<l-stories>` element that opened the viewer. */\n source: LuxenStories | null = null;\n\n @query('dialog') private _dialog!: HTMLDialogElement;\n @query('video') private _video!: HTMLVideoElement;\n\n /** Tracks the underlying `<video>` paused state so the play/pause button stays in sync. */\n @state() private _paused = false;\n\n /** True while the video is buffering / waiting for data. Drives the loading spinner. */\n @state() private _loading = false;\n\n private _raf = 0;\n private _holdTimer = 0;\n private _pointerStart: { x: number; y: number; t: number } | null = null;\n private _segments: HTMLElement[] = [];\n private _chapterStarts: number[] = [0];\n /** Tracks the URL we've already injected a prefetch hint for so we don't duplicate. */\n private _prefetchedSrc: string | null = null;\n\n // --- Lifecycle ---\n\n override connectedCallback() {\n super.connectedCallback();\n this.addEventListener('keydown', this._onKeyDown);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._onKeyDown);\n cancelAnimationFrame(this._raf);\n clearTimeout(this._holdTimer);\n this._clearPrefetchHints();\n }\n\n override firstUpdated() {\n this._dialog.addEventListener('cancel', (e) => this._onCancel(e));\n this._dialog.addEventListener('close', () => this._onNativeClose());\n this._dialog.addEventListener('click', (e) => this._onDialogClick(e));\n this._video.addEventListener('ended', () => this._onEnded());\n this._video.addEventListener('loadedmetadata', () => this._tick());\n this._video.addEventListener('play', () => (this._paused = false));\n this._video.addEventListener('pause', () => (this._paused = true));\n // Loading spinner state — shown during initial buffer and any subsequent stalls,\n // hidden as soon as the video is actually painting frames.\n this._video.addEventListener('loadstart', () => (this._loading = true));\n this._video.addEventListener('waiting', () => (this._loading = true));\n this._video.addEventListener('playing', () => (this._loading = false));\n this._video.addEventListener('canplay', () => (this._loading = false));\n this._video.addEventListener('error', () => (this._loading = false));\n // Prefetch the next story's video once the current one is past 50% so the\n // transition feels instantaneous. Idempotent via `_prefetchedSrc`.\n this._video.addEventListener('timeupdate', () => this._maybePrefetchNext());\n }\n\n override willUpdate(changed: PropertyValues<this>) {\n // Reset chapter state synchronously so it folds into the same update cycle —\n // avoids writing reactive props from updated() which would schedule a second update.\n const openBecoming = changed.has('open') && this.open;\n const indexChanged = changed.has('index');\n if (openBecoming || indexChanged) {\n const story = this.stories[this.index];\n if (story) {\n this._chapterStarts = story.getChapterStarts();\n this.chapter = 0;\n }\n }\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('open')) {\n if (this.open && !this._dialog.open) {\n if (!this.dispatchEvent(new ShowEvent())) {\n // Defer the revert so it lands outside the current update cycle and\n // doesn't schedule a new update from inside updated().\n queueMicrotask(() => {\n this.open = false;\n });\n return;\n }\n this.toggleAttribute('data-modal', true);\n this._dialog.showModal();\n queueMicrotask(() => this._loadCurrent());\n void this._emitAfter('after-show');\n } else if (!this.open && this._dialog.open) {\n if (!this.dispatchEvent(new HideEvent())) {\n // Defer the revert so it lands outside the current update cycle and\n // doesn't schedule a new update from inside updated().\n queueMicrotask(() => {\n this.open = true;\n });\n return;\n }\n this._dialog.close();\n }\n }\n\n // Story change → reload current story (refreshes chapters, segments, slots, video).\n if (changed.has('index') && this.open) {\n queueMicrotask(() => this._loadCurrent());\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new StoryChangeEvent(this.index, story));\n }\n\n // Chapter change within the current story → seek + refresh segment fills.\n if (changed.has('chapter') && this.open && !changed.has('index')) {\n this._applyChapterSeek();\n }\n\n if (changed.has('muted') && this._video) {\n this._video.muted = this.muted;\n }\n }\n\n // --- Dialog event handlers ---\n\n private _onCancel(e: Event) {\n if (!this.dispatchEvent(new HideEvent())) {\n e.preventDefault();\n }\n }\n\n private _onNativeClose() {\n cancelAnimationFrame(this._raf);\n this._video.pause();\n this._video.removeAttribute('src');\n this._video.load();\n this._clearPrefetchHints();\n // Mark all stories up to and including this.index as seen.\n for (let i = 0; i <= this.index && i < this.stories.length; i++) {\n this.stories[i].seen = true;\n }\n this._restoreFocus();\n this.open = false;\n this.removeAttribute('data-modal');\n void this._emitAfter('after-hide');\n }\n\n private _onDialogClick(e: MouseEvent) {\n if (!this.lightDismiss) return;\n // Backdrop click: target is the <dialog>, not a child.\n if (e.target === this._dialog) {\n this.open = false;\n }\n }\n\n // --- Keyboard ---\n\n private _onKeyDown = (e: KeyboardEvent) => {\n if (!this.open) return;\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.previous();\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.next();\n break;\n case ' ':\n e.preventDefault();\n if (this._video.paused) this.play();\n else this.pause();\n break;\n case 'm':\n case 'M':\n e.preventDefault();\n this.muted = !this.muted;\n this.dispatchEvent(new MuteChangeEvent(this.muted));\n break;\n }\n };\n\n // --- Pointer / touch (long-press to pause, tap-to-advance, swipe to close) ---\n\n private _onFramePointerDown = (e: PointerEvent) => {\n // Skip buttons, links, and explicitly-marked no-gesture areas.\n if ((e.target as HTMLElement).closest('button, a, [data-no-gesture]')) return;\n this._pointerStart = { x: e.clientX, y: e.clientY, t: performance.now() };\n this._holdTimer = window.setTimeout(() => this.pause(), 200);\n };\n\n private _onFramePointerUp = (e: PointerEvent) => {\n clearTimeout(this._holdTimer);\n const start = this._pointerStart;\n this._pointerStart = null;\n if (!start) return;\n\n const dx = e.clientX - start.x;\n const dy = e.clientY - start.y;\n const elapsed = performance.now() - start.t;\n const isTap = elapsed < 200 && Math.abs(dx) < 10 && Math.abs(dy) < 10;\n const wasLongPress = elapsed >= 200 && Math.abs(dx) < 10 && Math.abs(dy) < 10;\n const SWIPE = 50;\n\n if (wasLongPress) {\n if (this._video.paused) this.play();\n return;\n }\n\n if (isTap) {\n // Tap zones: chapter-aware nav. Left ~30% = previous chapter (or previous\n // story at the chapter boundary), anywhere else = next chapter (or next\n // story at the last chapter). Chevron buttons remain story-to-story.\n const tap = (e.target as HTMLElement).closest<HTMLElement>('[data-tap]');\n if (tap?.dataset.tap === 'previous') this.previous();\n else this.next();\n return;\n }\n\n if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > SWIPE) {\n if (dx < 0) this.nextStory();\n else this.previousStory();\n } else if (dy > SWIPE) {\n this.close();\n }\n };\n\n // --- Segments / progress (chapter-based) ---\n\n private _refreshSegmentRefs() {\n this._segments = Array.from(\n this.shadowRoot!.querySelectorAll<HTMLElement>('[part~=\"progress-fill\"]'),\n );\n }\n\n private _resetSegmentFills() {\n this._refreshSegmentRefs();\n for (let i = 0; i < this._segments.length; i++) {\n const p = i < this.chapter ? 1 : 0;\n this._segments[i].style.setProperty('--p', String(p));\n }\n }\n\n private _loadCurrent() {\n // Guard against a close racing the microtask: if the viewer is no longer\n // open, don't restart the video with stale state.\n if (!this.open || !this._dialog.open) return;\n const story = this.stories[this.index];\n if (!story) return;\n cancelAnimationFrame(this._raf);\n // Reset the prefetch tracker when the active story changes — the next\n // candidate has shifted and may need its own preload hint.\n this._prefetchedSrc = null;\n this._syncSlots(story);\n this._video.muted = this.muted;\n this._video.loop = this.loop;\n if (this._video.src !== story.src) {\n this._video.src = story.src;\n } else {\n this._video.currentTime = 0;\n }\n // Wait one frame for the new segment DOM to render before resetting fills.\n requestAnimationFrame(() => this._resetSegmentFills());\n this._video.play().catch(() => {\n // Autoplay-with-sound rejection: force muted, retry.\n this.muted = true;\n this._video.muted = true;\n this._video.play().catch(() => {});\n });\n }\n\n /**\n * Inject a `<link rel=\"preload\" as=\"video\">` for the next story once the\n * current video crosses 50% playback. Idempotent: tracked by `_prefetchedSrc`,\n * cleaned up on viewer close.\n */\n private _maybePrefetchNext() {\n const v = this._video;\n if (!v.duration || v.currentTime / v.duration < 0.5) return;\n const nextStory = this.stories[this.index + 1];\n if (!nextStory?.src) return;\n if (this._prefetchedSrc === nextStory.src) return;\n this._prefetchedSrc = nextStory.src;\n\n const link = document.createElement('link');\n link.rel = 'preload';\n link.as = 'video';\n link.href = nextStory.src;\n link.crossOrigin = 'anonymous';\n link.dataset.luxenStoriesPrefetch = '';\n document.head.appendChild(link);\n }\n\n /** Remove every prefetch hint we've injected. Called when the viewer closes. */\n private _clearPrefetchHints() {\n const links = document.head.querySelectorAll<HTMLLinkElement>(\n 'link[data-luxen-stories-prefetch]',\n );\n for (const link of links) link.remove();\n this._prefetchedSrc = null;\n }\n\n private _applyChapterSeek() {\n const target = this._chapterStarts[this.chapter] ?? 0;\n this._video.currentTime = target;\n this._resetSegmentFills();\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new ChapterChangeEvent(this.chapter, story));\n }\n\n private get _lastChapter() {\n return Math.max(0, this._chapterStarts.length - 1);\n }\n\n // Clone the active story's slot-marked children (cta, header) into the\n // viewer's own light DOM so the shadow root's `<slot name=\"cta\">` and\n // `<slot name=\"header\">` project them. Originals stay attached to the\n // story (hidden via CSS) and are reused on every `index` change.\n private _syncSlots(story: LuxenStory) {\n for (const node of Array.from(this.children)) {\n if ((node as HTMLElement).dataset.storySlot !== undefined) node.remove();\n }\n for (const child of Array.from(story.children)) {\n const slotName = (child as HTMLElement).getAttribute?.('slot');\n if (!slotName) continue;\n const clone = child.cloneNode(true) as HTMLElement;\n clone.dataset.storySlot = '';\n this.appendChild(clone);\n }\n }\n\n private _tick = () => {\n if (!this.open) return;\n const v = this._video;\n const seg = this._segments[this.chapter];\n if (v && seg) {\n const start = this._chapterStarts[this.chapter] ?? 0;\n const end = this._chapterStarts[this.chapter + 1] ?? v.duration;\n const span = Math.max(0, end - start);\n const p = span ? Math.max(0, Math.min((v.currentTime - start) / span, 1)) : 0;\n seg.style.setProperty('--p', String(p));\n // Auto-advance chapter when its segment is full and there's a next chapter in this story.\n // (The video element's `ended` event handles the last chapter via `_onEnded`.)\n if (p >= 1 && this.chapter < this._lastChapter) {\n this._advance(1);\n }\n }\n this._raf = requestAnimationFrame(this._tick);\n };\n\n private _onEnded() {\n // Last chapter of the current story finished.\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new StoryEndEvent(this.index));\n if (this.loop) {\n this._video.currentTime = this._chapterStarts[0] ?? 0;\n this.chapter = 0;\n this._video.play().catch(() => {});\n return;\n }\n if (!this.autoAdvance) return;\n if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n }\n\n /** chapter-then-story navigation. */\n private _advance(direction: 1 | -1) {\n if (direction === 1) {\n if (this.chapter < this._lastChapter) {\n this.chapter = this.chapter + 1;\n } else if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n return;\n }\n // direction === -1: Instagram pattern — restart the current chapter if past 1s in.\n const start = this._chapterStarts[this.chapter] ?? 0;\n if (this._video.currentTime > start + 1) {\n this._video.currentTime = start;\n this._resetSegmentFills();\n return;\n }\n if (this.chapter > 0) {\n this.chapter = this.chapter - 1;\n } else if (this.index > 0) {\n // Crossing into the previous story: load it, then jump to its last chapter.\n const previousStory = this.stories[this.index - 1];\n const previousLast = Math.max(0, previousStory.getChapterStarts().length - 1);\n this.index = this.index - 1;\n // After `index` updates, `_loadCurrent()` runs and resets `chapter` to 0.\n // Set `chapter` to the previous story's last chapter on the next microtask.\n queueMicrotask(() => {\n this.chapter = previousLast;\n });\n }\n }\n\n // --- Public API ---\n\n /** Open the viewer at the given index with an explicit playlist. */\n openAt(stories: LuxenStory[], index = 0, source: LuxenStories | null = null) {\n this.stories = stories;\n this.source = source;\n this.index = Math.max(0, Math.min(index, stories.length - 1));\n this.open = true;\n }\n\n close() {\n this.open = false;\n }\n\n /** Advance one chapter, or to the next story at the chapter boundary. */\n next() {\n this._advance(1);\n }\n\n /** Retreat one chapter, restart the current chapter past 1s in, or cross into the previous story. */\n previous() {\n this._advance(-1);\n }\n\n /** Jump to the next story, skipping any remaining chapters in the current story. */\n nextStory() {\n if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n }\n\n /** Jump to the previous story regardless of current chapter. Lands on chapter 0 of the previous story. */\n previousStory() {\n if (this.index > 0) {\n this.index = this.index - 1;\n }\n }\n\n play() {\n this._video?.play().catch(() => {});\n this._raf = requestAnimationFrame(this._tick);\n }\n\n pause() {\n this._video?.pause();\n cancelAnimationFrame(this._raf);\n }\n\n // --- Helpers ---\n\n private async _emitAfter(name: 'after-show' | 'after-hide') {\n await new Promise((r) => requestAnimationFrame(() => r(null)));\n const anims = this._dialog.getAnimations({ subtree: false });\n await Promise.all(anims.map((a) => a.finished.catch(() => {})));\n if ((name === 'after-show') !== this.open) return;\n this.dispatchEvent(name === 'after-show' ? new AfterShowEvent() : new AfterHideEvent());\n if (name === 'after-show') {\n this._raf = requestAnimationFrame(this._tick);\n }\n }\n\n private _restoreFocus() {\n const stories = this.source?.stories() ?? this.stories;\n const story = stories[this.index];\n const trigger = story?.querySelector<HTMLButtonElement>('button[data-story-trigger]');\n trigger?.focus({ preventScroll: false });\n }\n\n override render() {\n const total = this.stories.length;\n const currentStory = this.stories[this.index];\n // Chevrons navigate story-to-story, so visibility depends on `index` only (not chapter).\n const isFirstStory = this.index === 0;\n const isLastStory = this.index >= total - 1;\n // Render one segment per chapter of the CURRENT story. Use `_chapterStarts` if present\n // (set after `_loadCurrent`), or fall back to the current story's parsed chapters.\n const chapterStarts = this._chapterStarts.length\n ? this._chapterStarts\n : (currentStory?.getChapterStarts() ?? [0]);\n const iconTag = staticTag('icon');\n const spinnerTag = staticTag('spinner');\n\n return html`\n <dialog\n part=\"dialog\"\n aria-label=${this._localize.term('stories')}\n >\n <div\n part=\"frame\"\n class=\"frame\"\n @pointerdown=${this._onFramePointerDown}\n @pointerup=${this._onFramePointerUp}\n @pointercancel=${this._onFramePointerUp}\n >\n <div\n part=\"progress\"\n class=\"progress\"\n role=\"progressbar\"\n aria-label=${this._localize.term('storyProgress')}\n aria-valuenow=${this.chapter + 1}\n aria-valuemin=\"1\"\n aria-valuemax=${chapterStarts.length || 1}\n >\n ${map(\n chapterStarts,\n (_, i) => html`\n <span\n part=\"progress-segment\"\n class=\"progress-segment\"\n data-state=${i < this.chapter ? 'past' : i === this.chapter ? 'active' : 'future'}\n >\n <span\n part=\"progress-fill\"\n class=\"progress-fill\"\n ></span>\n </span>\n `,\n )}\n </div>\n\n <header\n part=\"header\"\n class=\"header\"\n data-no-gesture\n >\n <slot name=\"header\">\n ${\n currentStory?.poster\n ? html`<img\n class=\"header-thumb\"\n src=${currentStory.poster}\n alt=\"\"\n />`\n : nothing\n }\n ${\n currentStory?.label\n ? html`<div class=\"header-text\">\n <div\n part=\"header-label\"\n class=\"header-label\"\n >\n ${currentStory.label}\n </div>\n </div>`\n : nothing\n }\n </slot>\n </header>\n\n <div\n part=\"actions\"\n class=\"actions\"\n data-no-gesture\n >\n <slot name=\"close\">\n <button\n type=\"button\"\n part=\"button-close\"\n class=\"btn btn-close\"\n aria-label=${this._localize.term('close')}\n @click=${() => this.close()}\n >\n <${iconTag} name=\"mdi:close\"></${iconTag}>\n </button>\n </slot>\n <button\n type=\"button\"\n part=\"button-pause\"\n class=\"btn btn-pause\"\n aria-label=${this._paused ? 'Play' : 'Pause'}\n aria-pressed=${this._paused ? 'true' : 'false'}\n @click=${() => (this._paused ? this.play() : this.pause())}\n >\n <${iconTag} name=${this._paused ? 'mdi:play' : 'mdi:pause'}></${iconTag}>\n </button>\n <button\n type=\"button\"\n part=\"button-mute\"\n class=\"btn btn-mute\"\n aria-label=${this.muted ? this._localize.term('unmute') : this._localize.term('mute')}\n aria-pressed=${this.muted ? 'false' : 'true'}\n @click=${() => {\n this.muted = !this.muted;\n this.dispatchEvent(new MuteChangeEvent(this.muted));\n }}\n >\n <${iconTag} name=${this.muted ? 'mdi:volume-off' : 'mdi:volume-high'}></${iconTag}>\n </button>\n </div>\n\n <video\n part=\"video\"\n class=\"video\"\n playsinline\n preload=\"metadata\"\n ?muted=${this.muted}\n ></video>\n\n <${spinnerTag}\n part=\"spinner\"\n class=\"spinner\"\n aria-label=${this._localize.term('loading')}\n data-no-gesture\n data-state=${this._loading ? 'loading' : 'idle'}\n ></${spinnerTag}>\n\n <!-- Tap zones: invisible overlays that translate clicks into previous/next.\n Sit BELOW header / overlay / chevron buttons in z-order, above the video. -->\n <div\n class=\"tap-zone tap-previous\"\n data-tap=\"previous\"\n aria-hidden=\"true\"\n ></div>\n <div\n class=\"tap-zone tap-next\"\n data-tap=\"next\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n part=\"overlay\"\n class=\"overlay\"\n data-no-gesture\n >\n <slot name=\"cta\"></slot>\n </div>\n\n <div\n class=\"sr-only\"\n aria-live=\"polite\"\n >\n ${\n total\n ? `Story ${this.index + 1} of ${total}${\n this.stories[this.index]?.label ? ` — ${this.stories[this.index].label}` : ''\n }${chapterStarts.length > 1 ? ` — chapter ${this.chapter + 1} of ${chapterStarts.length}` : ''}`\n : ''\n }\n </div>\n </div>\n\n ${\n isFirstStory\n ? nothing\n : html`<button\n type=\"button\"\n part=\"button-previous\"\n class=\"btn btn-nav btn-previous\"\n aria-label=${this._localize.term('previousStory')}\n @click=${() => this.previousStory()}\n >\n <${iconTag} name=\"mdi:chevron-left\"></${iconTag}>\n </button>`\n }\n ${\n isLastStory\n ? nothing\n : html`<button\n type=\"button\"\n part=\"button-next\"\n class=\"btn btn-nav btn-next\"\n aria-label=${this._localize.term('nextStory')}\n @click=${() => this.nextStory()}\n >\n <${iconTag} name=\"mdi:chevron-right\"></${iconTag}>\n </button>`\n }\n </dialog>\n `;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stories-viewer.js","names":["unsafeCSS","rawStyles","hostStyles","visuallyHiddenStyles","map","nothing","_decorate","property","query","state","r","u","__decorate","e","A","o","host_styles_default","visually_hidden_styles_default","styles","StoryChangeEvent","index","story","StoryEndEvent","ChapterChangeEvent","chapter","MuteChangeEvent","muted","SCROLL_LOCK_SHEET","sheet","tagName","LuxenStoriesViewer","LuxenElement","_args","LocalizeController","start","dx","dy","elapsed","v","seg","end","span","p","openBecoming","changed","indexChanged","ShowEvent","HideEvent","i","nextStory","link","links","target","node","child","clone","direction","previousStory","previousLast","stories","source","anims","a","name","AfterShowEvent","AfterHideEvent","total","currentStory","isFirstStory","isLastStory","chapterStarts","iconTag","staticTag","spinnerTag","cls","n","r$1"],"sources":["../../../src/html/elements/stories-viewer/stories-viewer.css?inline","../../../src/html/elements/stories-viewer/stories-viewer.ts"],"sourcesContent":[":host {\n --width: min(420px, 100vw);\n --aspect-ratio: 9 / 16;\n --progress-color: white;\n --progress-bg: rgb(255 255 255 / 35%);\n --progress-gap: 4px;\n --show-duration: 200ms;\n --hide-duration: 200ms;\n /* Semi-transparent backdrop so the underlying page (e.g. a product page) stays visible behind the player, but with enough darkness to focus attention on the frame. Override with `--backdrop: …` or set to `oklch(0% 0 0 / 90%)` for an opaque blackout. */\n --backdrop: var(--l-backdrop-strong);\n\n display: contents;\n}\n\ndialog {\n position: fixed;\n inset: 0;\n width: 100vw;\n height: 100dvh;\n max-width: none;\n max-height: none;\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n color: white;\n overflow: clip;\n\n /* Center the frame at all times. `place-items` is on the base rule so it survives the close transition (only `display` and `opacity` are transitioned with `allow-discrete`; non-transitioned properties revert immediately when `[open]` is removed and would un-center the frame to top-left for one frame). */\n place-items: center;\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 }\n\n /* OPEN STATE */\n &[open] {\n display: grid;\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.frame {\n position: relative;\n width: var(--width);\n max-width: 100vw;\n max-height: 100dvh;\n aspect-ratio: var(--aspect-ratio);\n background: black;\n border-radius: 0.75rem;\n overflow: clip;\n isolation: isolate;\n user-select: none;\n -webkit-user-select: none;\n touch-action: pan-y;\n}\n\n@media (max-width: 480px) {\n .frame {\n width: 100vw;\n height: 100dvh;\n aspect-ratio: auto;\n border-radius: 0;\n }\n}\n\n.video {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n background: black;\n}\n\n/* Loading spinner — centered over the video. Delayed fade-in so a fast buffer never visibly flashes the spinner. */\n.spinner {\n position: absolute;\n inset: 0;\n margin: auto;\n z-index: 1;\n width: 2rem;\n height: 2rem;\n color: white;\n pointer-events: none;\n opacity: 0;\n transition: opacity 150ms ease;\n}\n\n.spinner[data-state='loading'] {\n opacity: 1;\n /* Only paint the spinner if loading actually exceeds 200ms. */\n transition-delay: 200ms;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .spinner {\n transition: none;\n }\n}\n\n.progress {\n position: absolute;\n top: 0.5rem;\n left: 0.5rem;\n right: 0.5rem;\n z-index: 2;\n display: flex;\n gap: var(--progress-gap);\n pointer-events: none;\n}\n\n.progress-segment {\n flex: 1 1 0;\n height: 2.5px;\n border-radius: 999px;\n background: var(--progress-bg);\n overflow: clip;\n}\n\n.progress-fill {\n display: block;\n width: 100%;\n height: 100%;\n background: var(--progress-color);\n transform: scaleX(var(--p, 0));\n transform-origin: left;\n transition: transform 80ms linear;\n}\n\n.progress-segment[data-state='past'] .progress-fill {\n transform: scaleX(1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-fill {\n transition: none;\n }\n :host {\n --show-duration: 0ms;\n --hide-duration: 0ms;\n }\n}\n\n/* Top-left header — story thumbnail + label (slot fallback) or consumer-supplied content. */\n.header {\n position: absolute;\n top: 1.25rem;\n left: 0.75rem;\n right: 4rem; /* leave room for the right-side action stack */\n z-index: 2;\n display: flex;\n align-items: center;\n gap: 0.625rem;\n min-width: 0;\n pointer-events: none;\n}\n\n.header > ::slotted(*) {\n pointer-events: auto;\n}\n\n.header-thumb {\n width: 2rem;\n height: 2rem;\n border-radius: 50%;\n object-fit: cover;\n flex-shrink: 0;\n pointer-events: auto;\n}\n\n.header-text {\n display: flex;\n flex-direction: column;\n min-width: 0;\n pointer-events: auto;\n}\n\n.header-label {\n font-size: 0.875rem;\n font-weight: var(--l-font-weight-semibold);\n line-height: 1.2;\n color: white;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Top-right vertical action stack — close, play/pause, mute. */\n.actions {\n position: absolute;\n top: 1.25rem;\n right: 0.75rem;\n z-index: 2;\n display: flex;\n flex-direction: column;\n /* 12px gap leaves room for the 6px hit-area extension on each adjacent button (see `.btn::before` below). */\n gap: 0.75rem;\n pointer-events: none;\n}\n\n.actions > ::slotted(*),\n.actions .btn {\n pointer-events: auto;\n}\n\n.btn {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: rgb(0 0 0 / 25%);\n color: white;\n font-size: 1rem;\n cursor: pointer;\n transition: background 150ms ease;\n\n &:hover {\n background: rgb(0 0 0 / 45%);\n }\n\n &:focus-visible {\n outline: 2px solid var(--progress-color);\n outline-offset: 2px;\n }\n\n /* WCAG 2.1 SC 2.5.5 / RGAA 13.4 — 44×44 hit area without changing the visible 32×32 disc. The pseudo carries no paint, only hit testing. */\n &::before {\n content: '';\n position: absolute;\n inset: -6px;\n }\n}\n\n.btn-nav {\n position: absolute;\n z-index: 2;\n width: 2.25rem;\n height: 2.25rem;\n font-size: 1.25rem;\n\n /* Chevrons start at 36×36 — 4px each side reaches 44. */\n &::before {\n inset: -4px;\n }\n}\n\n.btn-previous,\n.btn-next {\n top: 50%;\n transform: translateY(-50%);\n}\n\n/* Desktop ≥ 768px: chevrons sit outside the frame, on the dim backdrop. */\n@media (min-width: 768px) {\n .btn-previous {\n left: calc(50% - var(--width) / 2 - 3rem);\n }\n .btn-next {\n right: calc(50% - var(--width) / 2 - 3rem);\n }\n}\n\n/* Mobile < 768px: hide chevrons entirely — tap zones drive nav. */\n@media (max-width: 767px) {\n .btn-previous,\n .btn-next {\n display: none;\n }\n}\n\n/*\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆃🅰🅿 zones (invisible click/tap targets — left = previous, rest = next)\n▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n*/\n\n.tap-zone {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 0;\n}\n\n.tap-previous {\n left: 0;\n width: 30%;\n}\n\n.tap-next {\n left: 30%;\n right: 0;\n}\n\n.overlay {\n position: absolute;\n inset: auto 0 0 0;\n z-index: 1;\n padding: 1rem;\n pointer-events: none;\n}\n\n.overlay > ::slotted(*) {\n pointer-events: auto;\n}\n","import { nothing, unsafeCSS, type CSSResultGroup, type PropertyValues } from 'lit';\nimport { html } from 'lit/static-html.js';\nimport { property, query, state } from 'lit/decorators.js';\nimport { map } from 'lit/directives/map.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { cls, tagName } from '../../registry.js';\nimport { staticTag } from '../../static-tag.js';\nimport { ShowEvent, AfterShowEvent, HideEvent, AfterHideEvent } from '../../events/index.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport visuallyHiddenStyles from '../../shared/styles/visually-hidden.styles.js';\nimport type { LuxenStories } from '../stories/stories.js';\nimport type { LuxenStory } from '../story/story.js';\nimport rawStyles from './stories-viewer.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/** Fired when the active story changes. */\nexport class StoryChangeEvent extends Event {\n readonly index: number;\n readonly story: LuxenStory;\n constructor(index: number, story: LuxenStory) {\n super('story-change', { bubbles: false, composed: false, cancelable: false });\n this.index = index;\n this.story = story;\n }\n}\n\n/** Fired when the active story finishes playback. */\nexport class StoryEndEvent extends Event {\n readonly index: number;\n constructor(index: number) {\n super('story-end', { bubbles: false, composed: false, cancelable: false });\n this.index = index;\n }\n}\n\n/** Fired when the active chapter (within a story) changes. */\nexport class ChapterChangeEvent extends Event {\n readonly chapter: number;\n readonly story: LuxenStory;\n constructor(chapter: number, story: LuxenStory) {\n super('chapter-change', { bubbles: false, composed: false, cancelable: false });\n this.chapter = chapter;\n this.story = story;\n }\n}\n\n/** Fired when the mute state changes. */\nexport class MuteChangeEvent extends Event {\n readonly muted: boolean;\n constructor(muted: boolean) {\n super('mute-change', { bubbles: false, composed: false, cancelable: false });\n this.muted = muted;\n }\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n 'story-change': StoryChangeEvent;\n 'story-end': StoryEndEvent;\n 'chapter-change': ChapterChangeEvent;\n 'mute-change': MuteChangeEvent;\n }\n}\n\nconst SCROLL_LOCK_SHEET = Symbol.for('luxen-stories-viewer-scroll-lock');\nif (typeof document !== 'undefined' && !(SCROLL_LOCK_SHEET in document)) {\n const sheet = new CSSStyleSheet();\n // Runs at import time, after the consumer has called `setPrefix()` (or the\n // vite-plugin baked it into registry.js) — same ordering `define()` relies on.\n sheet.replaceSync(\n `html:has(${tagName('stories-viewer')}[data-modal]) { overflow: hidden; scrollbar-gutter: stable; }`,\n );\n document.adoptedStyleSheets.push(sheet);\n Object.defineProperty(document, SCROLL_LOCK_SHEET, { value: sheet });\n}\n\n/**\n * @summary Fullscreen modal that plays a sequence of `<l-story>` videos with an\n * Instagram-style segmented progress bar, previous/next navigation, mute toggle,\n * and auto-advance.\n *\n * Linked to one or more `<l-stories>` rows via matching `id` ↔ `for`. Open\n * programmatically via `open()` or by clicking a thumbnail in a linked row.\n *\n * @slot cta - Default CTA overlay (e.g. shoppable card). Per-story `slot=\"cta\"` inside `<l-story>` overrides this when that story is active.\n * @slot header - Default header overlay (e.g. avatar + author). Per-story override available the same way.\n * @slot close - Override the default close button.\n *\n * @csspart dialog - The native `<dialog>` element.\n * @csspart frame - The aspect-ratio video frame.\n * @csspart progress - The progress bar wrapper.\n * @csspart progress-segment - A single progress segment.\n * @csspart progress-fill - The fill element inside an active segment.\n * @csspart video - The `<video>` element.\n * @csspart overlay - The overlay wrapper that hosts CTA/header slots.\n * @csspart header - The top-left header area (story thumbnail + label fallback, or consumer-supplied content via the `header` slot).\n * @csspart header-label - The default story label inside the header.\n * @csspart actions - The top-right vertical button stack (close, play/pause, mute).\n * @csspart button-close - The close button.\n * @csspart button-pause - The play/pause toggle.\n * @csspart button-mute - The mute toggle.\n * @csspart button-previous - The previous story button.\n * @csspart button-next - The next story button.\n * @csspart spinner - The loading spinner shown while the current video is buffering.\n *\n * @cssproperty --width - Frame width. Default `min(420px, 100vw)`.\n * @cssproperty --progress-color - Active progress fill color. Default `white`.\n * @cssproperty --progress-bg - Inactive progress segment background. Default `rgb(255 255 255 / 35%)`.\n * @cssproperty --progress-gap - Gap between segments. Default `4px`.\n * @cssproperty --show-duration - Open transition duration. Default `200ms`.\n * @cssproperty --hide-duration - Close transition duration. Default `200ms`.\n * @cssproperty --backdrop - Backdrop color. Default `var(--l-backdrop-strong)` — darker than `--l-backdrop` to focus attention on the immersive frame, but still translucent so the page stays perceptible.\n *\n * @event show - Fired when the viewer is about to open. Cancelable — call `event.preventDefault()` to keep it closed.\n * @event after-show - Fired after the open transition completes. Not cancelable.\n * @event hide - Fired when the viewer is about to close. Cancelable.\n * @event after-hide - Fired after the close transition completes. Not cancelable.\n * @event story-change - Fired when the active story changes. Properties: `index: number`, `story: LuxenStory`.\n * @event story-end - Fired when the active story finishes playback. Properties: `index: number`.\n * @event chapter-change - Fired when the active chapter (within a story) changes. Properties: `chapter: number`, `story: LuxenStory`.\n * @event mute-change - Fired when the mute state changes. Properties: `muted: boolean`.\n *\n * @customElement l-stories-viewer\n */\nexport class LuxenStoriesViewer extends LuxenElement {\n static override styles: CSSResultGroup = [hostStyles, visuallyHiddenStyles, styles];\n\n private _localize = new LocalizeController(this);\n\n /** Whether the viewer is open. */\n @property({ type: Boolean, reflect: true })\n open = false;\n\n /** Active story index (0-based). */\n @property({ type: Number, reflect: true })\n index = 0;\n\n /** Whether playback is muted. Defaults to `true` so autoplay always succeeds across browsers. The user can unmute via the dedicated button or the `m` keyboard shortcut. */\n @property({ type: Boolean, reflect: true })\n muted = true;\n\n /** Loop the active story instead of advancing. */\n @property({ type: Boolean, reflect: true })\n loop = false;\n\n /** Move to the next story when the current one ends; close after the last story. */\n @property({ type: Boolean, reflect: true, attribute: 'auto-advance' })\n autoAdvance = true;\n\n /** Close when the backdrop is clicked. */\n @property({ type: Boolean, reflect: true, attribute: 'light-dismiss' })\n lightDismiss = true;\n\n /** Active chapter index within the current story. Reflected. */\n @property({ type: Number, reflect: true })\n chapter = 0;\n\n /** Internal: the playlist set by the source `<l-stories>`. */\n stories: LuxenStory[] = [];\n\n /** Internal: the source `<l-stories>` element that opened the viewer. */\n source: LuxenStories | null = null;\n\n @query('dialog') private _dialog!: HTMLDialogElement;\n @query('video') private _video!: HTMLVideoElement;\n\n /** Tracks the underlying `<video>` paused state so the play/pause button stays in sync. */\n @state() private _paused = false;\n\n /** True while the video is buffering / waiting for data. Drives the loading spinner. */\n @state() private _loading = false;\n\n private _raf = 0;\n private _holdTimer = 0;\n private _pointerStart: { x: number; y: number; t: number } | null = null;\n private _segments: HTMLElement[] = [];\n private _chapterStarts: number[] = [0];\n /** Tracks the URL we've already injected a prefetch hint for so we don't duplicate. */\n private _prefetchedSrc: string | null = null;\n\n // --- Lifecycle ---\n\n override connectedCallback() {\n super.connectedCallback();\n this.addEventListener('keydown', this._onKeyDown);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener('keydown', this._onKeyDown);\n cancelAnimationFrame(this._raf);\n clearTimeout(this._holdTimer);\n this._clearPrefetchHints();\n }\n\n override firstUpdated() {\n this._dialog.addEventListener('cancel', (e) => this._onCancel(e));\n this._dialog.addEventListener('close', () => this._onNativeClose());\n this._dialog.addEventListener('click', (e) => this._onDialogClick(e));\n this._video.addEventListener('ended', () => this._onEnded());\n this._video.addEventListener('loadedmetadata', () => this._tick());\n this._video.addEventListener('play', () => (this._paused = false));\n this._video.addEventListener('pause', () => (this._paused = true));\n // Loading spinner state — shown during initial buffer and any subsequent stalls,\n // hidden as soon as the video is actually painting frames.\n this._video.addEventListener('loadstart', () => (this._loading = true));\n this._video.addEventListener('waiting', () => (this._loading = true));\n this._video.addEventListener('playing', () => (this._loading = false));\n this._video.addEventListener('canplay', () => (this._loading = false));\n this._video.addEventListener('error', () => (this._loading = false));\n // Prefetch the next story's video once the current one is past 50% so the\n // transition feels instantaneous. Idempotent via `_prefetchedSrc`.\n this._video.addEventListener('timeupdate', () => this._maybePrefetchNext());\n }\n\n override willUpdate(changed: PropertyValues<this>) {\n // Reset chapter state synchronously so it folds into the same update cycle —\n // avoids writing reactive props from updated() which would schedule a second update.\n const openBecoming = changed.has('open') && this.open;\n const indexChanged = changed.has('index');\n if (openBecoming || indexChanged) {\n const story = this.stories[this.index];\n if (story) {\n this._chapterStarts = story.getChapterStarts();\n this.chapter = 0;\n }\n }\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('open')) {\n if (this.open && !this._dialog.open) {\n if (!this.dispatchEvent(new ShowEvent())) {\n // Defer the revert so it lands outside the current update cycle and\n // doesn't schedule a new update from inside updated().\n queueMicrotask(() => {\n this.open = false;\n });\n return;\n }\n this.toggleAttribute('data-modal', true);\n this._dialog.showModal();\n queueMicrotask(() => this._loadCurrent());\n void this._emitAfter('after-show');\n } else if (!this.open && this._dialog.open) {\n if (!this.dispatchEvent(new HideEvent())) {\n // Defer the revert so it lands outside the current update cycle and\n // doesn't schedule a new update from inside updated().\n queueMicrotask(() => {\n this.open = true;\n });\n return;\n }\n this._dialog.close();\n }\n }\n\n // Story change → reload current story (refreshes chapters, segments, slots, video).\n if (changed.has('index') && this.open) {\n queueMicrotask(() => this._loadCurrent());\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new StoryChangeEvent(this.index, story));\n }\n\n // Chapter change within the current story → seek + refresh segment fills.\n if (changed.has('chapter') && this.open && !changed.has('index')) {\n this._applyChapterSeek();\n }\n\n if (changed.has('muted') && this._video) {\n this._video.muted = this.muted;\n }\n }\n\n // --- Dialog event handlers ---\n\n private _onCancel(e: Event) {\n if (!this.dispatchEvent(new HideEvent())) {\n e.preventDefault();\n }\n }\n\n private _onNativeClose() {\n cancelAnimationFrame(this._raf);\n this._video.pause();\n this._video.removeAttribute('src');\n this._video.load();\n this._clearPrefetchHints();\n // Mark all stories up to and including this.index as seen.\n for (let i = 0; i <= this.index && i < this.stories.length; i++) {\n this.stories[i].seen = true;\n }\n this._restoreFocus();\n this.open = false;\n this.removeAttribute('data-modal');\n void this._emitAfter('after-hide');\n }\n\n private _onDialogClick(e: MouseEvent) {\n if (!this.lightDismiss) return;\n // Backdrop click: target is the <dialog>, not a child.\n if (e.target === this._dialog) {\n this.open = false;\n }\n }\n\n // --- Keyboard ---\n\n private _onKeyDown = (e: KeyboardEvent) => {\n if (!this.open) return;\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.previous();\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.next();\n break;\n case ' ':\n e.preventDefault();\n if (this._video.paused) this.play();\n else this.pause();\n break;\n case 'm':\n case 'M':\n e.preventDefault();\n this.muted = !this.muted;\n this.dispatchEvent(new MuteChangeEvent(this.muted));\n break;\n }\n };\n\n // --- Pointer / touch (long-press to pause, tap-to-advance, swipe to close) ---\n\n private _onFramePointerDown = (e: PointerEvent) => {\n // Skip buttons, links, and explicitly-marked no-gesture areas.\n if ((e.target as HTMLElement).closest('button, a, [data-no-gesture]')) return;\n this._pointerStart = { x: e.clientX, y: e.clientY, t: performance.now() };\n this._holdTimer = window.setTimeout(() => this.pause(), 200);\n };\n\n private _onFramePointerUp = (e: PointerEvent) => {\n clearTimeout(this._holdTimer);\n const start = this._pointerStart;\n this._pointerStart = null;\n if (!start) return;\n\n const dx = e.clientX - start.x;\n const dy = e.clientY - start.y;\n const elapsed = performance.now() - start.t;\n const isTap = elapsed < 200 && Math.abs(dx) < 10 && Math.abs(dy) < 10;\n const wasLongPress = elapsed >= 200 && Math.abs(dx) < 10 && Math.abs(dy) < 10;\n const SWIPE = 50;\n\n if (wasLongPress) {\n if (this._video.paused) this.play();\n return;\n }\n\n if (isTap) {\n // Tap zones: chapter-aware nav. Left ~30% = previous chapter (or previous\n // story at the chapter boundary), anywhere else = next chapter (or next\n // story at the last chapter). Chevron buttons remain story-to-story.\n const tap = (e.target as HTMLElement).closest<HTMLElement>('[data-tap]');\n if (tap?.dataset.tap === 'previous') this.previous();\n else this.next();\n return;\n }\n\n if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > SWIPE) {\n if (dx < 0) this.nextStory();\n else this.previousStory();\n } else if (dy > SWIPE) {\n this.close();\n }\n };\n\n // --- Segments / progress (chapter-based) ---\n\n private _refreshSegmentRefs() {\n this._segments = Array.from(\n this.shadowRoot!.querySelectorAll<HTMLElement>('[part~=\"progress-fill\"]'),\n );\n }\n\n private _resetSegmentFills() {\n this._refreshSegmentRefs();\n for (let i = 0; i < this._segments.length; i++) {\n const p = i < this.chapter ? 1 : 0;\n this._segments[i].style.setProperty('--p', String(p));\n }\n }\n\n private _loadCurrent() {\n // Guard against a close racing the microtask: if the viewer is no longer\n // open, don't restart the video with stale state.\n if (!this.open || !this._dialog.open) return;\n const story = this.stories[this.index];\n if (!story) return;\n cancelAnimationFrame(this._raf);\n // Reset the prefetch tracker when the active story changes — the next\n // candidate has shifted and may need its own preload hint.\n this._prefetchedSrc = null;\n this._syncSlots(story);\n this._video.muted = this.muted;\n this._video.loop = this.loop;\n if (this._video.src !== story.src) {\n this._video.src = story.src;\n } else {\n this._video.currentTime = 0;\n }\n // Wait one frame for the new segment DOM to render before resetting fills.\n requestAnimationFrame(() => this._resetSegmentFills());\n this._video.play().catch(() => {\n // Autoplay-with-sound rejection: force muted, retry.\n this.muted = true;\n this._video.muted = true;\n this._video.play().catch(() => {});\n });\n }\n\n /**\n * Inject a `<link rel=\"preload\" as=\"video\">` for the next story once the\n * current video crosses 50% playback. Idempotent: tracked by `_prefetchedSrc`,\n * cleaned up on viewer close.\n */\n private _maybePrefetchNext() {\n const v = this._video;\n if (!v.duration || v.currentTime / v.duration < 0.5) return;\n const nextStory = this.stories[this.index + 1];\n if (!nextStory?.src) return;\n if (this._prefetchedSrc === nextStory.src) return;\n this._prefetchedSrc = nextStory.src;\n\n const link = document.createElement('link');\n link.rel = 'preload';\n link.as = 'video';\n link.href = nextStory.src;\n link.crossOrigin = 'anonymous';\n link.dataset.luxenStoriesPrefetch = '';\n document.head.appendChild(link);\n }\n\n /** Remove every prefetch hint we've injected. Called when the viewer closes. */\n private _clearPrefetchHints() {\n const links = document.head.querySelectorAll<HTMLLinkElement>(\n 'link[data-luxen-stories-prefetch]',\n );\n for (const link of links) link.remove();\n this._prefetchedSrc = null;\n }\n\n private _applyChapterSeek() {\n const target = this._chapterStarts[this.chapter] ?? 0;\n this._video.currentTime = target;\n this._resetSegmentFills();\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new ChapterChangeEvent(this.chapter, story));\n }\n\n private get _lastChapter() {\n return Math.max(0, this._chapterStarts.length - 1);\n }\n\n // Clone the active story's slot-marked children (cta, header) into the\n // viewer's own light DOM so the shadow root's `<slot name=\"cta\">` and\n // `<slot name=\"header\">` project them. Originals stay attached to the\n // story (hidden via CSS) and are reused on every `index` change.\n private _syncSlots(story: LuxenStory) {\n for (const node of Array.from(this.children)) {\n if ((node as HTMLElement).dataset.storySlot !== undefined) node.remove();\n }\n for (const child of Array.from(story.children)) {\n const slotName = (child as HTMLElement).getAttribute?.('slot');\n if (!slotName) continue;\n const clone = child.cloneNode(true) as HTMLElement;\n clone.dataset.storySlot = '';\n this.appendChild(clone);\n }\n }\n\n private _tick = () => {\n if (!this.open) return;\n const v = this._video;\n const seg = this._segments[this.chapter];\n if (v && seg) {\n const start = this._chapterStarts[this.chapter] ?? 0;\n const end = this._chapterStarts[this.chapter + 1] ?? v.duration;\n const span = Math.max(0, end - start);\n const p = span ? Math.max(0, Math.min((v.currentTime - start) / span, 1)) : 0;\n seg.style.setProperty('--p', String(p));\n // Auto-advance chapter when its segment is full and there's a next chapter in this story.\n // (The video element's `ended` event handles the last chapter via `_onEnded`.)\n if (p >= 1 && this.chapter < this._lastChapter) {\n this._advance(1);\n }\n }\n this._raf = requestAnimationFrame(this._tick);\n };\n\n private _onEnded() {\n // Last chapter of the current story finished.\n const story = this.stories[this.index];\n if (story) this.dispatchEvent(new StoryEndEvent(this.index));\n if (this.loop) {\n this._video.currentTime = this._chapterStarts[0] ?? 0;\n this.chapter = 0;\n this._video.play().catch(() => {});\n return;\n }\n if (!this.autoAdvance) return;\n if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n }\n\n /** chapter-then-story navigation. */\n private _advance(direction: 1 | -1) {\n if (direction === 1) {\n if (this.chapter < this._lastChapter) {\n this.chapter = this.chapter + 1;\n } else if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n return;\n }\n // direction === -1: Instagram pattern — restart the current chapter if past 1s in.\n const start = this._chapterStarts[this.chapter] ?? 0;\n if (this._video.currentTime > start + 1) {\n this._video.currentTime = start;\n this._resetSegmentFills();\n return;\n }\n if (this.chapter > 0) {\n this.chapter = this.chapter - 1;\n } else if (this.index > 0) {\n // Crossing into the previous story: load it, then jump to its last chapter.\n const previousStory = this.stories[this.index - 1];\n const previousLast = Math.max(0, previousStory.getChapterStarts().length - 1);\n this.index = this.index - 1;\n // After `index` updates, `_loadCurrent()` runs and resets `chapter` to 0.\n // Set `chapter` to the previous story's last chapter on the next microtask.\n queueMicrotask(() => {\n this.chapter = previousLast;\n });\n }\n }\n\n // --- Public API ---\n\n /** Open the viewer at the given index with an explicit playlist. */\n openAt(stories: LuxenStory[], index = 0, source: LuxenStories | null = null) {\n this.stories = stories;\n this.source = source;\n this.index = Math.max(0, Math.min(index, stories.length - 1));\n this.open = true;\n }\n\n close() {\n this.open = false;\n }\n\n /** Advance one chapter, or to the next story at the chapter boundary. */\n next() {\n this._advance(1);\n }\n\n /** Retreat one chapter, restart the current chapter past 1s in, or cross into the previous story. */\n previous() {\n this._advance(-1);\n }\n\n /** Jump to the next story, skipping any remaining chapters in the current story. */\n nextStory() {\n if (this.index < this.stories.length - 1) {\n this.index = this.index + 1;\n } else {\n this.close();\n }\n }\n\n /** Jump to the previous story regardless of current chapter. Lands on chapter 0 of the previous story. */\n previousStory() {\n if (this.index > 0) {\n this.index = this.index - 1;\n }\n }\n\n play() {\n this._video?.play().catch(() => {});\n this._raf = requestAnimationFrame(this._tick);\n }\n\n pause() {\n this._video?.pause();\n cancelAnimationFrame(this._raf);\n }\n\n // --- Helpers ---\n\n private async _emitAfter(name: 'after-show' | 'after-hide') {\n await new Promise((r) => requestAnimationFrame(() => r(null)));\n const anims = this._dialog.getAnimations({ subtree: false });\n await Promise.all(anims.map((a) => a.finished.catch(() => {})));\n if ((name === 'after-show') !== this.open) return;\n this.dispatchEvent(name === 'after-show' ? new AfterShowEvent() : new AfterHideEvent());\n if (name === 'after-show') {\n this._raf = requestAnimationFrame(this._tick);\n }\n }\n\n private _restoreFocus() {\n const stories = this.source?.stories() ?? this.stories;\n const story = stories[this.index];\n const trigger = story?.querySelector<HTMLButtonElement>('button[data-story-trigger]');\n trigger?.focus({ preventScroll: false });\n }\n\n override render() {\n const total = this.stories.length;\n const currentStory = this.stories[this.index];\n // Chevrons navigate story-to-story, so visibility depends on `index` only (not chapter).\n const isFirstStory = this.index === 0;\n const isLastStory = this.index >= total - 1;\n // Render one segment per chapter of the CURRENT story. Use `_chapterStarts` if present\n // (set after `_loadCurrent`), or fall back to the current story's parsed chapters.\n const chapterStarts = this._chapterStarts.length\n ? this._chapterStarts\n : (currentStory?.getChapterStarts() ?? [0]);\n const iconTag = staticTag('icon');\n const spinnerTag = staticTag('spinner');\n\n return html`\n <dialog\n part=\"dialog\"\n aria-label=${this._localize.term('stories')}\n >\n <div\n part=\"frame\"\n class=\"frame\"\n @pointerdown=${this._onFramePointerDown}\n @pointerup=${this._onFramePointerUp}\n @pointercancel=${this._onFramePointerUp}\n >\n <div\n part=\"progress\"\n class=\"progress\"\n role=\"progressbar\"\n aria-label=${this._localize.term('storyProgress')}\n aria-valuenow=${this.chapter + 1}\n aria-valuemin=\"1\"\n aria-valuemax=${chapterStarts.length || 1}\n >\n ${map(\n chapterStarts,\n (_, i) => html`\n <span\n part=\"progress-segment\"\n class=\"progress-segment\"\n data-state=${i < this.chapter ? 'past' : i === this.chapter ? 'active' : 'future'}\n >\n <span\n part=\"progress-fill\"\n class=\"progress-fill\"\n ></span>\n </span>\n `,\n )}\n </div>\n\n <header\n part=\"header\"\n class=\"header\"\n data-no-gesture\n >\n <slot name=\"header\">\n ${\n currentStory?.poster\n ? html`<img\n class=\"header-thumb\"\n src=${currentStory.poster}\n alt=\"\"\n />`\n : nothing\n }\n ${\n currentStory?.label\n ? html`<div class=\"header-text\">\n <div\n part=\"header-label\"\n class=\"header-label\"\n >\n ${currentStory.label}\n </div>\n </div>`\n : nothing\n }\n </slot>\n </header>\n\n <div\n part=\"actions\"\n class=\"actions\"\n data-no-gesture\n >\n <slot name=\"close\">\n <button\n type=\"button\"\n part=\"button-close\"\n class=\"btn btn-close\"\n aria-label=${this._localize.term('close')}\n @click=${() => this.close()}\n >\n <${iconTag} name=\"mdi:close\"></${iconTag}>\n </button>\n </slot>\n <button\n type=\"button\"\n part=\"button-pause\"\n class=\"btn btn-pause\"\n aria-label=${this._paused ? 'Play' : 'Pause'}\n aria-pressed=${this._paused ? 'true' : 'false'}\n @click=${() => (this._paused ? this.play() : this.pause())}\n >\n <${iconTag} name=${this._paused ? 'mdi:play' : 'mdi:pause'}></${iconTag}>\n </button>\n <button\n type=\"button\"\n part=\"button-mute\"\n class=\"btn btn-mute\"\n aria-label=${this.muted ? this._localize.term('unmute') : this._localize.term('mute')}\n aria-pressed=${this.muted ? 'false' : 'true'}\n @click=${() => {\n this.muted = !this.muted;\n this.dispatchEvent(new MuteChangeEvent(this.muted));\n }}\n >\n <${iconTag} name=${this.muted ? 'mdi:volume-off' : 'mdi:volume-high'}></${iconTag}>\n </button>\n </div>\n\n <video\n part=\"video\"\n class=\"video\"\n playsinline\n preload=\"metadata\"\n ?muted=${this.muted}\n ></video>\n\n <${spinnerTag}\n part=\"spinner\"\n class=\"spinner\"\n aria-label=${this._localize.term('loading')}\n data-no-gesture\n data-state=${this._loading ? 'loading' : 'idle'}\n ></${spinnerTag}>\n\n <!-- Tap zones: invisible overlays that translate clicks into previous/next.\n Sit BELOW header / overlay / chevron buttons in z-order, above the video. -->\n <div\n class=\"tap-zone tap-previous\"\n data-tap=\"previous\"\n aria-hidden=\"true\"\n ></div>\n <div\n class=\"tap-zone tap-next\"\n data-tap=\"next\"\n aria-hidden=\"true\"\n ></div>\n\n <div\n part=\"overlay\"\n class=\"overlay\"\n data-no-gesture\n >\n <slot name=\"cta\"></slot>\n </div>\n\n <div\n class=${cls('visually-hidden')}\n aria-live=\"polite\"\n >\n ${\n total\n ? `Story ${this.index + 1} of ${total}${\n this.stories[this.index]?.label ? ` — ${this.stories[this.index].label}` : ''\n }${chapterStarts.length > 1 ? ` — chapter ${this.chapter + 1} of ${chapterStarts.length}` : ''}`\n : ''\n }\n </div>\n </div>\n\n ${\n isFirstStory\n ? nothing\n : html`<button\n type=\"button\"\n part=\"button-previous\"\n class=\"btn btn-nav btn-previous\"\n aria-label=${this._localize.term('previousStory')}\n @click=${() => this.previousStory()}\n >\n <${iconTag} name=\"mdi:chevron-left\"></${iconTag}>\n </button>`\n }\n ${\n isLastStory\n ? nothing\n : html`<button\n type=\"button\"\n part=\"button-next\"\n class=\"btn btn-nav btn-next\"\n aria-label=${this._localize.term('nextStory')}\n @click=${() => this.nextStory()}\n >\n <${iconTag} name=\"mdi:chevron-right\"></${iconTag}>\n </button>`\n }\n </dialog>\n `;\n }\n}\n"],"mappings":"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"}