@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
@@ -9,6 +9,10 @@ var __decorateClass = (decorators, target, key, kind) => {
9
9
  return result;
10
10
  };
11
11
 
12
+ // src/components/shared/form-field.ts
13
+ var fieldCount = 0;
14
+ var createFieldIds = () => ({ helper: `lt-field-${fieldCount++}-helper` });
15
+
12
16
  // src/components/datepicker/datepicker.ts
13
17
  import { html as html2, nothing } from "lit";
14
18
 
@@ -59,7 +63,7 @@ var datepickerStyles = css`
59
63
  }
60
64
 
61
65
  .required-indicator {
62
- color: var(--lt-color-error-500);
66
+ color: var(--lt-text-error);
63
67
  }
64
68
 
65
69
  input {
@@ -120,7 +124,7 @@ var datepickerStyles = css`
120
124
  box-shadow: 0 0 0 3px var(--lt-bg-warning-subtle);
121
125
  }
122
126
  :host([variant='error']) input {
123
- border-color: var(--lt-color-error-500);
127
+ border-color: var(--lt-text-error);
124
128
  }
125
129
  :host([variant='error']) input:focus {
126
130
  box-shadow: 0 0 0 3px var(--lt-bg-error-subtle);
@@ -132,19 +136,80 @@ var datepickerStyles = css`
132
136
  }
133
137
 
134
138
  :host([variant='error']) .helper-text {
135
- color: var(--lt-color-error-600);
139
+ color: var(--lt-text-error);
136
140
  }
137
141
  :host([variant='success']) .helper-text {
138
- color: var(--lt-color-success-600);
142
+ color: var(--lt-text-success);
139
143
  }
140
144
  :host([variant='warning']) .helper-text {
141
- color: var(--lt-color-warning-700);
145
+ color: var(--lt-text-warning);
146
+ }
147
+
148
+ /* ── Reduced motion ─────────────────────────────────────────────────────── */
149
+
150
+ @media (prefers-reduced-motion: reduce) {
151
+ input {
152
+ transition: none;
153
+ }
142
154
  }
143
155
  `;
144
156
 
145
157
  // src/utils/dispatch.ts
146
158
  var dispatch = (host, name, detail, init) => host.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true, ...init }));
147
159
 
160
+ // src/components/shared/form-default.ts
161
+ var FormDefaultController = class {
162
+ constructor(host, get) {
163
+ this._captured = false;
164
+ this._get = get;
165
+ host.addController(this);
166
+ }
167
+ hostConnected() {
168
+ if (this._captured) return;
169
+ this._captured = true;
170
+ this.value = this._get();
171
+ }
172
+ };
173
+
174
+ // src/components/shared/form-disabled.ts
175
+ var FormDisabledController = class {
176
+ constructor(host) {
177
+ this._fieldsetDisabled = false;
178
+ this._host = host;
179
+ host.addController(this);
180
+ }
181
+ // No-op — this controller has no connect-time work of its own (formDisabledCallback drives it instead), but
182
+ // ReactiveController's members are all optional, and a class satisfying an all-optional interface with no
183
+ // members in common is a TS error (TS2559); this gives it one.
184
+ hostConnected() {
185
+ }
186
+ /** Whether an ancestor `<fieldset>` is currently disabled. */
187
+ get fieldsetDisabled() {
188
+ return this._fieldsetDisabled;
189
+ }
190
+ setFieldsetDisabled(disabled) {
191
+ if (this._fieldsetDisabled === disabled) return;
192
+ this._fieldsetDisabled = disabled;
193
+ this._host.requestUpdate();
194
+ }
195
+ };
196
+
197
+ // src/components/shared/form-validity.ts
198
+ function toValidityFlags(validity) {
199
+ return {
200
+ valueMissing: validity.valueMissing,
201
+ typeMismatch: validity.typeMismatch,
202
+ patternMismatch: validity.patternMismatch,
203
+ tooLong: validity.tooLong,
204
+ tooShort: validity.tooShort,
205
+ rangeUnderflow: validity.rangeUnderflow,
206
+ rangeOverflow: validity.rangeOverflow,
207
+ stepMismatch: validity.stepMismatch,
208
+ badInput: validity.badInput,
209
+ customError: validity.customError
210
+ };
211
+ }
212
+
148
213
  // src/components/text/text.ts
