@latty-ds/web 0.10.3 → 1.0.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 (101) hide show
  1. package/custom-elements.json +1970 -229
  2. package/dist/components/accordion/index.js +36 -2
  3. package/dist/components/alert/alert.d.ts +29 -6
  4. package/dist/components/alert/alert.types.d.ts +5 -0
  5. package/dist/components/alert/index.js +127 -20
  6. package/dist/components/avatar/avatar.d.ts +2 -0
  7. package/dist/components/avatar/index.js +10 -3
  8. package/dist/components/badge/index.js +1 -1
  9. package/dist/components/breadcrumb/breadcrumb.d.ts +4 -0
  10. package/dist/components/breadcrumb/breadcrumb.types.d.ts +5 -0
  11. package/dist/components/breadcrumb/index.js +20 -3
  12. package/dist/components/button/button.d.ts +33 -0
  13. package/dist/components/button/index.js +138 -15
  14. package/dist/components/calendar/calendar.d.ts +15 -7
  15. package/dist/components/calendar/calendar.types.d.ts +17 -1
  16. package/dist/components/calendar/index.js +80 -27
  17. package/dist/components/checkbox/checkbox.d.ts +14 -2
  18. package/dist/components/checkbox/checkbox.types.d.ts +5 -0
  19. package/dist/components/checkbox/index.js +82 -5
  20. package/dist/components/chip/chip.d.ts +4 -1
  21. package/dist/components/chip/chip.types.d.ts +5 -0
  22. package/dist/components/chip/index.js +29 -1
  23. package/dist/components/color-input/color-input.d.ts +11 -2
  24. package/dist/components/color-input/color-input.types.d.ts +5 -0
  25. package/dist/components/color-input/index.js +92 -8
  26. package/dist/components/combobox/combobox.d.ts +18 -2
  27. package/dist/components/combobox/combobox.types.d.ts +7 -0
  28. package/dist/components/combobox/index.js +164 -23
  29. package/dist/components/date-input/date-input.d.ts +22 -7
  30. package/dist/components/date-input/date-input.types.d.ts +7 -0
  31. package/dist/components/date-input/index.js +218 -55
  32. package/dist/components/datepicker/datepicker.d.ts +9 -1
  33. package/dist/components/datepicker/index.js +108 -15
  34. package/dist/components/dialog/dialog.d.ts +39 -19
  35. package/dist/components/dialog/dialog.types.d.ts +5 -0
  36. package/dist/components/dialog/index.js +240 -93
  37. package/dist/components/divider/index.js +2 -2
  38. package/dist/components/dropdown/dropdown-item.d.ts +1 -1
  39. package/dist/components/dropdown/dropdown.d.ts +4 -0
  40. package/dist/components/dropdown/index.js +43 -3
  41. package/dist/components/icon-button/icon-button.d.ts +4 -0
  42. package/dist/components/icon-button/index.js +39 -4
  43. package/dist/components/image/image.d.ts +7 -7
  44. package/dist/components/image/index.js +6 -11
  45. package/dist/components/list/index.js +14 -4
  46. package/dist/components/nav/index.js +37 -17
  47. package/dist/components/nav/nav-item.d.ts +8 -8
  48. package/dist/components/pagination/index.js +57 -16
  49. package/dist/components/pagination/pagination.d.ts +8 -1
  50. package/dist/components/pagination/pagination.types.d.ts +15 -0
  51. package/dist/components/progress/index.js +44 -6
  52. package/dist/components/progress/progress.d.ts +12 -0
  53. package/dist/components/radio/index.js +148 -9
  54. package/dist/components/radio/radio.d.ts +34 -2
  55. package/dist/components/radio/radio.types.d.ts +5 -0
  56. package/dist/components/radio-group/index.js +120 -29
  57. package/dist/components/radio-group/radio-group.d.ts +27 -2
  58. package/dist/components/select/index.js +197 -44
  59. package/dist/components/select/select.d.ts +38 -4
  60. package/dist/components/select/select.types.d.ts +5 -0
  61. package/dist/components/shared/aria-forwarding.d.ts +1 -1
  62. package/dist/components/shared/form-default.d.ts +30 -0
  63. package/dist/components/shared/form-disabled.d.ts +40 -0
  64. package/dist/components/shared/form-field.d.ts +7 -0
  65. package/dist/components/shared/form-validity.d.ts +17 -0
  66. package/dist/components/shared/roving.d.ts +11 -0
  67. package/dist/components/shared/scroll-lock.d.ts +2 -0
  68. package/dist/components/sidepanel/index.js +278 -75
  69. package/dist/components/sidepanel/sidepanel.d.ts +32 -10
  70. package/dist/components/sidepanel/sidepanel.types.d.ts +10 -1
  71. package/dist/components/skeleton/index.js +3 -8
  72. package/dist/components/slider/index.js +307 -75
  73. package/dist/components/slider/slider.d.ts +16 -3
  74. package/dist/components/snackbar/index.js +82 -9
  75. package/dist/components/snackbar/snackbar.d.ts +17 -1
  76. package/dist/components/snackbar/snackbar.types.d.ts +5 -0
  77. package/dist/components/surface/index.js +8 -0
  78. package/dist/components/switch/index.js +101 -5
  79. package/dist/components/switch/switch.d.ts +13 -2
  80. package/dist/components/switch/switch.types.d.ts +5 -0
  81. package/dist/components/tab/index.js +15 -14
  82. package/dist/components/tab/tab.d.ts +0 -1
  83. package/dist/components/tab-group/index.js +102 -30
  84. package/dist/components/tab-group/tab-group.d.ts +17 -2
  85. package/dist/components/table/index.js +105 -61
  86. package/dist/components/table/table.d.ts +8 -2
  87. package/dist/components/table/table.types.d.ts +4 -3
  88. package/dist/components/text/index.js +16 -1
  89. package/dist/components/text/text.d.ts +1 -1
  90. package/dist/components/textfield/index.js +243 -35
  91. package/dist/components/textfield/textfield.d.ts +24 -2
  92. package/dist/components/textfield/textfield.types.d.ts +11 -0
  93. package/dist/components/tooltip/index.js +176 -49
  94. package/dist/components/tooltip/tooltip.d.ts +17 -0
  95. package/dist/components/tooltip/tooltip.types.d.ts +5 -1
  96. package/dist/index.cjs +2467 -692
  97. package/dist/index.js +2478 -699
  98. package/dist/manifest.json +202 -32
  99. package/dist/utils/dev-warn.d.ts +5 -0
  100. package/dist/utils/index.d.ts +1 -0
  101. package/package.json +4 -8
