luxen-ui 0.17.1 → 0.18.1

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 (109) hide show
  1. package/cdn/custom-elements.json +309 -180
  2. package/cdn/elements/alert/alert.d.ts +0 -5
  3. package/cdn/elements/alert/alert.d.ts.map +1 -1
  4. package/cdn/elements/alert/alert.js.map +1 -1
  5. package/cdn/elements/alert-dialog/alert-dialog.d.ts.map +1 -1
  6. package/cdn/elements/alert-dialog/alert-dialog.js +8 -8
  7. package/cdn/elements/alert-dialog/alert-dialog.js.map +1 -1
  8. package/cdn/elements/avatar/avatar.js +3 -3
  9. package/cdn/elements/form-field/form-field.d.ts.map +1 -1
  10. package/cdn/elements/form-field/form-field.js +1 -1
  11. package/cdn/elements/form-field/form-field.js.map +1 -1
  12. package/cdn/elements/input-otp/input-otp.d.ts.map +1 -1
  13. package/cdn/elements/input-otp/input-otp.js +1 -1
  14. package/cdn/elements/input-otp/input-otp.js.map +1 -1
  15. package/cdn/elements/input-stepper/input-stepper.js +1 -1
  16. package/cdn/elements/input-stepper/input-stepper.js.map +1 -1
  17. package/cdn/elements/prose-editor/prose-editor.d.ts +1 -0
  18. package/cdn/elements/prose-editor/prose-editor.d.ts.map +1 -1
  19. package/cdn/elements/prose-editor/prose-editor.js +8 -8
  20. package/cdn/elements/prose-editor/prose-editor.js.map +1 -1
  21. package/cdn/elements/select/select.js +1 -1
  22. package/cdn/elements/slider/slider.d.ts +0 -5
  23. package/cdn/elements/slider/slider.d.ts.map +1 -1
  24. package/cdn/elements/slider/slider.js.map +1 -1
  25. package/cdn/elements/stories-viewer/stories-viewer.d.ts.map +1 -1
  26. package/cdn/elements/stories-viewer/stories-viewer.js +13 -13
  27. package/cdn/elements/stories-viewer/stories-viewer.js.map +1 -1
  28. package/cdn/elements/story/story.d.ts.map +1 -1
  29. package/cdn/elements/story/story.js +11 -11
  30. package/cdn/elements/story/story.js.map +1 -1
  31. package/cdn/elements/tag/tag.d.ts +65 -2
  32. package/cdn/elements/tag/tag.d.ts.map +1 -1
  33. package/cdn/elements/tag/tag.js +53 -17
  34. package/cdn/elements/tag/tag.js.map +1 -1
  35. package/cdn/elements/tree-item/tree-item.js +6 -6
  36. package/cdn/elements/tree-item/tree-item.js.map +1 -1
  37. package/cdn/shared/styles/button-core.styles.js.map +1 -1
  38. package/cdn/shared/styles/checkbox-appearance.styles.js +1 -1
  39. package/cdn/shared/styles/checkbox-appearance.styles.js.map +1 -1
  40. package/cdn/standalone.css +277 -24
  41. package/cdn/styles/elements/button.css +6 -0
  42. package/cdn/styles/elements/checkbox.css +49 -0
  43. package/cdn/styles/elements/form-field.css +50 -0
  44. package/cdn/styles/elements/input-otp.css +7 -2
  45. package/cdn/styles/elements/input-stepper/default.css +10 -4
  46. package/cdn/styles/elements/input-stepper/rounded.css +6 -2
  47. package/cdn/styles/elements/input.css +19 -1
  48. package/cdn/styles/elements/prose-editor.css +46 -0
  49. package/cdn/styles/elements/radio.css +23 -1
  50. package/cdn/styles/elements/segmented-control.css +26 -7
  51. package/cdn/styles/elements/switch.css +28 -2
  52. package/cdn/styles/preset.css +7 -5
  53. package/cdn/styles/tokens/forms.css +7 -5
  54. package/cdn/styles/tokens.css +7 -5
  55. package/dist/css/elements/button.css +6 -0
  56. package/dist/css/elements/checkbox.css +49 -0
  57. package/dist/css/elements/form-field.css +50 -0
  58. package/dist/css/elements/input-otp.css +7 -2
  59. package/dist/css/elements/input-stepper/default.css +10 -4
  60. package/dist/css/elements/input-stepper/rounded.css +6 -2
  61. package/dist/css/elements/input.css +19 -1
  62. package/dist/css/elements/prose-editor.css +46 -0
  63. package/dist/css/elements/radio.css +23 -1
  64. package/dist/css/elements/segmented-control.css +26 -7
  65. package/dist/css/elements/switch.css +28 -2
  66. package/dist/css/preset.css +7 -5
  67. package/dist/css/tokens/forms.css +7 -5
  68. package/dist/css/tokens.css +7 -5
  69. package/dist/custom-elements.json +309 -180
  70. package/dist/elements/alert/alert.d.ts +0 -5
  71. package/dist/elements/alert/alert.d.ts.map +1 -1
  72. package/dist/elements/alert-dialog/alert-dialog.d.ts.map +1 -1
  73. package/dist/elements/alert-dialog/alert-dialog.js +15 -3
  74. package/dist/elements/form-field/form-field.d.ts.map +1 -1
  75. package/dist/elements/form-field/form-field.js +5 -3
  76. package/dist/elements/input-otp/input-otp.d.ts.map +1 -1
  77. package/dist/elements/input-otp/input-otp.js +4 -3
  78. package/dist/elements/input-stepper/input-stepper.js +4 -4
  79. package/dist/elements/prose-editor/prose-editor.css +31 -0
  80. package/dist/elements/prose-editor/prose-editor.d.ts +1 -0
  81. package/dist/elements/prose-editor/prose-editor.d.ts.map +1 -1
  82. package/dist/elements/prose-editor/prose-editor.js +1 -0
  83. package/dist/elements/slider/slider.css +6 -0
  84. package/dist/elements/slider/slider.d.ts +0 -5
  85. package/dist/elements/slider/slider.d.ts.map +1 -1
  86. package/dist/elements/stories-viewer/stories-viewer.d.ts.map +1 -1
  87. package/dist/elements/stories-viewer/stories-viewer.js +4 -1
  88. package/dist/elements/story/story.d.ts.map +1 -1
  89. package/dist/elements/story/story.js +9 -4
  90. package/dist/elements/tag/tag.css +202 -16
  91. package/dist/elements/tag/tag.d.ts +65 -2
  92. package/dist/elements/tag/tag.d.ts.map +1 -1
  93. package/dist/elements/tag/tag.js +184 -31
  94. package/dist/elements/tree-item/tree-item.js +2 -2
  95. package/dist/metadata/index.json +75 -4
  96. package/dist/metadata/prose-editor.json +5 -0
  97. package/dist/metadata/tag.json +69 -3
  98. package/dist/shared/styles/button-core.css +6 -0
  99. package/dist/shared/styles/checkbox-appearance.css +49 -0
  100. package/dist/templates/elements/checkbox.md +2 -0
  101. package/dist/templates/elements/form-field.md +33 -0
  102. package/dist/templates/elements/input.md +1 -1
  103. package/dist/templates/elements/prose-editor.md +2 -1
  104. package/dist/templates/elements/radio.md +2 -0
  105. package/dist/templates/elements/select.md +14 -0
  106. package/dist/templates/elements/tag.md +231 -8
  107. package/dist/templates/elements/textarea.md +1 -1
  108. package/package.json +1 -1
  109. package/vite-plugin.ts +7 -0
