@aceshooting/lyra-ui 0.1.3 → 0.1.5
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/README.md +2 -8
- package/custom-elements.json +4046 -3214
- package/dist/components/chart/bar-chart.js +17 -4
- package/dist/components/chart/box-plot.d.ts +8 -1
- package/dist/components/chart/box-plot.js +40 -4
- package/dist/components/chart/box-plot.styles.js +4 -0
- package/dist/components/chart/bubble-chart.d.ts +1 -2
- package/dist/components/chart/bubble-chart.js +17 -4
- package/dist/components/chart/chart-loader.d.ts +21 -4
- package/dist/components/chart/chart-loader.js +39 -12
- package/dist/components/chart/chart.d.ts +9 -3
- package/dist/components/chart/chart.js +42 -21
- package/dist/components/chart/doughnut-chart.d.ts +1 -2
- package/dist/components/chart/doughnut-chart.js +17 -4
- package/dist/components/chart/histogram-bin.js +12 -2
- package/dist/components/chart/histogram.d.ts +2 -2
- package/dist/components/chart/histogram.js +16 -2
- package/dist/components/chart/line-chart.js +17 -4
- package/dist/components/chart/pie-chart.d.ts +1 -2
- package/dist/components/chart/pie-chart.js +17 -4
- package/dist/components/chart/polar-area-chart.d.ts +1 -2
- package/dist/components/chart/polar-area-chart.js +17 -4
- package/dist/components/chart/radar-chart.d.ts +1 -2
- package/dist/components/chart/radar-chart.js +17 -4
- package/dist/components/chart/scatter-chart.js +17 -4
- package/dist/components/combobox/combobox.js +3 -4
- package/dist/components/combobox/combobox.stories.d.ts +6 -0
- package/dist/components/combobox/combobox.stories.js +27 -0
- package/dist/components/combobox/combobox.styles.js +2 -2
- package/dist/components/date-picker/date-input.stories.d.ts +5 -0
- package/dist/components/date-picker/date-input.stories.js +12 -0
- package/dist/components/date-picker/date-input.styles.js +2 -2
- package/dist/components/date-picker/date-picker.stories.d.ts +6 -0
- package/dist/components/date-picker/date-picker.stories.js +13 -0
- package/dist/components/empty/empty.d.ts +1 -1
- package/dist/components/empty/empty.js +1 -1
- package/dist/components/export-button/export-button.d.ts +2 -2
- package/dist/components/export-button/export-button.js +2 -2
- package/dist/components/file-input/accept.d.ts +1 -2
- package/dist/components/file-input/accept.js +1 -2
- package/dist/components/file-input/file-input.d.ts +2 -2
- package/dist/components/file-input/file-input.js +2 -2
- package/dist/components/flag/flag.stories.d.ts +5 -0
- package/dist/components/flag/flag.stories.js +17 -0
- package/dist/components/flag/flag.styles.js +1 -1
- package/dist/components/gauge/gauge.d.ts +1 -1
- package/dist/components/gauge/gauge.js +1 -1
- package/dist/components/graph/graph.d.ts +33 -4
- package/dist/components/graph/graph.js +146 -30
- package/dist/components/heatmap/calendar-grid.d.ts +31 -0
- package/dist/components/heatmap/calendar-grid.js +52 -0
- package/dist/components/heatmap/heatmap-scale.d.ts +8 -0
- package/dist/components/heatmap/heatmap-scale.js +21 -0
- package/dist/components/heatmap/heatmap.d.ts +19 -0
- package/dist/components/heatmap/heatmap.js +133 -20
- package/dist/components/heatmap/heatmap.styles.js +4 -0
- package/dist/components/map/map.d.ts +15 -0
- package/dist/components/map/map.js +89 -7
- package/dist/components/playback/playback.d.ts +2 -2
- package/dist/components/playback/playback.js +8 -2
- package/dist/components/skeleton/skeleton.d.ts +2 -2
- package/dist/components/skeleton/skeleton.js +2 -2
- package/dist/components/sparkline/sparkline.stories.d.ts +7 -0
- package/dist/components/sparkline/sparkline.stories.js +17 -0
- package/dist/components/stat/stat.d.ts +1 -1
- package/dist/components/stat/stat.js +6 -7
- package/dist/components/stat/stat.stories.d.ts +5 -0
- package/dist/components/stat/stat.stories.js +16 -0
- package/dist/components/table/table.d.ts +1 -2
- package/dist/components/table/table.js +1 -2
- package/dist/components/table/table.styles.js +2 -3
- package/dist/components/time-range/time-range.js +4 -4
- package/dist/components/toast/toast.stories.d.ts +5 -0
- package/dist/components/toast/toast.stories.js +32 -0
- package/dist/components/tree/tree-node.d.ts +2 -2
- package/dist/components/tree/tree-node.js +2 -2
- package/dist/internal/form-associated.js +1 -2
- package/dist/internal/icons.js +1 -1
- package/dist/internal/tokens.styles.js +3 -3
- package/llms-full.txt +59 -83
- package/llms.txt +1 -2
- package/package.json +4 -1
|
@@ -147,10 +147,10 @@ export class LyraTimeRange extends LyraElement {
|
|
|
147
147
|
this.end = Math.min(hi, Math.max(lo, this.end));
|
|
148
148
|
}
|
|
149
149
|
// Keep start <= end regardless of which side changed (a controlled
|
|
150
|
-
// caller may set only `end`, e.g. two-way-binding an external store
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
150
|
+
// caller may set only `end`, e.g. two-way-binding an external store).
|
|
151
|
+
// The handle that just moved wins; the other side is pulled to meet it,
|
|
152
|
+
// mirroring the direction `clamp()` already uses during interactive
|
|
153
|
+
// dragging.
|
|
154
154
|
if (changed.has('start') || changed.has('min') || changed.has('max')) {
|
|
155
155
|
this.start = Math.min(this.start, this.end);
|
|
156
156
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import { toast } from '../../lyra.js';
|
|
3
|
+
const meta = {
|
|
4
|
+
title: 'Toast',
|
|
5
|
+
tags: ['autodocs'],
|
|
6
|
+
parameters: {
|
|
7
|
+
docs: {
|
|
8
|
+
description: {
|
|
9
|
+
component: 'Click a button to fire a toast via the `toast()` helper — the ergonomic entry point that lazily mounts a single `<lyra-toast>` region on `document.body`.',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
export default meta;
|
|
15
|
+
export const Triggers = {
|
|
16
|
+
render: () => html `
|
|
17
|
+
<div style="display:flex; gap:1rem;">
|
|
18
|
+
<button @click=${() => toast('Just so you know')}>Neutral</button>
|
|
19
|
+
<button @click=${() => toast({ message: 'Saved!', variant: 'success' })}>Success</button>
|
|
20
|
+
<button
|
|
21
|
+
@click=${() => toast({
|
|
22
|
+
message: 'Item deleted',
|
|
23
|
+
variant: 'danger',
|
|
24
|
+
duration: 0,
|
|
25
|
+
action: { label: 'Undo', onClick: (item) => item.hide() },
|
|
26
|
+
})}
|
|
27
|
+
>
|
|
28
|
+
Danger + action
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
`,
|
|
32
|
+
};
|
|
@@ -9,8 +9,8 @@ import type { TreeItem } from './tree.js';
|
|
|
9
9
|
* the *host* element, not the internal `[part="row"]` div — that makes this
|
|
10
10
|
* node's own nested children (rendered in *its own* shadow root as further
|
|
11
11
|
* `role="group"` content) genuine DOM descendants of the treeitem, which is
|
|
12
|
-
* what the WAI-ARIA treeitem pattern requires (
|
|
13
|
-
*
|
|
12
|
+
* what the WAI-ARIA treeitem pattern requires (previously a shadow-root
|
|
13
|
+
* sibling).
|
|
14
14
|
*
|
|
15
15
|
* @customElement lyra-tree-node
|
|
16
16
|
* @csspart row, toggle, label, badge
|
|
@@ -81,8 +81,8 @@ const styles = css `
|
|
|
81
81
|
* the *host* element, not the internal `[part="row"]` div — that makes this
|
|
82
82
|
* node's own nested children (rendered in *its own* shadow root as further
|
|
83
83
|
* `role="group"` content) genuine DOM descendants of the treeitem, which is
|
|
84
|
-
* what the WAI-ARIA treeitem pattern requires (
|
|
85
|
-
*
|
|
84
|
+
* what the WAI-ARIA treeitem pattern requires (previously a shadow-root
|
|
85
|
+
* sibling).
|
|
86
86
|
*
|
|
87
87
|
* @customElement lyra-tree-node
|
|
88
88
|
* @csspart row, toggle, label, badge
|
|
@@ -62,8 +62,7 @@ export function FormAssociated(Base) {
|
|
|
62
62
|
/**
|
|
63
63
|
* Recomputes `ElementInternals`'s validity state. Without this,
|
|
64
64
|
* `internals` defaults to permanently "valid" and `required` never
|
|
65
|
-
* blocks form submission
|
|
66
|
-
* the 2026-07-10 cross-repo audit found (forms-core §internal/, High).
|
|
65
|
+
* blocks form submission.
|
|
67
66
|
*/
|
|
68
67
|
updateValidity() {
|
|
69
68
|
if (this.required && this._value === '') {
|
package/dist/internal/icons.js
CHANGED
|
@@ -5,7 +5,7 @@ import { svg } from 'lit';
|
|
|
5
5
|
* date-input calendar toggle) as literal Unicode/HTML-entity/emoji text
|
|
6
6
|
* glyphs — font-dependent, non-recolorable beyond plain text color, and
|
|
7
7
|
* inconsistent in weight/size across the OS/browser font stack a host page
|
|
8
|
-
* happens to ship
|
|
8
|
+
* happens to ship. These replace all of them.
|
|
9
9
|
*
|
|
10
10
|
* Every icon shares one 24x24 viewBox and one stroke-width so the whole set
|
|
11
11
|
* reads as one visual language; each renders at `1em` so it inherits the
|
|
@@ -22,7 +22,7 @@ export const tokens = css `
|
|
|
22
22
|
|
|
23
23
|
/* Motion — every component that animates (popovers, gauge fill, toast)
|
|
24
24
|
reads from these two instead of hand-rolling its own duration/easing,
|
|
25
|
-
so the library has one consistent rhythm
|
|
25
|
+
so the library has one consistent rhythm. */
|
|
26
26
|
--lyra-transition-fast: var(--wa-transition-fast, 120ms ease-out);
|
|
27
27
|
--lyra-transition-base: var(--wa-transition-normal, 180ms ease-out);
|
|
28
28
|
|
|
@@ -49,8 +49,8 @@ export const tokens = css `
|
|
|
49
49
|
/* Standalone (no Web Awesome theme) dark-mode fallback. A real --wa-* value
|
|
50
50
|
from a consumer's theme always wins — this only changes what a bare
|
|
51
51
|
lyra-ui component renders when dropped, unstyled, onto a dark host page
|
|
52
|
-
(
|
|
53
|
-
|
|
52
|
+
(previously zero dark-mode adaptation existed in the pure-fallback
|
|
53
|
+
case). */
|
|
54
54
|
@media (prefers-color-scheme: dark) {
|
|
55
55
|
:host {
|
|
56
56
|
--lyra-color-surface: var(--wa-color-surface-default, #1a1a1a);
|
package/llms-full.txt
CHANGED
|
@@ -64,7 +64,7 @@ Not a custom element itself — infrastructure every `lyra-*` component in the l
|
|
|
64
64
|
`lyra-skeleton`'s "Loading…" text).
|
|
65
65
|
|
|
66
66
|
**Known gotchas:**
|
|
67
|
-
-
|
|
67
|
+
- `required` is currently inert for real validation. `FormAssociated` never calls
|
|
68
68
|
`internals.setValidity(...)`; `ElementInternals` defaults to "valid" until that's called.
|
|
69
69
|
`checkValidity()`/`reportValidity()` always return `true`. Concretely:
|
|
70
70
|
`<form><lyra-date-input name="d" required></lyra-date-input></form>`; `form.reportValidity()`
|
|
@@ -72,14 +72,14 @@ Not a custom element itself — infrastructure every `lyra-*` component in the l
|
|
|
72
72
|
`:invalid`/`:user-invalid` match, no `aria-invalid`. This affects both `lyra-date-input` (via the
|
|
73
73
|
mixin) and `lyra-combobox` (via its own hand-rolled copy). **Do not rely on `required` for
|
|
74
74
|
constraint validation yet** — validate in application code until this is fixed.
|
|
75
|
-
-
|
|
75
|
+
- `formResetCallback()` always resets to blank (`''` / `[]`), not the field's
|
|
76
76
|
originally-constructed value. `form.reset()` on `<lyra-date-input value="2026-07-15">` clears it
|
|
77
77
|
to `''` instead of restoring `"2026-07-15"` the way a native input's `defaultValue` would.
|
|
78
|
-
-
|
|
78
|
+
- only `lyra-date-input` actually uses the shared mixin; `lyra-combobox` independently
|
|
79
79
|
hand-rolls the same internals/validity/reset logic (needed because its value can be an array in
|
|
80
80
|
`multiple` mode) — the two copies can silently diverge, and the `setValidity` gap above has to be
|
|
81
81
|
fixed in both places.
|
|
82
|
-
-
|
|
82
|
+
- no shared label/input association helper: `<label part="form-control-label">` in both
|
|
83
83
|
`lyra-combobox` and `lyra-date-input` is a DOM sibling of the input with no `id`/`for` pairing, so
|
|
84
84
|
clicking the visible label text doesn't focus the field the way a native `<label for>` would.
|
|
85
85
|
|
|
@@ -150,18 +150,18 @@ Multi-select submits as **repeated `FormData` entries** (not a joined string)
|
|
|
150
150
|
`new FormData(form).getAll(name)` behaves like a native multi-value control.
|
|
151
151
|
|
|
152
152
|
**Known gotchas:**
|
|
153
|
-
-
|
|
153
|
+
- `required` is declared but completely inert: never read for validity (see the shared
|
|
154
154
|
`internal/` gotchas above) **and** never reflected as `aria-required` onto the actual
|
|
155
155
|
`<input part="combobox-input">` — a screen-reader user gets zero indication the field is
|
|
156
156
|
mandatory even before the validity fix lands.
|
|
157
|
-
-
|
|
157
|
+
- declaratively-selected options are **ignored on initial render**:
|
|
158
158
|
`<lyra-option value="b" selected>` in markup never seeds the combobox's selection — only the
|
|
159
159
|
combobox's own state pushes down to options, never the reverse. Set `.value` programmatically
|
|
160
160
|
after mount instead of relying on the `selected` attribute in markup.
|
|
161
|
-
-
|
|
161
|
+
- naming drift from Web Awesome parity: the shipped attribute is **`with-clear`**, not
|
|
162
162
|
`clearable` (which is what `<wa-combobox>` uses) — don't copy-paste WA docs assuming a 1:1
|
|
163
163
|
attribute name here.
|
|
164
|
-
-
|
|
164
|
+
- no `size` variant (small/medium/large) unlike sibling `lyra-toast-item`.
|
|
165
165
|
- Full ARIA 1.2 combobox pattern (`role=combobox`, roving `aria-activedescendant`, real DOM focus
|
|
166
166
|
kept on the input) is implemented correctly — a genuine strength, safe to build on.
|
|
167
167
|
|
|
@@ -260,24 +260,24 @@ from the package root**, but worth knowing when reasoning about behavior): `pars
|
|
|
260
260
|
`resolveFirstDayOfWeek(value)`.
|
|
261
261
|
|
|
262
262
|
**Known gotchas:**
|
|
263
|
-
-
|
|
263
|
+
- `first-day-of-week="auto"` does **not** derive from `locale` despite the name:
|
|
264
264
|
`resolveFirstDayOfWeek()` hardcodes `0` (Sunday) whenever the value is `'auto'`, even though
|
|
265
265
|
`locale` is already used elsewhere (weekday labels, month title). A French/German-locale user gets
|
|
266
266
|
a Sunday-first grid instead of the Monday-first layout their OS would show. **Workaround:** pass
|
|
267
267
|
an explicit `first-day-of-week="mon"` (accepted values: `sun`/`mon`/`tue`/`wed`/`thu`/`fri`/`sat`)
|
|
268
268
|
if you need locale-correct week starts.
|
|
269
|
-
-
|
|
269
|
+
- (date-input only) — the calendar popover has **no Escape-to-close**: only
|
|
270
270
|
`Alt+ArrowDown` opens it; there's no keydown handling to `hide()` on Escape, unlike `lyra-combobox`
|
|
271
271
|
in the same package. A keyboard user must Tab all the way through the grid/nav/clear buttons or
|
|
272
272
|
click elsewhere to dismiss it.
|
|
273
|
-
-
|
|
273
|
+
- (date-input only) — opening the calendar doesn't move focus into it (`show()` only flips
|
|
274
274
|
`open`) — a keyboard user opening via `Alt+ArrowDown` lands on a visible popover with focus still
|
|
275
275
|
on the text input.
|
|
276
|
-
-
|
|
276
|
+
- one hardcoded, non-token color: selected/range-start/range-end day text is
|
|
277
277
|
`color: #fff` in `date-picker.styles.ts` — if you override
|
|
278
278
|
`--lyra-color-brand`/`--wa-color-brand-fill-loud` to something light, contrast on selected-day
|
|
279
279
|
text isn't guaranteed.
|
|
280
|
-
-
|
|
280
|
+
- (date-input only) — shares the mixin's reset-to-blank defect: `form.reset()` always clears to
|
|
281
281
|
`''` rather than restoring the originally-constructed `value`.
|
|
282
282
|
- The grid keyboard pattern (Arrow/PageUp/PageDown/Home/End navigation with correct focus
|
|
283
283
|
sequencing) is implemented correctly and safe to rely on.
|
|
@@ -365,11 +365,11 @@ re-mounts if removed) a single `<lyra-toast>` region on `document.body`.
|
|
|
365
365
|
```
|
|
366
366
|
|
|
367
367
|
**Known gotchas:**
|
|
368
|
-
-
|
|
368
|
+
- possible nested live-region double-announcement: the stack region is
|
|
369
369
|
`role="status" aria-live="polite"` **and** each `lyra-toast-item` independently self-assigns its
|
|
370
370
|
own `role` (`status`/`alert`) — nesting live regions can cause some screen readers to announce a
|
|
371
371
|
new toast twice. Plausible from the code, unverified against real AT.
|
|
372
|
-
-
|
|
372
|
+
- pause/resume-on-hover/focus (the component's main accessibility differentiator) has no
|
|
373
373
|
regression test coverage — don't assume a future refactor won't silently break it; test it
|
|
374
374
|
yourself if it's load-bearing for your app.
|
|
375
375
|
- Prefer the `toast()` helper over manually creating `<lyra-toast>`/`<lyra-toast-item>` — it already
|
|
@@ -459,7 +459,7 @@ pnpm add @aceshooting/lyra-flags # required peer — without it, <lyra-flag> r
|
|
|
459
459
|
- Rendering is async even when the peer *is* installed: `src` resolves after an `import()` +
|
|
460
460
|
resolver call, so there's a brief empty-render window on first paint/attribute change — don't
|
|
461
461
|
assume the `<img>` exists synchronously right after setting `country`/`language`.
|
|
462
|
-
-
|
|
462
|
+
- if `label` is omitted, the accessible name (`alt`) falls back to the **bare uppercase
|
|
463
463
|
ISO code** (e.g. `"FR"`, `"DE"`) — a screen reader will typically spell this out letter-by-letter
|
|
464
464
|
rather than announce a country name. Always pass an explicit `label` (e.g. `"France"`) if the flag
|
|
465
465
|
conveys real information rather than being purely decorative.
|
|
@@ -495,14 +495,14 @@ First-party "no data" state (no Web Awesome equivalent).
|
|
|
495
495
|
```
|
|
496
496
|
|
|
497
497
|
**Known gotchas:**
|
|
498
|
-
-
|
|
498
|
+
- no live-region semantics: a plain role-less `<div>`, so a list/table transitioning to
|
|
499
499
|
empty announces nothing to screen readers (unlike sibling `lyra-skeleton`'s `role="status"`).
|
|
500
|
-
-
|
|
500
|
+
- no compact/density variant; fixed `padding: var(--lyra-space-l)` and 2rem icon sizing
|
|
501
501
|
are tuned for full-page use, awkward embedded in a card or narrow container.
|
|
502
502
|
- Note: correctly works around the classic `:empty`-pseudo-class trap (a wrapper with a `<slot>`
|
|
503
503
|
inside can never match `:empty`) by tracking real slot assignment in JS (`hasIcon`/`hasActions`) —
|
|
504
|
-
`lyra-table` reuses this component for its own empty-rows state,
|
|
505
|
-
|
|
504
|
+
`lyra-table` reuses this component for its own empty-rows state, and `lyra-stat` (below) now uses
|
|
505
|
+
the same JS-tracked-slot-state pattern for its own icon/caption wrappers.
|
|
506
506
|
|
|
507
507
|
---
|
|
508
508
|
|
|
@@ -533,13 +533,13 @@ Loading placeholder (`text`/`circle`/`rect` shapes, `pulse`/`sheen` effects).
|
|
|
533
533
|
```
|
|
534
534
|
|
|
535
535
|
**Known gotchas:**
|
|
536
|
-
-
|
|
536
|
+
- every instance is an independent, simultaneously-firing `role="status"` live region
|
|
537
537
|
(via `willUpdate` setting `role="status"` plus visually-hidden "Loading…" text). A realistic
|
|
538
538
|
loading state (e.g. a 10×5 skeleton table = 50 instances) mounts 50 separate live regions at
|
|
539
539
|
nearly the same tick — expect screen readers to announce "Loading…" dozens of times, not once.
|
|
540
540
|
There's no grouping primitive to dedupe this; if it matters, wrap your own single `role="status"`
|
|
541
541
|
region around a set of skeletons and mark the individual ones `aria-hidden`.
|
|
542
|
-
-
|
|
542
|
+
- no `lines`/`count` shorthand for "N lines of skeleton text" — stamp out N elements
|
|
543
543
|
yourself.
|
|
544
544
|
- Respects `prefers-reduced-motion` (both effects) — safe to leave as-is for that concern.
|
|
545
545
|
|
|
@@ -557,11 +557,14 @@ KPI/stat card — value + unit + label + optional icon/trend/caption.
|
|
|
557
557
|
- `trend: number = NaN` (a `NaN` sentinel hides the trend pill entirely — set an actual number to
|
|
558
558
|
show it)
|
|
559
559
|
- `caption: string = ''`
|
|
560
|
+
- `goodDirection: 'up'|'down' = 'up'` (attribute `good-direction`) — which trend direction counts
|
|
561
|
+
as "good"; inverts arrow/color polarity for cost/latency/error-rate-style metrics where a
|
|
562
|
+
*decrease* is the win.
|
|
560
563
|
|
|
561
564
|
**Events:** none.
|
|
562
565
|
|
|
563
|
-
**Slots:** default (leading icon), `caption` (rich caption content —
|
|
564
|
-
|
|
566
|
+
**Slots:** default (leading icon), `caption` (rich caption content — overrides the `caption`
|
|
567
|
+
attribute when slotted content is provided)
|
|
565
568
|
|
|
566
569
|
**CSS parts:** `base`, `icon`, `label`, `value-row`, `value`, `unit`, `trend`, `caption`
|
|
567
570
|
|
|
@@ -577,23 +580,8 @@ the `variant`-colored value text and up/down trend pill).
|
|
|
577
580
|
```
|
|
578
581
|
|
|
579
582
|
**Known gotchas:**
|
|
580
|
-
-
|
|
581
|
-
|
|
582
|
-
contain a literal `<slot>` child, so `:empty` never matches regardless of whether content is
|
|
583
|
-
provided. `<lyra-stat label="x" value="1">` with **no** icon/caption still lays out both empty
|
|
584
|
-
wrappers (visible gap/padding). This is the exact bug `lyra-empty` (above) correctly works around
|
|
585
|
-
via JS-tracked slot state — that fix has not been ported here.
|
|
586
|
-
- **Medium** — the `caption` **slot does not override** the `caption` **attribute** despite the
|
|
587
|
-
intent implied by the docs: `render()` emits `${this.caption}<slot name="caption"></slot>`
|
|
588
|
-
unconditionally, so setting both concatenates rather than the slot replacing the attribute text.
|
|
589
|
-
Pick one or the other, don't set both.
|
|
590
|
-
- **Medium** — `trend` polarity is hardcoded "higher is better": `trend > 0` always renders an
|
|
591
|
-
up-arrow in the success color, `trend < 0` always down/danger, with no invert flag. For metrics
|
|
592
|
-
where a *decrease* is good (cost, latency, error rate, churn), pre-negate the number yourself —
|
|
593
|
-
but that also flips the arrow direction misleadingly, so there's no fully correct workaround
|
|
594
|
-
today.
|
|
595
|
-
- **Low** — the trend glyph (▲/▼/–) has no `aria-hidden`/accessible label, and there's no
|
|
596
|
-
`aria-live` for in-place `value`/`trend` updates.
|
|
583
|
+
- no `aria-live` for in-place `value`/`trend` updates — a value/trend change after first
|
|
584
|
+
render is not announced to screen readers.
|
|
597
585
|
|
|
598
586
|
---
|
|
599
587
|
|
|
@@ -614,6 +602,8 @@ of `rows` — this component only renders and emits intents.
|
|
|
614
602
|
- `moreLabel: string = 'Load more'` (attribute `more-label`)
|
|
615
603
|
- `emptyHeading: string = 'No data'` (attribute `empty-heading`)
|
|
616
604
|
- `emptyDescription: string = ''` (attribute `empty-description`)
|
|
605
|
+
- `noColumnsHeading: string = 'No columns configured'` (attribute `no-columns-heading`)
|
|
606
|
+
- `noColumnsDescription: string = ''` (attribute `no-columns-description`)
|
|
617
607
|
|
|
618
608
|
**Events:** `lyra-sort` (`detail: { key }`, fired on sortable-header activation), `lyra-row-click`
|
|
619
609
|
(`detail: { row }`), `lyra-load-more` (fired on the "load more" button)
|
|
@@ -643,17 +633,10 @@ body's `max-block-size`).
|
|
|
643
633
|
```
|
|
644
634
|
|
|
645
635
|
**Known gotchas:**
|
|
646
|
-
-
|
|
647
|
-
with **zero** `<th>`/`<td>` children — an ARIA-invalid row that's still `tabindex="0"` and
|
|
648
|
-
clickable despite no visible/accessible content. Only the zero-**rows** case is handled (falls
|
|
649
|
-
back to `<lyra-empty>`); always pass at least one column.
|
|
650
|
-
- **Medium** — a fresh `@click`/`@keydown` closure is allocated for every sortable header and every
|
|
651
|
-
row on **every render** — real, avoidable GC churn on wide/tall tables that re-render on each
|
|
652
|
-
sort.
|
|
653
|
-
- **Medium** — only single-row selection is modeled (`selectedKey: string | number | null`);
|
|
636
|
+
- only single-row selection is modeled (`selectedKey: string | number | null`);
|
|
654
637
|
there's no bulk-select/checkbox-column API — you must hand-roll a checkbox column entirely inside
|
|
655
638
|
a `cell()` callback if you need multi-select.
|
|
656
|
-
-
|
|
639
|
+
- no `caption`/`aria-label` property, so the rendered `role="grid"` has no accessible name
|
|
657
640
|
distinguishing it from any other grid on the page.
|
|
658
641
|
- Keyboard equivalents (Enter/Space) for both header-sort and row-activate are implemented and
|
|
659
642
|
solid — a genuine strength versus most siblings in this family.
|
|
@@ -692,15 +675,15 @@ gauge-specific custom property exists).
|
|
|
692
675
|
```
|
|
693
676
|
|
|
694
677
|
**Known gotchas:**
|
|
695
|
-
-
|
|
678
|
+
- no `aria-valuetext`: setting `valueLabel` (e.g. `"72°F"`) changes the visible SVG
|
|
696
679
|
text but never reaches the accessibility tree — `aria-valuenow` always carries the bare numeric
|
|
697
680
|
`value`. A screen reader announces the raw number, not your formatted string.
|
|
698
|
-
-
|
|
681
|
+
- the SVG `<text>` elements aren't `aria-hidden`, so they're exposed inside the same
|
|
699
682
|
`role="meter"` host that already carries `aria-valuenow`/`aria-label` — risks double/mismatched
|
|
700
683
|
announcements when `valueLabel` differs from the raw `value`.
|
|
701
|
-
-
|
|
684
|
+
- no color-threshold/variant support: a single fixed `--lyra-color-brand` fill regardless
|
|
702
685
|
of value (no amber/red-near-capacity idiom built in).
|
|
703
|
-
-
|
|
686
|
+
- no documented component-specific sizing custom property; host size is fixed em values
|
|
704
687
|
(`8em`/`12em` radial, `12em`/`1.5em` linear) — resize via plain CSS `width`/`height` on the
|
|
705
688
|
element instead.
|
|
706
689
|
- Divide-by-zero guarded (`max - min || 1`), and radial/linear share one component via the `type`
|
|
@@ -755,12 +738,7 @@ downloadBlob(content: string, filename: string, mime: string): void // trig
|
|
|
755
738
|
```
|
|
756
739
|
|
|
757
740
|
**Known gotchas:**
|
|
758
|
-
-
|
|
759
|
-
`lyra-combobox`/`lyra-date-input` in the same package, it has **no outside-pointerdown dismissal,
|
|
760
|
-
no Escape-to-close**, and `[part="trigger"]` never gets `aria-haspopup`/`aria-expanded`. Clicking
|
|
761
|
-
elsewhere on the page does not close an open format menu — handle this yourself (e.g. close on
|
|
762
|
-
your own outside-click listener) until fixed upstream.
|
|
763
|
-
- **Low** — XLSX isn't implemented (`ExportFormat` really is `'csv' | 'json'`) — don't pass
|
|
741
|
+
- XLSX isn't implemented (`ExportFormat` really is `'csv' | 'json'`) — don't pass
|
|
764
742
|
`'xlsx'` expecting it to work.
|
|
765
743
|
- CSV formula-injection guarding and the deferred (5s) `URL.revokeObjectURL` (works around Safari
|
|
766
744
|
cancelling in-flight downloads on immediate revoke) are genuine, safe-to-rely-on strengths.
|
|
@@ -804,19 +782,19 @@ Keyboard: focus a divider (`Tab`), then `ArrowRight`/`ArrowLeft` (horizontal) or
|
|
|
804
782
|
`ArrowDown`/`ArrowUp` (vertical) to resize by a fixed 2% step.
|
|
805
783
|
|
|
806
784
|
**Known gotchas:**
|
|
807
|
-
-
|
|
785
|
+
- `panelCount` is computed **once**, in `connectedCallback()`, from
|
|
808
786
|
`this.children.length`. Adding/removing a panel after mount (e.g. conditionally showing a side
|
|
809
787
|
panel) leaves `panelCount`/`sizes`/divider count silently stale — there's no
|
|
810
788
|
`slotchange`/`MutationObserver` to react to it. If your panel set can change, re-create the
|
|
811
789
|
`<lyra-split>` rather than mutating its children live.
|
|
812
|
-
-
|
|
790
|
+
- divider `aria-valuemax` (reported as `100 - min`) is only correct for exactly **two**
|
|
813
791
|
panels. With 3+ panels the real achievable max for divider `i` is `sizes[i] + sizes[i+1] - min`
|
|
814
792
|
(resizing only trades size between the adjacent pair) — e.g. three equal 33.3% panels with
|
|
815
793
|
`min=10` all report `aria-valuemax="90"` though the true achievable value is ~56.6. Don't trust
|
|
816
794
|
the reported max for 3+-panel layouts.
|
|
817
|
-
-
|
|
795
|
+
- no feasibility check on `min` vs. panel count (e.g. 3 panels with `min=40` is
|
|
818
796
|
unsatisfiable) — result is a silently frozen splitter with no warning.
|
|
819
|
-
-
|
|
797
|
+
- `localStorage.getItem`/`setItem` calls themselves are unguarded (only the `JSON.parse`
|
|
820
798
|
result is try/caught) — a blocked or quota-exceeded store can throw synchronously from inside a
|
|
821
799
|
`pointerup`/`keydown` handler.
|
|
822
800
|
- Pointer-drag lifecycle (pointer capture, window-listener cleanup on both drag-end and
|
|
@@ -960,15 +938,15 @@ resolved through a scratch canvas). Also consumes `--lyra-color-text-quiet` (axi
|
|
|
960
938
|
```
|
|
961
939
|
|
|
962
940
|
**Known gotchas:**
|
|
963
|
-
-
|
|
941
|
+
- the `ResizeObserver` is wired but effectively inert: `draw()` always sizes the canvas
|
|
964
942
|
as `PAD_LEFT + cols * cellSize`, never from the host's measured width, so a container resize
|
|
965
943
|
redraws identical dimensions. The stylesheet's `canvas { inline-size: 100% }` is also dead code —
|
|
966
944
|
`draw()` unconditionally sets an inline `canvas.style.width/height` on every redraw, and inline
|
|
967
945
|
style always wins over that external rule.
|
|
968
|
-
-
|
|
946
|
+
- `NaN`/non-finite cell values are **not** treated as no-data (only `-1` is): `NaN < 0`
|
|
969
947
|
is `false`, so it falls into the normal color-ramp branch, produces an invalid `fillStyle`, and
|
|
970
948
|
that cell silently keeps whatever color was left over from the previous loop iteration.
|
|
971
|
-
-
|
|
949
|
+
- moving the browser window repeatedly across displays with different pixel ratios
|
|
972
950
|
leaks one stale `MediaQueryList` listener per DPR crossing (each `onDprChange` creates a new
|
|
973
951
|
listener without removing the previous one).
|
|
974
952
|
- Per-cell values are not individually inspectable by assistive tech — the whole grid is one
|
|
@@ -1015,13 +993,13 @@ install with `pnpm add d3-force d3-drag d3-zoom d3-selection`).
|
|
|
1015
993
|
```
|
|
1016
994
|
|
|
1017
995
|
**Known gotchas:**
|
|
1018
|
-
-
|
|
996
|
+
- per-tick full re-render is expensive: every d3-force tick (up to ~300 by default,
|
|
1019
997
|
continuously while dragging via `alphaTarget(0.3)`) replaces `simNodes`/`simLinks` with fresh
|
|
1020
998
|
array references, forcing Lit to re-diff the entire node/link template and re-run
|
|
1021
999
|
`applyInteractions()`. Noticeable cost for graphs of a few hundred+ nodes.
|
|
1022
|
-
-
|
|
1000
|
+
- `chargeStrength`/`linkDistance` are **not** live-reactive: changing them post-mount
|
|
1023
1001
|
does nothing until `nodes`/`links` also change (only those two trigger `rebuildSimulation()`).
|
|
1024
|
-
-
|
|
1002
|
+
- zoom has no `.scaleExtent(...)` (unbounded in/out); pan/zoom/drag are pointer-only
|
|
1025
1003
|
with no keyboard equivalent. Links (`<line part="link">`) are click-only — no
|
|
1026
1004
|
`role`/`tabindex`/`aria-label`, unlike nodes which are properly keyboard-operable
|
|
1027
1005
|
(`tabindex="0"`, `role="button"`, Enter/Space).
|
|
@@ -1087,16 +1065,16 @@ indentation), plus the shared tokens listed above.
|
|
|
1087
1065
|
```
|
|
1088
1066
|
|
|
1089
1067
|
**Known gotchas:**
|
|
1090
|
-
-
|
|
1068
|
+
- the treeitem row has **zero keyboard interaction**: `role="treeitem"` has no
|
|
1091
1069
|
`tabindex` and no keydown handler, so it never enters the Tab order. The only keyboard-operable
|
|
1092
1070
|
control at all is the expand/collapse `<button>` — and that's removed (`tabindex="-1"`,
|
|
1093
1071
|
`aria-hidden`) for leaf nodes. `lyra-node-select` fires only from the label's `@click`, with no
|
|
1094
1072
|
keyboard equivalent, and there is no roving-tabindex/arrow-key navigation model.
|
|
1095
|
-
-
|
|
1073
|
+
- `role="group"` (the expanded-children wrapper) is a shadow-DOM **sibling**, not a DOM
|
|
1096
1074
|
descendant, of its `role="treeitem"` row — violates the WAI-ARIA treeitem pattern's
|
|
1097
1075
|
DOM-containment expectation. (The library's own axe test does not catch this — axe's structural
|
|
1098
1076
|
rules accept a group as a valid child of `role="tree"` regardless of true ownership.)
|
|
1099
|
-
-
|
|
1077
|
+
- by-id reconciliation (which preserves `expanded` state across data reassignment)
|
|
1100
1078
|
only applies at depth 0. Nested `children` arrays are rendered via a plain `.map()` with no keyed
|
|
1101
1079
|
`repeat()`, so reordering/inserting into a nested array can silently attach the wrong node's
|
|
1102
1080
|
`expanded` state.
|
|
@@ -1159,16 +1137,16 @@ properties only cascade downward); shared tokens `--lyra-space-xs`, `--lyra-colo
|
|
|
1159
1137
|
```
|
|
1160
1138
|
|
|
1161
1139
|
**Known gotchas:**
|
|
1162
|
-
-
|
|
1140
|
+
- "locked" `type` in the typed subclasses (below) is **not actually enforced**:
|
|
1163
1141
|
`<lyra-pie-chart type="bar">` or `el.type = 'bar'` at runtime silently turns it into a bar chart.
|
|
1164
1142
|
Conversely, `<lyra-chart type="pie">` already works today with no subclass needed at all —
|
|
1165
1143
|
`buildConfig()` passes `type` straight through unchecked.
|
|
1166
|
-
-
|
|
1144
|
+
- the `lyra-theme` window-event listener (meant for re-theming on theme switch) is
|
|
1167
1145
|
dead code: nothing in the library ever dispatches that event, and even if it were dispatched, no
|
|
1168
1146
|
`--lyra-*`/`getComputedStyle` color resolution happens anywhere in this component — Chart.js
|
|
1169
1147
|
always renders grid/tick/legend/tooltip text with its own hardcoded palette, which can read as
|
|
1170
1148
|
low-contrast in dark mode.
|
|
1171
|
-
-
|
|
1149
|
+
- generated `scales` config is cartesian (`x`/`y`/`y2`) only: for `lyra-radar-chart`/
|
|
1172
1150
|
`lyra-polar-area-chart` (which need a single radial `r` scale) and for `lyra-pie-chart`/
|
|
1173
1151
|
`lyra-doughnut-chart` (no scale at all), the inherited `xLabel`/`yLabel`/`beginAtZero`/`y2Label`
|
|
1174
1152
|
properties are silently inert with no warning. Reach the radial scale only via raw `config`.
|
|
@@ -1228,7 +1206,7 @@ Bins `values` into `bins` equal-width buckets and renders as a bar chart (extend
|
|
|
1228
1206
|
```
|
|
1229
1207
|
|
|
1230
1208
|
**Known gotchas:**
|
|
1231
|
-
-
|
|
1209
|
+
- **`bins <= 0` crashes** on non-empty `values`: `bins=0` throws `TypeError` inside
|
|
1232
1210
|
`binValues()` (bucket index resolves to `-1`, `buckets[-1].count++` fails); negative `bins` throws
|
|
1233
1211
|
`RangeError` even earlier (`Array.from({length: -1})`). No guard exists anywhere in the chain —
|
|
1234
1212
|
**always pass `bins >= 1`.**
|
|
@@ -1271,7 +1249,7 @@ independently of the base `chart-loader.ts`).
|
|
|
1271
1249
|
```
|
|
1272
1250
|
|
|
1273
1251
|
**Known gotchas:**
|
|
1274
|
-
-
|
|
1252
|
+
- no raw `config` passthrough (unlike every other chart tag) — limited to
|
|
1275
1253
|
`labels`/`boxes`/`legend`/`height`/`yLabel`/`beginAtZero`; can't reach the underlying
|
|
1276
1254
|
controller's own options (`itemRadius`, `outlierRadius`, `coef`).
|
|
1277
1255
|
- The peer-dependency-missing failure path (`.catch()` → warn + resolve `null`) is untested —
|
|
@@ -1330,11 +1308,11 @@ exists and was hit)
|
|
|
1330
1308
|
```
|
|
1331
1309
|
|
|
1332
1310
|
**Known gotchas:**
|
|
1333
|
-
-
|
|
1311
|
+
- clearing or swapping the choropleth **leaks the old layer**: `applyChoropleth()`
|
|
1334
1312
|
only ever adds or updates; setting `choropleth = undefined`, or changing `choropleth.sourceId` to
|
|
1335
1313
|
a different value, is a silent no-op — the previously added GeoJSON source and fill layer keep
|
|
1336
1314
|
rendering forever (there is no `removeLayer`/`removeSource` call anywhere).
|
|
1337
|
-
-
|
|
1315
|
+
- `mapStyle` is reactive but changing it after construction is a **silent no-op** —
|
|
1338
1316
|
unlike `center`/`zoom`, which do call `setCenter`/`setZoom` on change, there's no `setStyle()`
|
|
1339
1317
|
call wired up.
|
|
1340
1318
|
- No declarative point-marker API — only the single choropleth polygon-fill layer exists; placing
|
|
@@ -1392,7 +1370,7 @@ them via JS (`el.allowedMimeTypes = [...]`), not as a JSON string attribute; the
|
|
|
1392
1370
|
illustrative of intent only.
|
|
1393
1371
|
|
|
1394
1372
|
**Known gotchas:**
|
|
1395
|
-
-
|
|
1373
|
+
- **`accept` only constrains the native file-picker dialog; it is silently ignored on
|
|
1396
1374
|
the drag-drop path.** `isAllowed()` checks only `allowedMimeTypes`/`forbiddenMimeTypes` (exact
|
|
1397
1375
|
MIME-string match — no extension or `image/*`-wildcard support like native `accept` has);
|
|
1398
1376
|
`this.accept` itself is never read anywhere else in the component. Concretely:
|
|
@@ -1421,5 +1399,3 @@ These apply across the whole library rather than to any one component:
|
|
|
1421
1399
|
`package.json`'s `files` allowlist, with no `prepack`/publish CI step regenerating it — so a
|
|
1422
1400
|
manifest-driven IDE/tooling integration (e.g. custom-elements-manifest-based autocomplete) may
|
|
1423
1401
|
not see it in a published tarball until that packaging gap is fixed.
|
|
1424
|
-
- **Adoption status.** Zero real-world adoption of `@aceshooting/lyra-ui` currently exists in any
|
|
1425
|
-
consumer repo — treat any "battle-tested in production" framing with that caveat.
|
package/llms.txt
CHANGED
|
@@ -43,5 +43,4 @@ The library shipped in five incremental batches; `llms-full.txt` is ordered the
|
|
|
43
43
|
|
|
44
44
|
- Shared internals every tag builds on (`LyraElement` base class, the `FormAssociated` mixin, the
|
|
45
45
|
floating-ui `positioner`, and the `--lyra-*`/`--wa-*` design-token layer) are documented at the
|
|
46
|
-
top of `llms-full.txt`, along with cross-cutting notes
|
|
47
|
-
apply to the whole library.
|
|
46
|
+
top of `llms-full.txt`, along with cross-cutting packaging notes that apply to the whole library.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aceshooting/lyra-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Free, clean-room Lit web components — a companion to Web Awesome.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"publishConfig": {
|
|
21
21
|
"access": "public"
|
|
22
22
|
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
23
26
|
"files": [
|
|
24
27
|
"dist",
|
|
25
28
|
"custom-elements.json",
|