@@ -40,27 +40,50 @@ import { customElement as customElement2, property as property3 } from "lit/deco
40
40
  // src/components/snackbar/snackbar.styles.ts
41
41
  import { css } from "lit";
42
42
  var snackbarStyles = css`
43
+ /* ── Popover ─────────────────────────────────────────────── The host itself is the popover element (no
44
+ wrapped child, unlike lt-dialog/lt-sidepanel's inner <dialog>) — the browser's own [popover] UA stylesheet
45
+ sets inset:0, margin:auto, width/height:fit-content, padding, border, and Canvas colors, all of which are
46
+ reset below in favor of this component's own bottom-center placement. display is deliberately NOT set on
47
+ the base rule — a closed (not :popover-open) popover must stay display:none (the native default) so it's
48
+ genuinely non-interactive and out of the accessibility tree; it's set to block only for the open state,
49
+ below. display/overlay need transition-behavior: allow-discrete so hidePopover() doesn't remove it from
50
+ rendering instantly, cutting the exit transition off partway through — same reasoning as lt-dialog. ─────── */
51
+
43
52
  :host {
44
- display: block;
53
+ background: transparent;
54
+ border: none;
45
55
  bottom: 24px;
46
56
  left: 50%;
57
+ margin: 0;
47
58
  max-width: calc(100vw - 48px);
48
59
  opacity: 0;
60
+ overflow: visible;
61
+ padding: 0;
49
62
  position: fixed;
63
+ right: auto;
64
+ top: auto;
50
65
  transform: translateX(-50%) translateY(8px);
51
66
  transition:
52
67
  opacity 200ms ease,
53
68
  transform 200ms ease,
54
- visibility 200ms ease;
55
- visibility: hidden;
69
+ display 200ms allow-discrete,
70
+ overlay 200ms allow-discrete;
56
71
  width: max-content;
57
- z-index: 9000;
58
72
  }
59
73
 
60
74
  :host([open]) {
75
+ display: block;
61
76
  opacity: 1;
62
77
  transform: translateX(-50%) translateY(0);
63
- visibility: visible;
78
+ }
79
+
80
+ /* A newly-shown popover has no earlier rendered frame to transition from — @starting-style supplies one so
81
+ the fade/slide-in actually animates instead of snapping straight to the open position. */
82
+ @starting-style {
83
+ :host([open]) {
84
+ opacity: 0;
85
+ transform: translateX(-50%) translateY(8px);
86
+ }
64
87
  }
65
88
 
66
89
  /* ── Layout ──────────────────────────────────────────────── */
@@ -115,13 +138,14 @@ var snackbarStyles = css`
115
138
  font-size: 0.8125rem;
