@latty-ds/web 0.10.4 → 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 (99) hide show
  1. package/custom-elements.json +1969 -228
  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 +124 -17
  6. package/dist/components/avatar/avatar.d.ts +2 -0
  7. package/dist/components/avatar/index.js +10 -3
  8. package/dist/components/breadcrumb/breadcrumb.d.ts +4 -0
  9. package/dist/components/breadcrumb/breadcrumb.types.d.ts +5 -0
  10. package/dist/components/breadcrumb/index.js +20 -3
  11. package/dist/components/button/button.d.ts +33 -0
  12. package/dist/components/button/index.js +135 -12
  13. package/dist/components/calendar/calendar.d.ts +15 -7
  14. package/dist/components/calendar/calendar.types.d.ts +17 -1
  15. package/dist/components/calendar/index.js +78 -25
  16. package/dist/components/checkbox/checkbox.d.ts +14 -2
  17. package/dist/components/checkbox/checkbox.types.d.ts +5 -0
  18. package/dist/components/checkbox/index.js +82 -5
  19. package/dist/components/chip/chip.d.ts +4 -1
  20. package/dist/components/chip/chip.types.d.ts +5 -0
  21. package/dist/components/chip/index.js +29 -1
  22. package/dist/components/color-input/color-input.d.ts +11 -2
  23. package/dist/components/color-input/color-input.types.d.ts +5 -0
  24. package/dist/components/color-input/index.js +92 -8
  25. package/dist/components/combobox/combobox.d.ts +18 -2
  26. package/dist/components/combobox/combobox.types.d.ts +7 -0
  27. package/dist/components/combobox/index.js +164 -23
  28. package/dist/components/date-input/date-input.d.ts +22 -7
  29. package/dist/components/date-input/date-input.types.d.ts +7 -0
  30. package/dist/components/date-input/index.js +216 -53
  31. package/dist/components/datepicker/datepicker.d.ts +9 -1
  32. package/dist/components/datepicker/index.js +108 -15
  33. package/dist/components/dialog/dialog.d.ts +39 -19
  34. package/dist/components/dialog/dialog.types.d.ts +5 -0
  35. package/dist/components/dialog/index.js +237 -90
  36. package/dist/components/divider/index.js +2 -2
  37. package/dist/components/dropdown/dropdown.d.ts +4 -0
  38. package/dist/components/dropdown/index.js +43 -3
  39. package/dist/components/icon-button/icon-button.d.ts +4 -0
  40. package/dist/components/icon-button/index.js +36 -1
  41. package/dist/components/image/image.d.ts +7 -7
  42. package/dist/components/image/index.js +6 -11
  43. package/dist/components/list/index.js +14 -4
  44. package/dist/components/nav/index.js +37 -17
  45. package/dist/components/nav/nav-item.d.ts +8 -8
  46. package/dist/components/pagination/index.js +57 -16
  47. package/dist/components/pagination/pagination.d.ts +8 -1
  48. package/dist/components/pagination/pagination.types.d.ts +15 -0
  49. package/dist/components/progress/index.js +44 -6
  50. package/dist/components/progress/progress.d.ts +12 -0
  51. package/dist/components/radio/index.js +148 -9
  52. package/dist/components/radio/radio.d.ts +34 -2
  53. package/dist/components/radio/radio.types.d.ts +5 -0
  54. package/dist/components/radio-group/index.js +120 -29
  55. package/dist/components/radio-group/radio-group.d.ts +27 -2
  56. package/dist/components/select/index.js +197 -44
  57. package/dist/components/select/select.d.ts +38 -4
  58. package/dist/components/select/select.types.d.ts +5 -0
  59. package/dist/components/shared/aria-forwarding.d.ts +1 -1
  60. package/dist/components/shared/form-default.d.ts +30 -0
  61. package/dist/components/shared/form-disabled.d.ts +40 -0
  62. package/dist/components/shared/form-field.d.ts +7 -0
  63. package/dist/components/shared/form-validity.d.ts +17 -0
  64. package/dist/components/shared/roving.d.ts +11 -0
  65. package/dist/components/shared/scroll-lock.d.ts +2 -0
  66. package/dist/components/sidepanel/index.js +275 -72
  67. package/dist/components/sidepanel/sidepanel.d.ts +32 -10
  68. package/dist/components/sidepanel/sidepanel.types.d.ts +10 -1
  69. package/dist/components/skeleton/index.js +3 -8
  70. package/dist/components/slider/index.js +307 -75
  71. package/dist/components/slider/slider.d.ts +16 -3
  72. package/dist/components/snackbar/index.js +82 -9
  73. package/dist/components/snackbar/snackbar.d.ts +17 -1
  74. package/dist/components/snackbar/snackbar.types.d.ts +5 -0
  75. package/dist/components/surface/index.js +8 -0
  76. package/dist/components/switch/index.js +101 -5
  77. package/dist/components/switch/switch.d.ts +13 -2
  78. package/dist/components/switch/switch.types.d.ts +5 -0
  79. package/dist/components/tab/index.js +15 -14
  80. package/dist/components/tab/tab.d.ts +0 -1
  81. package/dist/components/tab-group/index.js +102 -30
  82. package/dist/components/tab-group/tab-group.d.ts +17 -2
  83. package/dist/components/table/index.js +105 -61
  84. package/dist/components/table/table.d.ts +8 -2
  85. package/dist/components/table/table.types.d.ts +4 -3
  86. package/dist/components/text/index.js +16 -1
  87. package/dist/components/text/text.d.ts +1 -1
  88. package/dist/components/textfield/index.js +243 -35
  89. package/dist/components/textfield/textfield.d.ts +24 -2
  90. package/dist/components/textfield/textfield.types.d.ts +11 -0
  91. package/dist/components/tooltip/index.js +176 -49
  92. package/dist/components/tooltip/tooltip.d.ts +17 -0
  93. package/dist/components/tooltip/tooltip.types.d.ts +5 -1
  94. package/dist/index.cjs +2458 -683
  95. package/dist/index.js +2469 -690
  96. package/dist/manifest.json +202 -32
  97. package/dist/utils/dev-warn.d.ts +5 -0
  98. package/dist/utils/index.d.ts +1 -0
  99. package/package.json +3 -7
