@aceshooting/lyra-ui 18.0.0 → 18.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.
- package/CHANGELOG.md +229 -0
- package/custom-elements.json +1 -1
- package/design-tokens.json +1 -1
- package/dist/cli/migration-contract.json +1 -1
- package/dist/components/conversation/message-actions/message-actions.class.d.ts +2 -1
- package/dist/components/forms/combobox/combobox.styles.js +1 -1
- package/dist/components/forms/icon-button/icon-button.class.d.ts +18 -9
- package/dist/components/forms/select/select.class.d.ts +34 -5
- package/dist/components/forms/select/select.class.js +4 -4
- package/dist/components/forms/select/select.styles.js +1 -1
- package/dist/components/layout/filter-bar/filter-bar.class.d.ts +80 -8
- package/dist/components/layout/filter-bar/filter-bar.class.js +11 -3
- package/dist/components/layout/filter-bar/filter-bar.styles.js +1 -1
- package/dist/components/layout/virtual-list/virtual-list.class.d.ts +47 -16
- package/dist/components/layout/virtual-list/virtual-list.class.js +1 -1
- package/dist/components/media/map/map.class.d.ts +134 -5
- package/dist/components/media/map/map.class.js +48 -21
- package/dist/components/media/map/map.styles.js +1 -1
- package/dist/components/utility/copy-button/copy-button.class.d.ts +4 -3
- package/dist/custom-elements-jsx.d.ts +1 -1
- package/dist/events.d.ts +7 -1
- package/dist/internal/package-metadata.d.ts +1 -1
- package/dist/internal/package-metadata.js +1 -1
- package/dist/internal/tokens.styles.js +1 -1
- package/dist/lyra.d.ts +1 -1
- package/dist/styles/tokens-root.css +1 -1
- package/dist/svelte.d.ts +1 -1
- package/dist/testing/lyra-tag-event-map.js +1 -1
- package/dist/vue.d.ts +1 -1
- package/llms/components/lr-combobox.md +8 -4
- package/llms/components/lr-filter-bar.md +37 -4
- package/llms/components/lr-icon-button.md +15 -7
- package/llms/components/lr-map.md +68 -6
- package/llms/components/lr-option.md +8 -4
- package/llms/components/lr-select.md +30 -5
- package/llms/shared.md +41 -0
- package/llms/tokens.md +7 -4
- package/llms-full.txt +203 -25
- package/package.json +1 -1
- package/vscode-css-data.json +1 -1
- package/vscode-html-data.json +1 -1
- package/web-types.json +1 -1
package/llms-full.txt
CHANGED
|
@@ -1218,6 +1218,47 @@ code. See each control's own reference page for its exact pair.
|
|
|
1218
1218
|
`lr-select`. It is a _floor_, not a fixed size. Keep the resolved value **at or above 24px**
|
|
1219
1219
|
(WCAG 2.2 SC 2.5.8 target size); the default leaves headroom. Lowering it below that shrinks
|
|
1220
1220
|
every affordance in the library at once.
|
|
1221
|
+
- **`--lr-icon-button-size-scope`** resizes icon buttons for **one subtree** instead of the whole
|
|
1222
|
+
application. Set it on any wrapper; it inherits past intervening components and reaches every
|
|
1223
|
+
icon-only control below it. Three names, three scopes — pick by how far you want the change to
|
|
1224
|
+
reach:
|
|
1225
|
+
|
|
1226
|
+
| Property | Scope | Set it on |
|
|
1227
|
+
| --- | --- | --- |
|
|
1228
|
+
| `--lr-theme-icon-button-size` | application-wide | `:root`, or any ancestor |
|
|
1229
|
+
| `--lr-icon-button-size-scope` | one subtree | the wrapper you want affected |
|
|
1230
|
+
| `--lr-icon-button-size` | one element | the icon-only control itself |
|
|
1231
|
+
|
|
1232
|
+
`--lr-theme-icon-button-size` wins wherever both ancestor inputs are set. `--lr-icon-button-size`
|
|
1233
|
+
is **element-scoped on purpose** and is not a wrapper knob: every component re-declares it on its
|
|
1234
|
+
own host so the touch-target floor can apply per element, so a value set on a wrapper is replaced
|
|
1235
|
+
at the first component in between and never reaches anything nested inside one. That behaviour is
|
|
1236
|
+
unchanged — use `--lr-icon-button-size-scope` for the wrapper case.
|
|
1237
|
+
|
|
1238
|
+
```css
|
|
1239
|
+
/* A dense toolbar, without touching the rest of the app. */
|
|
1240
|
+
.message-toolbar {
|
|
1241
|
+
--lr-icon-button-size-scope: 1.75rem;
|
|
1242
|
+
}
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
The coarse-pointer floor still applies to both ancestor inputs: on a touch device (`hover: none`
|
|
1246
|
+
or `pointer: coarse`) a resolved value below `2.75rem` is raised back to it, so the new subtree
|
|
1247
|
+
knob is not a way around WCAG 2.2 SC 2.5.8 — a deliberately dense desktop toolbar still becomes
|
|
1248
|
+
tappable on a phone.
|
|
1249
|
+
- **`--lr-color-surface-overlay` follows `--lr-theme-color-surface-default` in both modes.** It is
|
|
1250
|
+
the panel colour behind every floating surface — dropdowns, listboxes, menus, toasts, popovers,
|
|
1251
|
+
dialogs, and the `lr-app-rail` mobile drawer. In light mode it resolves straight to
|
|
1252
|
+
`--lr-color-surface`, so a re-skinned page surface carries them all with it. Dark mode cannot
|
|
1253
|
+
resolve to the page surface — panel and page would be the same near-black, and an open dialog
|
|
1254
|
+
would read as a scrim with text floating on it and no panel at all — so it is **derived** from
|
|
1255
|
+
the page surface instead: `color-mix(in srgb, var(--lr-color-surface) 85%, #8bade2)`, which lifts
|
|
1256
|
+
the panel a fixed amount above whatever the base happens to be. One
|
|
1257
|
+
`--lr-theme-color-surface-default` override therefore re-skins every floating surface in dark
|
|
1258
|
+
mode too, and the elevation delta survives the re-skin. At the built-in dark base the pair still
|
|
1259
|
+
resolves to the same panel colour it always has, so no existing dark theme moves.
|
|
1260
|
+
`--lr-theme-color-surface-overlay` still wins outright when you set it — reach for it only when
|
|
1261
|
+
you want a panel colour unrelated to the page surface.
|
|
1221
1262
|
- **Aligning your own content next to a checkbox or radio.** `--lr-checkbox-label-indent` /
|
|
1222
1263
|
`--lr-radio-label-indent` publish the label offset, but custom properties inherit _down_, not
|
|
1223
1264
|
sideways, so a sibling node in your tree cannot read them off the control. Compute the same
|
|
@@ -4934,10 +4975,14 @@ An async `source` row can carry the same two fields (`start`, `end`) alongside i
|
|
|
4934
4975
|
- `sync?: PlaceSync` (reflected) — copies the trigger's width, height, or both onto the listbox,
|
|
4935
4976
|
spelled the same as on `lr-dropdown`/`lr-popup`/`lr-popover`. Unset (the default), the listbox
|
|
4936
4977
|
sizes to its own content, clamped between `--lr-size-12rem` and `--lr-size-28rem`, exactly as
|
|
4937
|
-
before. `sync="width"` drops that content-based clamp
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4978
|
+
before. `sync="width"` drops that content-based clamp so a full-width trigger with short option
|
|
4979
|
+
labels gets a listbox that aligns to its own edges instead of floating narrower in the middle.
|
|
4980
|
+
A synced listbox is capped on `--lr-positioner-available-inline-size` **alone** — the space the
|
|
4981
|
+
positioner actually measured beside the anchor, which still keeps an over-wide trigger from
|
|
4982
|
+
pushing it off-screen. `--lr-popover-viewport-clamp` does **not** apply to a synced listbox — its
|
|
4983
|
+
92vw default used to shorten the listbox against its own trigger in exactly the full-width case
|
|
4984
|
+
`sync` exists for, and `lr-popup` never applied it either. It still applies with `sync` unset.
|
|
4985
|
+
Like `placement`, a change takes effect the next time the listbox opens
|
|
4941
4986
|
- `clearable: boolean = false` (reflected) — displays the clear button while there is something to
|
|
4942
4987
|
clear on **either** axis this control owns: a committed selection, or _visible_ filter text. See
|
|
4943
4988
|
"the clear button covers two axes" below
|
|
@@ -5522,7 +5567,11 @@ exactly like the multi-option case, until the trigger is actually activated.
|
|
|
5522
5567
|
|
|
5523
5568
|
**Properties:**
|
|
5524
5569
|
|
|
5525
|
-
- `placeholder: string = ''`
|
|
5570
|
+
- `placeholder: string = ''` — text shown on the trigger while nothing is selected, and the
|
|
5571
|
+
trigger's accessible name when neither a host `aria-label` nor a `label` supplies one. One
|
|
5572
|
+
exception to "an empty selection always shows this": while `loading` is `true` the trigger shows
|
|
5573
|
+
the localized `loading` text instead — see `loading` below. The accessible name is unaffected
|
|
5574
|
+
either way
|
|
5526
5575
|
- `disabled: boolean = false` (reflected)
|
|
5527
5576
|
- `required: boolean = false` (reflected — enforced via `internals.setValidity()`)
|
|
5528
5577
|
- `name: string = ''`
|
|
@@ -5561,6 +5610,17 @@ exactly like the multi-option case, until the trigger is actually activated.
|
|
|
5561
5610
|
authored, the listbox is actually placed with the cascading `--lr-positioning-strategy` custom
|
|
5562
5611
|
property honored ahead of that default — see the listbox's own **Themeable custom properties**
|
|
5563
5612
|
below
|
|
5613
|
+
- `sync?: PlaceSync` (reflected) — copies the trigger's width, height, or both onto the listbox,
|
|
5614
|
+
spelled and typed the same as on `lr-popup`/`lr-popover`/`lr-dropdown`/`lr-combobox`
|
|
5615
|
+
(`'width' | 'height' | 'both'`). Unset (the default), the listbox sizes to its own content,
|
|
5616
|
+
clamped between `--lr-size-12rem` and `min(--lr-popover-viewport-clamp, --lr-size-28rem)`,
|
|
5617
|
+
exactly as before. `sync="width"` drops that content-based clamp so a full-width trigger with
|
|
5618
|
+
short option labels gets a listbox that aligns to its own edges instead of floating narrower in
|
|
5619
|
+
the middle. A synced listbox is capped on `--lr-positioner-available-inline-size` **alone** — the
|
|
5620
|
+
space the positioner actually measured beside the trigger, which still keeps an over-wide trigger
|
|
5621
|
+
from pushing it off-screen; `--lr-popover-viewport-clamp` does **not** apply to a synced listbox,
|
|
5622
|
+
and still does with `sync` unset. Assignment while open repositions in place without closing;
|
|
5623
|
+
unsetting it releases the inline width the positioner wrote
|
|
5564
5624
|
- `showUnknownOption: boolean = false` (attribute `show-unknown-option`, reflected) — appends every
|
|
5565
5625
|
committed value that no `<lr-option>` claims to the end of the listbox as a synthetic, badged,
|
|
5566
5626
|
keyboard-reachable, re-selectable row. Off by default
|
|
@@ -5575,9 +5635,16 @@ exactly like the multi-option case, until the trigger is actually activated.
|
|
|
5575
5635
|
`loading` placeholder in the trigger label or the relevant `multiple` tag instead of the raw
|
|
5576
5636
|
value, with no `notInCatalog`/`[part='unknown-value']` badge and no synthetic
|
|
5577
5637
|
`showUnknownOption` listbox row — "not yet resolved" is a different state from "known to be
|
|
5578
|
-
missing".
|
|
5579
|
-
|
|
5580
|
-
|
|
5638
|
+
missing". It covers an **empty** selection too: with nothing selected — a create form whose
|
|
5639
|
+
catalogue is still being fetched, or an edit form whose saved selection is legitimately empty —
|
|
5640
|
+
the trigger renders that same localized text in place of `placeholder`, from the same `loading`
|
|
5641
|
+
message key, so both halves of a pending state read the same words and one
|
|
5642
|
+
`registerLyraLocale()` translation reaches both. Nothing to re-localize in the consuming app.
|
|
5643
|
+
With `loading` false an empty selection renders `placeholder` exactly as before. The trigger's
|
|
5644
|
+
accessible name never changes for this: a host `aria-label` wins, then `label`, then
|
|
5645
|
+
`placeholder`, then the localized `select` fallback. A value already matching a live option is
|
|
5646
|
+
unaffected. Never mutates `value`/`selectedOptions` itself, and does not itself disable the
|
|
5647
|
+
trigger — pair it with `disabled` when the control should also be non-interactive while pending
|
|
5581
5648
|
- `filled: boolean = false` (reflected) — Shoelace alias for the filled trigger treatment
|
|
5582
5649
|
- `autofocus: boolean = false` / `title: string = ''` — forwarded to the internal trigger
|
|
5583
5650
|
- `multiple: boolean = false` (reflected) — several options selectable at once; see "Multi-select"
|
|
@@ -5636,7 +5703,10 @@ a still-unmatched value then renders the localized `loading` placeholder instead
|
|
|
5636
5703
|
with no `unknown-value` badge and no synthetic `showUnknownOption` row, since it is not yet known
|
|
5637
5704
|
to be missing. Once the matching option mounts, the real label renders on the next render with no
|
|
5638
5705
|
`value`/`selectedOptions` re-assignment needed, whether or not `loading` is also flipped back to
|
|
5639
|
-
`false`.
|
|
5706
|
+
`false`. The same flag covers the other half of that state: with **nothing** selected the trigger
|
|
5707
|
+
renders the same localized `loading` text in place of `placeholder`, so a consumer never has to
|
|
5708
|
+
hand-write a conditional placeholder bound to the same flag and re-localize, in its own catalogue,
|
|
5709
|
+
the string this control already owns.
|
|
5640
5710
|
|
|
5641
5711
|
**Methods:** `focus(options?)`, `blur()`, and `click()` forward to the internal trigger button.
|
|
5642
5712
|
`show()` and `hide()` return `Promise<void>` and resolve after `lr-after-show`/`lr-after-hide` once
|
|
@@ -7082,15 +7152,23 @@ above 24px — see `llms/shared.md`. **`--lr-icon-button-size` is element-scoped
|
|
|
7082
7152
|
token in this section:** the shared token layer re-declares it on every `lr-*` host's own `:host`,
|
|
7083
7153
|
so a rule that sets `--lr-icon-button-size` on an ancestor wrapper is reset the moment it crosses
|
|
7084
7154
|
into any intervening `lr-*` component and never reaches a `<lr-icon-button>` composed inside it
|
|
7085
|
-
(e.g. one slotted through `<lr-popover>`). The
|
|
7155
|
+
(e.g. one slotted through `<lr-popover>`). The three levers that actually reach it are: setting
|
|
7086
7156
|
`--lr-icon-button-size` directly on the icon button element itself, where no intervening component
|
|
7087
|
-
sits between the rule and the property;
|
|
7088
|
-
|
|
7157
|
+
sits between the rule and the property; setting `--lr-icon-button-size-scope` on any ancestor to
|
|
7158
|
+
resize one subtree; or setting `--lr-theme-icon-button-size` on an ancestor to resize the whole
|
|
7159
|
+
application. The shared layer reads the latter two through `var()` at every level, so both reach a
|
|
7089
7160
|
`<lr-icon-button>` composed inside another component, e.g. `<lr-copy-button>`/
|
|
7090
|
-
`<lr-message-actions
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7161
|
+
`<lr-message-actions>` -- see `llms/shared.md` for the full scope table.
|
|
7162
|
+
|
|
7163
|
+
Lowering the floor for a dense action row below 24px is safe **only through the two ancestor
|
|
7164
|
+
levers**. The coarse-pointer/no-hover media rule reads those, and floors the RENDERED hit area back
|
|
7165
|
+
at 2.75rem/44px however far the override lowered it, so the control stays comfortably tappable the
|
|
7166
|
+
moment the pointer reaching it is a finger rather than a mouse. It does **not** rescue
|
|
7167
|
+
`--lr-icon-button-size` set directly on the element: that declaration comes from the outer tree and
|
|
7168
|
+
outranks the shadow tree's own `:host` rule, so the media rule never wins and the rendered hit area
|
|
7169
|
+
is exactly the value set -- `--lr-icon-button-size: 1rem` really does render a 16px target under a
|
|
7170
|
+
coarse pointer, which fails WCAG 2.2 SC 2.5.8. Prefer `--lr-icon-button-size-scope` when the intent
|
|
7171
|
+
is a denser row; reach for the element-scoped name only as a deliberate, localized trade-off. `--lr-icon-button-radius` (default
|
|
7094
7172
|
`--lr-radius`) is not re-declared anywhere in the shared layer, so — like every other token below —
|
|
7095
7173
|
it inherits normally from an ancestor even through an intervening component; it is the
|
|
7096
7174
|
`[part='button']` corner radius, retunable without a `::part(button)` rule — the same
|
|
@@ -20518,8 +20596,21 @@ until something else registers it, the same trade `icon-button-register.js` docu
|
|
|
20518
20596
|
- `disabled: boolean = false` (reflected) — disables every filter control and reset action.
|
|
20519
20597
|
- `loading: boolean = false` (reflected) — shows the status spinner and disables reset while leaving
|
|
20520
20598
|
filters editable.
|
|
20521
|
-
- `hasActiveFilters: boolean` (read-only) — whether any configured filter currently has a value
|
|
20522
|
-
Drives the reset button's own disabled
|
|
20599
|
+
- `hasActiveFilters: boolean` (read-only) — whether any configured filter currently has a value,
|
|
20600
|
+
including one sitting at its own declared `defaultValue`. Drives the reset button's own disabled
|
|
20601
|
+
state in every `activeFiltersDisplay` mode except `'changed'`; the getter itself is unaffected by
|
|
20602
|
+
`activeFiltersDisplay`.
|
|
20603
|
+
- `hasChangedFilters: boolean` (read-only) — whether any filter's value differs from its own
|
|
20604
|
+
declared `defaultValue`, using the same equality `activeFiltersDisplay: 'changed'` filters its
|
|
20605
|
+
chip row on: a `readonly string[]` default compares positionally, everything else compares with
|
|
20606
|
+
`Object.is`. This is the counterpart to `hasActiveFilters`, not a synonym — a bar whose filters
|
|
20607
|
+
were all declared with non-empty defaults and never touched reads `hasActiveFilters === true` and
|
|
20608
|
+
`hasChangedFilters === false`, because a bar whose defaults narrow the view on load has not been
|
|
20609
|
+
narrowed by the user. A filter with no declared `defaultValue` counts as changed the moment it
|
|
20610
|
+
holds any value at all (there is nothing for it to still equal), and clearing a filter that *does*
|
|
20611
|
+
declare one counts as changed too, since `reset()` would restore it — which is the one case where
|
|
20612
|
+
this getter and the `'changed'` chip row differ, the row's entries being non-empty by
|
|
20613
|
+
construction. Always live, never cached.
|
|
20523
20614
|
- `invalidFilterIds: readonly string[]` (read-only) — immutable ids of required filters whose
|
|
20524
20615
|
values are unset.
|
|
20525
20616
|
- `activeFiltersDisplay: 'all' | 'changed' | 'hidden' = 'all'` (reflected, attribute
|
|
@@ -20533,6 +20624,10 @@ until something else registers it, the same trade `icon-button-register.js` docu
|
|
|
20533
20624
|
at each index), matching this component's only other array-equality precedent (a custom adapter's
|
|
20534
20625
|
own `clearValue` comparison); a `'date-range'` value is a single composed string, so it compares
|
|
20535
20626
|
like any other string. Removing a chip always clears that filter, unaffected by this property.
|
|
20627
|
+
`'changed'` additionally gates the reset button on `hasChangedFilters` instead of
|
|
20628
|
+
`hasActiveFilters`, so an untouched defaults-only bar — which renders no chip in this mode — no
|
|
20629
|
+
longer offers an enabled reset that would change nothing. Enablement under `'all'` and `'hidden'`
|
|
20630
|
+
is unchanged, `disabled`/`loading` still win in every mode, and `reset()` itself is untouched.
|
|
20536
20631
|
|
|
20537
20632
|
The composed reset action uses `lr-button`'s default `m` size tier, matching the default rendered
|
|
20538
20633
|
height of adjacent select, combobox, input, and date fields instead of introducing a shorter action
|
|
@@ -20667,8 +20762,8 @@ control at all (see **Chip-only filters** below): its value belongs to a widget
|
|
|
20667
20762
|
page, so the bar renders only its active-filter chip and gives it no toolbar cell.
|
|
20668
20763
|
|
|
20669
20764
|
Every built-in (non-`'custom'`) filter definition additionally accepts optional `size: LyraSize`,
|
|
20670
|
-
`icon: unknown` and `labelVisibility: 'visible' | 'hidden'` fields, and every one whose
|
|
20671
|
-
control ships a clear action also accepts `clearable: boolean`. They are forwarded verbatim to that
|
|
20765
|
+
`icon: unknown` and `labelVisibility: 'visible' | 'hidden' | 'auto'` fields, and every one whose
|
|
20766
|
+
composed control ships a clear action also accepts `clearable: boolean`. They are forwarded verbatim to that
|
|
20672
20767
|
control's own same-named property — `icon` into its `start` slot exactly like a choice option's own
|
|
20673
20768
|
`icon`, rendered inert and `aria-hidden`; `clearable` reaching `<lr-date-input>` under its own
|
|
20674
20769
|
`with-clear` spelling, since that control has no `clearable`. `'text'` also accepts
|
|
@@ -20687,6 +20782,20 @@ so a compact toolbar row still names every field for assistive technology; visua
|
|
|
20687
20782
|
`::part(filter-control-label)` in CSS, the only previous option, removed the accessible name along
|
|
20688
20783
|
with the text.
|
|
20689
20784
|
|
|
20785
|
+
`labelVisibility: 'auto'` is the width-dependent middle between the two. It renders exactly what
|
|
20786
|
+
`'visible'` renders — the same stacked label element, the same accessible name computed from it, no
|
|
20787
|
+
`aria-label` and no placeholder fallback — and the bar's own stylesheet visually clips that label
|
|
20788
|
+
once the bar's allocation drops below `30rem`. The threshold is a container query on the host, so it
|
|
20789
|
+
reads the bar's own allocated width, not the viewport's: the same definitions render labelled across
|
|
20790
|
+
a dashboard and unlabelled in a 320px side panel, dialog or split pane, with no host-side breakpoint
|
|
20791
|
+
logic. The label element is never removed at any width, so the field's accessible name is identical
|
|
20792
|
+
in both states, and `'auto'` deliberately does not route the name onto the control the way
|
|
20793
|
+
`'hidden'` does — doing so would name a wide-allocation field twice. A `'checkbox-menu'` filter
|
|
20794
|
+
participates through its own trigger label run, the same one `'hidden'` already clips there. The
|
|
20795
|
+
threshold is fixed rather than themeable: a CSS container query's prelude cannot read a custom
|
|
20796
|
+
property (`var()` is not substituted in an at-rule prelude), so a `--lr-*` hook for it would parse
|
|
20797
|
+
and silently never apply.
|
|
20798
|
+
|
|
20690
20799
|
`'combobox'` also accepts the same `debounce?: number` (ms) `'text'` already had: it coalesces a
|
|
20691
20800
|
burst of rapid selection changes (picks, a multi-select toggle, an
|
|
20692
20801
|
`allowCustomValue`/`allowCreate` commit, or the clear action) into one delayed commit. Unlike
|
|
@@ -20716,6 +20825,8 @@ makes that text visually hidden — never removed — so the button keeps its ac
|
|
|
20716
20825
|
one case where the hidden label would be the *only* thing the button says (hidden routing, no
|
|
20717
20826
|
declared `placeholder`, nothing selected) the label routes to the visible summary instead of being
|
|
20718
20827
|
emitted twice, so the trigger's accessible name stays "Teams", never "Teams Teams".
|
|
20828
|
+
`labelVisibility: 'auto'` clips that same trigger label run, and only below the `30rem` threshold —
|
|
20829
|
+
the label run is always emitted under `'auto'`, since nothing is routed to the summary there.
|
|
20719
20830
|
|
|
20720
20831
|
Because its trigger is a button rather than a field, a `required` `'checkbox-menu'` deliberately
|
|
20721
20832
|
renders **no** required asterisk and sets **no** `aria-invalid`: the shared required marker has no
|
|
@@ -27222,6 +27333,21 @@ LyraMapLegendPattern }`, where `LyraMapLegendPattern` is `'solid' | 'diagonal' |
|
|
|
27222
27333
|
and their point-icon defaults, and the same validation applies (path data only, at most 8,192
|
|
27223
27334
|
characters, positive `viewBox` dimensions). An unusable record is dropped and that row keeps
|
|
27224
27335
|
rendering its colour swatch, exactly as a row that supplies no `icon` does.
|
|
27336
|
+
A row may finally carry a `group`: the section it belongs to, for a key that describes two
|
|
27337
|
+
layers at once and otherwise could not say which rows belong to which. The rule is pinned
|
|
27338
|
+
rather than inferred: **consecutive** entries sharing an identical `group` render as one section
|
|
27339
|
+
— a visible heading plus a `role="group"` that heading names — an entry with **no** `group`
|
|
27340
|
+
keeps its **declared** position rather than being hoisted above or sunk below a section, and a
|
|
27341
|
+
`group` that reappears after an interruption opens a *second* section rather than reordering
|
|
27342
|
+
rows to merge them. Declaration order is the one thing the legend never rewrites, because the
|
|
27343
|
+
order is itself information about the map. `group` is caller-supplied **data**: it renders
|
|
27344
|
+
verbatim and is never resolved through the locale catalogue. It is trimmed and bounded to 256
|
|
27345
|
+
characters (ellipsized, since it is rendered prose rather than a key matched against
|
|
27346
|
+
`point.field`); a non-string, empty or whitespace-only value leaves no `group` property on the
|
|
27347
|
+
frozen row at all, so an empty string means "ungrouped" instead of an empty heading. Like the
|
|
27348
|
+
row-level `value`, it does not count toward the aggregate label budget — the rendered total is
|
|
27349
|
+
already finite and stated: at most one heading per rendered row, so at most 100 of them. A
|
|
27350
|
+
section is not a row: the 100-row cap and the `legend-limit` summary count rows, never sections.
|
|
27225
27351
|
- readonly `legendProjection: LyraMapLegendProjection` — frozen `{ inputCount, renderedCount,
|
|
27226
27352
|
omittedCount, truncatedLabelCount, truncated }` result for the latest assignment. A truncated
|
|
27227
27353
|
projection renders a localized visible `1–N of M items` summary rather than silently claiming
|
|
@@ -27234,6 +27360,21 @@ omittedCount, truncatedLabelCount, truncated }` result for the latest assignment
|
|
|
27234
27360
|
roving tabindex: a 100-row interactive legend contributes 100 tab stops, exactly as a 100-series
|
|
27235
27361
|
`lr-chart` legend does. Each interactive row also grows to the shared `--lr-icon-button-size`
|
|
27236
27362
|
hit-area floor (WCAG 2.5.8), which the panel's existing `max-block-size` and scrolling contain.
|
|
27363
|
+
- `legendCollapsible: boolean = false` (attribute `legend-collapsible`, reflected) — opt-in: renders
|
|
27364
|
+
a `legend-disclosure` `button` inside the panel that collapses the key down to its header, so a
|
|
27365
|
+
large legend stops permanently covering part of the map. Unset, the panel renders exactly what it
|
|
27366
|
+
rendered before — no button, no `id` minted on the row list, and no `hidden` attribute anywhere.
|
|
27367
|
+
Collapsing hides the gradient bar, the rows, the `legend-limit` summary and the trailing `legend`
|
|
27368
|
+
slot; the `legend-start` slot and the disclosure itself stay visible, so a slotted header survives
|
|
27369
|
+
the collapse and the control that restores the key is never what the collapse hides.
|
|
27370
|
+
- `legendOpen: boolean = true` (attribute `legend-open`, reflected) — whether a `legendCollapsible`
|
|
27371
|
+
panel is expanded. It defaults **open**, so adding only `legendCollapsible` never hides an existing
|
|
27372
|
+
key, and it does nothing at all while `legendCollapsible` is unset. Because it is a
|
|
27373
|
+
`true`-defaulting boolean it uses a custom attribute converter, so `legend-open="false"` parses —
|
|
27374
|
+
the bare presence-based boolean form cannot express `false` at all — and the reflection follows the
|
|
27375
|
+
same converter: open (the default) reflects as an **absent** attribute and collapsed reflects as
|
|
27376
|
+
`legend-open="false"`. It is controlled public state and survives a disconnect/reconnect. Assigning
|
|
27377
|
+
it programmatically reconciles the rendered panel and emits nothing.
|
|
27237
27378
|
- `hiddenCategories: readonly string[] = []` (attribute: false) — the complete controlled set of
|
|
27238
27379
|
muted category keys, mirroring `lr-chart`'s `hiddenDatasets`. Clone-owned and frozen; non-string,
|
|
27239
27380
|
empty, whitespace-only and duplicate entries are dropped (first occurrence wins, matching
|
|
@@ -27522,8 +27663,8 @@ payload beside the map.
|
|
|
27522
27663
|
`LyraMapHeatmapZoomValue`, `LyraMapLineOptions`, `LyraMapPointOptions`, `LyraMapPointRadiusOptions`,
|
|
27523
27664
|
`LyraMapPointRadiusInterpolation`, `LyraMapPointIcon`, `LyraMapPointIconMode`,
|
|
27524
27665
|
`LyraMapPointIconLineCap`, `LyraMapPointIconLineJoin`, `LyraMapMarker`, `LyraMapMarkerActivationDetail`,
|
|
27525
|
-
`LyraMapMarkerActivationSource`, `LyraMapLegendToggleDetail`, `
|
|
27526
|
-
`LyraMapInstance`.
|
|
27666
|
+
`LyraMapMarkerActivationSource`, `LyraMapLegendToggleDetail`, `LyraMapLegendPanelToggleDetail`,
|
|
27667
|
+
`LyraMapStyleSpecification`, and `LyraMapInstance`.
|
|
27527
27668
|
The former `LegendEntry`, `ChoroplethLayer`, `GeoJsonDataLayer`, and `MapMarker` names are removed
|
|
27528
27669
|
in v9 rather than retained as aliases.
|
|
27529
27670
|
|
|
@@ -27549,6 +27690,13 @@ the set and assign its own value instead. There is deliberately no second, confi
|
|
|
27549
27690
|
committed state is `hiddenCategories`, which the host already observes, so a paired before/after
|
|
27550
27691
|
vocabulary would be permanent public surface nobody asked for. The event is a DOM-interaction
|
|
27551
27692
|
proposal only, so a programmatic `hiddenCategories` assignment reconciles without emitting it.
|
|
27693
|
+
Also `lr-map-legend-panel-toggle` (**cancelable**; frozen `LyraMapLegendPanelToggleDetail { open }` —
|
|
27694
|
+
the proposed `legendOpen` value), fired once when the `legendCollapsible` disclosure is activated by
|
|
27695
|
+
pointer or by Enter/Space. It is the *panel's* disclosure, not a *category's* visibility, so it
|
|
27696
|
+
deliberately does not reuse `lr-map-legend-toggle`. `preventDefault()` is the same genuine veto:
|
|
27697
|
+
`legendOpen` is not written, the rendered rows and the disclosure's `aria-expanded` do not change,
|
|
27698
|
+
so a host can own the open state and assign its own value from `event.detail.open`. A programmatic
|
|
27699
|
+
`legendOpen` assignment reconciles without emitting it, so a controlled host cannot loop.
|
|
27552
27700
|
Also `lr-map-marker-activate` (non-cancelable; frozen `LyraMapMarkerActivationDetail { id, lngLat,
|
|
27553
27701
|
marker, source }`; `id` is the trimmed explicit identity or `undefined`, `marker` is the accepted
|
|
27554
27702
|
declarative snapshot, and `source` is `'pointer' | 'keyboard'`), and `lr-map-click`
|
|
@@ -27571,9 +27719,14 @@ markup as described above.
|
|
|
27571
27719
|
|
|
27572
27720
|
**Slots:** `legend` — custom legend content, rendered inside the legend panel's own layout so it
|
|
27573
27721
|
stays positioned with the map instead of floating beside it. Supplying it opens the panel even
|
|
27574
|
-
when `legend` and `legendGradient` are both empty.
|
|
27575
|
-
|
|
27576
|
-
|
|
27722
|
+
when `legend` and `legendGradient` are both empty. `legend-start` — the same extension point at the
|
|
27723
|
+
**top** of the panel: it renders ahead of the gradient bar and every projected row, where `legend`
|
|
27724
|
+
renders after them, so a host-authored panel header is no longer forced to be a footer. Content in
|
|
27725
|
+
it alone opens the panel too, and neither slot is ever made interactive by `legendInteractive`,
|
|
27726
|
+
which only reaches rows projected from `legend`.
|
|
27727
|
+
|
|
27728
|
+
**CSS parts:** `base`, `container`, `legend`, `legend-disclosure`, `legend-disclosure-icon`,
|
|
27729
|
+
`legend-group`, `legend-group-heading`, `legend-swatch`, `legend-toggle`,
|
|
27577
27730
|
`legend-toggle-hidden`, `legend-gradient`, `legend-lo`,
|
|
27578
27731
|
`legend-hi`, `legend-limit`, `marker`, `popup`,
|
|
27579
27732
|
`popup-content`, `popup-close-button`, `attribution`, `attribution-toggle`, `navigation`,
|
|
@@ -27611,6 +27764,26 @@ quiet text token, so the label keeps AA contrast rather than fading with the who
|
|
|
27611
27764
|
forced colors the hidden row falls back to `line-through`, which survives a system-color collapse.
|
|
27612
27765
|
Each activation is announced through the shared light-DOM polite live region, using the same
|
|
27613
27766
|
`legendTypeShown`/`legendTypeHidden` strings `lr-graph-legend` announces with.
|
|
27767
|
+
Under `legendCollapsible`, `legend-disclosure` is a native `button` whose **visible localized text
|
|
27768
|
+
is its accessible name** and whose `aria-expanded` renders the literal `"true"`/`"false"` — never
|
|
27769
|
+
omitted — with `aria-controls` naming the row list in the same shadow root (idrefs do not cross
|
|
27770
|
+
shadow boundaries, so this is deliberately not an idref into the light DOM). It carries the shared
|
|
27771
|
+
`--lr-icon-button-size` hit-area floor, and `legend-disclosure-icon` is the decorative chevron it
|
|
27772
|
+
rotates: the shared icon set ships one right-pointing glyph and asks callers to rotate the wrapping
|
|
27773
|
+
part, so collapsed points along the reading direction and expanded points down in both directions.
|
|
27774
|
+
The rotation runs on `--lr-transition-fast`, which the token layer already flattens under
|
|
27775
|
+
`prefers-reduced-motion`. A collapsed panel hides its gradient, rows, `legend-limit` summary and
|
|
27776
|
+
trailing `legend` slot with the plain `hidden` attribute, so they leave layout, the accessibility
|
|
27777
|
+
tree and the tab order together.
|
|
27778
|
+
When at least one entry carries a `group`, each consecutive run renders inside `legend-group`
|
|
27779
|
+
(`role="group"`, named by its own `legend-group-heading` through `aria-labelledby`) and **each run
|
|
27780
|
+
gets its own `role="list"`**, because a `list` may only own `listitem`s — a `group` sitting directly
|
|
27781
|
+
inside the outer list is an `aria-required-children`/`aria-required-parent` violation. The outer
|
|
27782
|
+
container therefore drops its own `role` in that case; a legend with no groups keeps the single
|
|
27783
|
+
`role="list"` it has always had. Row `aria-posinset`/`aria-setsize` stay whole-key values inside a
|
|
27784
|
+
section: `aria-setsize` already reports the *input* count so a bounded key stays honest, and a
|
|
27785
|
+
dropped row carries no attributable group, so a section adds a labelled sub-region without
|
|
27786
|
+
renumbering the key.
|
|
27614
27787
|
`legend-limit` is the localized bounded-projection summary. The five peer-chrome parts project
|
|
27615
27788
|
stable Lyra names onto MapLibre-generated DOM without erasing peer-supplied part tokens;
|
|
27616
27789
|
`marker` retains a 24px minimum target in both axes even when a peer/custom marker has no intrinsic
|
|
@@ -30352,6 +30525,7 @@ These named interfaces and helper signatures are available to typed integrations
|
|
|
30352
30525
|
readonly label: string;
|
|
30353
30526
|
readonly pattern: LyraMapLegendPattern;
|
|
30354
30527
|
readonly icon?: LyraMapPointIcon | Omit<LyraMapPointIcon, 'value'>;
|
|
30528
|
+
readonly group?: string;
|
|
30355
30529
|
}`
|
|
30356
30530
|
Import: `@aceshooting/lyra-ui/components/media/map/map.class.js`.
|
|
30357
30531
|
`LyraMapLegendProjection {
|
|
@@ -30383,6 +30557,10 @@ These named interfaces and helper signatures are available to typed integrations
|
|
|
30383
30557
|
readonly hiddenCategories: readonly string[];
|
|
30384
30558
|
}`
|
|
30385
30559
|
Import: `@aceshooting/lyra-ui/components/media/map/map.class.js`.
|
|
30560
|
+
`LyraMapLegendPanelToggleDetail {
|
|
30561
|
+
readonly open: boolean;
|
|
30562
|
+
}`
|
|
30563
|
+
Import: `@aceshooting/lyra-ui/components/media/map/map.class.js`.
|
|
30386
30564
|
`LyraMapStyleSpecification {
|
|
30387
30565
|
readonly version: 8;
|
|
30388
30566
|
readonly sources: Readonly<Record<string, unknown>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aceshooting/lyra-ui",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.1.0",
|
|
4
4
|
"description": "Free, independent Lit web components: an open-source alternative to Shoelace and Web Awesome with accessible forms, dashboards, charts, and agent UI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web-components",
|