@@ -172,6 +172,181 @@
172
172
  }
173
173
  ]
174
174
  },
175
+ {
176
+ "kind": "javascript-module",
177
+ "path": "src/html/elements/avatar/avatar.ts",
178
+ "declarations": [
179
+ {
180
+ "kind": "class",
181
+ "description": "",
182
+ "name": "Avatar",
183
+ "cssProperties": [
184
+ {
185
+ "description": "Box size (any length). Set it inline (e.g. `style=\"--size: 20px\"`) for an arbitrary size beyond the `size` token scale; the font then follows proportionally. The `size` attribute sets it to the matching token.",
186
+ "name": "--size",
187
+ "default": "40px"
188
+ },
189
+ {
190
+ "description": "Background fill color for initials and the default icon.",
191
+ "name": "--color"
192
+ },
193
+ {
194
+ "description": "Initials/icon color over `--color`. Defaults to an auto-derived readable color; set it to enforce a specific brand color (overrides the automatic choice).",
195
+ "name": "--text-color"
196
+ },
197
+ {
198
+ "description": "Set to `circle` for a fully circular avatar (default is a rounded square).",
199
+ "name": "--appearance"
200
+ }
201
+ ],
202
+ "cssParts": [
203
+ {
204
+ "description": "The avatar container that paints `--color`; style it (e.g. `color`) to override the auto-derived text color.",
205
+ "name": "base"
206
+ }
207
+ ],
208
+ "members": [
209
+ {
210
+ "kind": "field",
211
+ "name": "src",
212
+ "type": {
213
+ "text": "string"
214
+ },
215
+ "default": "''",
216
+ "description": "Image URL. Falls back to initials (then the default icon) if it fails to load.",
217
+ "attribute": "src"
218
+ },
219
+ {
220
+ "kind": "field",
221
+ "name": "name",
222
+ "type": {
223
+ "text": "string"
224
+ },
225
+ "default": "''",
226
+ "description": "Name used as the accessible label and to derive the initials fallback.",
227
+ "attribute": "name"
228
+ },
229
+ {
230
+ "kind": "field",
231
+ "name": "size",
232
+ "type": {
233
+ "text": "string"
234
+ },
235
+ "default": "'md'",
236
+ "description": "Avatar size: `xs`, `sm`, `md` (default), `lg`, or `xl`.",
237
+ "attribute": "size",
238
+ "reflects": true
239
+ },
240
+ {
241
+ "kind": "field",
242
+ "name": "badge",
243
+ "type": {
244
+ "text": "number"
245
+ },
246
+ "default": "0",
247
+ "description": "Count shown in the corner badge. `0` hides the badge.",
248
+ "attribute": "badge"
249
+ },
250
+ {
251
+ "kind": "field",
252
+ "name": "interactive",
253
+ "type": {
254
+ "text": "boolean"
255
+ },
256
+ "default": "false",
257
+ "description": "Render as a `<button>` with focus ring and hover states.",
258
+ "attribute": "interactive",
259
+ "reflects": true
260
+ }
261
+ ],
262
+ "attributes": [
263
+ {
264
+ "name": "src",
265
+ "type": {
266
+ "text": "string"
267
+ },
268
+ "default": "''",
269
+ "description": "Image URL. Falls back to initials (then the default icon) if it fails to load.",
270
+ "fieldName": "src"
271
+ },
272
+ {
273
+ "name": "name",
274
+ "type": {
275
+ "text": "string"
276
+ },
277
+ "default": "''",
278
+ "description": "Name used as the accessible label and to derive the initials fallback.",
279
+ "fieldName": "name"
280
+ },
281
+ {
282
+ "name": "size",
283
+ "type": {
284
+ "text": "string"
285
+ },
286
+ "default": "'md'",
287
+ "description": "Avatar size: `xs`, `sm`, `md` (default), `lg`, or `xl`.",
288
+ "fieldName": "size"
289
+ },
290
+ {
291
+ "name": "badge",
292
+ "type": {
293
+ "text": "number"
294
+ },
295
+ "default": "0",
296
+ "description": "Count shown in the corner badge. `0` hides the badge.",
297
+ "fieldName": "badge"
298
+ },
299
+ {
300
+ "name": "interactive",
301
+ "type": {
302
+ "text": "boolean"
303
+ },
304
+ "default": "false",
305
+ "description": "Render as a `<button>` with focus ring and hover states.",
306
+ "fieldName": "interactive"
307
+ }
308
+ ],
309
+ "superclass": {
310
+ "name": "LuxenElement",
311
+ "module": "/src/html/shared/luxen-element.js"
312
+ },
313
+ "tagName": "l-avatar",
314
+ "customElement": true,
315
+ "summary": "An avatar component for displaying user images, initials, or a default icon.",
316
+ "_cssClasses": [
317
+ {
318
+ "name": ".l-avatar-group",
319
+ "description": "Flex wrapper that overlaps a row of stacked avatars."
320
+ }
321
+ ]
322
+ }
323
+ ],
324
+ "exports": [
325
+ {
326
+ "kind": "js",
327
+ "name": "Avatar",
328
+ "declaration": {
329
+ "name": "Avatar",
330
+ "module": "src/html/elements/avatar/avatar.ts"
331
+ }
332
+ }
333
+ ]
334
+ },
335
+ {
336
+ "kind": "javascript-module",
337
+ "path": "src/html/elements/avatar/index.ts",
338
+ "declarations": [],
339
+ "exports": [
340
+ {
341
+ "kind": "js",
342
+ "name": "*",
343
+ "declaration": {
344
+ "name": "*",
345
+ "module": "src/html/elements/avatar/avatar.js"
346
+ }
347
+ }
348
+ ]
349
+ },
175
350
  {
176
351
  "kind": "javascript-module",
177
352
  "path": "src/html/elements/alert-dialog/alert-dialog.ts",
@@ -652,181 +827,6 @@
652
827
  }
