luxen-ui 0.2.1 → 0.4.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 (70) hide show
  1. package/cdn/chunks/decorate.js +1 -1
  2. package/cdn/chunks/decorate.js.map +1 -1
  3. package/cdn/custom-elements.json +185 -112
  4. package/cdn/elements/avatar/avatar.js +1 -1
  5. package/cdn/elements/avatar/avatar.js.map +1 -1
  6. package/cdn/elements/badge/badge.js +1 -1
  7. package/cdn/elements/carousel/carousel.d.ts +9 -1
  8. package/cdn/elements/carousel/carousel.d.ts.map +1 -1
  9. package/cdn/elements/carousel/carousel.js +21 -20
  10. package/cdn/elements/carousel/carousel.js.map +1 -1
  11. package/cdn/elements/dialog/dialog.js +1 -1
  12. package/cdn/elements/dialog/dialog.styles.js +1 -1
  13. package/cdn/elements/dialog/dialog.styles.js.map +1 -1
  14. package/cdn/elements/divider/divider.js +1 -1
  15. package/cdn/elements/drawer/drawer.js +1 -1
  16. package/cdn/elements/dropdown/dropdown.d.ts +5 -2
  17. package/cdn/elements/dropdown/dropdown.d.ts.map +1 -1
  18. package/cdn/elements/dropdown/dropdown.js +6 -3
  19. package/cdn/elements/dropdown/dropdown.js.map +1 -1
  20. package/cdn/elements/dropdown-item/dropdown-item.js +1 -1
  21. package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
  22. package/cdn/elements/icon/icon.js +1 -1
  23. package/cdn/elements/input-otp/input-otp.d.ts +8 -2
  24. package/cdn/elements/input-otp/input-otp.d.ts.map +1 -1
  25. package/cdn/elements/input-otp/input-otp.js +1 -1
  26. package/cdn/elements/input-otp/input-otp.js.map +1 -1
  27. package/cdn/elements/input-stepper/input-stepper.js +1 -1
  28. package/cdn/elements/popover/popover.js +1 -1
  29. package/cdn/elements/popover/popover.js.map +1 -1
  30. package/cdn/elements/rating/rating.js +1 -1
  31. package/cdn/elements/tabs/tabs.js +1 -1
  32. package/cdn/elements/toast/toast.js +1 -1
  33. package/cdn/elements/toast/toast.js.map +1 -1
  34. package/cdn/elements/tooltip/tooltip.d.ts +3 -3
  35. package/cdn/elements/tooltip/tooltip.js +1 -1
  36. package/cdn/elements/tooltip/tooltip.js.map +1 -1
  37. package/cdn/elements/tree/tree.js +1 -1
  38. package/cdn/elements/tree/tree.js.map +1 -1
  39. package/cdn/elements/tree-item/tree-item.js +1 -1
  40. package/cdn/elements/tree-item/tree-item.js.map +1 -1
  41. package/cdn/shared/luxen-form-associated-element.js +1 -1
  42. package/cdn/styles/elements/divider.css +7 -0
  43. package/cdn/styles/elements/input-otp.css +63 -29
  44. package/cdn/styles/elements/select.css +3 -3
  45. package/cdn/styles/index.css +10 -0
  46. package/dist/css/elements/divider.css +7 -0
  47. package/dist/css/elements/input-otp.css +63 -29
  48. package/dist/css/elements/select.css +3 -3
  49. package/dist/css/index.css +10 -0
  50. package/dist/custom-elements.json +185 -112
  51. package/dist/elements/avatar/avatar.css +13 -7
  52. package/dist/elements/carousel/carousel.css +7 -0
  53. package/dist/elements/carousel/carousel.d.ts +9 -1
  54. package/dist/elements/carousel/carousel.d.ts.map +1 -1
  55. package/dist/elements/carousel/carousel.js +71 -37
  56. package/dist/elements/dialog/dialog.css +10 -0
  57. package/dist/elements/dropdown/dropdown.css +14 -3
  58. package/dist/elements/dropdown/dropdown.d.ts +5 -2
  59. package/dist/elements/dropdown/dropdown.d.ts.map +1 -1
  60. package/dist/elements/dropdown/dropdown.js +19 -7
  61. package/dist/elements/input-otp/input-otp.d.ts +8 -2
  62. package/dist/elements/input-otp/input-otp.d.ts.map +1 -1
  63. package/dist/elements/input-otp/input-otp.js +14 -5
  64. package/dist/elements/tooltip/tooltip.css +15 -7
  65. package/dist/elements/tooltip/tooltip.d.ts +3 -3
  66. package/dist/elements/tooltip/tooltip.js +3 -3
  67. package/dist/skills/luxen-ui/references/dialog.md +76 -0
  68. package/dist/skills/luxen-ui/references/drawer.md +8 -0
  69. package/dist/skills/luxen-ui/references/select.md +1 -1
  70. package/package.json +1 -1