116
139
  font-weight: 600;
117
140
  letter-spacing: 0.05em;
118
- opacity: 0.85;
119
141
  padding: 0 var(--lt-spacing-1);
120
142
  text-transform: uppercase;
121
143
  }
122
144
 
145
+ /* Text on a coloured fill is never dimmed with opacity (that blends it into the fill and lowers its contrast
146
+ below whatever the on-colour was picked for), so hover is shown with an underline instead. */
123
147
  .action:hover {
124
- opacity: 1;
148
+ text-decoration: underline;
125
149
  }
126
150
 
127
151
  /* ── Close button ────────────────────────────────────────── */
@@ -135,6 +159,8 @@ var snackbarStyles = css`
135
159
  display: flex;
136
160
  flex-shrink: 0;
137
161
  justify-content: center;
162
+ min-height: 24px; /* WCAG 2.5.8 */
163
+ min-width: 24px;
138
164
  opacity: 0.7;
139
165
  padding: 0;
140
166
  }
@@ -142,6 +168,14 @@ var snackbarStyles = css`
142
168
  .close:hover {
143
169
  opacity: 1;
144
170
  }
171
+
172
+ /* ── Reduced motion ─────────────────────────────────────────────────────── */
173
+
174
+ @media (prefers-reduced-motion: reduce) {
175
+ :host {
176
+ transition: none;
177
+ }
178
+ }
145
179
  `;
146
180
 
147
181
  // src/utils/dispatch.ts
@@ -208,6 +242,14 @@ var surfaceStyles = css2`
208
242
  :host([appearance='outlined']:hover) .surface {
209
243
  border-color: var(--lt-border-strong);
210
244
  }
