@aceshooting/lyra-ui 1.0.1 → 1.1.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 (148) hide show
  1. package/README.md +8 -9
  2. package/custom-elements.json +12479 -6535
  3. package/dist/components/chart/chart.d.ts +46 -1
  4. package/dist/components/chart/chart.js +148 -10
  5. package/dist/components/chart/chart.stories.d.ts +32 -0
  6. package/dist/components/chart/chart.stories.js +141 -0
  7. package/dist/components/chart/chart.styles.js +16 -0
  8. package/dist/components/chart/histogram.d.ts +2 -0
  9. package/dist/components/chart/histogram.js +18 -2
  10. package/dist/components/chart/line-chart.stories.d.ts +17 -0
  11. package/dist/components/chart/line-chart.stories.js +75 -0
  12. package/dist/components/chart/lite-chart.d.ts +74 -0
  13. package/dist/components/chart/lite-chart.js +384 -0
  14. package/dist/components/chart/lite-chart.stories.d.ts +12 -0
  15. package/dist/components/chart/lite-chart.stories.js +98 -0
  16. package/dist/components/chart/lite-chart.styles.d.ts +1 -0
  17. package/dist/components/chart/lite-chart.styles.js +83 -0
  18. package/dist/components/combobox/combobox.d.ts +9 -0
  19. package/dist/components/combobox/combobox.js +75 -12
  20. package/dist/components/combobox/combobox.stories.d.ts +18 -0
  21. package/dist/components/combobox/combobox.stories.js +75 -0
  22. package/dist/components/date-picker/calendar-core.js +13 -2
  23. package/dist/components/date-picker/date-input.d.ts +9 -1
  24. package/dist/components/date-picker/date-input.js +55 -7
  25. package/dist/components/date-picker/date-input.stories.d.ts +5 -0
  26. package/dist/components/date-picker/date-input.stories.js +36 -0
  27. package/dist/components/date-picker/date-picker.d.ts +11 -3
  28. package/dist/components/date-picker/date-picker.js +87 -37
  29. package/dist/components/date-picker/date-picker.stories.d.ts +4 -0
  30. package/dist/components/date-picker/date-picker.stories.js +12 -0
  31. package/dist/components/date-picker/date-picker.styles.js +1 -1
  32. package/dist/components/empty/empty.d.ts +12 -1
  33. package/dist/components/empty/empty.js +20 -3
  34. package/dist/components/empty/empty.stories.d.ts +2 -0
  35. package/dist/components/empty/empty.stories.js +20 -0
  36. package/dist/components/empty/empty.styles.js +11 -0
  37. package/dist/components/export-button/csv.js +6 -2
  38. package/dist/components/export-button/export-button.d.ts +15 -1
  39. package/dist/components/export-button/export-button.js +89 -12
  40. package/dist/components/export-button/export-button.stories.d.ts +1 -0
  41. package/dist/components/export-button/export-button.stories.js +5 -0
  42. package/dist/components/export-button/export-button.styles.js +11 -3
  43. package/dist/components/file-input/accept.d.ts +8 -2
  44. package/dist/components/file-input/accept.js +9 -3
  45. package/dist/components/file-input/file-input.d.ts +5 -2
  46. package/dist/components/file-input/file-input.js +23 -8
  47. package/dist/components/file-input/file-input.stories.d.ts +2 -0
  48. package/dist/components/file-input/file-input.stories.js +9 -0
  49. package/dist/components/flag/flag.d.ts +48 -3
  50. package/dist/components/flag/flag.js +65 -29
  51. package/dist/components/flag/flag.styles.js +1 -1
  52. package/dist/components/flag/language-map.d.ts +11 -0
  53. package/dist/components/flag/language-map.js +12 -1
  54. package/dist/components/gauge/gauge.d.ts +2 -0
  55. package/dist/components/gauge/gauge.js +19 -5
  56. package/dist/components/gauge/gauge.stories.d.ts +2 -0
  57. package/dist/components/gauge/gauge.stories.js +6 -0
  58. package/dist/components/graph/graph-loader.d.ts +28 -0
  59. package/dist/components/graph/graph-loader.js +44 -0
  60. package/dist/components/graph/graph.d.ts +41 -15
  61. package/dist/components/graph/graph.js +146 -71
  62. package/dist/components/graph/graph.stories.d.ts +3 -0
  63. package/dist/components/graph/graph.stories.js +38 -0
  64. package/dist/components/graph/graph.styles.js +6 -1
  65. package/dist/components/heatmap/calendar-grid.d.ts +7 -0
  66. package/dist/components/heatmap/calendar-grid.js +27 -10
  67. package/dist/components/heatmap/heatmap.d.ts +137 -12
  68. package/dist/components/heatmap/heatmap.js +427 -27
  69. package/dist/components/heatmap/heatmap.stories.d.ts +17 -0
  70. package/dist/components/heatmap/heatmap.stories.js +130 -0
  71. package/dist/components/heatmap/heatmap.styles.js +60 -2
  72. package/dist/components/map/map.d.ts +17 -1
  73. package/dist/components/map/map.js +68 -21
  74. package/dist/components/map/map.stories.d.ts +10 -0
  75. package/dist/components/map/map.stories.js +107 -12
  76. package/dist/components/map/map.styles.js +2 -2
  77. package/dist/components/playback/playback.d.ts +6 -2
  78. package/dist/components/playback/playback.js +36 -12
  79. package/dist/components/playback/playback.stories.d.ts +2 -0
  80. package/dist/components/playback/playback.stories.js +6 -0
  81. package/dist/components/playback/playback.styles.js +10 -2
  82. package/dist/components/select/select.d.ts +122 -0
  83. package/dist/components/select/select.js +516 -0
  84. package/dist/components/select/select.stories.d.ts +19 -0
  85. package/dist/components/select/select.stories.js +75 -0
  86. package/dist/components/select/select.styles.d.ts +1 -0
  87. package/dist/components/select/select.styles.js +188 -0
  88. package/dist/components/skeleton/skeleton.js +4 -7
  89. package/dist/components/sparkline/sparkline.d.ts +1 -1
  90. package/dist/components/sparkline/sparkline.js +40 -13
  91. package/dist/components/sparkline/sparkline.stories.d.ts +4 -0
  92. package/dist/components/sparkline/sparkline.stories.js +22 -0
  93. package/dist/components/split/split.d.ts +24 -3
  94. package/dist/components/split/split.js +155 -31
  95. package/dist/components/split/split.stories.d.ts +2 -0
  96. package/dist/components/split/split.stories.js +20 -0
  97. package/dist/components/stat/stat.d.ts +27 -1
  98. package/dist/components/stat/stat.js +56 -3
  99. package/dist/components/stat/stat.stories.d.ts +3 -0
  100. package/dist/components/stat/stat.stories.js +49 -1
  101. package/dist/components/stat/stat.styles.js +44 -0
  102. package/dist/components/table/table.d.ts +64 -2
  103. package/dist/components/table/table.js +213 -17
  104. package/dist/components/table/table.stories.d.ts +6 -0
  105. package/dist/components/table/table.stories.js +45 -0
  106. package/dist/components/table/table.styles.js +51 -4
  107. package/dist/components/time-range/time-range.d.ts +34 -5
  108. package/dist/components/time-range/time-range.js +217 -36
  109. package/dist/components/time-range/time-range.stories.d.ts +2 -0
  110. package/dist/components/time-range/time-range.stories.js +13 -0
  111. package/dist/components/time-range/time-range.styles.js +44 -2
  112. package/dist/components/toast/toast-item.d.ts +14 -2
  113. package/dist/components/toast/toast-item.js +134 -17
  114. package/dist/components/toast/toast-item.styles.js +28 -2
  115. package/dist/components/toast/toast.stories.d.ts +3 -0
  116. package/dist/components/toast/toast.stories.js +41 -1
  117. package/dist/components/toast/toast.styles.js +11 -11
  118. package/dist/components/toast/toaster.d.ts +1 -1
  119. package/dist/components/toast/toaster.js +13 -6
  120. package/dist/components/tree/tree-node.d.ts +13 -7
  121. package/dist/components/tree/tree-node.js +24 -10
  122. package/dist/components/tree/tree.d.ts +64 -15
  123. package/dist/components/tree/tree.js +113 -22
  124. package/dist/components/tree/tree.stories.d.ts +4 -0
  125. package/dist/components/tree/tree.stories.js +31 -1
  126. package/dist/components/tree/update-cascade.d.ts +13 -0
  127. package/dist/components/tree/update-cascade.js +13 -0
  128. package/dist/components/widget/widget.d.ts +15 -1
  129. package/dist/components/widget/widget.js +140 -5
  130. package/dist/components/widget/widget.stories.d.ts +1 -0
  131. package/dist/components/widget/widget.stories.js +10 -0
  132. package/dist/components/widget/widget.styles.js +11 -1
  133. package/dist/components/word-cloud/word-cloud-layout.d.ts +63 -0
  134. package/dist/components/word-cloud/word-cloud-layout.js +145 -0
  135. package/dist/components/word-cloud/word-cloud.d.ts +65 -0
  136. package/dist/components/word-cloud/word-cloud.js +255 -0
  137. package/dist/components/word-cloud/word-cloud.stories.d.ts +10 -0
  138. package/dist/components/word-cloud/word-cloud.stories.js +55 -0
  139. package/dist/components/word-cloud/word-cloud.styles.d.ts +1 -0
  140. package/dist/components/word-cloud/word-cloud.styles.js +54 -0
  141. package/dist/internal/a11y.js +2 -1
  142. package/dist/internal/rtl.d.ts +7 -0
  143. package/dist/internal/rtl.js +9 -0
  144. package/dist/lyra.d.ts +9 -1
  145. package/dist/lyra.js +6 -0
  146. package/llms-full.txt +892 -276
  147. package/llms.txt +12 -9
  148. package/package.json +6 -4