@@ -47,20 +47,39 @@ var tooltipStyles = css`
47
47
  --lt-tooltip-offset: 8px;
48
48
  }
49
49
 
50
+ /* The bubble is a "manual" popover so it renders in the top layer — above an open lt-dialog/lt-sidepanel
51
+ regardless of z-index, and never clipped by an overflow:hidden ancestor. Its position is set from JS (see
52
+ _reposition in tooltip.ts), since a top-layer element is positioned against the viewport, not the host.
53
+ display/overlay need allow-discrete so hidePopover() doesn't cut the exit fade short — same as lt-snackbar. */
50
54
  .tooltip {
51
55
  background: var(--lt-tooltip-bg, var(--lt-bg-inverse));
56
+ border: 0;
52
57
  border-radius: 4px;
53
58
  color: var(--lt-tooltip-color, var(--lt-text-inverse));
59
+ inset: auto;
60
+ margin: 0;
54
61
  opacity: 0;
62
+ overflow: visible;
55
63
  padding: var(--lt-spacing-1) var(--lt-spacing-2);
56
64
  pointer-events: none;
57
- position: absolute;
65
+ position: fixed;
58
66
  transition:
59
67
  opacity 150ms ease,
60
- visibility 150ms ease;
61
- visibility: hidden;
68
+ display 150ms allow-discrete,
69
+ overlay 150ms allow-discrete;
62
70
  white-space: nowrap;
63
- z-index: 1000;
71
+ }
72
+
73
+ .tooltip:popover-open {
74
+ opacity: 1;
75
+ }
76
+
77
+ /* A newly-shown popover has no earlier rendered frame to transition from — @starting-style supplies one so the
78
+ fade-in actually animates instead of snapping straight to the open state. */
79
+ @starting-style {
80
+ .tooltip:popover-open {
81
+ opacity: 0;
82
+ }
64
83
  }
65
84
 
66
85
  .arrow {
@@ -70,78 +89,60 @@ var tooltipStyles = css`
70
89
  width: 0;
71
90
  }
72
91
 
73
- :host(:hover) .tooltip,
74
- :host(:focus-within) .tooltip {
75
- opacity: 1;
76
- visibility: visible;
77
- }
78
-
79
92
  :host([disabled]) .tooltip,
80
93
  :host(:not([content])) .tooltip,
81
94
  :host([content='']) .tooltip {
82
95
  display: none;
83
96
  }
84
97
 