245
+
246
+ /* ── Reduced motion ─────────────────────────────────────────────────────── */
247
+
248
+ @media (prefers-reduced-motion: reduce) {
249
+ .surface {
250
+ transition: none;
251
+ }
252
+ }
211
253
  `;
212
254
 
213
255
  // src/components/surface/surface.ts
@@ -242,6 +284,9 @@ Surface = __decorateClass([
242
284
  ], Surface);
243
285
 
244
286
  // src/components/snackbar/snackbar.ts
287
+ var DEFAULT_LABELS = {
288
+ dismiss: "Dismiss"
289
+ };
245
290
  var Snackbar = class extends ThemeableElement {
246
291
  constructor() {
247
292
  super(...arguments);
@@ -253,6 +298,17 @@ var Snackbar = class extends ThemeableElement {
253
298
  this.icon = "";
254
299
  this.elevation = "5";
255
300
  this.appearance = "filled";
301
+ this.labels = {};
302
+ // `updated()` also runs after the very first render, where `changed.has('open')` is true even though nothing
303
+ // was ever shown — `open` starts false, so the else branch ran, dispatching a `hide` for a snackbar that was
304
+ // never visible (SNK-1). Same fix and reasoning as lt-dropdown's `_skipNextOpenChange` (DDN-1). This guards
305
+ // only the timer/event side effects, not the popover sync below — a snackbar mounted already open (markup
306
+ // `open=""`) still needs `showPopover()` called once, or it stays in the native `display: none` state despite
307
+ // `open` being true.
308
+ this._skipNextOpenChange = true;
309
+ }
310
+ get _labels() {
311
+ return { ...DEFAULT_LABELS, ...this.labels };
256
312
  }
257
313
  /** Shows the snackbar and starts the auto-dismiss timer. */
258
314
  show() {
@@ -262,9 +318,16 @@ var Snackbar = class extends ThemeableElement {
262
318
  hide() {
263
319
  this.open = false;
264
320
  }
321
+ connectedCallback() {
322
+ super.connectedCallback();
323
+ this.setAttribute("popover", "manual");
324
+ }
265
325
  updated(changedProperties) {
266
326
  if (changedProperties.has("open")) {
267
- if (this.open) {
327
+ this._syncPopoverState();
328
+ if (this._skipNextOpenChange) {
329
+ this._skipNextOpenChange = false;
330
+ } else if (this.open) {
268
331
  this._startTimer();
269
332
  dispatch(this, "show");
270
333
  } else {
@@ -273,6 +336,13 @@ var Snackbar = class extends ThemeableElement {
273
336
  }
274
337
  }
275
338
  }
339
+ _syncPopoverState() {
340
+ if (this.open) {
341
+ if (!this.matches(":popover-open")) this.showPopover();
342
+ } else if (this.matches(":popover-open")) {
343
+ this.hidePopover();
344
+ }
345
+ }
276
346
  disconnectedCallback() {
277
347
  super.disconnectedCallback();
278
348
  this._clearTimer();
@@ -312,7 +382,7 @@ var Snackbar = class extends ThemeableElement {
312
382
  <slot></slot>
313
383
  </span>
314
384
  ${this.actionLabel ? html2`<button class="action" @click=${this._handleAction} part="action">${this.actionLabel}</button>` : ""}
315
- ${this.closable ? html2`<button class="close" @click=${this._handleClose} aria-label="Dismiss" part="close">
385
+ ${this.closable ? html2`<button class="close" @click=${this._handleClose} aria-label=${this._labels.dismiss} part="close">
316
386
  <lt-icon name="xmark"></lt-icon>
317
387
  </button>` : ""}
318
388
  </div>