653
828
  ]
654
829
  },
655
- {
656
- "kind": "javascript-module",
657
- "path": "src/html/elements/avatar/avatar.ts",
658
- "declarations": [
659
- {
660
- "kind": "class",
661
- "description": "",
662
- "name": "Avatar",
663
- "cssProperties": [
664
- {
665
- "description": "Box size (any length). Set it inline (e.g. `style=\"--size: 20px\"`) for an arbitrary size beyond the `size` token scale; the font then follows proportionally. The `size` attribute sets it to the matching token.",
666
- "name": "--size",
667
- "default": "40px"
668
- },
669
- {
670
- "description": "Background fill color for initials and the default icon.",
671
- "name": "--color"
672
- },
673
- {
674
- "description": "Initials/icon color over `--color`. Defaults to an auto-derived readable color; set it to enforce a specific brand color (overrides the automatic choice).",
675
- "name": "--text-color"
676
- },
677
- {
678
- "description": "Set to `circle` for a fully circular avatar (default is a rounded square).",
679
- "name": "--appearance"
680
- }
681
- ],
682
- "cssParts": [
683
- {
684
- "description": "The avatar container that paints `--color`; style it (e.g. `color`) to override the auto-derived text color.",
685
- "name": "base"
686
- }
687
- ],
688
- "members": [
689
- {
690
- "kind": "field",
691
- "name": "src",
692
- "type": {
693
- "text": "string"
694
- },
695
- "default": "''",
696
- "description": "Image URL. Falls back to initials (then the default icon) if it fails to load.",
697
- "attribute": "src"
698
- },
699
- {
700
- "kind": "field",
701
- "name": "name",
702
- "type": {
703
- "text": "string"
704
- },
705
- "default": "''",
706
- "description": "Name used as the accessible label and to derive the initials fallback.",
707
- "attribute": "name"
708
- },
709
- {
710
- "kind": "field",
711
- "name": "size",
712
- "type": {
713
- "text": "string"
714
- },
715
- "default": "'md'",
716
- "description": "Avatar size: `xs`, `sm`, `md` (default), `lg`, or `xl`.",
717
- "attribute": "size",
718
- "reflects": true
719
- },
720
- {
721
- "kind": "field",
722
- "name": "badge",
723
- "type": {
724
- "text": "number"
725
- },
726
- "default": "0",
727
- "description": "Count shown in the corner badge. `0` hides the badge.",
728
- "attribute": "badge"
729
- },
730
- {
731
- "kind": "field",
732
- "name": "interactive",
733
- "type": {
734
- "text": "boolean"
735
- },
736
- "default": "false",
737
- "description": "Render as a `<button>` with focus ring and hover states.",
738
- "attribute": "interactive",
739
- "reflects": true
740
- }
741
- ],
742
- "attributes": [
743
- {
744
- "name": "src",
745
- "type": {
746
- "text": "string"
747
- },
748
- "default": "''",
749
- "description": "Image URL. Falls back to initials (then the default icon) if it fails to load.",
750
- "fieldName": "src"
751
- },
752
- {
753
- "name": "name",
754
- "type": {
755
- "text": "string"
756
- },
757
- "default": "''",
758
- "description": "Name used as the accessible label and to derive the initials fallback.",
759
- "fieldName": "name"
760
- },
761
- {
762
- "name": "size",
763
- "type": {
764
- "text": "string"
765
- },
766
- "default": "'md'",
767
- "description": "Avatar size: `xs`, `sm`, `md` (default), `lg`, or `xl`.",
768
- "fieldName": "size"
769
- },
770
- {
771
- "name": "badge",
772
- "type": {
773
- "text": "number"
774
- },
775
- "default": "0",
776
- "description": "Count shown in the corner badge. `0` hides the badge.",
777
- "fieldName": "badge"
778
- },
779
- {
780
- "name": "interactive",
781
- "type": {
782
- "text": "boolean"
783
- },
784
- "default": "false",
785
- "description": "Render as a `<button>` with focus ring and hover states.",
786
- "fieldName": "interactive"
787
- }
788
- ],
789
- "superclass": {
790
- "name": "LuxenElement",
791
- "module": "/src/html/shared/luxen-element.js"
792
- },
793
- "tagName": "l-avatar",
794
- "customElement": true,
795
- "summary": "An avatar component for displaying user images, initials, or a default icon.",
796
- "_cssClasses": [
797
- {
798
- "name": ".l-avatar-group",
799
- "description": "Flex wrapper that overlaps a row of stacked avatars."
800
- }
801
- ]
802
- }
803
- ],
804
- "exports": [
805
- {
806
- "kind": "js",
807
- "name": "Avatar",
808
- "declaration": {
809
- "name": "Avatar",
810
- "module": "src/html/elements/avatar/avatar.ts"
811
- }
812
- }
813
- ]
814
- },
815
- {
816
- "kind": "javascript-module",
817
- "path": "src/html/elements/avatar/index.ts",
818
- "declarations": [],
819
- "exports": [
820
- {
821
- "kind": "js",
822
- "name": "*",
823
- "declaration": {
824
- "name": "*",
825
- "module": "src/html/elements/avatar/avatar.js"
826
- }
827
- }
828
- ]
829
- },
830
830
  {
831
831
  "kind": "javascript-module",
832
832
  "path": "src/html/elements/badge/badge.ts",
@@ -5277,6 +5277,10 @@
5277
5277
  "description": "Color of the editor frame border.",
5278
5278
  "name": "--border-color"
5279
5279
  },