@@ -0,0 +1,255 @@
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, svg } from 'lit';
8
+ import { property, query, state } from 'lit/decorators.js';
9
+ import { LyraElement } from '../../internal/lyra-element.js';
10
+ import { defineElement } from '../../internal/prefix.js';
11
+ import { srOnly } from '../../internal/a11y.js';
12
+ import { layoutWordCloud, MAX_WORDS } from './word-cloud-layout.js';
13
+ import { styles } from './word-cloud.styles.js';
14
+ const DEFAULT_MIN_FONT_SIZE = 12;
15
+ const DEFAULT_MAX_FONT_SIZE = 48;
16
+ const PALETTE_SIZE = 8;
17
+ const FALLBACK_PALETTE = ['#0969da', '#1a7f37', '#9a6700', '#cf222e', '#8250df', '#bf3989', '#0a7d91', '#57606a'];
18
+ /** Must match `[part='word']`'s `font-weight` in word-cloud.styles.ts -- canvas
19
+ * measures at `normal` weight by default, which is narrower than the actually
20
+ * rendered bold glyphs, so the spiral layout's collision boxes would end up
21
+ * too small versus what's actually painted if this ever drifts out of sync. */
22
+ const WORD_FONT_WEIGHT = 600;
23
+ const NAV_KEYS = new Set(['ArrowRight', 'ArrowDown', 'ArrowLeft', 'ArrowUp', 'Home', 'End']);
24
+ /** Padding, in px, between a focused word's glyph box and its drawn focus ring. */
25
+ const FOCUS_RING_PAD = 2;
26
+ let scratchCtx;
27
+ /** A detached canvas used solely to measure rendered text width for layout. */
28
+ function getScratchCtx() {
29
+ if (scratchCtx === undefined) {
30
+ scratchCtx = document.createElement('canvas').getContext('2d');
31
+ }
32
+ return scratchCtx;
33
+ }
34
+ const warnedSkipCounts = new Set();
35
+ function warnSkippedWords(count) {
36
+ if (warnedSkipCounts.has(count))
37
+ return;
38
+ warnedSkipCounts.add(count);
39
+ console.warn(`<lyra-word-cloud> could not place ${count} word(s) (blank text, over the ${MAX_WORDS}-word cap, or ` +
40
+ 'the layout search was exhausted) -- they were dropped, not rendered.');
41
+ }
42
+ /**
43
+ * `<lyra-word-cloud>` — a zero-dependency SVG word/tag cloud. First-party
44
+ * invention (no Web Awesome equivalent). Each word's rendered size is scaled
45
+ * from its `weight` and placed via an outward Archimedean-spiral search (the
46
+ * standard word-cloud heuristic: heaviest words placed first, each one
47
+ * spiraling out from the center until it clears every word already placed).
48
+ *
49
+ * Unlike sibling `lyra-sparkline`/`lyra-heatmap` (one `role="img"` glyph
50
+ * standing in for an aggregate value), the individual words here are the
51
+ * meaningful interactive content — but with up to `MAX_WORDS` of them, making
52
+ * every single one its own tab stop would be a poor keyboard experience.
53
+ * Instead, like `lyra-heatmap`'s cells, the whole `[part="svg"]` is one tab
54
+ * stop with roving arrow-key focus (Home/End jump to the first/last word,
55
+ * Enter/Space activates the focused one), a drawn `[part="focus-ring"]`, and
56
+ * a visually-hidden `aria-live="polite"` status announcement.
57
+ *
58
+ * @customElement lyra-word-cloud
59
+ * @event lyra-word-click - Fired on click, or Enter/Space on the focused word.
60
+ * `detail: { text, weight, group }`.
61
+ * @csspart base, svg, word, focus-ring, live-region, empty
62
+ */
63
+ export class LyraWordCloud extends LyraElement {
64
+ constructor() {
65
+ super(...arguments);
66
+ /** The words to lay out. Re-laid-out whenever this (or a sizing property) changes. */
67
+ this.words = [];
68
+ /** Font size, in px, for the lowest-weight word. */
69
+ this.minFontSize = DEFAULT_MIN_FONT_SIZE;
70
+ /** Font size, in px, for the highest-weight word. */
71
+ this.maxFontSize = DEFAULT_MAX_FONT_SIZE;
72
+ /** `sqrt` compresses the weight->font-size mapping so one heavy word doesn't dwarf the rest. */
73
+ this.scale = 'linear';
74
+ /** `mixed` lets some words render rotated 90° for denser packing. */
75
+ this.orientations = 'horizontal';
76
+ this.cachedLayout = { placed: [], skipped: [], width: 0, height: 0 };
77
+ /** Roving-focus cursor -- an index into `navOrder()`, not into `cachedLayout.placed`. */
78
+ this.focusedIndex = null;
79
+ /** Text of the visually-hidden `aria-live="polite"` status announcement. */
80
+ this.liveText = '';
81
+ this.measureText = (text, fontSize) => {
82
+ const ctx = getScratchCtx();
83
+ if (!ctx)
84
+ return text.length * fontSize * 0.6;
85
+ ctx.font = `${WORD_FONT_WEIGHT} ${fontSize}px ${this.fontFamily()}`;
86
+ return ctx.measureText(text).width;
87
+ };
88
+ this.onWordClick = (word) => {
89
+ const order = this.navOrder();
90
+ const idx = order.findIndex((w) => w.originalIndex === word.originalIndex);
91
+ this.focusedIndex = idx === -1 ? null : idx;
92
+ if (idx !== -1)
93
+ this.announce(order[idx]);
94
+ this.svgEl?.focus();
95
+ this.activate(word);
96
+ };
97
+ this.onKeyDown = (e) => {
98
+ const order = this.navOrder();
99
+ if (order.length === 0)
100
+ return;
101
+ if (e.key === 'Enter' || e.key === ' ') {
102
+ e.preventDefault();
103
+ if (this.focusedIndex !== null)
104
+ this.activate(order[this.focusedIndex]);
105
+ return;
106
+ }
107
+ if (!NAV_KEYS.has(e.key))
108
+ return;
109
+ e.preventDefault();
110
+ if (this.focusedIndex === null) {
111
+ this.focusedIndex = 0;
112
+ this.announce(order[0]);
113
+ return;
114
+ }
115
+ let next = this.focusedIndex;
116
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown')
117
+ next = Math.min(order.length - 1, this.focusedIndex + 1);
118
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp')
119
+ next = Math.max(0, this.focusedIndex - 1);
120
+ else if (e.key === 'Home')
121
+ next = 0;
122
+ else if (e.key === 'End')
123
+ next = order.length - 1;
124
+ this.focusedIndex = next;
125
+ this.announce(order[next]);
126
+ };
127
+ }
128
+ static { this.styles = [LyraElement.styles, styles, srOnly]; }
129
+ fontFamily() {
130
+ return getComputedStyle(this).getPropertyValue('--lyra-font').trim() || 'sans-serif';
131
+ }
132
+ paletteColors() {
133
+ if (this.palette?.length)
134
+ return this.palette;
135
+ const cs = getComputedStyle(this);
136
+ const colors = [];
137
+ for (let i = 0; i < PALETTE_SIZE; i++) {
138
+ colors.push(cs.getPropertyValue(`--lyra-word-cloud-color-${i + 1}`).trim() || FALLBACK_PALETTE[i]);
139
+ }
140
+ return colors;
141
+ }
142
+ /** Stable keyboard tab order -- the order words were declared in `words`,
143
+ * independent of the weight-sorted placement order. */
144
+ navOrder() {
145
+ return [...this.cachedLayout.placed].sort((a, b) => a.originalIndex - b.originalIndex);
146
+ }
147
+ willUpdate(changed) {
148
+ if (changed.has('words') ||
149
+ changed.has('minFontSize') ||
150
+ changed.has('maxFontSize') ||
151
+ changed.has('scale') ||
152
+ changed.has('orientations')) {
153
+ this.cachedLayout = layoutWordCloud(this.words, {
154
+ minFontSize: this.minFontSize,
155
+ maxFontSize: this.maxFontSize,
156
+ scale: this.scale,
157
+ orientations: this.orientations,
158
+ measureText: this.measureText,
159
+ });
160
+ if (this.cachedLayout.skipped.length > 0)
161
+ warnSkippedWords(this.cachedLayout.skipped.length);
162
+ // The previous focus cursor may no longer address a real word once the
163
+ // data changes out from under it.
164
+ this.focusedIndex = null;
165
+ this.liveText = '';
166
+ }
167
+ this.setAttribute('role', 'group');
168
+ this.setAttribute('aria-label', `Word cloud of ${this.words.length} word${this.words.length === 1 ? '' : 's'}`);
169
+ }
170
+ activate(word) {
171
+ this.emit('lyra-word-click', { text: word.text, weight: word.weight, group: word.group });
172
+ }
173
+ announce(word) {
174
+ this.liveText = `${word.text}, ${word.weight}`;
175
+ }
176
+ /** Axis-aligned focus-ring rect for `w`, already accounting for its rotation. */
177
+ focusRingRect(w) {
178
+ const boxW = w.rotated ? w.height : w.width;
179
+ const boxH = w.rotated ? w.width : w.height;
180
+ return {
181
+ x: w.x - boxW / 2 - FOCUS_RING_PAD,
182
+ y: w.y - boxH / 2 - FOCUS_RING_PAD,
183
+ width: boxW + 2 * FOCUS_RING_PAD,
184
+ height: boxH + 2 * FOCUS_RING_PAD,
185
+ };
186
+ }
187
+ render() {
188
+ const layout = this.cachedLayout;
189
+ if (layout.placed.length === 0) {
190
+ return html `<div part="base"><div part="empty">No data</div></div>`;
191
+ }
192
+ const colors = this.paletteColors();
193
+ const groupColor = new Map();
194
+ const colorFor = (word) => {
195
+ if (word.color)
196
+ return word.color;
197
+ if (word.group) {
198
+ if (!groupColor.has(word.group))
199
+ groupColor.set(word.group, colors[groupColor.size % colors.length]);
200
+ return groupColor.get(word.group);
201
+ }
202
+ return colors[word.originalIndex % colors.length];
203
+ };
204
+ const order = this.navOrder();
205
+ const focused = this.focusedIndex !== null ? order[this.focusedIndex] : undefined;
206
+ const ring = focused ? this.focusRingRect(focused) : undefined;
207
+ return html `
208
+ <div part="base">
209
+ <svg part="svg" tabindex="0" viewBox="0 0 ${layout.width} ${layout.height}" @keydown=${this.onKeyDown}>
210
+ ${layout.placed.map((w) => svg `<text
211
+ part="word"
212
+ x=${w.x}
213
+ y=${w.y}
214
+ font-size=${w.fontSize}
215
+ fill=${colorFor(w)}
216
+ transform=${w.rotated ? `rotate(-90, ${w.x}, ${w.y})` : nothing}
217
+ @click=${() => this.onWordClick(w)}
218
+ >${w.text}</text>`)}
219
+ ${ring
220
+ ? svg `<rect part="focus-ring" x=${ring.x} y=${ring.y} width=${ring.width} height=${ring.height}></rect>`
221
+ : ''}
222
+ </svg>
223
+ <div part="live-region" class="sr-only" role="status" aria-live="polite">${this.liveText}</div>
224
+ </div>
225
+ `;
226
+ }
227
+ }
228
+ __decorate([
229
+ property({ attribute: false })
230
+ ], LyraWordCloud.prototype, "words", void 0);
231
+ __decorate([
232
+ property({ attribute: 'min-font-size', type: Number })
233
+ ], LyraWordCloud.prototype, "minFontSize", void 0);
234
+ __decorate([
235
+ property({ attribute: 'max-font-size', type: Number })
236
+ ], LyraWordCloud.prototype, "maxFontSize", void 0);
237
+ __decorate([
238
+ property()
239
+ ], LyraWordCloud.prototype, "scale", void 0);
240
+ __decorate([
241
+ property()
242
+ ], LyraWordCloud.prototype, "orientations", void 0);
243
+ __decorate([
244
+ property({ attribute: false })
245
+ ], LyraWordCloud.prototype, "palette", void 0);
246
+ __decorate([
247
+ query('[part="svg"]')
248
+ ], LyraWordCloud.prototype, "svgEl", void 0);
249
+ __decorate([
250
+ state()
251
+ ], LyraWordCloud.prototype, "focusedIndex", void 0);
252
+ __decorate([
253
+ state()
254
+ ], LyraWordCloud.prototype, "liveText", void 0);
255
+ defineElement('word-cloud', LyraWordCloud);
@@ -0,0 +1,10 @@
1
+ import type { Meta, StoryObj } from '@storybook/web-components-vite';
2
+ declare const meta: Meta;
3
+ export default meta;
4
+ type Story = StoryObj;
5
+ export declare const Default: Story;
6
+ export declare const SqrtScale: Story;
7
+ export declare const MixedOrientation: Story;
8
+ export declare const GroupedColors: Story;
9
+ export declare const CustomPalette: Story;
10
+ export declare const Empty: Story;
@@ -0,0 +1,55 @@
1
+ import { html } from 'lit';
2
+ const meta = {
3
+ title: 'WordCloud',
4
+ component: 'lyra-word-cloud',
5
+ tags: ['autodocs'],
6
+ };
7
+ export default meta;
8
+ const WORDS = [
9
+ { text: 'JavaScript', weight: 90 },
10
+ { text: 'TypeScript', weight: 75 },
11
+ { text: 'Lit', weight: 60 },
12
+ { text: 'Web Components', weight: 55 },
13
+ { text: 'HTML', weight: 40 },
14
+ { text: 'CSS', weight: 38 },
15
+ { text: 'Shadow DOM', weight: 25 },
16
+ { text: 'Custom Elements', weight: 22 },
17
+ { text: 'Accessibility', weight: 18 },
18
+ { text: 'SVG', weight: 15 },
19
+ { text: 'Design Tokens', weight: 12 },
20
+ { text: 'Testing', weight: 10 },
21
+ ];
22
+ export const Default = {
23
+ render: () => html `<lyra-word-cloud .words=${WORDS} style="height: 20rem"></lyra-word-cloud>`,
24
+ };
25
+ export const SqrtScale = {
26
+ render: () => html `<lyra-word-cloud .words=${WORDS} scale="sqrt" style="height: 20rem"></lyra-word-cloud>`,
27
+ };
28
+ export const MixedOrientation = {
29
+ render: () => html `<lyra-word-cloud .words=${WORDS} orientations="mixed" style="height: 20rem"></lyra-word-cloud>`,
30
+ };
31
+ export const GroupedColors = {
32
+ render: () => html `<lyra-word-cloud
33
+ style="height: 20rem"
34
+ .words=${[
35
+ { text: 'React', weight: 80, group: 'framework' },
36
+ { text: 'Vue', weight: 60, group: 'framework' },
37
+ { text: 'Svelte', weight: 40, group: 'framework' },
38
+ { text: 'Jest', weight: 55, group: 'testing' },
39
+ { text: 'Playwright', weight: 45, group: 'testing' },
40
+ { text: 'Vitest', weight: 30, group: 'testing' },
41
+ { text: 'Vite', weight: 50, group: 'tooling' },
42
+ { text: 'esbuild', weight: 20, group: 'tooling' },
43
+ ]}
44
+ ></lyra-word-cloud>`,
45
+ };
46
+ export const CustomPalette = {
47
+ render: () => html `<lyra-word-cloud
48
+ style="height: 20rem"
49
+ .words=${WORDS}
50
+ .palette=${['#e63946', '#f1a208', '#2a9d8f', '#264653']}
51
+ ></lyra-word-cloud>`,
52
+ };
53
+ export const Empty = {
54
+ render: () => html `<lyra-word-cloud style="height: 10rem"></lyra-word-cloud>`,
55
+ };
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,54 @@
1
+ import { css } from 'lit';
2
+ export const styles = css `
3
+ :host {
4
+ display: block;
5
+ /* Categorical palette for words with no explicit color/group, cycled by
6
+ index (and reused per-group once a group's first color is assigned).
7
+ Data-driven literals, same rationale as lyra-heatmap's
8
+ --lyra-heatmap-scale-lo/-hi -- exposed as retheme-able custom
9
+ properties instead of hardcoded in word-cloud.ts. */
10
+ --lyra-word-cloud-color-1: #0969da;
11
+ --lyra-word-cloud-color-2: #1a7f37;
12
+ --lyra-word-cloud-color-3: #9a6700;
13
+ --lyra-word-cloud-color-4: #cf222e;
14
+ --lyra-word-cloud-color-5: #8250df;
15
+ --lyra-word-cloud-color-6: #bf3989;
16
+ --lyra-word-cloud-color-7: #0a7d91;
17
+ --lyra-word-cloud-color-8: #57606a;
18
+ }
19
+ [part='base'] {
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: var(--lyra-space-xs);
23
+ }
24
+ svg {
25
+ display: block;
26
+ inline-size: 100%;
27
+ block-size: 100%;
28
+ }
29
+ svg:focus-visible {
30
+ outline: var(--lyra-focus-ring-width) solid var(--lyra-focus-ring-color);
31
+ outline-offset: var(--lyra-focus-ring-offset);
32
+ }
33
+ [part='word'] {
34
+ cursor: pointer;
35
+ font-family: var(--lyra-font);
36
+ font-weight: 600;
37
+ text-anchor: middle;
38
+ dominant-baseline: middle;
39
+ transition: opacity var(--lyra-transition-fast);
40
+ }
41
+ [part='word']:hover {
42
+ opacity: 0.75;
43
+ }
44
+ [part='focus-ring'] {
45
+ fill: none;
46
+ stroke: var(--lyra-focus-ring-color);
47
+ stroke-width: 2px;
48
+ pointer-events: none;
49
+ }
50
+ [part='empty'] {
51
+ color: var(--lyra-color-text-quiet);
52
+ font-size: 0.875rem;
53
+ }
54
+ `;
@@ -1,7 +1,8 @@
1
1
  import { css } from 'lit';