@@ -358,6 +428,9 @@ __decorateClass([
358
428
  __decorateClass([
359
429
  property3({ reflect: true })
360
430
  ], Snackbar.prototype, "appearance", 2);
431
+ __decorateClass([
432
+ property3({ type: Object })
433
+ ], Snackbar.prototype, "labels", 2);
361
434
  Snackbar = __decorateClass([
362
435
  customElement2("lt-snackbar")
363
436
  ], Snackbar);
@@ -1,11 +1,21 @@
1
1
  import { ThemeableElement } from '../../base';
2
- import type { SnackbarVariant } from './snackbar.types';
2
+ import type { SnackbarVariant, SnackbarLabels } from './snackbar.types';
3
3
  import type { SurfaceElevation, SurfaceAppearance } from '../surface/surface.types';
4
4
  import '@latty-ds/icons';
5
5
  import '../surface/surface';
6
6
  /**
7
7
  * Brief notification that appears temporarily at the bottom of the viewport.
8
8
  *
9
+ * Renders as a native "manual" popover on its own host, so it always appears in the top layer — above an open
10
+ * `lt-dialog`/`lt-sidepanel`, regardless of z-index, since top-layer content always wins over non-top-layer
11
+ * content no matter how high (SNK-companion to §6.1's `<dialog>` migration). Confirmed live: fully visible and
12
+ * readable above an open modal. One narrower, unavoidable limitation (confirmed live too, not fixable from this
13
+ * component's side): a native `showModal()` makes everything **outside the dialog's own subtree** inert,
14
+ * including a sibling popover — explicitly setting `inert = false` on the snackbar does not override it. A
15
+ * snackbar shown while a dialog is open is visible but not clickable until the dialog closes; its own
16
+ * `duration` timer still fires normally regardless (a plain `setTimeout`, unaffected by inertness), so only a
17
+ * `duration="0"` snackbar with a close/action button would be briefly stuck non-interactive in that scenario.
18
+ *
9
19
  * @element lt-snackbar
10
20
  *
11
21
  * @slot - Notification content. Accepts any HTML — bold, links, etc.
@@ -56,12 +66,18 @@ export declare class Snackbar extends ThemeableElement {
56
66
  * @default 'filled'
57
67
  */
58
68
  appearance: SurfaceAppearance;
69
+ /** Overridable UI text (the close button's accessible name), for i18n. English defaults — override only the keys you need. */
70
+ labels: Partial<SnackbarLabels>;
71
+ private get _labels();
59
72
  private _timer?;
73
+ private _skipNextOpenChange;
60
74
  /** Shows the snackbar and starts the auto-dismiss timer. */
61
75
  show(): void;
62
76
  /** Dismisses the snackbar. */
63
77
  hide(): void;
78
+ connectedCallback(): void;
64
79
  updated(changedProperties: Map<string, unknown>): void;
80
+ private _syncPopoverState;
65
81
  disconnectedCallback(): void;
66
82
  private _startTimer;
67
83
  private _clearTimer;
@@ -1 +1,6 @@
1
1
  export type SnackbarVariant = 'default' | 'success' | 'warning' | 'error' | 'info';
2
+ /** Overridable UI text, for i18n — English defaults; override only the keys you need. */
3
+ export interface SnackbarLabels {
4
+ /** Accessible label for the dismiss button. */
5
+ dismiss: string;
6
+ }
@@ -88,6 +88,14 @@ var surfaceStyles = css`
88
88
  :host([appearance='outlined']:hover) .surface {
89
89
  border-color: var(--lt-border-strong);
90
90
  }
91
+
92
+ /* ── Reduced motion ─────────────────────────────────────────────────────── */
93
+
94
+ @media (prefers-reduced-motion: reduce) {
95
+ .surface {
96
+ transition: none;
97
+ }
98
+ }
91
99
  `;
92
100
 
93
101
  // src/utils/color.ts
@@ -56,7 +56,10 @@ var switchStyles = css`
56
56
  flex-direction: row-reverse;
57
57
  }
58
58
 
59
- :host([disabled]) .switch-wrapper {
59
+ /* :host(:disabled) — the browser matches this automatically for a form-associated element inside a
60
+ disabled <fieldset>, no JS required; :host([disabled]) covers the component's own explicit attribute. */
61
+ :host([disabled]) .switch-wrapper,
62
+ :host(:disabled) .switch-wrapper {
60
63
  cursor: not-allowed;
61
64
  opacity: 0.6;
62
65
  }
@@ -200,12 +203,91 @@ var switchStyles = css`
200
203
  :host([variant='info']) input[type='checkbox']:checked:hover:not(:disabled) {
201
204
  background: var(--lt-interactive-info-bg-hover);
202
205
  }
206
+
207
+ /* ── Reduced motion ─────────────────────────────────────────────────────── */
208
+
209
+ @media (prefers-reduced-motion: reduce) {
210
+ input[type='checkbox'],
211
+ .thumb {
212
+ transition: none;
213
+ }
214
+ }
215
+
216
+ /* ── Forced colors (Windows High Contrast) ─────────────────────────────────
217
+ Under forced-colors, every author background-color here (the unchecked track's neutral fill, every
218
+ variant's checked fill, and the thumb's plain white) is flattened to Canvas — the track has no border of
219
+ its own to fall back on, and the thumb disappears into it, leaving neither track nor thumb visible at all.
220
+ System color keywords are exempt from that flattening; Highlight/HighlightText is the canonical OS
221
+ "selected state" pairing, unaffected by which variant was selected (forced-colors overrides that anyway).
222
+ !important matches every variant's own :checked rule in specificity — standard for a forced-colors
223
+ override, whose entire point is overriding the page's own author styling. ─────────────────────────────── */
224
+
225
+ @media (forced-colors: active) {
226
+ input[type='checkbox'] {
227
+ border: 1px solid ButtonText;
228
+ }
229
+
230
+ input[type='checkbox']:checked {
231
+ background: Highlight !important;
232
+ border-color: Highlight !important;
233
+ }
234
+
235
+ .thumb {
236
+ background: ButtonText;
237
+ }
238
+
239
+ input[type='checkbox']:checked ~ .thumb {
240
+ background: HighlightText;
241
+ }
242
+ }
203
243
  `;
204
244
 
245
+ // src/components/shared/form-default.ts
246
+ var FormDefaultController = class {
247
+ constructor(host, get) {
248
+ this._captured = false;
249
+ this._get = get;
250
+ host.addController(this);
251
+ }
252
+ hostConnected() {
253
+ if (this._captured) return;
254
+ this._captured = true;
255
+ this.value = this._get();
256
+ }
257
+ };
258
+
259
+ // src/components/shared/form-disabled.ts
260
+ var FormDisabledController = class {
261
+ constructor(host) {
262
+ this._fieldsetDisabled = false;
263
+ this._host = host;
264
+ host.addController(this);
265
+ }
266
+ // No-op — this controller has no connect-time work of its own (formDisabledCallback drives it instead), but
267
+ // ReactiveController's members are all optional, and a class satisfying an all-optional interface with no
268
+ // members in common is a TS error (TS2559); this gives it one.
269
+ hostConnected() {
270
+ }
271
+ /** Whether an ancestor `<fieldset>` is currently disabled. */
272
+ get fieldsetDisabled() {
273
+ return this._fieldsetDisabled;
274
+ }
275
+ setFieldsetDisabled(disabled) {
276
+ if (this._fieldsetDisabled === disabled) return;
277
+ this._fieldsetDisabled = disabled;
278
+ this._host.requestUpdate();
279
+ }
280
+ };
281
+
205
282
  // src/components/switch/switch.ts
283
+ var DEFAULT_LABELS = {
284
+ validationMessage: "Please check this switch"
285
+ };
206
286
  var Switch = class extends ThemeableElement {
207
287
  constructor() {
208
288
  super();
289
+ this._default = new FormDefaultController(this, () => this.checked);
290
+ this._fieldsetDisabled = new FormDisabledController(this);
209
291
  this.variant = "primary";
210
292
  this.size = "md";
211
293
  this.checked = false;
@@ -215,8 +297,12 @@ var Switch = class extends ThemeableElement {
215
297
  this.labelPosition = "right";
216
298
  this.name = "";
217
299
  this.value = "on";
300
+ this.labels = {};
218
301
  this._internals = this.attachInternals();
219
302
  }
303
+ get _labels() {
304
+ return { ...DEFAULT_LABELS, ...this.labels };
305
+ }
220
306
  /**
221
307
  * Handles switch change events.
222
308
  * Updates the checked state and dispatches a custom change event.
@@ -228,13 +314,20 @@ var Switch = class extends ThemeableElement {
228
314
  this._internals.setFormValue(this.checked ? this.value : null);
229
315
  const input = this.shadowRoot?.querySelector("input");
230
316
  if (this.required && !this.checked && input) {
231
- this._internals.setValidity({ valueMissing: true }, "Please check this switch", input);
317
+ this._internals.setValidity({ valueMissing: true }, this._labels.validationMessage, input);
232
318
  } else {
233
319
  this._internals.setValidity({});
234
320
  }
235
321
  }
236
322
  formResetCallback() {
237
- this.checked = false;
323
+ this.checked = this._default.value;
324
+ }
325
+ formDisabledCallback(disabled) {
326
+ this._fieldsetDisabled.setFieldsetDisabled(disabled);
327
+ }
328
+ /** `this.disabled` combined with an ancestor `<fieldset>`'s own disabled state (see `FormDisabledController`). */
329
+ get _isDisabled() {
330
+ return this.disabled || this._fieldsetDisabled.fieldsetDisabled;
238
331
  }
239
332
  checkValidity() {
240
333
  return this._internals.checkValidity();
@@ -261,7 +354,7 @@ var Switch = class extends ThemeableElement {
261
354
  <input
262
355
  type="checkbox"
263
356
  .checked=${this.checked}
264
- ?disabled=${this.disabled}
357
+ ?disabled=${this._isDisabled}
265
358
  ?required=${this.required}
266
359
  name=${this.name}
267
360
  value=${this.value}
@@ -286,7 +379,7 @@ __decorateClass([
286
379
  property2({ reflect: true })
287
380
  ], Switch.prototype, "size", 2);
288
381
  __decorateClass([
289
- property2({ type: Boolean, reflect: true })
382
+ property2({ type: Boolean })
290
383
  ], Switch.prototype, "checked", 2);
291
384
  __decorateClass([
292
385
  property2({ type: Boolean, reflect: true })
@@ -306,6 +399,9 @@ __decorateClass([
306
399
  __decorateClass([
307
400
  property2({ reflect: true })
308
401
  ], Switch.prototype, "value", 2);
402
+ __decorateClass([
403
+ property2({ type: Object })
404
+ ], Switch.prototype, "labels", 2);
309
405
  Switch = __decorateClass([
310
406
  customElement("lt-switch")
311
407
  ], Switch);
@@ -1,5 +1,5 @@
1
1
  import { ThemeableElement } from '../../base';
2
- import { SwitchSize, SwitchVariant, SwitchLabelPosition } from './switch.types';
2
+ import { SwitchSize, SwitchVariant, SwitchLabelPosition, type SwitchLabels } from './switch.types';
3
3
  /**
4
4
  * A toggle switch component with sliding animation.
5
5
  * Internally uses a checkbox input with custom toggle styling.
@@ -35,6 +35,8 @@ export declare class Switch extends ThemeableElement {
35
35
  static styles: import("lit").CSSResult;
36
36
  static formAssociated: boolean;
37
37
  private _internals;
38
+ private _default;
39
+ private _fieldsetDisabled;
38
40
  constructor();
39
41
  /**
40
42
  * Visual variant that determines the color when checked.
@@ -47,7 +49,10 @@ export declare class Switch extends ThemeableElement {
47
49
  */
48
50
  size: SwitchSize;
49
51
  /**
50
- * Whether the switch is checked (on).
52
+ * Whether the switch is checked (on). Not reflected as an attribute — a native checkbox's `checked` _content_
53
+ * attribute stays fixed at the markup default and never tracks interaction; reflecting it live meant
54
+ * `formResetCallback` could no longer tell what the original default was. Read the property, or the `change`
55
+ * event's `detail.checked`, instead of `[checked]`/`hasAttribute('checked')`.
51
56
  * @default false
52
57
  */
53
58
  checked: boolean;
@@ -81,6 +86,9 @@ export declare class Switch extends ThemeableElement {
81
86
  * @default 'on'
82
87
  */
83
88
  value: string;
89
+ /** Overridable UI text (the required-field validation message), for i18n. English defaults — override only the keys you need. */
90
+ labels: Partial<SwitchLabels>;
91
+ private get _labels();
84
92
  /**
85
93
  * Handles switch change events.
86
94
  * Updates the checked state and dispatches a custom change event.
@@ -90,6 +98,9 @@ export declare class Switch extends ThemeableElement {
90
98
  */
91
99
  updated(): void;
92
100
  formResetCallback(): void;
101
+ formDisabledCallback(disabled: boolean): void;
102
+ /** `this.disabled` combined with an ancestor `<fieldset>`'s own disabled state (see `FormDisabledController`). */
103
+ private get _isDisabled();
93
104
  checkValidity(): boolean;
94
105
  reportValidity(): boolean;
95
106
  private handleChange;
@@ -19,3 +19,8 @@ export type SwitchSize = 'sm' | 'md' | 'lg';
19
19
  */
20
20
  export type SwitchVariant = 'primary' | 'secondary' | 'success' | 'error' | 'info';
21
21
  export type SwitchLabelPosition = 'left' | 'right';
22
+ /** Overridable UI text for `lt-switch`, for i18n. */
23
+ export interface SwitchLabels {
24
+ /** Validation message shown when a required switch is left unchecked. */
25
+ validationMessage: string;
26
+ }
@@ -82,7 +82,7 @@ var tabStyles = css`
82
82
  }
83
83
 
84
84
  :host([disabled]) .tab {
85
- color: var(--lt-color-neutral-300);
85
+ color: var(--lt-text-disabled);
86
86
  cursor: not-allowed;
87
87
  opacity: 0.5;
88
88
  }
@@ -125,6 +125,17 @@ var tabStyles = css`
125
125
  box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
126
126
  color: var(--lt-text-primary);
127
127
  }
128
+
129
+ /* ── Reduced motion ─────────────────────────────────────────────────────── */
130
+
131
+ @media (prefers-reduced-motion: reduce) {
132
+ /* :host([data-variant='pills']) .tab must be listed explicitly, not just .tab — it outranks a bare .tab in
133
+ specificity (an extra :host() attribute selector), so it would otherwise keep its own transition. */
134
+ .tab,
135
+ :host([data-variant='pills']) .tab {
136
+ transition: none;
137
+ }
138
+ }
128
139
  `;
129
140
 
130
141
  // src/components/tab/tab.ts
@@ -138,21 +149,13 @@ var Tab = class extends ThemeableElement {
138
149
  this.active = false;
139
150
  this.disabled = false;
140
151
  this.size = "md";
141
- this._handleHostClick = () => {
142
- if (this.disabled) return;
143
- this.dispatchEvent(
144
- new CustomEvent("tab-click", {
145
- detail: { value: this.value },
146
- bubbles: true,
147
- composed: true
148
- })
149
- );
150
- };
152
+ // Enter/Space activate the tab exactly like a mouse click: a native `click` on the host, which
153
+ // `lt-tab-group` (or any consumer) listens for. There is no separate tab event.
151
154
  this._handleHostKeydown = (e) => {
152
155
  if (this.disabled) return;
153
156
  if (e.key === "Enter" || e.key === " ") {
154
157
  e.preventDefault();
155
- this._handleHostClick();
158
+ this.click();
156
159
  }
157
160
  };
158
161
  }
@@ -160,12 +163,10 @@ var Tab = class extends ThemeableElement {
160
163
  super.connectedCallback();
161
164
  this.setAttribute("role", "tab");
162
165
  this.tabIndex = 0;
163
- this.addEventListener("click", this._handleHostClick);
164
166
  this.addEventListener("keydown", this._handleHostKeydown);
165
167
  }
166
168
  disconnectedCallback() {
167
169
  super.disconnectedCallback();
168
- this.removeEventListener("click", this._handleHostClick);
169
170
  this.removeEventListener("keydown", this._handleHostKeydown);
170
171
  }
171
172
  updated(changedProperties) {
@@ -53,7 +53,6 @@ export declare class Tab extends ThemeableElement {
53
53
  connectedCallback(): void;
54
54
  disconnectedCallback(): void;
55
55
  protected updated(changedProperties: Map<string, unknown>): void;
56
- private _handleHostClick;
57
56
  private _handleHostKeydown;
58
57
  render(): import("lit").TemplateResult<1>;
59
58
  }