@aceshooting/lyra-ui 0.1.0 → 0.1.2

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 (125) hide show
  1. package/README.md +77 -6
  2. package/custom-elements.json +12015 -0
  3. package/dist/components/chart/bar-chart.d.ts +14 -0
  4. package/dist/components/chart/bar-chart.js +14 -0
  5. package/dist/components/chart/box-plot.d.ts +46 -0
  6. package/dist/components/chart/box-plot.js +145 -0
  7. package/dist/components/chart/box-plot.styles.d.ts +1 -0
  8. package/dist/components/chart/box-plot.styles.js +22 -0
  9. package/dist/components/chart/bubble-chart.d.ts +19 -0
  10. package/dist/components/chart/bubble-chart.js +18 -0
  11. package/dist/components/chart/chart-loader.d.ts +8 -0
  12. package/dist/components/chart/chart-loader.js +26 -0
  13. package/dist/components/chart/chart.d.ts +91 -0
  14. package/dist/components/chart/chart.js +275 -0
  15. package/dist/components/chart/chart.styles.d.ts +1 -0
  16. package/dist/components/chart/chart.styles.js +33 -0
  17. package/dist/components/chart/doughnut-chart.d.ts +15 -0
  18. package/dist/components/chart/doughnut-chart.js +14 -0
  19. package/dist/components/chart/histogram-bin.d.ts +6 -0
  20. package/dist/components/chart/histogram-bin.js +19 -0
  21. package/dist/components/chart/histogram.d.ts +20 -0
  22. package/dist/components/chart/histogram.js +70 -0
  23. package/dist/components/chart/histogram.styles.d.ts +1 -0
  24. package/dist/components/chart/histogram.styles.js +1 -0
  25. package/dist/components/chart/line-chart.d.ts +14 -0
  26. package/dist/components/chart/line-chart.js +14 -0
  27. package/dist/components/chart/pie-chart.d.ts +16 -0
  28. package/dist/components/chart/pie-chart.js +15 -0
  29. package/dist/components/chart/polar-area-chart.d.ts +15 -0
  30. package/dist/components/chart/polar-area-chart.js +14 -0
  31. package/dist/components/chart/radar-chart.d.ts +15 -0
  32. package/dist/components/chart/radar-chart.js +14 -0
  33. package/dist/components/chart/scatter-chart.d.ts +15 -0
  34. package/dist/components/chart/scatter-chart.js +15 -0
  35. package/dist/components/combobox/combobox.d.ts +16 -0
  36. package/dist/components/combobox/combobox.js +113 -6
  37. package/dist/components/combobox/combobox.styles.js +68 -7
  38. package/dist/components/combobox/option.d.ts +2 -2
  39. package/dist/components/combobox/option.js +11 -7
  40. package/dist/components/date-picker/date-input.d.ts +12 -1
  41. package/dist/components/date-picker/date-input.js +68 -6
  42. package/dist/components/date-picker/date-input.styles.js +58 -6
  43. package/dist/components/date-picker/date-picker.js +5 -2
  44. package/dist/components/date-picker/date-picker.styles.js +9 -2
  45. package/dist/components/empty/empty.d.ts +32 -0
  46. package/dist/components/empty/empty.js +90 -0
  47. package/dist/components/empty/empty.styles.d.ts +1 -0
  48. package/dist/components/empty/empty.styles.js +42 -0
  49. package/dist/components/export-button/csv.d.ts +10 -0
  50. package/dist/components/export-button/csv.js +28 -0
  51. package/dist/components/export-button/export-button.d.ts +41 -0
  52. package/dist/components/export-button/export-button.js +151 -0
  53. package/dist/components/export-button/export-button.styles.d.ts +1 -0
  54. package/dist/components/export-button/export-button.styles.js +79 -0
  55. package/dist/components/file-input/accept.d.ts +14 -0
  56. package/dist/components/file-input/accept.js +30 -0
  57. package/dist/components/file-input/file-input.d.ts +47 -0
  58. package/dist/components/file-input/file-input.js +182 -0
  59. package/dist/components/file-input/file-input.styles.d.ts +1 -0
  60. package/dist/components/file-input/file-input.styles.js +44 -0
  61. package/dist/components/flag/flag.d.ts +4 -0
  62. package/dist/components/flag/flag.js +24 -1
  63. package/dist/components/flag/flag.styles.js +1 -1
  64. package/dist/components/gauge/gauge.d.ts +29 -0
  65. package/dist/components/gauge/gauge.js +133 -0
  66. package/dist/components/gauge/gauge.styles.d.ts +1 -0
  67. package/dist/components/gauge/gauge.styles.js +60 -0
  68. package/dist/components/graph/graph.d.ts +68 -0
  69. package/dist/components/graph/graph.js +276 -0
  70. package/dist/components/graph/graph.styles.d.ts +1 -0
  71. package/dist/components/graph/graph.styles.js +34 -0
  72. package/dist/components/heatmap/heatmap-scale.d.ts +8 -0
  73. package/dist/components/heatmap/heatmap-scale.js +17 -0
  74. package/dist/components/heatmap/heatmap.d.ts +66 -0
  75. package/dist/components/heatmap/heatmap.js +252 -0
  76. package/dist/components/heatmap/heatmap.styles.d.ts +1 -0
  77. package/dist/components/heatmap/heatmap.styles.js +39 -0
  78. package/dist/components/map/map-loader.d.ts +8 -0
  79. package/dist/components/map/map-loader.js +17 -0
  80. package/dist/components/map/map.d.ts +49 -0
  81. package/dist/components/map/map.js +189 -0
  82. package/dist/components/map/map.styles.d.ts +1 -0
  83. package/dist/components/map/map.styles.js +48 -0
  84. package/dist/components/playback/playback.d.ts +51 -0
  85. package/dist/components/playback/playback.js +146 -0
  86. package/dist/components/playback/playback.styles.d.ts +1 -0
  87. package/dist/components/playback/playback.styles.js +40 -0
  88. package/dist/components/skeleton/skeleton.d.ts +26 -0
  89. package/dist/components/skeleton/skeleton.js +65 -0
  90. package/dist/components/skeleton/skeleton.styles.d.ts +1 -0
  91. package/dist/components/skeleton/skeleton.styles.js +57 -0
  92. package/dist/components/split/split.d.ts +41 -0
  93. package/dist/components/split/split.js +171 -0
  94. package/dist/components/split/split.styles.d.ts +1 -0
  95. package/dist/components/split/split.styles.js +51 -0
  96. package/dist/components/stat/stat.d.ts +36 -0
  97. package/dist/components/stat/stat.js +117 -0
  98. package/dist/components/stat/stat.styles.d.ts +1 -0
  99. package/dist/components/stat/stat.styles.js +86 -0
  100. package/dist/components/table/table.d.ts +54 -0
  101. package/dist/components/table/table.js +201 -0
  102. package/dist/components/table/table.styles.d.ts +1 -0
  103. package/dist/components/table/table.styles.js +98 -0
  104. package/dist/components/time-range/time-range.d.ts +40 -0
  105. package/dist/components/time-range/time-range.js +219 -0
  106. package/dist/components/time-range/time-range.styles.d.ts +1 -0
  107. package/dist/components/time-range/time-range.styles.js +78 -0
  108. package/dist/components/toast/toast-item.js +10 -2
  109. package/dist/components/toast/toast-item.styles.js +13 -4
  110. package/dist/components/toast/toast.styles.js +0 -1
  111. package/dist/components/tree/tree-node.d.ts +50 -0
  112. package/dist/components/tree/tree-node.js +196 -0
  113. package/dist/components/tree/tree.d.ts +65 -0
  114. package/dist/components/tree/tree.js +227 -0
  115. package/dist/components/tree/tree.styles.d.ts +1 -0
  116. package/dist/components/tree/tree.styles.js +13 -0
  117. package/dist/internal/form-associated.js +49 -1
  118. package/dist/internal/icons.d.ts +11 -0
  119. package/dist/internal/icons.js +64 -0
  120. package/dist/internal/tokens.styles.js +41 -1
  121. package/dist/lyra.d.ts +67 -0
  122. package/dist/lyra.js +55 -0
  123. package/llms-full.txt +1425 -0
  124. package/llms.txt +47 -0
  125. package/package.json +60 -11