2
+ import { tag } from './prefix.js';
2
3
  let counter = 0;
3
4
  /** Monotonic unique id, scoped by a short label (e.g. `nextId('listbox')`). */
4
- export const nextId = (scope) => `lyra-${scope}-${++counter}`;
5
+ export const nextId = (scope) => `${tag(scope)}-${++counter}`;
5
6
  /** Visually-hidden-but-screen-reader-available helper class. */
6
7
  export const srOnly = css `
7
8
  .sr-only {
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Whether `el` resolves to RTL text direction. Logical CSS properties
3
+ * (`inset-inline-start`, etc.) already flip visual layout for free, but
4
+ * pointer/keyboard math that reasons about physical left/right (drag ratios,
5
+ * arrow-key direction) has to consult this explicitly to match.
6
+ */
7
+ export declare function isRtl(el: Element): boolean;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Whether `el` resolves to RTL text direction. Logical CSS properties
3
+ * (`inset-inline-start`, etc.) already flip visual layout for free, but
4
+ * pointer/keyboard math that reasons about physical left/right (drag ratios,
5
+ * arrow-key direction) has to consult this explicitly to match.
6
+ */
7
+ export function isRtl(el) {
8
+ return getComputedStyle(el).direction === 'rtl';
9
+ }
package/dist/lyra.d.ts CHANGED
@@ -3,6 +3,7 @@ import './components/toast/toast.js';
3
3
  import './components/toast/toast-item.js';
4
4
  import './components/combobox/combobox.js';
5
5
  import './components/combobox/option.js';
6
+ import './components/select/select.js';
6
7
  import './components/date-picker/date-picker.js';
7
8
  import './components/date-picker/date-input.js';
8
9
  import './components/flag/flag.js';
@@ -20,6 +21,7 @@ import './components/graph/graph.js';
20
21
  import './components/tree/tree.js';
21
22
  import './components/tree/tree-node.js';
22
23
  import './components/chart/chart.js';
24
+ import './components/chart/lite-chart.js';
23
25
  import './components/chart/bar-chart.js';
24
26
  import './components/chart/line-chart.js';
25
27
  import './components/chart/pie-chart.js';
@@ -33,6 +35,7 @@ import './components/chart/histogram.js';
33
35
  import './components/map/map.js';
34
36
  import './components/file-input/file-input.js';
35
37
  import './components/widget/widget.js';
38
+ import './components/word-cloud/word-cloud.js';
36
39
  export { LyraSparkline } from './components/sparkline/sparkline.js';
37
40
  export { LyraToast } from './components/toast/toast.js';
38
41
  export type { ToastPlacement, ToastCreateOptions } from './components/toast/toast.js';
@@ -43,6 +46,7 @@ export type { ToastOptions, ToastHandle } from './components/toast/toaster.js';
43
46
  export { LyraCombobox } from './components/combobox/combobox.js';
44
47
  export type { OptionFilter } from './components/combobox/combobox.js';
45
48
  export { LyraOption } from './components/combobox/option.js';
49
+ export { LyraSelect } from './components/select/select.js';
46
50
  export { LyraDatePicker } from './components/date-picker/date-picker.js';
47
51
  export type { DateRange } from './components/date-picker/date-picker.js';
48
52
  export { LyraDateInput } from './components/date-picker/date-input.js';
@@ -52,7 +56,7 @@ export { LyraEmpty } from './components/empty/empty.js';
52
56
  export { LyraSkeleton } from './components/skeleton/skeleton.js';
53
57
  export type { SkeletonVariant, SkeletonEffect } from './components/skeleton/skeleton.js';
54
58
  export { LyraStat } from './components/stat/stat.js';
55
- export type { StatVariant } from './components/stat/stat.js';
59
+ export type { StatVariant, StatGoodDirection } from './components/stat/stat.js';
56
60
  export { LyraTable } from './components/table/table.js';
57
61
  export type { TableColumn } from './components/table/table.js';
58
62
  export { LyraGauge } from './components/gauge/gauge.js';
@@ -73,6 +77,8 @@ export type { TreeItem } from './components/tree/tree.js';
73
77
  export { LyraTreeNode } from './components/tree/tree-node.js';
74
78
  export { LyraChart } from './components/chart/chart.js';
75
79
  export type { Series, LyraChartType } from './components/chart/chart.js';
80
+ export { LyraLiteChart } from './components/chart/lite-chart.js';
81
+ export type { LiteSeries, LyraLiteChartType } from './components/chart/lite-chart.js';
76
82
  export { LyraBarChart } from './components/chart/bar-chart.js';
77
83
  export { LyraLineChart } from './components/chart/line-chart.js';
78
84
  export { LyraPieChart } from './components/chart/pie-chart.js';
@@ -90,6 +96,8 @@ export { LyraMap } from './components/map/map.js';
90
96
  export type { LegendEntry, ChoroplethLayer } from './components/map/map.js';
91
97
  export { LyraFileInput } from './components/file-input/file-input.js';
92
98
  export { LyraWidget } from './components/widget/widget.js';
99
+ export { LyraWordCloud } from './components/word-cloud/word-cloud.js';
100
+ export type { WordCloudWord } from './components/word-cloud/word-cloud.js';
93
101
  export type { MapMarker } from './components/map/map.js';
94
102
  export type { ComboboxSource, ComboboxSourceRow } from './components/combobox/combobox.js';
95
103
  export type { CalendarDay } from './components/heatmap/calendar-grid.js';
package/dist/lyra.js CHANGED
@@ -4,6 +4,7 @@ import './components/toast/toast.js';
4
4
  import './components/toast/toast-item.js';
5
5
  import './components/combobox/combobox.js';
6
6
  import './components/combobox/option.js';
7
+ import './components/select/select.js';
7
8
  import './components/date-picker/date-picker.js';
8
9
  import './components/date-picker/date-input.js';
9
10
  import './components/flag/flag.js';
@@ -21,6 +22,7 @@ import './components/graph/graph.js';
21
22
  import './components/tree/tree.js';
22
23
  import './components/tree/tree-node.js';
23
24
  import './components/chart/chart.js';
25
+ import './components/chart/lite-chart.js';
24
26
  import './components/chart/bar-chart.js';
25
27
  import './components/chart/line-chart.js';
26
28
  import './components/chart/pie-chart.js';
@@ -34,6 +36,7 @@ import './components/chart/histogram.js';
34
36
  import './components/map/map.js';
35
37
  import './components/file-input/file-input.js';
36
38
  import './components/widget/widget.js';
39
+ import './components/word-cloud/word-cloud.js';
37
40
  // …and the barrel re-exports classes, helpers, and types.
38
41
  export { LyraSparkline } from './components/sparkline/sparkline.js';
39
42
  export { LyraToast } from './components/toast/toast.js';
@@ -41,6 +44,7 @@ export { LyraToastItem } from './components/toast/toast-item.js';
41
44
  export { toast } from './components/toast/toaster.js';
42
45
  export { LyraCombobox } from './components/combobox/combobox.js';
43
46
  export { LyraOption } from './components/combobox/option.js';
47
+ export { LyraSelect } from './components/select/select.js';
44
48
  export { LyraDatePicker } from './components/date-picker/date-picker.js';
45
49
  export { LyraDateInput } from './components/date-picker/date-input.js';
46
50
  export { LyraFlag } from './components/flag/flag.js';
@@ -61,6 +65,7 @@ export { LyraGraph } from './components/graph/graph.js';
61
65
  export { LyraTree } from './components/tree/tree.js';
62
66
  export { LyraTreeNode } from './components/tree/tree-node.js';
63
67
  export { LyraChart } from './components/chart/chart.js';
68
+ export { LyraLiteChart } from './components/chart/lite-chart.js';
64
69
  export { LyraBarChart } from './components/chart/bar-chart.js';
65
70
  export { LyraLineChart } from './components/chart/line-chart.js';
66
71
  export { LyraPieChart } from './components/chart/pie-chart.js';
@@ -75,6 +80,7 @@ export { binValues } from './components/chart/histogram-bin.js';
75
80
  export { LyraMap } from './components/map/map.js';
76
81
  export { LyraFileInput } from './components/file-input/file-input.js';
77
82
  export { LyraWidget } from './components/widget/widget.js';
83
+ export { LyraWordCloud } from './components/word-cloud/word-cloud.js';
78
84
  export { LyraElement } from './internal/lyra-element.js';
79
85
  export { FormAssociated } from './internal/form-associated.js';
80
86
  export { LYRA_PREFIX, tag, defineElement } from './internal/prefix.js';