149
214
  import { customElement, property as property2 } from "lit/decorators.js";
150
215
  import { html, unsafeStatic } from "lit/static-html.js";
@@ -314,6 +379,20 @@ var textStyles = css2`
314
379
  `;
315
380
 
316
381
  // src/components/text/text.ts
382
+ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
383
+ "h1",
384
+ "h2",
385
+ "h3",
386
+ "h4",
387
+ "h5",
388
+ "h6",
389
+ "p",
390
+ "span",
391
+ "div",
392
+ "label",
393
+ "strong",
394
+ "em"
395
+ ]);
317
396
  var VARIANT_TAG = {
318
397
  "display-2xl": "p",
319
398
  "display-xl": "p",
@@ -338,7 +417,8 @@ var Text = class extends ThemeableElement {
338
417
  this.as = "";
339
418
  }
340
419
  render() {
341
- const tag = unsafeStatic(this.as || VARIANT_TAG[this.variant] || "p");
420
+ const requested = this.as && ALLOWED_TAGS.has(this.as) ? this.as : "";
421
+ const tag = unsafeStatic(requested || VARIANT_TAG[this.variant] || "p");
342
422
  return html`<${tag} part="base"><slot></slot></${tag}>`;
343
423
  }
344
424
  };
@@ -357,12 +437,15 @@ Text = __decorateClass([
357
437
  var Datepicker = class extends ThemeableElement {
358
438
  constructor() {
359
439
  super();
440
+ this._default = new FormDefaultController(this, () => this.value);
441
+ this._fieldsetDisabled = new FormDisabledController(this);
360
442
  this.type = "date";
361
443
  this.value = "";
362
444
  this.min = "";
363
445
  this.max = "";
364
446
  this.label = "";
365
447
  this.helperText = "";
448
+ this._fieldIds = createFieldIds();
366
449
  this.variant = "default";
367
450
  this.size = "md";
368
451
  this.disabled = false;
@@ -372,17 +455,24 @@ var Datepicker = class extends ThemeableElement {
372
455
  this._internals = this.attachInternals();
373
456
  }
374
457
  updated(changed) {
375
- if (changed.has("value") || changed.has("required")) {
458
+ if (changed.has("value") || changed.has("required") || changed.has("min") || changed.has("max")) {
376
459
  this._internals.setFormValue(this.value || null);
377
- if (this.required && !this.value) {
378
- this._internals.setValidity({ valueMissing: true }, "Please select a date", this.input);
460
+ if (!this.input.validity.valid) {
461
+ this._internals.setValidity(toValidityFlags(this.input.validity), this.input.validationMessage, this.input);
379
462
  } else {
380
463
  this._internals.setValidity({});
381
464
  }
382
465
  }
383
466
  }
384
467
  formResetCallback() {
385
- this.value = "";
468
+ this.value = this._default.value;
469
+ }
470
+ formDisabledCallback(disabled) {
471
+ this._fieldsetDisabled.setFieldsetDisabled(disabled);
472
+ }
473
+ /** `this.disabled` combined with an ancestor `<fieldset>`'s own disabled state (see `FormDisabledController`). */
474
+ get _isDisabled() {
475
+ return this.disabled || this._fieldsetDisabled.fieldsetDisabled;
386
476
  }
387
477
  checkValidity() {
388
478
  return this._internals.checkValidity();
@@ -390,11 +480,13 @@ var Datepicker = class extends ThemeableElement {
390
480
  reportValidity() {
391
481
  return this._internals.reportValidity();
392
482
  }
393
- handleChange() {
483
+ handleChange(e) {
484
+ e.stopPropagation();
394
485
  this.value = this.input.value;
395
486
  dispatch(this, "change", { value: this.value });
396
487
  }
397
- handleInput() {
488
+ handleInput(e) {
489
+ e.stopPropagation();
398
490
  this.value = this.input.value;
399
491
  dispatch(this, "input", { value: this.value });
400
492
  }
@@ -405,8 +497,9 @@ var Datepicker = class extends ThemeableElement {
405
497
  .value=${this.value}
406
498
  min=${this.min || nothing}
407
499
  max=${this.max || nothing}
408
- ?disabled=${this.disabled}
500
+ ?disabled=${this._isDisabled}
409
501
  ?required=${this.required}
502
+ aria-describedby=${this.helperText ? this._fieldIds.helper : nothing}
410
503
  ?readonly=${this.readonly}
411
504
  @change=${this.handleChange}
412
505
  @input=${this.handleInput}
@@ -418,7 +511,7 @@ var Datepicker = class extends ThemeableElement {
418
511
  <lt-text variant="label" as="span">${this.label}</lt-text>
419
512
  ${this.required ? html2`<span class="required-indicator">*</span>` : nothing} ${inputEl}
420
513
  </label>` : inputEl}
421
- ${this.helperText ? html2`<span class="helper-text">${this.helperText}</span>` : nothing}
514
+ ${this.helperText ? html2`<span id=${this._fieldIds.helper} class="helper-text">${this.helperText}</span>` : nothing}
422
515
  </div>
423
516
  `;
424
517
  }
@@ -429,7 +522,7 @@ __decorateClass([
429
522
  property3({ reflect: true })
430
523
  ], Datepicker.prototype, "type", 2);
431
524
  __decorateClass([
432
- property3({ reflect: true })
525
+ property3()
433
526
  ], Datepicker.prototype, "value", 2);
434
527
  __decorateClass([
435
528
  property3({ reflect: true })
@@ -1,37 +1,41 @@
1
+ import { type PropertyValues } from 'lit';
1
2
  import { ThemeableElement } from '../../base';
2
- import { DialogSize } from './dialog.types';
3
+ import { DialogSize, type DialogLabels } from './dialog.types';
3
4
  import type { SurfaceElevation, SurfaceAppearance } from '../surface/surface.types';
4
5
  import '../icon-button/icon-button';
5
6
  import '../surface/surface';
6
7
  import '../text/text';
7
8
  /**
8
- * Dialog component (modal) with backdrop, animations, and accessibility features.
9
+ * Dialog component (modal) built on the native `<dialog>` element, with a backdrop, animations, and full
10
+ * accessibility features for free — top-layer stacking, a real focus trap, an inert page background, and
11
+ * Escape-to-close all come from the platform (`showModal()`), not from anything this component builds itself
12
+ * (DLG-1). Body scroll locking is the one thing native `<dialog>` doesn't do on its own — this component keeps
13
+ * its own ref-counted lock for that (DLG-3, see `shared/scroll-lock.ts`).
9
14
  *
10
15
  * @element lt-dialog
11
16
  *
12
17
  * Features:
13
18
  * - Multiple size options (sm, md, lg, xl, fullscreen)
14
19
  * - Backdrop overlay with configurable click-to-close
15
- * - Close on Escape key
16
- * - Header with optional title and close button
20
+ * - Close on Escape key (native; `close-on-escape="false"` prevents it)
21
+ * - Header with optional heading and close button
17
22
  * - Scrollable body content
18
23
  * - Optional footer for actions
19
- * - Focus trap and restoration
20
- * - Custom open/close events
21
- * - Accessible with aria-modal and role="dialog"
24
+ * - Native focus trap and restoration
25
+ * - `open` / `close` events
26
+ * - Accessible via the native `<dialog>` element's own implicit modal semantics
22
27
  *
23
28
  * @slot - Dialog body content
24
- * @slot header - Custom header content (overrides title prop)
29
+ * @slot header - Custom header content (overrides heading prop)
25
30
  * @slot footer - Footer content (typically for action buttons)
26
31
  *
27
32
  * @fires open - Fired when dialog opens
28
- * @fires close - Fired when dialog closes
29
- * @fires dialog-open - Fired when dialog opens (deprecated, use `open`)
30
- * @fires dialog-close - Fired when dialog closes (deprecated, use `close`)
33
+ * @fires close - Fired when dialog closes (however it closed — the close button, Escape, a backdrop click, or
34
+ * setting `open` to `false` directly)
31
35
  *
32
36
  * @example
33
37
  * ```html
34
- * <lt-dialog open title="Confirm Action">
38
+ * <lt-dialog open heading="Confirm Action">
35
39
  * <p>Are you sure you want to proceed?</p>
36
40
  * <div slot="footer">
37
41
  * <lt-button variant="neutral">Cancel</lt-button>
@@ -42,7 +46,7 @@ import '../text/text';
42
46
  *
43
47
  * @example
44
48
  * ```html
45
- * <lt-dialog size="lg" open title="Settings" no-close-button>
49
+ * <lt-dialog size="lg" open heading="Settings" no-close-button>
46
50
  * <p>Dialog content here...</p>
47
51
  * </lt-dialog>
48
52
  * ```
@@ -60,10 +64,19 @@ export declare class Dialog extends ThemeableElement {
60
64
  */
61
65
  open: boolean;
62
66
  /**
63
- * Dialog title displayed in the header.
67
+ * Dialog heading displayed in the header.
64
68
  * @default ''
65
69
  */
66
- title: string;
70
+ heading: string;
71
+ /**
72
+ * @deprecated Use `heading` instead. `title` collides with the browser's own native tooltip attribute — every
73
+ * HTML element has one regardless of custom-element status, so `<lt-dialog title="…">` made hovering anywhere
74
+ * over the dialog show a browser tooltip, and assistive tech could read it as a duplicate description (DLG-6).
75
+ * This alias keeps existing markup/JS working; the native `title` attribute is always stripped as soon as
76
+ * it's read, so the tooltip never appears.
77
+ */
78
+ get title(): string;
79
+ set title(value: string);
67
80
  /**
68
81
  * Suppress the built-in close button in the header.
69
82
  * @default false
@@ -80,7 +93,7 @@ export declare class Dialog extends ThemeableElement {
80
93
  */
81
94
  closeOnEscape: boolean;
82
95
  /**
83
- * Renders the dialog title in small caps with wider letter spacing.
96
+ * Renders the dialog heading in small caps with wider letter spacing.
84
97
  * @default false
85
98
  */
86
99
  uppercase: boolean;
@@ -99,8 +112,13 @@ export declare class Dialog extends ThemeableElement {
99
112
  * @default 'filled'
100
113
  */
101
114
  appearance: SurfaceAppearance;
115
+ /** Overridable UI text (the close button's accessible name), for i18n. English defaults — override only the keys you need. */
116
+ labels: Partial<DialogLabels>;
117
+ private get _labels();
102
118
  private dialogElement?;
103
119
  private previouslyFocusedElement?;
120
+ private _scrollLocked;
121
+ private _skipNextOpenChange;
104
122
  /**
105
123
  * Opens the dialog.
106
124
  */
@@ -113,14 +131,16 @@ export declare class Dialog extends ThemeableElement {
113
131
  * Toggles the dialog open/closed state.
114
132
  */
115
133
  toggle(): void;
134
+ protected firstUpdated(): void;
116
135
  connectedCallback(): void;
117
136
  disconnectedCallback(): void;
118
- updated(changedProperties: Map<string, unknown>): void;
137
+ updated(changedProperties: PropertyValues<this>): void;
119
138
  private handleOpen;
120
139
  private handleClose;
121
140
  private restoreFocus;
122
- private handleKeydown;
123
- private handleBackdropClick;
141
+ private _handleCancel;
142
+ private _handleNativeClose;
143
+ private _handleDialogClick;
124
144
  private handleCloseClick;
125
145
  render(): import("lit").TemplateResult<1>;
126
146
  private hasSlot;
@@ -9,3 +9,8 @@
9
9
  * - `fullscreen`: Full viewport width and height
10
10
  */
11
11
  export type DialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'fullscreen';
12
+ /** Overridable UI text, for i18n — English defaults; override only the keys you need. */
13
+ export interface DialogLabels {
14
+ /** Accessible label for the close button. */
15
+ close: string;
16
+ }