luxen-ui 0.17.1 → 0.18.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 (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 +51 -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 +51 -2
  92. package/dist/elements/tag/tag.d.ts.map +1 -1
  93. package/dist/elements/tag/tag.js +151 -22
  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 +225 -8
  107. package/dist/templates/elements/textarea.md +1 -1
  108. package/package.json +1 -1
  109. package/vite-plugin.ts +7 -0
@@ -1 +1 @@
1
- {"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../../src/html/elements/alert-dialog/alert-dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAIpE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAI7C,OAAO,qBAAqB,CAAC;AAO7B;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,KAAK;;CAItC;AAED;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,KAAK;;CAIrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,qBAAa,WAAY,SAAQ,MAAM;IACrC,OAAgB,MAAM,4BAAyD;IAE/E,oCAAoC;IAEpC,WAAW,SAAa;IAExB,mCAAmC;IAEnC,UAAU,SAAY;IAEtB,uEAAuE;IAEvE,IAAI,CAAC,EAAE,QAAQ,CAAC;IAEhB;2DACuD;IAEvD,OAAO,UAAS;IAEhB,cAAuB,KAAK,WAE3B;IAID,OAAO,CAAC,YAAY,CAA4C;IAChE,cAAuB,OAAO,uBAE7B;cAKkB,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;cAOxC,SAAS,CAAC,CAAC,EAAE,KAAK;cAUlB,aAAa;IAehC,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,cAAc,CASpB;cAEiB,YAAY;CAkChC;AAMD,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CACd,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,EACvD,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;IACR,gBAAgB,CACd,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,KAAK,IAAI,EACtD,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;IACR,gBAAgB,CACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,EACvD,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GACvC,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,KAAK,IAAI,EACtD,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GACvC,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GACvC,IAAI,CAAC;CACT"}
1
+ {"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../../src/html/elements/alert-dialog/alert-dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAUpE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAI7C,OAAO,qBAAqB,CAAC;AAU7B;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,KAAK;;CAItC;AAED;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,KAAK;;CAIrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,qBAAa,WAAY,SAAQ,MAAM;IACrC,OAAgB,MAAM,4BAAyD;IAE/E,oCAAoC;IAEpC,WAAW,SAAa;IAExB,mCAAmC;IAEnC,UAAU,SAAY;IAEtB,uEAAuE;IAEvE,IAAI,CAAC,EAAE,QAAQ,CAAC;IAEhB;2DACuD;IAEvD,OAAO,UAAS;IAEhB,cAAuB,KAAK,WAE3B;IAID,OAAO,CAAC,YAAY,CAA4C;IAChE,cAAuB,OAAO,uBAE7B;cAKkB,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;cAOxC,SAAS,CAAC,CAAC,EAAE,KAAK;cAUlB,aAAa;IAehC,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,cAAc,CASpB;cAEiB,YAAY;CAqChC;AAMD,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CACd,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,EACvD,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;IACR,gBAAgB,CACd,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,KAAK,IAAI,EACtD,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;IACR,gBAAgB,CACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,EACvD,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GACvC,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,KAAK,IAAI,EACtD,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GACvC,IAAI,CAAC;IACR,mBAAmB,CACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kCAAkC,EAC5C,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB,GACvC,IAAI,CAAC;CACT"}
@@ -1,4 +1,4 @@
1
- import{i as e}from"../../chunks/lit.js";import{i as t,t as n}from"../../chunks/decorate.js";import{a as r,t as i}from"../../chunks/lit-html.js";import a from"../../shared/styles/host.styles.js";import{HasSlotController as o}from"../../shared/controllers/has-slot-controller.js";import s from"../dialog/dialog.styles.js";import{Dialog as c}from"../dialog/dialog.js";import l from"../../shared/styles/button-core.styles.js";var u=e(`:host{--backdrop:var(--l-backdrop-strong);--backdrop-blur:2px}[part=header]{padding-block-end:.5rem}[part=body]{color:var(--l-color-text-secondary);font-size:var(--l-text-sm);padding-block-end:var(--padding);line-height:1.5}[part=footer]{padding:1rem var(--padding);border-top:1px solid var(--l-color-border);background:var(--l-color-bg-fill-neutral-subtle);border-end-end-radius:var(--border-radius);border-end-start-radius:var(--border-radius)}`),d=class extends Event{constructor(){super(`confirm`,{bubbles:!1,composed:!1,cancelable:!0})}},f=class extends Event{constructor(){super(`cancel`,{bubbles:!1,composed:!1,cancelable:!0})}},p=class extends c{constructor(...e){super(...e),this.confirmText=`Confirm`,this.cancelText=`Cancel`,this.loading=!1,this._bodyContent=new o(this,`[default]`),this._onFooterClick=e=>{for(let t of e.composedPath()){if(t===e.currentTarget)break;if(t instanceof Element){if(t.matches(`[slot="confirm"], [data-confirm]`))return this._activate(t,new d);if(t.matches(`[slot="cancel"], [data-cancel]`))return this._activate(t,new f)}}}}static{this.styles=[a,s,l,u]}get _role(){return`alertdialog`}get _bodyId(){return this._bodyContent.test(`[default]`)?`description`:``}willUpdate(e){super.willUpdate(e),this.lightDismiss&&=!1}_onCancel(e){this.dispatchEvent(new f)||e.preventDefault()}_focusInitial(){let e=[this.querySelector(`[autofocus]`),this.querySelector(`[slot="cancel"]`),this.renderRoot.querySelector(`[data-cancel]`)];for(let t of e)if(!(!t||t.matches(`[disabled], [aria-disabled="true"]`))&&(t.focus({preventScroll:!0}),t===document.activeElement||t.contains(document.activeElement)))return}_close(){this.dialog.open&&this.dialog.close()}_activate(e,t){e.hasAttribute(`data-confirm`)&&this.loading||e.matches(`[disabled], [aria-disabled="true"]`)||this.dispatchEvent(t)&&this._close()}renderFooter(){let e=this.tone===`danger`?`destructive`:`primary`;return r`
1
+ import{cls as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,t as r}from"../../chunks/decorate.js";import{a as i,t as a}from"../../chunks/lit-html.js";import{r as o}from"../../chunks/static-html.js";import{staticTag as s}from"../../static-tag.js";import c from"../../shared/styles/host.styles.js";import{HasSlotController as l}from"../../shared/controllers/has-slot-controller.js";import u from"../dialog/dialog.styles.js";import{Dialog as d}from"../dialog/dialog.js";import f from"../../shared/styles/button-core.styles.js";var p=t(`:host{--backdrop:var(--l-backdrop-strong);--backdrop-blur:2px}[part=header]{padding-block-end:.5rem}[part=body]{color:var(--l-color-text-secondary);font-size:var(--l-text-sm);padding-block-end:var(--padding);line-height:1.5}[part=footer]{padding:1rem var(--padding);border-top:1px solid var(--l-color-border);background:var(--l-color-bg-fill-neutral-subtle);border-end-end-radius:var(--border-radius);border-end-start-radius:var(--border-radius)}`),m=class extends Event{constructor(){super(`confirm`,{bubbles:!1,composed:!1,cancelable:!0})}},h=class extends Event{constructor(){super(`cancel`,{bubbles:!1,composed:!1,cancelable:!0})}},g=class extends d{constructor(...e){super(...e),this.confirmText=`Confirm`,this.cancelText=`Cancel`,this.loading=!1,this._bodyContent=new l(this,`[default]`),this._onFooterClick=e=>{for(let t of e.composedPath()){if(t===e.currentTarget)break;if(t instanceof Element){if(t.matches(`[slot="confirm"], [data-confirm]`))return this._activate(t,new m);if(t.matches(`[slot="cancel"], [data-cancel]`))return this._activate(t,new h)}}}}static{this.styles=[c,u,f,p]}get _role(){return`alertdialog`}get _bodyId(){return this._bodyContent.test(`[default]`)?`description`:``}willUpdate(e){super.willUpdate(e),this.lightDismiss&&=!1}_onCancel(e){this.dispatchEvent(new h)||e.preventDefault()}_focusInitial(){let e=[this.querySelector(`[autofocus]`),this.querySelector(`[slot="cancel"]`),this.renderRoot.querySelector(`[data-cancel]`)];for(let t of e)if(!(!t||t.matches(`[disabled], [aria-disabled="true"]`))&&(t.focus({preventScroll:!0}),t===document.activeElement||t.contains(document.activeElement)))return}_close(){this.dialog.open&&this.dialog.close()}_activate(e,t){e.hasAttribute(`data-confirm`)&&this.loading||e.matches(`[disabled], [aria-disabled="true"]`)||this.dispatchEvent(t)&&this._close()}renderFooter(){let t=this.tone===`danger`?`destructive`:`primary`,n=s(`spinner`);return i`
2
2
  <footer
3
3
  part="footer"
4
4
  @click=${this._onFooterClick}
@@ -6,7 +6,7 @@ import{i as e}from"../../chunks/lit.js";import{i as t,t as n}from"../../chunks/d
6
6
  <slot name="cancel">
7
7
  <button
8
8
  part="button cancel"
9
- class="l-button"
9
+ class=${e(`button`)}
10
10
  type="button"
11
11
  data-cancel
12
12
  >
@@ -16,17 +16,17 @@ import{i as e}from"../../chunks/lit.js";import{i as t,t as n}from"../../chunks/d
16
16
  <slot name="confirm">
17
17
  <button
18
18
  part="button confirm"
19
- class="l-button"
19
+ class=${e(`button`)}
20
20
  type="button"
21
21
  data-confirm
22
- data-variant=${e}
23
- aria-disabled=${this.loading?`true`:i}
24
- aria-busy=${this.loading?`true`:i}
22
+ data-variant=${t}
23
+ aria-disabled=${this.loading?`true`:a}
24
+ aria-busy=${this.loading?`true`:a}
25
25
  >
26
- ${this.loading?r`<l-spinner aria-hidden="true"></l-spinner>`:i}
26
+ ${this.loading?o`<${n} aria-hidden="true"></${n}>`:a}
27
27
  ${this.confirmText}
28
28
  </button>
29
29
  </slot>
30
30
  </footer>
31
- `}};n([t({attribute:`confirm-text`})],p.prototype,`confirmText`,void 0),n([t({attribute:`cancel-text`})],p.prototype,`cancelText`,void 0),n([t({reflect:!0})],p.prototype,`tone`,void 0),n([t({type:Boolean,reflect:!0})],p.prototype,`loading`,void 0);export{p as AlertDialog,f as CancelEvent,d as ConfirmEvent};
31
+ `}};r([n({attribute:`confirm-text`})],g.prototype,`confirmText`,void 0),r([n({attribute:`cancel-text`})],g.prototype,`cancelText`,void 0),r([n({reflect:!0})],g.prototype,`tone`,void 0),r([n({type:Boolean,reflect:!0})],g.prototype,`loading`,void 0);export{g as AlertDialog,h as CancelEvent,m as ConfirmEvent};
32
32
  //# sourceMappingURL=alert-dialog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"alert-dialog.js","names":[],"sources":["../../../src/html/elements/alert-dialog/alert-dialog.css?inline","../../../src/html/elements/alert-dialog/alert-dialog.ts"],"sourcesContent":["/*\n * Layout for l-alert-dialog. Unlayered to match dialog.css (the inherited\n * shared styles) so these overrides win — inside a shadow root there's no host\n * cascade to guard against, and layered rules can't override dialog.css's\n * unlayered ones. The `.l-button` appearance comes from the shared button-core\n * (imported in alert-dialog.ts), not from here.\n *\n * Visual rhythm: a confirmation reads better with the title and description\n * grouped tight and the actions set off in their own bar.\n */\n\n/* Interruptive by nature: a stronger backdrop (the token meant for immersive\n overlays) plus a touch of blur pulls focus firmly onto the confirmation.\n Consumers can still retune --backdrop / --backdrop-blur. */\n:host {\n --backdrop: var(--l-backdrop-strong);\n --backdrop-blur: 2px;\n}\n\n/* Pull the description up close under the title (title keeps the inherited\n 1.125rem / 600 from dialog.css). */\n[part='header'] {\n padding-block-end: 0.5rem;\n}\n\n/* Muted, smaller description with a comfortable reading measure, plus its own\n block-end gap so the footer divider isn't crowded. */\n[part='body'] {\n padding-block-end: var(--padding);\n color: var(--l-color-text-secondary);\n font-size: var(--l-text-sm);\n line-height: 1.5;\n}\n\n/* Footer as a distinct action bar: subtle fill + top divider, corners rounded\n to match the dialog (which doesn't clip its overflow). */\n[part='footer'] {\n padding: 1rem var(--padding);\n border-top: 1px solid var(--l-color-border);\n background: var(--l-color-bg-fill-neutral-subtle);\n border-end-start-radius: var(--border-radius);\n border-end-end-radius: var(--border-radius);\n}\n","import { html, nothing, unsafeCSS, type PropertyValues } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport { HasSlotController } from '../../shared/controllers/has-slot-controller.js';\nimport { Dialog } from '../dialog/dialog.js';\nimport dialogStyles from '../dialog/dialog.styles.js';\nimport buttonStyles from '../../shared/styles/button-core.styles.js';\nimport rawAlertStyles from './alert-dialog.css?inline';\nimport '../spinner/index.js';\n\n// The built-in actions render as `.l-button`, styled inside the shadow root from\n// the SAME shared button-core as the light-DOM `.l-button` (single source of\n// truth — no copy to drift). alert-dialog.css adds only the dialog layout.\nconst alertStyles = unsafeCSS(rawAlertStyles);\n\n/**\n * Fired when the user accepts an alert dialog (the confirm action). Cancelable —\n * call `event.preventDefault()` to keep the dialog open (e.g. to await an async\n * task); close it yourself afterwards by setting `open = false`.\n *\n * Does not bubble and is not composed: listen on the element itself.\n */\nexport class ConfirmEvent extends Event {\n constructor() {\n super('confirm', { bubbles: false, composed: false, cancelable: true });\n }\n}\n\n/**\n * Fired when the user dismisses an alert dialog (the cancel action, Escape, or a\n * close request). Cancelable — call `event.preventDefault()` to keep it open.\n *\n * Does not bubble and is not composed: listen on the element itself.\n */\nexport class CancelEvent extends Event {\n constructor() {\n super('cancel', { bubbles: false, composed: false, cancelable: true });\n }\n}\n\n/**\n * An interruptive confirmation dialog built on `<l-dialog>`. Renders its own\n * cancel/confirm actions and exposes `role=\"alertdialog\"`. Use it to confirm a\n * consequential action (delete, publish, discard) — not for generic content.\n *\n * Open and close by toggling the `open` property (or the `--show` / `--hide`\n * Invoker commands). The dialog closes automatically when the user picks an\n * action, unless the matching `confirm` / `cancel` event is canceled.\n *\n * @slot - Description text. Provides the dialog's accessible description.\n * @slot title - Custom heading element. Overrides the default `<h2>` rendered from the `title` property. Also provides the dialog's accessible name.\n * @slot cancel - Replaces the built-in cancel action (e.g. a link). Keep it focusable.\n * @slot confirm - Replaces the built-in confirm action.\n *\n * @csspart dialog - The native `<dialog>` element.\n * @csspart header - The header wrapper containing the title.\n * @csspart title - The dialog title heading.\n * @csspart body - The body wrapper around the description.\n * @csspart footer - The footer wrapper around the actions.\n * @csspart button - Both built-in action buttons.\n * @csspart cancel - The built-in cancel button.\n * @csspart confirm - The built-in confirm button.\n *\n * @cssproperty [--width=31rem] - Dialog width.\n * @cssproperty [--border-radius=6px] - Dialog border radius.\n * @cssproperty [--padding=1.5rem] - Padding applied to the header, footer, and inline-padding of the body.\n * @cssproperty [--show-duration=200ms] - Open transition duration.\n * @cssproperty [--hide-duration=200ms] - Close transition duration.\n * @cssproperty --backdrop - Backdrop color.\n * @cssproperty [--backdrop-blur=0] - Backdrop blur amount (any CSS length).\n *\n * @event confirm - Fired when the user accepts. Cancelable — `preventDefault()` keeps it open for async work.\n * @event cancel - Fired when the user dismisses (cancel action, Escape). Cancelable — `preventDefault()` keeps it open.\n * @event show - Fired when the dialog is about to open. Cancelable.\n * @event hide - Fired when the dialog is about to close via the `open` property. Cancelable. (The confirm, cancel, and Escape paths close natively and emit `confirm`/`cancel` instead of `hide`.)\n * @event after-show - Fired after the open animation completes. Not cancelable.\n * @event after-hide - Fired after the close animation completes. Not cancelable.\n *\n * @command --show - Sets `open = true`.\n * @command --hide - Sets `open = false`.\n *\n * @customElement l-alert-dialog\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below.\nexport class AlertDialog extends Dialog {\n static override styles = [hostStyles, dialogStyles, buttonStyles, alertStyles];\n\n /** Label for the confirm action. */\n @property({ attribute: 'confirm-text' })\n confirmText = 'Confirm';\n\n /** Label for the cancel action. */\n @property({ attribute: 'cancel-text' })\n cancelText = 'Cancel';\n\n /** Visual tone. `danger` renders the confirm action as destructive. */\n @property({ reflect: true })\n tone?: 'danger';\n\n /** Show a busy state on the confirm action (spinner + `aria-disabled`); the\n * cancel action stays operable as an escape hatch. */\n @property({ type: Boolean, reflect: true })\n loading = false;\n\n protected override get _role() {\n return 'alertdialog';\n }\n\n // Wire aria-describedby to the body only when it actually has content, so a\n // title-only confirmation doesn't advertise an empty description.\n private _bodyContent = new HasSlotController(this, '[default]');\n protected override get _bodyId() {\n return this._bodyContent.test('[default]') ? 'description' : '';\n }\n\n // Alert dialogs are interruptive: they are never dismissable by clicking\n // outside, so the inherited `light-dismiss` is forced off (a vetoed dismissal\n // gesture must go through an explicit action).\n protected override willUpdate(changed: PropertyValues<this>) {\n super.willUpdate(changed);\n if (this.lightDismiss) this.lightDismiss = false;\n }\n\n // Native Escape / close request → cancel intent. A vetoed `cancel` keeps the\n // dialog open; otherwise the native close proceeds (emitting `after-hide`).\n protected override _onCancel(e: Event) {\n if (!this.dispatchEvent(new CancelEvent())) e.preventDefault();\n }\n\n // Focus the least destructive action on open: an author `[autofocus]`, else\n // the slotted cancel action, else the built-in cancel button (in shadow DOM,\n // so the base light-DOM lookup can't reach it). Try each in order and keep the\n // first that actually takes focus — this skips a non-focusable slotted cancel\n // without rejecting a focusable one that reports `tabIndex === -1` (a custom\n // element with `delegatesFocus`, `<div role=\"button\" tabindex=\"0\">`, etc.).\n protected override _focusInitial() {\n const candidates = [\n this.querySelector<HTMLElement>('[autofocus]'),\n this.querySelector<HTMLElement>('[slot=\"cancel\"]'),\n this.renderRoot.querySelector<HTMLElement>('[data-cancel]'),\n ];\n for (const el of candidates) {\n if (!el || el.matches('[disabled], [aria-disabled=\"true\"]')) continue;\n el.focus({ preventScroll: true });\n // `document.activeElement` resolves to the shadow host when focus lands\n // inside an open shadow root, so this also covers delegatesFocus.\n if (el === document.activeElement || el.contains(document.activeElement)) return;\n }\n }\n\n private _close() {\n // Close natively rather than via `open = false` so we don't also emit the\n // inherited cancelable `hide` — `confirm` / `cancel` are the intent events.\n if (this.dialog.open) this.dialog.close();\n }\n\n // Activate an action unless its control is disabled. The built-in confirm also\n // respects `loading` via the property — immune to render timing, so a fast\n // second click in the window before `aria-disabled` is flushed can't fire a\n // duplicate confirm mid-async. A slotted action the consumer drives carries no\n // disabled state unless the author adds one, so it stays operable.\n private _activate(el: Element, event: ConfirmEvent | CancelEvent) {\n if (el.hasAttribute('data-confirm') && this.loading) return;\n if (el.matches('[disabled], [aria-disabled=\"true\"]')) return;\n if (this.dispatchEvent(event)) this._close();\n }\n\n // Single delegated handler covering both the built-in buttons (marked with\n // `data-confirm` / `data-cancel`) and slotted replacements (`slot=\"confirm\"` /\n // `slot=\"cancel\"`). `composedPath()` flattens across the slot boundary, so a\n // click on light-DOM slotted content still resolves here.\n private _onFooterClick = (e: Event) => {\n for (const node of e.composedPath()) {\n if (node === e.currentTarget) break;\n if (!(node instanceof Element)) continue;\n if (node.matches('[slot=\"confirm\"], [data-confirm]'))\n return this._activate(node, new ConfirmEvent());\n if (node.matches('[slot=\"cancel\"], [data-cancel]'))\n return this._activate(node, new CancelEvent());\n }\n };\n\n protected override renderFooter() {\n const confirmVariant = this.tone === 'danger' ? 'destructive' : 'primary';\n return html`\n <footer\n part=\"footer\"\n @click=${this._onFooterClick}\n >\n <slot name=\"cancel\">\n <button\n part=\"button cancel\"\n class=\"l-button\"\n type=\"button\"\n data-cancel\n >\n ${this.cancelText}\n </button>\n </slot>\n <slot name=\"confirm\">\n <button\n part=\"button confirm\"\n class=\"l-button\"\n type=\"button\"\n data-confirm\n data-variant=${confirmVariant}\n aria-disabled=${this.loading ? 'true' : nothing}\n aria-busy=${this.loading ? 'true' : nothing}\n >\n ${this.loading ? html`<l-spinner aria-hidden=\"true\"></l-spinner>` : nothing}\n ${this.confirmText}\n </button>\n </slot>\n </footer>\n `;\n }\n}\n\n// `confirm` and `cancel` collide with lib.dom's event maps, so we cannot augment\n// the global map (as the shared lifecycle events do). Declaration-merge typed\n// listener overloads onto the element instead — must come after the class so the\n// CEM analyzer reads the class first.\nexport interface AlertDialog {\n addEventListener(\n type: 'confirm',\n listener: (this: AlertDialog, ev: ConfirmEvent) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: 'cancel',\n listener: (this: AlertDialog, ev: CancelEvent) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener(\n type: 'confirm',\n listener: (this: AlertDialog, ev: ConfirmEvent) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: 'cancel',\n listener: (this: AlertDialog, ev: CancelEvent) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"saCaA,IAAM,EAAc,EAAU,gcAAc,EAS/B,EAAb,cAAkC,KAAM,CACtC,aAAc,CACZ,MAAM,UAAW,CAAE,QAAS,GAAO,SAAU,GAAO,WAAY,EAAK,CAAC,CACxE,CACF,EAQa,EAAb,cAAiC,KAAM,CACrC,aAAc,CACZ,MAAM,SAAU,CAAE,QAAS,GAAO,SAAU,GAAO,WAAY,EAAK,CAAC,CACvE,CACF,EA8Ca,EAAb,cAAiC,CAAO,gDAKxB,0BAID,sBASH,qBAQa,IAAI,EAAkB,KAAM,WAAW,sBA6DpC,GAAa,CACrC,IAAK,IAAM,KAAQ,EAAE,aAAa,EAAG,CACnC,GAAI,IAAS,EAAE,cAAe,MACxB,gBAAgB,QACtB,IAAI,EAAK,QAAQ,kCAAkC,EACjD,OAAO,KAAK,UAAU,EAAM,IAAI,CAAc,EAChD,GAAI,EAAK,QAAQ,gCAAgC,EAC/C,OAAO,KAAK,UAAU,EAAM,IAAI,CAAa,CAFC,CAGlD,CACF,qBA/FyB,CAAC,EAAY,EAAc,EAAc,CAAW,EAmB7E,IAAuB,OAAQ,CAC7B,MAAO,aACT,CAKA,IAAuB,SAAU,CAC/B,OAAO,KAAK,aAAa,KAAK,WAAW,EAAI,cAAgB,EAC/D,CAKA,WAA8B,EAA+B,CAC3D,MAAM,WAAW,CAAO,EACxB,AAAuB,KAAK,eAAe,EAC7C,CAIA,UAA6B,EAAU,CAChC,KAAK,cAAc,IAAI,CAAa,GAAG,EAAE,eAAe,CAC/D,CAQA,eAAmC,CACjC,IAAM,EAAa,CACjB,KAAK,cAA2B,aAAa,EAC7C,KAAK,cAA2B,iBAAiB,EACjD,KAAK,WAAW,cAA2B,eAAe,CAC5D,EACA,IAAK,IAAM,KAAM,EACX,MAAC,GAAM,EAAG,QAAQ,oCAAoC,KAC1D,EAAG,MAAM,CAAE,cAAe,EAAK,CAAC,EAG5B,IAAO,SAAS,eAAiB,EAAG,SAAS,SAAS,aAAa,GAAG,MAE9E,CAEA,QAAiB,CAGX,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,CAC1C,CAOA,UAAkB,EAAa,EAAmC,CAC5D,EAAG,aAAa,cAAc,GAAK,KAAK,SACxC,EAAG,QAAQ,oCAAoC,GAC/C,KAAK,cAAc,CAAK,GAAG,KAAK,OAAO,CAC7C,CAiBA,cAAkC,CAChC,IAAM,EAAiB,KAAK,OAAS,SAAW,cAAgB,UAChE,MAAO,EAAI;;;iBAGE,KAAK,eAAe;;;;;;;;;cASvB,KAAK,WAAW;;;;;;;;;2BASH,EAAe;4BACd,KAAK,QAAU,OAAS,EAAQ;wBACpC,KAAK,QAAU,OAAS,EAAQ;;cAE1C,KAAK,QAAU,CAAI,6CAA+C,EAAQ;cAC1E,KAAK,YAAY;;;;KAK7B,CACF,KAhIG,EAAS,CAAE,UAAW,cAAe,CAAC,CAAA,EAAA,EAAA,UAAA,cAAA,IAAA,EAAA,KAItC,EAAS,CAAE,UAAW,aAAc,CAAC,CAAA,EAAA,EAAA,UAAA,aAAA,IAAA,EAAA,KAIrC,EAAS,CAAE,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAK1B,EAAS,CAAE,KAAM,QAAS,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,UAAA,IAAA,EAAA"}
1
+ {"version":3,"file":"alert-dialog.js","names":[],"sources":["../../../src/html/elements/alert-dialog/alert-dialog.css?inline","../../../src/html/elements/alert-dialog/alert-dialog.ts"],"sourcesContent":["/*\n * Layout for l-alert-dialog. Unlayered to match dialog.css (the inherited\n * shared styles) so these overrides win — inside a shadow root there's no host\n * cascade to guard against, and layered rules can't override dialog.css's\n * unlayered ones. The `.l-button` appearance comes from the shared button-core\n * (imported in alert-dialog.ts), not from here.\n *\n * Visual rhythm: a confirmation reads better with the title and description\n * grouped tight and the actions set off in their own bar.\n */\n\n/* Interruptive by nature: a stronger backdrop (the token meant for immersive\n overlays) plus a touch of blur pulls focus firmly onto the confirmation.\n Consumers can still retune --backdrop / --backdrop-blur. */\n:host {\n --backdrop: var(--l-backdrop-strong);\n --backdrop-blur: 2px;\n}\n\n/* Pull the description up close under the title (title keeps the inherited\n 1.125rem / 600 from dialog.css). */\n[part='header'] {\n padding-block-end: 0.5rem;\n}\n\n/* Muted, smaller description with a comfortable reading measure, plus its own\n block-end gap so the footer divider isn't crowded. */\n[part='body'] {\n padding-block-end: var(--padding);\n color: var(--l-color-text-secondary);\n font-size: var(--l-text-sm);\n line-height: 1.5;\n}\n\n/* Footer as a distinct action bar: subtle fill + top divider, corners rounded\n to match the dialog (which doesn't clip its overflow). */\n[part='footer'] {\n padding: 1rem var(--padding);\n border-top: 1px solid var(--l-color-border);\n background: var(--l-color-bg-fill-neutral-subtle);\n border-end-start-radius: var(--border-radius);\n border-end-end-radius: var(--border-radius);\n}\n","import { html, nothing, unsafeCSS, type PropertyValues } from 'lit';\n// Only the spinner fragment needs the static tag function; the outer template\n// stays on lit's `html` so `renderFooter()` keeps the return type `Dialog`\n// declares. A static-html result nests inside a regular one just fine.\nimport { html as staticHtml } from 'lit/static-html.js';\nimport { property } from 'lit/decorators.js';\nimport { cls } from '../../registry.js';\nimport { staticTag } from '../../static-tag.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport { HasSlotController } from '../../shared/controllers/has-slot-controller.js';\nimport { Dialog } from '../dialog/dialog.js';\nimport dialogStyles from '../dialog/dialog.styles.js';\nimport buttonStyles from '../../shared/styles/button-core.styles.js';\nimport rawAlertStyles from './alert-dialog.css?inline';\nimport '../spinner/index.js';\n\n// The built-in actions render as `.l-button`, styled inside the shadow root from\n// the SAME shared button-core as the light-DOM `.l-button` (single source of\n// truth — no copy to drift). alert-dialog.css adds only the dialog layout.\n// The class is emitted through `cls('button')` so it tracks a renamed\n// `cssPrefix` — button-core.css ships as a real CSS file the consumer's postcss\n// re-prefixes, and a static literal would desync from it (see button-core.css).\nconst alertStyles = unsafeCSS(rawAlertStyles);\n\n/**\n * Fired when the user accepts an alert dialog (the confirm action). Cancelable —\n * call `event.preventDefault()` to keep the dialog open (e.g. to await an async\n * task); close it yourself afterwards by setting `open = false`.\n *\n * Does not bubble and is not composed: listen on the element itself.\n */\nexport class ConfirmEvent extends Event {\n constructor() {\n super('confirm', { bubbles: false, composed: false, cancelable: true });\n }\n}\n\n/**\n * Fired when the user dismisses an alert dialog (the cancel action, Escape, or a\n * close request). Cancelable — call `event.preventDefault()` to keep it open.\n *\n * Does not bubble and is not composed: listen on the element itself.\n */\nexport class CancelEvent extends Event {\n constructor() {\n super('cancel', { bubbles: false, composed: false, cancelable: true });\n }\n}\n\n/**\n * An interruptive confirmation dialog built on `<l-dialog>`. Renders its own\n * cancel/confirm actions and exposes `role=\"alertdialog\"`. Use it to confirm a\n * consequential action (delete, publish, discard) — not for generic content.\n *\n * Open and close by toggling the `open` property (or the `--show` / `--hide`\n * Invoker commands). The dialog closes automatically when the user picks an\n * action, unless the matching `confirm` / `cancel` event is canceled.\n *\n * @slot - Description text. Provides the dialog's accessible description.\n * @slot title - Custom heading element. Overrides the default `<h2>` rendered from the `title` property. Also provides the dialog's accessible name.\n * @slot cancel - Replaces the built-in cancel action (e.g. a link). Keep it focusable.\n * @slot confirm - Replaces the built-in confirm action.\n *\n * @csspart dialog - The native `<dialog>` element.\n * @csspart header - The header wrapper containing the title.\n * @csspart title - The dialog title heading.\n * @csspart body - The body wrapper around the description.\n * @csspart footer - The footer wrapper around the actions.\n * @csspart button - Both built-in action buttons.\n * @csspart cancel - The built-in cancel button.\n * @csspart confirm - The built-in confirm button.\n *\n * @cssproperty [--width=31rem] - Dialog width.\n * @cssproperty [--border-radius=6px] - Dialog border radius.\n * @cssproperty [--padding=1.5rem] - Padding applied to the header, footer, and inline-padding of the body.\n * @cssproperty [--show-duration=200ms] - Open transition duration.\n * @cssproperty [--hide-duration=200ms] - Close transition duration.\n * @cssproperty --backdrop - Backdrop color.\n * @cssproperty [--backdrop-blur=0] - Backdrop blur amount (any CSS length).\n *\n * @event confirm - Fired when the user accepts. Cancelable — `preventDefault()` keeps it open for async work.\n * @event cancel - Fired when the user dismisses (cancel action, Escape). Cancelable — `preventDefault()` keeps it open.\n * @event show - Fired when the dialog is about to open. Cancelable.\n * @event hide - Fired when the dialog is about to close via the `open` property. Cancelable. (The confirm, cancel, and Escape paths close natively and emit `confirm`/`cancel` instead of `hide`.)\n * @event after-show - Fired after the open animation completes. Not cancelable.\n * @event after-hide - Fired after the close animation completes. Not cancelable.\n *\n * @command --show - Sets `open = true`.\n * @command --hide - Sets `open = false`.\n *\n * @customElement l-alert-dialog\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below.\nexport class AlertDialog extends Dialog {\n static override styles = [hostStyles, dialogStyles, buttonStyles, alertStyles];\n\n /** Label for the confirm action. */\n @property({ attribute: 'confirm-text' })\n confirmText = 'Confirm';\n\n /** Label for the cancel action. */\n @property({ attribute: 'cancel-text' })\n cancelText = 'Cancel';\n\n /** Visual tone. `danger` renders the confirm action as destructive. */\n @property({ reflect: true })\n tone?: 'danger';\n\n /** Show a busy state on the confirm action (spinner + `aria-disabled`); the\n * cancel action stays operable as an escape hatch. */\n @property({ type: Boolean, reflect: true })\n loading = false;\n\n protected override get _role() {\n return 'alertdialog';\n }\n\n // Wire aria-describedby to the body only when it actually has content, so a\n // title-only confirmation doesn't advertise an empty description.\n private _bodyContent = new HasSlotController(this, '[default]');\n protected override get _bodyId() {\n return this._bodyContent.test('[default]') ? 'description' : '';\n }\n\n // Alert dialogs are interruptive: they are never dismissable by clicking\n // outside, so the inherited `light-dismiss` is forced off (a vetoed dismissal\n // gesture must go through an explicit action).\n protected override willUpdate(changed: PropertyValues<this>) {\n super.willUpdate(changed);\n if (this.lightDismiss) this.lightDismiss = false;\n }\n\n // Native Escape / close request → cancel intent. A vetoed `cancel` keeps the\n // dialog open; otherwise the native close proceeds (emitting `after-hide`).\n protected override _onCancel(e: Event) {\n if (!this.dispatchEvent(new CancelEvent())) e.preventDefault();\n }\n\n // Focus the least destructive action on open: an author `[autofocus]`, else\n // the slotted cancel action, else the built-in cancel button (in shadow DOM,\n // so the base light-DOM lookup can't reach it). Try each in order and keep the\n // first that actually takes focus — this skips a non-focusable slotted cancel\n // without rejecting a focusable one that reports `tabIndex === -1` (a custom\n // element with `delegatesFocus`, `<div role=\"button\" tabindex=\"0\">`, etc.).\n protected override _focusInitial() {\n const candidates = [\n this.querySelector<HTMLElement>('[autofocus]'),\n this.querySelector<HTMLElement>('[slot=\"cancel\"]'),\n this.renderRoot.querySelector<HTMLElement>('[data-cancel]'),\n ];\n for (const el of candidates) {\n if (!el || el.matches('[disabled], [aria-disabled=\"true\"]')) continue;\n el.focus({ preventScroll: true });\n // `document.activeElement` resolves to the shadow host when focus lands\n // inside an open shadow root, so this also covers delegatesFocus.\n if (el === document.activeElement || el.contains(document.activeElement)) return;\n }\n }\n\n private _close() {\n // Close natively rather than via `open = false` so we don't also emit the\n // inherited cancelable `hide` — `confirm` / `cancel` are the intent events.\n if (this.dialog.open) this.dialog.close();\n }\n\n // Activate an action unless its control is disabled. The built-in confirm also\n // respects `loading` via the property — immune to render timing, so a fast\n // second click in the window before `aria-disabled` is flushed can't fire a\n // duplicate confirm mid-async. A slotted action the consumer drives carries no\n // disabled state unless the author adds one, so it stays operable.\n private _activate(el: Element, event: ConfirmEvent | CancelEvent) {\n if (el.hasAttribute('data-confirm') && this.loading) return;\n if (el.matches('[disabled], [aria-disabled=\"true\"]')) return;\n if (this.dispatchEvent(event)) this._close();\n }\n\n // Single delegated handler covering both the built-in buttons (marked with\n // `data-confirm` / `data-cancel`) and slotted replacements (`slot=\"confirm\"` /\n // `slot=\"cancel\"`). `composedPath()` flattens across the slot boundary, so a\n // click on light-DOM slotted content still resolves here.\n private _onFooterClick = (e: Event) => {\n for (const node of e.composedPath()) {\n if (node === e.currentTarget) break;\n if (!(node instanceof Element)) continue;\n if (node.matches('[slot=\"confirm\"], [data-confirm]'))\n return this._activate(node, new ConfirmEvent());\n if (node.matches('[slot=\"cancel\"], [data-cancel]'))\n return this._activate(node, new CancelEvent());\n }\n };\n\n protected override renderFooter() {\n const confirmVariant = this.tone === 'danger' ? 'destructive' : 'primary';\n const spinnerTag = staticTag('spinner');\n return html`\n <footer\n part=\"footer\"\n @click=${this._onFooterClick}\n >\n <slot name=\"cancel\">\n <button\n part=\"button cancel\"\n class=${cls('button')}\n type=\"button\"\n data-cancel\n >\n ${this.cancelText}\n </button>\n </slot>\n <slot name=\"confirm\">\n <button\n part=\"button confirm\"\n class=${cls('button')}\n type=\"button\"\n data-confirm\n data-variant=${confirmVariant}\n aria-disabled=${this.loading ? 'true' : nothing}\n aria-busy=${this.loading ? 'true' : nothing}\n >\n ${this.loading\n ? staticHtml`<${spinnerTag} aria-hidden=\"true\"></${spinnerTag}>`\n : nothing}\n ${this.confirmText}\n </button>\n </slot>\n </footer>\n `;\n }\n}\n\n// `confirm` and `cancel` collide with lib.dom's event maps, so we cannot augment\n// the global map (as the shared lifecycle events do). Declaration-merge typed\n// listener overloads onto the element instead — must come after the class so the\n// CEM analyzer reads the class first.\nexport interface AlertDialog {\n addEventListener(\n type: 'confirm',\n listener: (this: AlertDialog, ev: ConfirmEvent) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: 'cancel',\n listener: (this: AlertDialog, ev: CancelEvent) => void,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n removeEventListener(\n type: 'confirm',\n listener: (this: AlertDialog, ev: ConfirmEvent) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: 'cancel',\n listener: (this: AlertDialog, ev: CancelEvent) => void,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n}\n"],"mappings":"8iBCsBA,IAAM,EAAc,EAAU,gcAAc,EAS/B,EAAb,cAAkC,KAAM,CACtC,aAAc,CACZ,MAAM,UAAW,CAAE,QAAS,GAAO,SAAU,GAAO,WAAY,EAAK,CAAC,CACxE,CACF,EAQa,EAAb,cAAiC,KAAM,CACrC,aAAc,CACZ,MAAM,SAAU,CAAE,QAAS,GAAO,SAAU,GAAO,WAAY,EAAK,CAAC,CACvE,CACF,EA8Ca,EAAb,cAAiC,CAAO,gDAKxB,0BAID,sBASH,qBAQa,IAAI,EAAkB,KAAM,WAAW,sBA6DpC,GAAa,CACrC,IAAK,IAAM,KAAQ,EAAE,aAAa,EAAG,CACnC,GAAI,IAAS,EAAE,cAAe,MACxB,gBAAgB,QACtB,IAAI,EAAK,QAAQ,kCAAkC,EACjD,OAAO,KAAK,UAAU,EAAM,IAAI,CAAc,EAChD,GAAI,EAAK,QAAQ,gCAAgC,EAC/C,OAAO,KAAK,UAAU,EAAM,IAAI,CAAa,CAFC,CAGlD,CACF,qBA/FyB,CAAC,EAAY,EAAc,EAAc,CAAW,EAmB7E,IAAuB,OAAQ,CAC7B,MAAO,aACT,CAKA,IAAuB,SAAU,CAC/B,OAAO,KAAK,aAAa,KAAK,WAAW,EAAI,cAAgB,EAC/D,CAKA,WAA8B,EAA+B,CAC3D,MAAM,WAAW,CAAO,EACxB,AAAuB,KAAK,eAAe,EAC7C,CAIA,UAA6B,EAAU,CAChC,KAAK,cAAc,IAAI,CAAa,GAAG,EAAE,eAAe,CAC/D,CAQA,eAAmC,CACjC,IAAM,EAAa,CACjB,KAAK,cAA2B,aAAa,EAC7C,KAAK,cAA2B,iBAAiB,EACjD,KAAK,WAAW,cAA2B,eAAe,CAC5D,EACA,IAAK,IAAM,KAAM,EACX,MAAC,GAAM,EAAG,QAAQ,oCAAoC,KAC1D,EAAG,MAAM,CAAE,cAAe,EAAK,CAAC,EAG5B,IAAO,SAAS,eAAiB,EAAG,SAAS,SAAS,aAAa,GAAG,MAE9E,CAEA,QAAiB,CAGX,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,CAC1C,CAOA,UAAkB,EAAa,EAAmC,CAC5D,EAAG,aAAa,cAAc,GAAK,KAAK,SACxC,EAAG,QAAQ,oCAAoC,GAC/C,KAAK,cAAc,CAAK,GAAG,KAAK,OAAO,CAC7C,CAiBA,cAAkC,CAChC,IAAM,EAAiB,KAAK,OAAS,SAAW,cAAgB,UAC1D,EAAa,EAAU,SAAS,EACtC,MAAO,EAAI;;;iBAGE,KAAK,eAAe;;;;;oBAKjB,EAAI,QAAQ,EAAE;;;;cAIpB,KAAK,WAAW;;;;;;oBAMV,EAAI,QAAQ,EAAE;;;2BAGP,EAAe;4BACd,KAAK,QAAU,OAAS,EAAQ;wBACpC,KAAK,QAAU,OAAS,EAAQ;;cAE1C,KAAK,QACH,CAAU,IAAI,EAAW,wBAAwB,EAAW,GAC5D,EAAQ;cACV,KAAK,YAAY;;;;KAK7B,CACF,KAnIG,EAAS,CAAE,UAAW,cAAe,CAAC,CAAA,EAAA,EAAA,UAAA,cAAA,IAAA,EAAA,KAItC,EAAS,CAAE,UAAW,aAAc,CAAC,CAAA,EAAA,EAAA,UAAA,aAAA,IAAA,EAAA,KAIrC,EAAS,CAAE,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAK1B,EAAS,CAAE,KAAM,QAAS,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,UAAA,IAAA,EAAA"}
@@ -1,10 +1,10 @@
1
- import{i as e}from"../../chunks/lit.js";import{i as t,r as n,t as r}from"../../chunks/decorate.js";import{a as i,o as a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import c from"../../shared/styles/host.styles.js";import{r as l,t as u}from"../../chunks/static-html.js";var d=e(`:host{--size:40px;--_font-size:calc(var(--size) * .4);width:var(--size);height:var(--size);border-radius:calc(var(--size) * .15);isolation:isolate;vertical-align:middle;color:var(--l-color-text-secondary);flex-shrink:0;justify-content:center;align-items:center;display:inline-flex;position:relative}:host([size=xs]){--size:24px;--_font-size:.75rem}:host([size=sm]){--size:32px;--_font-size:.875rem}:host([size=lg]){--size:48px;--_font-size:1.25rem}:host([size=xl]){--size:56px;--_font-size:1.25rem}img{object-fit:cover;border-radius:inherit;width:100%;height:100%}.initials{margin:auto}.icon{color:currentColor;width:60%;height:60%}.badge{z-index:1;min-width:12px;height:12px;box-shadow:0 0 0 2px var(--l-color-surface);background-color:var(--l-color-surface);font-variant-numeric:tabular-nums;color:var(--l-color-text-primary);border-radius:3px;justify-content:center;align-items:center;padding-inline:2px;font-size:.625rem;font-weight:500;line-height:1;display:flex;position:absolute;bottom:0;right:0;overflow:hidden}.base:after{content:"";border-radius:inherit;pointer-events:none;position:absolute;inset:0;box-shadow:inset 0 0 0 1px var(--lightningcss-light,oklch(0% 0 0/.07))var(--lightningcss-dark,oklch(100% 0 0/.1))}.base{border-radius:calc(var(--size) * .15);background-color:var(--color,var(--l-color-bg-fill-neutral-soft));--_on:calc(255 * ( 1 - sign(.2126 * (r / 255) * (r / 255) + .7152 * (g / 255) * (g / 255) + .0722 * (b / 255) * (b / 255) - .2) ) / 2);width:100%;height:100%;color:var(--text-color,rgb(from var(--color) var(--_on) var(--_on) var(--_on)));font-size:var(--_font-size);text-transform:uppercase;cursor:inherit;border:none;justify-content:center;align-items:center;padding:0;font-weight:500;line-height:1;display:inline-flex}@supports (color:contrast-color(red)){.base{color:var(--text-color,contrast-color(var(--color)))}}button.base{cursor:pointer}button.base:focus-visible{outline:2px solid var(--l-focus-ring);outline-offset:2px}button.base:hover{box-shadow:inset 0 0 0 1.5px var(--l-color-border-interactive)}@container style(--appearance:circle){.base{border-radius:50%}}`);function f(e=``){let t=e.normalize(`NFC`).trim().split(/\s+/).filter(Boolean);if(t.length===0)return``;let n=e=>[...e][0];return(n(t[0])+(t.length>1?n(t[t.length-1]):``)).toUpperCase()}var p=a`<svg class="icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
1
+ import{i as e}from"../../chunks/lit.js";import{i as t,r as n,t as r}from"../../chunks/decorate.js";import{a as i,o as a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import{r as c,t as l}from"../../chunks/static-html.js";import u from"../../shared/styles/host.styles.js";var d=e(`:host{--size:40px;--_font-size:calc(var(--size) * .4);width:var(--size);height:var(--size);border-radius:calc(var(--size) * .15);isolation:isolate;vertical-align:middle;color:var(--l-color-text-secondary);flex-shrink:0;justify-content:center;align-items:center;display:inline-flex;position:relative}:host([size=xs]){--size:24px;--_font-size:.75rem}:host([size=sm]){--size:32px;--_font-size:.875rem}:host([size=lg]){--size:48px;--_font-size:1.25rem}:host([size=xl]){--size:56px;--_font-size:1.25rem}img{object-fit:cover;border-radius:inherit;width:100%;height:100%}.initials{margin:auto}.icon{color:currentColor;width:60%;height:60%}.badge{z-index:1;min-width:12px;height:12px;box-shadow:0 0 0 2px var(--l-color-surface);background-color:var(--l-color-surface);font-variant-numeric:tabular-nums;color:var(--l-color-text-primary);border-radius:3px;justify-content:center;align-items:center;padding-inline:2px;font-size:.625rem;font-weight:500;line-height:1;display:flex;position:absolute;bottom:0;right:0;overflow:hidden}.base:after{content:"";border-radius:inherit;pointer-events:none;position:absolute;inset:0;box-shadow:inset 0 0 0 1px var(--lightningcss-light,oklch(0% 0 0/.07))var(--lightningcss-dark,oklch(100% 0 0/.1))}.base{border-radius:calc(var(--size) * .15);background-color:var(--color,var(--l-color-bg-fill-neutral-soft));--_on:calc(255 * ( 1 - sign(.2126 * (r / 255) * (r / 255) + .7152 * (g / 255) * (g / 255) + .0722 * (b / 255) * (b / 255) - .2) ) / 2);width:100%;height:100%;color:var(--text-color,rgb(from var(--color) var(--_on) var(--_on) var(--_on)));font-size:var(--_font-size);text-transform:uppercase;cursor:inherit;border:none;justify-content:center;align-items:center;padding:0;font-weight:500;line-height:1;display:inline-flex}@supports (color:contrast-color(red)){.base{color:var(--text-color,contrast-color(var(--color)))}}button.base{cursor:pointer}button.base:focus-visible{outline:2px solid var(--l-focus-ring);outline-offset:2px}button.base:hover{box-shadow:inset 0 0 0 1.5px var(--l-color-border-interactive)}@container style(--appearance:circle){.base{border-radius:50%}}`);function f(e=``){let t=e.normalize(`NFC`).trim().split(/\s+/).filter(Boolean);if(t.length===0)return``;let n=e=>[...e][0];return(n(t[0])+(t.length>1?n(t[t.length-1]):``)).toUpperCase()}var p=a`<svg class="icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
2
2
  <path d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" />
3
- </svg>`,m=class extends s{constructor(...e){super(...e),this.src=``,this.name=``,this.size=`md`,this.badge=0,this.interactive=!1,this._hasError=!1,this._onError=()=>{this._hasError=!0}}static{this.styles=[c,d]}willUpdate(e){e.has(`src`)&&(this._hasError=!1)}connectedCallback(){super.connectedCallback(),!this.interactive&&!this.hasAttribute(`role`)&&this.setAttribute(`role`,`img`)}updated(){this.interactive?(this.getAttribute(`role`)===`img`&&this.removeAttribute(`role`),this.removeAttribute(`aria-label`)):(this.hasAttribute(`role`)||this.setAttribute(`role`,`img`),this.name&&this.setAttribute(`aria-label`,this.name));let e=getComputedStyle(this).getPropertyValue(`--appearance`).trim()===`circle`;this.style.borderRadius=e?`50%`:``}get _tag(){return this.interactive?u`button`:u`div`}render(){let e=this.src&&!this._hasError?i`<img
3
+ </svg>`,m=class extends s{constructor(...e){super(...e),this.src=``,this.name=``,this.size=`md`,this.badge=0,this.interactive=!1,this._hasError=!1,this._onError=()=>{this._hasError=!0}}static{this.styles=[u,d]}willUpdate(e){e.has(`src`)&&(this._hasError=!1)}connectedCallback(){super.connectedCallback(),!this.interactive&&!this.hasAttribute(`role`)&&this.setAttribute(`role`,`img`)}updated(){this.interactive?(this.getAttribute(`role`)===`img`&&this.removeAttribute(`role`),this.removeAttribute(`aria-label`)):(this.hasAttribute(`role`)||this.setAttribute(`role`,`img`),this.name&&this.setAttribute(`aria-label`,this.name));let e=getComputedStyle(this).getPropertyValue(`--appearance`).trim()===`circle`;this.style.borderRadius=e?`50%`:``}get _tag(){return this.interactive?l`button`:l`div`}render(){let e=this.src&&!this._hasError?i`<img
4
4
  src=${this.src}
5
5
  alt=${this.name||``}
6
6
  @error=${this._onError}
7
- />`:this.name?i`<span class="initials">${f(this.name)}</span>`:i`<slot>${p}</slot>`;return l`
7
+ />`:this.name?i`<span class="initials">${f(this.name)}</span>`:i`<slot>${p}</slot>`;return c`
8
8
  <${this._tag}
9
9
  class="base"
10
10
  part="base"
@@ -1 +1 @@
1
- {"version":3,"file":"form-field.d.ts","sourceRoot":"","sources":["../../../src/html/elements/form-field/form-field.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,SAAU,SAAQ,YAAY;IAChC,gBAAgB;IAIzB,4HAA4H;IAE5H,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAE9B,uEAAuE;IAEvE,QAAQ,UAAS;IAEjB,kDAAkD;IAElD,QAAQ,UAAS;IAEjB,mGAAmG;IAEnG,OAAO,UAAS;IAEhB,yEAAyE;IAEzE,QAAQ,UAAS;IAEjB,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAS;IACvB,+DAA+D;IAC/D,OAAO,CAAC,oBAAoB,CAAgB;IAEnC,iBAAiB;IAoBjB,oBAAoB;IAM7B,OAAO,CAAC,MAAM;IA4Dd;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,UAAU,CAKhB;IAEF,OAAO,CAAC,WAAW,CAGjB;IAEF,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,kBAAkB;CAS3B"}
1
+ {"version":3,"file":"form-field.d.ts","sourceRoot":"","sources":["../../../src/html/elements/form-field/form-field.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,SAAU,SAAQ,YAAY;IAChC,gBAAgB;IAIzB,4HAA4H;IAE5H,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAE9B,uEAAuE;IAEvE,QAAQ,UAAS;IAEjB,kDAAkD;IAElD,QAAQ,UAAS;IAEjB,mGAAmG;IAEnG,OAAO,UAAS;IAEhB,yEAAyE;IAEzE,QAAQ,UAAS;IAEjB,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAS;IACvB,+DAA+D;IAC/D,OAAO,CAAC,oBAAoB,CAAgB;IAEnC,iBAAiB;IAoBjB,oBAAoB;IAM7B,OAAO,CAAC,MAAM;IA8Dd;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,UAAU,CAKhB;IAEF,OAAO,CAAC,WAAW,CAGjB;IAEF,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,kBAAkB;CAS3B"}
@@ -1,2 +1,2 @@
1
- import{uniqueId as e}from"../../registry.js";import{i as t,t as n}from"../../chunks/decorate.js";import{LuxenElement as r}from"../../shared/luxen-element.js";var i=`input, select, textarea`,a=class extends r{constructor(...e){super(...e),this.required=!1,this.optional=!1,this.invalid=!1,this.unstyled=!1,this._control=null,this._hint=null,this._error=null,this._hasInteracted=!1,this._wired=!1,this._externalDescribedby=[],this._onInvalid=e=>{e.preventDefault(),this._hasInteracted=!0,this._updateValidity()},this._onInteract=()=>{this._hasInteracted=!0,this._updateValidity()}}createRenderRoot(){return this}connectedCallback(){if(super.connectedCallback(),!this._setup()){setTimeout(()=>this._setup(),0);for(let e of this.querySelectorAll(`*`)){let t=e.localName;t.includes(`-`)&&!customElements.get(t)&&customElements.whenDefined(t).then(()=>this._setup())}}}disconnectedCallback(){super.disconnectedCallback(),this._teardown(),this._wired=!1}_setup(){if(this._wired)return!0;if(this._control=this._findControl(),!this._control)return!1;this._wired=!0;let t=this._control;this._hint=this.querySelector(`.l-hint`),this._error=this.querySelector(`.l-error`),this._error&&!this._error.hasAttribute(`role`)&&this._error.setAttribute(`role`,`alert`),t.id||=e(`field`);let n=this.querySelector(`label`);n&&!n.htmlFor&&!n.contains(t)&&(n.htmlFor=t.id),this._hint&&!this._hint.id&&(this._hint.id=e(`hint`)),this._error&&!this._error.id&&(this._error.id=e(`error`));let r=new Set([this._hint?.id,this._error?.id].filter(Boolean));if(this._externalDescribedby=(t.getAttribute(`aria-describedby`)??``).split(/\s+/).filter(e=>e&&!r.has(e)),!this.layout){let e=t.tagName===`INPUT`&&([`checkbox`,`radio`].includes(t.type)||t.getAttribute(`role`)===`switch`);this.layout=e?`inline`:`stacked`}this.required=t.hasAttribute(`required`),this.optional=!this.required;let i=this.invalid||t.getAttribute(`aria-invalid`)===`true`;return this._hasInteracted=i,t.addEventListener(`invalid`,this._onInvalid),t.addEventListener(`blur`,this._onInteract),t.addEventListener(`change`,this._onInteract),t.addEventListener(`input`,this._onInteract),this._updateValidity(),!0}_findControl(){let e=this.querySelector(i);if(e)return e;for(let e of this.querySelectorAll(`*`))if(e.constructor.formAssociated)return e;return null}_teardown(){let e=this._control;e&&(e.removeEventListener(`invalid`,this._onInvalid),e.removeEventListener(`blur`,this._onInteract),e.removeEventListener(`change`,this._onInteract),e.removeEventListener(`input`,this._onInteract))}_updateValidity(){let e=this._control;if(!e)return;let t=this._hasInteracted&&!e.checkValidity();this.invalid=t,t?e.setAttribute(`aria-invalid`,`true`):e.removeAttribute(`aria-invalid`),this._updateDescribedby()}_updateDescribedby(){let e=this._control;if(!e)return;let t=[...this._externalDescribedby];this._hint?.id&&t.push(this._hint.id),this._error&&this.invalid&&this._error.id&&t.push(this._error.id),t.length?e.setAttribute(`aria-describedby`,t.join(` `)):e.removeAttribute(`aria-describedby`)}};n([t({reflect:!0})],a.prototype,`layout`,void 0),n([t({type:Boolean,reflect:!0})],a.prototype,`required`,void 0),n([t({type:Boolean,reflect:!0})],a.prototype,`optional`,void 0),n([t({type:Boolean,reflect:!0})],a.prototype,`invalid`,void 0),n([t({type:Boolean,reflect:!0})],a.prototype,`unstyled`,void 0);export{a as FormField};
1
+ import{cls as e,uniqueId as t}from"../../registry.js";import{i as n,t as r}from"../../chunks/decorate.js";import{LuxenElement as i}from"../../shared/luxen-element.js";var a=`input, select, textarea`,o=class extends i{constructor(...e){super(...e),this.required=!1,this.optional=!1,this.invalid=!1,this.unstyled=!1,this._control=null,this._hint=null,this._error=null,this._hasInteracted=!1,this._wired=!1,this._externalDescribedby=[],this._onInvalid=e=>{e.preventDefault(),this._hasInteracted=!0,this._updateValidity()},this._onInteract=()=>{this._hasInteracted=!0,this._updateValidity()}}createRenderRoot(){return this}connectedCallback(){if(super.connectedCallback(),!this._setup()){setTimeout(()=>this._setup(),0);for(let e of this.querySelectorAll(`*`)){let t=e.localName;t.includes(`-`)&&!customElements.get(t)&&customElements.whenDefined(t).then(()=>this._setup())}}}disconnectedCallback(){super.disconnectedCallback(),this._teardown(),this._wired=!1}_setup(){if(this._wired)return!0;if(this._control=this._findControl(),!this._control)return!1;this._wired=!0;let n=this._control;this._hint=this.querySelector(`.${e(`hint`)}`),this._error=this.querySelector(`.${e(`error`)}`),this._error&&!this._error.hasAttribute(`role`)&&this._error.setAttribute(`role`,`alert`),n.id||=t(`field`);let r=this.querySelector(`label`);r&&!r.htmlFor&&!r.contains(n)&&(r.htmlFor=n.id),this._hint&&!this._hint.id&&(this._hint.id=t(`hint`)),this._error&&!this._error.id&&(this._error.id=t(`error`));let i=new Set([this._hint?.id,this._error?.id].filter(Boolean));if(this._externalDescribedby=(n.getAttribute(`aria-describedby`)??``).split(/\s+/).filter(e=>e&&!i.has(e)),!this.layout){let e=n.tagName===`INPUT`&&([`checkbox`,`radio`].includes(n.type)||n.getAttribute(`role`)===`switch`);this.layout=e?`inline`:`stacked`}this.required=n.hasAttribute(`required`),this.optional=!this.required;let a=this.invalid||n.getAttribute(`aria-invalid`)===`true`;return this._hasInteracted=a,n.addEventListener(`invalid`,this._onInvalid),n.addEventListener(`blur`,this._onInteract),n.addEventListener(`change`,this._onInteract),n.addEventListener(`input`,this._onInteract),this._updateValidity(),!0}_findControl(){let e=this.querySelector(a);if(e)return e;for(let e of this.querySelectorAll(`*`))if(e.constructor.formAssociated)return e;return null}_teardown(){let e=this._control;e&&(e.removeEventListener(`invalid`,this._onInvalid),e.removeEventListener(`blur`,this._onInteract),e.removeEventListener(`change`,this._onInteract),e.removeEventListener(`input`,this._onInteract))}_updateValidity(){let e=this._control;if(!e)return;let t=this._hasInteracted&&!e.checkValidity();this.invalid=t,t?e.setAttribute(`aria-invalid`,`true`):e.removeAttribute(`aria-invalid`),this._updateDescribedby()}_updateDescribedby(){let e=this._control;if(!e)return;let t=[...this._externalDescribedby];this._hint?.id&&t.push(this._hint.id),this._error&&this.invalid&&this._error.id&&t.push(this._error.id),t.length?e.setAttribute(`aria-describedby`,t.join(` `)):e.removeAttribute(`aria-describedby`)}};r([n({reflect:!0})],o.prototype,`layout`,void 0),r([n({type:Boolean,reflect:!0})],o.prototype,`required`,void 0),r([n({type:Boolean,reflect:!0})],o.prototype,`optional`,void 0),r([n({type:Boolean,reflect:!0})],o.prototype,`invalid`,void 0),r([n({type:Boolean,reflect:!0})],o.prototype,`unstyled`,void 0);export{o as FormField};
2
2
  //# sourceMappingURL=form-field.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"form-field.js","names":[],"sources":["../../../src/html/elements/form-field/form-field.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { uniqueId } from '../../registry.js';\n\ntype NativeControl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;\n/** A form-associated custom element (e.g. `l-slider`) exposes the same constraint\n * surface as a native control but doesn't match the native selector. */\ntype FormControl = NativeControl | (HTMLElement & { checkValidity(): boolean });\n\nconst CONTROL_SELECTOR = 'input, select, textarea';\n\n/**\n * @summary Progressively enhances a label + control + messages group: wires the\n * accessibility plumbing (`id`/`for`, `aria-describedby`, `aria-invalid`,\n * required marker) and picks an inline or stacked layout from the control type.\n *\n * Styling stays on the control (`.l-checkbox`, …); inside a field a bare control\n * is auto-styled by the element CSS. Add `unstyled` to keep the ARIA wiring while\n * opting out of that auto-styling (e.g. for a third-party control).\n *\n * @example\n * ```html\n * <l-form-field>\n * <label>Subscribe to the newsletter</label>\n * <input type=\"checkbox\" />\n * <p class=\"l-hint\">One email a month, unsubscribe anytime.</p>\n * <p class=\"l-error\">Please make a choice to continue.</p>\n * </l-form-field>\n * ```\n *\n * @slot - A `<label>`, one form control (a native `input` / `select` /\n * `textarea`, or a form-associated custom element such as `l-slider`), and\n * optional `.l-hint` / `.l-error` message elements, in any order.\n *\n * @cssClass .l-hint - Helper text element. Always visible; linked via `aria-describedby`.\n * @cssClass .l-error - Error message element. Hidden until invalid, then revealed with `role=\"alert\"` and linked via `aria-describedby`.\n *\n * @customElement l-form-field\n */\nexport class FormField extends LuxenElement {\n override createRenderRoot() {\n return this;\n }\n\n /** Layout, derived from the control type (`inline` for checkbox/radio/switch, otherwise `stacked`). Set one to force it. */\n @property({ reflect: true })\n layout?: 'inline' | 'stacked';\n\n /** Reflected when the control is required. Drives the label marker. */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /** Reflected when the control is not required. */\n @property({ type: Boolean, reflect: true })\n optional = false;\n\n /** Reflected once the control is invalid after interaction (or set to force the invalid state). */\n @property({ type: Boolean, reflect: true })\n invalid = false;\n\n /** Opt out of auto-styling the control; the ARIA wiring is preserved. */\n @property({ type: Boolean, reflect: true })\n unstyled = false;\n\n private _control: FormControl | null = null;\n private _hint: HTMLElement | null = null;\n private _error: HTMLElement | null = null;\n private _hasInteracted = false;\n private _wired = false;\n /** describedby ids set by the author that we must preserve. */\n private _externalDescribedby: string[] = [];\n\n override connectedCallback() {\n super.connectedCallback();\n // Run synchronously when the children are already parsed (the common case:\n // the element is defined after its markup). Fall back to a macrotask — which,\n // unlike requestAnimationFrame, still fires in a hidden tab — if the element\n // upgrades mid-parse before its children exist.\n if (this._setup()) return;\n setTimeout(() => this._setup(), 0);\n // A custom-element control (e.g. l-slider) can upgrade after us — its module\n // may load lazily — so the macrotask retry can still miss it. Re-attempt once\n // each pending custom-element child is defined. `_setup()` is idempotent via\n // `_wired`, so the earliest successful attempt wins.\n for (const el of this.querySelectorAll('*')) {\n const tag = el.localName;\n if (tag.includes('-') && !customElements.get(tag)) {\n void customElements.whenDefined(tag).then(() => this._setup());\n }\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._teardown();\n this._wired = false;\n }\n\n private _setup(): boolean {\n if (this._wired) return true;\n this._control = this._findControl();\n if (!this._control) return false;\n this._wired = true;\n const control = this._control;\n\n this._hint = this.querySelector('.l-hint');\n this._error = this.querySelector('.l-error');\n // The error is announced when revealed; the field owns this (the message is\n // a plain element with no JS of its own).\n if (this._error && !this._error.hasAttribute('role')) {\n this._error.setAttribute('role', 'alert');\n }\n\n // id / label[for]\n if (!control.id) control.id = uniqueId('field');\n const label = this.querySelector('label');\n if (label && !label.htmlFor && !label.contains(control)) {\n label.htmlFor = control.id;\n }\n\n // Ensure message elements have ids for aria-describedby.\n if (this._hint && !this._hint.id) this._hint.id = uniqueId('hint');\n if (this._error && !this._error.id) this._error.id = uniqueId('error');\n\n // Capture author-provided describedby ids (minus our managed ones).\n const managed = new Set([this._hint?.id, this._error?.id].filter(Boolean) as string[]);\n this._externalDescribedby = (control.getAttribute('aria-describedby') ?? '')\n .split(/\\s+/)\n .filter((id) => id && !managed.has(id));\n\n // Layout from control type (unless explicitly forced).\n if (!this.layout) {\n const isToggle =\n control.tagName === 'INPUT' &&\n (['checkbox', 'radio'].includes((control as HTMLInputElement).type) ||\n control.getAttribute('role') === 'switch');\n this.layout = isToggle ? 'inline' : 'stacked';\n }\n\n // Required marker (control is the source of truth).\n this.required = control.hasAttribute('required');\n this.optional = !this.required;\n\n // The error stays hidden until the field is invalid after interaction (or is\n // already flagged invalid by the author / server). Visibility is owned by CSS\n // off the reflected `invalid` state — the field never toggles `hidden`.\n const preInvalid = this.invalid || control.getAttribute('aria-invalid') === 'true';\n this._hasInteracted = preInvalid;\n\n control.addEventListener('invalid', this._onInvalid);\n control.addEventListener('blur', this._onInteract);\n control.addEventListener('change', this._onInteract);\n control.addEventListener('input', this._onInteract);\n\n this._updateValidity();\n return true;\n }\n\n /**\n * The field's control: a native `input`/`select`/`textarea`, or — when none is\n * present — the first form-associated custom element (e.g. `l-slider`). Those\n * expose the same `checkValidity()` / `invalid` event surface but don't match\n * the native selector, so without this fallback the field never wires up and\n * the `l-error` message stays visible on load.\n */\n private _findControl(): FormControl | null {\n const native = this.querySelector<NativeControl>(CONTROL_SELECTOR);\n if (native) return native;\n for (const el of this.querySelectorAll<HTMLElement>('*')) {\n if ((el.constructor as { formAssociated?: boolean }).formAssociated) {\n return el as FormControl;\n }\n }\n return null;\n }\n\n private _teardown() {\n const control = this._control;\n if (!control) return;\n control.removeEventListener('invalid', this._onInvalid);\n control.removeEventListener('blur', this._onInteract);\n control.removeEventListener('change', this._onInteract);\n control.removeEventListener('input', this._onInteract);\n }\n\n private _onInvalid = (event: Event) => {\n // Suppress the native validation bubble in favour of `l-error`.\n event.preventDefault();\n this._hasInteracted = true;\n this._updateValidity();\n };\n\n private _onInteract = () => {\n this._hasInteracted = true;\n this._updateValidity();\n };\n\n private _updateValidity() {\n const control = this._control;\n if (!control) return;\n const showError = this._hasInteracted && !control.checkValidity();\n\n // `invalid` reflects to the host; CSS reveals `.l-error` from there.\n this.invalid = showError;\n if (showError) control.setAttribute('aria-invalid', 'true');\n else control.removeAttribute('aria-invalid');\n\n this._updateDescribedby();\n }\n\n private _updateDescribedby() {\n const control = this._control;\n if (!control) return;\n const ids = [...this._externalDescribedby];\n if (this._hint?.id) ids.push(this._hint.id);\n if (this._error && this.invalid && this._error.id) ids.push(this._error.id);\n if (ids.length) control.setAttribute('aria-describedby', ids.join(' '));\n else control.removeAttribute('aria-describedby');\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"form-field.js","names":[],"sources":["../../../src/html/elements/form-field/form-field.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { cls, uniqueId } from '../../registry.js';\n\ntype NativeControl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;\n/** A form-associated custom element (e.g. `l-slider`) exposes the same constraint\n * surface as a native control but doesn't match the native selector. */\ntype FormControl = NativeControl | (HTMLElement & { checkValidity(): boolean });\n\nconst CONTROL_SELECTOR = 'input, select, textarea';\n\n/**\n * @summary Progressively enhances a label + control + messages group: wires the\n * accessibility plumbing (`id`/`for`, `aria-describedby`, `aria-invalid`,\n * required marker) and picks an inline or stacked layout from the control type.\n *\n * Styling stays on the control (`.l-checkbox`, …); inside a field a bare control\n * is auto-styled by the element CSS. Add `unstyled` to keep the ARIA wiring while\n * opting out of that auto-styling (e.g. for a third-party control).\n *\n * @example\n * ```html\n * <l-form-field>\n * <label>Subscribe to the newsletter</label>\n * <input type=\"checkbox\" />\n * <p class=\"l-hint\">One email a month, unsubscribe anytime.</p>\n * <p class=\"l-error\">Please make a choice to continue.</p>\n * </l-form-field>\n * ```\n *\n * @slot - A `<label>`, one form control (a native `input` / `select` /\n * `textarea`, or a form-associated custom element such as `l-slider`), and\n * optional `.l-hint` / `.l-error` message elements, in any order.\n *\n * @cssClass .l-hint - Helper text element. Always visible; linked via `aria-describedby`.\n * @cssClass .l-error - Error message element. Hidden until invalid, then revealed with `role=\"alert\"` and linked via `aria-describedby`.\n *\n * @customElement l-form-field\n */\nexport class FormField extends LuxenElement {\n override createRenderRoot() {\n return this;\n }\n\n /** Layout, derived from the control type (`inline` for checkbox/radio/switch, otherwise `stacked`). Set one to force it. */\n @property({ reflect: true })\n layout?: 'inline' | 'stacked';\n\n /** Reflected when the control is required. Drives the label marker. */\n @property({ type: Boolean, reflect: true })\n required = false;\n\n /** Reflected when the control is not required. */\n @property({ type: Boolean, reflect: true })\n optional = false;\n\n /** Reflected once the control is invalid after interaction (or set to force the invalid state). */\n @property({ type: Boolean, reflect: true })\n invalid = false;\n\n /** Opt out of auto-styling the control; the ARIA wiring is preserved. */\n @property({ type: Boolean, reflect: true })\n unstyled = false;\n\n private _control: FormControl | null = null;\n private _hint: HTMLElement | null = null;\n private _error: HTMLElement | null = null;\n private _hasInteracted = false;\n private _wired = false;\n /** describedby ids set by the author that we must preserve. */\n private _externalDescribedby: string[] = [];\n\n override connectedCallback() {\n super.connectedCallback();\n // Run synchronously when the children are already parsed (the common case:\n // the element is defined after its markup). Fall back to a macrotask — which,\n // unlike requestAnimationFrame, still fires in a hidden tab — if the element\n // upgrades mid-parse before its children exist.\n if (this._setup()) return;\n setTimeout(() => this._setup(), 0);\n // A custom-element control (e.g. l-slider) can upgrade after us — its module\n // may load lazily — so the macrotask retry can still miss it. Re-attempt once\n // each pending custom-element child is defined. `_setup()` is idempotent via\n // `_wired`, so the earliest successful attempt wins.\n for (const el of this.querySelectorAll('*')) {\n const tag = el.localName;\n if (tag.includes('-') && !customElements.get(tag)) {\n void customElements.whenDefined(tag).then(() => this._setup());\n }\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._teardown();\n this._wired = false;\n }\n\n private _setup(): boolean {\n if (this._wired) return true;\n this._control = this._findControl();\n if (!this._control) return false;\n this._wired = true;\n const control = this._control;\n\n // Consumer-authored markup, so these carry the consumer's configured\n // `cssPrefix` — match on `cls()`, not a hardcoded `.l-hint` / `.l-error`.\n this._hint = this.querySelector(`.${cls('hint')}`);\n this._error = this.querySelector(`.${cls('error')}`);\n // The error is announced when revealed; the field owns this (the message is\n // a plain element with no JS of its own).\n if (this._error && !this._error.hasAttribute('role')) {\n this._error.setAttribute('role', 'alert');\n }\n\n // id / label[for]\n if (!control.id) control.id = uniqueId('field');\n const label = this.querySelector('label');\n if (label && !label.htmlFor && !label.contains(control)) {\n label.htmlFor = control.id;\n }\n\n // Ensure message elements have ids for aria-describedby.\n if (this._hint && !this._hint.id) this._hint.id = uniqueId('hint');\n if (this._error && !this._error.id) this._error.id = uniqueId('error');\n\n // Capture author-provided describedby ids (minus our managed ones).\n const managed = new Set([this._hint?.id, this._error?.id].filter(Boolean) as string[]);\n this._externalDescribedby = (control.getAttribute('aria-describedby') ?? '')\n .split(/\\s+/)\n .filter((id) => id && !managed.has(id));\n\n // Layout from control type (unless explicitly forced).\n if (!this.layout) {\n const isToggle =\n control.tagName === 'INPUT' &&\n (['checkbox', 'radio'].includes((control as HTMLInputElement).type) ||\n control.getAttribute('role') === 'switch');\n this.layout = isToggle ? 'inline' : 'stacked';\n }\n\n // Required marker (control is the source of truth).\n this.required = control.hasAttribute('required');\n this.optional = !this.required;\n\n // The error stays hidden until the field is invalid after interaction (or is\n // already flagged invalid by the author / server). Visibility is owned by CSS\n // off the reflected `invalid` state — the field never toggles `hidden`.\n const preInvalid = this.invalid || control.getAttribute('aria-invalid') === 'true';\n this._hasInteracted = preInvalid;\n\n control.addEventListener('invalid', this._onInvalid);\n control.addEventListener('blur', this._onInteract);\n control.addEventListener('change', this._onInteract);\n control.addEventListener('input', this._onInteract);\n\n this._updateValidity();\n return true;\n }\n\n /**\n * The field's control: a native `input`/`select`/`textarea`, or — when none is\n * present — the first form-associated custom element (e.g. `l-slider`). Those\n * expose the same `checkValidity()` / `invalid` event surface but don't match\n * the native selector, so without this fallback the field never wires up and\n * the `l-error` message stays visible on load.\n */\n private _findControl(): FormControl | null {\n const native = this.querySelector<NativeControl>(CONTROL_SELECTOR);\n if (native) return native;\n for (const el of this.querySelectorAll<HTMLElement>('*')) {\n if ((el.constructor as { formAssociated?: boolean }).formAssociated) {\n return el as FormControl;\n }\n }\n return null;\n }\n\n private _teardown() {\n const control = this._control;\n if (!control) return;\n control.removeEventListener('invalid', this._onInvalid);\n control.removeEventListener('blur', this._onInteract);\n control.removeEventListener('change', this._onInteract);\n control.removeEventListener('input', this._onInteract);\n }\n\n private _onInvalid = (event: Event) => {\n // Suppress the native validation bubble in favour of `l-error`.\n event.preventDefault();\n this._hasInteracted = true;\n this._updateValidity();\n };\n\n private _onInteract = () => {\n this._hasInteracted = true;\n this._updateValidity();\n };\n\n private _updateValidity() {\n const control = this._control;\n if (!control) return;\n const showError = this._hasInteracted && !control.checkValidity();\n\n // `invalid` reflects to the host; CSS reveals `.l-error` from there.\n this.invalid = showError;\n if (showError) control.setAttribute('aria-invalid', 'true');\n else control.removeAttribute('aria-invalid');\n\n this._updateDescribedby();\n }\n\n private _updateDescribedby() {\n const control = this._control;\n if (!control) return;\n const ids = [...this._externalDescribedby];\n if (this._hint?.id) ids.push(this._hint.id);\n if (this._error && this.invalid && this._error.id) ids.push(this._error.id);\n if (ids.length) control.setAttribute('aria-describedby', ids.join(' '));\n else control.removeAttribute('aria-describedby');\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"input-otp.d.ts","sourceRoot":"","sources":["../../../src/html/elements/input-otp/input-otp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,QAAS,SAAQ,YAAY;IAC/B,gBAAgB;IAIzB,sFAAsF;IAEtF,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAK;IAEf,iBAAiB;IAUjB,oBAAoB;IAM7B,mFAAmF;IACnF,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,MAAM;IAgEd,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,YAAY,CAyBlB;IAEF,OAAO,CAAC,WAAW,CAIjB;IAEF,OAAO,CAAC,oBAAoB,CAE1B;CACH"}
1
+ {"version":3,"file":"input-otp.d.ts","sourceRoot":"","sources":["../../../src/html/elements/input-otp/input-otp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,QAAS,SAAQ,YAAY;IAC/B,gBAAgB;IAIzB,sFAAsF;IAEtF,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAK;IAEf,iBAAiB;IAUjB,oBAAoB;IAM7B,mFAAmF;IACnF,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,MAAM;IAgEd,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,YAAY,CAyBlB;IAEF,OAAO,CAAC,WAAW,CAIjB;IAEF,OAAO,CAAC,oBAAoB,CAE1B;CACH"}
@@ -1,2 +1,2 @@
1
- import{i as e,t}from"../../chunks/decorate.js";import{LuxenElement as n}from"../../shared/luxen-element.js";var r=class extends n{constructor(...e){super(...e),this._cells=[],this._separatorEl=null,this._initialized=!1,this._setupTimer=0,this._updateCells=()=>{let e=this._input.value,t=this._input.maxLength||6,n=Math.min(this._input.selectionStart??0,t-1),r=document.activeElement===this._input;for(let t=0;t<this._cells.length;t++){let i=this._cells[t],a=i.firstElementChild,o=e[t]??``;a.textContent=o,o?i.setAttribute(`data-filled`,``):i.removeAttribute(`data-filled`),r&&t===n?i.setAttribute(`data-active`,``):i.removeAttribute(`data-active`)}},this._clearCells=()=>{for(let e of this._cells)e.removeAttribute(`data-active`)},this._scheduleUpdateCells=()=>{requestAnimationFrame(this._updateCells)}}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this._trySetup()||(this._setupTimer=window.setTimeout(()=>this._trySetup(),0))}disconnectedCallback(){super.disconnectedCallback(),clearTimeout(this._setupTimer),this._teardown()}_trySetup(){return this._initialized||!this.isConnected?!0:this.querySelector(`input`)?(this._setup(),!0):!1}_setup(){let e=this.querySelector(`input`);if(!e)return;this._input=e;let t=Number(getComputedStyle(this).getPropertyValue(`--digits`).trim())||6,n={type:`text`,inputmode:`numeric`,autocomplete:`one-time-code`,maxlength:String(t),pattern:String.raw`\d{${t}}`};for(let[e,t]of Object.entries(n))this._input.hasAttribute(e)||this._input.setAttribute(e,t);this._container=document.createElement(`div`),this._container.className=`l-input-otp-cells`;for(let e=0;e<t;e++){let n=document.createElement(`div`);n.className=`l-input-otp-cell`,n.setAttribute(`aria-hidden`,`true`),n.appendChild(document.createElement(`span`)),this._cells.push(n),this._container.appendChild(n),this.separatorAfter&&e===this.separatorAfter-1&&e<t-1&&(this._separatorEl=document.createElement(`span`),this._separatorEl.className=`l-input-otp-separator`,this._separatorEl.setAttribute(`aria-hidden`,`true`),this._container.appendChild(this._separatorEl))}this._input.replaceWith(this._container),this._container.appendChild(this._input),this._initialized=!0,this._updateCells(),this._input.addEventListener(`input`,this._updateCells),this._input.addEventListener(`click`,this._updateCells),this._input.addEventListener(`keyup`,this._updateCells),this._input.addEventListener(`focus`,this._scheduleUpdateCells),this._input.addEventListener(`blur`,this._clearCells)}_teardown(){this._initialized&&=(this._input.removeEventListener(`input`,this._updateCells),this._input.removeEventListener(`click`,this._updateCells),this._input.removeEventListener(`keyup`,this._updateCells),this._input.removeEventListener(`focus`,this._scheduleUpdateCells),this._input.removeEventListener(`blur`,this._clearCells),this._container.replaceWith(this._input),this._separatorEl?.remove(),this._cells=[],this._separatorEl=null,!1)}};t([e({type:Number,reflect:!0,attribute:`separator-after`})],r.prototype,`separatorAfter`,void 0);export{r as InputOtp};
1
+ import{cls as e}from"../../registry.js";import{i as t,t as n}from"../../chunks/decorate.js";import{LuxenElement as r}from"../../shared/luxen-element.js";var i=class extends r{constructor(...e){super(...e),this._cells=[],this._separatorEl=null,this._initialized=!1,this._setupTimer=0,this._updateCells=()=>{let e=this._input.value,t=this._input.maxLength||6,n=Math.min(this._input.selectionStart??0,t-1),r=document.activeElement===this._input;for(let t=0;t<this._cells.length;t++){let i=this._cells[t],a=i.firstElementChild,o=e[t]??``;a.textContent=o,o?i.setAttribute(`data-filled`,``):i.removeAttribute(`data-filled`),r&&t===n?i.setAttribute(`data-active`,``):i.removeAttribute(`data-active`)}},this._clearCells=()=>{for(let e of this._cells)e.removeAttribute(`data-active`)},this._scheduleUpdateCells=()=>{requestAnimationFrame(this._updateCells)}}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this._trySetup()||(this._setupTimer=window.setTimeout(()=>this._trySetup(),0))}disconnectedCallback(){super.disconnectedCallback(),clearTimeout(this._setupTimer),this._teardown()}_trySetup(){return this._initialized||!this.isConnected?!0:this.querySelector(`input`)?(this._setup(),!0):!1}_setup(){let t=this.querySelector(`input`);if(!t)return;this._input=t;let n=Number(getComputedStyle(this).getPropertyValue(`--digits`).trim())||6,r={type:`text`,inputmode:`numeric`,autocomplete:`one-time-code`,maxlength:String(n),pattern:String.raw`\d{${n}}`};for(let[e,t]of Object.entries(r))this._input.hasAttribute(e)||this._input.setAttribute(e,t);this._container=document.createElement(`div`),this._container.className=e(`input-otp-cells`);for(let t=0;t<n;t++){let r=document.createElement(`div`);r.className=e(`input-otp-cell`),r.setAttribute(`aria-hidden`,`true`),r.appendChild(document.createElement(`span`)),this._cells.push(r),this._container.appendChild(r),this.separatorAfter&&t===this.separatorAfter-1&&t<n-1&&(this._separatorEl=document.createElement(`span`),this._separatorEl.className=e(`input-otp-separator`),this._separatorEl.setAttribute(`aria-hidden`,`true`),this._container.appendChild(this._separatorEl))}this._input.replaceWith(this._container),this._container.appendChild(this._input),this._initialized=!0,this._updateCells(),this._input.addEventListener(`input`,this._updateCells),this._input.addEventListener(`click`,this._updateCells),this._input.addEventListener(`keyup`,this._updateCells),this._input.addEventListener(`focus`,this._scheduleUpdateCells),this._input.addEventListener(`blur`,this._clearCells)}_teardown(){this._initialized&&=(this._input.removeEventListener(`input`,this._updateCells),this._input.removeEventListener(`click`,this._updateCells),this._input.removeEventListener(`keyup`,this._updateCells),this._input.removeEventListener(`focus`,this._scheduleUpdateCells),this._input.removeEventListener(`blur`,this._clearCells),this._container.replaceWith(this._input),this._separatorEl?.remove(),this._cells=[],this._separatorEl=null,!1)}};n([t({type:Number,reflect:!0,attribute:`separator-after`})],i.prototype,`separatorAfter`,void 0);export{i as InputOtp};
2
2
  //# sourceMappingURL=input-otp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"input-otp.js","names":[],"sources":["../../../src/html/elements/input-otp/input-otp.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\n\n/**\n * Enhances a child `<input>` with visual digit cells (Stripe-style OTP input).\n *\n * A single hidden `<input>` handles keyboard, paste, and autocomplete.\n * Visual cells are rendered as real DOM elements with individual borders and focus ring.\n *\n * @summary Stripe-style OTP input with visual digit cells over a hidden native input.\n * @customElement l-input-otp\n *\n * @attribute size - sm | lg — Cell size. Default is md.\n *\n * @cssproperty --digits - Number of digit boxes (default: 6). Must match input's maxlength.\n * @cssproperty --cell-size - Cell width and height (default: 2.75rem). Font size scales automatically.\n * @cssproperty --cell-gap - Space between cells (default: 0.5rem).\n * @cssproperty --cell-bg-color - Cell background color.\n * @cssproperty --cell-border-color - Cell border color.\n * @cssproperty --cell-border-radius - Cell border-radius.\n * @cssproperty --cell-focus-color - Border + ring color of the active (focused) cell.\n * @cssproperty --cell-focus-ring - `box-shadow` of the active cell ring (defaults to a 1px solid ring; set to `none` to disable).\n */\nexport class InputOtp extends LuxenElement {\n override createRenderRoot() {\n return this;\n }\n\n /** Position after which to insert a visual separator (e.g., 3 for a 3-3 grouping). */\n @property({ type: Number, reflect: true, attribute: 'separator-after' })\n separatorAfter?: number;\n\n private _input!: HTMLInputElement;\n private _container!: HTMLDivElement;\n private _cells: HTMLDivElement[] = [];\n private _separatorEl: HTMLSpanElement | null = null;\n private _initialized = false;\n private _setupTimer = 0;\n\n override connectedCallback() {\n super.connectedCallback();\n // Children may not be parsed yet when the element upgrades mid-parse: try\n // synchronously, then retry once on a macrotask. setTimeout, not rAF — rAF\n // is suspended in hidden documents, so the element would stay inert there.\n if (!this._trySetup()) {\n this._setupTimer = window.setTimeout(() => this._trySetup(), 0);\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n clearTimeout(this._setupTimer);\n this._teardown();\n }\n\n /** @returns true when setup ran or was already done; false to schedule a retry. */\n private _trySetup(): boolean {\n if (this._initialized || !this.isConnected) return true;\n if (!this.querySelector('input')) return false;\n this._setup();\n return true;\n }\n\n // --- Setup / Teardown ---\n\n private _setup() {\n const input = this.querySelector<HTMLInputElement>('input');\n if (!input) return;\n\n this._input = input;\n\n // Derive digit count from --digits CSS custom property (default 6)\n const digits = Number(getComputedStyle(this).getPropertyValue('--digits').trim()) || 6;\n\n // Set sensible defaults — author can still override via HTML attributes\n const defaults: Record<string, string> = {\n type: 'text',\n inputmode: 'numeric',\n autocomplete: 'one-time-code',\n maxlength: String(digits),\n pattern: String.raw`\\d{${digits}}`,\n };\n for (const [attr, value] of Object.entries(defaults)) {\n if (!this._input.hasAttribute(attr)) {\n this._input.setAttribute(attr, value);\n }\n }\n\n // Build visual cells container. The real <input> is moved INTO this\n // container (below), so the container itself must stay exposed to AT —\n // aria-hidden goes on each decorative cell/separator, never on the wrapper\n // (a focusable element inside an aria-hidden subtree violates WCAG 4.1.2).\n this._container = document.createElement('div');\n this._container.className = 'l-input-otp-cells';\n\n for (let i = 0; i < digits; i++) {\n const cell = document.createElement('div');\n cell.className = 'l-input-otp-cell';\n cell.setAttribute('aria-hidden', 'true');\n cell.appendChild(document.createElement('span'));\n this._cells.push(cell);\n this._container.appendChild(cell);\n\n // Insert separator after the specified position\n if (this.separatorAfter && i === this.separatorAfter - 1 && i < digits - 1) {\n this._separatorEl = document.createElement('span');\n this._separatorEl.className = 'l-input-otp-separator';\n this._separatorEl.setAttribute('aria-hidden', 'true');\n this._container.appendChild(this._separatorEl);\n }\n }\n\n // Wrap: insert container before input, then move input inside\n this._input.replaceWith(this._container);\n this._container.appendChild(this._input);\n this._initialized = true;\n\n // Populate cells if input already has a value (e.g. disabled with prefilled value)\n this._updateCells();\n\n // Events — focus is deferred so it runs after the click that triggered it\n // (otherwise selectionStart is stale and the active cell flickers).\n this._input.addEventListener('input', this._updateCells);\n this._input.addEventListener('click', this._updateCells);\n this._input.addEventListener('keyup', this._updateCells);\n this._input.addEventListener('focus', this._scheduleUpdateCells);\n this._input.addEventListener('blur', this._clearCells);\n }\n\n private _teardown() {\n if (!this._initialized) return;\n\n this._input.removeEventListener('input', this._updateCells);\n this._input.removeEventListener('click', this._updateCells);\n this._input.removeEventListener('keyup', this._updateCells);\n this._input.removeEventListener('focus', this._scheduleUpdateCells);\n this._input.removeEventListener('blur', this._clearCells);\n\n // Restore input to direct child\n this._container.replaceWith(this._input);\n this._separatorEl?.remove();\n\n this._cells = [];\n this._separatorEl = null;\n this._initialized = false;\n }\n\n // --- Cell updates ---\n\n private _updateCells = (): void => {\n const value = this._input.value;\n const maxLen = this._input.maxLength || 6;\n const pos = Math.min(this._input.selectionStart ?? 0, maxLen - 1);\n const isFocused = document.activeElement === this._input;\n\n for (let i = 0; i < this._cells.length; i++) {\n const cell = this._cells[i];\n const span = cell.firstElementChild as HTMLSpanElement;\n const char = value[i] ?? '';\n\n span.textContent = char;\n\n if (char) {\n cell.setAttribute('data-filled', '');\n } else {\n cell.removeAttribute('data-filled');\n }\n\n if (isFocused && i === pos) {\n cell.setAttribute('data-active', '');\n } else {\n cell.removeAttribute('data-active');\n }\n }\n };\n\n private _clearCells = (): void => {\n for (const cell of this._cells) {\n cell.removeAttribute('data-active');\n }\n };\n\n private _scheduleUpdateCells = (): void => {\n requestAnimationFrame(this._updateCells);\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"input-otp.js","names":[],"sources":["../../../src/html/elements/input-otp/input-otp.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { cls } from '../../registry.js';\n\n/**\n * Enhances a child `<input>` with visual digit cells (Stripe-style OTP input).\n *\n * A single hidden `<input>` handles keyboard, paste, and autocomplete.\n * Visual cells are rendered as real DOM elements with individual borders and focus ring.\n *\n * @summary Stripe-style OTP input with visual digit cells over a hidden native input.\n * @customElement l-input-otp\n *\n * @attribute size - sm | lg — Cell size. Default is md.\n *\n * @cssproperty --digits - Number of digit boxes (default: 6). Must match input's maxlength.\n * @cssproperty --cell-size - Cell width and height (default: 2.75rem). Font size scales automatically.\n * @cssproperty --cell-gap - Space between cells (default: 0.5rem).\n * @cssproperty --cell-bg-color - Cell background color.\n * @cssproperty --cell-border-color - Cell border color.\n * @cssproperty --cell-border-radius - Cell border-radius.\n * @cssproperty --cell-focus-color - Border + ring color of the active (focused) cell.\n * @cssproperty --cell-focus-ring - `box-shadow` of the active cell ring (defaults to a 1px solid ring; set to `none` to disable).\n */\nexport class InputOtp extends LuxenElement {\n override createRenderRoot() {\n return this;\n }\n\n /** Position after which to insert a visual separator (e.g., 3 for a 3-3 grouping). */\n @property({ type: Number, reflect: true, attribute: 'separator-after' })\n separatorAfter?: number;\n\n private _input!: HTMLInputElement;\n private _container!: HTMLDivElement;\n private _cells: HTMLDivElement[] = [];\n private _separatorEl: HTMLSpanElement | null = null;\n private _initialized = false;\n private _setupTimer = 0;\n\n override connectedCallback() {\n super.connectedCallback();\n // Children may not be parsed yet when the element upgrades mid-parse: try\n // synchronously, then retry once on a macrotask. setTimeout, not rAF — rAF\n // is suspended in hidden documents, so the element would stay inert there.\n if (!this._trySetup()) {\n this._setupTimer = window.setTimeout(() => this._trySetup(), 0);\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n clearTimeout(this._setupTimer);\n this._teardown();\n }\n\n /** @returns true when setup ran or was already done; false to schedule a retry. */\n private _trySetup(): boolean {\n if (this._initialized || !this.isConnected) return true;\n if (!this.querySelector('input')) return false;\n this._setup();\n return true;\n }\n\n // --- Setup / Teardown ---\n\n private _setup() {\n const input = this.querySelector<HTMLInputElement>('input');\n if (!input) return;\n\n this._input = input;\n\n // Derive digit count from --digits CSS custom property (default 6)\n const digits = Number(getComputedStyle(this).getPropertyValue('--digits').trim()) || 6;\n\n // Set sensible defaults — author can still override via HTML attributes\n const defaults: Record<string, string> = {\n type: 'text',\n inputmode: 'numeric',\n autocomplete: 'one-time-code',\n maxlength: String(digits),\n pattern: String.raw`\\d{${digits}}`,\n };\n for (const [attr, value] of Object.entries(defaults)) {\n if (!this._input.hasAttribute(attr)) {\n this._input.setAttribute(attr, value);\n }\n }\n\n // Build visual cells container. The real <input> is moved INTO this\n // container (below), so the container itself must stay exposed to AT —\n // aria-hidden goes on each decorative cell/separator, never on the wrapper\n // (a focusable element inside an aria-hidden subtree violates WCAG 4.1.2).\n this._container = document.createElement('div');\n this._container.className = cls('input-otp-cells');\n\n for (let i = 0; i < digits; i++) {\n const cell = document.createElement('div');\n cell.className = cls('input-otp-cell');\n cell.setAttribute('aria-hidden', 'true');\n cell.appendChild(document.createElement('span'));\n this._cells.push(cell);\n this._container.appendChild(cell);\n\n // Insert separator after the specified position\n if (this.separatorAfter && i === this.separatorAfter - 1 && i < digits - 1) {\n this._separatorEl = document.createElement('span');\n this._separatorEl.className = cls('input-otp-separator');\n this._separatorEl.setAttribute('aria-hidden', 'true');\n this._container.appendChild(this._separatorEl);\n }\n }\n\n // Wrap: insert container before input, then move input inside\n this._input.replaceWith(this._container);\n this._container.appendChild(this._input);\n this._initialized = true;\n\n // Populate cells if input already has a value (e.g. disabled with prefilled value)\n this._updateCells();\n\n // Events — focus is deferred so it runs after the click that triggered it\n // (otherwise selectionStart is stale and the active cell flickers).\n this._input.addEventListener('input', this._updateCells);\n this._input.addEventListener('click', this._updateCells);\n this._input.addEventListener('keyup', this._updateCells);\n this._input.addEventListener('focus', this._scheduleUpdateCells);\n this._input.addEventListener('blur', this._clearCells);\n }\n\n private _teardown() {\n if (!this._initialized) return;\n\n this._input.removeEventListener('input', this._updateCells);\n this._input.removeEventListener('click', this._updateCells);\n this._input.removeEventListener('keyup', this._updateCells);\n this._input.removeEventListener('focus', this._scheduleUpdateCells);\n this._input.removeEventListener('blur', this._clearCells);\n\n // Restore input to direct child\n this._container.replaceWith(this._input);\n this._separatorEl?.remove();\n\n this._cells = [];\n this._separatorEl = null;\n this._initialized = false;\n }\n\n // --- Cell updates ---\n\n private _updateCells = (): void => {\n const value = this._input.value;\n const maxLen = this._input.maxLength || 6;\n const pos = Math.min(this._input.selectionStart ?? 0, maxLen - 1);\n const isFocused = document.activeElement === this._input;\n\n for (let i = 0; i < this._cells.length; i++) {\n const cell = this._cells[i];\n const span = cell.firstElementChild as HTMLSpanElement;\n const char = value[i] ?? '';\n\n span.textContent = char;\n\n if (char) {\n cell.setAttribute('data-filled', '');\n } else {\n cell.removeAttribute('data-filled');\n }\n\n if (isFocused && i === pos) {\n cell.setAttribute('data-active', '');\n } else {\n cell.removeAttribute('data-active');\n }\n }\n };\n\n private _clearCells = (): void => {\n for (const cell of this._cells) {\n cell.removeAttribute('data-active');\n }\n };\n\n private _scheduleUpdateCells = (): void => {\n requestAnimationFrame(this._updateCells);\n };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{tagName as e}from"../../registry.js";import{i as t,t as n}from"../../chunks/decorate.js";import{LuxenElement as r}from"../../shared/luxen-element.js";import{LocalizeController as i}from"../../shared/localize.js";var a=class extends Event{constructor(e){super(`change`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},o=class extends r{constructor(...e){super(...e),this._localize=new i(this),this.size=`md`,this.withRoller=!1,this.decrementIcon=`lucide:minus`,this.incrementIcon=`lucide:plus`,this._initialized=!1,this._setupTimer=0,this._input=null,this._decrementBtn=null,this._incrementBtn=null,this._valueWrapper=null,this._trackDisplay=null,this._track=null,this._observer=null,this._skipTransition=!1,this._onDecrement=()=>this.decrement(),this._onIncrement=()=>this.increment(),this._onInputChange=e=>{if(!this._input)return;e.stopPropagation();let{min:t,max:n}=this._getConstraints(),r=Math.min(Math.max(this._input.valueAsNumber,t),n);this._input.value=String(r),this._skipTransition=!0,this._updateButtonStates(),this._updateTrack(),this.dispatchEvent(new a(r))}}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this._trySetup()||(this._setupTimer=window.setTimeout(()=>this._trySetup(),0))}disconnectedCallback(){super.disconnectedCallback(),clearTimeout(this._setupTimer),this._teardown()}_trySetup(){return this._initialized||!this.isConnected?!0:this.querySelector(`input[type="number"]`)?(this._setup(),!0):!1}updated(e){this._input&&((e.has(`min`)||e.has(`max`)||e.has(`step`))&&(this._syncConstraints(),this._updateButtonStates()),this._decrementBtn?.setAttribute(`aria-label`,this._localize.term(`decreaseValue`)),this._incrementBtn?.setAttribute(`aria-label`,this._localize.term(`increaseValue`)))}decrement(){if(!this._input)return;let{min:e,step:t}=this._getConstraints(),n=Math.max(this._input.valueAsNumber-t,e);this._applyValue(n)}increment(){if(!this._input)return;let{max:e,step:t}=this._getConstraints(),n=Math.min(this._input.valueAsNumber+t,e);this._applyValue(n)}_setup(){this._input=this.querySelector(`input[type="number"]`),this._input&&(this._initialized=!0,this._input.inputMode=`numeric`,this._valueWrapper=document.createElement(`div`),this._valueWrapper.className=`l-input-stepper-value`,this._input.replaceWith(this._valueWrapper),this._valueWrapper.appendChild(this._input),this.withRoller&&this._buildTrack(),this._decrementBtn=this._createButton(this.decrementIcon,this._localize.term(`decreaseValue`)),this._incrementBtn=this._createButton(this.incrementIcon,this._localize.term(`increaseValue`)),this._valueWrapper.before(this._decrementBtn),this._valueWrapper.after(this._incrementBtn),this._decrementBtn.addEventListener(`click`,this._onDecrement),this._incrementBtn.addEventListener(`click`,this._onIncrement),this._input.addEventListener(`change`,this._onInputChange),this._observer=new MutationObserver(()=>this._updateButtonStates()),this._observer.observe(this._input,{attributes:!0,attributeFilter:[`disabled`]}),this._syncConstraints(),this._updateButtonStates(),this._updateTrack())}_teardown(){this._decrementBtn?.removeEventListener(`click`,this._onDecrement),this._incrementBtn?.removeEventListener(`click`,this._onIncrement),this._input?.removeEventListener(`change`,this._onInputChange),this._observer?.disconnect(),this._input&&this._valueWrapper&&this._valueWrapper.replaceWith(this._input),this._decrementBtn?.remove(),this._incrementBtn?.remove(),this._input=null,this._decrementBtn=null,this._incrementBtn=null,this._valueWrapper=null,this._trackDisplay=null,this._track=null,this._observer=null,this._initialized=!1}_createButton(t,n){let r=document.createElement(`button`);r.type=`button`,r.setAttribute(`aria-label`,n);let i=document.createElement(e(`icon`));return i.setAttribute(`name`,t),r.append(i),r}_buildTrack(){this._valueWrapper&&(this._trackDisplay=document.createElement(`div`),this._trackDisplay.className=`l-input-stepper-track-display`,this._track=document.createElement(`div`),this._track.className=`l-input-stepper-track`,this._track.setAttribute(`aria-hidden`,`true`),this._rebuildTrackNumbers(),this._trackDisplay.appendChild(this._track),this._valueWrapper.appendChild(this._trackDisplay))}_rebuildTrackNumbers(){if(!this._track)return;let{min:e,max:t,step:n}=this._getConstraints(),r=Number.isFinite(t)?t:(this._input?.valueAsNumber??0)+100;this._track.innerHTML=``;for(let t=e;t<=r;t+=n){let e=document.createElement(`div`);e.textContent=String(t),this._track.appendChild(e)}}_getConstraints(){return{min:this.min??(Number(this._input?.getAttribute(`min`))||0),max:this.max??(this._input?.hasAttribute(`max`)?Number(this._input.getAttribute(`max`)):1/0),step:this.step??(Number(this._input?.getAttribute(`step`))||1)}}_syncConstraints(){if(!this._input)return;let{min:e,max:t,step:n}=this._getConstraints();this._input.min=String(e),Number.isFinite(t)&&(this._input.max=String(t)),this._input.step=String(n),this._rebuildTrackNumbers(),this._updateTrack()}_applyValue(e){this._input&&(this._input.value=String(e),this._updateButtonStates(),this._updateTrack(),this.dispatchEvent(new a(e)))}_updateTrack(){if(!this._track)return;let{min:e,step:t}=this._getConstraints(),n=(e-(this._input?.valueAsNumber??e))/t;this._track.style.translate=`0 calc(${n} * var(--_button-size))`,this._skipTransition&&=(this._track.style.transition=`none`,this._track.offsetHeight,this._track.style.transition=``,!1)}_updateButtonStates(){if(!this._input||!this._decrementBtn||!this._incrementBtn)return;let{min:e,max:t}=this._getConstraints(),n=this._input.valueAsNumber,r=this._input.disabled;this._decrementBtn.disabled=r||n<=e,this._incrementBtn.disabled=r||n>=t}};n([t({type:Number})],o.prototype,`min`,void 0),n([t({type:Number})],o.prototype,`max`,void 0),n([t({type:Number})],o.prototype,`step`,void 0),n([t({reflect:!0})],o.prototype,`size`,void 0),n([t({type:Boolean,reflect:!0,attribute:`with-roller`})],o.prototype,`withRoller`,void 0),n([t({attribute:`decrement-icon`})],o.prototype,`decrementIcon`,void 0),n([t({attribute:`increment-icon`})],o.prototype,`incrementIcon`,void 0);export{o as InputStepper,a as StepperChangeEvent};
1
+ import{cls as e,tagName as t}from"../../registry.js";import{i as n,t as r}from"../../chunks/decorate.js";import{LuxenElement as i}from"../../shared/luxen-element.js";import{LocalizeController as a}from"../../shared/localize.js";var o=class extends Event{constructor(e){super(`change`,{bubbles:!0,composed:!1,cancelable:!1}),this.value=e}},s=class extends i{constructor(...e){super(...e),this._localize=new a(this),this.size=`md`,this.withRoller=!1,this.decrementIcon=`lucide:minus`,this.incrementIcon=`lucide:plus`,this._initialized=!1,this._setupTimer=0,this._input=null,this._decrementBtn=null,this._incrementBtn=null,this._valueWrapper=null,this._trackDisplay=null,this._track=null,this._observer=null,this._skipTransition=!1,this._onDecrement=()=>this.decrement(),this._onIncrement=()=>this.increment(),this._onInputChange=e=>{if(!this._input)return;e.stopPropagation();let{min:t,max:n}=this._getConstraints(),r=Math.min(Math.max(this._input.valueAsNumber,t),n);this._input.value=String(r),this._skipTransition=!0,this._updateButtonStates(),this._updateTrack(),this.dispatchEvent(new o(r))}}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this._trySetup()||(this._setupTimer=window.setTimeout(()=>this._trySetup(),0))}disconnectedCallback(){super.disconnectedCallback(),clearTimeout(this._setupTimer),this._teardown()}_trySetup(){return this._initialized||!this.isConnected?!0:this.querySelector(`input[type="number"]`)?(this._setup(),!0):!1}updated(e){this._input&&((e.has(`min`)||e.has(`max`)||e.has(`step`))&&(this._syncConstraints(),this._updateButtonStates()),this._decrementBtn?.setAttribute(`aria-label`,this._localize.term(`decreaseValue`)),this._incrementBtn?.setAttribute(`aria-label`,this._localize.term(`increaseValue`)))}decrement(){if(!this._input)return;let{min:e,step:t}=this._getConstraints(),n=Math.max(this._input.valueAsNumber-t,e);this._applyValue(n)}increment(){if(!this._input)return;let{max:e,step:t}=this._getConstraints(),n=Math.min(this._input.valueAsNumber+t,e);this._applyValue(n)}_setup(){this._input=this.querySelector(`input[type="number"]`),this._input&&(this._initialized=!0,this._input.inputMode=`numeric`,this._valueWrapper=document.createElement(`div`),this._valueWrapper.className=e(`input-stepper-value`),this._input.replaceWith(this._valueWrapper),this._valueWrapper.appendChild(this._input),this.withRoller&&this._buildTrack(),this._decrementBtn=this._createButton(this.decrementIcon,this._localize.term(`decreaseValue`)),this._incrementBtn=this._createButton(this.incrementIcon,this._localize.term(`increaseValue`)),this._valueWrapper.before(this._decrementBtn),this._valueWrapper.after(this._incrementBtn),this._decrementBtn.addEventListener(`click`,this._onDecrement),this._incrementBtn.addEventListener(`click`,this._onIncrement),this._input.addEventListener(`change`,this._onInputChange),this._observer=new MutationObserver(()=>this._updateButtonStates()),this._observer.observe(this._input,{attributes:!0,attributeFilter:[`disabled`]}),this._syncConstraints(),this._updateButtonStates(),this._updateTrack())}_teardown(){this._decrementBtn?.removeEventListener(`click`,this._onDecrement),this._incrementBtn?.removeEventListener(`click`,this._onIncrement),this._input?.removeEventListener(`change`,this._onInputChange),this._observer?.disconnect(),this._input&&this._valueWrapper&&this._valueWrapper.replaceWith(this._input),this._decrementBtn?.remove(),this._incrementBtn?.remove(),this._input=null,this._decrementBtn=null,this._incrementBtn=null,this._valueWrapper=null,this._trackDisplay=null,this._track=null,this._observer=null,this._initialized=!1}_createButton(e,n){let r=document.createElement(`button`);r.type=`button`,r.setAttribute(`aria-label`,n);let i=document.createElement(t(`icon`));return i.setAttribute(`name`,e),r.append(i),r}_buildTrack(){this._valueWrapper&&(this._trackDisplay=document.createElement(`div`),this._trackDisplay.className=e(`input-stepper-track-display`),this._track=document.createElement(`div`),this._track.className=e(`input-stepper-track`),this._track.setAttribute(`aria-hidden`,`true`),this._rebuildTrackNumbers(),this._trackDisplay.appendChild(this._track),this._valueWrapper.appendChild(this._trackDisplay))}_rebuildTrackNumbers(){if(!this._track)return;let{min:e,max:t,step:n}=this._getConstraints(),r=Number.isFinite(t)?t:(this._input?.valueAsNumber??0)+100;this._track.innerHTML=``;for(let t=e;t<=r;t+=n){let e=document.createElement(`div`);e.textContent=String(t),this._track.appendChild(e)}}_getConstraints(){return{min:this.min??(Number(this._input?.getAttribute(`min`))||0),max:this.max??(this._input?.hasAttribute(`max`)?Number(this._input.getAttribute(`max`)):1/0),step:this.step??(Number(this._input?.getAttribute(`step`))||1)}}_syncConstraints(){if(!this._input)return;let{min:e,max:t,step:n}=this._getConstraints();this._input.min=String(e),Number.isFinite(t)&&(this._input.max=String(t)),this._input.step=String(n),this._rebuildTrackNumbers(),this._updateTrack()}_applyValue(e){this._input&&(this._input.value=String(e),this._updateButtonStates(),this._updateTrack(),this.dispatchEvent(new o(e)))}_updateTrack(){if(!this._track)return;let{min:e,step:t}=this._getConstraints(),n=(e-(this._input?.valueAsNumber??e))/t;this._track.style.translate=`0 calc(${n} * var(--_button-size))`,this._skipTransition&&=(this._track.style.transition=`none`,this._track.offsetHeight,this._track.style.transition=``,!1)}_updateButtonStates(){if(!this._input||!this._decrementBtn||!this._incrementBtn)return;let{min:e,max:t}=this._getConstraints(),n=this._input.valueAsNumber,r=this._input.disabled;this._decrementBtn.disabled=r||n<=e,this._incrementBtn.disabled=r||n>=t}};r([n({type:Number})],s.prototype,`min`,void 0),r([n({type:Number})],s.prototype,`max`,void 0),r([n({type:Number})],s.prototype,`step`,void 0),r([n({reflect:!0})],s.prototype,`size`,void 0),r([n({type:Boolean,reflect:!0,attribute:`with-roller`})],s.prototype,`withRoller`,void 0),r([n({attribute:`decrement-icon`})],s.prototype,`decrementIcon`,void 0),r([n({attribute:`increment-icon`})],s.prototype,`incrementIcon`,void 0);export{s as InputStepper,o as StepperChangeEvent};
2
2
  //# sourceMappingURL=input-stepper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"input-stepper.js","names":[],"sources":["../../../src/html/elements/input-stepper/input-stepper.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { tagName } from '../../registry.js';\n\nexport type InputStepperSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\n/** Fired when the stepper value changes. Bubbles; not composed. */\nexport class StepperChangeEvent extends Event {\n readonly value: number;\n constructor(value: number) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\ninterface InputStepperEventMap {\n change: StepperChangeEvent;\n}\n\n/**\n * A stepper control that enhances a native `<input type=\"number\">` with\n * decrement/increment buttons and an optional animated number track.\n *\n * @link https://www.nngroup.com/articles/input-steppers/\n *\n * @example\n * ```html\n * <l-input-stepper>\n * <input type=\"number\" min=\"0\" max=\"10\" value=\"5\" />\n * </l-input-stepper>\n * ```\n *\n * @event change - Fired when the value changes. Bubbles. Properties: `value: number`.\n *\n * @cssproperty --border-color - Border color of the stepper container (default appearance) and of each button (rounded appearance). Defaults to `--l-color-border`.\n * @cssproperty --border-radius - Border radius of the stepper container (default appearance). Defaults to `--radius-md`.\n *\n * @customElement l-input-stepper\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class InputStepper extends LuxenElement {\n private _localize = new LocalizeController(this);\n\n override createRenderRoot() {\n return this;\n }\n\n /** Minimum allowed value. Falls back to the input's `min` attribute. */\n @property({ type: Number })\n min?: number;\n\n /** Maximum allowed value. Falls back to the input's `max` attribute. */\n @property({ type: Number })\n max?: number;\n\n /** Step increment. Falls back to the input's `step` attribute. */\n @property({ type: Number })\n step?: number;\n\n /** Control size. */\n @property({ reflect: true })\n size: InputStepperSize = 'md';\n\n /** Enable the animated number roller overlay. */\n @property({ type: Boolean, reflect: true, attribute: 'with-roller' })\n withRoller = false;\n\n /** Icon name for the decrement button. */\n @property({ attribute: 'decrement-icon' })\n decrementIcon = 'lucide:minus';\n\n /** Icon name for the increment button. */\n @property({ attribute: 'increment-icon' })\n incrementIcon = 'lucide:plus';\n\n private _initialized = false;\n private _setupTimer = 0;\n private _input: HTMLInputElement | null = null;\n private _decrementBtn: HTMLButtonElement | null = null;\n private _incrementBtn: HTMLButtonElement | null = null;\n private _valueWrapper: HTMLDivElement | null = null;\n private _trackDisplay: HTMLDivElement | null = null;\n private _track: HTMLDivElement | null = null;\n private _observer: MutationObserver | null = null;\n private _skipTransition = false;\n\n override connectedCallback() {\n super.connectedCallback();\n // Children may not be parsed yet when the element upgrades mid-parse: try\n // synchronously, then retry once on a macrotask. setTimeout, not rAF — rAF\n // is suspended in hidden documents, so the element would stay inert there.\n if (!this._trySetup()) {\n this._setupTimer = window.setTimeout(() => this._trySetup(), 0);\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n clearTimeout(this._setupTimer);\n this._teardown();\n }\n\n /** @returns true when setup ran or was already done; false to schedule a retry. */\n private _trySetup(): boolean {\n if (this._initialized || !this.isConnected) return true;\n if (!this.querySelector('input[type=\"number\"]')) return false;\n this._setup();\n return true;\n }\n\n override updated(changed: Map<string, unknown>) {\n if (!this._input) return;\n\n if (changed.has('min') || changed.has('max') || changed.has('step')) {\n this._syncConstraints();\n this._updateButtonStates();\n }\n\n // Button labels are set imperatively in _setup() (outside any render), so\n // re-apply them on every update to track a language change (the localize\n // controller calls requestUpdate when <html lang> changes).\n this._decrementBtn?.setAttribute('aria-label', this._localize.term('decreaseValue'));\n this._incrementBtn?.setAttribute('aria-label', this._localize.term('increaseValue'));\n }\n\n /** Decrease the value by one step. */\n decrement() {\n if (!this._input) return;\n const { min, step } = this._getConstraints();\n const newVal = Math.max(this._input.valueAsNumber - step, min);\n this._applyValue(newVal);\n }\n\n /** Increase the value by one step. */\n increment() {\n if (!this._input) return;\n const { max, step } = this._getConstraints();\n const newVal = Math.min(this._input.valueAsNumber + step, max);\n this._applyValue(newVal);\n }\n\n // --- Setup / Teardown ---\n\n private _setup() {\n this._input = this.querySelector('input[type=\"number\"]');\n if (!this._input) return;\n this._initialized = true;\n\n this._input.inputMode = 'numeric';\n\n // Wrap input in value container\n this._valueWrapper = document.createElement('div');\n this._valueWrapper.className = 'l-input-stepper-value';\n this._input.replaceWith(this._valueWrapper);\n this._valueWrapper.appendChild(this._input);\n\n // Inject track overlay\n if (this.withRoller) {\n this._buildTrack();\n }\n\n // Create buttons\n this._decrementBtn = this._createButton(\n this.decrementIcon,\n this._localize.term('decreaseValue'),\n );\n this._incrementBtn = this._createButton(\n this.incrementIcon,\n this._localize.term('increaseValue'),\n );\n\n this._valueWrapper.before(this._decrementBtn);\n this._valueWrapper.after(this._incrementBtn);\n\n // Events\n this._decrementBtn.addEventListener('click', this._onDecrement);\n this._incrementBtn.addEventListener('click', this._onIncrement);\n this._input.addEventListener('change', this._onInputChange);\n\n // Observe disabled attribute on input\n this._observer = new MutationObserver(() => this._updateButtonStates());\n this._observer.observe(this._input, { attributes: true, attributeFilter: ['disabled'] });\n\n this._syncConstraints();\n this._updateButtonStates();\n this._updateTrack();\n }\n\n private _teardown() {\n this._decrementBtn?.removeEventListener('click', this._onDecrement);\n this._incrementBtn?.removeEventListener('click', this._onIncrement);\n this._input?.removeEventListener('change', this._onInputChange);\n this._observer?.disconnect();\n\n // Restore input to direct child\n if (this._input && this._valueWrapper) {\n this._valueWrapper.replaceWith(this._input);\n }\n this._decrementBtn?.remove();\n this._incrementBtn?.remove();\n\n this._input = null;\n this._decrementBtn = null;\n this._incrementBtn = null;\n this._valueWrapper = null;\n this._trackDisplay = null;\n this._track = null;\n this._observer = null;\n this._initialized = false;\n }\n\n // --- DOM helpers ---\n\n private _createButton(icon: string, label: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.setAttribute('aria-label', label);\n const iconEl = document.createElement(tagName('icon'));\n iconEl.setAttribute('name', icon);\n btn.append(iconEl);\n return btn;\n }\n\n private _buildTrack() {\n if (!this._valueWrapper) return;\n\n this._trackDisplay = document.createElement('div');\n this._trackDisplay.className = 'l-input-stepper-track-display';\n\n this._track = document.createElement('div');\n this._track.className = 'l-input-stepper-track';\n this._track.setAttribute('aria-hidden', 'true');\n\n this._rebuildTrackNumbers();\n this._trackDisplay.appendChild(this._track);\n this._valueWrapper.appendChild(this._trackDisplay);\n }\n\n private _rebuildTrackNumbers() {\n if (!this._track) return;\n\n const { min, max, step } = this._getConstraints();\n const effectiveMax = Number.isFinite(max) ? max : (this._input?.valueAsNumber ?? 0) + 100;\n\n this._track.innerHTML = '';\n for (let i = min; i <= effectiveMax; i += step) {\n const div = document.createElement('div');\n div.textContent = String(i);\n this._track.appendChild(div);\n }\n }\n\n // --- Constraints ---\n\n private _getConstraints() {\n const min = this.min ?? (Number(this._input?.getAttribute('min')) || 0);\n const max =\n this.max ??\n (this._input?.hasAttribute('max') ? Number(this._input.getAttribute('max')) : Infinity);\n const step = this.step ?? (Number(this._input?.getAttribute('step')) || 1);\n return { min, max, step };\n }\n\n private _syncConstraints() {\n if (!this._input) return;\n const { min, max, step } = this._getConstraints();\n\n this._input.min = String(min);\n if (Number.isFinite(max)) this._input.max = String(max);\n this._input.step = String(step);\n\n this._rebuildTrackNumbers();\n this._updateTrack();\n }\n\n // --- Value ---\n\n private _applyValue(val: number) {\n if (!this._input) return;\n this._input.value = String(val);\n this._updateButtonStates();\n this._updateTrack();\n this.dispatchEvent(new StepperChangeEvent(val));\n }\n\n // --- Track ---\n\n private _updateTrack() {\n if (!this._track) return;\n\n const { min, step } = this._getConstraints();\n const value = this._input?.valueAsNumber ?? min;\n const offset = (min - value) / step;\n\n this._track.style.translate = `0 calc(${offset} * var(--_button-size))`;\n\n if (this._skipTransition) {\n this._track.style.transition = 'none';\n void this._track.offsetHeight;\n this._track.style.transition = '';\n this._skipTransition = false;\n }\n }\n\n // --- Button states ---\n\n private _updateButtonStates() {\n if (!this._input || !this._decrementBtn || !this._incrementBtn) return;\n\n const { min, max } = this._getConstraints();\n const value = this._input.valueAsNumber;\n const disabled = this._input.disabled;\n\n this._decrementBtn.disabled = disabled || value <= min;\n this._incrementBtn.disabled = disabled || value >= max;\n }\n\n // --- Event handlers ---\n\n private _onDecrement = () => this.decrement();\n private _onIncrement = () => this.increment();\n\n private _onInputChange = (e: Event) => {\n if (!this._input) return;\n // Swallow the inner <input>'s native `change` so the host emits a single,\n // typed `change` (StepperChangeEvent) rather than two events — the native\n // one bubbles through the light-DOM host alongside our own.\n e.stopPropagation();\n\n const { min, max } = this._getConstraints();\n const clamped = Math.min(Math.max(this._input.valueAsNumber, min), max);\n this._input.value = String(clamped);\n\n this._skipTransition = true;\n this._updateButtonStates();\n this._updateTrack();\n this.dispatchEvent(new StepperChangeEvent(clamped));\n };\n}\n\n// Types `addEventListener('change', …)` as `StepperChangeEvent` on this element\n// (the global event map can't be augmented for the colliding name `change`).\n// See the Tabs interface in tabs.ts for the full rationale and the gotchas.\nexport interface InputStepper {\n addEventListener<K extends keyof InputStepperEventMap>(\n type: K,\n listener: (this: InputStepper, ev: InputStepperEventMap[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 InputStepperEventMap>(\n type: K,\n listener: (this: InputStepper, ev: InputStepperEventMap[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":"input-stepper.js","names":[],"sources":["../../../src/html/elements/input-stepper/input-stepper.ts"],"sourcesContent":["import { property } from 'lit/decorators.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { LocalizeController } from '../../shared/localize.js';\nimport { cls, tagName } from '../../registry.js';\n\nexport type InputStepperSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n\n/** Fired when the stepper value changes. Bubbles; not composed. */\nexport class StepperChangeEvent extends Event {\n readonly value: number;\n constructor(value: number) {\n super('change', { bubbles: true, composed: false, cancelable: false });\n this.value = value;\n }\n}\n\ninterface InputStepperEventMap {\n change: StepperChangeEvent;\n}\n\n/**\n * A stepper control that enhances a native `<input type=\"number\">` with\n * decrement/increment buttons and an optional animated number track.\n *\n * @link https://www.nngroup.com/articles/input-steppers/\n *\n * @example\n * ```html\n * <l-input-stepper>\n * <input type=\"number\" min=\"0\" max=\"10\" value=\"5\" />\n * </l-input-stepper>\n * ```\n *\n * @event change - Fired when the value changes. Bubbles. Properties: `value: number`.\n *\n * @cssproperty --border-color - Border color of the stepper container (default appearance) and of each button (rounded appearance). Defaults to `--l-color-border`.\n * @cssproperty --border-radius - Border radius of the stepper container (default appearance). Defaults to `--radius-md`.\n *\n * @customElement l-input-stepper\n */\n// oxlint-disable-next-line typescript/no-unsafe-declaration-merging -- typed addEventListener overloads merged below; no uninitialized properties.\nexport class InputStepper extends LuxenElement {\n private _localize = new LocalizeController(this);\n\n override createRenderRoot() {\n return this;\n }\n\n /** Minimum allowed value. Falls back to the input's `min` attribute. */\n @property({ type: Number })\n min?: number;\n\n /** Maximum allowed value. Falls back to the input's `max` attribute. */\n @property({ type: Number })\n max?: number;\n\n /** Step increment. Falls back to the input's `step` attribute. */\n @property({ type: Number })\n step?: number;\n\n /** Control size. */\n @property({ reflect: true })\n size: InputStepperSize = 'md';\n\n /** Enable the animated number roller overlay. */\n @property({ type: Boolean, reflect: true, attribute: 'with-roller' })\n withRoller = false;\n\n /** Icon name for the decrement button. */\n @property({ attribute: 'decrement-icon' })\n decrementIcon = 'lucide:minus';\n\n /** Icon name for the increment button. */\n @property({ attribute: 'increment-icon' })\n incrementIcon = 'lucide:plus';\n\n private _initialized = false;\n private _setupTimer = 0;\n private _input: HTMLInputElement | null = null;\n private _decrementBtn: HTMLButtonElement | null = null;\n private _incrementBtn: HTMLButtonElement | null = null;\n private _valueWrapper: HTMLDivElement | null = null;\n private _trackDisplay: HTMLDivElement | null = null;\n private _track: HTMLDivElement | null = null;\n private _observer: MutationObserver | null = null;\n private _skipTransition = false;\n\n override connectedCallback() {\n super.connectedCallback();\n // Children may not be parsed yet when the element upgrades mid-parse: try\n // synchronously, then retry once on a macrotask. setTimeout, not rAF — rAF\n // is suspended in hidden documents, so the element would stay inert there.\n if (!this._trySetup()) {\n this._setupTimer = window.setTimeout(() => this._trySetup(), 0);\n }\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n clearTimeout(this._setupTimer);\n this._teardown();\n }\n\n /** @returns true when setup ran or was already done; false to schedule a retry. */\n private _trySetup(): boolean {\n if (this._initialized || !this.isConnected) return true;\n if (!this.querySelector('input[type=\"number\"]')) return false;\n this._setup();\n return true;\n }\n\n override updated(changed: Map<string, unknown>) {\n if (!this._input) return;\n\n if (changed.has('min') || changed.has('max') || changed.has('step')) {\n this._syncConstraints();\n this._updateButtonStates();\n }\n\n // Button labels are set imperatively in _setup() (outside any render), so\n // re-apply them on every update to track a language change (the localize\n // controller calls requestUpdate when <html lang> changes).\n this._decrementBtn?.setAttribute('aria-label', this._localize.term('decreaseValue'));\n this._incrementBtn?.setAttribute('aria-label', this._localize.term('increaseValue'));\n }\n\n /** Decrease the value by one step. */\n decrement() {\n if (!this._input) return;\n const { min, step } = this._getConstraints();\n const newVal = Math.max(this._input.valueAsNumber - step, min);\n this._applyValue(newVal);\n }\n\n /** Increase the value by one step. */\n increment() {\n if (!this._input) return;\n const { max, step } = this._getConstraints();\n const newVal = Math.min(this._input.valueAsNumber + step, max);\n this._applyValue(newVal);\n }\n\n // --- Setup / Teardown ---\n\n private _setup() {\n this._input = this.querySelector('input[type=\"number\"]');\n if (!this._input) return;\n this._initialized = true;\n\n this._input.inputMode = 'numeric';\n\n // Wrap input in value container\n this._valueWrapper = document.createElement('div');\n this._valueWrapper.className = cls('input-stepper-value');\n this._input.replaceWith(this._valueWrapper);\n this._valueWrapper.appendChild(this._input);\n\n // Inject track overlay\n if (this.withRoller) {\n this._buildTrack();\n }\n\n // Create buttons\n this._decrementBtn = this._createButton(\n this.decrementIcon,\n this._localize.term('decreaseValue'),\n );\n this._incrementBtn = this._createButton(\n this.incrementIcon,\n this._localize.term('increaseValue'),\n );\n\n this._valueWrapper.before(this._decrementBtn);\n this._valueWrapper.after(this._incrementBtn);\n\n // Events\n this._decrementBtn.addEventListener('click', this._onDecrement);\n this._incrementBtn.addEventListener('click', this._onIncrement);\n this._input.addEventListener('change', this._onInputChange);\n\n // Observe disabled attribute on input\n this._observer = new MutationObserver(() => this._updateButtonStates());\n this._observer.observe(this._input, { attributes: true, attributeFilter: ['disabled'] });\n\n this._syncConstraints();\n this._updateButtonStates();\n this._updateTrack();\n }\n\n private _teardown() {\n this._decrementBtn?.removeEventListener('click', this._onDecrement);\n this._incrementBtn?.removeEventListener('click', this._onIncrement);\n this._input?.removeEventListener('change', this._onInputChange);\n this._observer?.disconnect();\n\n // Restore input to direct child\n if (this._input && this._valueWrapper) {\n this._valueWrapper.replaceWith(this._input);\n }\n this._decrementBtn?.remove();\n this._incrementBtn?.remove();\n\n this._input = null;\n this._decrementBtn = null;\n this._incrementBtn = null;\n this._valueWrapper = null;\n this._trackDisplay = null;\n this._track = null;\n this._observer = null;\n this._initialized = false;\n }\n\n // --- DOM helpers ---\n\n private _createButton(icon: string, label: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.setAttribute('aria-label', label);\n const iconEl = document.createElement(tagName('icon'));\n iconEl.setAttribute('name', icon);\n btn.append(iconEl);\n return btn;\n }\n\n private _buildTrack() {\n if (!this._valueWrapper) return;\n\n this._trackDisplay = document.createElement('div');\n this._trackDisplay.className = cls('input-stepper-track-display');\n\n this._track = document.createElement('div');\n this._track.className = cls('input-stepper-track');\n this._track.setAttribute('aria-hidden', 'true');\n\n this._rebuildTrackNumbers();\n this._trackDisplay.appendChild(this._track);\n this._valueWrapper.appendChild(this._trackDisplay);\n }\n\n private _rebuildTrackNumbers() {\n if (!this._track) return;\n\n const { min, max, step } = this._getConstraints();\n const effectiveMax = Number.isFinite(max) ? max : (this._input?.valueAsNumber ?? 0) + 100;\n\n this._track.innerHTML = '';\n for (let i = min; i <= effectiveMax; i += step) {\n const div = document.createElement('div');\n div.textContent = String(i);\n this._track.appendChild(div);\n }\n }\n\n // --- Constraints ---\n\n private _getConstraints() {\n const min = this.min ?? (Number(this._input?.getAttribute('min')) || 0);\n const max =\n this.max ??\n (this._input?.hasAttribute('max') ? Number(this._input.getAttribute('max')) : Infinity);\n const step = this.step ?? (Number(this._input?.getAttribute('step')) || 1);\n return { min, max, step };\n }\n\n private _syncConstraints() {\n if (!this._input) return;\n const { min, max, step } = this._getConstraints();\n\n this._input.min = String(min);\n if (Number.isFinite(max)) this._input.max = String(max);\n this._input.step = String(step);\n\n this._rebuildTrackNumbers();\n this._updateTrack();\n }\n\n // --- Value ---\n\n private _applyValue(val: number) {\n if (!this._input) return;\n this._input.value = String(val);\n this._updateButtonStates();\n this._updateTrack();\n this.dispatchEvent(new StepperChangeEvent(val));\n }\n\n // --- Track ---\n\n private _updateTrack() {\n if (!this._track) return;\n\n const { min, step } = this._getConstraints();\n const value = this._input?.valueAsNumber ?? min;\n const offset = (min - value) / step;\n\n this._track.style.translate = `0 calc(${offset} * var(--_button-size))`;\n\n if (this._skipTransition) {\n this._track.style.transition = 'none';\n void this._track.offsetHeight;\n this._track.style.transition = '';\n this._skipTransition = false;\n }\n }\n\n // --- Button states ---\n\n private _updateButtonStates() {\n if (!this._input || !this._decrementBtn || !this._incrementBtn) return;\n\n const { min, max } = this._getConstraints();\n const value = this._input.valueAsNumber;\n const disabled = this._input.disabled;\n\n this._decrementBtn.disabled = disabled || value <= min;\n this._incrementBtn.disabled = disabled || value >= max;\n }\n\n // --- Event handlers ---\n\n private _onDecrement = () => this.decrement();\n private _onIncrement = () => this.increment();\n\n private _onInputChange = (e: Event) => {\n if (!this._input) return;\n // Swallow the inner <input>'s native `change` so the host emits a single,\n // typed `change` (StepperChangeEvent) rather than two events — the native\n // one bubbles through the light-DOM host alongside our own.\n e.stopPropagation();\n\n const { min, max } = this._getConstraints();\n const clamped = Math.min(Math.max(this._input.valueAsNumber, min), max);\n this._input.value = String(clamped);\n\n this._skipTransition = true;\n this._updateButtonStates();\n this._updateTrack();\n this.dispatchEvent(new StepperChangeEvent(clamped));\n };\n}\n\n// Types `addEventListener('change', …)` as `StepperChangeEvent` on this element\n// (the global event map can't be augmented for the colliding name `change`).\n// See the Tabs interface in tabs.ts for the full rationale and the gotchas.\nexport interface InputStepper {\n addEventListener<K extends keyof InputStepperEventMap>(\n type: K,\n listener: (this: InputStepper, ev: InputStepperEventMap[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 InputStepperEventMap>(\n type: K,\n listener: (this: InputStepper, ev: InputStepperEventMap[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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"}
@@ -49,6 +49,7 @@ type ToolbarCommandName = 'heading-1' | 'heading-2' | 'heading-3' | 'bold' | 'it
49
49
  * @csspart editor - The container around the editable content.
50
50
  *
51
51
  * @cssproperty --border-color - Color of the editor frame border.
52
+ * @cssproperty --border-color-focus - Color of the editor frame border while the editable area has focus.
52
53
  * @cssproperty --border-width - Width of the editor frame border.
53
54
  * @cssproperty --border-radius - Corner radius of the editor frame.
54
55
  * @cssproperty --background - Background color of the editor.
@@ -1 +1 @@
1
- {"version":3,"file":"prose-editor.d.ts","sourceRoot":"","sources":["../../../src/html/elements/prose-editor/prose-editor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prose-editor.d.ts","sourceRoot":"","sources":["../../../src/html/elements/prose-editor/prose-editor.ts"],"names":[],"mappings":"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"}