5280
+ {
5281
+ "description": "Color of the editor frame border while the editable area has focus.",
5282
+ "name": "--border-color-focus"
5283
+ },
5280
5284
  {
5281
5285
  "description": "Width of the editor frame border.",
5282
5286
  "name": "--border-width"
@@ -8727,13 +8731,45 @@
8727
8731
  },
8728
8732
  {
8729
8733
  "kind": "class",
8730
- "description": "A compact chip for tokens, filters, and multi-select values. Shadow DOM so its\nstyles are self-contained and it renders correctly **anywhere**, including\ninside another element's shadow root (e.g. the `<l-select>` multi-select\ntrigger).",
8734
+ "description": "Fired when a `selectable` tag is toggled. Bubbles; not composed.",
8735
+ "name": "TagChangeEvent",
8736
+ "members": [
8737
+ {
8738
+ "kind": "field",
8739
+ "name": "selected",
8740
+ "type": {
8741
+ "text": "boolean"
8742
+ },
8743
+ "readonly": true,
8744
+ "default": "selected"
8745
+ }
8746
+ ],
8747
+ "superclass": {
8748
+ "name": "Event",
8749
+ "module": "src/html/elements/tag/tag.ts"
8750
+ }
8751
+ },
8752
+ {
8753
+ "kind": "class",
8754
+ "description": "A compact chip for tokens, filters, and multi-select values. Shadow DOM so its\nstyles are self-contained and it renders correctly **anywhere**, including\ninside another element's shadow root (e.g. the `<l-select>` multi-select\ntrigger).\n\nAdd `selectable` to turn the chip into a filter control: the tag itself\nbecomes a toggle button (`aria-pressed`), and re-activating a selected tag\ndeselects it — the affordance a radio group cannot offer, so a facet always\nhas a way back to \"all\". Add `control=\"checkbox\"` for a multi-select axis and\nthe library's own checkbox rides inside, with the chip as its label.",
8731
8755
  "name": "Tag",
8732
8756
  "cssProperties": [
8733
8757
  {
8734
8758
  "description": "Corner radius. Defaults to a full pill.",
8735
8759
  "name": "--border-radius"
8736
8760
  },
8761
+ {
8762
+ "description": "Chip height. Defaults to the `size` step (a selectable chip is taller, to keep a comfortable target).",
8763
+ "name": "--height"
8764
+ },
8765
+ {
8766
+ "description": "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.",
8767
+ "name": "--font-size"
8768
+ },
8769
+ {
8770
+ "description": "Horizontal padding. Defaults to the `size` step.",
8771
+ "name": "--padding-inline"
8772
+ },
8737
8773
  {
8738
8774
  "description": "Chip background.",
8739
8775
  "name": "--background"
@@ -8741,6 +8777,14 @@
8741
8777
  {
8742
8778
  "description": "Text color.",
8743
8779
  "name": "--color"
8780
+ },
8781
+ {
8782
+ "description": "Text, border, and checkbox accent when selected. Defaults to the library's form-control accent, lightened in dark mode.",
8783
+ "name": "--selected-color"
8784
+ },
8785
+ {
8786
+ "description": "Chip background when selected. Defaults to a tint of `--selected-color`.",
8787
+ "name": "--selected-background"
8744
8788
  }
8745
8789
  ],
8746
8790
  "cssParts": [
@@ -8752,6 +8796,14 @@
8752
8796
  "description": "The label wrapper.",
8753
8797
  "name": "content"
8754
8798
  },
8799
+ {
8800
+ "description": "The toggle button rendered by `selectable` (not with `control=\"checkbox\"`).",
8801
+ "name": "toggle"
8802
+ },
8803
+ {
8804
+ "description": "The native checkbox rendered by `control=\"checkbox\"`.",
8805
+ "name": "checkbox"
8806
+ },
8755
8807
  {
8756
8808
  "description": "The remove button.",
8757
8809
  "name": "remove"
@@ -8765,6 +8817,10 @@
8765
8817
  {
8766
8818
  "description": "Leading content, e.g. an `<l-icon>` or `<l-avatar>`.",
8767
8819
  "name": "prefix"
8820
+ },
8821
+ {
8822
+ "description": "Trailing content, e.g. a result count. Gets the chip's own gutter, so no margin is needed.",
8823
+ "name": "suffix"
8768
8824
  }
8769
8825
  ],
8770
8826
  "members": [
@@ -8790,6 +8846,39 @@
8790
8846
  "attribute": "removable",
8791
8847
  "reflects": true
8792
8848
  },
8849
+ {
8850
+ "kind": "field",
8851
+ "name": "selectable",
8852
+ "type": {
8853
+ "text": "boolean"
8854
+ },
8855
+ "default": "false",
8856
+ "description": "Make the tag a filter control the user can toggle on and off.",
8857
+ "attribute": "selectable",
8858
+ "reflects": true
8859
+ },
8860
+ {
8861
+ "kind": "field",
8862
+ "name": "selected",
8863
+ "type": {
8864
+ "text": "boolean"
8865
+ },
8866
+ "default": "false",
8867
+ "description": "Whether the tag is selected. Reflected, so `[selected]` is styleable.",
8868
+ "attribute": "selected",
8869
+ "reflects": true
8870
+ },
8871
+ {
8872
+ "kind": "field",
8873
+ "name": "control",
8874
+ "type": {
8875
+ "text": "TagControl"
8876
+ },
8877
+ "default": "'none'",
8878
+ "description": "What drives the selection: `none` (default) makes the chip itself a toggle\nbutton; `checkbox` renders a checkbox inside and makes the chip its label —\nthe right choice for a multi-select facet. Implies `selectable`.",
8879
+ "attribute": "control",
8880
+ "reflects": true
8881
+ },
8793
8882
  {
8794
8883
  "kind": "field",
8795
8884
  "name": "disabled",
@@ -8797,7 +8886,7 @@
8797
8886
  "text": "boolean"
8798
8887
  },
8799
8888
  "default": "false",
8800
- "description": "Disable the tag — dims it and blocks removal.",
8889
+ "description": "Disable the tag — dims it and blocks selection and removal.",
8801
8890
  "attribute": "disabled",
8802
8891
  "reflects": true
8803
8892
  }
@@ -8807,6 +8896,11 @@
8807
8896
  "name": "remove",
8808
8897
  "description": "Fired when the user removes the tag (× click or Backspace/Delete). Cancelable; if not prevented the tag removes itself. Not composed, does not bubble.",
8809
8898
  "cancelable": true
8899
+ },
8900
+ {
8901
+ "name": "change",
8902
+ "description": "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.",
8903
+ "cancelable": false
8810
8904
  }
