luxen-ui 0.24.0 → 0.26.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 (249) hide show
  1. package/README.md +1 -0
  2. package/bin/cli.mjs +16 -1
  3. package/cdn/custom-elements.json +295 -169
  4. package/cdn/elements/alert-dialog/alert-dialog.d.ts +3 -2
  5. package/cdn/elements/alert-dialog/alert-dialog.d.ts.map +1 -1
  6. package/cdn/elements/alert-dialog/alert-dialog.js.map +1 -1
  7. package/cdn/elements/avatar/avatar.js +1 -1
  8. package/cdn/elements/avatar/avatar.js.map +1 -1
  9. package/cdn/elements/carousel/carousel.js +1 -1
  10. package/cdn/elements/carousel/carousel.js.map +1 -1
  11. package/cdn/elements/combobox/combobox.d.ts.map +1 -1
  12. package/cdn/elements/combobox/combobox.js +2 -1
  13. package/cdn/elements/combobox/combobox.js.map +1 -1
  14. package/cdn/elements/dialog/dialog.d.ts +2 -1
  15. package/cdn/elements/dialog/dialog.d.ts.map +1 -1
  16. package/cdn/elements/dialog/dialog.js.map +1 -1
  17. package/cdn/elements/dialog/dialog.styles.js +1 -1
  18. package/cdn/elements/dialog/dialog.styles.js.map +1 -1
  19. package/cdn/elements/drawer/drawer.d.ts +7 -7
  20. package/cdn/elements/drawer/drawer.js +1 -1
  21. package/cdn/elements/drawer/drawer.js.map +1 -1
  22. package/cdn/elements/dropdown-item/dropdown-item.js +1 -1
  23. package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
  24. package/cdn/elements/input-otp/input-otp.d.ts +2 -2
  25. package/cdn/elements/input-otp/input-otp.js.map +1 -1
  26. package/cdn/elements/prose-editor/prose-editor.js +1 -1
  27. package/cdn/elements/prose-editor/prose-editor.js.map +1 -1
  28. package/cdn/elements/rating/rating.js +1 -1
  29. package/cdn/elements/rating/rating.js.map +1 -1
  30. package/cdn/elements/select/select.d.ts.map +1 -1
  31. package/cdn/elements/select/select.js +2 -1
  32. package/cdn/elements/select/select.js.map +1 -1
  33. package/cdn/elements/slider/slider.js +1 -1
  34. package/cdn/elements/slider/slider.js.map +1 -1
  35. package/cdn/elements/stories-viewer/stories-viewer.js +1 -1
  36. package/cdn/elements/stories-viewer/stories-viewer.js.map +1 -1
  37. package/cdn/elements/table/table.meta.d.ts +55 -0
  38. package/cdn/elements/table/table.meta.d.ts.map +1 -0
  39. package/cdn/elements/table/table.meta.js +0 -0
  40. package/cdn/elements/tag/tag.js +1 -1
  41. package/cdn/elements/tag/tag.js.map +1 -1
  42. package/cdn/elements/tree/tree.js +1 -1
  43. package/cdn/elements/tree/tree.js.map +1 -1
  44. package/cdn/elements/tree-item/tree-item.js +1 -1
  45. package/cdn/elements/tree-item/tree-item.js.map +1 -1
  46. package/cdn/shared/controllers/listbox-nav.d.ts +7 -0
  47. package/cdn/shared/controllers/listbox-nav.d.ts.map +1 -1
  48. package/cdn/shared/controllers/listbox-nav.js +1 -1
  49. package/cdn/shared/controllers/listbox-nav.js.map +1 -1
  50. package/cdn/shared/styles/button-core.styles.js +1 -1
  51. package/cdn/shared/styles/button-core.styles.js.map +1 -1
  52. package/cdn/shared/styles/checkbox-appearance.styles.js +1 -1
  53. package/cdn/shared/styles/checkbox-appearance.styles.js.map +1 -1
  54. package/cdn/shared/styles/picker-core.styles.js +1 -1
  55. package/cdn/shared/styles/picker-core.styles.js.map +1 -1
  56. package/cdn/standalone.css +392 -86
  57. package/cdn/standalone.js +38 -28
  58. package/cdn/standalone.js.map +1 -1
  59. package/cdn/styles/base.css +11 -0
  60. package/cdn/styles/elements/breadcrumb.css +2 -2
  61. package/cdn/styles/elements/button.css +2 -2
  62. package/cdn/styles/elements/checkbox.css +2 -2
  63. package/cdn/styles/elements/close-button/circle.css +2 -2
  64. package/cdn/styles/elements/close-button/ring.css +2 -2
  65. package/cdn/styles/elements/close-button/square.css +2 -2
  66. package/cdn/styles/elements/disclosure.css +2 -2
  67. package/cdn/styles/elements/input-otp.css +20 -4
  68. package/cdn/styles/elements/input-stepper/default.css +2 -2
  69. package/cdn/styles/elements/input-stepper/pill.css +2 -2
  70. package/cdn/styles/elements/input-stepper/rounded.css +2 -2
  71. package/cdn/styles/elements/input.css +50 -17
  72. package/cdn/styles/elements/prose-editor.css +26 -11
  73. package/cdn/styles/elements/radio-group.css +2 -2
  74. package/cdn/styles/elements/radio.css +2 -2
  75. package/cdn/styles/elements/segmented-control.css +2 -2
  76. package/cdn/styles/elements/select.css +26 -5
  77. package/cdn/styles/elements/story.css +2 -2
  78. package/cdn/styles/elements/switch.css +2 -2
  79. package/cdn/styles/elements/table.css +186 -0
  80. package/cdn/styles/elements/tabs/enclosed.css +4 -4
  81. package/cdn/styles/elements/tabs/line.css +4 -4
  82. package/cdn/styles/elements/textarea.css +23 -9
  83. package/cdn/styles/preset.css +21 -2
  84. package/cdn/styles/tokens/aliases.css +6 -2
  85. package/cdn/styles/tokens/forms.css +4 -0
  86. package/cdn/styles/tokens.css +10 -2
  87. package/dist/css/base.css +11 -0
  88. package/dist/css/elements/breadcrumb.css +2 -2
  89. package/dist/css/elements/button.css +2 -2
  90. package/dist/css/elements/checkbox.css +2 -2
  91. package/dist/css/elements/close-button/circle.css +2 -2
  92. package/dist/css/elements/close-button/ring.css +2 -2
  93. package/dist/css/elements/close-button/square.css +2 -2
  94. package/dist/css/elements/disclosure.css +2 -2
  95. package/dist/css/elements/input-otp.css +20 -4
  96. package/dist/css/elements/input-stepper/default.css +2 -2
  97. package/dist/css/elements/input-stepper/pill.css +2 -2
  98. package/dist/css/elements/input-stepper/rounded.css +2 -2
  99. package/dist/css/elements/input.css +50 -17
  100. package/dist/css/elements/prose-editor.css +26 -11
  101. package/dist/css/elements/radio-group.css +2 -2
  102. package/dist/css/elements/radio.css +2 -2
  103. package/dist/css/elements/segmented-control.css +2 -2
  104. package/dist/css/elements/select.css +26 -5
  105. package/dist/css/elements/story.css +2 -2
  106. package/dist/css/elements/switch.css +2 -2
  107. package/dist/css/elements/table.css +186 -0
  108. package/dist/css/elements/tabs/enclosed.css +4 -4
  109. package/dist/css/elements/tabs/line.css +4 -4
  110. package/dist/css/elements/textarea.css +23 -9
  111. package/dist/css/preset.css +21 -2
  112. package/dist/css/tokens/aliases.css +6 -2
  113. package/dist/css/tokens/forms.css +4 -0
  114. package/dist/css/tokens.css +10 -2
  115. package/dist/custom-elements.json +295 -169
  116. package/dist/elements/alert-dialog/alert-dialog.d.ts +3 -2
  117. package/dist/elements/alert-dialog/alert-dialog.d.ts.map +1 -1
  118. package/dist/elements/alert-dialog/alert-dialog.js +3 -2
  119. package/dist/elements/avatar/avatar.css +2 -2
  120. package/dist/elements/carousel/carousel.css +18 -0
  121. package/dist/elements/combobox/combobox.css +9 -0
  122. package/dist/elements/combobox/combobox.d.ts.map +1 -1
  123. package/dist/elements/combobox/combobox.js +1 -0
  124. package/dist/elements/dialog/dialog.css +12 -0
  125. package/dist/elements/dialog/dialog.d.ts +2 -1
  126. package/dist/elements/dialog/dialog.d.ts.map +1 -1
  127. package/dist/elements/dialog/dialog.js +2 -1
  128. package/dist/elements/drawer/drawer.css +11 -1
  129. package/dist/elements/drawer/drawer.d.ts +7 -7
  130. package/dist/elements/drawer/drawer.js +7 -7
  131. package/dist/elements/dropdown-item/dropdown-item.css +5 -4
  132. package/dist/elements/input-otp/input-otp.d.ts +2 -2
  133. package/dist/elements/input-otp/input-otp.js +2 -2
  134. package/dist/elements/prose-editor/prose-editor.css +5 -3
  135. package/dist/elements/rating/rating.css +9 -10
  136. package/dist/elements/select/select.css +10 -9
  137. package/dist/elements/select/select.d.ts.map +1 -1
  138. package/dist/elements/select/select.js +1 -0
  139. package/dist/elements/slider/slider.css +5 -1
  140. package/dist/elements/stories-viewer/stories-viewer.css +10 -2
  141. package/dist/elements/table/table.meta.d.ts +55 -0
  142. package/dist/elements/table/table.meta.d.ts.map +1 -0
  143. package/dist/elements/table/table.meta.js +65 -0
  144. package/dist/elements/tag/tag.css +4 -4
  145. package/dist/elements/tree/tree.css +3 -3
  146. package/dist/elements/tree-item/tree-item.css +5 -3
  147. package/dist/metadata/alert-dialog.json +90 -85
  148. package/dist/metadata/alert.json +22 -22
  149. package/dist/metadata/avatar.json +26 -26
  150. package/dist/metadata/badge.json +13 -13
  151. package/dist/metadata/breadcrumb.json +9 -9
  152. package/dist/metadata/button.json +40 -40
  153. package/dist/metadata/carousel.json +133 -133
  154. package/dist/metadata/checkbox.json +13 -13
  155. package/dist/metadata/close-button.json +15 -15
  156. package/dist/metadata/color-scheme-icon.json +13 -13
  157. package/dist/metadata/combobox.json +77 -94
  158. package/dist/metadata/dialog.json +49 -44
  159. package/dist/metadata/disclosure.json +17 -17
  160. package/dist/metadata/divider.json +12 -12
  161. package/dist/metadata/drawer.json +61 -61
  162. package/dist/metadata/dropdown-item.json +50 -50
  163. package/dist/metadata/dropdown.json +32 -32
  164. package/dist/metadata/form-field.json +15 -15
  165. package/dist/metadata/icon.json +8 -8
  166. package/dist/metadata/index.json +1761 -1765
  167. package/dist/metadata/input-group.json +5 -5
  168. package/dist/metadata/input-otp.json +17 -17
  169. package/dist/metadata/input-stepper.json +24 -24
  170. package/dist/metadata/input.json +31 -31
  171. package/dist/metadata/popover.json +49 -49
  172. package/dist/metadata/progress.json +15 -15
  173. package/dist/metadata/prose-editor.json +110 -116
  174. package/dist/metadata/radio.json +15 -15
  175. package/dist/metadata/rating.json +25 -47
  176. package/dist/metadata/segmented-control.json +24 -46
  177. package/dist/metadata/select.json +81 -98
  178. package/dist/metadata/skeleton.json +12 -12
  179. package/dist/metadata/slider.json +60 -78
  180. package/dist/metadata/spinner.json +5 -5
  181. package/dist/metadata/sticky-bar.json +13 -13
  182. package/dist/metadata/stories-viewer.json +105 -105
  183. package/dist/metadata/stories.json +17 -17
  184. package/dist/metadata/story.json +41 -41
  185. package/dist/metadata/switch.json +9 -9
  186. package/dist/metadata/table.json +88 -0
  187. package/dist/metadata/tabs.json +26 -26
  188. package/dist/metadata/tag.json +50 -50
  189. package/dist/metadata/textarea.json +28 -28
  190. package/dist/metadata/toast.json +16 -16
  191. package/dist/metadata/tooltip.json +49 -49
  192. package/dist/metadata/tree-item.json +73 -73
  193. package/dist/metadata/tree.json +39 -39
  194. package/dist/shared/controllers/listbox-nav.d.ts +7 -0
  195. package/dist/shared/controllers/listbox-nav.d.ts.map +1 -1
  196. package/dist/shared/controllers/listbox-nav.js +12 -0
  197. package/dist/shared/styles/button-core.css +2 -2
  198. package/dist/shared/styles/checkbox-appearance.css +2 -2
  199. package/dist/shared/styles/picker-core.css +31 -7
  200. package/dist/templates/elements/alert-dialog.md +19 -18
  201. package/dist/templates/elements/alert.md +4 -4
  202. package/dist/templates/elements/avatar.md +9 -9
  203. package/dist/templates/elements/badge.md +4 -4
  204. package/dist/templates/elements/breadcrumb.md +4 -4
  205. package/dist/templates/elements/button.md +8 -8
  206. package/dist/templates/elements/carousel.md +32 -32
  207. package/dist/templates/elements/checkbox.md +4 -4
  208. package/dist/templates/elements/close-button.md +2 -2
  209. package/dist/templates/elements/color-scheme-icon.md +13 -13
  210. package/dist/templates/elements/combobox.md +19 -19
  211. package/dist/templates/elements/dialog.md +11 -10
  212. package/dist/templates/elements/disclosure.md +3 -3
  213. package/dist/templates/elements/divider.md +2 -2
  214. package/dist/templates/elements/drawer.md +15 -15
  215. package/dist/templates/elements/dropdown.md +15 -15
  216. package/dist/templates/elements/form-field.md +3 -3
  217. package/dist/templates/elements/icon.md +1 -1
  218. package/dist/templates/elements/input-otp.md +5 -5
  219. package/dist/templates/elements/input-stepper.md +4 -4
  220. package/dist/templates/elements/input.md +15 -8
  221. package/dist/templates/elements/popover.md +8 -8
  222. package/dist/templates/elements/progress.md +3 -3
  223. package/dist/templates/elements/prose-editor.md +38 -39
  224. package/dist/templates/elements/radio.md +6 -6
  225. package/dist/templates/elements/rating.md +5 -13
  226. package/dist/templates/elements/segmented-control.md +4 -4
  227. package/dist/templates/elements/select.md +23 -23
  228. package/dist/templates/elements/skeleton.md +2 -2
  229. package/dist/templates/elements/slider.md +13 -13
  230. package/dist/templates/elements/spinner.md +1 -1
  231. package/dist/templates/elements/sticky-bar.md +2 -2
  232. package/dist/templates/elements/stories-viewer.md +29 -29
  233. package/dist/templates/elements/stories.md +4 -4
  234. package/dist/templates/elements/story.md +11 -11
  235. package/dist/templates/elements/switch.md +5 -5
  236. package/dist/templates/elements/table.md +210 -0
  237. package/dist/templates/elements/tabs.md +4 -4
  238. package/dist/templates/elements/tag.md +14 -14
  239. package/dist/templates/elements/textarea.md +8 -8
  240. package/dist/templates/elements/toast.md +4 -4
  241. package/dist/templates/elements/tooltip.md +9 -9
  242. package/dist/templates/elements/tree-item.md +18 -18
  243. package/dist/templates/elements/tree.md +13 -13
  244. package/dist/templates/foundations/focus.md +303 -0
  245. package/elements.json +9 -0
  246. package/package.json +1 -1
  247. package/postcss-plugin-prefix.js +2 -2
  248. package/templates/SKILL.md.tpl +1 -0
  249. package/templates/choosing-elements.md.tpl +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"tag.js","names":["unsafeCSS","rawStyles","hostStyles","checkboxAppearance","nothing","_decorate","property","r","n","__decorate","b","A","host_styles_default","checkbox_appearance_styles_default","styles","TagRemoveEvent","TagChangeEvent","selected","Tag","LuxenElement","#_size_accessor_storage","value","#_removable_accessor_storage","#_selectable_accessor_storage","#_selected_accessor_storage","#_control_accessor_storage","#_disabled_accessor_storage","LocalizeController","event","box","cls"],"sources":["../../../src/html/elements/tag/tag.css?inline","../../../src/html/elements/tag/tag.ts"],"sourcesContent":["@layer components {\n :host {\n /* Size defaults (md). A removable tag is always ≥24px tall (target-size).\n The three public knobs are folded into the private vars here rather than\n at each use site, so an override also carries to what derives from them —\n the toggle's negative inline margins and the × button's spacing math. The\n trio mirrors `.l-button` (`--height` / `--padding-inline` / `--font-size`)\n so a chip can be tuned to a dense filter panel without ::part(base). */\n --_height: var(--height, 1.5rem);\n --_font-size: var(--font-size, var(--l-text-xs));\n --_padding-inline: var(--padding-inline, 0.5rem);\n --_gap: 0.25rem;\n\n /* Skin, resolved once so `[selected]` can swap all three at the source\n instead of re-declaring the properties that consume them. */\n --_background: var(--background-color, var(--l-color-bg-fill-neutral-subtle));\n --_color: var(--text-color, var(--l-color-text-neutral));\n --_border-strength: 14%;\n /* By default the line is a tint of the ink, so a chip stays coherent under\n any `--text-color` with one declaration. `--border-color` replaces that\n derivation outright rather than shifting it: a consumer with a token\n system wants *its* line token, not a 14% mix of its text color — and\n wants it to hold in every state, which is why it is the fallback in both\n of the pair below rather than a value the hover rule mixes further.\n `--_border-strength` stays private. Selecting a chip swaps the pair\n wholesale, the same way it swaps the fill and the ink. */\n --_border-color: var(\n --border-color,\n color-mix(in oklab, currentColor var(--_border-strength), transparent)\n );\n --_border-color-hover: var(\n --border-color,\n color-mix(in oklab, currentColor calc(var(--_border-strength) + 12%), transparent)\n );\n /* Resolved here rather than at `.base`, because the × segment has to round\n its outer end to the same curve. */\n --_border-radius: var(--border-radius, var(--l-radius-full));\n --_remove-glyph: 0.75rem;\n\n display: inline-flex;\n vertical-align: middle;\n max-inline-size: 100%;\n }\n\n :host([size='sm']) {\n --_padding-inline: var(--padding-inline, 0.375rem);\n --_gap: 0.125rem;\n }\n\n :host([size='lg']) {\n --_height: var(--height, 1.75rem);\n --_font-size: var(--font-size, var(--l-text-sm));\n --_padding-inline: var(--padding-inline, 0.625rem);\n --_remove-glyph: 0.875rem;\n }\n\n /* Disabled is conveyed by the greyed × button + cursor, NOT by washing out\n * the label — dimming the whole chip with opacity composites the text below\n * the WCAG AA contrast floor (axe flags it; a disabled <button> is exempt but\n * the label span is not). */\n :host([disabled]) .base {\n cursor: not-allowed;\n }\n\n /*\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅲🅷🅸🅿\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n */\n\n .base {\n display: inline-flex;\n align-items: center;\n gap: var(--_gap);\n min-block-size: var(--_height);\n padding-inline: var(--_padding-inline);\n border: 1px solid var(--_border-color);\n border-radius: var(--_border-radius);\n background-color: var(--_background);\n color: var(--_color);\n font-family: var(--l-font-neo-grotesque);\n font-size: var(--_font-size);\n font-weight: var(--l-font-weight-medium);\n line-height: 1;\n min-inline-size: 0;\n transition:\n background-color 120ms ease,\n border-color 120ms ease;\n }\n\n /* The remove segment is exactly as tall as the chip, so the chip is where the\n target floor has to live: `--height` still tunes a removable chip, but not\n under the 24px minimum (WCAG 2.5.8). Without this the promise held only for\n the default value. */\n :host([removable]) .base {\n min-block-size: max(1.5rem, var(--_height));\n }\n\n /* Whole-chip hover — only on a chip that is *wholly* a control. A display chip\n has no target at all, and a removable one has exactly its × segment: tinting\n the label under the pointer there promises a click that does nothing, and on\n a filter bar it fights the segment for the eye. `[selectable]` also covers\n `control=\"checkbox\"`, which derives the attribute. */\n @media (hover: hover) {\n :host([selectable]:hover:not([disabled])) .base {\n background-color: color-mix(in oklab, var(--_background), currentColor 7%);\n border-color: var(--_border-color-hover);\n }\n }\n\n .content {\n display: inline-flex;\n align-items: center;\n min-inline-size: 0;\n /* `.base` sets `line-height: 1` so the chip's height comes from its own size\n tokens rather than the document's leading. That makes the line box exactly\n one em tall, and a plain `overflow: hidden` then shears every descender\n (g, j, p, q, y) at all three sizes — measured 1px short.\n\n Fixed by clipping the inline axis only — the one the ellipsis needs — so\n nothing is ever cut vertically and the box keeps its exact geometry.\n Growing the line box instead (`line-height: normal`) moves the text half a\n pixel against the × and the prefix icon, since the font's ascent and\n descent are not symmetrical; widening the clip with\n `overflow-clip-margin` leaves Safari shearing the text (WebKit has no\n such property). `clip` — unlike `hidden` — lets the block axis stay\n `visible`. */\n overflow-x: clip;\n overflow-y: visible;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n ::slotted([slot='prefix']) {\n display: inline-flex;\n flex: none;\n }\n\n /* A count or trailing glyph. It sits in its own slot so `.base`'s gap gives it\n a gutter by construction — the label is a bare text node and can never be\n targeted from the outside. Lighter weight than the medium label, and\n tabular so a column of counts stays aligned. */\n ::slotted([slot='suffix']) {\n flex: none;\n font-weight: var(--l-font-weight-normal);\n font-variant-numeric: tabular-nums;\n }\n\n /*\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆂🅴🅻🅴🅲🆃🅰🅱🅻🅴 (filter chip — toggle button, or a checkbox riding inside)\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n */\n\n :host([selectable]) {\n /* Taller than a display chip at the same `size`: a chip you click is a\n target (WCAG 2.5.8), and this is the dense step between `sm` and `lg` —\n more room, same 12px label, where `lg` would jump the label to body size.\n `--height` / `--font-size` tune it from there. */\n --_height: var(--height, 1.75rem);\n\n /* The selection accent is the same one the library's checkbox, radio and\n switch use, so a chip with a checkbox inside reads as a single control. */\n --_accent: var(--l-form-control-activated-color);\n /* The label is 12px, so it owes 4.5:1 over its own tint — and the raw accent\n misses it on the light one (4.35:1 measured). Darken it there, lighten it\n on the dark tint, and both modes clear 6:1. `color-mix()` resolves\n reliably inside `light-dark()` — `oklch(from …)` does not. */\n --_selected-color: var(\n --selected-text-color,\n light-dark(\n color-mix(in oklab, var(--_accent), black 14%),\n color-mix(in oklab, var(--_accent), white 45%)\n )\n );\n /* Derived from the text color, so overriding `--selected-text-color` alone\n repaints the whole chip coherently. Kept at 10%: a consumer accent skips\n the mode correction above, so the tint has to stay pale enough for a\n semantic token (`--l-color-text-success` and friends) to clear 4.5:1 on\n it unaided. The border carries the rest of the emphasis. */\n --_selected-background: var(\n --selected-background-color,\n color-mix(in oklab, var(--_selected-color) 10%, var(--l-color-surface))\n );\n /* The third member of the trio, beside `--selected-text-color` and\n `--selected-background-color`. Selection already replaces the fill and the ink\n outright, so the line has to be replaceable the same way — without this,\n a chip that sets `--border-color` keeps that exact line when picked and\n the emphasis step that says \"selected\" never happens. 45% by default, the\n strength the derived line has always used here. */\n --_selected-border-color: var(\n --selected-border-color,\n color-mix(in oklab, var(--_selected-color) 45%, transparent)\n );\n }\n\n :host([selectable][size='lg']) {\n --_height: var(--height, 2rem);\n }\n\n :host([selectable][selected]) {\n --_background: var(--_selected-background);\n --_color: var(--_selected-color);\n --_border-color: var(--_selected-border-color);\n /* Same +12 step the resting line takes on hover, and pinned in the same way\n when the consumer supplies the value. */\n --_border-color-hover: var(\n --selected-border-color,\n color-mix(in oklab, var(--_selected-color) 57%, transparent)\n );\n }\n\n :host([selectable]) .base {\n cursor: pointer;\n /* A chip is tapped, not read: never leave a text selection behind a\n double-click on a filter. */\n user-select: none;\n }\n\n :host([selectable][disabled]) .base {\n cursor: not-allowed;\n }\n\n /* The chip *is* the button. Negative inline margins let it cover `.base`'s\n padding so the whole pill is the target, while `.base` keeps owning the\n border, background, and the × button's spacing math. */\n .toggle {\n flex: 1 1 auto;\n display: inline-flex;\n align-items: center;\n align-self: stretch;\n gap: var(--_gap);\n min-inline-size: 0;\n margin-inline: calc(var(--_padding-inline) * -1);\n padding-inline: var(--_padding-inline);\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n text-align: start;\n cursor: inherit;\n }\n\n /* Stop before the × instead of running under it. */\n :host([removable]) .toggle {\n margin-inline-end: 0;\n padding-inline-end: 0;\n }\n\n /* One ring for the whole chip: the inner control is a detail of how the chip\n is operated, not a second thing to focus visually — and the ring drawn\n below fully encloses the box, so the indicator only grows (WCAG 2.4.7 /\n 2.4.13).\n\n Qualified by `:host(…)` on purpose. The shared appearance nests\n `&:focus-visible` under a selector list, so it lands as\n `:is(.l-checkbox, … input[type='checkbox']:not(…)):focus-visible` — 0,3,1,\n which outranks a bare `.checkbox:focus-visible` (0,2,0) and paints a second\n ring around the box. `:host([selectable])` brings this to 0,4,0. */\n :host([selectable]) .toggle:focus-visible,\n :host([selectable]) .checkbox:focus-visible {\n outline: none;\n }\n\n .base:has(:is(.toggle, .checkbox):focus-visible) {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 2px;\n }\n\n /* The skin comes from the shared `.l-checkbox` appearance, applied through\n `cls('checkbox')` rather than a static class literal: `dist` ships that CSS\n as a real file, so a consumer's PostCSS rewrites its selector under a\n renamed `cssPrefix` — a baked `l-checkbox` in the template would stop\n matching it. This rule only carries what is specific to a chip.\n\n The shared appearance sizes the box in `em`, and a UA gives `<input>` its\n own font — inheriting the chip's makes the box track the `size` step. The\n accent follows the selected color, so a themed chip keeps its checkbox in\n the same hue. */\n .checkbox {\n --accent: var(--selected-text-color, var(--l-form-control-activated-color));\n /* Hover halo — same ratio and same token flip as the light-DOM\n `.l-checkbox`, which keeps this rule out of the shared partial (see\n `css/elements/checkbox.css`). Repeated here rather than moved into the\n partial: `l-tree-item` shares that file and never opted into a glow. */\n --_halo: var(--l-form-control-border-color-hover);\n\n flex: none;\n font: inherit;\n transition-property: background-color, border-color, box-shadow;\n }\n\n .checkbox:checked,\n .checkbox:indeterminate {\n --_halo: var(--accent);\n }\n\n /* Driven by the chip, not by the box: the chip is the label and the whole\n click target, so a halo lighting up only over 15px of a 28px pill would\n read as a glitch. */\n @media (hover: hover) {\n :host([selectable]:not([disabled])) .base:hover .checkbox:not(:disabled) {\n box-shadow: 0 0 0 calc(var(--size) * var(--l-form-control-toggle-halo-ratio))\n color-mix(in oklab, var(--_halo), transparent 84%);\n }\n }\n\n /* Forced colors drop the tint (backgrounds are forced to Canvas), so pin the\n border to the system highlight — otherwise selection becomes invisible. */\n @media (forced-colors: active) {\n :host([selectable][selected]) .base {\n border-color: Highlight;\n }\n }\n\n /*\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆁🅴🅼🅾🆅🅴 (≥24px target-size, mdi:close mask — mirrors l-close)\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n */\n\n .remove {\n flex: none;\n display: grid;\n place-items: center;\n /* Full height, and split off by a rule: the affordance is drawn *at rest*\n rather than conjured on hover. Keyboard and touch users never get a hover\n state, and a filter bar of chips whose delete zone only appears under the\n pointer is a row of faint marks until you go hunting. */\n align-self: stretch;\n /* Square, and never under the 24px hit target (WCAG 2.5.8) however a\n consumer tunes `--height`. */\n inline-size: max(1.5rem, var(--_height));\n /* The segment spans `.base`'s *border* box, not its content box: the −1px\n block margins cancel the border on each side (a stretched flex item is\n sized by the line minus its own margins), which both lands the target on\n a full 24px — 22px otherwise, one short — and runs the rule edge to edge\n so it meets the chip's own border instead of stopping just inside it. */\n margin-block: -1px;\n /* End: flush to the chip's inner edge. Start: pushed off the label so it\n gets the same air on both sides. `.base`'s `gap` spaces *slotted items*\n (icon ↔ label) and is deliberately tighter than the chip's padding, so\n letting it also front the rule left the label with 8px on its left and\n 4px on its right — visibly crammed against the divider. This tops the gap\n back up to a full `--_padding-inline`, at every size. */\n margin-inline: calc(var(--_padding-inline) - var(--_gap))\n calc(var(--_padding-inline) * -1 - 1px);\n padding: 0;\n /* Transparent on three sides, so `background-clip` below keeps the hover\n fill off the 1px the chip's own border occupies. */\n border: 1px solid transparent;\n border-inline-start-color: var(--_border-color);\n /* The chip's own curve — the padding box rounds to one pixel less on its\n own, so the fill stops on the arc rather than squaring off the pill. */\n border-start-end-radius: var(--_border-radius);\n border-end-end-radius: var(--_border-radius);\n background-color: transparent;\n background-clip: padding-box;\n color: inherit;\n cursor: pointer;\n transition: background-color 120ms ease;\n /* The press dip goes through a variable so the reduced-motion block can\n cancel it by re-declaring *this* one line. Pairing the two `:active`\n selectors instead is a trap: the override only wins on source order while\n the specificities tie, and adding the `:not(:disabled)` guard below was\n enough to silently break it. */\n --_press-scale: 0.86;\n\n &::after {\n content: '';\n inline-size: var(--_remove-glyph);\n block-size: var(--_remove-glyph);\n background-color: currentColor;\n transition: scale 120ms ease;\n /* https://pictogrammers.com/library/mdi/icon/close */\n mask: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z\" /></svg>')\n center / contain no-repeat;\n }\n\n /* The whole segment fills. The chip already tints under the pointer (7%),\n so this has to read as the stronger of the two — it is what will actually\n be clicked. */\n @media (hover: hover) {\n &:hover:not(:disabled) {\n background-color: color-mix(in oklab, transparent, currentColor 12%);\n }\n }\n\n &:active:not(:disabled) {\n background-color: color-mix(in oklab, transparent, currentColor 22%);\n }\n\n /* The glyph dips, not the segment: scaling the box would shrink the rule\n and pull it away from the chip's border. */\n &:active:not(:disabled)::after {\n scale: var(--_press-scale);\n }\n\n /* Inset, unlike the library's usual +2px: the segment is flush with the\n chip's border on three sides, so an outset ring would sit half outside\n the chip and clip against its neighbour in a wrapped filter bar. */\n &:focus-visible {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: -2px;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n /* `background-color` is forced to Canvas in high contrast, which would erase\n a masked glyph entirely — repaint it in the system button color. */\n @media (forced-colors: active) {\n &::after {\n background-color: ButtonText;\n }\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .base,\n .checkbox,\n .remove,\n .remove::after {\n transition-duration: 0ms;\n }\n .remove {\n --_press-scale: 1;\n }\n }\n}\n","import { html, nothing, unsafeCSS } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { cls } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport checkboxAppearance from '../../shared/styles/checkbox-appearance.styles.js';\nimport rawStyles from './tag.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type TagSize = 'sm' | 'md' | 'lg';\nexport type TagControl = 'none' | 'checkbox';\n\n/**\n * Fired when the user asks to remove the tag — clicking the × button, or\n * pressing Backspace/Delete while it is focused. Cancelable: call\n * `preventDefault()` to keep the tag (the controlling host then manages the\n * list itself); otherwise the tag removes itself from the DOM. Does not bubble.\n */\nexport class TagRemoveEvent extends Event {\n constructor() {\n super('remove', { bubbles: false, composed: false, cancelable: true });\n }\n}\n\n/** Fired when a `selectable` tag is toggled. Bubbles; not composed. */\nexport class TagChangeEvent extends Event {\n readonly selected: boolean;\n constructor(selected: boolean) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.selected = selected;\n }\n}\n\ninterface TagEventMap {\n change: TagChangeEvent;\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n remove: TagRemoveEvent;\n }\n}\n\n/**\n * A compact chip for tokens, filters, and multi-select values. Shadow DOM so its\n * styles are self-contained and it renders correctly **anywhere**, including\n * inside another element's shadow root (e.g. the `<l-select>` multi-select\n * trigger).\n *\n * Add `selectable` to turn the chip into a filter control: the tag itself\n * becomes a toggle button (`aria-pressed`), and re-activating a selected tag\n * deselects it — the affordance a radio group cannot offer, so a facet always\n * has a way back to \"all\". Add `control=\"checkbox\"` for a multi-select axis and\n * the library's own checkbox rides inside, with the chip as its label.\n *\n * @summary A compact, optionally removable or selectable chip.\n *\n * @example\n * ```html\n * <l-tag removable>Design</l-tag>\n * <l-tag selectable selected>A3</l-tag>\n * <l-tag selectable control=\"checkbox\">Color <span slot=\"suffix\">79</span></l-tag>\n * ```\n *\n * @event remove - Fired when the user removes the tag (× click or Backspace/Delete). Cancelable; if not prevented the tag removes itself. Not composed, does not bubble.\n * @event change - Fired when a `selectable` tag is toggled. Not cancelable — like the platform's own `change`, and like `l-segmented-control`. Bubbles. Properties: `selected: boolean`. The chip is uncontrolled: it applies the new state before dispatching, so a host that vetoes a toggle (a \"max 3 filters\" rule, an async guard) sets `selected` back in the listener — the revert lands in the same render and is never painted.\n *\n * @slot - The tag label.\n * @slot prefix - Leading content, e.g. an `<l-icon>` or `<l-avatar>`.\n * @slot suffix - Trailing content, e.g. a result count. Gets the chip's own gutter, so no margin is needed.\n *\n * @csspart base - The chip container.\n * @csspart content - The label wrapper.\n * @csspart toggle - The toggle button rendered by `selectable` (not with `control=\"checkbox\"`).\n * @csspart checkbox - The native checkbox rendered by `control=\"checkbox\"`.\n * @csspart remove - The remove button — a full-height segment at the end of the chip.\n *\n * @cssproperty [--text-color] - Label color.\n * @cssproperty [--background-color] - Chip fill.\n * @cssproperty [--border-color] - Chip border, and the rule that splits off the remove button. Defaults to a tint of `--text-color`; setting it pins the line in every state, so a token-based theme keeps its own value on hover.\n * @cssproperty [--selected-text-color] - Label, border, and checkbox accent when selected. Defaults to the library's form-control accent, lightened in dark mode.\n * @cssproperty [--selected-background-color] - Chip fill when selected. Defaults to a tint of `--selected-text-color`.\n * @cssproperty [--selected-border-color] - Chip border when selected. Defaults to a 45% tint of `--selected-text-color`. Selection replaces the line the same way it replaces the fill and the ink, so a chip themed with `--border-color` still gets its emphasis step; set this to theme that step too.\n * @cssproperty [--height] - Chip height. Defaults to the `size` step (a selectable chip is taller, to keep a comfortable target).\n * @cssproperty [--font-size] - Label size. Defaults to the `size` step — 12px, or 14px at `size=\"lg\"`. Set it with `--height` to land between the two steps in a dense filter panel.\n * @cssproperty [--padding-inline] - Horizontal padding. Defaults to the `size` step.\n * @cssproperty [--border-radius] - Corner radius. Defaults to a full pill.\n *\n * @customElement l-tag\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Tag extends LuxenElement {\n static override styles = [hostStyles, checkboxAppearance, styles];\n\n private _localize = new LocalizeController(this);\n\n /** Tag size: `sm`, `md` (default), or `lg`. */\n @property({ reflect: true })\n accessor size: TagSize = 'md';\n\n /** Show a remove button (and enable Backspace/Delete removal). */\n @property({ type: Boolean, reflect: true })\n accessor removable = false;\n\n /** Make the tag a filter control the user can toggle on and off. */\n @property({ type: Boolean, reflect: true })\n accessor selectable = false;\n\n /** Whether the tag is selected. Reflected, so `[selected]` is styleable. */\n @property({ type: Boolean, reflect: true })\n accessor selected = false;\n\n /**\n * What drives the selection: `none` (default) makes the chip itself a toggle\n * button; `checkbox` renders a checkbox inside and makes the chip its label —\n * the right choice for a multi-select facet. Implies `selectable`.\n */\n @property({ reflect: true })\n accessor control: TagControl = 'none';\n\n /** Disable the tag — dims it and blocks selection and removal. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n constructor() {\n super();\n // The chip is operated from inside the shadow root, which a click\n // dispatched on the host never reaches — see `_onHostClick`.\n this.addEventListener('click', this._onHostClick);\n }\n\n override willUpdate() {\n // A checkbox is only ever rendered on a selectable chip, so the attribute\n // (which every selectable style rule keys off) is derived rather than\n // duplicated in every selector — and in the consumer's markup.\n if (this.control === 'checkbox') this.selectable = true;\n }\n\n override updated() {\n // Re-assert the box, don't rely on the `.checked` binding. Lit dirty-checks\n // a property part against the value it last wrote, and a click has the user\n // agent flip `checked` behind its back — so when a controlling host vetoes\n // the toggle by setting `selected` back to what it already was, Lit sees no\n // change, skips the write, and leaves a checked box on an unselected chip\n // (announced \"checked\" too). Cheap: the query is null on every other path.\n const box = this.renderRoot.querySelector<HTMLInputElement>('.checkbox');\n if (box) box.checked = this.selected;\n }\n\n private _requestRemove = () => {\n if (this.disabled) return;\n const event = new TagRemoveEvent();\n this.dispatchEvent(event);\n if (!event.defaultPrevented) this.remove();\n };\n\n private _onKeyDown = (e: KeyboardEvent) => {\n if (!this.removable || this.disabled) return;\n if (e.key === 'Backspace' || e.key === 'Delete') {\n e.preventDefault();\n this._requestRemove();\n }\n };\n\n private _toggle = () => {\n if (this.disabled) return;\n // Toggling, not setting: re-activating a selected tag releases it.\n this.selected = !this.selected;\n this.dispatchEvent(new TagChangeEvent(this.selected));\n };\n\n /**\n * Events do not propagate *down* into a shadow tree, so a click dispatched on\n * the host — `tag.click()`, a test runner resolving the chip by its test id,\n * a runner computing the chip's centre (`elementFromPoint()` retargets shadow\n * content back to the host) — never reaches the toggle button or the\n * checkbox, and silently does nothing. Delegate it.\n *\n * Only when the host *is* the target: a real gesture originates at the inner\n * button, at the checkbox, or at slotted content, and is already handled\n * where it lands — so this never double-toggles. `removable` stays out of it;\n * removing is the × button's own gesture.\n */\n private _onHostClick = (e: Event) => {\n if (!this.selectable || this.disabled) return;\n if (e.composedPath()[0] !== this) return;\n // Right for `control=\"checkbox\"` too: `_toggle()` flips `selected` and\n // fires `change`, and `updated()` re-asserts `box.checked` from it.\n this._toggle();\n };\n\n private _onCheckboxChange = (e: Event) => {\n this.selected = (e.target as HTMLInputElement).checked;\n this.dispatchEvent(new TagChangeEvent(this.selected));\n };\n\n private _label() {\n return html`\n <slot name=\"prefix\"></slot>\n <span\n class=\"content\"\n part=\"content\"\n >\n <slot></slot>\n </span>\n <slot name=\"suffix\"></slot>\n `;\n }\n\n private _removeButton() {\n return this.removable\n ? html`<button\n class=\"remove\"\n part=\"remove\"\n type=\"button\"\n ?disabled=${this.disabled}\n aria-label=${this._localize.term('remove')}\n @click=${this._requestRemove}\n ></button>`\n : nothing;\n }\n\n override render() {\n if (this.control === 'checkbox') {\n return html`\n <label\n class=\"base\"\n part=\"base\"\n @keydown=${this._onKeyDown}\n >\n <input\n class=\"checkbox ${cls('checkbox')}\"\n part=\"checkbox\"\n type=\"checkbox\"\n .checked=${this.selected}\n ?disabled=${this.disabled}\n @change=${this._onCheckboxChange}\n />\n ${this._label()}${this._removeButton()}\n </label>\n `;\n }\n\n if (this.selectable) {\n return html`\n <span\n class=\"base\"\n part=\"base\"\n @keydown=${this._onKeyDown}\n >\n <button\n class=\"toggle\"\n part=\"toggle\"\n type=\"button\"\n aria-pressed=${this.selected ? 'true' : 'false'}\n ?disabled=${this.disabled}\n @click=${this._toggle}\n >\n ${this._label()}\n </button>\n ${this._removeButton()}\n </span>\n `;\n }\n\n return html`\n <span\n class=\"base\"\n part=\"base\"\n @keydown=${this._onKeyDown}\n >\n ${this._label()}${this._removeButton()}\n </span>\n `;\n }\n}\n\n// Type `addEventListener('change', …)` so listeners receive a `TagChangeEvent`\n// (with `.selected`) instead of a bare `Event`. `change` collides with lib.dom's\n// `GlobalEventHandlersEventMap`, so it can't be augmented globally — the\n// overloads are declaration-merged onto this element's interface instead. The\n// interface MUST come after the class (the CEM analyzer dedups on the first\n// declaration of the name). See `tabs.ts` for the fully-annotated reference.\nexport interface Tag {\n addEventListener<K extends keyof TagEventMap>(\n type: K,\n listener: (this: Tag, ev: TagEventMap[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 TagEventMap>(\n type: K,\n listener: (this: Tag, ev: TagEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tag.js","names":["unsafeCSS","rawStyles","hostStyles","checkboxAppearance","nothing","_decorate","property","r","n","__decorate","b","A","host_styles_default","checkbox_appearance_styles_default","styles","TagRemoveEvent","TagChangeEvent","selected","Tag","LuxenElement","#_size_accessor_storage","value","#_removable_accessor_storage","#_selectable_accessor_storage","#_selected_accessor_storage","#_control_accessor_storage","#_disabled_accessor_storage","LocalizeController","event","box","cls"],"sources":["../../../src/html/elements/tag/tag.css?inline","../../../src/html/elements/tag/tag.ts"],"sourcesContent":["@layer components {\n :host {\n /* Size defaults (md). A removable tag is always ≥24px tall (target-size).\n The three public knobs are folded into the private vars here rather than\n at each use site, so an override also carries to what derives from them —\n the toggle's negative inline margins and the × button's spacing math. The\n trio mirrors `.l-button` (`--height` / `--padding-inline` / `--font-size`)\n so a chip can be tuned to a dense filter panel without ::part(base). */\n --_height: var(--height, 1.5rem);\n --_font-size: var(--font-size, var(--l-text-xs));\n --_padding-inline: var(--padding-inline, 0.5rem);\n --_gap: 0.25rem;\n\n /* Skin, resolved once so `[selected]` can swap all three at the source\n instead of re-declaring the properties that consume them. */\n --_background: var(--background-color, var(--l-color-bg-fill-neutral-subtle));\n --_color: var(--text-color, var(--l-color-text-neutral));\n --_border-strength: 14%;\n /* By default the line is a tint of the ink, so a chip stays coherent under\n any `--text-color` with one declaration. `--border-color` replaces that\n derivation outright rather than shifting it: a consumer with a token\n system wants *its* line token, not a 14% mix of its text color — and\n wants it to hold in every state, which is why it is the fallback in both\n of the pair below rather than a value the hover rule mixes further.\n `--_border-strength` stays private. Selecting a chip swaps the pair\n wholesale, the same way it swaps the fill and the ink. */\n --_border-color: var(\n --border-color,\n color-mix(in oklab, currentColor var(--_border-strength), transparent)\n );\n --_border-color-hover: var(\n --border-color,\n color-mix(in oklab, currentColor calc(var(--_border-strength) + 12%), transparent)\n );\n /* Resolved here rather than at `.base`, because the × segment has to round\n its outer end to the same curve. */\n --_border-radius: var(--border-radius, var(--l-radius-full));\n --_remove-glyph: 0.75rem;\n\n display: inline-flex;\n vertical-align: middle;\n max-inline-size: 100%;\n }\n\n :host([size='sm']) {\n --_padding-inline: var(--padding-inline, 0.375rem);\n --_gap: 0.125rem;\n }\n\n :host([size='lg']) {\n --_height: var(--height, 1.75rem);\n --_font-size: var(--font-size, var(--l-text-sm));\n --_padding-inline: var(--padding-inline, 0.625rem);\n --_remove-glyph: 0.875rem;\n }\n\n /* Disabled is conveyed by the greyed × button + cursor, NOT by washing out\n * the label — dimming the whole chip with opacity composites the text below\n * the WCAG AA contrast floor (axe flags it; a disabled <button> is exempt but\n * the label span is not). */\n :host([disabled]) .base {\n cursor: not-allowed;\n }\n\n /*\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🅲🅷🅸🅿\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n */\n\n .base {\n display: inline-flex;\n align-items: center;\n gap: var(--_gap);\n min-block-size: var(--_height);\n padding-inline: var(--_padding-inline);\n border: 1px solid var(--_border-color);\n border-radius: var(--_border-radius);\n background-color: var(--_background);\n color: var(--_color);\n font-family: var(--l-font-neo-grotesque);\n font-size: var(--_font-size);\n font-weight: var(--l-font-weight-medium);\n line-height: 1;\n min-inline-size: 0;\n transition:\n background-color 120ms ease,\n border-color 120ms ease;\n }\n\n /* The remove segment is exactly as tall as the chip, so the chip is where the\n target floor has to live: `--height` still tunes a removable chip, but not\n under the 24px minimum (WCAG 2.5.8). Without this the promise held only for\n the default value. */\n :host([removable]) .base {\n min-block-size: max(1.5rem, var(--_height));\n }\n\n /* Whole-chip hover — only on a chip that is *wholly* a control. A display chip\n has no target at all, and a removable one has exactly its × segment: tinting\n the label under the pointer there promises a click that does nothing, and on\n a filter bar it fights the segment for the eye. `[selectable]` also covers\n `control=\"checkbox\"`, which derives the attribute. */\n @media (hover: hover) {\n :host([selectable]:hover:not([disabled])) .base {\n background-color: color-mix(in oklab, var(--_background), currentColor 7%);\n border-color: var(--_border-color-hover);\n }\n }\n\n .content {\n display: inline-flex;\n align-items: center;\n min-inline-size: 0;\n /* `.base` sets `line-height: 1` so the chip's height comes from its own size\n tokens rather than the document's leading. That makes the line box exactly\n one em tall, and a plain `overflow: hidden` then shears every descender\n (g, j, p, q, y) at all three sizes — measured 1px short.\n\n Fixed by clipping the inline axis only — the one the ellipsis needs — so\n nothing is ever cut vertically and the box keeps its exact geometry.\n Growing the line box instead (`line-height: normal`) moves the text half a\n pixel against the × and the prefix icon, since the font's ascent and\n descent are not symmetrical; widening the clip with\n `overflow-clip-margin` leaves Safari shearing the text (WebKit has no\n such property). `clip` — unlike `hidden` — lets the block axis stay\n `visible`. */\n overflow-x: clip;\n overflow-y: visible;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n ::slotted([slot='prefix']) {\n display: inline-flex;\n flex: none;\n }\n\n /* A count or trailing glyph. It sits in its own slot so `.base`'s gap gives it\n a gutter by construction — the label is a bare text node and can never be\n targeted from the outside. Lighter weight than the medium label, and\n tabular so a column of counts stays aligned. */\n ::slotted([slot='suffix']) {\n flex: none;\n font-weight: var(--l-font-weight-normal);\n font-variant-numeric: tabular-nums;\n }\n\n /*\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆂🅴🅻🅴🅲🆃🅰🅱🅻🅴 (filter chip — toggle button, or a checkbox riding inside)\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n */\n\n :host([selectable]) {\n /* Taller than a display chip at the same `size`: a chip you click is a\n target (WCAG 2.5.8), and this is the dense step between `sm` and `lg` —\n more room, same 12px label, where `lg` would jump the label to body size.\n `--height` / `--font-size` tune it from there. */\n --_height: var(--height, 1.75rem);\n\n /* The selection accent is the same one the library's checkbox, radio and\n switch use, so a chip with a checkbox inside reads as a single control. */\n --_accent: var(--l-form-control-activated-color);\n /* The label is 12px, so it owes 4.5:1 over its own tint — and the raw accent\n misses it on the light one (4.35:1 measured). Darken it there, lighten it\n on the dark tint, and both modes clear 6:1. `color-mix()` resolves\n reliably inside `light-dark()` — `oklch(from …)` does not. */\n --_selected-color: var(\n --selected-text-color,\n light-dark(\n color-mix(in oklab, var(--_accent), black 14%),\n color-mix(in oklab, var(--_accent), white 45%)\n )\n );\n /* Derived from the text color, so overriding `--selected-text-color` alone\n repaints the whole chip coherently. Kept at 10%: a consumer accent skips\n the mode correction above, so the tint has to stay pale enough for a\n semantic token (`--l-color-text-success` and friends) to clear 4.5:1 on\n it unaided. The border carries the rest of the emphasis. */\n --_selected-background: var(\n --selected-background-color,\n color-mix(in oklab, var(--_selected-color) 10%, var(--l-color-surface))\n );\n /* The third member of the trio, beside `--selected-text-color` and\n `--selected-background-color`. Selection already replaces the fill and the ink\n outright, so the line has to be replaceable the same way — without this,\n a chip that sets `--border-color` keeps that exact line when picked and\n the emphasis step that says \"selected\" never happens. 45% by default, the\n strength the derived line has always used here. */\n --_selected-border-color: var(\n --selected-border-color,\n color-mix(in oklab, var(--_selected-color) 45%, transparent)\n );\n }\n\n :host([selectable][size='lg']) {\n --_height: var(--height, 2rem);\n }\n\n :host([selectable][selected]) {\n --_background: var(--_selected-background);\n --_color: var(--_selected-color);\n --_border-color: var(--_selected-border-color);\n /* Same +12 step the resting line takes on hover, and pinned in the same way\n when the consumer supplies the value. */\n --_border-color-hover: var(\n --selected-border-color,\n color-mix(in oklab, var(--_selected-color) 57%, transparent)\n );\n }\n\n :host([selectable]) .base {\n cursor: pointer;\n /* A chip is tapped, not read: never leave a text selection behind a\n double-click on a filter. */\n user-select: none;\n }\n\n :host([selectable][disabled]) .base {\n cursor: not-allowed;\n }\n\n /* The chip *is* the button. Negative inline margins let it cover `.base`'s\n padding so the whole pill is the target, while `.base` keeps owning the\n border, background, and the × button's spacing math. */\n .toggle {\n flex: 1 1 auto;\n display: inline-flex;\n align-items: center;\n align-self: stretch;\n gap: var(--_gap);\n min-inline-size: 0;\n margin-inline: calc(var(--_padding-inline) * -1);\n padding-inline: var(--_padding-inline);\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n text-align: start;\n cursor: inherit;\n }\n\n /* Stop before the × instead of running under it. */\n :host([removable]) .toggle {\n margin-inline-end: 0;\n padding-inline-end: 0;\n }\n\n /* One ring for the whole chip: the inner control is a detail of how the chip\n is operated, not a second thing to focus visually — and the ring drawn\n below fully encloses the box, so the indicator only grows (WCAG 2.4.7 /\n 2.4.13).\n\n Qualified by `:host(…)` on purpose. The shared appearance nests\n `&:focus-visible` under a selector list, so it lands as\n `:is(.l-checkbox, … input[type='checkbox']:not(…)):focus-visible` — 0,3,1,\n which outranks a bare `.checkbox:focus-visible` (0,2,0) and paints a second\n ring around the box. `:host([selectable])` brings this to 0,4,0. */\n :host([selectable]) .toggle:focus-visible,\n :host([selectable]) .checkbox:focus-visible {\n outline: none;\n }\n\n .base:has(:is(.toggle, .checkbox):focus-visible) {\n outline: var(--l-focus-ring-width) solid var(--l-focus-ring-color);\n outline-offset: var(--l-focus-ring-offset);\n }\n\n /* The skin comes from the shared `.l-checkbox` appearance, applied through\n `cls('checkbox')` rather than a static class literal: `dist` ships that CSS\n as a real file, so a consumer's PostCSS rewrites its selector under a\n renamed `cssPrefix` — a baked `l-checkbox` in the template would stop\n matching it. This rule only carries what is specific to a chip.\n\n The shared appearance sizes the box in `em`, and a UA gives `<input>` its\n own font — inheriting the chip's makes the box track the `size` step. The\n accent follows the selected color, so a themed chip keeps its checkbox in\n the same hue. */\n .checkbox {\n --accent: var(--selected-text-color, var(--l-form-control-activated-color));\n /* Hover halo — same ratio and same token flip as the light-DOM\n `.l-checkbox`, which keeps this rule out of the shared partial (see\n `css/elements/checkbox.css`). Repeated here rather than moved into the\n partial: `l-tree-item` shares that file and never opted into a glow. */\n --_halo: var(--l-form-control-border-color-hover);\n\n flex: none;\n font: inherit;\n transition-property: background-color, border-color, box-shadow;\n }\n\n .checkbox:checked,\n .checkbox:indeterminate {\n --_halo: var(--accent);\n }\n\n /* Driven by the chip, not by the box: the chip is the label and the whole\n click target, so a halo lighting up only over 15px of a 28px pill would\n read as a glitch. */\n @media (hover: hover) {\n :host([selectable]:not([disabled])) .base:hover .checkbox:not(:disabled) {\n box-shadow: 0 0 0 calc(var(--size) * var(--l-form-control-toggle-halo-ratio))\n color-mix(in oklab, var(--_halo), transparent 84%);\n }\n }\n\n /* Forced colors drop the tint (backgrounds are forced to Canvas), so pin the\n border to the system highlight — otherwise selection becomes invisible. */\n @media (forced-colors: active) {\n :host([selectable][selected]) .base {\n border-color: Highlight;\n }\n }\n\n /*\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n 🆁🅴🅼🅾🆅🅴 (≥24px target-size, mdi:close mask — mirrors l-close)\n ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬\n */\n\n .remove {\n flex: none;\n display: grid;\n place-items: center;\n /* Full height, and split off by a rule: the affordance is drawn *at rest*\n rather than conjured on hover. Keyboard and touch users never get a hover\n state, and a filter bar of chips whose delete zone only appears under the\n pointer is a row of faint marks until you go hunting. */\n align-self: stretch;\n /* Square, and never under the 24px hit target (WCAG 2.5.8) however a\n consumer tunes `--height`. */\n inline-size: max(1.5rem, var(--_height));\n /* The segment spans `.base`'s *border* box, not its content box: the −1px\n block margins cancel the border on each side (a stretched flex item is\n sized by the line minus its own margins), which both lands the target on\n a full 24px — 22px otherwise, one short — and runs the rule edge to edge\n so it meets the chip's own border instead of stopping just inside it. */\n margin-block: -1px;\n /* End: flush to the chip's inner edge. Start: pushed off the label so it\n gets the same air on both sides. `.base`'s `gap` spaces *slotted items*\n (icon ↔ label) and is deliberately tighter than the chip's padding, so\n letting it also front the rule left the label with 8px on its left and\n 4px on its right — visibly crammed against the divider. This tops the gap\n back up to a full `--_padding-inline`, at every size. */\n margin-inline: calc(var(--_padding-inline) - var(--_gap))\n calc(var(--_padding-inline) * -1 - 1px);\n padding: 0;\n /* Transparent on three sides, so `background-clip` below keeps the hover\n fill off the 1px the chip's own border occupies. */\n border: 1px solid transparent;\n border-inline-start-color: var(--_border-color);\n /* The chip's own curve — the padding box rounds to one pixel less on its\n own, so the fill stops on the arc rather than squaring off the pill. */\n border-start-end-radius: var(--_border-radius);\n border-end-end-radius: var(--_border-radius);\n background-color: transparent;\n background-clip: padding-box;\n color: inherit;\n cursor: pointer;\n transition: background-color 120ms ease;\n /* The press dip goes through a variable so the reduced-motion block can\n cancel it by re-declaring *this* one line. Pairing the two `:active`\n selectors instead is a trap: the override only wins on source order while\n the specificities tie, and adding the `:not(:disabled)` guard below was\n enough to silently break it. */\n --_press-scale: 0.86;\n\n &::after {\n content: '';\n inline-size: var(--_remove-glyph);\n block-size: var(--_remove-glyph);\n background-color: currentColor;\n transition: scale 120ms ease;\n /* https://pictogrammers.com/library/mdi/icon/close */\n mask: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z\" /></svg>')\n center / contain no-repeat;\n }\n\n /* The whole segment fills. The chip already tints under the pointer (7%),\n so this has to read as the stronger of the two — it is what will actually\n be clicked. */\n @media (hover: hover) {\n &:hover:not(:disabled) {\n background-color: color-mix(in oklab, transparent, currentColor 12%);\n }\n }\n\n &:active:not(:disabled) {\n background-color: color-mix(in oklab, transparent, currentColor 22%);\n }\n\n /* The glyph dips, not the segment: scaling the box would shrink the rule\n and pull it away from the chip's border. */\n &:active:not(:disabled)::after {\n scale: var(--_press-scale);\n }\n\n /* Inset, unlike the library's usual +2px: the segment is flush with the\n chip's border on three sides, so an outset ring would sit half outside\n the chip and clip against its neighbour in a wrapped filter bar. */\n &:focus-visible {\n outline: var(--l-focus-ring-width) solid var(--l-focus-ring-color);\n outline-offset: calc(var(--l-focus-ring-width) * -1);\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n /* `background-color` is forced to Canvas in high contrast, which would erase\n a masked glyph entirely — repaint it in the system button color. */\n @media (forced-colors: active) {\n &::after {\n background-color: ButtonText;\n }\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .base,\n .checkbox,\n .remove,\n .remove::after {\n transition-duration: 0ms;\n }\n .remove {\n --_press-scale: 1;\n }\n }\n}\n","import { html, nothing, unsafeCSS } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { cls } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport checkboxAppearance from '../../shared/styles/checkbox-appearance.styles.js';\nimport rawStyles from './tag.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type TagSize = 'sm' | 'md' | 'lg';\nexport type TagControl = 'none' | 'checkbox';\n\n/**\n * Fired when the user asks to remove the tag — clicking the × button, or\n * pressing Backspace/Delete while it is focused. Cancelable: call\n * `preventDefault()` to keep the tag (the controlling host then manages the\n * list itself); otherwise the tag removes itself from the DOM. Does not bubble.\n */\nexport class TagRemoveEvent extends Event {\n constructor() {\n super('remove', { bubbles: false, composed: false, cancelable: true });\n }\n}\n\n/** Fired when a `selectable` tag is toggled. Bubbles; not composed. */\nexport class TagChangeEvent extends Event {\n readonly selected: boolean;\n constructor(selected: boolean) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.selected = selected;\n }\n}\n\ninterface TagEventMap {\n change: TagChangeEvent;\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n remove: TagRemoveEvent;\n }\n}\n\n/**\n * A compact chip for tokens, filters, and multi-select values. Shadow DOM so its\n * styles are self-contained and it renders correctly **anywhere**, including\n * inside another element's shadow root (e.g. the `<l-select>` multi-select\n * trigger).\n *\n * Add `selectable` to turn the chip into a filter control: the tag itself\n * becomes a toggle button (`aria-pressed`), and re-activating a selected tag\n * deselects it — the affordance a radio group cannot offer, so a facet always\n * has a way back to \"all\". Add `control=\"checkbox\"` for a multi-select axis and\n * the library's own checkbox rides inside, with the chip as its label.\n *\n * @summary A compact, optionally removable or selectable chip.\n *\n * @example\n * ```html\n * <l-tag removable>Design</l-tag>\n * <l-tag selectable selected>A3</l-tag>\n * <l-tag selectable control=\"checkbox\">Color <span slot=\"suffix\">79</span></l-tag>\n * ```\n *\n * @event remove - Fired when the user removes the tag (× click or Backspace/Delete). Cancelable; if not prevented the tag removes itself. Not composed, does not bubble.\n * @event change - Fired when a `selectable` tag is toggled. Not cancelable — like the platform's own `change`, and like `l-segmented-control`. Bubbles. Properties: `selected: boolean`. The chip is uncontrolled: it applies the new state before dispatching, so a host that vetoes a toggle (a \"max 3 filters\" rule, an async guard) sets `selected` back in the listener — the revert lands in the same render and is never painted.\n *\n * @slot - The tag label.\n * @slot prefix - Leading content, e.g. an `<l-icon>` or `<l-avatar>`.\n * @slot suffix - Trailing content, e.g. a result count. Gets the chip's own gutter, so no margin is needed.\n *\n * @csspart base - The chip container.\n * @csspart content - The label wrapper.\n * @csspart toggle - The toggle button rendered by `selectable` (not with `control=\"checkbox\"`).\n * @csspart checkbox - The native checkbox rendered by `control=\"checkbox\"`.\n * @csspart remove - The remove button — a full-height segment at the end of the chip.\n *\n * @cssproperty [--text-color] - Label color.\n * @cssproperty [--background-color] - Chip fill.\n * @cssproperty [--border-color] - Chip border, and the rule that splits off the remove button. Defaults to a tint of `--text-color`; setting it pins the line in every state, so a token-based theme keeps its own value on hover.\n * @cssproperty [--selected-text-color] - Label, border, and checkbox accent when selected. Defaults to the library's form-control accent, lightened in dark mode.\n * @cssproperty [--selected-background-color] - Chip fill when selected. Defaults to a tint of `--selected-text-color`.\n * @cssproperty [--selected-border-color] - Chip border when selected. Defaults to a 45% tint of `--selected-text-color`. Selection replaces the line the same way it replaces the fill and the ink, so a chip themed with `--border-color` still gets its emphasis step; set this to theme that step too.\n * @cssproperty [--height] - Chip height. Defaults to the `size` step (a selectable chip is taller, to keep a comfortable target).\n * @cssproperty [--font-size] - Label size. Defaults to the `size` step — 12px, or 14px at `size=\"lg\"`. Set it with `--height` to land between the two steps in a dense filter panel.\n * @cssproperty [--padding-inline] - Horizontal padding. Defaults to the `size` step.\n * @cssproperty [--border-radius] - Corner radius. Defaults to a full pill.\n *\n * @customElement l-tag\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class Tag extends LuxenElement {\n static override styles = [hostStyles, checkboxAppearance, styles];\n\n private _localize = new LocalizeController(this);\n\n /** Tag size: `sm`, `md` (default), or `lg`. */\n @property({ reflect: true })\n accessor size: TagSize = 'md';\n\n /** Show a remove button (and enable Backspace/Delete removal). */\n @property({ type: Boolean, reflect: true })\n accessor removable = false;\n\n /** Make the tag a filter control the user can toggle on and off. */\n @property({ type: Boolean, reflect: true })\n accessor selectable = false;\n\n /** Whether the tag is selected. Reflected, so `[selected]` is styleable. */\n @property({ type: Boolean, reflect: true })\n accessor selected = false;\n\n /**\n * What drives the selection: `none` (default) makes the chip itself a toggle\n * button; `checkbox` renders a checkbox inside and makes the chip its label —\n * the right choice for a multi-select facet. Implies `selectable`.\n */\n @property({ reflect: true })\n accessor control: TagControl = 'none';\n\n /** Disable the tag — dims it and blocks selection and removal. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n constructor() {\n super();\n // The chip is operated from inside the shadow root, which a click\n // dispatched on the host never reaches — see `_onHostClick`.\n this.addEventListener('click', this._onHostClick);\n }\n\n override willUpdate() {\n // A checkbox is only ever rendered on a selectable chip, so the attribute\n // (which every selectable style rule keys off) is derived rather than\n // duplicated in every selector — and in the consumer's markup.\n if (this.control === 'checkbox') this.selectable = true;\n }\n\n override updated() {\n // Re-assert the box, don't rely on the `.checked` binding. Lit dirty-checks\n // a property part against the value it last wrote, and a click has the user\n // agent flip `checked` behind its back — so when a controlling host vetoes\n // the toggle by setting `selected` back to what it already was, Lit sees no\n // change, skips the write, and leaves a checked box on an unselected chip\n // (announced \"checked\" too). Cheap: the query is null on every other path.\n const box = this.renderRoot.querySelector<HTMLInputElement>('.checkbox');\n if (box) box.checked = this.selected;\n }\n\n private _requestRemove = () => {\n if (this.disabled) return;\n const event = new TagRemoveEvent();\n this.dispatchEvent(event);\n if (!event.defaultPrevented) this.remove();\n };\n\n private _onKeyDown = (e: KeyboardEvent) => {\n if (!this.removable || this.disabled) return;\n if (e.key === 'Backspace' || e.key === 'Delete') {\n e.preventDefault();\n this._requestRemove();\n }\n };\n\n private _toggle = () => {\n if (this.disabled) return;\n // Toggling, not setting: re-activating a selected tag releases it.\n this.selected = !this.selected;\n this.dispatchEvent(new TagChangeEvent(this.selected));\n };\n\n /**\n * Events do not propagate *down* into a shadow tree, so a click dispatched on\n * the host — `tag.click()`, a test runner resolving the chip by its test id,\n * a runner computing the chip's centre (`elementFromPoint()` retargets shadow\n * content back to the host) — never reaches the toggle button or the\n * checkbox, and silently does nothing. Delegate it.\n *\n * Only when the host *is* the target: a real gesture originates at the inner\n * button, at the checkbox, or at slotted content, and is already handled\n * where it lands — so this never double-toggles. `removable` stays out of it;\n * removing is the × button's own gesture.\n */\n private _onHostClick = (e: Event) => {\n if (!this.selectable || this.disabled) return;\n if (e.composedPath()[0] !== this) return;\n // Right for `control=\"checkbox\"` too: `_toggle()` flips `selected` and\n // fires `change`, and `updated()` re-asserts `box.checked` from it.\n this._toggle();\n };\n\n private _onCheckboxChange = (e: Event) => {\n this.selected = (e.target as HTMLInputElement).checked;\n this.dispatchEvent(new TagChangeEvent(this.selected));\n };\n\n private _label() {\n return html`\n <slot name=\"prefix\"></slot>\n <span\n class=\"content\"\n part=\"content\"\n >\n <slot></slot>\n </span>\n <slot name=\"suffix\"></slot>\n `;\n }\n\n private _removeButton() {\n return this.removable\n ? html`<button\n class=\"remove\"\n part=\"remove\"\n type=\"button\"\n ?disabled=${this.disabled}\n aria-label=${this._localize.term('remove')}\n @click=${this._requestRemove}\n ></button>`\n : nothing;\n }\n\n override render() {\n if (this.control === 'checkbox') {\n return html`\n <label\n class=\"base\"\n part=\"base\"\n @keydown=${this._onKeyDown}\n >\n <input\n class=\"checkbox ${cls('checkbox')}\"\n part=\"checkbox\"\n type=\"checkbox\"\n .checked=${this.selected}\n ?disabled=${this.disabled}\n @change=${this._onCheckboxChange}\n />\n ${this._label()}${this._removeButton()}\n </label>\n `;\n }\n\n if (this.selectable) {\n return html`\n <span\n class=\"base\"\n part=\"base\"\n @keydown=${this._onKeyDown}\n >\n <button\n class=\"toggle\"\n part=\"toggle\"\n type=\"button\"\n aria-pressed=${this.selected ? 'true' : 'false'}\n ?disabled=${this.disabled}\n @click=${this._toggle}\n >\n ${this._label()}\n </button>\n ${this._removeButton()}\n </span>\n `;\n }\n\n return html`\n <span\n class=\"base\"\n part=\"base\"\n @keydown=${this._onKeyDown}\n >\n ${this._label()}${this._removeButton()}\n </span>\n `;\n }\n}\n\n// Type `addEventListener('change', …)` so listeners receive a `TagChangeEvent`\n// (with `.selected`) instead of a bare `Event`. `change` collides with lib.dom's\n// `GlobalEventHandlersEventMap`, so it can't be augmented globally — the\n// overloads are declaration-merged onto this element's interface instead. The\n// interface MUST come after the class (the CEM analyzer dedups on the first\n// declaration of the name). See `tabs.ts` for the fully-annotated reference.\nexport interface Tag {\n addEventListener<K extends keyof TagEventMap>(\n type: K,\n listener: (this: Tag, ev: TagEventMap[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 TagEventMap>(\n type: K,\n listener: (this: Tag, ev: TagEventMap[K]) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import{i as e}from"../../chunks/lit.js";import{r as t,t as n}from"../../chunks/decorate.js";import{LuxenElement as r}from"../../shared/luxen-element.js";import{a as i}from"../../chunks/lit-html.js";import{tagName as a}from"../../registry.js";import o from"../../shared/styles/host.styles.js";var s=e(`:host{--indent-size:1rem;--indent-guide-width:1px;--indent-guide-style:solid;--indent-guide-color:var(--l-color-border-interactive);--row-height:1.75rem;--row-padding-inline:.25rem;--chevron-size:1.125rem;--item-gap:.375rem;color:var(--l-color-text-primary);font-family:inherit;line-height:1.5;display:block}.tree{outline:none;display:block}.tree:focus-visible{outline:2px solid var(--l-focus-ring);outline-offset:2px;border-radius:.375rem}`),c=class extends Event{constructor(e){super(`selection-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.selection=e}},l=class extends r{constructor(...e){super(...e),this._internals=this.attachInternals(),this._lastFocusedItem=null,this.#e=`single`,this.#t=!1,this._onItemToggle=e=>{this._selectItem(e.item,e.checked)},this._onClick=e=>{let t=this._itemFromEvent(e);if(!t||t.disabled)return;let n=e.composedPath();if(n.some(e=>e instanceof HTMLInputElement&&e.type===`checkbox`))return;let r=new Set([`BUTTON`,`A`,`INPUT`,`SELECT`,`TEXTAREA`]),i=new Set([`button`,`link`,`menuitem`,`menuitemcheckbox`]);if(n.some(e=>{if(!(e instanceof HTMLElement)||e===t||e.getAttribute?.(`part`)===`expand-button`||e instanceof HTMLInputElement&&e.type===`checkbox`)return!1;if(r.has(e.tagName))return!0;let n=e.getAttribute?.(`role`);return n!==null&&i.has(n)}))return;let a=n.some(e=>e instanceof HTMLElement&&e.getAttribute?.(`part`)===`expand-button`);if(this._focusItem(t),a)t.toggle();else switch(this.selection){case`single`:this._setSingleSelection(t),t.isLeaf()||t.toggle();break;case`leaf`:t.isLeaf()?this._setSingleSelection(t):t.toggle();break;case`multiple`:this._selectItem(t,!t.selected);break;case`none`:t.toggle()}},this._onFocusIn=e=>{let t=e.target;if(t instanceof HTMLElement){let e=t.closest(a(`tree-item`));e&&(this._lastFocusedItem=e)}},this._onKeyDown=e=>{let t=this._lastFocusedItem??this._visibleItems()[0];if(!t)return;let n=this._visibleItems(),r=n.indexOf(t);switch(e.key){case`ArrowDown`:{e.preventDefault();let t=n[Math.min(r+1,n.length-1)];t&&this._focusItem(t);break}case`ArrowUp`:{e.preventDefault();let t=n[Math.max(r-1,0)];t&&this._focusItem(t);break}case`ArrowRight`:if(e.preventDefault(),!t.isLeaf()&&!t.expanded)t.expanded=!0;else if(t.expanded){let e=t.getChildrenItems()[0];e&&this._focusItem(e)}break;case`ArrowLeft`:if(e.preventDefault(),t.expanded&&!t.isLeaf())t.expanded=!1;else{let e=t.parentElement?.closest(a(`tree-item`));e&&this._focusItem(e)}break;case`Home`:e.preventDefault(),n[0]&&this._focusItem(n[0]);break;case`End`:{e.preventDefault();let t=n[n.length-1];t&&this._focusItem(t);break}case`Enter`:case` `:e.preventDefault(),this._handleRowActivate(t);break;case`*`:{e.preventDefault();let n=(t.parentElement?Array.from(t.parentElement.children):[]).filter(e=>e.tagName===a(`tree-item`).toUpperCase());for(let e of n)e.isLeaf()||(e.expanded=!0);break}}}}static{this.styles=[o,s]}#e;get selection(){return this.#e}set selection(e){this.#e=e}#t;get independent(){return this.#t}set independent(e){this.#t=e}connectedCallback(){super.connectedCallback(),this._internals.role=`tree`,this.hasAttribute(`role`)||this.setAttribute(`role`,`tree`),this._mutationObserver=new MutationObserver(()=>this._syncAll()),this._mutationObserver.observe(this,{childList:!0,subtree:!0}),this.addEventListener(`selection-toggle`,this._onItemToggle),queueMicrotask(()=>this._syncAll())}disconnectedCallback(){super.disconnectedCallback(),this._mutationObserver?.disconnect(),this.removeEventListener(`selection-toggle`,this._onItemToggle)}updated(e){if(e.has(`selection`)){let e=this.selection===`multiple`?`true`:`false`;this._internals.ariaMultiSelectable=e,this.setAttribute(`aria-multiselectable`,e)}(e.has(`selection`)||e.has(`independent`))&&this._syncAll()}getAllItems({includeDisabled:e=!0}={}){let t=a(`tree-item`);return Array.from(this.querySelectorAll(t)).filter(t=>e||!t.disabled)}getSelection(){return this.getAllItems().filter(e=>e.selected)}expandAll(){for(let e of this.getAllItems())e.isLeaf()||(e.expanded=!0)}collapseAll(){for(let e of this.getAllItems())e.expanded=!1}_syncAll(){customElements.upgrade(this);let e=a(`tree-item`),t=this._rootItems();if(t.some(e=>typeof e.getChildrenItems!=`function`)){customElements.whenDefined(e).then(()=>this._syncAll());return}let n=this.selection===`multiple`;this._syncLevel(t,0,n),this._updateBranchStates(),this._ensureTabStop()}_syncLevel(e,t,n){let r=e.length;e.forEach((e,i)=>{e.depth=t,e.showCheckbox=n&&this._canShowCheckboxOn(e),e.setPosition(t+1,i+1,r),this._syncLevel(e.getChildrenItems(),t+1,n)})}_canShowCheckboxOn(e){return this.selection===`multiple`}_rootItems(){let e=a(`tree-item`).toUpperCase();return Array.from(this.children).filter(t=>t.tagName===e)}_ensureTabStop(){let e=this._visibleItems();if(e.length!==0&&!e.some(e=>e.tabIndex===0)){for(let t of e)t.tabIndex=-1;e[0].tabIndex=0}}_visibleItems(){let e=[],t=n=>{for(let r of n)e.push(r),r.expanded&&t(r.getChildrenItems())};return t(this._rootItems()),e}_handleRowActivate(e){if(!e.disabled)switch(this.selection){case`single`:this._setSingleSelection(e),e.isLeaf()||e.toggle();break;case`leaf`:e.isLeaf()?this._setSingleSelection(e):e.toggle();break;case`multiple`:this._selectItem(e,!e.selected);break;case`none`:e.toggle()}}_setSingleSelection(e){for(let t of this.getAllItems())t!==e&&t.selected&&(t.selected=!1);e.selected=!0,this._emitSelectionChange()}_selectItem(e,t){e.disabled||(e.selected=t,this._cascades&&(this._setSubtreeSelection(e,t),e._setCascadeIndeterminate(!1)),this._updateBranchStates(),this._emitSelectionChange())}_setSubtreeSelection(e,t){for(let n of e.getChildrenItems())n.disabled||(n.selected=t,n._setCascadeIndeterminate(!1),this._setSubtreeSelection(n,t))}get _cascades(){return this.selection===`multiple`&&!this.independent}_updateBranchStates(){if(!this._cascades){for(let e of this.getAllItems())e._cascadeIndeterminate&&e._setCascadeIndeterminate(!1);return}let e=t=>{let n=t.getChildrenItems({includeDisabled:!1});if(n.length===0)return t._setCascadeIndeterminate(!1),{all:t.selected,any:t.selected};let r=!0,i=!1;for(let t of n){let n=e(t);n.all||(r=!1),n.any&&(i=!0)}return t.selected=r,t._setCascadeIndeterminate(!r&&i),{all:r&&(t.getChildrenItems().length>0?r:t.selected),any:i}};for(let t of this._rootItems())e(t)}_emitSelectionChange(){this.dispatchEvent(new c(this.getSelection()))}_itemFromEvent(e){let t=a(`tree-item`),n=e.composedPath();for(let e of n)if(e instanceof HTMLElement&&e.matches?.(t))return e;return null}_focusItem(e){let t=this._visibleItems();for(let e of t)e.tabIndex=-1;e.tabIndex=0,e.focus(),this._lastFocusedItem=e}render(){return i`
1
+ import{i as e}from"../../chunks/lit.js";import{r as t,t as n}from"../../chunks/decorate.js";import{LuxenElement as r}from"../../shared/luxen-element.js";import{a as i}from"../../chunks/lit-html.js";import{tagName as a}from"../../registry.js";import o from"../../shared/styles/host.styles.js";var s=e(`:host{--indent-size:1rem;--indent-guide-width:1px;--indent-guide-style:solid;--indent-guide-color:var(--l-color-border-interactive);--row-height:1.75rem;--row-padding-inline:.25rem;--chevron-size:1.125rem;--item-gap:.375rem;color:var(--l-color-text-primary);font-family:inherit;line-height:1.5;display:block}.tree{outline:none;display:block}.tree:focus-visible{outline:var(--l-focus-ring-width) solid var(--l-focus-ring-color);outline-offset:var(--l-focus-ring-offset);border-radius:var(--l-radius-md)}`),c=class extends Event{constructor(e){super(`selection-change`,{bubbles:!1,composed:!1,cancelable:!1}),this.selection=e}},l=class extends r{constructor(...e){super(...e),this._internals=this.attachInternals(),this._lastFocusedItem=null,this.#e=`single`,this.#t=!1,this._onItemToggle=e=>{this._selectItem(e.item,e.checked)},this._onClick=e=>{let t=this._itemFromEvent(e);if(!t||t.disabled)return;let n=e.composedPath();if(n.some(e=>e instanceof HTMLInputElement&&e.type===`checkbox`))return;let r=new Set([`BUTTON`,`A`,`INPUT`,`SELECT`,`TEXTAREA`]),i=new Set([`button`,`link`,`menuitem`,`menuitemcheckbox`]);if(n.some(e=>{if(!(e instanceof HTMLElement)||e===t||e.getAttribute?.(`part`)===`expand-button`||e instanceof HTMLInputElement&&e.type===`checkbox`)return!1;if(r.has(e.tagName))return!0;let n=e.getAttribute?.(`role`);return n!==null&&i.has(n)}))return;let a=n.some(e=>e instanceof HTMLElement&&e.getAttribute?.(`part`)===`expand-button`);if(this._focusItem(t),a)t.toggle();else switch(this.selection){case`single`:this._setSingleSelection(t),t.isLeaf()||t.toggle();break;case`leaf`:t.isLeaf()?this._setSingleSelection(t):t.toggle();break;case`multiple`:this._selectItem(t,!t.selected);break;case`none`:t.toggle()}},this._onFocusIn=e=>{let t=e.target;if(t instanceof HTMLElement){let e=t.closest(a(`tree-item`));e&&(this._lastFocusedItem=e)}},this._onKeyDown=e=>{let t=this._lastFocusedItem??this._visibleItems()[0];if(!t)return;let n=this._visibleItems(),r=n.indexOf(t);switch(e.key){case`ArrowDown`:{e.preventDefault();let t=n[Math.min(r+1,n.length-1)];t&&this._focusItem(t);break}case`ArrowUp`:{e.preventDefault();let t=n[Math.max(r-1,0)];t&&this._focusItem(t);break}case`ArrowRight`:if(e.preventDefault(),!t.isLeaf()&&!t.expanded)t.expanded=!0;else if(t.expanded){let e=t.getChildrenItems()[0];e&&this._focusItem(e)}break;case`ArrowLeft`:if(e.preventDefault(),t.expanded&&!t.isLeaf())t.expanded=!1;else{let e=t.parentElement?.closest(a(`tree-item`));e&&this._focusItem(e)}break;case`Home`:e.preventDefault(),n[0]&&this._focusItem(n[0]);break;case`End`:{e.preventDefault();let t=n[n.length-1];t&&this._focusItem(t);break}case`Enter`:case` `:e.preventDefault(),this._handleRowActivate(t);break;case`*`:{e.preventDefault();let n=(t.parentElement?Array.from(t.parentElement.children):[]).filter(e=>e.tagName===a(`tree-item`).toUpperCase());for(let e of n)e.isLeaf()||(e.expanded=!0);break}}}}static{this.styles=[o,s]}#e;get selection(){return this.#e}set selection(e){this.#e=e}#t;get independent(){return this.#t}set independent(e){this.#t=e}connectedCallback(){super.connectedCallback(),this._internals.role=`tree`,this.hasAttribute(`role`)||this.setAttribute(`role`,`tree`),this._mutationObserver=new MutationObserver(()=>this._syncAll()),this._mutationObserver.observe(this,{childList:!0,subtree:!0}),this.addEventListener(`selection-toggle`,this._onItemToggle),queueMicrotask(()=>this._syncAll())}disconnectedCallback(){super.disconnectedCallback(),this._mutationObserver?.disconnect(),this.removeEventListener(`selection-toggle`,this._onItemToggle)}updated(e){if(e.has(`selection`)){let e=this.selection===`multiple`?`true`:`false`;this._internals.ariaMultiSelectable=e,this.setAttribute(`aria-multiselectable`,e)}(e.has(`selection`)||e.has(`independent`))&&this._syncAll()}getAllItems({includeDisabled:e=!0}={}){let t=a(`tree-item`);return Array.from(this.querySelectorAll(t)).filter(t=>e||!t.disabled)}getSelection(){return this.getAllItems().filter(e=>e.selected)}expandAll(){for(let e of this.getAllItems())e.isLeaf()||(e.expanded=!0)}collapseAll(){for(let e of this.getAllItems())e.expanded=!1}_syncAll(){customElements.upgrade(this);let e=a(`tree-item`),t=this._rootItems();if(t.some(e=>typeof e.getChildrenItems!=`function`)){customElements.whenDefined(e).then(()=>this._syncAll());return}let n=this.selection===`multiple`;this._syncLevel(t,0,n),this._updateBranchStates(),this._ensureTabStop()}_syncLevel(e,t,n){let r=e.length;e.forEach((e,i)=>{e.depth=t,e.showCheckbox=n&&this._canShowCheckboxOn(e),e.setPosition(t+1,i+1,r),this._syncLevel(e.getChildrenItems(),t+1,n)})}_canShowCheckboxOn(e){return this.selection===`multiple`}_rootItems(){let e=a(`tree-item`).toUpperCase();return Array.from(this.children).filter(t=>t.tagName===e)}_ensureTabStop(){let e=this._visibleItems();if(e.length!==0&&!e.some(e=>e.tabIndex===0)){for(let t of e)t.tabIndex=-1;e[0].tabIndex=0}}_visibleItems(){let e=[],t=n=>{for(let r of n)e.push(r),r.expanded&&t(r.getChildrenItems())};return t(this._rootItems()),e}_handleRowActivate(e){if(!e.disabled)switch(this.selection){case`single`:this._setSingleSelection(e),e.isLeaf()||e.toggle();break;case`leaf`:e.isLeaf()?this._setSingleSelection(e):e.toggle();break;case`multiple`:this._selectItem(e,!e.selected);break;case`none`:e.toggle()}}_setSingleSelection(e){for(let t of this.getAllItems())t!==e&&t.selected&&(t.selected=!1);e.selected=!0,this._emitSelectionChange()}_selectItem(e,t){e.disabled||(e.selected=t,this._cascades&&(this._setSubtreeSelection(e,t),e._setCascadeIndeterminate(!1)),this._updateBranchStates(),this._emitSelectionChange())}_setSubtreeSelection(e,t){for(let n of e.getChildrenItems())n.disabled||(n.selected=t,n._setCascadeIndeterminate(!1),this._setSubtreeSelection(n,t))}get _cascades(){return this.selection===`multiple`&&!this.independent}_updateBranchStates(){if(!this._cascades){for(let e of this.getAllItems())e._cascadeIndeterminate&&e._setCascadeIndeterminate(!1);return}let e=t=>{let n=t.getChildrenItems({includeDisabled:!1});if(n.length===0)return t._setCascadeIndeterminate(!1),{all:t.selected,any:t.selected};let r=!0,i=!1;for(let t of n){let n=e(t);n.all||(r=!1),n.any&&(i=!0)}return t.selected=r,t._setCascadeIndeterminate(!r&&i),{all:r&&(t.getChildrenItems().length>0?r:t.selected),any:i}};for(let t of this._rootItems())e(t)}_emitSelectionChange(){this.dispatchEvent(new c(this.getSelection()))}_itemFromEvent(e){let t=a(`tree-item`),n=e.composedPath();for(let e of n)if(e instanceof HTMLElement&&e.matches?.(t))return e;return null}_focusItem(e){let t=this._visibleItems();for(let e of t)e.tabIndex=-1;e.tabIndex=0,e.focus(),this._lastFocusedItem=e}render(){return i`
2
2
  <div
3
3
  class="tree"
4
4
  part="base"
@@ -1 +1 @@
1
- {"version":3,"file":"tree.js","names":["unsafeCSS","rawStyles","hostStyles","_decorate","property","r","n","__decorate","b","host_styles_default","styles","SelectionChangeEvent","selection","Tree","LuxenElement","_args","#_selection_accessor_storage","#_independent_accessor_storage","event","item","path","INTERACTIVE_TAGS","INTERACTIVE_ROLES","role","onExpand","target","tagName","current","visible","index","next","prev","first","parent","last","siblings","el","sib","value","changed","multiselectable","tag","includeDisabled","i","itemTag","roots","showCheckbox","setSize","items","depth","out","walk","child","recompute","children","all","any","state","root","node"],"sources":["../../../src/html/elements/tree/tree.css?inline","../../../src/html/elements/tree/tree.ts"],"sourcesContent":[":host {\n --indent-size: 1rem;\n --indent-guide-width: 1px;\n --indent-guide-style: solid;\n --indent-guide-color: var(--l-color-border-interactive);\n --row-height: 1.75rem;\n --row-padding-inline: 0.25rem;\n --chevron-size: 1.125rem;\n --item-gap: 0.375rem;\n\n display: block;\n color: var(--l-color-text-primary);\n font-family: inherit;\n line-height: 1.5;\n}\n\n.tree {\n display: block;\n outline: none;\n}\n\n.tree:focus-visible {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 2px;\n border-radius: 0.375rem;\n}\n","import { html, unsafeCSS, type PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { tagName } from '../../registry.js';\nimport type { TreeItem } from '../tree-item/tree-item.js';\nimport type { TreeItemSelectionToggleEvent } from '../tree-item/tree-item.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './tree.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type TreeSelection = 'single' | 'multiple' | 'leaf' | 'none';\n\n/** Fired when the tree's selected items change. */\nexport class SelectionChangeEvent extends Event {\n readonly selection: TreeItem[];\n constructor(selection: TreeItem[]) {\n super('selection-change', { bubbles: false, composed: false, cancelable: false });\n this.selection = selection;\n }\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n 'selection-change': SelectionChangeEvent;\n }\n}\n\n/**\n * A hierarchical tree view composed of `<l-tree-item>` children.\n *\n * The host carries `role=\"tree\"`, so give it an accessible name with\n * `aria-label` or `aria-labelledby` (e.g. `<l-tree aria-label=\"Files\">`).\n *\n * @slot - One or more `l-tree-item` elements.\n *\n * @csspart base - The root tree container.\n *\n * @cssproperty --indent-size - Horizontal indent per depth level. Default `1rem`.\n * @cssproperty --indent-guide-width - Thickness of the vertical guide line between a parent and its children. Default `1px`. Set to `0` to hide guides.\n * @cssproperty --indent-guide-style - Line style of the guide (`solid`, `dashed`, `dotted`, `double`…). Default `solid`.\n * @cssproperty --indent-guide-color - Color of the guide line.\n * @cssproperty --row-height - Minimum row height. Default `1.75rem`.\n * @cssproperty --row-padding-inline - Inner inline padding of the row; also drives the content slot left indent and the indent guide column. Default `0.25rem`.\n * @cssproperty --chevron-size - Size of the expand/collapse chevron box. Default `1.125rem`.\n * @cssproperty --item-gap - Horizontal gap between chevron, prefix, label and suffix on the row; also drives the content slot left indent. Default `0.375rem`.\n *\n * @event selection-change - Fired when the selected items change. Properties: `selection: TreeItem[]`.\n *\n * @customElement l-tree\n */\nexport class Tree extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n private _internals = this.attachInternals();\n private _mutationObserver?: MutationObserver;\n private _lastFocusedItem: TreeItem | null = null;\n\n /**\n * Selection behaviour:\n * - `single` (default): at most one item selected via `aria-selected`.\n * - `multiple`: any number of items selected. Checkboxes are rendered.\n * - `leaf`: only leaf items can be selected (single). Branches just toggle.\n * - `none`: purely navigable, no selection state.\n */\n @property({ reflect: true })\n accessor selection: TreeSelection = 'single';\n\n /**\n * When set with `selection=\"multiple\"`, parent and children selection are decoupled:\n * toggling a parent does NOT toggle its descendants and vice versa.\n * Without it, selection cascades both ways and branches may become indeterminate.\n *\n * In independent mode the tree never writes `indeterminate`: the consumer owns\n * it (e.g. to show a partial state computed from rows not in the DOM). On\n * leaving the cascade, the tree clears only the flags its cascade wrote.\n */\n @property({ type: Boolean, reflect: true })\n accessor independent = false;\n\n override connectedCallback() {\n super.connectedCallback();\n this._internals.role = 'tree';\n // Mirror the role to a DOM attribute too. The ElementInternals role alone is\n // not `[role]`-selectable (CSS, querySelector, Cypress/Playwright CSS), which\n // silently breaks consumers migrating from libraries that expose an attribute\n // role. Respect an author-provided role if one is already set.\n if (!this.hasAttribute('role')) this.setAttribute('role', 'tree');\n this._mutationObserver = new MutationObserver(() => this._syncAll());\n this._mutationObserver.observe(this, { childList: true, subtree: true });\n this.addEventListener('selection-toggle', this._onItemToggle as EventListener);\n\n // Defer sync to let light DOM upgrade.\n queueMicrotask(() => this._syncAll());\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._mutationObserver?.disconnect();\n this.removeEventListener('selection-toggle', this._onItemToggle as EventListener);\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('selection')) {\n // Mirror to ElementInternals (a11y tree) and a content attribute, so\n // `[aria-multiselectable]` selectors keep matching — see tree-item `_aria`.\n const multiselectable = this.selection === 'multiple' ? 'true' : 'false';\n this._internals.ariaMultiSelectable = multiselectable;\n this.setAttribute('aria-multiselectable', multiselectable);\n }\n if (changed.has('selection') || changed.has('independent')) {\n this._syncAll();\n }\n }\n\n // --- Public API ---\n\n /** Returns all items in document (flat) order, including nested ones. */\n getAllItems({ includeDisabled = true } = {}): TreeItem[] {\n const tag = tagName('tree-item');\n return Array.from(this.querySelectorAll<TreeItem>(tag)).filter(\n (item) => includeDisabled || !item.disabled,\n );\n }\n\n /** Returns currently selected items. */\n getSelection(): TreeItem[] {\n return this.getAllItems().filter((i) => i.selected);\n }\n\n /** Expands every item that has children. */\n expandAll() {\n for (const item of this.getAllItems()) {\n if (!item.isLeaf()) item.expanded = true;\n }\n }\n\n /** Collapses every item. */\n collapseAll() {\n for (const item of this.getAllItems()) {\n item.expanded = false;\n }\n }\n\n // --- Sync / ARIA / depth / checkbox visibility ---\n\n private _syncAll() {\n // `_syncAll()` may run from `updated()` before `<l-tree-item>` is registered\n // (e.g. when the tree module is imported before tree-item, or in async chunks).\n // Force-upgrade any pending custom elements in our subtree, then bail and retry\n // once the registration completes if any item is still un-upgraded.\n customElements.upgrade(this);\n const itemTag = tagName('tree-item');\n const roots = this._rootItems();\n if (roots.some((r) => typeof r.getChildrenItems !== 'function')) {\n void customElements.whenDefined(itemTag).then(() => this._syncAll());\n return;\n }\n\n const showCheckbox = this.selection === 'multiple';\n this._syncLevel(roots, 0, showCheckbox);\n this._updateBranchStates();\n // Ensure at least one item is tabbable.\n this._ensureTabStop();\n }\n\n /**\n * Sync depth, checkbox visibility and ARIA position for a sibling group, then\n * recurse. `aria-level`/`aria-setsize`/`aria-posinset` let screen readers\n * announce \"level N, M of K\" — valuable here because `lazy` items mean the\n * full set isn't always in the DOM (see WAI-ARIA Tree View pattern).\n */\n private _syncLevel(items: TreeItem[], depth: number, showCheckbox: boolean) {\n const setSize = items.length;\n items.forEach((item, index) => {\n item.depth = depth;\n item.showCheckbox = showCheckbox && this._canShowCheckboxOn(item);\n item.setPosition(depth + 1, index + 1, setSize);\n this._syncLevel(item.getChildrenItems(), depth + 1, showCheckbox);\n });\n }\n\n private _canShowCheckboxOn(_item: TreeItem): boolean {\n if (this.selection !== 'multiple') return false;\n // In cascade mode, branches get a checkbox too so you can bulk-toggle children.\n // In leaf-only selection, hidden here because selection !== 'multiple'.\n return true;\n }\n\n private _rootItems(): TreeItem[] {\n const tag = tagName('tree-item').toUpperCase();\n return (Array.from(this.children) as TreeItem[]).filter((el) => el.tagName === tag);\n }\n\n private _ensureTabStop() {\n const items = this._visibleItems();\n if (items.length === 0) return;\n const hasTabStop = items.some((i) => i.tabIndex === 0);\n if (!hasTabStop) {\n for (const i of items) i.tabIndex = -1;\n items[0].tabIndex = 0;\n }\n }\n\n /** Items currently visible (parent chain all expanded). */\n private _visibleItems(): TreeItem[] {\n const out: TreeItem[] = [];\n const walk = (items: TreeItem[]) => {\n for (const i of items) {\n out.push(i);\n if (i.expanded) walk(i.getChildrenItems());\n }\n };\n walk(this._rootItems());\n return out;\n }\n\n // --- Selection handling ---\n\n private _onItemToggle = (event: TreeItemSelectionToggleEvent) => {\n this._selectItem(event.item, event.checked);\n };\n\n private _handleRowActivate(item: TreeItem) {\n if (item.disabled) return;\n\n switch (this.selection) {\n case 'single':\n this._setSingleSelection(item);\n // Mirror the row-click behaviour: activating a branch also toggles it,\n // so keyboard users expand lazy branches (and trigger their fetch) too.\n if (!item.isLeaf()) item.toggle();\n break;\n case 'leaf':\n if (item.isLeaf()) this._setSingleSelection(item);\n else item.toggle();\n break;\n case 'multiple':\n this._selectItem(item, !item.selected);\n break;\n case 'none':\n item.toggle();\n break;\n }\n }\n\n private _setSingleSelection(item: TreeItem) {\n for (const i of this.getAllItems()) {\n if (i !== item && i.selected) i.selected = false;\n }\n item.selected = true;\n this._emitSelectionChange();\n }\n\n private _selectItem(item: TreeItem, value: boolean) {\n if (item.disabled) return;\n item.selected = value;\n\n if (this._cascades) {\n // Cascade DOWN: toggling a branch toggles all descendants.\n this._setSubtreeSelection(item, value);\n item._setCascadeIndeterminate(false);\n }\n\n this._updateBranchStates();\n this._emitSelectionChange();\n }\n\n private _setSubtreeSelection(item: TreeItem, value: boolean) {\n for (const child of item.getChildrenItems()) {\n if (child.disabled) continue;\n child.selected = value;\n child._setCascadeIndeterminate(false);\n this._setSubtreeSelection(child, value);\n }\n }\n\n /** Whether selection cascades between parents and children. */\n private get _cascades(): boolean {\n return this.selection === 'multiple' && !this.independent;\n }\n\n /** Propagate child state UP to parents (indeterminate / auto-checked). */\n private _updateBranchStates() {\n if (!this._cascades) {\n // Outside the cascade the tree does not own `indeterminate` (the consumer\n // drives it in independent mode): only clear the flags the cascade wrote,\n // so a value the consumer set — even in the same tick as the mode switch —\n // survives.\n for (const i of this.getAllItems()) {\n if (i._cascadeIndeterminate) i._setCascadeIndeterminate(false);\n }\n return;\n }\n\n const recompute = (item: TreeItem): { all: boolean; any: boolean } => {\n const children = item.getChildrenItems({ includeDisabled: false });\n if (children.length === 0) {\n // A leaf (or a branch with only disabled children) has no partial state;\n // drop any flag the consumer left from independent mode.\n item._setCascadeIndeterminate(false);\n return { all: item.selected, any: item.selected };\n }\n\n let all = true;\n let any = false;\n for (const child of children) {\n const state = recompute(child);\n if (!state.all) all = false;\n if (state.any) any = true;\n }\n\n item.selected = all;\n item._setCascadeIndeterminate(!all && any);\n return { all: all && (item.getChildrenItems().length > 0 ? all : item.selected), any };\n };\n\n for (const root of this._rootItems()) recompute(root);\n }\n\n private _emitSelectionChange() {\n this.dispatchEvent(new SelectionChangeEvent(this.getSelection()));\n }\n\n // --- Keyboard / focus ---\n\n private _onClick = (event: MouseEvent) => {\n const item = this._itemFromEvent(event);\n if (!item || item.disabled) return;\n\n const path = event.composedPath();\n const onCheckbox = path.some((n) => n instanceof HTMLInputElement && n.type === 'checkbox');\n if (onCheckbox) return; // handled via change event\n\n // Clicks on consumer-provided interactive elements (buttons, links, form\n // controls, menu items…) must not toggle the row — the consumer owns that\n // interaction. Works regardless of which slot the element was placed in.\n const INTERACTIVE_TAGS = new Set(['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA']);\n const INTERACTIVE_ROLES = new Set(['button', 'link', 'menuitem', 'menuitemcheckbox']);\n const onInteractive = path.some((n) => {\n if (!(n instanceof HTMLElement) || n === item) return false;\n if (n.getAttribute?.('part') === 'expand-button') return false;\n if (n instanceof HTMLInputElement && n.type === 'checkbox') return false;\n if (INTERACTIVE_TAGS.has(n.tagName)) return true;\n const role = n.getAttribute?.('role');\n return role !== null && INTERACTIVE_ROLES.has(role);\n });\n if (onInteractive) return;\n\n const onExpand = path.some(\n (n) => n instanceof HTMLElement && n.getAttribute?.('part') === 'expand-button',\n );\n\n this._focusItem(item);\n\n if (onExpand) {\n item.toggle();\n return;\n }\n\n // Row click (label area): mode-dependent behaviour.\n switch (this.selection) {\n case 'single':\n this._setSingleSelection(item);\n if (!item.isLeaf()) item.toggle();\n break;\n case 'leaf':\n if (item.isLeaf()) this._setSingleSelection(item);\n else item.toggle();\n break;\n case 'multiple':\n // The whole row acts like a <label> for the checkbox: clicking anywhere\n // on it toggles selection. Use the chevron to expand/collapse branches.\n this._selectItem(item, !item.selected);\n break;\n case 'none':\n item.toggle();\n break;\n }\n };\n\n private _itemFromEvent(event: Event): TreeItem | null {\n const tag = tagName('tree-item');\n const path = event.composedPath();\n for (const node of path) {\n if (node instanceof HTMLElement && node.matches?.(tag)) {\n return node as TreeItem;\n }\n }\n return null;\n }\n\n private _focusItem(item: TreeItem) {\n const visible = this._visibleItems();\n for (const i of visible) i.tabIndex = -1;\n item.tabIndex = 0;\n item.focus();\n this._lastFocusedItem = item;\n }\n\n private _onFocusIn = (event: FocusEvent) => {\n const target = event.target;\n if (target instanceof HTMLElement) {\n const item = target.closest<TreeItem>(tagName('tree-item'));\n if (item) this._lastFocusedItem = item;\n }\n };\n\n private _onKeyDown = (event: KeyboardEvent) => {\n const current = this._lastFocusedItem ?? this._visibleItems()[0];\n if (!current) return;\n\n const visible = this._visibleItems();\n const index = visible.indexOf(current);\n\n switch (event.key) {\n case 'ArrowDown': {\n event.preventDefault();\n const next = visible[Math.min(index + 1, visible.length - 1)];\n if (next) this._focusItem(next);\n break;\n }\n case 'ArrowUp': {\n event.preventDefault();\n const prev = visible[Math.max(index - 1, 0)];\n if (prev) this._focusItem(prev);\n break;\n }\n case 'ArrowRight': {\n event.preventDefault();\n if (!current.isLeaf() && !current.expanded) {\n current.expanded = true;\n } else if (current.expanded) {\n const first = current.getChildrenItems()[0];\n if (first) this._focusItem(first);\n }\n break;\n }\n case 'ArrowLeft': {\n event.preventDefault();\n if (current.expanded && !current.isLeaf()) {\n current.expanded = false;\n } else {\n const parent = current.parentElement?.closest(tagName('tree-item')) as TreeItem | null;\n if (parent) this._focusItem(parent);\n }\n break;\n }\n case 'Home': {\n event.preventDefault();\n if (visible[0]) this._focusItem(visible[0]);\n break;\n }\n case 'End': {\n event.preventDefault();\n const last = visible[visible.length - 1];\n if (last) this._focusItem(last);\n break;\n }\n case 'Enter':\n case ' ': {\n event.preventDefault();\n this._handleRowActivate(current);\n break;\n }\n case '*': {\n event.preventDefault();\n // Expand all siblings of the current item.\n const siblings = (\n current.parentElement ? (Array.from(current.parentElement.children) as TreeItem[]) : []\n ).filter((el) => el.tagName === tagName('tree-item').toUpperCase());\n for (const sib of siblings) {\n if (!sib.isLeaf()) sib.expanded = true;\n }\n break;\n }\n }\n };\n\n override render() {\n return html`\n <div\n class=\"tree\"\n part=\"base\"\n @click=${this._onClick}\n @keydown=${this._onKeyDown}\n @focusin=${this._onFocusIn}\n >\n <slot></slot>\n </div>\n `;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tree.js","names":["unsafeCSS","rawStyles","hostStyles","_decorate","property","r","n","__decorate","b","host_styles_default","styles","SelectionChangeEvent","selection","Tree","LuxenElement","_args","#_selection_accessor_storage","#_independent_accessor_storage","event","item","path","INTERACTIVE_TAGS","INTERACTIVE_ROLES","role","onExpand","target","tagName","current","visible","index","next","prev","first","parent","last","siblings","el","sib","value","changed","multiselectable","tag","includeDisabled","i","itemTag","roots","showCheckbox","setSize","items","depth","out","walk","child","recompute","children","all","any","state","root","node"],"sources":["../../../src/html/elements/tree/tree.css?inline","../../../src/html/elements/tree/tree.ts"],"sourcesContent":[":host {\n --indent-size: 1rem;\n --indent-guide-width: 1px;\n --indent-guide-style: solid;\n --indent-guide-color: var(--l-color-border-interactive);\n --row-height: 1.75rem;\n --row-padding-inline: 0.25rem;\n --chevron-size: 1.125rem;\n --item-gap: 0.375rem;\n\n display: block;\n color: var(--l-color-text-primary);\n font-family: inherit;\n line-height: 1.5;\n}\n\n.tree {\n display: block;\n outline: none;\n}\n\n.tree:focus-visible {\n outline: var(--l-focus-ring-width) solid var(--l-focus-ring-color);\n outline-offset: var(--l-focus-ring-offset);\n border-radius: var(--l-radius-md);\n}\n","import { html, unsafeCSS, type PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { tagName } from '../../registry.js';\nimport type { TreeItem } from '../tree-item/tree-item.js';\nimport type { TreeItemSelectionToggleEvent } from '../tree-item/tree-item.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './tree.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type TreeSelection = 'single' | 'multiple' | 'leaf' | 'none';\n\n/** Fired when the tree's selected items change. */\nexport class SelectionChangeEvent extends Event {\n readonly selection: TreeItem[];\n constructor(selection: TreeItem[]) {\n super('selection-change', { bubbles: false, composed: false, cancelable: false });\n this.selection = selection;\n }\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n 'selection-change': SelectionChangeEvent;\n }\n}\n\n/**\n * A hierarchical tree view composed of `<l-tree-item>` children.\n *\n * The host carries `role=\"tree\"`, so give it an accessible name with\n * `aria-label` or `aria-labelledby` (e.g. `<l-tree aria-label=\"Files\">`).\n *\n * @slot - One or more `l-tree-item` elements.\n *\n * @csspart base - The root tree container.\n *\n * @cssproperty --indent-size - Horizontal indent per depth level. Default `1rem`.\n * @cssproperty --indent-guide-width - Thickness of the vertical guide line between a parent and its children. Default `1px`. Set to `0` to hide guides.\n * @cssproperty --indent-guide-style - Line style of the guide (`solid`, `dashed`, `dotted`, `double`…). Default `solid`.\n * @cssproperty --indent-guide-color - Color of the guide line.\n * @cssproperty --row-height - Minimum row height. Default `1.75rem`.\n * @cssproperty --row-padding-inline - Inner inline padding of the row; also drives the content slot left indent and the indent guide column. Default `0.25rem`.\n * @cssproperty --chevron-size - Size of the expand/collapse chevron box. Default `1.125rem`.\n * @cssproperty --item-gap - Horizontal gap between chevron, prefix, label and suffix on the row; also drives the content slot left indent. Default `0.375rem`.\n *\n * @event selection-change - Fired when the selected items change. Properties: `selection: TreeItem[]`.\n *\n * @customElement l-tree\n */\nexport class Tree extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n private _internals = this.attachInternals();\n private _mutationObserver?: MutationObserver;\n private _lastFocusedItem: TreeItem | null = null;\n\n /**\n * Selection behaviour:\n * - `single` (default): at most one item selected via `aria-selected`.\n * - `multiple`: any number of items selected. Checkboxes are rendered.\n * - `leaf`: only leaf items can be selected (single). Branches just toggle.\n * - `none`: purely navigable, no selection state.\n */\n @property({ reflect: true })\n accessor selection: TreeSelection = 'single';\n\n /**\n * When set with `selection=\"multiple\"`, parent and children selection are decoupled:\n * toggling a parent does NOT toggle its descendants and vice versa.\n * Without it, selection cascades both ways and branches may become indeterminate.\n *\n * In independent mode the tree never writes `indeterminate`: the consumer owns\n * it (e.g. to show a partial state computed from rows not in the DOM). On\n * leaving the cascade, the tree clears only the flags its cascade wrote.\n */\n @property({ type: Boolean, reflect: true })\n accessor independent = false;\n\n override connectedCallback() {\n super.connectedCallback();\n this._internals.role = 'tree';\n // Mirror the role to a DOM attribute too. The ElementInternals role alone is\n // not `[role]`-selectable (CSS, querySelector, Cypress/Playwright CSS), which\n // silently breaks consumers migrating from libraries that expose an attribute\n // role. Respect an author-provided role if one is already set.\n if (!this.hasAttribute('role')) this.setAttribute('role', 'tree');\n this._mutationObserver = new MutationObserver(() => this._syncAll());\n this._mutationObserver.observe(this, { childList: true, subtree: true });\n this.addEventListener('selection-toggle', this._onItemToggle as EventListener);\n\n // Defer sync to let light DOM upgrade.\n queueMicrotask(() => this._syncAll());\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._mutationObserver?.disconnect();\n this.removeEventListener('selection-toggle', this._onItemToggle as EventListener);\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('selection')) {\n // Mirror to ElementInternals (a11y tree) and a content attribute, so\n // `[aria-multiselectable]` selectors keep matching — see tree-item `_aria`.\n const multiselectable = this.selection === 'multiple' ? 'true' : 'false';\n this._internals.ariaMultiSelectable = multiselectable;\n this.setAttribute('aria-multiselectable', multiselectable);\n }\n if (changed.has('selection') || changed.has('independent')) {\n this._syncAll();\n }\n }\n\n // --- Public API ---\n\n /** Returns all items in document (flat) order, including nested ones. */\n getAllItems({ includeDisabled = true } = {}): TreeItem[] {\n const tag = tagName('tree-item');\n return Array.from(this.querySelectorAll<TreeItem>(tag)).filter(\n (item) => includeDisabled || !item.disabled,\n );\n }\n\n /** Returns currently selected items. */\n getSelection(): TreeItem[] {\n return this.getAllItems().filter((i) => i.selected);\n }\n\n /** Expands every item that has children. */\n expandAll() {\n for (const item of this.getAllItems()) {\n if (!item.isLeaf()) item.expanded = true;\n }\n }\n\n /** Collapses every item. */\n collapseAll() {\n for (const item of this.getAllItems()) {\n item.expanded = false;\n }\n }\n\n // --- Sync / ARIA / depth / checkbox visibility ---\n\n private _syncAll() {\n // `_syncAll()` may run from `updated()` before `<l-tree-item>` is registered\n // (e.g. when the tree module is imported before tree-item, or in async chunks).\n // Force-upgrade any pending custom elements in our subtree, then bail and retry\n // once the registration completes if any item is still un-upgraded.\n customElements.upgrade(this);\n const itemTag = tagName('tree-item');\n const roots = this._rootItems();\n if (roots.some((r) => typeof r.getChildrenItems !== 'function')) {\n void customElements.whenDefined(itemTag).then(() => this._syncAll());\n return;\n }\n\n const showCheckbox = this.selection === 'multiple';\n this._syncLevel(roots, 0, showCheckbox);\n this._updateBranchStates();\n // Ensure at least one item is tabbable.\n this._ensureTabStop();\n }\n\n /**\n * Sync depth, checkbox visibility and ARIA position for a sibling group, then\n * recurse. `aria-level`/`aria-setsize`/`aria-posinset` let screen readers\n * announce \"level N, M of K\" — valuable here because `lazy` items mean the\n * full set isn't always in the DOM (see WAI-ARIA Tree View pattern).\n */\n private _syncLevel(items: TreeItem[], depth: number, showCheckbox: boolean) {\n const setSize = items.length;\n items.forEach((item, index) => {\n item.depth = depth;\n item.showCheckbox = showCheckbox && this._canShowCheckboxOn(item);\n item.setPosition(depth + 1, index + 1, setSize);\n this._syncLevel(item.getChildrenItems(), depth + 1, showCheckbox);\n });\n }\n\n private _canShowCheckboxOn(_item: TreeItem): boolean {\n if (this.selection !== 'multiple') return false;\n // In cascade mode, branches get a checkbox too so you can bulk-toggle children.\n // In leaf-only selection, hidden here because selection !== 'multiple'.\n return true;\n }\n\n private _rootItems(): TreeItem[] {\n const tag = tagName('tree-item').toUpperCase();\n return (Array.from(this.children) as TreeItem[]).filter((el) => el.tagName === tag);\n }\n\n private _ensureTabStop() {\n const items = this._visibleItems();\n if (items.length === 0) return;\n const hasTabStop = items.some((i) => i.tabIndex === 0);\n if (!hasTabStop) {\n for (const i of items) i.tabIndex = -1;\n items[0].tabIndex = 0;\n }\n }\n\n /** Items currently visible (parent chain all expanded). */\n private _visibleItems(): TreeItem[] {\n const out: TreeItem[] = [];\n const walk = (items: TreeItem[]) => {\n for (const i of items) {\n out.push(i);\n if (i.expanded) walk(i.getChildrenItems());\n }\n };\n walk(this._rootItems());\n return out;\n }\n\n // --- Selection handling ---\n\n private _onItemToggle = (event: TreeItemSelectionToggleEvent) => {\n this._selectItem(event.item, event.checked);\n };\n\n private _handleRowActivate(item: TreeItem) {\n if (item.disabled) return;\n\n switch (this.selection) {\n case 'single':\n this._setSingleSelection(item);\n // Mirror the row-click behaviour: activating a branch also toggles it,\n // so keyboard users expand lazy branches (and trigger their fetch) too.\n if (!item.isLeaf()) item.toggle();\n break;\n case 'leaf':\n if (item.isLeaf()) this._setSingleSelection(item);\n else item.toggle();\n break;\n case 'multiple':\n this._selectItem(item, !item.selected);\n break;\n case 'none':\n item.toggle();\n break;\n }\n }\n\n private _setSingleSelection(item: TreeItem) {\n for (const i of this.getAllItems()) {\n if (i !== item && i.selected) i.selected = false;\n }\n item.selected = true;\n this._emitSelectionChange();\n }\n\n private _selectItem(item: TreeItem, value: boolean) {\n if (item.disabled) return;\n item.selected = value;\n\n if (this._cascades) {\n // Cascade DOWN: toggling a branch toggles all descendants.\n this._setSubtreeSelection(item, value);\n item._setCascadeIndeterminate(false);\n }\n\n this._updateBranchStates();\n this._emitSelectionChange();\n }\n\n private _setSubtreeSelection(item: TreeItem, value: boolean) {\n for (const child of item.getChildrenItems()) {\n if (child.disabled) continue;\n child.selected = value;\n child._setCascadeIndeterminate(false);\n this._setSubtreeSelection(child, value);\n }\n }\n\n /** Whether selection cascades between parents and children. */\n private get _cascades(): boolean {\n return this.selection === 'multiple' && !this.independent;\n }\n\n /** Propagate child state UP to parents (indeterminate / auto-checked). */\n private _updateBranchStates() {\n if (!this._cascades) {\n // Outside the cascade the tree does not own `indeterminate` (the consumer\n // drives it in independent mode): only clear the flags the cascade wrote,\n // so a value the consumer set — even in the same tick as the mode switch —\n // survives.\n for (const i of this.getAllItems()) {\n if (i._cascadeIndeterminate) i._setCascadeIndeterminate(false);\n }\n return;\n }\n\n const recompute = (item: TreeItem): { all: boolean; any: boolean } => {\n const children = item.getChildrenItems({ includeDisabled: false });\n if (children.length === 0) {\n // A leaf (or a branch with only disabled children) has no partial state;\n // drop any flag the consumer left from independent mode.\n item._setCascadeIndeterminate(false);\n return { all: item.selected, any: item.selected };\n }\n\n let all = true;\n let any = false;\n for (const child of children) {\n const state = recompute(child);\n if (!state.all) all = false;\n if (state.any) any = true;\n }\n\n item.selected = all;\n item._setCascadeIndeterminate(!all && any);\n return { all: all && (item.getChildrenItems().length > 0 ? all : item.selected), any };\n };\n\n for (const root of this._rootItems()) recompute(root);\n }\n\n private _emitSelectionChange() {\n this.dispatchEvent(new SelectionChangeEvent(this.getSelection()));\n }\n\n // --- Keyboard / focus ---\n\n private _onClick = (event: MouseEvent) => {\n const item = this._itemFromEvent(event);\n if (!item || item.disabled) return;\n\n const path = event.composedPath();\n const onCheckbox = path.some((n) => n instanceof HTMLInputElement && n.type === 'checkbox');\n if (onCheckbox) return; // handled via change event\n\n // Clicks on consumer-provided interactive elements (buttons, links, form\n // controls, menu items…) must not toggle the row — the consumer owns that\n // interaction. Works regardless of which slot the element was placed in.\n const INTERACTIVE_TAGS = new Set(['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA']);\n const INTERACTIVE_ROLES = new Set(['button', 'link', 'menuitem', 'menuitemcheckbox']);\n const onInteractive = path.some((n) => {\n if (!(n instanceof HTMLElement) || n === item) return false;\n if (n.getAttribute?.('part') === 'expand-button') return false;\n if (n instanceof HTMLInputElement && n.type === 'checkbox') return false;\n if (INTERACTIVE_TAGS.has(n.tagName)) return true;\n const role = n.getAttribute?.('role');\n return role !== null && INTERACTIVE_ROLES.has(role);\n });\n if (onInteractive) return;\n\n const onExpand = path.some(\n (n) => n instanceof HTMLElement && n.getAttribute?.('part') === 'expand-button',\n );\n\n this._focusItem(item);\n\n if (onExpand) {\n item.toggle();\n return;\n }\n\n // Row click (label area): mode-dependent behaviour.\n switch (this.selection) {\n case 'single':\n this._setSingleSelection(item);\n if (!item.isLeaf()) item.toggle();\n break;\n case 'leaf':\n if (item.isLeaf()) this._setSingleSelection(item);\n else item.toggle();\n break;\n case 'multiple':\n // The whole row acts like a <label> for the checkbox: clicking anywhere\n // on it toggles selection. Use the chevron to expand/collapse branches.\n this._selectItem(item, !item.selected);\n break;\n case 'none':\n item.toggle();\n break;\n }\n };\n\n private _itemFromEvent(event: Event): TreeItem | null {\n const tag = tagName('tree-item');\n const path = event.composedPath();\n for (const node of path) {\n if (node instanceof HTMLElement && node.matches?.(tag)) {\n return node as TreeItem;\n }\n }\n return null;\n }\n\n private _focusItem(item: TreeItem) {\n const visible = this._visibleItems();\n for (const i of visible) i.tabIndex = -1;\n item.tabIndex = 0;\n item.focus();\n this._lastFocusedItem = item;\n }\n\n private _onFocusIn = (event: FocusEvent) => {\n const target = event.target;\n if (target instanceof HTMLElement) {\n const item = target.closest<TreeItem>(tagName('tree-item'));\n if (item) this._lastFocusedItem = item;\n }\n };\n\n private _onKeyDown = (event: KeyboardEvent) => {\n const current = this._lastFocusedItem ?? this._visibleItems()[0];\n if (!current) return;\n\n const visible = this._visibleItems();\n const index = visible.indexOf(current);\n\n switch (event.key) {\n case 'ArrowDown': {\n event.preventDefault();\n const next = visible[Math.min(index + 1, visible.length - 1)];\n if (next) this._focusItem(next);\n break;\n }\n case 'ArrowUp': {\n event.preventDefault();\n const prev = visible[Math.max(index - 1, 0)];\n if (prev) this._focusItem(prev);\n break;\n }\n case 'ArrowRight': {\n event.preventDefault();\n if (!current.isLeaf() && !current.expanded) {\n current.expanded = true;\n } else if (current.expanded) {\n const first = current.getChildrenItems()[0];\n if (first) this._focusItem(first);\n }\n break;\n }\n case 'ArrowLeft': {\n event.preventDefault();\n if (current.expanded && !current.isLeaf()) {\n current.expanded = false;\n } else {\n const parent = current.parentElement?.closest(tagName('tree-item')) as TreeItem | null;\n if (parent) this._focusItem(parent);\n }\n break;\n }\n case 'Home': {\n event.preventDefault();\n if (visible[0]) this._focusItem(visible[0]);\n break;\n }\n case 'End': {\n event.preventDefault();\n const last = visible[visible.length - 1];\n if (last) this._focusItem(last);\n break;\n }\n case 'Enter':\n case ' ': {\n event.preventDefault();\n this._handleRowActivate(current);\n break;\n }\n case '*': {\n event.preventDefault();\n // Expand all siblings of the current item.\n const siblings = (\n current.parentElement ? (Array.from(current.parentElement.children) as TreeItem[]) : []\n ).filter((el) => el.tagName === tagName('tree-item').toUpperCase());\n for (const sib of siblings) {\n if (!sib.isLeaf()) sib.expanded = true;\n }\n break;\n }\n }\n };\n\n override render() {\n return html`\n <div\n class=\"tree\"\n part=\"base\"\n @click=${this._onClick}\n @keydown=${this._onKeyDown}\n @focusin=${this._onFocusIn}\n >\n <slot></slot>\n </div>\n `;\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import{i as e}from"../../chunks/lit.js";import{r as t,t as n}from"../../chunks/decorate.js";import{a as r,o as i,r as a,t as o}from"../../chunks/lit-html.js";import{n as s,r as c,t as l}from"../../chunks/directive.js";import{LuxenElement as u}from"../../shared/luxen-element.js";import{cls as d,tagName as f}from"../../registry.js";import p from"../../shared/styles/host.styles.js";import m from"../../shared/styles/checkbox-appearance.styles.js";var{I:h}=i,g=e=>e.strings===void 0,_={},v=(e,t=_)=>e._$AH=t,y=l(class extends s{constructor(e){if(super(e),e.type!==c.PROPERTY&&e.type!==c.ATTRIBUTE&&e.type!==c.BOOLEAN_ATTRIBUTE)throw Error("The `live` directive is not allowed on child or event bindings");if(!g(e))throw Error("`live` bindings can only contain a single expression")}render(e){return e}update(e,[t]){if(t===a||t===o)return t;let n=e.element,r=e.name;if(e.type===c.PROPERTY){if(t===n[r])return a}else if(e.type===c.BOOLEAN_ATTRIBUTE){if(!!t===n.hasAttribute(r))return a}else if(e.type===c.ATTRIBUTE&&n.getAttribute(r)===t+``)return a;return v(e),t}}),b=e(`:host{color:var(--l-color-text-primary);outline:none;font-size:.875rem;line-height:1.5;display:block}:host([disabled]){opacity:.4}:host([disabled]) .item{cursor:not-allowed}.item{align-items:center;gap:var(--item-gap);min-block-size:var(--row-height);padding-inline:var(--row-padding-inline);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:.375rem;padding-inline-start:calc(var(--indent-size) * var(--_depth,0) + var(--row-padding-inline));transition:background-color .12s,color .12s;display:flex;position:relative}.item:focus-visible,:host(:focus-visible) .item{outline:2px solid var(--l-focus-ring);outline-offset:1px}@media (hover:hover){:host(:not([disabled])) .item:hover{background-color:var(--l-color-bg-state-hover)}}:host([selected]:not([disabled])) .item{background-color:var(--l-color-bg-state-selected)}.expand{inline-size:var(--chevron-size);block-size:var(--chevron-size);color:var(--l-color-text-secondary);cursor:pointer;border-radius:3px;flex:none;place-items:center;display:grid}:host(:not(:state(has-children)):not([lazy])) .expand>slot>svg{display:none}.expand svg{width:100%;height:100%;display:block}:host(:not(:state(checkbox))) .checkbox{display:none}.label{min-inline-size:0;padding-inline:var(--item-gap) var(--row-padding-inline);margin-inline:calc(-1 * var(--item-gap)) calc(-1 * var(--row-padding-inline));text-overflow:ellipsis;white-space:nowrap;flex:1;align-items:center;gap:.375rem;display:flex;overflow:clip visible}.label ::slotted(*){min-inline-size:0}.branch{position:relative}.content{padding-inline-start:calc(var(--indent-size) * var(--_depth,0) + var(--row-padding-inline) + var(--chevron-size) + var(--item-gap));padding-inline-end:var(--row-padding-inline);display:block}:host(:state(has-children):not([expanded])) .content,.children{display:none}:host([expanded]) .children{display:block}.branch:before{content:"";border-inline-start:var(--indent-guide-width) var(--indent-guide-style) var(--indent-guide-color);pointer-events:none;inline-size:0;position:absolute;inset-block:0;inset-inline-start:calc(var(--indent-size) * var(--_depth,0) + var(--row-padding-inline) + (var(--chevron-size) / 2) - (var(--indent-guide-width) / 2))}:host(:not([expanded])) .branch:before,:host(:not(:state(has-children))) .branch:before{display:none}.spinner{border:2px solid var(--l-color-border);border-block-start-color:#0000;border-radius:50%;block-size:.875rem;inline-size:.875rem;animation:.7s linear infinite spin}@media (prefers-reduced-motion:reduce){.spinner{animation:none}}@keyframes spin{to{transform:rotate(360deg)}}`),x=class extends Event{constructor(){super(`expand`,{bubbles:!0,composed:!1,cancelable:!1})}},S=class extends Event{constructor(){super(`collapse`,{bubbles:!0,composed:!1,cancelable:!1})}},C=class extends Event{constructor(){super(`lazy-load`,{bubbles:!0,composed:!1,cancelable:!1})}},w=class extends Event{constructor(e,t){super(`selection-toggle`,{bubbles:!0,composed:!1,cancelable:!1}),this.item=e,this.checked=t}},T=class extends u{constructor(...e){super(...e),this._internals=this.attachInternals(),this.#e=!1,this.#t=!1,this._indeterminate=!1,this._cascadeIndeterminate=!1,this.#n=!1,this.#r=!1,this.#i=!1,this._showCheckbox=!1,this._depth=0,this._hasChildren=!1,this._onCheckboxChange=e=>{e.stopPropagation();let t=e.target;this.dispatchEvent(new w(this,t.checked))}}static{this.styles=[p,m,b]}#e;get expanded(){return this.#e}set expanded(e){this.#e=e}#t;get selected(){return this.#t}set selected(e){this.#t=e}get indeterminate(){return this._indeterminate}set indeterminate(e){this._indeterminate=e,this._cascadeIndeterminate=!1}_setCascadeIndeterminate(e){this.indeterminate=e,this._cascadeIndeterminate=e}#n;get disabled(){return this.#n}set disabled(e){this.#n=e}#r;get lazy(){return this.#r}set lazy(e){this.#r=e}#i;get loading(){return this.#i}set loading(e){this.#i=e}set showCheckbox(e){this._showCheckbox=e,this._setState(`checkbox`,e)}get showCheckbox(){return this._showCheckbox}set depth(e){this._depth=e,this.style.setProperty(`--_depth`,String(e))}get depth(){return this._depth}setPosition(e,t,n){this._aria(`ariaLevel`,`aria-level`,String(e)),this._aria(`ariaPosInSet`,`aria-posinset`,String(t)),this._aria(`ariaSetSize`,`aria-setsize`,String(n))}get hasChildren(){return this._hasChildren}getChildrenItems({includeDisabled:e=!0}={}){let t=f(`tree-item`).toUpperCase();return Array.from(this.children).filter(n=>n.tagName===t&&(e||!n.disabled))}isLeaf(){return!this.lazy&&this.getChildrenItems().length===0}getTextLabel(){let e=this.shadowRoot?.querySelector(`slot:not([name])`);return e?e.assignedNodes({flatten:!0}).map(e=>e.textContent??``).join(``).trim():(this.textContent??``).trim()}connectedCallback(){super.connectedCallback(),this._internals.role=`treeitem`,this.hasAttribute(`role`)||this.setAttribute(`role`,`treeitem`),this._childObserver=new MutationObserver(()=>this._syncChildren()),this._childObserver.observe(this,{childList:!0}),this._syncChildren()}disconnectedCallback(){super.disconnectedCallback(),this._childObserver?.disconnect()}updated(e){e.has(`expanded`)&&(this._reflectExpanded(),this.expanded&&this.lazy&&this.dispatchEvent(new C),this.dispatchEvent(this.expanded?new x:new S)),e.has(`selected`)&&this._aria(`ariaSelected`,`aria-selected`,String(this.selected)),e.has(`disabled`)&&this._aria(`ariaDisabled`,`aria-disabled`,this.disabled?`true`:null),e.has(`loading`)&&this._aria(`ariaBusy`,`aria-busy`,this.loading?`true`:null)}_reflectExpanded(){this._aria(`ariaExpanded`,`aria-expanded`,this.isLeaf()?null:String(this.expanded))}_aria(e,t,n){this._internals[e]=n,n===null?this.removeAttribute(t):this.setAttribute(t,n)}_setState(e,t){this._internals.states&&(t?this._internals.states.add(e):this._internals.states.delete(e))}_syncChildren(){let e=f(`tree-item`).toUpperCase(),t=0;for(let n of Array.from(this.children))n.tagName===e&&(t++,n.slot!==`children`&&(n.slot=`children`));this._hasChildren=t>0,this._setState(`has-children`,this._hasChildren),!this._hasChildren&&!this.lazy&&this.expanded&&(this.expanded=!1),this._reflectExpanded()}toggle(){(!this.isLeaf()||this.lazy)&&(this.expanded=!this.expanded)}render(){return r`
1
+ import{i as e}from"../../chunks/lit.js";import{r as t,t as n}from"../../chunks/decorate.js";import{a as r,o as i,r as a,t as o}from"../../chunks/lit-html.js";import{n as s,r as c,t as l}from"../../chunks/directive.js";import{LuxenElement as u}from"../../shared/luxen-element.js";import{cls as d,tagName as f}from"../../registry.js";import p from"../../shared/styles/host.styles.js";import m from"../../shared/styles/checkbox-appearance.styles.js";var{I:h}=i,g=e=>e.strings===void 0,_={},v=(e,t=_)=>e._$AH=t,y=l(class extends s{constructor(e){if(super(e),e.type!==c.PROPERTY&&e.type!==c.ATTRIBUTE&&e.type!==c.BOOLEAN_ATTRIBUTE)throw Error("The `live` directive is not allowed on child or event bindings");if(!g(e))throw Error("`live` bindings can only contain a single expression")}render(e){return e}update(e,[t]){if(t===a||t===o)return t;let n=e.element,r=e.name;if(e.type===c.PROPERTY){if(t===n[r])return a}else if(e.type===c.BOOLEAN_ATTRIBUTE){if(!!t===n.hasAttribute(r))return a}else if(e.type===c.ATTRIBUTE&&n.getAttribute(r)===t+``)return a;return v(e),t}}),b=e(`:host{color:var(--l-color-text-primary);outline:none;font-size:.875rem;line-height:1.5;display:block}:host([disabled]){opacity:.4}:host([disabled]) .item{cursor:not-allowed}.item{align-items:center;gap:var(--item-gap);min-block-size:var(--row-height);padding-inline:var(--row-padding-inline);border-radius:var(--l-radius-md);cursor:pointer;-webkit-user-select:none;user-select:none;padding-inline-start:calc(var(--indent-size) * var(--_depth,0) + var(--row-padding-inline));transition:background-color .12s,color .12s;display:flex;position:relative}.item:focus-visible,:host(:focus-visible) .item{outline:var(--l-focus-ring-width) solid var(--l-focus-ring-color);outline-offset:calc(var(--l-focus-ring-offset) / 2)}@media (hover:hover){:host(:not([disabled])) .item:hover{background-color:var(--l-color-bg-state-hover)}}:host([selected]:not([disabled])) .item{background-color:var(--l-color-bg-state-selected)}.expand{inline-size:var(--chevron-size);block-size:var(--chevron-size);color:var(--l-color-text-secondary);cursor:pointer;border-radius:3px;flex:none;place-items:center;display:grid}:host(:not(:state(has-children)):not([lazy])) .expand>slot>svg{display:none}.expand svg{width:100%;height:100%;display:block}:host(:not(:state(checkbox))) .checkbox{display:none}.label{min-inline-size:0;padding-inline:var(--item-gap) var(--row-padding-inline);margin-inline:calc(-1 * var(--item-gap)) calc(-1 * var(--row-padding-inline));text-overflow:ellipsis;white-space:nowrap;flex:1;align-items:center;gap:.375rem;display:flex;overflow:clip visible}.label ::slotted(*){min-inline-size:0}.branch{position:relative}.content{padding-inline-start:calc(var(--indent-size) * var(--_depth,0) + var(--row-padding-inline) + var(--chevron-size) + var(--item-gap));padding-inline-end:var(--row-padding-inline);display:block}:host(:state(has-children):not([expanded])) .content,.children{display:none}:host([expanded]) .children{display:block}.branch:before{content:"";border-inline-start:var(--indent-guide-width) var(--indent-guide-style) var(--indent-guide-color);pointer-events:none;inline-size:0;position:absolute;inset-block:0;inset-inline-start:calc(var(--indent-size) * var(--_depth,0) + var(--row-padding-inline) + (var(--chevron-size) / 2) - (var(--indent-guide-width) / 2))}:host(:not([expanded])) .branch:before,:host(:not(:state(has-children))) .branch:before{display:none}.spinner{border:2px solid var(--l-color-border);border-block-start-color:#0000;border-radius:50%;block-size:.875rem;inline-size:.875rem;animation:.7s linear infinite spin}@media (prefers-reduced-motion:reduce){.spinner{animation:none}}@keyframes spin{to{transform:rotate(360deg)}}`),x=class extends Event{constructor(){super(`expand`,{bubbles:!0,composed:!1,cancelable:!1})}},S=class extends Event{constructor(){super(`collapse`,{bubbles:!0,composed:!1,cancelable:!1})}},C=class extends Event{constructor(){super(`lazy-load`,{bubbles:!0,composed:!1,cancelable:!1})}},w=class extends Event{constructor(e,t){super(`selection-toggle`,{bubbles:!0,composed:!1,cancelable:!1}),this.item=e,this.checked=t}},T=class extends u{constructor(...e){super(...e),this._internals=this.attachInternals(),this.#e=!1,this.#t=!1,this._indeterminate=!1,this._cascadeIndeterminate=!1,this.#n=!1,this.#r=!1,this.#i=!1,this._showCheckbox=!1,this._depth=0,this._hasChildren=!1,this._onCheckboxChange=e=>{e.stopPropagation();let t=e.target;this.dispatchEvent(new w(this,t.checked))}}static{this.styles=[p,m,b]}#e;get expanded(){return this.#e}set expanded(e){this.#e=e}#t;get selected(){return this.#t}set selected(e){this.#t=e}get indeterminate(){return this._indeterminate}set indeterminate(e){this._indeterminate=e,this._cascadeIndeterminate=!1}_setCascadeIndeterminate(e){this.indeterminate=e,this._cascadeIndeterminate=e}#n;get disabled(){return this.#n}set disabled(e){this.#n=e}#r;get lazy(){return this.#r}set lazy(e){this.#r=e}#i;get loading(){return this.#i}set loading(e){this.#i=e}set showCheckbox(e){this._showCheckbox=e,this._setState(`checkbox`,e)}get showCheckbox(){return this._showCheckbox}set depth(e){this._depth=e,this.style.setProperty(`--_depth`,String(e))}get depth(){return this._depth}setPosition(e,t,n){this._aria(`ariaLevel`,`aria-level`,String(e)),this._aria(`ariaPosInSet`,`aria-posinset`,String(t)),this._aria(`ariaSetSize`,`aria-setsize`,String(n))}get hasChildren(){return this._hasChildren}getChildrenItems({includeDisabled:e=!0}={}){let t=f(`tree-item`).toUpperCase();return Array.from(this.children).filter(n=>n.tagName===t&&(e||!n.disabled))}isLeaf(){return!this.lazy&&this.getChildrenItems().length===0}getTextLabel(){let e=this.shadowRoot?.querySelector(`slot:not([name])`);return e?e.assignedNodes({flatten:!0}).map(e=>e.textContent??``).join(``).trim():(this.textContent??``).trim()}connectedCallback(){super.connectedCallback(),this._internals.role=`treeitem`,this.hasAttribute(`role`)||this.setAttribute(`role`,`treeitem`),this._childObserver=new MutationObserver(()=>this._syncChildren()),this._childObserver.observe(this,{childList:!0}),this._syncChildren()}disconnectedCallback(){super.disconnectedCallback(),this._childObserver?.disconnect()}updated(e){e.has(`expanded`)&&(this._reflectExpanded(),this.expanded&&this.lazy&&this.dispatchEvent(new C),this.dispatchEvent(this.expanded?new x:new S)),e.has(`selected`)&&this._aria(`ariaSelected`,`aria-selected`,String(this.selected)),e.has(`disabled`)&&this._aria(`ariaDisabled`,`aria-disabled`,this.disabled?`true`:null),e.has(`loading`)&&this._aria(`ariaBusy`,`aria-busy`,this.loading?`true`:null)}_reflectExpanded(){this._aria(`ariaExpanded`,`aria-expanded`,this.isLeaf()?null:String(this.expanded))}_aria(e,t,n){this._internals[e]=n,n===null?this.removeAttribute(t):this.setAttribute(t,n)}_setState(e,t){this._internals.states&&(t?this._internals.states.add(e):this._internals.states.delete(e))}_syncChildren(){let e=f(`tree-item`).toUpperCase(),t=0;for(let n of Array.from(this.children))n.tagName===e&&(t++,n.slot!==`children`&&(n.slot=`children`));this._hasChildren=t>0,this._setState(`has-children`,this._hasChildren),!this._hasChildren&&!this.lazy&&this.expanded&&(this.expanded=!1),this._reflectExpanded()}toggle(){(!this.isLeaf()||this.lazy)&&(this.expanded=!this.expanded)}render(){return r`
2
2
  <div
3
3
  class="item"
4
4
  part="base"
@@ -1 +1 @@
1
- {"version":3,"file":"tree-item.js","names":["o","l","i","t","r","n","o","e","s","unsafeCSS","rawStyles","hostStyles","checkboxAppearance","live","_decorate","property","r$1","n$1","__decorate","b","j$1","E","A","i$1","t$1","e$1","host_styles_default","checkbox_appearance_styles_default","t","r","o","m","p","l","r$2","i","styles","ExpandEvent","CollapseEvent","LazyLoadEvent","TreeItemSelectionToggleEvent","item","checked","TreeItem","LuxenElement","_args","#_expanded_accessor_storage","#_selected_accessor_storage","#_disabled_accessor_storage","#_lazy_accessor_storage","#_loading_accessor_storage","event","input","value","level","posInSet","setSize","childTag","tagName","el","includeDisabled","slot","n","changed","key","attr","on","name","count","child","cls"],"sources":["../../../../../node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directive-helpers.js","../../../../../node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directives/live.js","../../../src/html/elements/tree-item/tree-item.css?inline","../../../src/html/elements/tree-item/tree-item.ts"],"sourcesContent":["import{_$LH as o}from\"./lit-html.js\";\n/**\n * @license\n * Copyright 2020 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{I:t}=o,i=o=>o,n=o=>null===o||\"object\"!=typeof o&&\"function\"!=typeof o,e={HTML:1,SVG:2,MATHML:3},l=(o,t)=>void 0===t?void 0!==o?._$litType$:o?._$litType$===t,d=o=>null!=o?._$litType$?.h,c=o=>void 0!==o?._$litDirective$,f=o=>o?._$litDirective$,r=o=>void 0===o.strings,s=()=>document.createComment(\"\"),v=(o,n,e)=>{const l=o._$AA.parentNode,d=void 0===n?o._$AB:n._$AA;if(void 0===e){const i=l.insertBefore(s(),d),n=l.insertBefore(s(),d);e=new t(i,n,o,o.options)}else{const t=e._$AB.nextSibling,n=e._$AM,c=n!==o;if(c){let t;e._$AQ?.(o),e._$AM=o,void 0!==e._$AP&&(t=o._$AU)!==n._$AU&&e._$AP(t)}if(t!==d||c){let o=e._$AA;for(;o!==t;){const t=i(o).nextSibling;i(l).insertBefore(o,d),o=t}}}return e},u=(o,t,i=o)=>(o._$AI(t,i),o),m={},p=(o,t=m)=>o._$AH=t,M=o=>o._$AH,h=o=>{o._$AR(),o._$AA.remove()},j=o=>{o._$AR()};export{e as TemplateResultType,j as clearPart,M as getCommittedValue,f as getDirectiveClass,v as insertPart,d as isCompiledTemplateResult,c as isDirectiveResult,n as isPrimitive,r as isSingleExpression,l as isTemplateResult,h as removePart,u as setChildPartValue,p as setCommittedValue};\n//# sourceMappingURL=directive-helpers.js.map\n","import{noChange as r,nothing as e}from\"../lit-html.js\";import{directive as i,Directive as t,PartType as n}from\"../directive.js\";import{isSingleExpression as o,setCommittedValue as s}from\"../directive-helpers.js\";\n/**\n * @license\n * Copyright 2020 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const l=i(class extends t{constructor(r){if(super(r),r.type!==n.PROPERTY&&r.type!==n.ATTRIBUTE&&r.type!==n.BOOLEAN_ATTRIBUTE)throw Error(\"The `live` directive is not allowed on child or event bindings\");if(!o(r))throw Error(\"`live` bindings can only contain a single expression\")}render(r){return r}update(i,[t]){if(t===r||t===e)return t;const o=i.element,l=i.name;if(i.type===n.PROPERTY){if(t===o[l])return r}else if(i.type===n.BOOLEAN_ATTRIBUTE){if(!!t===o.hasAttribute(l))return r}else if(i.type===n.ATTRIBUTE&&o.getAttribute(l)===t+\"\")return r;return s(i),t}});export{l as live};\n//# sourceMappingURL=live.js.map\n",":host {\n display: block;\n color: var(--l-color-text-primary);\n font-size: 0.875rem;\n line-height: 1.5;\n /* The host is the roving-tabindex focus target, but the visible ring is drawn\n on the inner `.item` row. Suppress the host's UA outline so it doesn't paint\n a second ring around the whole subtree (row + children). */\n outline: none;\n}\n\n:host([disabled]) {\n opacity: 0.4;\n}\n\n:host([disabled]) .item {\n cursor: not-allowed;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: var(--item-gap);\n min-block-size: var(--row-height);\n padding-inline: var(--row-padding-inline);\n padding-inline-start: calc(var(--indent-size) * var(--_depth, 0) + var(--row-padding-inline));\n border-radius: 0.375rem;\n cursor: pointer;\n user-select: none;\n transition:\n background-color 120ms ease,\n color 120ms ease;\n position: relative;\n}\n\n.item:focus-visible,\n:host(:focus-visible) .item {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 1px;\n}\n\n@media (hover: hover) {\n :host(:not([disabled])) .item:hover {\n background-color: var(--l-color-bg-state-hover);\n }\n}\n\n:host([selected]:not([disabled])) .item {\n background-color: var(--l-color-bg-state-selected);\n}\n\n.expand {\n inline-size: var(--chevron-size);\n block-size: var(--chevron-size);\n display: grid;\n place-items: center;\n flex: none;\n color: var(--l-color-text-secondary);\n border-radius: 3px;\n cursor: pointer;\n}\n\n/* Hide the DEFAULT fallback chevron SVG on a leaf — slotted content\n (user-provided icon, avatar, etc.) remains visible because it lives outside\n the slot in the DOM tree. The `.expand` span keeps its fixed --chevron-size\n so leaf rows stay aligned with branches. */\n:host(:not(:state(has-children)):not([lazy])) .expand > slot > svg {\n display: none;\n}\n\n.expand svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n/* The checkbox appearance comes from the shared `.l-checkbox` skin imported\n above; this rule only governs visibility. */\n:host(:not(:state(checkbox))) .checkbox {\n display: none;\n}\n\n.label {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n /* The label truncates long text to an ellipsis, yet a slotted interactive\n control's hover/focus decoration (a row-action button, an `<l-dropdown>`\n trigger) must bleed past the row-height label box instead of being cut.\n\n Only the inline axis is clipped — `clip`, unlike `hidden`, lets the block\n axis stay `visible` — so nothing is ever cut above or below. On the inline\n axis the clip follows the padding box, so padding cancelled by an equal\n negative margin widens it without moving anything; it reaches exactly the\n row's own spacing (the gap before the label, the row padding after it), so\n the label never overlaps a sibling control, a neighbouring row, or the\n tree's edge. Growing the box on the block axis instead would spill into the\n rows above and below and steal their clicks; `overflow-clip-margin` would\n not, but WebKit has no such property. */\n overflow-x: clip;\n overflow-y: visible;\n padding-inline: var(--item-gap) var(--row-padding-inline);\n margin-inline: calc(-1 * var(--item-gap)) calc(-1 * var(--row-padding-inline));\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.label ::slotted(*) {\n min-inline-size: 0;\n}\n\n/* Wrapper around the content slot + children — anchor for the indent guide. */\n.branch {\n position: relative;\n}\n\n/* Content slot — block area between the row and the children.\n Aligned under the label text: same left offset as the row's label\n (depth indent + row padding + chevron + gap). Visible for leaves (no\n children) and for expanded branches; hidden when a branch is collapsed\n (mirrors the children visibility). */\n.content {\n display: block;\n padding-inline-start: calc(\n var(--indent-size) * var(--_depth, 0) + var(--row-padding-inline) + var(--chevron-size) +\n var(--item-gap)\n );\n padding-inline-end: var(--row-padding-inline);\n}\n\n:host(:state(has-children):not([expanded])) .content {\n display: none;\n}\n\n.children {\n display: none;\n}\n\n:host([expanded]) .children {\n display: block;\n}\n\n/* Vertical indent guide — spans the content + children block, starting\n right below the row so it never overlaps the chevron/avatar.\n The guide's visual centre sits exactly on the parent chevron's centre. */\n.branch::before {\n content: '';\n position: absolute;\n inset-block: 0;\n inset-inline-start: calc(\n var(--indent-size) * var(--_depth, 0) + var(--row-padding-inline) + (var(--chevron-size) / 2) -\n (var(--indent-guide-width) / 2)\n );\n inline-size: 0;\n border-inline-start: var(--indent-guide-width) var(--indent-guide-style) var(--indent-guide-color);\n pointer-events: none;\n}\n\n/* Only render the guide for open branches that have children. */\n:host(:not([expanded])) .branch::before,\n:host(:not(:state(has-children))) .branch::before {\n display: none;\n}\n\n.spinner {\n inline-size: 0.875rem;\n block-size: 0.875rem;\n border-radius: 50%;\n border: 2px solid var(--l-color-border);\n border-block-start-color: transparent;\n animation: spin 700ms linear infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .spinner {\n animation: none;\n }\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n","import { html, unsafeCSS, type PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { live } from 'lit/directives/live.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { cls, tagName } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport checkboxAppearance from '../../shared/styles/checkbox-appearance.styles.js';\nimport rawStyles from './tree-item.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/** Fired when a tree item is expanded. Bubbles; not composed. */\nexport class ExpandEvent extends Event {\n constructor() {\n super('expand', { bubbles: true, composed: false, cancelable: false });\n }\n}\n\n/** Fired when a tree item is collapsed. Bubbles; not composed. */\nexport class CollapseEvent extends Event {\n constructor() {\n super('collapse', { bubbles: true, composed: false, cancelable: false });\n }\n}\n\n/** Fired when a lazy item is expanded for the first time. Bubbles; not composed. */\nexport class LazyLoadEvent extends Event {\n constructor() {\n super('lazy-load', { bubbles: true, composed: false, cancelable: false });\n }\n}\n\n/**\n * Internal contract: a tree-item's checkbox toggle, bubbling to its `<l-tree>`.\n * Not part of the public API — consumers listen on `<l-tree>` for\n * `selection-change` instead.\n */\nexport class TreeItemSelectionToggleEvent extends Event {\n readonly item: TreeItem;\n readonly checked: boolean;\n constructor(item: TreeItem, checked: boolean) {\n super('selection-toggle', { bubbles: true, composed: false, cancelable: false });\n this.item = item;\n this.checked = checked;\n }\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n expand: ExpandEvent;\n collapse: CollapseEvent;\n 'lazy-load': LazyLoadEvent;\n }\n}\n\n/**\n * A node inside `<l-tree>`. Nested `<l-tree-item>` children become sub-nodes.\n *\n * @slot - Label content (kept to a single row).\n * @slot prefix - Leading content before the label (e.g. icon).\n * @slot suffix - Trailing content.\n * @slot expand-icon - Icon shown when the item is collapsed.\n * @slot collapse-icon - Icon shown when the item is expanded.\n * @slot content - Block content that belongs to the item but not to its header row (e.g. comment body, action bar). Hidden when a branch is collapsed.\n *\n * @csspart base - The item row.\n * @csspart expand-button - The chevron toggle area.\n * @csspart checkbox - The native checkbox input.\n * @csspart label - The label container.\n * @csspart branch - Wrapper around the content and children slots; carries the indent guide.\n * @csspart content - The content slot wrapper (block area between the row and the children).\n * @csspart children - The nested items container.\n *\n * @cssproperty [--_depth] - Internal depth index driving indentation. Set by `<l-tree>`.\n *\n * Layout tokens (`--chevron-size`, `--row-height`, `--row-padding-inline`, `--item-gap`) live on `<l-tree>` and cascade down — see its CSS custom properties.\n *\n * @event expand - Fired when the item is expanded.\n * @event collapse - Fired when the item is collapsed.\n * @event lazy-load - Fired when a lazy item is expanded for the first time. Consumers should append children and set `lazy=false`.\n *\n * @customElement l-tree-item\n */\nexport class TreeItem extends LuxenElement {\n static override styles = [hostStyles, checkboxAppearance, styles];\n\n private _internals = this.attachInternals();\n private _childObserver?: MutationObserver;\n\n /** Whether the item is expanded. */\n @property({ type: Boolean, reflect: true })\n accessor expanded = false;\n\n /** Whether the item is selected. */\n @property({ type: Boolean, reflect: true })\n accessor selected = false;\n\n /**\n * Whether the checkbox is indeterminate (some descendants selected). Managed by\n * `<l-tree>` in cascade mode; set it yourself with `independent`.\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n get indeterminate(): boolean {\n return this._indeterminate;\n }\n set indeterminate(value: boolean) {\n this._indeterminate = value;\n // Any write from outside the cascade hands the state to the consumer.\n this._cascadeIndeterminate = false;\n }\n private _indeterminate = false;\n\n /**\n * Whether the current `indeterminate` was written by `<l-tree>`'s cascade, so\n * the tree can clear its own flags on leaving the cascade without touching the\n * consumer's.\n * @internal\n */\n _cascadeIndeterminate = false;\n\n /** @internal Set by `<l-tree>`'s cascade. */\n _setCascadeIndeterminate(value: boolean) {\n this.indeterminate = value;\n this._cascadeIndeterminate = value;\n }\n\n /** Whether the item is disabled. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n /** Marks this item as having children that will be loaded on first expand. */\n @property({ type: Boolean, reflect: true })\n accessor lazy = false;\n\n /** Whether the item is currently loading (shows a spinner). */\n @property({ type: Boolean, reflect: true })\n accessor loading = false;\n\n /** Set by `<l-tree>`: whether a checkbox is shown. */\n set showCheckbox(value: boolean) {\n this._showCheckbox = value;\n this._setState('checkbox', value);\n }\n get showCheckbox(): boolean {\n return this._showCheckbox;\n }\n private _showCheckbox = false;\n\n /** Set by `<l-tree>`: depth of the item in the tree (0 = root). */\n set depth(value: number) {\n this._depth = value;\n this.style.setProperty('--_depth', String(value));\n }\n get depth(): number {\n return this._depth;\n }\n private _depth = 0;\n\n /**\n * Set by `<l-tree>`: ARIA position within the tree. `level` is 1-based depth,\n * `posInSet`/`setSize` describe the item's rank among its siblings. These let\n * screen readers announce \"level 2, 3 of 5\" even when `lazy` children keep the\n * full set out of the DOM.\n */\n setPosition(level: number, posInSet: number, setSize: number) {\n this._aria('ariaLevel', 'aria-level', String(level));\n this._aria('ariaPosInSet', 'aria-posinset', String(posInSet));\n this._aria('ariaSetSize', 'aria-setsize', String(setSize));\n }\n\n /** Whether this item has nested tree-item children. */\n get hasChildren(): boolean {\n return this._hasChildren;\n }\n private _hasChildren = false;\n\n /** Returns the child `<l-tree-item>` elements directly under this one. */\n getChildrenItems({ includeDisabled = true } = {}): TreeItem[] {\n const childTag = tagName('tree-item').toUpperCase();\n return (Array.from(this.children) as TreeItem[]).filter(\n (el) => el.tagName === childTag && (includeDisabled || !el.disabled),\n );\n }\n\n /** Returns true if this item has no expandable children. */\n isLeaf(): boolean {\n return !this.lazy && this.getChildrenItems().length === 0;\n }\n\n /** Returns the text label of this item. */\n getTextLabel(): string {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return (this.textContent ?? '').trim();\n return slot\n .assignedNodes({ flatten: true })\n .map((n) => n.textContent ?? '')\n .join('')\n .trim();\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this._internals.role = 'treeitem';\n // Mirror the role to a DOM attribute too, so `[role=\"treeitem\"]` selectors\n // (CSS, querySelector, Cypress/Playwright CSS) keep matching — the\n // ElementInternals role alone is not attribute-selectable. ARIA states are\n // mirrored the same way in `_aria()` (aria-expanded/selected/disabled/…).\n if (!this.hasAttribute('role')) this.setAttribute('role', 'treeitem');\n this._childObserver = new MutationObserver(() => this._syncChildren());\n this._childObserver.observe(this, { childList: true });\n this._syncChildren();\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._childObserver?.disconnect();\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('expanded')) {\n this._reflectExpanded();\n // Emit lazy-load for ANY transition to expanded (keyboard, chevron,\n // `expandAll()`…), not just `toggle()` — otherwise keyboard users expand a\n // lazy branch with no children and no fetch. Fires while still `lazy`; the\n // consumer appends children and clears `lazy`, so it won't re-fire.\n if (this.expanded && this.lazy) this.dispatchEvent(new LazyLoadEvent());\n this.dispatchEvent(this.expanded ? new ExpandEvent() : new CollapseEvent());\n }\n\n if (changed.has('selected')) {\n this._aria('ariaSelected', 'aria-selected', String(this.selected));\n }\n\n if (changed.has('disabled')) {\n this._aria('ariaDisabled', 'aria-disabled', this.disabled ? 'true' : null);\n }\n\n if (changed.has('loading')) {\n this._aria('ariaBusy', 'aria-busy', this.loading ? 'true' : null);\n }\n }\n\n /** Leaf items omit `aria-expanded` entirely; branches reflect their state. */\n private _reflectExpanded() {\n this._aria('ariaExpanded', 'aria-expanded', this.isLeaf() ? null : String(this.expanded));\n }\n\n /**\n * Write an ARIA state to BOTH ElementInternals (the semantic source, in the\n * accessibility tree) and a content attribute (so `[aria-*]` CSS / query / test\n * selectors keep matching — same belt-and-suspenders as the mirrored `role`).\n * A `null` value clears both.\n */\n private _aria(key: keyof ElementInternals, attr: string, value: string | null) {\n (this._internals as unknown as Record<string, string | null>)[key] = value;\n if (value === null) this.removeAttribute(attr);\n else this.setAttribute(attr, value);\n }\n\n private _setState(name: string, on: boolean) {\n if (!this._internals.states) return;\n if (on) this._internals.states.add(name);\n else this._internals.states.delete(name);\n }\n\n private _syncChildren() {\n // Auto-slot nested tree-items into the `children` slot so they render in the group container,\n // while label text/elements remain in the default slot.\n const childTag = tagName('tree-item').toUpperCase();\n let count = 0;\n for (const child of Array.from(this.children) as HTMLElement[]) {\n if (child.tagName === childTag) {\n count++;\n if (child.slot !== 'children') child.slot = 'children';\n }\n }\n this._hasChildren = count > 0;\n this._setState('has-children', this._hasChildren);\n if (!this._hasChildren && !this.lazy && this.expanded) {\n this.expanded = false;\n }\n this._reflectExpanded();\n }\n\n /** Toggle expand state. Opening a `lazy` item emits `lazy-load` (via `updated`). */\n toggle() {\n if (this.isLeaf() && !this.lazy) return;\n // lazy-load is emitted centrally from `updated()` on the expand transition.\n this.expanded = !this.expanded;\n }\n\n private _onCheckboxChange = (event: Event) => {\n event.stopPropagation();\n const input = event.target as HTMLInputElement;\n this.dispatchEvent(new TreeItemSelectionToggleEvent(this, input.checked));\n };\n\n override render() {\n return html`\n <div\n class=\"item\"\n part=\"base\"\n >\n <span\n class=\"expand\"\n part=\"expand-button\"\n aria-hidden=\"true\"\n >\n ${\n this.loading\n ? html`<span\n class=\"spinner\"\n role=\"status\"\n ></span>`\n : this.expanded || this.isLeaf()\n ? html`<slot name=\"collapse-icon\">\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M3.5 6l4.5 5 4.5-5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </slot>`\n : html`<slot name=\"expand-icon\">\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M6 3.5l5 4.5-5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </slot>`\n }\n </span>\n\n <input\n class=\"checkbox ${cls('checkbox')}\"\n part=\"checkbox\"\n type=\"checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n .checked=${live(this.selected)}\n .indeterminate=${live(this.indeterminate)}\n ?disabled=${this.disabled}\n @click=${(e: Event) => e.stopPropagation()}\n @change=${this._onCheckboxChange}\n />\n\n <slot name=\"prefix\"></slot>\n <span\n class=\"label\"\n part=\"label\"\n ><slot></slot\n ></span>\n <slot name=\"suffix\"></slot>\n </div>\n\n <div\n class=\"branch\"\n part=\"branch\"\n >\n <div\n class=\"content\"\n part=\"content\"\n >\n <slot name=\"content\"></slot>\n </div>\n <div\n class=\"children\"\n part=\"children\"\n role=\"group\"\n >\n <slot name=\"children\"></slot>\n </div>\n </div>\n `;\n }\n}\n"],"ignoreList":[0,1],"mappings":"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"}
1
+ {"version":3,"file":"tree-item.js","names":["o","l","i","t","r","n","o","e","s","unsafeCSS","rawStyles","hostStyles","checkboxAppearance","live","_decorate","property","r$1","n$1","__decorate","b","j$1","E","A","i$1","t$1","e$1","host_styles_default","checkbox_appearance_styles_default","t","r","o","m","p","l","r$2","i","styles","ExpandEvent","CollapseEvent","LazyLoadEvent","TreeItemSelectionToggleEvent","item","checked","TreeItem","LuxenElement","_args","#_expanded_accessor_storage","#_selected_accessor_storage","#_disabled_accessor_storage","#_lazy_accessor_storage","#_loading_accessor_storage","event","input","value","level","posInSet","setSize","childTag","tagName","el","includeDisabled","slot","n","changed","key","attr","on","name","count","child","cls"],"sources":["../../../../../node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directive-helpers.js","../../../../../node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directives/live.js","../../../src/html/elements/tree-item/tree-item.css?inline","../../../src/html/elements/tree-item/tree-item.ts"],"sourcesContent":["import{_$LH as o}from\"./lit-html.js\";\n/**\n * @license\n * Copyright 2020 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const{I:t}=o,i=o=>o,n=o=>null===o||\"object\"!=typeof o&&\"function\"!=typeof o,e={HTML:1,SVG:2,MATHML:3},l=(o,t)=>void 0===t?void 0!==o?._$litType$:o?._$litType$===t,d=o=>null!=o?._$litType$?.h,c=o=>void 0!==o?._$litDirective$,f=o=>o?._$litDirective$,r=o=>void 0===o.strings,s=()=>document.createComment(\"\"),v=(o,n,e)=>{const l=o._$AA.parentNode,d=void 0===n?o._$AB:n._$AA;if(void 0===e){const i=l.insertBefore(s(),d),n=l.insertBefore(s(),d);e=new t(i,n,o,o.options)}else{const t=e._$AB.nextSibling,n=e._$AM,c=n!==o;if(c){let t;e._$AQ?.(o),e._$AM=o,void 0!==e._$AP&&(t=o._$AU)!==n._$AU&&e._$AP(t)}if(t!==d||c){let o=e._$AA;for(;o!==t;){const t=i(o).nextSibling;i(l).insertBefore(o,d),o=t}}}return e},u=(o,t,i=o)=>(o._$AI(t,i),o),m={},p=(o,t=m)=>o._$AH=t,M=o=>o._$AH,h=o=>{o._$AR(),o._$AA.remove()},j=o=>{o._$AR()};export{e as TemplateResultType,j as clearPart,M as getCommittedValue,f as getDirectiveClass,v as insertPart,d as isCompiledTemplateResult,c as isDirectiveResult,n as isPrimitive,r as isSingleExpression,l as isTemplateResult,h as removePart,u as setChildPartValue,p as setCommittedValue};\n//# sourceMappingURL=directive-helpers.js.map\n","import{noChange as r,nothing as e}from\"../lit-html.js\";import{directive as i,Directive as t,PartType as n}from\"../directive.js\";import{isSingleExpression as o,setCommittedValue as s}from\"../directive-helpers.js\";\n/**\n * @license\n * Copyright 2020 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */const l=i(class extends t{constructor(r){if(super(r),r.type!==n.PROPERTY&&r.type!==n.ATTRIBUTE&&r.type!==n.BOOLEAN_ATTRIBUTE)throw Error(\"The `live` directive is not allowed on child or event bindings\");if(!o(r))throw Error(\"`live` bindings can only contain a single expression\")}render(r){return r}update(i,[t]){if(t===r||t===e)return t;const o=i.element,l=i.name;if(i.type===n.PROPERTY){if(t===o[l])return r}else if(i.type===n.BOOLEAN_ATTRIBUTE){if(!!t===o.hasAttribute(l))return r}else if(i.type===n.ATTRIBUTE&&o.getAttribute(l)===t+\"\")return r;return s(i),t}});export{l as live};\n//# sourceMappingURL=live.js.map\n",":host {\n display: block;\n color: var(--l-color-text-primary);\n font-size: 0.875rem;\n line-height: 1.5;\n /* The host is the roving-tabindex focus target, but the visible ring is drawn\n on the inner `.item` row. Suppress the host's UA outline so it doesn't paint\n a second ring around the whole subtree (row + children). */\n outline: none;\n}\n\n:host([disabled]) {\n opacity: 0.4;\n}\n\n:host([disabled]) .item {\n cursor: not-allowed;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: var(--item-gap);\n min-block-size: var(--row-height);\n padding-inline: var(--row-padding-inline);\n padding-inline-start: calc(var(--indent-size) * var(--_depth, 0) + var(--row-padding-inline));\n border-radius: var(--l-radius-md);\n cursor: pointer;\n user-select: none;\n transition:\n background-color 120ms ease,\n color 120ms ease;\n position: relative;\n}\n\n.item:focus-visible,\n:host(:focus-visible) .item {\n outline: var(--l-focus-ring-width) solid var(--l-focus-ring-color);\n /* Half the gap: rows/buttons sit edge to edge, a full offset would\n reach into the neighbour. */\n outline-offset: calc(var(--l-focus-ring-offset) / 2);\n}\n\n@media (hover: hover) {\n :host(:not([disabled])) .item:hover {\n background-color: var(--l-color-bg-state-hover);\n }\n}\n\n:host([selected]:not([disabled])) .item {\n background-color: var(--l-color-bg-state-selected);\n}\n\n.expand {\n inline-size: var(--chevron-size);\n block-size: var(--chevron-size);\n display: grid;\n place-items: center;\n flex: none;\n color: var(--l-color-text-secondary);\n border-radius: 3px;\n cursor: pointer;\n}\n\n/* Hide the DEFAULT fallback chevron SVG on a leaf — slotted content\n (user-provided icon, avatar, etc.) remains visible because it lives outside\n the slot in the DOM tree. The `.expand` span keeps its fixed --chevron-size\n so leaf rows stay aligned with branches. */\n:host(:not(:state(has-children)):not([lazy])) .expand > slot > svg {\n display: none;\n}\n\n.expand svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n/* The checkbox appearance comes from the shared `.l-checkbox` skin imported\n above; this rule only governs visibility. */\n:host(:not(:state(checkbox))) .checkbox {\n display: none;\n}\n\n.label {\n flex: 1;\n min-inline-size: 0;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n /* The label truncates long text to an ellipsis, yet a slotted interactive\n control's hover/focus decoration (a row-action button, an `<l-dropdown>`\n trigger) must bleed past the row-height label box instead of being cut.\n\n Only the inline axis is clipped — `clip`, unlike `hidden`, lets the block\n axis stay `visible` — so nothing is ever cut above or below. On the inline\n axis the clip follows the padding box, so padding cancelled by an equal\n negative margin widens it without moving anything; it reaches exactly the\n row's own spacing (the gap before the label, the row padding after it), so\n the label never overlaps a sibling control, a neighbouring row, or the\n tree's edge. Growing the box on the block axis instead would spill into the\n rows above and below and steal their clicks; `overflow-clip-margin` would\n not, but WebKit has no such property. */\n overflow-x: clip;\n overflow-y: visible;\n padding-inline: var(--item-gap) var(--row-padding-inline);\n margin-inline: calc(-1 * var(--item-gap)) calc(-1 * var(--row-padding-inline));\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.label ::slotted(*) {\n min-inline-size: 0;\n}\n\n/* Wrapper around the content slot + children — anchor for the indent guide. */\n.branch {\n position: relative;\n}\n\n/* Content slot — block area between the row and the children.\n Aligned under the label text: same left offset as the row's label\n (depth indent + row padding + chevron + gap). Visible for leaves (no\n children) and for expanded branches; hidden when a branch is collapsed\n (mirrors the children visibility). */\n.content {\n display: block;\n padding-inline-start: calc(\n var(--indent-size) * var(--_depth, 0) + var(--row-padding-inline) + var(--chevron-size) +\n var(--item-gap)\n );\n padding-inline-end: var(--row-padding-inline);\n}\n\n:host(:state(has-children):not([expanded])) .content {\n display: none;\n}\n\n.children {\n display: none;\n}\n\n:host([expanded]) .children {\n display: block;\n}\n\n/* Vertical indent guide — spans the content + children block, starting\n right below the row so it never overlaps the chevron/avatar.\n The guide's visual centre sits exactly on the parent chevron's centre. */\n.branch::before {\n content: '';\n position: absolute;\n inset-block: 0;\n inset-inline-start: calc(\n var(--indent-size) * var(--_depth, 0) + var(--row-padding-inline) + (var(--chevron-size) / 2) -\n (var(--indent-guide-width) / 2)\n );\n inline-size: 0;\n border-inline-start: var(--indent-guide-width) var(--indent-guide-style) var(--indent-guide-color);\n pointer-events: none;\n}\n\n/* Only render the guide for open branches that have children. */\n:host(:not([expanded])) .branch::before,\n:host(:not(:state(has-children))) .branch::before {\n display: none;\n}\n\n.spinner {\n inline-size: 0.875rem;\n block-size: 0.875rem;\n border-radius: 50%;\n border: 2px solid var(--l-color-border);\n border-block-start-color: transparent;\n animation: spin 700ms linear infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .spinner {\n animation: none;\n }\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n","import { html, unsafeCSS, type PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { live } from 'lit/directives/live.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { cls, tagName } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport checkboxAppearance from '../../shared/styles/checkbox-appearance.styles.js';\nimport rawStyles from './tree-item.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/** Fired when a tree item is expanded. Bubbles; not composed. */\nexport class ExpandEvent extends Event {\n constructor() {\n super('expand', { bubbles: true, composed: false, cancelable: false });\n }\n}\n\n/** Fired when a tree item is collapsed. Bubbles; not composed. */\nexport class CollapseEvent extends Event {\n constructor() {\n super('collapse', { bubbles: true, composed: false, cancelable: false });\n }\n}\n\n/** Fired when a lazy item is expanded for the first time. Bubbles; not composed. */\nexport class LazyLoadEvent extends Event {\n constructor() {\n super('lazy-load', { bubbles: true, composed: false, cancelable: false });\n }\n}\n\n/**\n * Internal contract: a tree-item's checkbox toggle, bubbling to its `<l-tree>`.\n * Not part of the public API — consumers listen on `<l-tree>` for\n * `selection-change` instead.\n */\nexport class TreeItemSelectionToggleEvent extends Event {\n readonly item: TreeItem;\n readonly checked: boolean;\n constructor(item: TreeItem, checked: boolean) {\n super('selection-toggle', { bubbles: true, composed: false, cancelable: false });\n this.item = item;\n this.checked = checked;\n }\n}\n\ndeclare global {\n interface GlobalEventHandlersEventMap {\n expand: ExpandEvent;\n collapse: CollapseEvent;\n 'lazy-load': LazyLoadEvent;\n }\n}\n\n/**\n * A node inside `<l-tree>`. Nested `<l-tree-item>` children become sub-nodes.\n *\n * @slot - Label content (kept to a single row).\n * @slot prefix - Leading content before the label (e.g. icon).\n * @slot suffix - Trailing content.\n * @slot expand-icon - Icon shown when the item is collapsed.\n * @slot collapse-icon - Icon shown when the item is expanded.\n * @slot content - Block content that belongs to the item but not to its header row (e.g. comment body, action bar). Hidden when a branch is collapsed.\n *\n * @csspart base - The item row.\n * @csspart expand-button - The chevron toggle area.\n * @csspart checkbox - The native checkbox input.\n * @csspart label - The label container.\n * @csspart branch - Wrapper around the content and children slots; carries the indent guide.\n * @csspart content - The content slot wrapper (block area between the row and the children).\n * @csspart children - The nested items container.\n *\n * @cssproperty [--_depth] - Internal depth index driving indentation. Set by `<l-tree>`.\n *\n * Layout tokens (`--chevron-size`, `--row-height`, `--row-padding-inline`, `--item-gap`) live on `<l-tree>` and cascade down — see its CSS custom properties.\n *\n * @event expand - Fired when the item is expanded.\n * @event collapse - Fired when the item is collapsed.\n * @event lazy-load - Fired when a lazy item is expanded for the first time. Consumers should append children and set `lazy=false`.\n *\n * @customElement l-tree-item\n */\nexport class TreeItem extends LuxenElement {\n static override styles = [hostStyles, checkboxAppearance, styles];\n\n private _internals = this.attachInternals();\n private _childObserver?: MutationObserver;\n\n /** Whether the item is expanded. */\n @property({ type: Boolean, reflect: true })\n accessor expanded = false;\n\n /** Whether the item is selected. */\n @property({ type: Boolean, reflect: true })\n accessor selected = false;\n\n /**\n * Whether the checkbox is indeterminate (some descendants selected). Managed by\n * `<l-tree>` in cascade mode; set it yourself with `independent`.\n * @default false\n */\n @property({ type: Boolean, reflect: true })\n get indeterminate(): boolean {\n return this._indeterminate;\n }\n set indeterminate(value: boolean) {\n this._indeterminate = value;\n // Any write from outside the cascade hands the state to the consumer.\n this._cascadeIndeterminate = false;\n }\n private _indeterminate = false;\n\n /**\n * Whether the current `indeterminate` was written by `<l-tree>`'s cascade, so\n * the tree can clear its own flags on leaving the cascade without touching the\n * consumer's.\n * @internal\n */\n _cascadeIndeterminate = false;\n\n /** @internal Set by `<l-tree>`'s cascade. */\n _setCascadeIndeterminate(value: boolean) {\n this.indeterminate = value;\n this._cascadeIndeterminate = value;\n }\n\n /** Whether the item is disabled. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n /** Marks this item as having children that will be loaded on first expand. */\n @property({ type: Boolean, reflect: true })\n accessor lazy = false;\n\n /** Whether the item is currently loading (shows a spinner). */\n @property({ type: Boolean, reflect: true })\n accessor loading = false;\n\n /** Set by `<l-tree>`: whether a checkbox is shown. */\n set showCheckbox(value: boolean) {\n this._showCheckbox = value;\n this._setState('checkbox', value);\n }\n get showCheckbox(): boolean {\n return this._showCheckbox;\n }\n private _showCheckbox = false;\n\n /** Set by `<l-tree>`: depth of the item in the tree (0 = root). */\n set depth(value: number) {\n this._depth = value;\n this.style.setProperty('--_depth', String(value));\n }\n get depth(): number {\n return this._depth;\n }\n private _depth = 0;\n\n /**\n * Set by `<l-tree>`: ARIA position within the tree. `level` is 1-based depth,\n * `posInSet`/`setSize` describe the item's rank among its siblings. These let\n * screen readers announce \"level 2, 3 of 5\" even when `lazy` children keep the\n * full set out of the DOM.\n */\n setPosition(level: number, posInSet: number, setSize: number) {\n this._aria('ariaLevel', 'aria-level', String(level));\n this._aria('ariaPosInSet', 'aria-posinset', String(posInSet));\n this._aria('ariaSetSize', 'aria-setsize', String(setSize));\n }\n\n /** Whether this item has nested tree-item children. */\n get hasChildren(): boolean {\n return this._hasChildren;\n }\n private _hasChildren = false;\n\n /** Returns the child `<l-tree-item>` elements directly under this one. */\n getChildrenItems({ includeDisabled = true } = {}): TreeItem[] {\n const childTag = tagName('tree-item').toUpperCase();\n return (Array.from(this.children) as TreeItem[]).filter(\n (el) => el.tagName === childTag && (includeDisabled || !el.disabled),\n );\n }\n\n /** Returns true if this item has no expandable children. */\n isLeaf(): boolean {\n return !this.lazy && this.getChildrenItems().length === 0;\n }\n\n /** Returns the text label of this item. */\n getTextLabel(): string {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return (this.textContent ?? '').trim();\n return slot\n .assignedNodes({ flatten: true })\n .map((n) => n.textContent ?? '')\n .join('')\n .trim();\n }\n\n override connectedCallback() {\n super.connectedCallback();\n this._internals.role = 'treeitem';\n // Mirror the role to a DOM attribute too, so `[role=\"treeitem\"]` selectors\n // (CSS, querySelector, Cypress/Playwright CSS) keep matching — the\n // ElementInternals role alone is not attribute-selectable. ARIA states are\n // mirrored the same way in `_aria()` (aria-expanded/selected/disabled/…).\n if (!this.hasAttribute('role')) this.setAttribute('role', 'treeitem');\n this._childObserver = new MutationObserver(() => this._syncChildren());\n this._childObserver.observe(this, { childList: true });\n this._syncChildren();\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._childObserver?.disconnect();\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('expanded')) {\n this._reflectExpanded();\n // Emit lazy-load for ANY transition to expanded (keyboard, chevron,\n // `expandAll()`…), not just `toggle()` — otherwise keyboard users expand a\n // lazy branch with no children and no fetch. Fires while still `lazy`; the\n // consumer appends children and clears `lazy`, so it won't re-fire.\n if (this.expanded && this.lazy) this.dispatchEvent(new LazyLoadEvent());\n this.dispatchEvent(this.expanded ? new ExpandEvent() : new CollapseEvent());\n }\n\n if (changed.has('selected')) {\n this._aria('ariaSelected', 'aria-selected', String(this.selected));\n }\n\n if (changed.has('disabled')) {\n this._aria('ariaDisabled', 'aria-disabled', this.disabled ? 'true' : null);\n }\n\n if (changed.has('loading')) {\n this._aria('ariaBusy', 'aria-busy', this.loading ? 'true' : null);\n }\n }\n\n /** Leaf items omit `aria-expanded` entirely; branches reflect their state. */\n private _reflectExpanded() {\n this._aria('ariaExpanded', 'aria-expanded', this.isLeaf() ? null : String(this.expanded));\n }\n\n /**\n * Write an ARIA state to BOTH ElementInternals (the semantic source, in the\n * accessibility tree) and a content attribute (so `[aria-*]` CSS / query / test\n * selectors keep matching — same belt-and-suspenders as the mirrored `role`).\n * A `null` value clears both.\n */\n private _aria(key: keyof ElementInternals, attr: string, value: string | null) {\n (this._internals as unknown as Record<string, string | null>)[key] = value;\n if (value === null) this.removeAttribute(attr);\n else this.setAttribute(attr, value);\n }\n\n private _setState(name: string, on: boolean) {\n if (!this._internals.states) return;\n if (on) this._internals.states.add(name);\n else this._internals.states.delete(name);\n }\n\n private _syncChildren() {\n // Auto-slot nested tree-items into the `children` slot so they render in the group container,\n // while label text/elements remain in the default slot.\n const childTag = tagName('tree-item').toUpperCase();\n let count = 0;\n for (const child of Array.from(this.children) as HTMLElement[]) {\n if (child.tagName === childTag) {\n count++;\n if (child.slot !== 'children') child.slot = 'children';\n }\n }\n this._hasChildren = count > 0;\n this._setState('has-children', this._hasChildren);\n if (!this._hasChildren && !this.lazy && this.expanded) {\n this.expanded = false;\n }\n this._reflectExpanded();\n }\n\n /** Toggle expand state. Opening a `lazy` item emits `lazy-load` (via `updated`). */\n toggle() {\n if (this.isLeaf() && !this.lazy) return;\n // lazy-load is emitted centrally from `updated()` on the expand transition.\n this.expanded = !this.expanded;\n }\n\n private _onCheckboxChange = (event: Event) => {\n event.stopPropagation();\n const input = event.target as HTMLInputElement;\n this.dispatchEvent(new TreeItemSelectionToggleEvent(this, input.checked));\n };\n\n override render() {\n return html`\n <div\n class=\"item\"\n part=\"base\"\n >\n <span\n class=\"expand\"\n part=\"expand-button\"\n aria-hidden=\"true\"\n >\n ${\n this.loading\n ? html`<span\n class=\"spinner\"\n role=\"status\"\n ></span>`\n : this.expanded || this.isLeaf()\n ? html`<slot name=\"collapse-icon\">\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M3.5 6l4.5 5 4.5-5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </slot>`\n : html`<slot name=\"expand-icon\">\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M6 3.5l5 4.5-5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </slot>`\n }\n </span>\n\n <input\n class=\"checkbox ${cls('checkbox')}\"\n part=\"checkbox\"\n type=\"checkbox\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n .checked=${live(this.selected)}\n .indeterminate=${live(this.indeterminate)}\n ?disabled=${this.disabled}\n @click=${(e: Event) => e.stopPropagation()}\n @change=${this._onCheckboxChange}\n />\n\n <slot name=\"prefix\"></slot>\n <span\n class=\"label\"\n part=\"label\"\n ><slot></slot\n ></span>\n <slot name=\"suffix\"></slot>\n </div>\n\n <div\n class=\"branch\"\n part=\"branch\"\n >\n <div\n class=\"content\"\n part=\"content\"\n >\n <slot name=\"content\"></slot>\n </div>\n <div\n class=\"children\"\n part=\"children\"\n role=\"group\"\n >\n <slot name=\"children\"></slot>\n </div>\n </div>\n `;\n }\n}\n"],"ignoreList":[0,1],"mappings":"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"}
@@ -32,6 +32,13 @@ export declare class ListboxNavController implements ReactiveController {
32
32
  private config;
33
33
  /** Index of the active option, or `-1` when none is active. */
34
34
  activeIndex: number;
35
+ /**
36
+ * Whether the keyboard (not the pointer) last moved the active option. DOM
37
+ * focus stays on the input, so `:focus-visible` cannot tell: hosts render it
38
+ * on the listbox (`data-nav="keyboard"`) and the active option then draws the
39
+ * focus ring, while a pointer-driven one keeps the plain hover tint.
40
+ */
41
+ keyboard: boolean;
35
42
  constructor(host: ReactiveControllerHost, config: ListboxNavConfig);
36
43
  hostConnected(): void;
37
44
  /** The DOM id for the option at `index` (matches the rendered option `id`). */
@@ -1 +1 @@
1
- {"version":3,"file":"listbox-nav.d.ts","sourceRoot":"","sources":["../../../src/html/shared/controllers/listbox-nav.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,KAAK,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,MAAM,CAAC;IACvB,4EAA4E;IAC5E,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACvC,6CAA6C;IAC7C,MAAM,EAAE,MAAM,OAAO,CAAC;IACtB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,oEAAoE;IACpE,iBAAiB,EAAE,MAAM,SAAS,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;CACpE;AAED;;;;;;;;;GASG;AACH,qBAAa,oBAAqB,YAAW,kBAAkB;IAC7D,OAAO,CAAC,IAAI,CAAyB;IACrC,OAAO,CAAC,MAAM,CAAmB;IAEjC,+DAA+D;IAC/D,WAAW,SAAM;IAEjB,YAAY,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,gBAAgB,EAIjE;IAED,aAAa,SAAK;IAElB,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9B;IAED,6EAA6E;IAC7E,IAAI,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAIzC;IAED,+BAA+B;IAC/B,KAAK,SAIJ;IAED,8EAA8E;IAC9E,SAAS,CAAC,KAAK,EAAE,MAAM,QAItB;IAED,6EAA6E;IAC7E,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,QAOhB;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,MAAO,YAAY,UAQvC;IAEF;;;OAGG;IACH,QAAQ,CAAC,cAAc,MAAO,YAAY,UAGxC;IAEF;;;;OAIG;IACH,SAAS,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAanC;IAED,OAAO,CAAC,KAAK;IAmBb,OAAO,CAAC,qBAAqB;CAI9B"}
1
+ {"version":3,"file":"listbox-nav.d.ts","sourceRoot":"","sources":["../../../src/html/shared/controllers/listbox-nav.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,KAAK,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,MAAM,CAAC;IACvB,4EAA4E;IAC5E,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACvC,6CAA6C;IAC7C,MAAM,EAAE,MAAM,OAAO,CAAC;IACtB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,oEAAoE;IACpE,iBAAiB,EAAE,MAAM,SAAS,CAAC,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;CACpE;AAED;;;;;;;;;GASG;AACH,qBAAa,oBAAqB,YAAW,kBAAkB;IAC7D,OAAO,CAAC,IAAI,CAAyB;IACrC,OAAO,CAAC,MAAM,CAAmB;IAEjC,+DAA+D;IAC/D,WAAW,SAAM;IAEjB;;;;;OAKG;IACH,QAAQ,UAAS;IAEjB,YAAY,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,gBAAgB,EAIjE;IAED,aAAa,SAAK;IAElB,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9B;IAED,6EAA6E;IAC7E,IAAI,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAIzC;IAED,+BAA+B;IAC/B,KAAK,SAIJ;IAED,8EAA8E;IAC9E,SAAS,CAAC,KAAK,EAAE,MAAM,QAItB;IAED,6EAA6E;IAC7E,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,QAQhB;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,MAAO,YAAY,UAYvC;IAEF;;;OAGG;IACH,QAAQ,CAAC,cAAc,MAAO,YAAY,UAGxC;IAEF;;;;OAIG;IACH,SAAS,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAanC;IAED,OAAO,CAAC,KAAK;IAmBb,OAAO,CAAC,qBAAqB;CAI9B"}
@@ -1,2 +1,2 @@
1
- var e=class{constructor(e,t){this.activeIndex=-1,this.onPointerMove=e=>{if(e.pointerType===`touch`)return;let t=e.target.closest(`[role="option"]`);if(!t)return;let n=Array.prototype.indexOf.call(this.config.getOptionElements()??[],t);n<0||(this.config.isDisabled(n)?this.reset():this.setActive(n))},this.onPointerLeave=e=>{e.pointerType!==`touch`&&this.reset()},this.host=e,this.config=t,e.addController(this)}hostConnected(){}optionId(e){return`${this.config.listboxId}-opt-${e}`}get activeDescendant(){return this.config.isOpen()&&this.activeIndex>=0?this.optionId(this.activeIndex):void 0}reset(){this.activeIndex!==-1&&(this.activeIndex=-1,this.host.requestUpdate())}setActive(e){this.activeIndex!==e&&(this.activeIndex=e,this.host.requestUpdate())}move(e){this.config.isOpen()||(this.config.open(),this.activeIndex=e>0?-1:this.config.getCount()),this._step(e)}onKeyDown(e){switch(e.key){case`ArrowDown`:return e.preventDefault(),this.move(1),!0;case`ArrowUp`:return e.preventDefault(),this.move(-1),!0;default:return!1}}_step(e){let t=this.config.getCount();if(t===0)return;let n=this.activeIndex+e;n<0&&(n=t-1),n>=t&&(n=0);let r=n;for(;this.config.isDisabled(n);)if(n+=e,n<0&&(n=t-1),n>=t&&(n=0),n===r)return;this.activeIndex=n,this.host.requestUpdate(),this.host.updateComplete.then(()=>this._scrollActiveIntoView())}_scrollActiveIntoView(){this.config.getOptionElements()?.[this.activeIndex]?.scrollIntoView({block:`nearest`})}};export{e as ListboxNavController};
1
+ var e=class{constructor(e,t){this.activeIndex=-1,this.keyboard=!1,this.onPointerMove=e=>{if(e.pointerType===`touch`)return;let t=e.target.closest(`[role="option"]`);if(!t)return;this.keyboard&&(this.keyboard=!1,this.host.requestUpdate());let n=Array.prototype.indexOf.call(this.config.getOptionElements()??[],t);n<0||(this.config.isDisabled(n)?this.reset():this.setActive(n))},this.onPointerLeave=e=>{e.pointerType!==`touch`&&this.reset()},this.host=e,this.config=t,e.addController(this)}hostConnected(){}optionId(e){return`${this.config.listboxId}-opt-${e}`}get activeDescendant(){return this.config.isOpen()&&this.activeIndex>=0?this.optionId(this.activeIndex):void 0}reset(){this.activeIndex!==-1&&(this.activeIndex=-1,this.host.requestUpdate())}setActive(e){this.activeIndex!==e&&(this.activeIndex=e,this.host.requestUpdate())}move(e){this.keyboard=!0,this.config.isOpen()||(this.config.open(),this.activeIndex=e>0?-1:this.config.getCount()),this._step(e)}onKeyDown(e){switch(e.key){case`ArrowDown`:return e.preventDefault(),this.move(1),!0;case`ArrowUp`:return e.preventDefault(),this.move(-1),!0;default:return!1}}_step(e){let t=this.config.getCount();if(t===0)return;let n=this.activeIndex+e;n<0&&(n=t-1),n>=t&&(n=0);let r=n;for(;this.config.isDisabled(n);)if(n+=e,n<0&&(n=t-1),n>=t&&(n=0),n===r)return;this.activeIndex=n,this.host.requestUpdate(),this.host.updateComplete.then(()=>this._scrollActiveIntoView())}_scrollActiveIntoView(){this.config.getOptionElements()?.[this.activeIndex]?.scrollIntoView({block:`nearest`})}};export{e as ListboxNavController};
2
2
  //# sourceMappingURL=listbox-nav.js.map