85
- /* ── Top (default) ──────────────────────────── */
98
+ /* ── Arrows ─────────────────────────────────────────────────────────────── The bubble's own position is set
99
+ from JS; each arrow sits on the bubble edge facing the host. Centering is direction-symmetric, so
100
+ inset-inline-start:50% + translateX(-50%) is correct in both LTR and RTL. Start/end use logical properties, so
101
+ they flip with the page direction without a custom-property trick. ─────────────────────────── */
86
102
 
87
- :host(:not([placement])) .tooltip,
88
- :host([placement='top']) .tooltip {
89
- bottom: calc(100% + var(--lt-tooltip-offset));
90
- left: 50%;
91
- transform: translateX(-50%);
92
- }
103
+ /* ── Top (default) ──────────────────────────── */
93
104
 
94
105
  :host(:not([placement])) .arrow,
95
106
  :host([placement='top']) .arrow {
96
107
  border-top-color: var(--lt-tooltip-bg, var(--lt-color-neutral-900));
97
- left: 50%;
108
+ inset-inline-start: 50%;
98
109
  top: 100%;
99
110
  transform: translateX(-50%);
100
111
  }
101
112
 
102
113
  /* ── Bottom ─────────────────────────────────── */
103
114
 
104
- :host([placement='bottom']) .tooltip {
105
- left: 50%;
106
- top: calc(100% + var(--lt-tooltip-offset));
107
- transform: translateX(-50%);
108
- }
109
-
110
115
  :host([placement='bottom']) .arrow {
111
116
  border-bottom-color: var(--lt-tooltip-bg, var(--lt-color-neutral-900));
112
117
  bottom: 100%;
113
- left: 50%;
118
+ inset-inline-start: 50%;
114
119
  transform: translateX(-50%);
115
120
  }
116
121
 
117
- /* ── Left ───────────────────────────────────── */
122
+ /* ── Start ──────────────────────────────────── */
118
123
 