8811
8905
  ],
8812
8906
  "attributes": [
@@ -8828,13 +8922,40 @@
8828
8922
  "description": "Show a remove button (and enable Backspace/Delete removal).",
8829
8923
  "fieldName": "removable"
8830
8924
  },
8925
+ {
8926
+ "name": "selectable",
8927
+ "type": {
8928
+ "text": "boolean"
8929
+ },
8930
+ "default": "false",
8931
+ "description": "Make the tag a filter control the user can toggle on and off.",
8932
+ "fieldName": "selectable"
8933
+ },
8934
+ {
8935
+ "name": "selected",
8936
+ "type": {
8937
+ "text": "boolean"
8938
+ },
8939
+ "default": "false",
8940
+ "description": "Whether the tag is selected. Reflected, so `[selected]` is styleable.",
8941
+ "fieldName": "selected"
8942
+ },
8943
+ {
8944
+ "name": "control",
8945
+ "type": {
8946
+ "text": "TagControl"
8947
+ },
8948
+ "default": "'none'",
8949
+ "description": "What drives the selection: `none` (default) makes the chip itself a toggle\nbutton; `checkbox` renders a checkbox inside and makes the chip its label —\nthe right choice for a multi-select facet. Implies `selectable`.",
8950
+ "fieldName": "control"
8951
+ },
8831
8952
  {
8832
8953
  "name": "disabled",
8833
8954
  "type": {
8834
8955
  "text": "boolean"
8835
8956
  },
8836
8957
  "default": "false",
8837
- "description": "Disable the tag — dims it and blocks removal.",
8958
+ "description": "Disable the tag — dims it and blocks selection and removal.",
8838
8959
  "fieldName": "disabled"
8839
8960
  }
8840
8961
  ],
@@ -8844,11 +8965,11 @@
8844
8965
  },
8845
8966
  "tagName": "l-tag",
8846
8967
  "customElement": true,
8847
- "summary": "A compact, optionally removable chip.",
8968
+ "summary": "A compact, optionally removable or selectable chip.",
8848
8969
  "examples": [
8849
8970
  {
8850
8971
  "language": "html",
8851
- "code": "<l-tag removable>Design</l-tag>"
8972
+ "code": "<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>"
8852
8973
  }
8853
8974
  ]
8854
8975
  }
@@ -8862,6 +8983,14 @@
8862
8983
  "module": "src/html/elements/tag/tag.ts"
8863
8984
  }
8864
8985
  },
