@aceshooting/lyra-ui 1.0.0 → 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 +10463 -4519
  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
@@ -14,8 +14,8 @@ export interface DateRange {
14
14
  * @customElement lyra-date-picker
15
15
  * @event change - The user committed a value.
16
16
  * @event input - The value changed during interaction (range: after the first click).
17
- * @csspart base, month, header, title, previous, next, weekdays, weekday, grid
18
- * @csspart day, day-today, day-outside, day-selected, day-range-start, day-range-end, day-range-inner
17
+ * @csspart base, month, header, title, previous, next, weekdays, weekday, grid, week
18
+ * @csspart day, day-today, day-outside, day-selected, day-range-start, day-range-end, day-range-inner, day-placeholder
19
19
  */
20
20
  export declare class LyraDatePicker extends LyraElement {
21
21
  static styles: import("lit").CSSResultGroup[];
@@ -36,6 +36,7 @@ export declare class LyraDatePicker extends LyraElement {
36
36
  private viewDate;
37
37
  private focusedDate;
38
38
  private focusPending;
39
+ private suppressViewSync;
39
40
  get selection(): DateRange;
40
41
  /** Read-only Date view of a single-mode value. */
41
42
  get valueAsDate(): Date | null;
@@ -48,10 +49,17 @@ export declare class LyraDatePicker extends LyraElement {
48
49
  clear(): void;
49
50
  /** Navigate to today and focus it. */
50
51
  goToToday(): void;
51
- /** Navigate the view to a date and focus it. */
52
+ /** Navigate the view to a date and focus it, clamped to `min`/`max`. */
52
53
  goToDate(date: string | Date): void;
53
54
  private nav;
54
55
  private onGridKey;
56
+ /**
57
+ * The anchor month for a newly-focused date, sliding the view by the
58
+ * minimum amount needed to bring it into view. With `months` > 1, a date
59
+ * that's already visible in a later grid must not discard an earlier grid
60
+ * that's already on-screen.
61
+ */
62
+ private viewDateForFocus;
55
63
  protected updated(): void;
56
64
  private renderDay;
57
65
  private renderMonth;
@@ -4,13 +4,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { html } from 'lit';
7
+ import { html, nothing } from 'lit';
8
8
  import { property, state } from 'lit/decorators.js';
9
9
  import { LyraElement } from '../../internal/lyra-element.js';
10
10
  import { defineElement } from '../../internal/prefix.js';
11
11
  import { chevronIcon } from '../../internal/icons.js';
12
12
  import { styles } from './date-picker.styles.js';
13
- import { monthMatrix, weekdayLabels, monthTitle, formatISO, parseISO, isSameDay, addMonths, resolveFirstDayOfWeek, } from './calendar-core.js';
13
+ import { monthMatrix, weekdayLabels, monthTitle, formatISO, parseISO, isSameDay, addMonths, clampDate, resolveFirstDayOfWeek, } from './calendar-core.js';
14
14
  /**
15
15
  * `<lyra-date-picker>` — an inline month-grid calendar for picking a single date
16
16
  * or a date range. Mirrors the core `<wa-date-picker>` API under `lyra-`.
@@ -20,8 +20,8 @@ import { monthMatrix, weekdayLabels, monthTitle, formatISO, parseISO, isSameDay,
20
20
  * @customElement lyra-date-picker
21
21
  * @event change - The user committed a value.
22
22
  * @event input - The value changed during interaction (range: after the first click).
23
- * @csspart base, month, header, title, previous, next, weekdays, weekday, grid
24
- * @csspart day, day-today, day-outside, day-selected, day-range-start, day-range-end, day-range-inner
23
+ * @csspart base, month, header, title, previous, next, weekdays, weekday, grid, week
24
+ * @csspart day, day-today, day-outside, day-selected, day-range-start, day-range-end, day-range-inner, day-placeholder
25
25
  */
26
26
  export class LyraDatePicker extends LyraElement {
27
27
  constructor() {
@@ -43,6 +43,12 @@ export class LyraDatePicker extends LyraElement {
43
43
  this.viewDate = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
44
44
  this.focusedDate = null;
45
45
  this.focusPending = false;
46
+ // Set right before `commit()` assigns `value`, so `willUpdate` can tell its
47
+ // own write apart from an external assignment. Internal commits already
48
+ // know the right date is on-screen (it's the cell the user just clicked, or
49
+ // the view the user navigated to), so they must not force the view back to
50
+ // `selection.from`'s month -- only an externally-set `value` should do that.
51
+ this.suppressViewSync = false;
46
52
  this.onGridKey = (e) => {
47
53
  const current = this.focusedDate ?? this.selection.from ?? new Date();
48
54
  let next = null;
@@ -81,7 +87,7 @@ export class LyraDatePicker extends LyraElement {
81
87
  }
82
88
  e.preventDefault();
83
89
  this.focusedDate = next;
84
- this.viewDate = new Date(next.getFullYear(), next.getMonth(), 1);
90
+ this.viewDate = this.viewDateForFocus(next);
85
91
  this.focusPending = true;
86
92
  };
87
93
  }
@@ -95,26 +101,26 @@ export class LyraDatePicker extends LyraElement {
95
101
  return this.mode === 'single' ? this.selection.from : null;
96
102
  }
97
103
  willUpdate(changed) {
98
- if (changed.has('value') && this.value) {
99
- const from = this.selection.from;
100
- if (from)
101
- this.viewDate = new Date(from.getFullYear(), from.getMonth(), 1);
104
+ if (changed.has('value')) {
105
+ const external = !this.suppressViewSync;
106
+ this.suppressViewSync = false;
107
+ if (external && this.value) {
108
+ const from = this.selection.from;
109
+ if (from)
110
+ this.viewDate = new Date(from.getFullYear(), from.getMonth(), 1);
111
+ }
102
112
  }
103
113
  }
104
114
  get fdow() {
105
115
  return resolveFirstDayOfWeek(this.firstDayOfWeek, this.locale);
106
116
  }
107
- isDisabled(d) {
117
+ isDisabled(d, min, max, today) {
108
118
  if (this.disabled || this.readonly)
109
119
  return true;
110
- const min = parseISO(this.min);
111
- const max = parseISO(this.max);
112
120
  if (min && d < min)
113
121
  return true;
114
122
  if (max && d > max)
115
123
  return true;
116
- const today = new Date();
117
- today.setHours(0, 0, 0, 0);
118
124
  if (this.disablePast && d < today)
119
125
  return true;
120
126
  if (this.disableFuture && d > today)
@@ -122,22 +128,31 @@ export class LyraDatePicker extends LyraElement {
122
128
  return false;
123
129
  }
124
130
  commit(from, to, fire) {
125
- this.value =
126
- this.mode === 'range'
127
- ? from && to
128
- ? `${formatISO(from)}/${formatISO(to)}`
129
- : from
130
- ? formatISO(from)
131
- : ''
131
+ const next = this.mode === 'range'
132
+ ? from && to
133
+ ? `${formatISO(from)}/${formatISO(to)}`
132
134
  : from
133
135
  ? formatISO(from)
134
- : '';
136
+ : ''
137
+ : from
138
+ ? formatISO(from)
139
+ : '';
140
+ // Only arm the suppression when `value` is actually about to change --
141
+ // that's the only case `willUpdate` will see `changed.has('value')` and
142
+ // get a chance to consume (and clear) the flag.
143
+ if (next !== this.value)
144
+ this.suppressViewSync = true;
145
+ this.value = next;
135
146
  this.emit('input');
136
147
  if (fire)
137
148
  this.emit('change');
138
149
  }
139
150
  selectDate(date) {
140
- if (this.isDisabled(date))
151
+ const min = parseISO(this.min);
152
+ const max = parseISO(this.max);
153
+ const today = new Date();
154
+ today.setHours(0, 0, 0, 0);
155
+ if (this.isDisabled(date, min, max, today))
141
156
  return;
142
157
  this.focusedDate = date;
143
158
  if (this.mode === 'range') {
@@ -165,18 +180,35 @@ export class LyraDatePicker extends LyraElement {
165
180
  goToToday() {
166
181
  this.goToDate(new Date());
167
182
  }
168
- /** Navigate the view to a date and focus it. */
183
+ /** Navigate the view to a date and focus it, clamped to `min`/`max`. */
169
184
  goToDate(date) {
170
185
  const d = typeof date === 'string' ? parseISO(date) : date;
171
186
  if (!d)
172
187
  return;
173
- this.viewDate = new Date(d.getFullYear(), d.getMonth(), 1);
174
- this.focusedDate = d;
188
+ const clamped = clampDate(d, parseISO(this.min), parseISO(this.max));
189
+ this.viewDate = new Date(clamped.getFullYear(), clamped.getMonth(), 1);
190
+ this.focusedDate = clamped;
175
191
  this.focusPending = true;
176
192
  }
177
193
  nav(delta) {
178
194
  this.viewDate = addMonths(this.viewDate, delta);
179
195
  }
196
+ /**
197
+ * The anchor month for a newly-focused date, sliding the view by the
198
+ * minimum amount needed to bring it into view. With `months` > 1, a date
199
+ * that's already visible in a later grid must not discard an earlier grid
200
+ * that's already on-screen.
201
+ */
202
+ viewDateForFocus(next) {
203
+ const firstMonth = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth(), 1);
204
+ const nextMonth = new Date(next.getFullYear(), next.getMonth(), 1);
205
+ const lastMonth = addMonths(firstMonth, this.months - 1);
206
+ if (nextMonth < firstMonth)
207
+ return nextMonth;
208
+ if (nextMonth > lastMonth)
209
+ return addMonths(nextMonth, -(this.months - 1));
210
+ return firstMonth;
211
+ }
180
212
  updated() {
181
213
  if (this.focusPending && this.focusedDate) {
182
214
  this.focusPending = false;
@@ -185,16 +217,24 @@ export class LyraDatePicker extends LyraElement {
185
217
  cell?.focus();
186
218
  }
187
219
  }
188
- renderDay(date, shownMonth) {
220
+ renderDay(date, shownMonth, selection, min, max, today, rowHasVisibleDay) {
189
221
  const outside = date.getMonth() !== shownMonth;
190
222
  // Outside-month days are empty placeholders by default (matches WA), keeping the
191
223
  // 6-row grid aligned without low-contrast faded numbers.
192
224
  if (outside && !this.withOutsideDays) {
193
- return html `<span part="day-placeholder" role="gridcell"></span>`;
225
+ // role="gridcell" stays either way. A trailing week can land entirely
226
+ // outside the shown month (monthMatrix always emits 6 rows of 7), and
227
+ // ARIA's row role requires at least one visible gridcell descendant --
228
+ // so only rows that already have a real, visible day cell may hide
229
+ // their placeholders from the accessibility tree.
230
+ return html `<span
231
+ part="day-placeholder"
232
+ role="gridcell"
233
+ aria-hidden=${rowHasVisibleDay ? 'true' : nothing}
234
+ ></span>`;
194
235
  }
195
- const { from, to } = this.selection;
196
- const disabled = this.isDisabled(date);
197
- const today = new Date();
236
+ const { from, to } = selection;
237
+ const disabled = this.isDisabled(date, min, max, today);
198
238
  const isToday = isSameDay(date, today);
199
239
  const isStart = from && isSameDay(date, from);
200
240
  const isEnd = to && isSameDay(date, to);
@@ -232,12 +272,11 @@ export class LyraDatePicker extends LyraElement {
232
272
  ${date.getDate()}
233
273
  </button>`;
234
274
  }
235
- renderMonth(offset) {
275
+ renderMonth(offset, selection, min, max, today, fdow, labels) {
236
276
  const base = addMonths(this.viewDate, offset);
237
277
  const year = base.getFullYear();
238
278
  const month = base.getMonth();
239
- const matrix = monthMatrix(year, month, this.fdow);
240
- const labels = weekdayLabels(this.fdow, this.weekdayFormat, this.locale);
279
+ const matrix = monthMatrix(year, month, fdow);
241
280
  const isFirst = offset === 0;
242
281
  const isLast = offset === this.months - 1;
243
282
  return html `<div part="month">
@@ -256,14 +295,25 @@ export class LyraDatePicker extends LyraElement {
256
295
  </div>
257
296
  <div part="weekdays">${labels.map((l) => html `<span part="weekday">${l}</span>`)}</div>
258
297
  <div part="grid" role="grid" @keydown=${this.onGridKey}>
259
- ${matrix.map((week) => html `<div part="week" role="row">${week.map((d) => this.renderDay(d, month))}</div>`)}
298
+ ${matrix.map((week) => {
299
+ const rowHasVisibleDay = week.some((d) => d.getMonth() === month);
300
+ return html `<div part="week" role="row">${week.map((d) => this.renderDay(d, month, selection, min, max, today, rowHasVisibleDay))}</div>`;
301
+ })}
260
302
  </div>
261
303
  </div>`;
262
304
  }
263
305
  render() {
306
+ const selection = this.selection;
307
+ const min = parseISO(this.min);
308
+ const max = parseISO(this.max);
309
+ const today = new Date();
310
+ today.setHours(0, 0, 0, 0);
311
+ const fdow = this.fdow;
312
+ const labels = weekdayLabels(fdow, this.weekdayFormat, this.locale);
264
313
  const monthEls = [];
265
- for (let i = 0; i < this.months; i++)
266
- monthEls.push(this.renderMonth(i));
314
+ for (let i = 0; i < this.months; i++) {
315
+ monthEls.push(this.renderMonth(i, selection, min, max, today, fdow, labels));
316
+ }
267
317
  return html `<div part="base">${monthEls}</div>`;
268
318
  }
269
319
  }
@@ -4,3 +4,7 @@ export default meta;
4
4
  type Story = StoryObj;
5
5
  export declare const Single: Story;
6
6
  export declare const Range: Story;
7
+ export declare const MinMax: Story;
8
+ export declare const DisablePast: Story;
9
+ export declare const DisableFuture: Story;
10
+ export declare const WithOutsideDays: Story;
@@ -11,3 +11,15 @@ export const Single = {
11
11
  export const Range = {
12
12
  render: () => html `<lyra-date-picker mode="range" months="2"></lyra-date-picker>`,
13
13
  };
14
+ export const MinMax = {
15
+ render: () => html `<lyra-date-picker min="2026-07-10" max="2026-07-20" value="2026-07-15"></lyra-date-picker>`,
16
+ };
17
+ export const DisablePast = {
18
+ render: () => html `<lyra-date-picker disable-past></lyra-date-picker>`,
19
+ };
20
+ export const DisableFuture = {
21
+ render: () => html `<lyra-date-picker disable-future></lyra-date-picker>`,
22
+ };
23
+ export const WithOutsideDays = {
24
+ render: () => html `<lyra-date-picker with-outside-days value="2026-07-15"></lyra-date-picker>`,
25
+ };
@@ -102,7 +102,7 @@ export const styles = css `
102
102
  }
103
103
  [part~='day']:disabled {
104
104
  color: var(--lyra-color-text-quiet);
105
- opacity: 0.35;
105
+ opacity: var(--lyra-opacity-disabled);
106
106
  cursor: not-allowed;
107
107
  }
108
108
  [part~='day']:focus-visible {
@@ -7,7 +7,11 @@ import { LyraElement } from '../../internal/lyra-element.js';
7
7
  * @customElement lyra-empty
8
8
  * @slot - Custom icon or illustration (defaults to none).
9
9
  * @slot actions - Buttons/links shown below the description.
10
- * @csspart base, icon, heading, description, actions
10
+ * @csspart base - The outer container.
11
+ * @csspart icon - The wrapper around the default-slotted icon/illustration.
12
+ * @csspart heading - The heading paragraph.
13
+ * @csspart description - The description paragraph.
14
+ * @csspart actions - The wrapper around the `actions`-slotted content.
11
15
  */
12
16
  export declare class LyraEmpty extends LyraElement {
13
17
  static styles: import("lit").CSSResultGroup[];
@@ -15,6 +19,13 @@ export declare class LyraEmpty extends LyraElement {
15
19
  heading: string;
16
20
  /** Supporting copy, e.g. "Try a different search." */
17
21
  description: string;
22
+ /**
23
+ * Compact rendering for use inside a constrained space (e.g. a widget body
24
+ * or table cell) rather than as a full-page state: left-aligned, tighter
25
+ * padding, and a lighter heading weight instead of the centered/spacious
26
+ * default.
27
+ */
28
+ compact: boolean;
18
29
  private hasIcon;
19
30
  private hasActions;
20
31
  protected willUpdate(): void;
@@ -16,7 +16,11 @@ import { styles } from './empty.styles.js';
16
16
  * @customElement lyra-empty
17
17
  * @slot - Custom icon or illustration (defaults to none).
18
18
  * @slot actions - Buttons/links shown below the description.
19
- * @csspart base, icon, heading, description, actions
19
+ * @csspart base - The outer container.
20
+ * @csspart icon - The wrapper around the default-slotted icon/illustration.
21
+ * @csspart heading - The heading paragraph.
22
+ * @csspart description - The description paragraph.
23
+ * @csspart actions - The wrapper around the `actions`-slotted content.
20
24
  */
21
25
  export class LyraEmpty extends LyraElement {
22
26
  constructor() {
@@ -25,6 +29,13 @@ export class LyraEmpty extends LyraElement {
25
29
  this.heading = '';
26
30
  /** Supporting copy, e.g. "Try a different search." */
27
31
  this.description = '';
32
+ /**
33
+ * Compact rendering for use inside a constrained space (e.g. a widget body
34
+ * or table cell) rather than as a full-page state: left-aligned, tighter
35
+ * padding, and a lighter heading weight instead of the centered/spacious
36
+ * default.
37
+ */
38
+ this.compact = false;
28
39
  // `[part='icon']:empty` never matches because the part always contains a
29
40
  // `<slot>` element (CSS `:empty` only ignores text/comment nodes). Track
30
41
  // real slot assignment in JS instead and key the CSS off these instead.
@@ -44,7 +55,10 @@ export class LyraEmpty extends LyraElement {
44
55
  // `firstUpdated` (after the update completes) would schedule a second,
45
56
  // wasted update (Lit's dev-mode "change-in-update" warning).
46
57
  if (!this.hasUpdated) {
47
- this.hasIcon = Array.from(this.children).some((el) => !el.hasAttribute('slot'));
58
+ // An explicit `slot=""` still assigns to the default slot per the HTML
59
+ // slot algorithm, so check the attribute's value rather than its mere
60
+ // presence.
61
+ this.hasIcon = Array.from(this.children).some((el) => !el.getAttribute('slot'));
48
62
  this.hasActions = Array.from(this.children).some((el) => el.getAttribute('slot') === 'actions');
49
63
  }
50
64
  }
@@ -64,7 +78,7 @@ export class LyraEmpty extends LyraElement {
64
78
  }
65
79
  render() {
66
80
  return html `
67
- <div part="base">
81
+ <div part="base" role="status" aria-live="polite">
68
82
  <div part="icon" ?hidden=${!this.hasIcon}><slot @slotchange=${this.onIconSlotChange}></slot></div>
69
83
  <p part="heading">${this.heading}</p>
70
84
  <p part="description">${this.description}</p>
@@ -81,6 +95,9 @@ __decorate([
81
95
  __decorate([
82
96
  property()
83
97
  ], LyraEmpty.prototype, "description", void 0);
98
+ __decorate([
99
+ property({ type: Boolean, reflect: true })
100
+ ], LyraEmpty.prototype, "compact", void 0);
84
101
  __decorate([
85
102
  state()
86
103
  ], LyraEmpty.prototype, "hasIcon", void 0);
@@ -3,3 +3,5 @@ declare const meta: Meta;
3
3
  export default meta;
4
4
  type Story = StoryObj;
5
5
  export declare const Default: Story;
6
+ export declare const WithIcon: Story;
7
+ export declare const Compact: Story;
@@ -12,3 +12,23 @@ export const Default = {
12
12
  </lyra-empty>
13
13
  `,
14
14
  };
15
+ export const WithIcon = {
16
+ render: () => html `
17
+ <lyra-empty heading="No results" description="Try a different search.">
18
+ <svg slot="" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor">
19
+ <circle cx="11" cy="11" r="7" stroke-width="2"></circle>
20
+ <path d="M21 21l-4.35-4.35" stroke-width="2" stroke-linecap="round"></path>
21
+ </svg>
22
+ <span slot="actions"><button>Reset</button></span>
23
+ </lyra-empty>
24
+ `,
25
+ };
26
+ export const Compact = {
27
+ render: () => html `
28
+ <div style="max-width: 16rem; border: 1px solid var(--lyra-color-border);">
29
+ <lyra-empty compact heading="No results" description="Try a different search.">
30
+ <span slot="actions"><button>Reset</button></span>
31
+ </lyra-empty>
32
+ </div>
33
+ `,
34
+ };
@@ -13,6 +13,11 @@ export const styles = css `
13
13
  color: var(--lyra-color-text-quiet);
14
14
  inline-size: 100%;
15
15
  }
16
+ :host([compact]) [part='base'] {
17
+ align-items: flex-start;
18
+ text-align: start;
19
+ padding: var(--lyra-space-xs);
20
+ }
16
21
  [part='icon'] {
17
22
  font-size: 2rem;
18
23
  line-height: 1;
@@ -26,6 +31,12 @@ export const styles = css `
26
31
  color: var(--lyra-color-text);
27
32
  margin: 0;
28
33
  }
34
+ [part='heading']:empty {
35
+ display: none;
36
+ }
37
+ :host([compact]) [part='heading'] {
38
+ font-weight: 400;
39
+ }
29
40
  [part='description'] {
30
41
  font-size: 0.875rem;
31
42
  margin: 0;
@@ -1,5 +1,9 @@
1
- const UNSAFE_LEADING = /^[=+\-@\t\r]/;
2
- const NEEDS_QUOTING = /[",\n]/;
1
+ // A bare leading '-' is not itself formula syntax (only '=', and context-dependent '+'/'@',
2
+ // reliably trigger spreadsheet formula evaluation) — guarding it forces text rendering on
3
+ // ordinary negative numbers/currency. Per OWASP CSV-injection guidance, '-' is intentionally
4
+ // excluded here.
5
+ const UNSAFE_LEADING = /^[=+@\t\r]/;
6
+ const NEEDS_QUOTING = /[",\r\n]/;
3
7
  /** Escapes a CSV field: quotes as needed, guards against formula injection. */
4
8
  export function escapeCsvField(value) {
5
9
  let s = value == null ? '' : String(value);
@@ -11,7 +11,9 @@ export type ExportFormat = 'csv' | 'json';
11
11
  * @event lyra-export - `detail: { format }`, cancelable — call `preventDefault()`
12
12
  * to substitute the built-in client-side download with a server-generated one.
13
13
  * @event lyra-export-complete - Fired after a non-cancelled download completes.
14
- * @csspart trigger, menu, menu-item
14
+ * @csspart trigger - The button that triggers the export (or opens the format menu).
15
+ * @csspart menu - The format-choice menu, shown when more than one format is configured.
16
+ * @csspart menu-item - A single format option inside the menu.
15
17
  */
16
18
  export declare class LyraExportButton extends LyraElement {
17
19
  static styles: import("lit").CSSResultGroup[];
@@ -22,14 +24,26 @@ export declare class LyraExportButton extends LyraElement {
22
24
  disabled: boolean;
23
25
  label: string;
24
26
  open: boolean;
27
+ private triggerEl?;
28
+ private menuEl?;
29
+ private readonly menuId;
25
30
  private cleanup?;
31
+ /** Which menu item to focus the next time `open` flips true; reset after use. */
32
+ private pendingMenuFocusIndex;
26
33
  disconnectedCallback(): void;
27
34
  private onDocPointer;
28
35
  private openMenu;
29
36
  private closeMenu;
37
+ private menuItemEls;
38
+ /** Focuses the menu item at `index` (clamped), if the menu is already open and rendered. */
39
+ private focusMenuItem;
40
+ /** Opens the menu (if closed) and focuses `index`, or moves focus there directly if already open. */
41
+ private focusMenuItemOnOpen;
30
42
  private onKeyDown;
31
43
  protected firstUpdated(): void;
32
44
  protected updated(changed: PropertyValues): void;
45
+ /** Applies the same `columns` allow-list CSV exports use, so JSON can't leak fields CSV hides. */
46
+ private rowsForExport;
33
47
  private doExport;
34
48
  private onTriggerClick;
35
49
  render(): TemplateResult;