@@ -93,7 +93,9 @@ img {
93
93
  border: none;
94
94
  border-radius: calc(var(--_size) * 0.2);
95
95
  background-color: var(--color, var(--l-color-bg-fill-neutral-soft));
96
- color: oklch(from var(--color) 0.45 calc(c * 2) h);
96
+ color: oklch(
97
+ from var(--color) calc(0.65 - 0.2 * sign(l - 0.5)) calc(c * (1.75 + 0.25 * sign(l - 0.5))) h
98
+ );
97
99
  font-size: var(--_font-size);
98
100
  font-weight: 500;
99
101
  line-height: 1;
@@ -102,6 +104,16 @@ img {
102
104
  cursor: inherit;
103
105
  }
104
106
 
107
+ @supports (color: contrast-color(red vs black, white)) {
108
+ .base {
109
+ color: contrast-color(
110
+ var(--color, var(--l-color-bg-fill-neutral-soft)) vs
111
+ oklch(from var(--color) 0.45 calc(c * 2) h),
112
+ oklch(from var(--color) 0.85 calc(c * 1.5) h) to AA
113
+ );
114
+ }
115
+ }
116
+
105
117
  button.base {
106
118
  cursor: pointer;
107
119
  }
@@ -115,12 +127,6 @@ button.base:hover {
115
127
  box-shadow: inset 0 0 0 1.5px var(--l-color-border-interactive);
116
128
  }
117
129
 
118
- @media (prefers-color-scheme: dark) {
119
- .base {
120
- color: oklch(from var(--color) 0.85 calc(c * 1.5) h);
121
- }
122
- }
123
-
124
130
  @container style(--appearance: circle) {
125
131
  .base {
126
132
  border-radius: 50%;
@@ -16,6 +16,7 @@
16
16
  --dot-color: #9ca3af;
17
17
  --dot-color-active: #111827;
18
18
  --dot-margin: 0.5rem 0;
19
+ --dot-edge-scale: 0.5;
19
20
 
20
21
  --scrollbar-color: #d1d5db transparent;
21
22
 
@@ -180,6 +181,12 @@
180
181
  padding-block: 0.5rem;
181
182
  border: 0;
182
183
  cursor: pointer;
184
+ transition: transform 180ms ease;
185
+ transform-origin: center;
186
+ }
187
+
188
+ .dot[data-edge] {
189
+ transform: scale(var(--dot-edge-scale));
183
190
  }
184
191
 
185
192
  .dot--bar i {
@@ -33,6 +33,7 @@ import { LuxenElement } from '../../shared/luxen-element';
33
33
  * @cssproperty --dot-color - Color of inactive dots.
34
34
  * @cssproperty --dot-color-active - Color of active dot.
35
35
  * @cssproperty --dot-margin - Margin around dots container.
36
+ * @cssproperty --dot-edge-scale - Scale factor applied to edge dots that signal more dots exist beyond the visible window (default `0.5`).
36
37
  */
37
38
  export declare class LuxenCarousel extends LuxenElement {
38
39
  static styles: CSSResultGroup;
@@ -110,12 +111,18 @@ export declare class LuxenCarousel extends LuxenElement {
110
111
  accessor withScrollbar: boolean;
111
112
  accessor withFullscreen: boolean;
112
113
  accessor dotAppearance: 'circle' | 'bar';
114
+ /**
115
+ * Maximum number of dots rendered at once. When the snap count exceeds this,
116
+ * a sliding window keeps the active dot in view and shrinks the edge dot(s)
117
+ * on the side where dots are hidden. `0` (default) renders all dots.
118
+ */
119
+ accessor maxVisibleDots: number;
113
120
  accessor scrollButtonsPosition: 'inside' | 'outside';
121
+ private accessor _selectedSnap;
114
122
  scrollButtons: HTMLElement;
115
123
  previousBtn: HTMLButtonElement;
116
124
  nextBtn: HTMLButtonElement;
117
125
  container: HTMLSlotElement;
118
- dotNodes: HTMLButtonElement[];
119
126
  constructor();
120
127
  connectedCallback(): void;
121
128
  disconnectedCallback(): void;
@@ -142,6 +149,7 @@ export declare class LuxenCarousel extends LuxenElement {
142
149
  isActive(): boolean;
143
150
  renderFullscreenButton(): import("lit").TemplateResult<1>;
144
151
  renderNextPreviousButtons(): import("lit").TemplateResult<1>;
152
+ private renderDots;
145
153
  render(): import("lit").TemplateResult<1>;
146
154
  }
147
155
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"carousel.d.ts","sourceRoot":"","sources":["../../../src/html/elements/carousel/carousel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAG1E,KAAK,cAAc,GAAG,GAAG,GAAG,GAAG,CAAC;AAChC,KAAK,mBAAmB,GACpB,OAAO,GACP,QAAQ,GACR,KAAK,GACL,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AACpE,KAAK,wBAAwB,GAAG,MAAM,GAAG,MAAM,CAAC;AAChD,KAAK,uBAAuB,GAAG,WAAW,GAAG,WAAW,GAAG,KAAK,CAAC;AAEjE,OAAO,EAAmB,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGhF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAM1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAgB,MAAM,EAAE,cAAc,CAAwB;IAE9D,KAAK,EAAG,iBAAiB,CAAC;IAE1B;;OAEG;IAEH,QAAQ,CAAC,QAAQ,SAAK;IAEtB;;;;OAIG;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC;IAE9B;;;;OAIG;IAEH,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAO;IAEpC;;;;OAIG;IAEH,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAW;IAE9C;;OAEG;IAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAM;IAE/B;;;;OAIG;IAEH,QAAQ,CAAC,IAAI,UAAS;IAEtB;;;;OAIG;IAEH,QAAQ,CAAC,QAAQ,UAAS;IAE1B;;;;OAIG;IAEH,QAAQ,CAAC,QAAQ,SAAM;IAEvB;;;;OAIG;IAEH,QAAQ,CAAC,SAAS,UAAS;IAE3B;;;;OAIG;IAEH,QAAQ,CAAC,cAAc,EAAE,wBAAwB,CAAK;IAEtD;;;;OAIG;IAEH,QAAQ,CAAC,UAAU,SAAK;IAExB;;;;OAIG;IAEH,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAe;IAG9D,QAAQ,CAAC,MAAM,UAAS;IAGxB,QAAQ,CAAC,QAAQ,UAAS;IAG1B,QAAQ,CAAC,aAAa,UAAS;IAG/B,QAAQ,CAAC,cAAc,UAAS;IAGhC,QAAQ,CAAC,aAAa,EAAE,QAAQ,GAAG,KAAK,CAAS;IAGjD,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,GAAG,SAAS,CAAY;IAEtC,aAAa,EAAG,WAAW,CAAC;IAC3B,WAAW,EAAG,iBAAiB,CAAC;IACpC,OAAO,EAAG,iBAAiB,CAAC;IAC9B,SAAS,EAAG,eAAe,CAAC;IAC/B,QAAQ,EAAG,iBAAiB,EAAE,CAAC;;IAcxC,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB;cAOV,YAAY,IAAI,IAAI;IAIvC,SAAS,CAAC,WAAW;IAWrB,SAAS,CAAC,cAAc;IAMxB,SAAS,CAAC,MAAM;IAIhB,SAAS,CAAC,QAAQ;IAKlB,SAAS,CAAC,QAAQ;IAOlB,SAAS,CAAC,QAAQ;IAIlB,SAAS,CAAC,oBAAoB;IAK9B,SAAS,CAAC,oBAAoB;IAK9B,SAAS,CAAC,gBAAgB;IAiBX,OAAO,CAAC,iBAAiB,EAAE,cAAc,CAAC,IAAI,CAAC;IAM9D,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,cAAc;IAKtB,OAAO,IAAI,gBAAgB;IA0B3B,IAAI;IAIJ,QAAQ;IAIR,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIvC,UAAU;IAIV,YAAY;IAIZ,QAAQ;IAIR,sBAAsB;IAoBtB,yBAAyB;IAyChB,MAAM;CAuChB"}
1
+ {"version":3,"file":"carousel.d.ts","sourceRoot":"","sources":["../../../src/html/elements/carousel/carousel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAG1E,KAAK,cAAc,GAAG,GAAG,GAAG,GAAG,CAAC;AAChC,KAAK,mBAAmB,GACpB,OAAO,GACP,QAAQ,GACR,KAAK,GACL,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AACpE,KAAK,wBAAwB,GAAG,MAAM,GAAG,MAAM,CAAC;AAChD,KAAK,uBAAuB,GAAG,WAAW,GAAG,WAAW,GAAG,KAAK,CAAC;AAEjE,OAAO,EAA4B,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGzF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAM1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAgB,MAAM,EAAE,cAAc,CAAwB;IAE9D,KAAK,EAAG,iBAAiB,CAAC;IAE1B;;OAEG;IAEH,QAAQ,CAAC,QAAQ,SAAK;IAEtB;;;;OAIG;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC;IAE9B;;;;OAIG;IAEH,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAO;IAEpC;;;;OAIG;IAEH,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAW;IAE9C;;OAEG;IAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAM;IAE/B;;;;OAIG;IAEH,QAAQ,CAAC,IAAI,UAAS;IAEtB;;;;OAIG;IAEH,QAAQ,CAAC,QAAQ,UAAS;IAE1B;;;;OAIG;IAEH,QAAQ,CAAC,QAAQ,SAAM;IAEvB;;;;OAIG;IAEH,QAAQ,CAAC,SAAS,UAAS;IAE3B;;;;OAIG;IAEH,QAAQ,CAAC,cAAc,EAAE,wBAAwB,CAAK;IAEtD;;;;OAIG;IAEH,QAAQ,CAAC,UAAU,SAAK;IAExB;;;;OAIG;IAEH,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAe;IAG9D,QAAQ,CAAC,MAAM,UAAS;IAGxB,QAAQ,CAAC,QAAQ,UAAS;IAG1B,QAAQ,CAAC,aAAa,UAAS;IAG/B,QAAQ,CAAC,cAAc,UAAS;IAGhC,QAAQ,CAAC,aAAa,EAAE,QAAQ,GAAG,KAAK,CAAS;IAEjD;;;;OAIG;IAEH,QAAQ,CAAC,cAAc,SAAK;IAG5B,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,GAAG,SAAS,CAAY;IAEvD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAK;IAElB,aAAa,EAAG,WAAW,CAAC;IAC3B,WAAW,EAAG,iBAAiB,CAAC;IACpC,OAAO,EAAG,iBAAiB,CAAC;IAC9B,SAAS,EAAG,eAAe,CAAC;;IAcxC,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB;cAOV,YAAY,IAAI,IAAI;IAIvC,SAAS,CAAC,WAAW;IAWrB,SAAS,CAAC,cAAc;IAMxB,SAAS,CAAC,MAAM;IAIhB,SAAS,CAAC,QAAQ;IAKlB,SAAS,CAAC,QAAQ;IAOlB,SAAS,CAAC,QAAQ;IAIlB,SAAS,CAAC,oBAAoB;IAK9B,SAAS,CAAC,oBAAoB;IAK9B,SAAS,CAAC,gBAAgB;IAUX,OAAO,CAAC,iBAAiB,EAAE,cAAc,CAAC,IAAI,CAAC;IAM9D,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,cAAc;IAKtB,OAAO,IAAI,gBAAgB;IA0B3B,IAAI;IAIJ,QAAQ;IAIR,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIvC,UAAU;IAIV,YAAY;IAIZ,QAAQ;IAIR,sBAAsB;IAoBtB,yBAAyB;IAyCzB,OAAO,CAAC,UAAU;IAgDT,MAAM;CAkBhB"}
@@ -15,11 +15,11 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
15
15
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
16
16
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
17
17
  };
18
- var _LuxenCarousel_autoplay_accessor_storage, _LuxenCarousel_autoplayOptions_accessor_storage, _LuxenCarousel_axis_accessor_storage, _LuxenCarousel_align_accessor_storage, _LuxenCarousel_breakpoints_accessor_storage, _LuxenCarousel_loop_accessor_storage, _LuxenCarousel_dragFree_accessor_storage, _LuxenCarousel_duration_accessor_storage, _LuxenCarousel_skipSnaps_accessor_storage, _LuxenCarousel_slidesToScroll_accessor_storage, _LuxenCarousel_startIndex_accessor_storage, _LuxenCarousel_containScroll_accessor_storage, _LuxenCarousel_single_accessor_storage, _LuxenCarousel_withDots_accessor_storage, _LuxenCarousel_withScrollbar_accessor_storage, _LuxenCarousel_withFullscreen_accessor_storage, _LuxenCarousel_dotAppearance_accessor_storage, _LuxenCarousel_scrollButtonsPosition_accessor_storage;
18
+ var _LuxenCarousel_autoplay_accessor_storage, _LuxenCarousel_autoplayOptions_accessor_storage, _LuxenCarousel_axis_accessor_storage, _LuxenCarousel_align_accessor_storage, _LuxenCarousel_breakpoints_accessor_storage, _LuxenCarousel_loop_accessor_storage, _LuxenCarousel_dragFree_accessor_storage, _LuxenCarousel_duration_accessor_storage, _LuxenCarousel_skipSnaps_accessor_storage, _LuxenCarousel_slidesToScroll_accessor_storage, _LuxenCarousel_startIndex_accessor_storage, _LuxenCarousel_containScroll_accessor_storage, _LuxenCarousel_single_accessor_storage, _LuxenCarousel_withDots_accessor_storage, _LuxenCarousel_withScrollbar_accessor_storage, _LuxenCarousel_withFullscreen_accessor_storage, _LuxenCarousel_dotAppearance_accessor_storage, _LuxenCarousel_maxVisibleDots_accessor_storage, _LuxenCarousel_scrollButtonsPosition_accessor_storage, _LuxenCarousel__selectedSnap_accessor_storage;
19
19
  import EmblaCarousel from 'embla-carousel';
20
20
  import Autoplay from 'embla-carousel-autoplay';
21
- import { html, unsafeCSS } from 'lit';
22
- import { property, query, queryAll } from 'lit/decorators.js';
21
+ import { html, nothing, unsafeCSS } from 'lit';
22
+ import { property, query, state } from 'lit/decorators.js';
23
23
  import { map } from 'lit/directives/map.js';
24
24
  import { LuxenElement } from '../../shared/luxen-element';
25
25
  import hostStyles from '../../shared/styles/host.styles';
@@ -53,6 +53,7 @@ const styles = unsafeCSS(rawStyles);
53
53
  * @cssproperty --dot-color - Color of inactive dots.
54
54
  * @cssproperty --dot-color-active - Color of active dot.
55
55
  * @cssproperty --dot-margin - Margin around dots container.
56
+ * @cssproperty --dot-edge-scale - Scale factor applied to edge dots that signal more dots exist beyond the visible window (default `0.5`).
56
57
  */
57
58
  export class LuxenCarousel extends LuxenElement {
58
59
  /**
@@ -145,8 +146,17 @@ export class LuxenCarousel extends LuxenElement {
145
146
  set withFullscreen(value) { __classPrivateFieldSet(this, _LuxenCarousel_withFullscreen_accessor_storage, value, "f"); }
146
147
  get dotAppearance() { return __classPrivateFieldGet(this, _LuxenCarousel_dotAppearance_accessor_storage, "f"); }
147
148
  set dotAppearance(value) { __classPrivateFieldSet(this, _LuxenCarousel_dotAppearance_accessor_storage, value, "f"); }
149
+ /**
150
+ * Maximum number of dots rendered at once. When the snap count exceeds this,
151
+ * a sliding window keeps the active dot in view and shrinks the edge dot(s)
152
+ * on the side where dots are hidden. `0` (default) renders all dots.
153
+ */
154
+ get maxVisibleDots() { return __classPrivateFieldGet(this, _LuxenCarousel_maxVisibleDots_accessor_storage, "f"); }
155
+ set maxVisibleDots(value) { __classPrivateFieldSet(this, _LuxenCarousel_maxVisibleDots_accessor_storage, value, "f"); }
148
156
  get scrollButtonsPosition() { return __classPrivateFieldGet(this, _LuxenCarousel_scrollButtonsPosition_accessor_storage, "f"); }
149
157
  set scrollButtonsPosition(value) { __classPrivateFieldSet(this, _LuxenCarousel_scrollButtonsPosition_accessor_storage, value, "f"); }
158
+ get _selectedSnap() { return __classPrivateFieldGet(this, _LuxenCarousel__selectedSnap_accessor_storage, "f"); }
159
+ set _selectedSnap(value) { __classPrivateFieldSet(this, _LuxenCarousel__selectedSnap_accessor_storage, value, "f"); }
150
160
  constructor() {
151
161
  super();
152
162
  _LuxenCarousel_autoplay_accessor_storage.set(this, 0);
@@ -166,7 +176,9 @@ export class LuxenCarousel extends LuxenElement {
166
176
  _LuxenCarousel_withScrollbar_accessor_storage.set(this, false);
167
177
  _LuxenCarousel_withFullscreen_accessor_storage.set(this, false);
168
178
  _LuxenCarousel_dotAppearance_accessor_storage.set(this, 'bar');
179
+ _LuxenCarousel_maxVisibleDots_accessor_storage.set(this, 0);
169
180
  _LuxenCarousel_scrollButtonsPosition_accessor_storage.set(this, 'inside');
181
+ _LuxenCarousel__selectedSnap_accessor_storage.set(this, 0);
170
182
  this.next = this.next.bind(this);
171
183
  this.previous = this.previous.bind(this);
172
184
  this.onInit = this.onInit.bind(this);
@@ -233,14 +245,7 @@ export class LuxenCarousel extends LuxenElement {
233
245
  this.previousBtn.toggleAttribute('disabled', !this.embla.canScrollPrev());
234
246
  this.nextBtn.toggleAttribute('disabled', !this.embla.canScrollNext());
235
247
  this.scrollButtons.classList.toggle('scroll-buttons--disabled', !canScroll);
236
- if (this.withDots) {
237
- const previous = this.embla.previousScrollSnap();
238
- const selected = this.embla.selectedScrollSnap();
239
- this.dotNodes[previous]?.classList.remove('dot--selected');
240
- this.dotNodes[previous]?.removeAttribute('aria-selected');
241
- this.dotNodes[selected]?.classList.add('dot--selected');
242
- this.dotNodes[selected]?.setAttribute('aria-selected', 'true');
243
- }
248
+ this._selectedSnap = this.embla.selectedScrollSnap();
244
249
  }
245
250
  async updated(changedProperties) {
246
251
  if (changedProperties.has('autoplay') && this.autoplay) {
@@ -360,6 +365,53 @@ export class LuxenCarousel extends LuxenElement {
360
365
  </button>
361
366
  </div>`;
362
367
  }
368
+ renderDots() {
369
+ if (!this.embla)
370
+ return nothing;
371
+ const snaps = this.embla.scrollSnapList();
372
+ const total = snaps.length;
373
+ if (total === 0)
374
+ return nothing;
375
+ const selected = this._selectedSnap;
376
+ const max = this.maxVisibleDots;
377
+ let start = 0;
378
+ let end = total;
379
+ if (max > 0 && max < total) {
380
+ const half = Math.floor((max - 1) / 2);
381
+ start = Math.max(0, selected - half);
382
+ end = Math.min(total, start + max);
383
+ if (end - start < max)
384
+ start = Math.max(0, end - max);
385
+ }
386
+ const edgeStart = start > 0;
387
+ const edgeEnd = end < total;
388
+ return html `<div
389
+ class="dots"
390
+ part="dots"
391
+ role="tablist"
392
+ >
393
+ ${map(snaps.slice(start, end), (_, i) => {
394
+ const index = start + i;
395
+ const isFirst = i === 0;
396
+ const isLast = i === end - start - 1;
397
+ const isSelected = index === selected;
398
+ const isEdge = !isSelected && ((isFirst && edgeStart) || (isLast && edgeEnd));
399
+ return html `<button
400
+ part="button-dot"
401
+ type="button"
402
+ role="tab"
403
+ class="dot dot--${this.dotAppearance} ${isSelected ? 'dot--selected' : ''}"
404
+ aria-label="Go to slide ${index + 1}"
405
+ aria-selected=${isSelected ? 'true' : nothing}
406
+ data-index="${index}"
407
+ data-edge=${isEdge ? '' : nothing}
408
+ @click=${this.handleDotClick}
409
+ >
410
+ <i></i>
411
+ </button>`;
412
+ })}
413
+ </div>`;
414
+ }
363
415
  render() {
364
416
  return html `
365
417
  <div class="wrapper ${this.isActive() ? '' : 'inactive'}">
@@ -374,33 +426,12 @@ export class LuxenCarousel extends LuxenElement {
374
426
  ></slot>
375
427
  </div>
376
428
  ${this.withFullscreen ? this.renderFullscreenButton() : ''}
377
- ${this.renderNextPreviousButtons()}
378
- ${this.withDots
379
- ? html `<div
380
- class="dots"
381
- part="dots"
382
- role="tablist"
383
- >
384
- ${map(this.embla.scrollSnapList(), (_, index) => {
385
- return html `<button
386
- part="button-dot"
387
- type="button"
388
- role="tab"
389
- class="dot dot--${this.dotAppearance}"
390
- aria-label="Go to slide ${index + 1}"
391
- data-index="${index}"
392
- @click=${this.handleDotClick}
393
- >
394
- <i></i>
395
- </button>`;
396
- })}
397
- </div> `
398
- : ''}
429
+ ${this.renderNextPreviousButtons()} ${this.withDots ? this.renderDots() : ''}
399
430
  </div>
400
431
  `;
401
432
  }
402
433
  }
403
- _LuxenCarousel_autoplay_accessor_storage = new WeakMap(), _LuxenCarousel_autoplayOptions_accessor_storage = new WeakMap(), _LuxenCarousel_axis_accessor_storage = new WeakMap(), _LuxenCarousel_align_accessor_storage = new WeakMap(), _LuxenCarousel_breakpoints_accessor_storage = new WeakMap(), _LuxenCarousel_loop_accessor_storage = new WeakMap(), _LuxenCarousel_dragFree_accessor_storage = new WeakMap(), _LuxenCarousel_duration_accessor_storage = new WeakMap(), _LuxenCarousel_skipSnaps_accessor_storage = new WeakMap(), _LuxenCarousel_slidesToScroll_accessor_storage = new WeakMap(), _LuxenCarousel_startIndex_accessor_storage = new WeakMap(), _LuxenCarousel_containScroll_accessor_storage = new WeakMap(), _LuxenCarousel_single_accessor_storage = new WeakMap(), _LuxenCarousel_withDots_accessor_storage = new WeakMap(), _LuxenCarousel_withScrollbar_accessor_storage = new WeakMap(), _LuxenCarousel_withFullscreen_accessor_storage = new WeakMap(), _LuxenCarousel_dotAppearance_accessor_storage = new WeakMap(), _LuxenCarousel_scrollButtonsPosition_accessor_storage = new WeakMap();
434
+ _LuxenCarousel_autoplay_accessor_storage = new WeakMap(), _LuxenCarousel_autoplayOptions_accessor_storage = new WeakMap(), _LuxenCarousel_axis_accessor_storage = new WeakMap(), _LuxenCarousel_align_accessor_storage = new WeakMap(), _LuxenCarousel_breakpoints_accessor_storage = new WeakMap(), _LuxenCarousel_loop_accessor_storage = new WeakMap(), _LuxenCarousel_dragFree_accessor_storage = new WeakMap(), _LuxenCarousel_duration_accessor_storage = new WeakMap(), _LuxenCarousel_skipSnaps_accessor_storage = new WeakMap(), _LuxenCarousel_slidesToScroll_accessor_storage = new WeakMap(), _LuxenCarousel_startIndex_accessor_storage = new WeakMap(), _LuxenCarousel_containScroll_accessor_storage = new WeakMap(), _LuxenCarousel_single_accessor_storage = new WeakMap(), _LuxenCarousel_withDots_accessor_storage = new WeakMap(), _LuxenCarousel_withScrollbar_accessor_storage = new WeakMap(), _LuxenCarousel_withFullscreen_accessor_storage = new WeakMap(), _LuxenCarousel_dotAppearance_accessor_storage = new WeakMap(), _LuxenCarousel_maxVisibleDots_accessor_storage = new WeakMap(), _LuxenCarousel_scrollButtonsPosition_accessor_storage = new WeakMap(), _LuxenCarousel__selectedSnap_accessor_storage = new WeakMap();
404
435
  LuxenCarousel.styles = [hostStyles, styles];
405
436
  __decorate([
406
437
  property({ type: Number, reflect: true })
@@ -453,9 +484,15 @@ __decorate([
453
484
  __decorate([
454
485
  property({ type: String, attribute: 'dot-appearance' })
455
486
  ], LuxenCarousel.prototype, "dotAppearance", null);
487
+ __decorate([
488
+ property({ type: Number, attribute: 'max-visible-dots' })
489
+ ], LuxenCarousel.prototype, "maxVisibleDots", null);
456
490
  __decorate([
457
491
  property({ type: String, attribute: 'scroll-buttons-position' })
458
492
  ], LuxenCarousel.prototype, "scrollButtonsPosition", null);
493
+ __decorate([
494
+ state()
495
+ ], LuxenCarousel.prototype, "_selectedSnap", null);
459
496
  __decorate([
460
497
  query('.scroll-buttons')
461
498
  ], LuxenCarousel.prototype, "scrollButtons", void 0);
@@ -468,6 +505,3 @@ __decorate([
468
505
  __decorate([
469
506
  query('.container')
470
507
  ], LuxenCarousel.prototype, "container", void 0);
471
- __decorate([
472
- queryAll('.dot')
473
- ], LuxenCarousel.prototype, "dotNodes", void 0);
@@ -69,18 +69,28 @@ dialog {
69
69
  line-height: 1.4;
70
70
  }
71
71
 
72
+ /* Pin body and footer so the layout stays correct when [without-header]
73
+ removes the header and only two children auto-place into the grid. */
72
74
  [part='body'] {
75
+ grid-row: 2;
73
76
  padding-inline: var(--padding);
74
77
  overflow-y: auto;
75
78
  }
76
79
 
77
80
  [part='footer'] {
81
+ grid-row: 3;
78
82
  display: flex;
79
83
  place-content: end;
80
84
  gap: 0.5rem;
81
85
  padding: var(--padding);
82
86
  }
83
87
 
88
+ /* Without a header, the body has to provide its own block-start padding
89
+ (normally inherited visually from the header padding above it). */
90
+ :host([without-header]) [part='body'] {
91
+ padding-block-start: var(--padding);
92
+ }
93
+
84
94
  ::slotted(menu[slot='footer']) {
85
95
  display: contents;
86
96
  }
@@ -1,6 +1,8 @@
1
1
  :host {
2
+ --padding: 0.25rem;
3
+
2
4
  --background: var(--l-color-bg-surface, Canvas);
3
- --radius: 6px;
5
+ --border-radius: 6px;
4
6
  --shadow: 0 4px 6px -1px rgb(0 0 0 / 8%), 0 2px 4px -2px rgb(0 0 0 / 6%);
5
7
  --show-duration: 150;
6
8
  --hide-duration: 150;
@@ -19,13 +21,22 @@
19
21
  box-sizing: border-box;
20
22
  width: max-content;
21
23
  min-width: anchor-size(width);
22
- padding: 0.25rem;
24
+ padding: var(--padding);
23
25
  margin: 0;
24
26
  border: 1px solid var(--l-color-border-overlay, light-dark(#e5e7eb, #374151));
25
- border-radius: var(--radius);
27
+ border-radius: var(--border-radius);
26
28
  background: var(--background);
27
29
  color: var(--l-color-text-primary, CanvasText);
28
30
  box-shadow: var(--shadow);
29
31
  font-size: 0.875rem;
30
32
  line-height: 1.5;
31
33
  }
34
+
35
+ /* Lightweight separator alternative to <l-divider> — same dimensions, no extra import. */
36
+ ::slotted(hr) {
37
+ height: 1px;
38
+ margin-block: var(--padding);
39
+ margin-inline: calc(var(--padding) * -1);
40
+ border: 0;
41
+ background: var(--l-color-divider);
42
+ }
@@ -5,12 +5,15 @@ import type { Placement } from '@floating-ui/dom';
5
5
  * A dropdown menu anchored to a trigger element.
6
6
  *
7
7
  * @slot trigger - The element that triggers the dropdown.
8
- * @slot - Menu content (`l-dropdown-item` elements).
8
+ * @slot header - Optional content rendered above the menu items (e.g. a user profile row). Use an `<l-divider>` (or `<hr>`) after it to separate from items.
9
+ * @slot - Menu content (`l-dropdown-item` elements). Drop an `<l-divider>` (or `<hr>`) between items to render a section separator.
10
+ * @slot footer - Optional content rendered below the menu items (e.g. a version label or shortcut row). Use an `<l-divider>` (or `<hr>`) before it to separate from items.
9
11
  *
10
12
  * @csspart panel - The floating menu container.
11
13
  *
12
14
  * @cssproperty --background - Panel background color.
13
- * @cssproperty --radius - Panel border radius. Default `8px`.
15
+ * @cssproperty --border-radius - Panel border radius. Default `8px`.
16
+ * @cssproperty --padding - Panel inner padding. Default `0.25rem`. Slotted `<l-divider>` elements bleed by this amount on each side to span the panel edges.
14
17
  * @cssproperty --shadow - Panel box shadow.
15
18
  * @cssproperty --show-duration - Show animation duration in ms. Default `150`.
16
19
  * @cssproperty --hide-duration - Hide animation duration in ms. Default `150`.
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown/dropdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AASlD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAgB,MAAM,4BAAwB;IAE9C,OAAO,CAAC,SAAS,CAId;IAEH,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,iBAAiB,CAAK;IAE9B,oCAAoC;IAEpC,QAAQ,CAAC,IAAI,UAAS;IAEtB,wCAAwC;IAExC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAkB;IAE/C,2CAA2C;IAE3C,QAAQ,CAAC,QAAQ,SAAK;IAEtB,qCAAqC;IAErC,QAAQ,CAAC,QAAQ,UAAS;IAE1B,OAAO,KAAK,UAAU,GAGrB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,YAAY;IAOpB,IAAI;IAKJ,IAAI;IAKJ,MAAM;IAOG,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;YAQhC,iBAAiB;IAyB/B,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,eAAe,CAErB;IAEF,OAAO,CAAC,iBAAiB,CAYvB;IAEF,OAAO,CAAC,eAAe,CAiCrB;IAEF,OAAO,CAAC,YAAY,CAKlB;IAEF,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,WAAW;IAWnB,2DAA2D;IAC3D,OAAO,CAAC,SAAS,CAMf;IAEO,MAAM;CAqBhB"}
1
+ {"version":3,"file":"dropdown.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown/dropdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AASlD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAgB,MAAM,4BAAwB;IAE9C,OAAO,CAAC,SAAS,CAId;IAEH,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,iBAAiB,CAAK;IAE9B,oCAAoC;IAEpC,QAAQ,CAAC,IAAI,UAAS;IAEtB,wCAAwC;IAExC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAkB;IAE/C,2CAA2C;IAE3C,QAAQ,CAAC,QAAQ,SAAK;IAEtB,qCAAqC;IAErC,QAAQ,CAAC,QAAQ,UAAS;IAE1B,OAAO,KAAK,UAAU,GAGrB;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,YAAY;IAOpB,IAAI;IAKJ,IAAI;IAKJ,MAAM;IAOG,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;YAQhC,iBAAiB;IAyB/B,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,eAAe,CAQrB;IAEF,OAAO,CAAC,iBAAiB,CAYvB;IAEF,OAAO,CAAC,eAAe,CAiCrB;IAEF,OAAO,CAAC,YAAY,CAKlB;IAEF,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,WAAW;IAWnB,2DAA2D;IAC3D,OAAO,CAAC,SAAS,CAMf;IAEO,MAAM;CAwBhB"}
@@ -28,12 +28,15 @@ const styles = unsafeCSS(rawStyles);
28
28
  * A dropdown menu anchored to a trigger element.
29
29
  *
30
30
  * @slot trigger - The element that triggers the dropdown.
31
- * @slot - Menu content (`l-dropdown-item` elements).
31
+ * @slot header - Optional content rendered above the menu items (e.g. a user profile row). Use an `<l-divider>` (or `<hr>`) after it to separate from items.
32
+ * @slot - Menu content (`l-dropdown-item` elements). Drop an `<l-divider>` (or `<hr>`) between items to render a section separator.
33
+ * @slot footer - Optional content rendered below the menu items (e.g. a version label or shortcut row). Use an `<l-divider>` (or `<hr>`) before it to separate from items.
32
34
  *
33
35
  * @csspart panel - The floating menu container.
34
36
  *
35
37
  * @cssproperty --background - Panel background color.
36
- * @cssproperty --radius - Panel border radius. Default `8px`.
38
+ * @cssproperty --border-radius - Panel border radius. Default `8px`.
39
+ * @cssproperty --padding - Panel inner padding. Default `0.25rem`. Slotted `<l-divider>` elements bleed by this amount on each side to span the panel edges.
37
40
  * @cssproperty --shadow - Panel box shadow.
38
41
  * @cssproperty --show-duration - Show animation duration in ms. Default `150`.
39
42
  * @cssproperty --hide-duration - Hide animation duration in ms. Default `150`.
@@ -59,9 +62,15 @@ export class LuxenDropdown extends LuxenElement {
59
62
  _LuxenDropdown_distance_accessor_storage.set(this, 4);
60
63
  _LuxenDropdown_disabled_accessor_storage.set(this, false);
61
64
  // --- Event handlers ---
62
- this._onTriggerClick = () => {
63
- if (!this.disabled)
64
- this.toggle();
65
+ this._onTriggerClick = (e) => {
66
+ if (this.disabled)
67
+ return;
68
+ this.toggle();
69
+ // Space/Enter on a native button dispatches click with detail=0; focus the
70
+ // first item so the menu is keyboard-navigable immediately on open.
71
+ if (this.open && e.detail === 0) {
72
+ requestAnimationFrame(() => this._focusFirstItem());
73
+ }
65
74
  };
66
75
  this._onTriggerKeyDown = (e) => {
67
76
  if (this.disabled)
@@ -296,12 +305,15 @@ export class LuxenDropdown extends LuxenElement {
296
305
  <div
297
306
  popover="auto"
298
307
  part="panel"
299
- role="menu"
300
308
  @keydown=${this._onPanelKeyDown}
301
309
  @click=${this._onItemClick}
302
310
  @toggle=${this._onToggle}
303
311
  >
304
- <slot></slot>
312
+ <slot name="header"></slot>
313
+ <div role="menu">
314
+ <slot></slot>
315
+ </div>
316
+ <slot name="footer"></slot>
305
317
  </div>
306
318
  `;
307
319
  }
@@ -9,8 +9,13 @@ import { LuxenElement } from '../../shared/luxen-element';
9
9
  * @customElement l-input-otp
10
10
  *
11
11
  * @cssproperty --digits - Number of digit boxes (default: 6). Must match input's maxlength.
12
- * @cssproperty --size - Cell width and height (default: 2.75rem). Font size scales automatically.
13
- * @cssproperty --gap - Space between cells (default: 0.5rem).
12
+ * @cssproperty --cell-size - Cell width and height (default: 2.75rem). Font size scales automatically.
13
+ * @cssproperty --cell-gap - Space between cells (default: 0.5rem).
14
+ * @cssproperty --cell-bg-color - Cell background color.
15
+ * @cssproperty --cell-border-color - Cell border color.
16
+ * @cssproperty --cell-border-radius - Cell border-radius.
17
+ * @cssproperty --cell-focus-color - Border + ring color of the active (focused) cell.
18
+ * @cssproperty --cell-focus-ring - `box-shadow` of the active cell ring (defaults to a 1px solid ring; set to `none` to disable).
14
19
  */
15
20
  export declare class LuxenInputOtp extends LuxenElement {
16
21
  createRenderRoot(): this;
@@ -27,5 +32,6 @@ export declare class LuxenInputOtp extends LuxenElement {
27
32
  private _teardown;
28
33
  private _updateCells;
29
34
  private _clearCells;
35
+ private _scheduleUpdateCells;
30
36
  }
31
37
  //# sourceMappingURL=input-otp.d.ts.map
@@ -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,4BAA4B,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAc,SAAQ,YAAY;IACpC,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;IAEpB,iBAAiB;IAKjB,oBAAoB;IAO7B,OAAO,CAAC,MAAM;IA2Dd,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,YAAY,CAyBlB;IAEF,OAAO,CAAC,WAAW,CAIjB;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,4BAA4B,CAAC;AAE1D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,aAAc,SAAQ,YAAY;IACpC,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;IAEpB,iBAAiB;IAKjB,oBAAoB;IAO7B,OAAO,CAAC,MAAM;IA4Dd,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,YAAY,CAyBlB;IAEF,OAAO,CAAC,WAAW,CAIjB;IAEF,OAAO,CAAC,oBAAoB,CAE1B;CACH"}
@@ -16,8 +16,13 @@ import { LuxenElement } from '../../shared/luxen-element';
16
16
  * @customElement l-input-otp
17
17
  *
18
18
  * @cssproperty --digits - Number of digit boxes (default: 6). Must match input's maxlength.
19
- * @cssproperty --size - Cell width and height (default: 2.75rem). Font size scales automatically.
20
- * @cssproperty --gap - Space between cells (default: 0.5rem).
19
+ * @cssproperty --cell-size - Cell width and height (default: 2.75rem). Font size scales automatically.
20
+ * @cssproperty --cell-gap - Space between cells (default: 0.5rem).
21
+ * @cssproperty --cell-bg-color - Cell background color.
22
+ * @cssproperty --cell-border-color - Cell border color.
23
+ * @cssproperty --cell-border-radius - Cell border-radius.
24
+ * @cssproperty --cell-focus-color - Border + ring color of the active (focused) cell.
25
+ * @cssproperty --cell-focus-ring - `box-shadow` of the active cell ring (defaults to a 1px solid ring; set to `none` to disable).
21
26
  */
22
27
  export class LuxenInputOtp extends LuxenElement {
23
28
  constructor() {
@@ -55,6 +60,9 @@ export class LuxenInputOtp extends LuxenElement {
55
60
  cell.removeAttribute('data-active');
56
61
  }
57
62
  };
63
+ this._scheduleUpdateCells = () => {
64
+ requestAnimationFrame(this._updateCells);
65
+ };
58
66
  }
59
67
  createRenderRoot() {
60
68
  return this;
@@ -111,11 +119,12 @@ export class LuxenInputOtp extends LuxenElement {
111
119
  this._initialized = true;
112
120
  // Populate cells if input already has a value (e.g. disabled with prefilled value)
113
121
  this._updateCells();
114
- // Events
122
+ // Events — focus is deferred so it runs after the click that triggered it
123
+ // (otherwise selectionStart is stale and the active cell flickers).
115
124
  this._input.addEventListener('input', this._updateCells);
116
125
  this._input.addEventListener('click', this._updateCells);
117
126
  this._input.addEventListener('keyup', this._updateCells);
118
- this._input.addEventListener('focus', this._updateCells);
127
+ this._input.addEventListener('focus', this._scheduleUpdateCells);
119
128
  this._input.addEventListener('blur', this._clearCells);
120
129
  }
121
130
  _teardown() {
@@ -124,7 +133,7 @@ export class LuxenInputOtp extends LuxenElement {
124
133
  this._input.removeEventListener('input', this._updateCells);
125
134
  this._input.removeEventListener('click', this._updateCells);
126
135
  this._input.removeEventListener('keyup', this._updateCells);
127
- this._input.removeEventListener('focus', this._updateCells);
136
+ this._input.removeEventListener('focus', this._scheduleUpdateCells);
128
137
  this._input.removeEventListener('blur', this._clearCells);
129
138
  // Restore input to direct child
130
139
  this._container.replaceWith(this._input);
@@ -1,7 +1,6 @@
1
1
  :host {
2
- --background: var(--l-color-bg-fill-brand, light-dark(#1f2937, #f9fafb));
3
- --color: light-dark(#fff, #111827);
4
- --radius: 4px;
2
+ --background-color: var(--l-color-bg-fill-brand, light-dark(#1f2937, #f9fafb));
3
+ --border-radius: 4px;
5
4
  --max-width: 180px;
6
5
  --arrow-size: 6px;
7
6
  --show-duration: 150ms;
@@ -18,20 +17,29 @@
18
17
  max-width: var(--max-width);
19
18
  padding: 4px 8px;
20
19
  border: 0;
21
- border-radius: var(--radius);
22
- background: var(--background);
23
- color: var(--color);
20
+ border-radius: var(--border-radius);
21
+ background: var(--background-color);
22
+ color: var(
23
+ --text-color,
24
+ oklch(from var(--background-color) calc(0.59 - 0.41 * sign(l - 0.5)) 0 0)
25
+ );
24
26
  font-size: 0.8125rem;
25
27
  line-height: 1.4;
26
28
  filter: drop-shadow(0 1px 2px rgb(0 0 0 / 16%));
27
29
  pointer-events: none;
28
30
  }
29
31
 
32
+ @supports (color: contrast-color(red vs black, white)) {
33
+ [popover] {
34
+ color: var(--text-color, contrast-color(var(--background-color) vs #111827, #fff to AA));
35
+ }
36
+ }
37
+
30
38
  i {
31
39
  position: absolute;
32
40
  display: block;
33
41
  width: var(--arrow-size);
34
42
  height: var(--arrow-size);
35
- background: var(--background);
43
+ background: var(--background-color);
36
44
  transform: rotate(45deg);
37
45
  }
@@ -10,9 +10,9 @@ import type { Placement } from '@floating-ui/dom';
10
10
  * @csspart body - The tooltip popover container.
11
11
  * @csspart arrow - The directional arrow element.
12
12
  *
13
- * @cssproperty --background - Background color. Default: dark in light mode, light in dark mode.
14
- * @cssproperty --color - Text color.
15
- * @cssproperty --radius - Border radius. Default `4px`.
13
+ * @cssproperty --background-color - Background color. Default: dark in light mode, light in dark mode.
14
+ * @cssproperty --text-color - Text color. If unset, auto-derived from `--background-color` luminance.
15
+ * @cssproperty --border-radius - Border radius. Default `4px`.
16
16
  * @cssproperty --max-width - Maximum width. Default `180px`.
17
17
  * @cssproperty --arrow-size - Arrow size. Default `6px`.
18
18
  * @cssproperty --show-duration - Show animation duration. Default `150ms`.
@@ -33,9 +33,9 @@ const styles = unsafeCSS(rawStyles);
33
33
  * @csspart body - The tooltip popover container.
34
34
  * @csspart arrow - The directional arrow element.
35
35
  *
36
- * @cssproperty --background - Background color. Default: dark in light mode, light in dark mode.
37
- * @cssproperty --color - Text color.
38
- * @cssproperty --radius - Border radius. Default `4px`.
36
+ * @cssproperty --background-color - Background color. Default: dark in light mode, light in dark mode.
37
+ * @cssproperty --text-color - Text color. If unset, auto-derived from `--background-color` luminance.
38
+ * @cssproperty --border-radius - Border radius. Default `4px`.
39
39
  * @cssproperty --max-width - Maximum width. Default `180px`.
40
40
  * @cssproperty --arrow-size - Arrow size. Default `6px`.
41
41
  * @cssproperty --show-duration - Show animation duration. Default `150ms`.