8986
+ {
8987
+ "kind": "js",
8988
+ "name": "TagChangeEvent",
8989
+ "declaration": {
8990
+ "name": "TagChangeEvent",
8991
+ "module": "src/html/elements/tag/tag.ts"
8992
+ }
8993
+ },
8865
8994
  {
8866
8995
  "kind": "js",
8867
8996
  "name": "Tag",
@@ -55,9 +55,4 @@ export declare class Alert extends LuxenElement {
55
55
  private _dismiss;
56
56
  private _animateOut;
57
57
  }
58
- declare global {
59
- interface HTMLElementTagNameMap {
60
- 'l-alert': Alert;
61
- }
62
- }
63
58
  //# sourceMappingURL=alert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"alert.d.ts","sourceRoot":"","sources":["../../../src/html/elements/alert/alert.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAK7D,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAWrE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,KAAM,SAAQ,YAAY;IAC5B,gBAAgB;IAIzB,uFAAuF;IAEvF,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,oFAAoF;IAEpF,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,sCAAsC;IAEtC,WAAW,UAAS;IAEpB,iEAAiE;IAEjE,WAAW,UAAS;IAEpB,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,aAAa,CAAC,CAAgC;IAKtD,OAAO,CAAC,SAAS,CAA4C;IAEpD,iBAAiB;IAKjB,oBAAoB;IAMpB,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC;IAI/C,mFAAmF;IACnF,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,SAAS;IAsBjB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,QAAQ,CAQd;IAEF,OAAO,CAAC,WAAW;CA4BpB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,SAAS,EAAE,KAAK,CAAC;KAClB;CACF"}
1
+ {"version":3,"file":"alert.d.ts","sourceRoot":"","sources":["../../../src/html/elements/alert/alert.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAK7D,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAWrE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,KAAM,SAAQ,YAAY;IAC5B,gBAAgB;IAIzB,uFAAuF;IAEvF,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,oFAAoF;IAEpF,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,sCAAsC;IAEtC,WAAW,UAAS;IAEpB,iEAAiE;IAEjE,WAAW,UAAS;IAEpB,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,aAAa,CAAC,CAAgC;IAKtD,OAAO,CAAC,SAAS,CAA4C;IAEpD,iBAAiB;IAKjB,oBAAoB;IAMpB,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC;IAI/C,mFAAmF;IACnF,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,SAAS;IAsBjB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,QAAQ,CAQd;IAEF,OAAO,CAAC,WAAW;CA4BpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"alert.js","names":[],"sources":["../../../src/html/elements/alert/alert.ts"],"sourcesContent":["import 'iconify-icon';\nimport type { PropertyValues } 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 { HideEvent, AfterHideEvent } from '../../events/index.js';\n\nexport type AlertVariant = 'info' | 'success' | 'warning' | 'danger';\n\n/** Default Iconify icon per variant (neutral falls back to the info glyph). */\nconst DEFAULT_ICONS: Record<string, string> = {\n neutral: 'lucide:info',\n info: 'lucide:info',\n success: 'lucide:circle-check',\n warning: 'lucide:triangle-alert',\n danger: 'lucide:octagon-alert',\n};\n\n/**\n * @summary A callout box that highlights contextual information with a semantic color and a leading icon.\n * @customElement l-alert\n *\n * @event hide - Emitted when a dismissible alert is about to close. Cancelable — call `event.preventDefault()` to keep it open.\n * @event after-hide - Emitted after the dismiss animation completes and the alert is removed from the DOM. Not cancelable.\n *\n * @cssproperty [--gap=0.75rem] - Gap between the icon, content, and close button.\n * @cssproperty [--padding=1rem] - Inner padding.\n * @cssproperty [--border-radius=8px] - Corner radius.\n *\n * @cssClass .l-alert-icon - The leading icon, colored by variant (auto-injected).\n * @cssClass .l-alert-content - Wrapper the authored children are moved into; stacks them vertically (auto-injected).\n * @cssClass .l-alert-title - Optional bold heading placed above the body content. Use a real heading element (e.g. `<h3 class=\"l-alert-title\">`) when the alert is a section callout so it is reachable by heading navigation.\n *\n * @example\n * ```html\n * <l-alert variant=\"warning\" dismissible>\n * <span class=\"l-alert-title\">Heads up</span>\n * This action cannot be undone.\n * </l-alert>\n * ```\n */\nexport class Alert extends LuxenElement {\n override createRenderRoot() {\n return this;\n }\n\n /** Semantic variant: `info`, `success`, `warning`, or `danger`. Default is neutral. */\n @property({ reflect: true })\n variant?: AlertVariant;\n\n /** Iconify icon name (e.g. `lucide:bell`) overriding the variant's default icon. */\n @property()\n icon?: string;\n\n /** Hide the leading icon entirely. */\n @property({ attribute: 'without-icon', type: Boolean })\n withoutIcon = false;\n\n /** Show a close button that dismisses the alert when clicked. */\n @property({ type: Boolean, reflect: true })\n dismissible = false;\n\n private _localize = new LocalizeController(this);\n private _dismissTimer?: ReturnType<typeof setTimeout>;\n // Re-home children the parser (upgrade-before-parse) or the consumer\n // (imperative `append`) adds directly to the host after a sync. Lit's\n // `updated()` only fires on reactive-property changes, so without this an\n // appended text-node body would escape `.l-alert-content`.\n private _observer = new MutationObserver(() => this._sync());\n\n override connectedCallback() {\n super.connectedCallback();\n this._sync();\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._observer.disconnect();\n if (this._dismissTimer) clearTimeout(this._dismissTimer);\n }\n\n override updated(_changed: PropertyValues<this>) {\n this._sync();\n }\n\n /** Idempotently reconcile the injected icon, content wrapper, and close button. */\n private _sync() {\n // Pause the observer so our own DOM moves below don't re-trigger it.\n this._observer.disconnect();\n this._syncIcon();\n this._syncContent();\n this._syncClose();\n if (this.isConnected) this._observer.observe(this, { childList: true });\n }\n\n private _iconName(): string | null {\n if (this.withoutIcon) return null;\n if (this.icon) return this.icon;\n return DEFAULT_ICONS[this.variant ?? 'neutral'];\n }\n\n private _syncIcon() {\n const iconClass = cls('alert-icon');\n let iconEl = this.querySelector<HTMLElement>(`:scope > .${iconClass}`);\n const name = this._iconName();\n\n if (!name) {\n iconEl?.remove();\n return;\n }\n\n if (!iconEl) {\n iconEl = document.createElement('iconify-icon');\n iconEl.className = iconClass;\n iconEl.setAttribute('aria-hidden', 'true');\n this.prepend(iconEl);\n } else if (this.firstElementChild !== iconEl) {\n this.prepend(iconEl);\n }\n\n if (iconEl.getAttribute('icon') !== name) iconEl.setAttribute('icon', name);\n }\n\n /**\n * Move the authored children into a `.l-alert-content` wrapper so they stack\n * as one column regardless of node type. A wrapper (rather than placing bare\n * children directly) is required because text nodes — a plain string body —\n * cannot be targeted by CSS, so they would otherwise escape the layout.\n */\n private _syncContent() {\n const contentClass = cls('alert-content');\n const iconClass = cls('alert-icon');\n const closeClass = cls('close');\n\n let content = this.querySelector<HTMLElement>(`:scope > .${contentClass}`);\n if (!content) {\n content = document.createElement('div');\n content.className = contentClass;\n this.append(content);\n }\n\n for (const node of Array.from(this.childNodes)) {\n if (node === content) continue;\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n if (el.classList.contains(iconClass) || el.classList.contains(closeClass)) continue;\n } else if (node.nodeType !== Node.TEXT_NODE) {\n // Leave comment/marker nodes in place — notably Lit's empty\n // ChildPart marker (`<!---->`) from the default light-DOM render.\n continue;\n }\n content.append(node);\n }\n }\n\n private _syncClose() {\n const closeClass = cls('close');\n let btn = this.querySelector<HTMLButtonElement>(`:scope > .${closeClass}[data-alert-close]`);\n\n if (!this.dismissible) {\n btn?.remove();\n return;\n }\n\n if (!btn) {\n btn = document.createElement('button');\n btn.type = 'button';\n btn.className = closeClass;\n btn.dataset.appearance = 'ring';\n btn.dataset.alertClose = '';\n btn.addEventListener('click', this._dismiss);\n this.append(btn);\n } else if (this.lastElementChild !== btn) {\n this.append(btn);\n }\n\n btn.setAttribute('aria-label', this._localize.term('close'));\n }\n\n private _dismiss = () => {\n // Ignore repeat activations (fast double-click / double AT activation)\n // once a dismissal is already animating out.\n if (this.hasAttribute('data-dismissing')) return;\n if (!this.dispatchEvent(new HideEvent())) return;\n // A `hide` listener may have removed the alert itself; don't animate a\n // node that is no longer in the document.\n if (this.isConnected) this._animateOut();\n };\n\n private _animateOut() {\n this.setAttribute('data-dismissing', '');\n\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n this.removeEventListener('transitionend', onEnd);\n if (this._dismissTimer) clearTimeout(this._dismissTimer);\n this.dispatchEvent(new AfterHideEvent());\n this.remove();\n };\n const onEnd = (e: TransitionEvent) => {\n if (e.target !== this) return;\n finish();\n };\n\n const duration = parseFloat(getComputedStyle(this).transitionDuration) * 1000;\n if (!duration) {\n finish();\n return;\n }\n\n this.addEventListener('transitionend', onEnd);\n // Fallback: hidden Preview tabs never fire `transitionend`. Tracked so\n // `disconnectedCallback` can cancel it if the alert is removed mid-animation.\n this._dismissTimer = setTimeout(finish, duration + 50);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'l-alert': Alert;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"alert.js","names":[],"sources":["../../../src/html/elements/alert/alert.ts"],"sourcesContent":["import 'iconify-icon';\nimport type { PropertyValues } 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 { HideEvent, AfterHideEvent } from '../../events/index.js';\n\nexport type AlertVariant = 'info' | 'success' | 'warning' | 'danger';\n\n/** Default Iconify icon per variant (neutral falls back to the info glyph). */\nconst DEFAULT_ICONS: Record<string, string> = {\n neutral: 'lucide:info',\n info: 'lucide:info',\n success: 'lucide:circle-check',\n warning: 'lucide:triangle-alert',\n danger: 'lucide:octagon-alert',\n};\n\n/**\n * @summary A callout box that highlights contextual information with a semantic color and a leading icon.\n * @customElement l-alert\n *\n * @event hide - Emitted when a dismissible alert is about to close. Cancelable — call `event.preventDefault()` to keep it open.\n * @event after-hide - Emitted after the dismiss animation completes and the alert is removed from the DOM. Not cancelable.\n *\n * @cssproperty [--gap=0.75rem] - Gap between the icon, content, and close button.\n * @cssproperty [--padding=1rem] - Inner padding.\n * @cssproperty [--border-radius=8px] - Corner radius.\n *\n * @cssClass .l-alert-icon - The leading icon, colored by variant (auto-injected).\n * @cssClass .l-alert-content - Wrapper the authored children are moved into; stacks them vertically (auto-injected).\n * @cssClass .l-alert-title - Optional bold heading placed above the body content. Use a real heading element (e.g. `<h3 class=\"l-alert-title\">`) when the alert is a section callout so it is reachable by heading navigation.\n *\n * @example\n * ```html\n * <l-alert variant=\"warning\" dismissible>\n * <span class=\"l-alert-title\">Heads up</span>\n * This action cannot be undone.\n * </l-alert>\n * ```\n */\nexport class Alert extends LuxenElement {\n override createRenderRoot() {\n return this;\n }\n\n /** Semantic variant: `info`, `success`, `warning`, or `danger`. Default is neutral. */\n @property({ reflect: true })\n variant?: AlertVariant;\n\n /** Iconify icon name (e.g. `lucide:bell`) overriding the variant's default icon. */\n @property()\n icon?: string;\n\n /** Hide the leading icon entirely. */\n @property({ attribute: 'without-icon', type: Boolean })\n withoutIcon = false;\n\n /** Show a close button that dismisses the alert when clicked. */\n @property({ type: Boolean, reflect: true })\n dismissible = false;\n\n private _localize = new LocalizeController(this);\n private _dismissTimer?: ReturnType<typeof setTimeout>;\n // Re-home children the parser (upgrade-before-parse) or the consumer\n // (imperative `append`) adds directly to the host after a sync. Lit's\n // `updated()` only fires on reactive-property changes, so without this an\n // appended text-node body would escape `.l-alert-content`.\n private _observer = new MutationObserver(() => this._sync());\n\n override connectedCallback() {\n super.connectedCallback();\n this._sync();\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._observer.disconnect();\n if (this._dismissTimer) clearTimeout(this._dismissTimer);\n }\n\n override updated(_changed: PropertyValues<this>) {\n this._sync();\n }\n\n /** Idempotently reconcile the injected icon, content wrapper, and close button. */\n private _sync() {\n // Pause the observer so our own DOM moves below don't re-trigger it.\n this._observer.disconnect();\n this._syncIcon();\n this._syncContent();\n this._syncClose();\n if (this.isConnected) this._observer.observe(this, { childList: true });\n }\n\n private _iconName(): string | null {\n if (this.withoutIcon) return null;\n if (this.icon) return this.icon;\n return DEFAULT_ICONS[this.variant ?? 'neutral'];\n }\n\n private _syncIcon() {\n const iconClass = cls('alert-icon');\n let iconEl = this.querySelector<HTMLElement>(`:scope > .${iconClass}`);\n const name = this._iconName();\n\n if (!name) {\n iconEl?.remove();\n return;\n }\n\n if (!iconEl) {\n iconEl = document.createElement('iconify-icon');\n iconEl.className = iconClass;\n iconEl.setAttribute('aria-hidden', 'true');\n this.prepend(iconEl);\n } else if (this.firstElementChild !== iconEl) {\n this.prepend(iconEl);\n }\n\n if (iconEl.getAttribute('icon') !== name) iconEl.setAttribute('icon', name);\n }\n\n /**\n * Move the authored children into a `.l-alert-content` wrapper so they stack\n * as one column regardless of node type. A wrapper (rather than placing bare\n * children directly) is required because text nodes — a plain string body —\n * cannot be targeted by CSS, so they would otherwise escape the layout.\n */\n private _syncContent() {\n const contentClass = cls('alert-content');\n const iconClass = cls('alert-icon');\n const closeClass = cls('close');\n\n let content = this.querySelector<HTMLElement>(`:scope > .${contentClass}`);\n if (!content) {\n content = document.createElement('div');\n content.className = contentClass;\n this.append(content);\n }\n\n for (const node of Array.from(this.childNodes)) {\n if (node === content) continue;\n if (node.nodeType === Node.ELEMENT_NODE) {\n const el = node as Element;\n if (el.classList.contains(iconClass) || el.classList.contains(closeClass)) continue;\n } else if (node.nodeType !== Node.TEXT_NODE) {\n // Leave comment/marker nodes in place — notably Lit's empty\n // ChildPart marker (`<!---->`) from the default light-DOM render.\n continue;\n }\n content.append(node);\n }\n }\n\n private _syncClose() {\n const closeClass = cls('close');\n let btn = this.querySelector<HTMLButtonElement>(`:scope > .${closeClass}[data-alert-close]`);\n\n if (!this.dismissible) {\n btn?.remove();\n return;\n }\n\n if (!btn) {\n btn = document.createElement('button');\n btn.type = 'button';\n btn.className = closeClass;\n btn.dataset.appearance = 'ring';\n btn.dataset.alertClose = '';\n btn.addEventListener('click', this._dismiss);\n this.append(btn);\n } else if (this.lastElementChild !== btn) {\n this.append(btn);\n }\n\n btn.setAttribute('aria-label', this._localize.term('close'));\n }\n\n private _dismiss = () => {\n // Ignore repeat activations (fast double-click / double AT activation)\n // once a dismissal is already animating out.\n if (this.hasAttribute('data-dismissing')) return;\n if (!this.dispatchEvent(new HideEvent())) return;\n // A `hide` listener may have removed the alert itself; don't animate a\n // node that is no longer in the document.\n if (this.isConnected) this._animateOut();\n };\n\n private _animateOut() {\n this.setAttribute('data-dismissing', '');\n\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n this.removeEventListener('transitionend', onEnd);\n if (this._dismissTimer) clearTimeout(this._dismissTimer);\n this.dispatchEvent(new AfterHideEvent());\n this.remove();\n };\n const onEnd = (e: TransitionEvent) => {\n if (e.target !== this) return;\n finish();\n };\n\n const duration = parseFloat(getComputedStyle(this).transitionDuration) * 1000;\n if (!duration) {\n finish();\n return;\n }\n\n this.addEventListener('transitionend', onEnd);\n // Fallback: hidden Preview tabs never fire `transitionend`. Tracked so\n // `disconnectedCallback` can cancel it if the alert is removed mid-animation.\n this._dismissTimer = setTimeout(finish, duration + 50);\n }\n}\n"],"mappings":"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"}