@@ -0,0 +1,117 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { html, nothing } from 'lit';
8
+ import { property, state } from 'lit/decorators.js';
9
+ import { LyraElement } from '../../internal/lyra-element.js';
10
+ import { defineElement } from '../../internal/prefix.js';
11
+ import { chevronIcon } from '../../internal/icons.js';
12
+ import { styles } from './stat.styles.js';
13
+ /**
14
+ * `<lyra-stat>` — a KPI/stat card. First-party invention consolidating the
15
+ * "metric row" / "KPI card" pattern reinvented in every surveyed repo.
16
+ *
17
+ * @customElement lyra-stat
18
+ * @slot - Leading icon.
19
+ * @slot caption - Rich caption content (overrides the `caption` attribute).
20
+ * @csspart base, icon, label, value, unit, trend, caption
21
+ */
22
+ export class LyraStat extends LyraElement {
23
+ constructor() {
24
+ super(...arguments);
25
+ this.label = '';
26
+ this.value = '';
27
+ this.unit = '';
28
+ this.variant = 'neutral';
29
+ this.trend = NaN;
30
+ this.caption = '';
31
+ /** Which trend direction counts as "good" — inverts arrow/color polarity for
32
+ * cost/latency/error-rate-style metrics where a decrease is the win. */
33
+ this.goodDirection = 'up';
34
+ // Same fix `lyra-empty` already established: `[part]:empty` never matches
35
+ // because the part always contains a literal `<slot>` child. Track real
36
+ // slot assignment in JS instead (2026-07-10 audit, "dashboard-atoms"
37
+ // §lyra-stat, High).
38
+ this.hasIcon = false;
39
+ this.hasCaptionSlot = false;
40
+ this.onIconSlotChange = (e) => {
41
+ this.hasIcon = e.target.assignedElements({ flatten: true }).length > 0;
42
+ };
43
+ this.onCaptionSlotChange = (e) => {
44
+ this.hasCaptionSlot = e.target.assignedElements({ flatten: true }).length > 0;
45
+ };
46
+ }
47
+ static { this.styles = [LyraElement.styles, styles]; }
48
+ willUpdate() {
49
+ if (!this.hasUpdated) {
50
+ this.hasIcon = Array.from(this.children).some((el) => !el.hasAttribute('slot'));
51
+ this.hasCaptionSlot = Array.from(this.children).some((el) => el.getAttribute('slot') === 'caption');
52
+ }
53
+ }
54
+ render() {
55
+ const hasTrend = !isNaN(this.trend);
56
+ const rawDirection = this.trend > 0 ? 'up' : this.trend < 0 ? 'down' : 'flat';
57
+ const isGood = rawDirection === 'flat' ? null : rawDirection === this.goodDirection;
58
+ // 2026-07-10 design review, "dashboard-atoms" §lyra-stat: the trend
59
+ // pill rendered literal ▲/▼ glyphs — font-dependent and inconsistent
60
+ // with the rest of the icon set — swapped for the shared chevronIcon(),
61
+ // rotated per direction via CSS on the wrapping [part='trend'].
62
+ const arrow = rawDirection === 'flat' ? '–' : chevronIcon();
63
+ const hasCaption = this.hasCaptionSlot || this.caption.length > 0;
64
+ return html `
65
+ <div part="base">
66
+ <span part="icon" ?hidden=${!this.hasIcon}
67
+ ><slot @slotchange=${this.onIconSlotChange}></slot
68
+ ></span>
69
+ <span part="label">${this.label}</span>
70
+ <div part="value-row">
71
+ <span part="value">${this.value}</span>
72
+ <span part="unit">${this.unit}</span>
73
+ </div>
74
+ ${hasTrend
75
+ ? html `<span
76
+ part="trend"
77
+ data-direction=${rawDirection}
78
+ data-polarity=${isGood == null ? nothing : isGood ? 'good' : 'bad'}
79
+ >
80
+ <span aria-hidden="true">${arrow}</span> ${Math.abs(this.trend)}%
81
+ </span>`
82
+ : nothing}
83
+ <div part="caption" ?hidden=${!hasCaption}>
84
+ <slot name="caption" @slotchange=${this.onCaptionSlotChange}>${this.caption}</slot>
85
+ </div>
86
+ </div>
87
+ `;
88
+ }
89
+ }
90
+ __decorate([
91
+ property()
92
+ ], LyraStat.prototype, "label", void 0);
93
+ __decorate([
94
+ property()
95
+ ], LyraStat.prototype, "value", void 0);
96
+ __decorate([
97
+ property()
98
+ ], LyraStat.prototype, "unit", void 0);
99
+ __decorate([
100
+ property({ reflect: true })
101
+ ], LyraStat.prototype, "variant", void 0);
102
+ __decorate([
103
+ property({ type: Number })
104
+ ], LyraStat.prototype, "trend", void 0);
105
+ __decorate([
106
+ property()
107
+ ], LyraStat.prototype, "caption", void 0);
108
+ __decorate([
109
+ property({ attribute: 'good-direction' })
110
+ ], LyraStat.prototype, "goodDirection", void 0);
111
+ __decorate([
112
+ state()
113
+ ], LyraStat.prototype, "hasIcon", void 0);
114
+ __decorate([
115
+ state()
116
+ ], LyraStat.prototype, "hasCaptionSlot", void 0);
117
+ defineElement('stat', LyraStat);
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,86 @@
1
+ import { css } from 'lit';
2
+ export const styles = css `
3
+ :host {
4
+ display: block;
5
+ }
6
+ [part='base'] {
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: var(--lyra-space-xs);
10
+ padding: var(--lyra-space-m);
11
+ border: 1px solid var(--lyra-color-border);
12
+ border-radius: var(--lyra-radius);
13
+ background: var(--lyra-color-surface);
14
+ }
15
+ [part='icon'] {
16
+ color: var(--lyra-color-text-quiet);
17
+ }
18
+ [part='icon'][hidden] {
19
+ display: none;
20
+ }
21
+ [part='label'] {
22
+ font-size: 0.75rem;
23
+ font-weight: 600;
24
+ text-transform: uppercase;
25
+ letter-spacing: 0.04em;
26
+ color: var(--lyra-color-text-quiet);
27
+ }
28
+ [part='value-row'] {
29
+ display: flex;
30
+ align-items: baseline;
31
+ gap: var(--lyra-space-xs);
32
+ }
33
+ [part='value'] {
34
+ font-size: 1.75rem;
35
+ font-weight: 700;
36
+ font-family: ui-monospace, SFMono-Regular, monospace;
37
+ }
38
+ [part='unit'] {
39
+ font-size: 0.875rem;
40
+ color: var(--lyra-color-text-quiet);
41
+ }
42
+ [part='trend'] {
43
+ display: inline-flex;
44
+ align-items: center;
45
+ gap: var(--lyra-space-xs);
46
+ font-size: 0.8125rem;
47
+ font-weight: 600;
48
+ border-radius: var(--lyra-radius);
49
+ /* 0.05rem/0.4rem don't cleanly map to any --lyra-space-* step (the
50
+ smallest, --lyra-space-xs, is 0.25rem): rounding the vertical value
51
+ up to xs would 5x this chip's padding and blow out the compact
52
+ trend-chip look, so both stay literal here. */
53
+ padding: 0.05rem 0.4rem;
54
+ align-self: flex-start;
55
+ }
56
+ [part='trend'][data-direction='up'] svg {
57
+ transform: rotate(-90deg);
58
+ }
59
+ [part='trend'][data-direction='down'] svg {
60
+ transform: rotate(90deg);
61
+ }
62
+ [part='trend'][data-polarity='good'] {
63
+ color: var(--lyra-color-success);
64
+ background: color-mix(in srgb, var(--lyra-color-success) 8%, transparent);
65
+ }
66
+ [part='trend'][data-polarity='bad'] {
67
+ color: var(--lyra-color-danger);
68
+ background: color-mix(in srgb, var(--lyra-color-danger) 8%, transparent);
69
+ }
70
+ :host([variant='success']) [part='value'] {
71
+ color: var(--lyra-color-success);
72
+ }
73
+ :host([variant='warning']) [part='value'] {
74
+ color: var(--lyra-color-warning);
75
+ }
76
+ :host([variant='danger']) [part='value'] {
77
+ color: var(--lyra-color-danger);
78
+ }
79
+ [part='caption'] {
80
+ font-size: 0.8125rem;
81
+ color: var(--lyra-color-text-quiet);
82
+ }
83
+ [part='caption'][hidden] {
84
+ display: none;
85
+ }
86
+ `;
@@ -0,0 +1,54 @@
1
+ import { type TemplateResult, type PropertyValues } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ import '../empty/empty.js';
4
+ export interface TableColumn<T> {
5
+ key: string;
6
+ label: string;
7
+ sortable?: boolean;
8
+ align?: 'start' | 'end';
9
+ cell: (row: T) => unknown;
10
+ }
11
+ /**
12
+ * `<lyra-table>` — a presentational, sort/select-aware data table.
13
+ *
14
+ * Header/row activation is delegated: one `click` and one `keydown`
15
+ * listener on `<table>` resolve the target via `closest('[data-col-key]'
16
+ * | '[data-row-key]')` and a key→object lookup map, instead of allocating
17
+ * fresh per-column/per-row closures on every render (2026-07-10 audit,
18
+ * "dashboard-atoms" §lyra-table, Medium).
19
+ *
20
+ * @customElement lyra-table
21
+ * @event lyra-sort - A sortable header was activated. `detail: { key }`.
22
+ * @event lyra-row-click - A row was activated. `detail: { row }`.
23
+ * @event lyra-load-more - The "load more" control was activated.
24
+ * @csspart base, table, head, header-cell, row, cell, more-button, sort-icon
25
+ */
26
+ export declare class LyraTable<T = unknown> extends LyraElement {
27
+ static styles: import("lit").CSSResultGroup[];
28
+ columns: TableColumn<T>[];
29
+ rows: T[];
30
+ sortKey: string;
31
+ sortDir: 'asc' | 'desc';
32
+ rowKey?: (row: T) => string | number;
33
+ selectedKey: string | number | null;
34
+ hasMore: boolean;
35
+ moreLabel: string;
36
+ emptyHeading: string;
37
+ emptyDescription: string;
38
+ noColumnsHeading: string;
39
+ noColumnsDescription: string;
40
+ private rowsByKey;
41
+ private columnsByKey;
42
+ private keyOf;
43
+ protected willUpdate(changed: PropertyValues): void;
44
+ private activateColumn;
45
+ private activateRow;
46
+ private onTableClick;
47
+ private onTableKeyDown;
48
+ render(): TemplateResult;
49
+ }
50
+ declare global {
51
+ interface HTMLElementTagNameMap {
52
+ 'lyra-table': LyraTable;
53
+ }
54
+ }
@@ -0,0 +1,201 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { html, nothing } from 'lit';
8
+ import { property } from 'lit/decorators.js';
9
+ import { repeat } from 'lit/directives/repeat.js';
10
+ import { LyraElement } from '../../internal/lyra-element.js';
11
+ import { defineElement } from '../../internal/prefix.js';
12
+ import { styles } from './table.styles.js';
13
+ import { chevronIcon } from '../../internal/icons.js';
14
+ import '../empty/empty.js';
15
+ /**
16
+ * `<lyra-table>` — a presentational, sort/select-aware data table.
17
+ *
18
+ * Header/row activation is delegated: one `click` and one `keydown`
19
+ * listener on `<table>` resolve the target via `closest('[data-col-key]'
20
+ * | '[data-row-key]')` and a key→object lookup map, instead of allocating
21
+ * fresh per-column/per-row closures on every render (2026-07-10 audit,
22
+ * "dashboard-atoms" §lyra-table, Medium).
23
+ *
24
+ * @customElement lyra-table
25
+ * @event lyra-sort - A sortable header was activated. `detail: { key }`.
26
+ * @event lyra-row-click - A row was activated. `detail: { row }`.
27
+ * @event lyra-load-more - The "load more" control was activated.
28
+ * @csspart base, table, head, header-cell, row, cell, more-button, sort-icon
29
+ */
30
+ export class LyraTable extends LyraElement {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.columns = [];
34
+ this.rows = [];
35
+ this.sortKey = '';
36
+ this.sortDir = 'asc';
37
+ this.selectedKey = null;
38
+ this.hasMore = false;
39
+ this.moreLabel = 'Load more';
40
+ this.emptyHeading = 'No data';
41
+ this.emptyDescription = '';
42
+ this.noColumnsHeading = 'No columns configured';
43
+ this.noColumnsDescription = '';
44
+ this.rowsByKey = new Map();
45
+ this.columnsByKey = new Map();
46
+ this.onTableClick = (e) => {
47
+ const target = e.target;
48
+ const th = target.closest('[data-col-key]');
49
+ if (th)
50
+ return this.activateColumn(th.dataset.colKey);
51
+ const tr = target.closest('[data-row-key]');
52
+ if (tr)
53
+ this.activateRow(tr.dataset.rowKey);
54
+ };
55
+ this.onTableKeyDown = (e) => {
56
+ if (e.key !== 'Enter' && e.key !== ' ')
57
+ return;
58
+ const target = e.target;
59
+ const th = target.closest('[data-col-key]');
60
+ if (th) {
61
+ e.preventDefault();
62
+ return this.activateColumn(th.dataset.colKey);
63
+ }
64
+ const tr = target.closest('[data-row-key]');
65
+ if (tr) {
66
+ e.preventDefault();
67
+ this.activateRow(tr.dataset.rowKey);
68
+ }
69
+ };
70
+ }
71
+ static { this.styles = [LyraElement.styles, styles]; }
72
+ keyOf(row, index) {
73
+ return this.rowKey ? this.rowKey(row) : index;
74
+ }
75
+ willUpdate(changed) {
76
+ if (changed.has('rows') || changed.has('rowKey')) {
77
+ this.rowsByKey = new Map(this.rows.map((row, i) => [String(this.keyOf(row, i)), row]));
78
+ }
79
+ if (changed.has('columns')) {
80
+ this.columnsByKey = new Map(this.columns.map((c) => [c.key, c]));
81
+ }
82
+ }
83
+ activateColumn(key) {
84
+ const col = this.columnsByKey.get(key);
85
+ if (col?.sortable)
86
+ this.emit('lyra-sort', { key: col.key });
87
+ }
88
+ activateRow(key) {
89
+ const row = this.rowsByKey.get(key);
90
+ if (row !== undefined)
91
+ this.emit('lyra-row-click', { row });
92
+ }
93
+ render() {
94
+ if (this.columns.length === 0) {
95
+ return html `<lyra-empty
96
+ heading=${this.noColumnsHeading}
97
+ description=${this.noColumnsDescription}
98
+ ></lyra-empty>`;
99
+ }
100
+ if (this.rows.length === 0) {
101
+ return html `<lyra-empty
102
+ heading=${this.emptyHeading}
103
+ description=${this.emptyDescription}
104
+ ></lyra-empty>`;
105
+ }
106
+ return html `
107
+ <div part="base">
108
+ <table part="table" role="grid" @click=${this.onTableClick} @keydown=${this.onTableKeyDown}>
109
+ <thead part="head">
110
+ <tr role="row">
111
+ ${this.columns.map((col) => {
112
+ const active = Boolean(col.sortable) && this.sortKey === col.key;
113
+ const ariaSort = active ? (this.sortDir === 'asc' ? 'ascending' : 'descending') : 'none';
114
+ return html `<th
115
+ part="header-cell"
116
+ role="columnheader"
117
+ scope="col"
118
+ data-col-key=${col.key}
119
+ data-align=${col.align ?? 'start'}
120
+ ?data-sortable=${col.sortable}
121
+ aria-sort=${col.sortable ? ariaSort : nothing}
122
+ tabindex=${col.sortable ? '0' : '-1'}
123
+ >
124
+ ${col.label}
125
+ ${active
126
+ ? html `<span part="sort-icon" data-dir=${this.sortDir} aria-hidden="true"
127
+ >${chevronIcon()}</span
128
+ >`
129
+ : nothing}
130
+ </th>`;
131
+ })}
132
+ </tr>
133
+ </thead>
134
+ <tbody>
135
+ ${repeat(this.rows, (row, i) => this.keyOf(row, i), (row, i) => {
136
+ const key = this.keyOf(row, i);
137
+ const selected = this.selectedKey !== null && this.selectedKey === key;
138
+ return html `<tr
139
+ part="row"
140
+ role="row"
141
+ data-row-key=${String(key)}
142
+ aria-selected=${selected ? 'true' : 'false'}
143
+ tabindex="0"
144
+ >
145
+ ${this.columns.map((col) => html `<td part="cell" role="gridcell" data-align=${col.align ?? 'start'}>
146
+ ${col.cell(row)}
147
+ </td>`)}
148
+ </tr>`;
149
+ })}
150
+ </tbody>
151
+ </table>
152
+ ${this.hasMore
153
+ ? html `<button
154
+ part="more-button"
155
+ type="button"
156
+ @click=${() => this.emit('lyra-load-more')}
157
+ >
158
+ ${this.moreLabel}
159
+ </button>`
160
+ : nothing}
161
+ </div>
162
+ `;
163
+ }
164
+ }
165
+ __decorate([
166
+ property({ attribute: false })
167
+ ], LyraTable.prototype, "columns", void 0);
168
+ __decorate([
169
+ property({ attribute: false })
170
+ ], LyraTable.prototype, "rows", void 0);
171
+ __decorate([
172
+ property({ attribute: 'sort-key' })
173
+ ], LyraTable.prototype, "sortKey", void 0);
174
+ __decorate([
175
+ property({ attribute: 'sort-dir' })
176
+ ], LyraTable.prototype, "sortDir", void 0);
177
+ __decorate([
178
+ property({ attribute: false })
179
+ ], LyraTable.prototype, "rowKey", void 0);
180
+ __decorate([
181
+ property({ attribute: false })
182
+ ], LyraTable.prototype, "selectedKey", void 0);
183
+ __decorate([
184
+ property({ type: Boolean, attribute: 'has-more', reflect: true })
185
+ ], LyraTable.prototype, "hasMore", void 0);
186
+ __decorate([
187
+ property({ attribute: 'more-label' })
188
+ ], LyraTable.prototype, "moreLabel", void 0);
189
+ __decorate([
190
+ property({ attribute: 'empty-heading' })
191
+ ], LyraTable.prototype, "emptyHeading", void 0);
192
+ __decorate([
193
+ property({ attribute: 'empty-description' })
194
+ ], LyraTable.prototype, "emptyDescription", void 0);
195
+ __decorate([
196
+ property({ attribute: 'no-columns-heading' })
197
+ ], LyraTable.prototype, "noColumnsHeading", void 0);
198
+ __decorate([
199
+ property({ attribute: 'no-columns-description' })
200
+ ], LyraTable.prototype, "noColumnsDescription", void 0);
201
+ defineElement('table', LyraTable);
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,98 @@
1
+ import { css } from 'lit';
2
+ export const styles = css `
3
+ :host {
4
+ display: block;
5
+ inline-size: 100%;
6
+ }
7
+ [part='base'] {
8
+ overflow: auto;
9
+ max-block-size: var(--lyra-table-max-height, none);
10
+ border: 1px solid var(--lyra-color-border);
11
+ border-radius: var(--lyra-radius);
12
+ }
13
+ [part='table'] {
14
+ inline-size: 100%;
15
+ border-collapse: collapse;
16
+ font-size: 0.875rem;
17
+ }
18
+ [part='header-cell'] {
19
+ position: sticky;
20
+ inset-block-start: 0;
21
+ background: var(--lyra-color-surface);
22
+ text-align: start;
23
+ font-weight: 600;
24
+ padding: var(--lyra-space-s);
25
+ border-block-end: 1px solid var(--lyra-color-border);
26
+ cursor: default;
27
+ white-space: nowrap;
28
+ }
29
+ [part='header-cell'][aria-sort]:not([aria-sort='none']),
30
+ [part='header-cell'][data-sortable] {
31
+ cursor: pointer;
32
+ }
33
+ [part='header-cell'][data-sortable]:hover {
34
+ background: var(--lyra-color-brand-quiet);
35
+ }
36
+ [part='header-cell'][data-sortable]:focus-visible {
37
+ outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
38
+ outline-offset: var(--lyra-focus-ring-offset);
39
+ }
40
+ [part='header-cell'][data-align='end'] {
41
+ text-align: end;
42
+ }
43
+ [part='sort-icon'] {
44
+ display: inline-block;
45
+ margin-inline-start: var(--lyra-space-xs);
46
+ vertical-align: middle;
47
+ transition: transform var(--lyra-transition-fast);
48
+ }
49
+ [part='sort-icon'] svg {
50
+ display: block;
51
+ }
52
+ /* Rotate the wrapping part element, not the svg — internal/icons.ts's
53
+ documented contract ("callers ... rotate the wrapping part element via
54
+ CSS transform: rotate(...), not the svg"). Design-review finding on
55
+ Task 3 (lyra-table sort indicator): this previously rotated the inner
56
+ <svg> directly. */
57
+ [part='sort-icon'][data-dir='asc'] {
58
+ transform: rotate(-90deg);
59
+ }
60
+ [part='sort-icon'][data-dir='desc'] {
61
+ transform: rotate(90deg);
62
+ }
63
+ [part='row']:hover {
64
+ background: var(--lyra-color-brand-quiet);
65
+ }
66
+ [part='row'][aria-selected='true'] {
67
+ background: var(--lyra-color-brand-quiet);
68
+ }
69
+ [part='row']:focus-visible {
70
+ outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
71
+ outline-offset: var(--lyra-focus-ring-offset);
72
+ }
73
+ [part='cell'] {
74
+ padding: var(--lyra-space-s);
75
+ border-block-end: 1px solid var(--lyra-color-border);
76
+ }
77
+ [part='cell'][data-align='end'] {
78
+ text-align: end;
79
+ }
80
+ [part='more-button'] {
81
+ display: block;
82
+ inline-size: 100%;
83
+ padding: var(--lyra-space-s);
84
+ border: none;
85
+ background: none;
86
+ color: var(--lyra-color-brand);
87
+ font: inherit;
88
+ cursor: pointer;
89
+ border-block-start: 1px solid var(--lyra-color-border);
90
+ }
91
+ [part='more-button']:hover {
92
+ background: var(--lyra-color-brand-quiet);
93
+ }
94
+ [part='more-button']:focus-visible {
95
+ outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
96
+ outline-offset: var(--lyra-focus-ring-offset);
97
+ }
98
+ `;
@@ -0,0 +1,40 @@
1
+ import { type TemplateResult, type PropertyValues } from 'lit';
2
+ import { LyraElement } from '../../internal/lyra-element.js';
3
+ /**
4
+ * `<lyra-time-range>` — a two-handle brush/scrubber over a numeric domain.
5
+ * Callers map their own time axis to `[min, max]`; no date logic lives here
6
+ * (matches the no-date-library constraint used elsewhere in this library).
7
+ *
8
+ * @customElement lyra-time-range
9
+ * @event lyra-input - Fired continuously while dragging. `detail: { start, end }`.
10
+ * @event lyra-change - Fired on release / keyup-commit. `detail: { start, end }`.
11
+ * @csspart base, track, range, handle-start, handle-end
12
+ */
13
+ export declare class LyraTimeRange extends LyraElement {
14
+ static styles: import("lit").CSSResultGroup[];
15
+ min: number;
16
+ max: number;
17
+ start: number;
18
+ end: number;
19
+ step: number;
20
+ disabled: boolean;
21
+ private dragging;
22
+ disconnectedCallback(): void;
23
+ private percentOf;
24
+ private clamp;
25
+ private setValue;
26
+ private onKeyDown;
27
+ private onKeyUp;
28
+ private onPointerDown;
29
+ private onPointerMove;
30
+ private onPointerUp;
31
+ /** Stop an in-progress drag, optionally committing a final lyra-change. */
32
+ private endDrag;
33
+ protected willUpdate(changed: PropertyValues): void;
34
+ render(): TemplateResult;
35
+ }
36
+ declare global {
37
+ interface HTMLElementTagNameMap {
38
+ 'lyra-time-range': LyraTimeRange;
39
+ }
40
+ }