119
- :host([placement='left']) .tooltip {
120
- right: calc(100% + var(--lt-tooltip-offset));
124
+ :host([placement='start']) .arrow {
125
+ border-inline-start-color: var(--lt-tooltip-bg, var(--lt-color-neutral-900));
126
+ inset-inline-start: 100%;
121
127
  top: 50%;
122
128
  transform: translateY(-50%);
123
129
  }
124
130
 
125
- :host([placement='left']) .arrow {
126
- border-left-color: var(--lt-tooltip-bg, var(--lt-color-neutral-900));
127
- left: 100%;
128
- top: 50%;
129
- transform: translateY(-50%);
130
- }
131
+ /* ── End ────────────────────────────────────── */
131
132
 
132
- /* ── Right ──────────────────────────────────── */
133
-
134
- :host([placement='right']) .tooltip {
135
- left: calc(100% + var(--lt-tooltip-offset));
133
+ :host([placement='end']) .arrow {
134
+ border-inline-end-color: var(--lt-tooltip-bg, var(--lt-color-neutral-900));
135
+ inset-inline-end: 100%;
136
136
  top: 50%;
137
137
  transform: translateY(-50%);
138
138
  }
139
139
 
140
- :host([placement='right']) .arrow {
141
- border-right-color: var(--lt-tooltip-bg, var(--lt-color-neutral-900));
142
- right: 100%;
143
- top: 50%;
144
- transform: translateY(-50%);
140
+ /* ── Reduced motion ─────────────────────────────────────────────────────── */
141
+
142
+ @media (prefers-reduced-motion: reduce) {
143
+ .tooltip {
144
+ transition: none;
145
+ }
145
146
  }
146
147
  `;
147
148
 
@@ -317,6 +318,20 @@ var textStyles = css2`
317
318
  `;
318
319
 
319
320
  // src/components/text/text.ts
321
+ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
322
+ "h1",
323
+ "h2",
324
+ "h3",
325
+ "h4",
326
+ "h5",
327
+ "h6",
328
+ "p",
329
+ "span",
330
+ "div",
331
+ "label",
332
+ "strong",
333
+ "em"
334
+ ]);
320
335
  var VARIANT_TAG = {
321
336
  "display-2xl": "p",
322
337
  "display-xl": "p",
@@ -341,7 +356,8 @@ var Text = class extends ThemeableElement {
341
356
  this.as = "";
342
357
  }
343
358
  render() {
344
- const tag = unsafeStatic(this.as || VARIANT_TAG[this.variant] || "p");
359
+ const requested = this.as && ALLOWED_TAGS.has(this.as) ? this.as : "";
360
+ const tag = unsafeStatic(requested || VARIANT_TAG[this.variant] || "p");
345
361
  return html`<${tag} part="base"><slot></slot></${tag}>`;
346
362
  }
347
363
  };
@@ -362,17 +378,117 @@ var Tooltip = class extends ThemeableElement {
362
378
  constructor() {
363
379
  super(...arguments);
364
380
  this._tooltipId = `lt-tooltip-${++_tooltipIdCounter}`;
381
+ this._hovered = false;
382
+ this._focused = false;
365
383
  this.content = "";
366
384
  this.placement = "top";
367
385
  this.disabled = false;
368
386
  this.background = "";
369
387
  this.color = "";
370
- }
371
- firstUpdated() {
388
+ // A top-layer popover is positioned against the viewport, not the host, so the bubble's fixed coordinates are
389
+ // computed here from the host's rect. Fixed coordinates need a physical side, so the logical start/end placements
390
+ // are resolved against the host's computed direction (start is the left side in LTR, the right side in RTL).
391
+ this._reposition = () => {
392
+ if (!this._isShown()) return;
393
+ const host = this.getBoundingClientRect();
394
+ const { width, height } = this._tooltipEl.getBoundingClientRect();
395
+ const style = getComputedStyle(this);
396
+ const offset = parseFloat(style.getPropertyValue("--lt-tooltip-offset")) || 8;
397
+ const rtl = style.direction === "rtl";
398
+ let left;
399
+ let top;
400
+ switch (this.placement) {
401
+ case "bottom":
402
+ left = host.left + host.width / 2 - width / 2;
403
+ top = host.bottom + offset;
404
+ break;
405
+ case "start":
406
+ case "end": {
407
+ const onLeft = this.placement === "start" !== rtl;
408
+ left = onLeft ? host.left - offset - width : host.right + offset;
409
+ top = host.top + host.height / 2 - height / 2;
410
+ break;
411
+ }
412
+ default:
413
+ left = host.left + host.width / 2 - width / 2;
414
+ top = host.top - offset - height;
415
+ }
416
+ this._tooltipEl.style.left = `${left}px`;
417
+ this._tooltipEl.style.top = `${top}px`;
418
+ };
419
+ this._onPointerEnter = () => {
420
+ this._hovered = true;
421
+ this._sync();
422
+ };
423
+ this._onPointerLeave = () => {
424
+ this._hovered = false;
425
+ this._sync();
426
+ };
427
+ this._onFocusIn = () => {
428
+ this._focused = true;
429
+ this._sync();
430
+ };
431
+ this._onFocusOut = (e) => {
432
+ if (this.contains(e.relatedTarget)) return;
433
+ this._focused = false;
434
+ this._sync();
435
+ };
436
+ }
437
+ // aria-describedby's id lookup only works within one tree: the tooltip bubble lives in this element's own
438
+ // shadow root, but the trigger the id is set on is the slotted (light DOM) element — an id reference can't
439
+ // cross that boundary, so no AT ever actually announces it (verified: resolves to nothing in the accessibility
440
+ // tree). aria-description carries the text itself, so there's no id, and nothing to fail to resolve.
441
+ _describeTrigger() {
372
442
  const trigger = this._slot?.assignedElements()[0];
373
- if (trigger) {
374
- trigger.setAttribute("aria-describedby", this._tooltipId);
443
+ trigger?.setAttribute("aria-description", this.content);
444
+ }
445
+ _isShown() {
446
+ return this._tooltipEl?.matches(":popover-open") ?? false;
447
+ }
448
+ _sync() {
449
+ const shouldShow = (this._hovered || this._focused) && !this.disabled && !!this.content;
450
+ if (shouldShow) this._show();
451
+ else this._hide();
452
+ }
453
+ _show() {
454
+ if (!this._isShown()) {
455
+ this._tooltipEl.showPopover();
456
+ window.addEventListener("scroll", this._reposition, true);
457
+ window.addEventListener("resize", this._reposition);
375
458
  }
459
+ this._reposition();
460
+ }
461
+ _hide() {
462
+ if (!this._isShown()) return;
463
+ this._tooltipEl.hidePopover();
464
+ window.removeEventListener("scroll", this._reposition, true);
465
+ window.removeEventListener("resize", this._reposition);
466
+ }
467
+ connectedCallback() {
468
+ super.connectedCallback();
469
+ this.addEventListener("pointerenter", this._onPointerEnter);
470
+ this.addEventListener("pointerleave", this._onPointerLeave);
471
+ this.addEventListener("focusin", this._onFocusIn);
472
+ this.addEventListener("focusout", this._onFocusOut);
473
+ }
474
+ disconnectedCallback() {
475
+ super.disconnectedCallback();
476
+ this.removeEventListener("pointerenter", this._onPointerEnter);
477
+ this.removeEventListener("pointerleave", this._onPointerLeave);
478
+ this.removeEventListener("focusin", this._onFocusIn);
479
+ this.removeEventListener("focusout", this._onFocusOut);
480
+ this._hovered = false;
481
+ this._focused = false;
482
+ this._hide();
483
+ }
484
+ firstUpdated() {
485
+ this._describeTrigger();
486
+ }
487
+ updated(changed) {
488
+ super.updated(changed);
489
+ if (changed.has("content")) this._describeTrigger();
490
+ if (changed.has("content") || changed.has("disabled")) this._sync();
491
+ else if (changed.has("placement")) this._reposition();
376
492
  }
377
493
  render() {
378
494
  const tooltipStyle = styleMap({
@@ -381,7 +497,15 @@ var Tooltip = class extends ThemeableElement {
381
497
  });
382
498
  return html2`
383
499
  <slot></slot>
384
- <div class="tooltip" id=${this._tooltipId} role="tooltip" part="tooltip" aria-hidden="true" style=${tooltipStyle}>
500
+ <div
501
+ class="tooltip"
502
+ id=${this._tooltipId}
503
+ popover="manual"
504
+ role="tooltip"
505
+ part="tooltip"
506
+ aria-hidden="true"
507
+ style=${tooltipStyle}
508
+ >
385
509
  <lt-text variant="caption" as="span">${this.content}</lt-text>
386
510
  <span class="arrow" part="arrow"></span>
387
511
  </div>
@@ -392,6 +516,9 @@ Tooltip.styles = tooltipStyles;
392
516
  __decorateClass([
393
517
  query("slot")
394
518
  ], Tooltip.prototype, "_slot", 2);
519
+ __decorateClass([
520
+ query(".tooltip")
521
+ ], Tooltip.prototype, "_tooltipEl", 2);
395
522
  __decorateClass([
396
523
  property3({ reflect: true })
397
524
  ], Tooltip.prototype, "content", 2);
@@ -1,3 +1,4 @@
1
+ import { type PropertyValues } from 'lit';
1
2
  import { ThemeableElement } from '../../base';
2
3
  import type { TooltipPlacement } from './tooltip.types';
3
4
  import '../text/text';
@@ -27,6 +28,9 @@ export declare class Tooltip extends ThemeableElement {
27
28
  static styles: import("lit").CSSResult;
28
29
  private _tooltipId;
29
30
  private _slot;
31
+ private _tooltipEl;
32
+ private _hovered;
33
+ private _focused;
30
34
  /** Tooltip label text. */
31
35
  content: string;
32
36
  /** Placement of the tooltip relative to the trigger: top, bottom, left, or right. */
@@ -37,6 +41,19 @@ export declare class Tooltip extends ThemeableElement {
37
41
  background: string;
38
42
  /** Text color override. Accepts a hex value (#ffffff) or a CSS token name (--lt-color-neutral-50). */
39
43
  color: string;
44
+ private _describeTrigger;
45
+ private _isShown;
46
+ private _sync;
47
+ private _show;
48
+ private _hide;
49
+ private _reposition;
50
+ private _onPointerEnter;
51
+ private _onPointerLeave;
52
+ private _onFocusIn;
53
+ private _onFocusOut;
54
+ connectedCallback(): void;
55
+ disconnectedCallback(): void;
40
56
  protected firstUpdated(): void;
57
+ protected updated(changed: PropertyValues<this>): void;
41
58
  render(): import("lit").TemplateResult<1>;
42
59
  }
@@ -1 +1,5 @@
1
- export type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
1
+ /**
2
+ * Side the tooltip appears on. `'start'`/`'end'` are logical — they resolve to the left/right side based on the
3
+ * page's reading direction (left/right in LTR, right/left in RTL), not a fixed physical side.
4
+ */
5
+ export type TooltipPlacement = 'top' | 'bottom' | 'start' | 'end';