@aceshooting/lyra-ui 18.0.0 → 18.2.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 (42) hide show
  1. package/CHANGELOG.md +274 -0
  2. package/custom-elements.json +1 -1
  3. package/design-tokens.json +1 -1
  4. package/dist/cli/migration-contract.json +1 -1
  5. package/dist/components/conversation/message-actions/message-actions.class.d.ts +2 -1
  6. package/dist/components/forms/combobox/combobox.styles.js +1 -1
  7. package/dist/components/forms/icon-button/icon-button.class.d.ts +18 -9
  8. package/dist/components/forms/select/select.class.d.ts +34 -5
  9. package/dist/components/forms/select/select.class.js +4 -4
  10. package/dist/components/forms/select/select.styles.js +1 -1
  11. package/dist/components/layout/filter-bar/filter-bar.class.d.ts +80 -8
  12. package/dist/components/layout/filter-bar/filter-bar.class.js +11 -3
  13. package/dist/components/layout/filter-bar/filter-bar.styles.js +1 -1
  14. package/dist/components/layout/virtual-list/virtual-list.class.d.ts +47 -16
  15. package/dist/components/layout/virtual-list/virtual-list.class.js +1 -1
  16. package/dist/components/media/map/map.class.d.ts +161 -5
  17. package/dist/components/media/map/map.class.js +50 -21
  18. package/dist/components/media/map/map.styles.js +1 -1
  19. package/dist/components/utility/copy-button/copy-button.class.d.ts +4 -3
  20. package/dist/custom-elements-jsx.d.ts +1 -1
  21. package/dist/events.d.ts +7 -1
  22. package/dist/internal/package-metadata.d.ts +1 -1
  23. package/dist/internal/package-metadata.js +1 -1
  24. package/dist/internal/tokens.styles.js +1 -1
  25. package/dist/lyra.d.ts +1 -1
  26. package/dist/styles/tokens-root.css +1 -1
  27. package/dist/svelte.d.ts +1 -1
  28. package/dist/testing/lyra-tag-event-map.js +1 -1
  29. package/dist/vue.d.ts +1 -1
  30. package/llms/components/lr-combobox.md +8 -4
  31. package/llms/components/lr-filter-bar.md +37 -4
  32. package/llms/components/lr-icon-button.md +15 -7
  33. package/llms/components/lr-map.md +87 -10
  34. package/llms/components/lr-option.md +8 -4
  35. package/llms/components/lr-select.md +30 -5
  36. package/llms/shared.md +46 -0
  37. package/llms/tokens.md +8 -3
  38. package/llms-full.txt +227 -29
  39. package/package.json +1 -1
  40. package/vscode-css-data.json +1 -1
  41. package/vscode-html-data.json +1 -1
  42. package/web-types.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,279 @@
1
1
  # Changelog
2
2
 
3
+ ## 18.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3c1e25c: `<lr-map>`: add `legendControlRole` for checkbox legend semantics.
8
+
9
+ Every `legendInteractive` row rendered as a `<button aria-pressed>`, which reads as a set of
10
+ pressed/unpressed actions rather than a checklist of independent show/hide toggles. The new
11
+ `legendControlRole: 'button' | 'checkbox'` property (default `'button'`) lets a consumer switch the
12
+ presentation without changing anything else: under `'checkbox'` the SAME `<button>` element renders
13
+ `role="checkbox"` and `aria-checked` (both `"true"`/`"false"`, inverted from `hiddenCategories`
14
+ exactly as `aria-pressed` was) in place of `aria-pressed`. The swatch, the label, the click handler
15
+ and the platform's own Enter/Space activation are unchanged, so the cancelable
16
+ `lr-map-legend-toggle` veto, `hiddenCategories` round-tripping, `group` sections and
17
+ `legendCollapsible` all compose with either role. With `legendControlRole` unset, the rendered
18
+ legend row is unchanged.
19
+ - 2a25254: Fix `--lr-icon-button-size-scope` being inert for most consumers.
20
+
21
+ 18.1.0 introduced `--lr-icon-button-size-scope` as the ancestor-scoped icon-button size
22
+ input, resolving it as
23
+ `var(--lr-theme-icon-button-size, var(--lr-icon-button-size-scope, 2.5rem))`. That order
24
+ made the new token dead on arrival for anyone loading the shipped `design-tokens.css`,
25
+ which declares `--lr-theme-icon-button-size` on `:root`: a `var()` chain only falls
26
+ through when the referenced property is unset **everywhere**, not merely shadowed nearer
27
+ the element, so the theme tier always answered first and the subtree override never
28
+ resolved. Silently — which is precisely the failure mode the token was added to remove.
29
+
30
+ The chain now reads the scope input first. That is also the correct precedence on its own
31
+ merits: a subtree override should beat an application-wide default. `--lr-icon-button-size`
32
+ keeps its element-scoped meaning, the coarse-pointer floor still applies to whichever value
33
+ wins, and the resolved default is unchanged at `2.5rem`.
34
+
35
+ If you worked around this by declaring `--lr-theme-icon-button-size: initial` at `:root`,
36
+ that workaround is no longer needed and can be removed.
37
+
38
+ The library's own tests missed this because their fixtures compose only the base token
39
+ layer and never load `design-tokens.css`. A regression test now declares the theme tier on
40
+ `:root`, reproducing a real consumer's setup.
41
+
42
+ ### Patch Changes
43
+
44
+ - 3c1e25c: Document `<lr-map>`'s `legendControlRole` property in the authored `llms/media.md` reference: its
45
+ `'button' | 'checkbox'` values, the `'button'` default, the `aria-checked` inversion (a hidden
46
+ category renders unchecked), and when to prefer each role.
47
+
48
+ ## 18.1.0
49
+
50
+ ### Minor Changes
51
+
52
+ - 6137062: `<lr-filter-bar>`: under `activeFiltersDisplay="changed"`, the reset button now keys its enablement
53
+ on `hasChangedFilters` instead of `hasActiveFilters`.
54
+
55
+ `'changed'` exists to say that a filter sitting at its own declared `defaultValue` is not something
56
+ the user applied, and it already suppressed that filter's chip. The reset button next to the empty
57
+ chip row stayed enabled anyway, so a defaults-only bar rendered "nothing is applied" and "press here
58
+ to clear what's applied" side by side, and pressing it was a no-op that still emitted `lr-input` and
59
+ `lr-reset`.
60
+
61
+ Nothing else changes: enablement under `activeFiltersDisplay="all"` (the default) and `"hidden"` is
62
+ byte for byte what it was, `disabled`/`loading` still win in every mode, `hasActiveFilters` keeps its
63
+ meaning, and `reset()` itself is untouched — it still restores every declared `defaultValue`.
64
+ - 6137062: `<lr-filter-bar>`: add a public read-only `hasChangedFilters`.
65
+
66
+ `hasActiveFilters` answers "does any filter hold a value", which is deliberately `true` for a bar
67
+ whose filters were declared with their own non-empty `defaultValue` and have never been touched —
68
+ so a host that wanted to show "3 filters applied", enable an "Apply"/"Save search" action, or badge
69
+ a collapsed filter panel had to re-derive the default comparison itself from `filters` and `value`.
70
+ `hasChangedFilters` exposes it directly, using the exact equality `activeFiltersDisplay: 'changed'`
71
+ already filters its chip row on: a `readonly string[]` default compares positionally, everything
72
+ else compares with `Object.is`, and a filter with no declared `defaultValue` counts as changed the
73
+ moment it holds any value at all. Clearing a filter that *does* declare one also counts as changed,
74
+ since `reset()` would restore it — which is the one case where it differs from the `'changed'` chip
75
+ row, whose entries are non-empty values by construction.
76
+
77
+ `hasActiveFilters` is unchanged, and so is every existing behaviour keyed on it.
78
+ - 6137062: `<lr-filter-bar>`: a filter definition's `labelVisibility` accepts a third value, `'auto'`.
79
+
80
+ `'visible'` and `'hidden'` were an all-or-nothing choice made once, at definition time, for a
81
+ component that is as likely to sit in a 320px side panel as across a full page — so a bar authored
82
+ for a dashboard lost a whole row of vertical space in a drawer, and one authored for the drawer
83
+ shipped unlabelled fields to the dashboard. `'auto'` renders exactly what `'visible'` does (same
84
+ stacked label element, same accessible name computed from it, no `aria-label` and no placeholder
85
+ fallback) and visually clips that label once the bar's **own** allocation — a container query on the
86
+ host, not the viewport — drops below `30rem`. The label element stays in the DOM at every width, so
87
+ the field's accessible name is identical in both states; the previous workaround, visually hiding
88
+ `::part(filter-control-label)` from a consumer stylesheet, removed the name along with the text.
89
+
90
+ `'checkbox-menu'` participates through its own trigger label run, the same one `'hidden'` already
91
+ clips there.
92
+
93
+ `'visible'`, `'hidden'` and an unset `labelVisibility` are unchanged at every allocation. The host
94
+ does become an inline-size query container (with the library's standard
95
+ `contain-intrinsic-inline-size` fallback), which is a no-op for a bar in an ordinary block or flex
96
+ allocation.
97
+ - 3f4be41: `<lr-map>`: add `legendCollapsible`/`legendOpen` and the cancelable `lr-map-legend-panel-toggle`.
98
+
99
+ The legend panel could not be collapsed at all, so a large key permanently covered part of the map.
100
+ `legend-collapsible` renders a native disclosure button inside the panel whose visible localized
101
+ text is its accessible name, whose `aria-expanded` renders the literal `"true"`/`"false"`, and whose
102
+ `aria-controls` names the row list in the same shadow root. `legendOpen` defaults **open**, so adding
103
+ only `legendCollapsible` never hides an existing key; it is a `true`-defaulting boolean, so
104
+ `legend-open="false"` parses and the open default reflects as an absent attribute.
105
+
106
+ Activation emits the cancelable `lr-map-legend-panel-toggle` (`detail: { open }`). `preventDefault()`
107
+ is a real veto — nothing is written and nothing re-renders — so a host can own the open state, and a
108
+ programmatic `legendOpen` assignment reconciles without emitting, so a controlled host cannot loop.
109
+ Collapsing hides the gradient, the rows, the `legend-limit` summary and the trailing `legend` slot;
110
+ the `legend-start` slot and the disclosure stay visible. New `legend-disclosure` and
111
+ `legend-disclosure-icon` parts. With `legendCollapsible` unset the rendered legend is unchanged.
112
+ - 3f4be41: `<lr-map>`: add an optional `group` to `LyraMapLegendEntry`, splitting the legend into sections.
113
+
114
+ `legend` was one flat array rendered as a single list, so a key covering two layers could not say
115
+ which rows belonged to which. Consecutive entries sharing an identical `group` now render as one
116
+ section — a visible `legend-group-heading` plus a `role="group"` that heading names through
117
+ `aria-labelledby`. The grouping rule is pinned rather than inferred: an entry with no `group` keeps
118
+ its **declared** position (never hoisted or sunk), and a `group` that reappears after an interruption
119
+ opens a second section rather than reordering rows to merge them.
120
+
121
+ `group` is caller-supplied data, so it renders verbatim and is never passed through the locale
122
+ catalog; it is trimmed, bounded to 256 characters, and a non-string, empty or whitespace-only value
123
+ means "ungrouped" rather than an empty heading. Sections are not rows: the 100-row cap and the
124
+ `legend-limit` summary still count rows. A grouped legend gives each run its own `role="list"`
125
+ (a `list` may only own `listitem`s), while a legend with no groups renders exactly as before. New
126
+ `legend-group` and `legend-group-heading` parts.
127
+ - 3f4be41: `<lr-map>`: add a `legend-start` slot that renders at the top of the legend panel.
128
+
129
+ The panel's only host extension point was `slot="legend"`, which renders after the gradient bar and
130
+ every projected row — so a host-authored panel header (a title, a source note, a control) could only
131
+ ever be a footer. `legend-start` renders ahead of both. Content in it alone opens the panel, exactly
132
+ as `legend` content alone already did, and it is never made interactive by `legendInteractive`. The
133
+ existing `legend` slot keeps its position, so an unset map's legend markup is unchanged.
134
+ - 42af229: `<lr-select>`: `loading` now covers the whole pending state, not only its committed-value half.
135
+ It already rewrote an unresolved committed value's label to the localized `loading` text and
136
+ suppressed the "not in catalog" badge; with nothing selected at all -- a create form whose option
137
+ catalogue is still being fetched, or an edit form whose saved selection is legitimately empty --
138
+ `labelFor()` was never reached and the trigger fell through to the consumer's own `placeholder`.
139
+ Covering that half meant hand-writing a conditional placeholder bound to the same flag and
140
+ re-localizing, in the consuming app's own catalogue, the exact string this control already owns.
141
+
142
+ The trigger now renders the same localized text in place of `placeholder` while `loading` is
143
+ `true` and the selection is empty -- the existing `loading` message key, so both halves always read
144
+ the same words and a `registerLyraLocale()` translation reaches them both. With `loading` false an
145
+ empty selection renders the consumer's `placeholder` exactly as before, `value` is never touched,
146
+ and the trigger's accessible name is unchanged: a host `aria-label` still wins, then `label`, then
147
+ `placeholder`, then the localized `select` fallback.
148
+ - 42af229: `<lr-select>`: new `sync` property, closing the shared anchored-surface sizing vocabulary
149
+ `<lr-popup>`, `<lr-popover>`, `<lr-dropdown>` and `<lr-combobox>` already spell. It takes the same
150
+ `'width' | 'height' | 'both'` type (`PlaceSync`), the same unset default, and is wired through the
151
+ same `place()` option, so `sync="width"` copies the rendered trigger width onto the listbox: a
152
+ full-width select with short option labels no longer opens a listbox visibly narrower than, and
153
+ floating centred under, its own trigger. Changes reposition an already-open listbox without closing
154
+ it, and unsetting it releases the inline width the positioner wrote.
155
+
156
+ With `sync` unset the listbox renders exactly as before -- `inline-size: max-content` between
157
+ `--lr-size-12rem` and `min(--lr-popover-viewport-clamp, --lr-size-28rem)`. A synced listbox is
158
+ capped on `--lr-positioner-available-inline-size` alone, adopting the corrected clamp rather than
159
+ the viewport-clamp shortfall it would otherwise have inherited, so it matches its trigger at any
160
+ width while the measured available space still keeps it on screen.
161
+ - b22fdbb: Design tokens: add `--lr-icon-button-size-scope`, an ancestor-scoped input for the icon-only
162
+ control size.
163
+
164
+ `--lr-icon-button-size` is element-scoped and has to stay that way: the shared token layer
165
+ re-declares it on every `lr-*` host so the coarse-pointer touch-target floor can apply per element.
166
+ The side effect is that a wrapper setting `--lr-icon-button-size` is replaced at the first
167
+ component in between, so it never reaches an icon button composed inside another component — a
168
+ `<lr-icon-button>` slotted through `<lr-popover>`, or the copy affordance inside `<lr-code-block>`.
169
+ The only ancestor lever was `--lr-theme-icon-button-size`, which is application-wide by design, so
170
+ "make just this toolbar denser" meant reaching for the global theme input and scoping it by hand.
171
+
172
+ `--lr-icon-button-size-scope` is declared nowhere, so it inherits the whole way down and reaches
173
+ every icon-only control below the wrapper that sets it:
174
+
175
+ ```css
176
+ .message-toolbar {
177
+ --lr-icon-button-size-scope: 1.75rem;
178
+ }
179
+ ```
180
+
181
+ Precedence is `--lr-theme-icon-button-size` → `--lr-icon-button-size-scope` → the `2.5rem` default,
182
+ and the coarse-pointer floor still raises a resolved value below `2.75rem` back to it, so the new
183
+ knob is not a route around WCAG 2.2 SC 2.5.8. Purely additive: `--lr-icon-button-size` keeps its
184
+ current element-scoped behaviour exactly, and the resolved default is unchanged.
185
+
186
+ ### Patch Changes
187
+
188
+ - 6137062: Docs: `<lr-filter-bar>`'s consumer reference now covers `hasChangedFilters` (and how it differs from
189
+ `hasActiveFilters` on a defaults-only bar), the `activeFiltersDisplay="changed"` reset-enablement
190
+ coupling, and `labelVisibility: 'auto'` with its `30rem` container-query threshold.
191
+ - 3f4be41: Document `<lr-map>`'s collapsible legend, `legend-start` slot and legend sections in the authored
192
+ `llms/media.md` reference: `legendCollapsible`/`legendOpen` (including the `true` default and the
193
+ `legend-open="false"` parse), the cancelable `lr-map-legend-panel-toggle`, the `group` field and its
194
+ pinned ordering rule, and the four new parts.
195
+ - 42af229: `<lr-combobox>`: a `sync="width"`/`sync="both"` listbox is no longer shortened by
196
+ `--lr-popover-viewport-clamp`. The synced rule capped the listbox at
197
+ `min(var(--lr-popover-viewport-clamp), var(--lr-positioner-available-inline-size, 100vw))`, and the
198
+ clamp's 92vw default resolves below the width of exactly the full-width trigger `sync` exists to
199
+ serve -- a 738px trigger in an 800px viewport got a 736px listbox, and the shortfall grows with the
200
+ trigger. The synced rule now caps on `var(--lr-positioner-available-inline-size, 100vw)` alone,
201
+ matching `<lr-popup>`: a width-synced listbox is anchored to an element already on screen and
202
+ measured against the space actually available beside it, so the extra viewport ceiling had nothing
203
+ left to protect against, while the available-space term still keeps an over-wide anchor from
204
+ pushing the listbox off-screen. An unsynced listbox is unchanged and keeps
205
+ `min(--lr-popover-viewport-clamp, --lr-size-28rem)`.
206
+ - 42af229: Docs: `llms/forms.md` now documents `<lr-select>`'s new `sync` property (same vocabulary note as
207
+ `<lr-combobox>`'s, stating its unset default), records that a width-synced listbox on **both**
208
+ controls is capped on `--lr-positioner-available-inline-size` alone rather than also by
209
+ `--lr-popover-viewport-clamp`, and describes `loading`'s coverage of an empty selection alongside
210
+ the `placeholder` entry it now takes precedence over.
211
+ - b22fdbb: Design tokens: derive the dark-mode overlay surface from the page surface instead of pinning it.
212
+
213
+ `--lr-color-surface-overlay` is the panel colour behind every floating surface — dropdowns,
214
+ listboxes, menus, toasts, popovers, dialogs, and the `lr-app-rail` mobile drawer. In light mode it
215
+ already resolves straight to `--lr-color-surface`, so one `--lr-theme-color-surface-default`
216
+ override carries all of them with it. Dark mode pinned a literal instead, because panel and page
217
+ resolving to the same near-black makes an open dialog read as a scrim with text floating on it and
218
+ no panel at all. The cost was that re-skinning the dark base surface left every floating surface at
219
+ the stock colour — a mismatched panel rather than a themed one.
220
+
221
+ It is now derived, keeping the elevation delta the literal existed to provide:
222
+
223
+ ```css
224
+ --lr-color-surface-overlay: var(
225
+ --lr-theme-color-surface-overlay,
226
+ color-mix(in srgb, var(--lr-color-surface) 85%, #8bade2)
227
+ );
228
+ ```
229
+
230
+ At the built-in dark base the pair resolves to the same panel colour it always has, so no existing
231
+ dark theme moves. Light mode is untouched, and an explicit `--lr-theme-color-surface-overlay` still
232
+ wins outright.
233
+ - b22fdbb: Docs: state the icon-button size scopes and the derived dark overlay surface in `llms/shared.md`.
234
+
235
+ Three properties now resize icon-only controls and they differ only in how far they reach, which is
236
+ not something a consumer can infer from the names: `--lr-theme-icon-button-size` is
237
+ application-wide, `--lr-icon-button-size-scope` covers one subtree, and `--lr-icon-button-size` is
238
+ element-scoped because the shared token layer re-declares it on every host. The reference now says
239
+ so in one table, with the precedence order and the coarse-pointer floor spelled out, instead of
240
+ leaving "my wrapper rule does nothing" to be rediscovered per project.
241
+
242
+ The overlay-surface entry records that `--lr-color-surface-overlay` follows
243
+ `--lr-theme-color-surface-default` in dark mode as well as light, so a single base-surface override
244
+ re-skins every dropdown, listbox, menu, toast and drawer, and explains why dark derives the value
245
+ rather than resolving straight to the page surface the way light does.
246
+ - 90449c6: `<lr-virtual-list>`: `row-height="auto"` driven by an external `scrollElement` no longer raises the
247
+ uncaught window error "ResizeObserver loop completed with undelivered notifications." when the
248
+ scroller jumps to its end and the newly revealed window measures. Nothing was ever dropped -- the
249
+ browser re-delivers on the following frame -- but the notice arrives as an uncaught `ErrorEvent` on
250
+ `window`, so it landed on whatever was running at the time and failed consumers' clean-console e2e
251
+ gates with a message that named no component.
252
+
253
+ The offending write is `[part="spacer"]`'s height, the list's whole virtual extent. `onRowsResized()`
254
+ folded each newly measured row height into the offsets and asked for a render; Lit flushes that
255
+ render on the microtask checkpoint that follows the observer callback, which is still inside the
256
+ browser's resize-observation delivery. Under an external `scrollElement` that write resizes an
257
+ *observed* box, because `[part="base"][data-external-scroll]` takes its own block size from the
258
+ spacer and is watched by the container `ResizeObserver` -- and `[part="base"]` sits shallower in the
259
+ tree than the rows just broadcast, so the browser records the resize as a skipped observation and
260
+ ends the loop with the error. Reproduced deterministically in WebKit and, with a large enough first
261
+ measurement delta, in Chromium too.
262
+
263
+ Reading inside the delivery is fine; folding the result into the offsets is what resizes the box.
264
+ So the measurement callbacks now only stash what they observed, and the offsets rebuild -- with the
265
+ scroll-anchor correction that belongs to it, so neither is ever painted without the other -- runs on
266
+ the animation frame this component already used to defer `observe()` calls out of the same delivery.
267
+ A render that still lands mid-delivery then re-reads offsets nothing has changed and writes the
268
+ extent already in the DOM, which resizes nothing. Renders themselves are never held: this element's
269
+ `updateComplete` keeps settling in the same microtask run as before, which is what every component
270
+ composing it relies on when it reads rendered rows back after its own update. Only the
271
+ external-`scrollElement` case defers at all -- with the list's own viewport scrolling,
272
+ `[part="base"]` takes its block size from `--lr-virtual-list-height` rather than from the spacer, so
273
+ the extent write reaches no observed box and every measurement there stays as immediate as it was,
274
+ fixed numeric `row-height` included. The error message itself is neither suppressed nor filtered
275
+ anywhere.
276
+
3
277
  ## 18.0.0
4
278
 
5
279
  ### Major Changes