@santi020k/lumen-elements 3.0.0 → 4.0.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/README.md +497 -5
- package/dist/bullet-chart-html.d.ts +5 -0
- package/dist/bullet-chart-html.d.ts.map +1 -0
- package/dist/bullet-chart-html.js +56 -0
- package/dist/chart-activation.d.ts +15 -0
- package/dist/chart-activation.d.ts.map +1 -0
- package/dist/chart-activation.js +93 -0
- package/dist/chart-html.d.ts +22 -0
- package/dist/chart-html.d.ts.map +1 -0
- package/dist/chart-html.js +1 -0
- package/dist/comparison-chart-html.d.ts +4 -0
- package/dist/comparison-chart-html.d.ts.map +1 -0
- package/dist/comparison-chart-html.js +36 -0
- package/dist/components/ai-surfaces.d.ts +115 -0
- package/dist/components/ai-surfaces.d.ts.map +1 -0
- package/dist/components/ai-surfaces.js +270 -0
- package/dist/components/button.d.ts +51 -2
- package/dist/components/button.d.ts.map +1 -1
- package/dist/components/button.js +81 -2
- package/dist/components/card.d.ts +6 -0
- package/dist/components/card.d.ts.map +1 -1
- package/dist/components/card.js +6 -1
- package/dist/components/combobox.d.ts +1 -2
- package/dist/components/combobox.d.ts.map +1 -1
- package/dist/components/combobox.js +27 -121
- package/dist/components/dashboard.d.ts +41 -0
- package/dist/components/dashboard.d.ts.map +1 -0
- package/dist/components/dashboard.js +88 -0
- package/dist/components/device-frame.d.ts +31 -0
- package/dist/components/device-frame.d.ts.map +1 -0
- package/dist/components/device-frame.js +95 -0
- package/dist/components/foundations.d.ts +30 -2
- package/dist/components/foundations.d.ts.map +1 -1
- package/dist/components/foundations.js +27 -2
- package/dist/components/image-comparison.d.ts +33 -0
- package/dist/components/image-comparison.d.ts.map +1 -0
- package/dist/components/image-comparison.js +144 -0
- package/dist/components/media-selection.d.ts +41 -0
- package/dist/components/media-selection.d.ts.map +1 -0
- package/dist/components/media-selection.js +114 -0
- package/dist/components/media-viewport.d.ts +30 -0
- package/dist/components/media-viewport.d.ts.map +1 -0
- package/dist/components/media-viewport.js +108 -0
- package/dist/components/virtual-list.d.ts +38 -0
- package/dist/components/virtual-list.d.ts.map +1 -0
- package/dist/components/virtual-list.js +30 -0
- package/dist/components/visual-interactions.d.ts +65 -0
- package/dist/components/visual-interactions.d.ts.map +1 -0
- package/dist/components/visual-interactions.js +91 -0
- package/dist/components/world-map.d.ts +46 -0
- package/dist/components/world-map.d.ts.map +1 -0
- package/dist/components/world-map.js +312 -0
- package/dist/consumer-behaviors.d.ts +36 -0
- package/dist/consumer-behaviors.d.ts.map +1 -0
- package/dist/consumer-behaviors.js +1 -0
- package/dist/define.d.ts +229 -6
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +1 -1
- package/dist/element-base.js +1 -1
- package/dist/expanded-chart-html.d.ts +9 -0
- package/dist/expanded-chart-html.d.ts.map +1 -0
- package/dist/expanded-chart-html.js +61 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +34 -6
- package/styles/critical.css +634 -61
package/README.md
CHANGED
|
@@ -1,9 +1,80 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://lumen.santi020k.com">
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/santi020k/lumen/main/docs/assets/readme/package-dark.svg">
|
|
5
|
+
<img src="https://raw.githubusercontent.com/santi020k/lumen/main/docs/assets/readme/package-light.svg" alt="Lumen UI — Web. Native. Thoughtfully connected." width="1200" height="184">
|
|
6
|
+
</picture>
|
|
7
|
+
</a>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<h1 align="center">Lumen UI · Web Components</h1>
|
|
11
|
+
|
|
12
|
+
<p align="center">Custom elements · Browser-native composition · Shared styles</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://www.npmjs.com/package/@santi020k/lumen-elements"><img src="https://img.shields.io/npm/v/@santi020k/lumen-elements?style=flat-square&color=0369a0" alt="npm version"></a>
|
|
16
|
+
<a href="https://github.com/santi020k/lumen/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-13967e?style=flat-square" alt="MIT license"></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="https://lumen.santi020k.com/docs/frameworks/elements">Documentation</a>
|
|
21
|
+
·
|
|
22
|
+
<a href="https://www.npmjs.com/package/@santi020k/lumen-elements">npm</a>
|
|
23
|
+
·
|
|
24
|
+
<a href="https://github.com/santi020k/lumen/tree/main/packages/elements">Source</a>
|
|
25
|
+
·
|
|
26
|
+
<a href="https://github.com/santi020k/lumen/issues">Issues</a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
**Package:** `@santi020k/lumen-elements`
|
|
30
|
+
|
|
31
|
+
**On this page:** [Install](#install) · [Usage](#usage) · [Appearance presets](#appearance-presets) · [Resources](#resources)
|
|
32
|
+
|
|
33
|
+
<details>
|
|
34
|
+
<summary>Browse all sections</summary>
|
|
35
|
+
|
|
36
|
+
- [Install](#install)
|
|
37
|
+
- [Appearance presets](#appearance-presets)
|
|
38
|
+
- [Usage](#usage)
|
|
39
|
+
- [Language selection](#language-selection)
|
|
40
|
+
- [Forms](#forms)
|
|
41
|
+
- [Context navigation](#context-navigation)
|
|
42
|
+
- [Statistics](#statistics)
|
|
43
|
+
- [Data visualization](#data-visualization)
|
|
44
|
+
- [Optimized images](#optimized-images)
|
|
45
|
+
- [Interactive behavior](#interactive-behavior)
|
|
46
|
+
- [Motion](#motion)
|
|
47
|
+
- [Glass surfaces](#glass-surfaces)
|
|
48
|
+
- [Consumer composition recipes](#consumer-composition-recipes)
|
|
49
|
+
- [Exact amount fields](#exact-amount-fields)
|
|
50
|
+
- [Phone presentation in v4](#phone-presentation-in-v4)
|
|
51
|
+
- [Combobox keyboard behavior](#combobox-keyboard-behavior)
|
|
52
|
+
- [Content flow](#content-flow)
|
|
53
|
+
- [Attachment composition](#attachment-composition)
|
|
54
|
+
- [Dashboard composition](#dashboard-composition)
|
|
55
|
+
- [Combobox lifecycle](#combobox-lifecycle)
|
|
56
|
+
- [Device demonstrations](#device-demonstrations)
|
|
57
|
+
- [Visual interactions and product blocks](#visual-interactions-and-product-blocks)
|
|
58
|
+
- [World map](#world-map)
|
|
59
|
+
- [Studio media workspace](#studio-media-workspace)
|
|
60
|
+
- [Resources](#resources)
|
|
61
|
+
|
|
62
|
+
</details>
|
|
2
63
|
|
|
3
64
|
Web Components for Lumen UI.
|
|
4
65
|
|
|
5
66
|
This package registers standards-based custom elements for the shared Lumen primitive catalog.
|
|
6
67
|
|
|
68
|
+
`ImageComparison` form resets restore the latest externally configured `value`; user range input
|
|
69
|
+
does not replace that reset baseline.
|
|
70
|
+
|
|
71
|
+
See the [shared web form contracts](https://github.com/santi020k/lumen/blob/main/docs/form-controls.md) for value ownership, reset,
|
|
72
|
+
submission, disabled state, and event behavior.
|
|
73
|
+
|
|
74
|
+
Form controls use `visualSize` (`visual-size` in Elements) with `default`, `sm` and `lg`.
|
|
75
|
+
Select, PhoneInput and Segmented follow Input and NativeSelect; numeric input/select `size` keeps
|
|
76
|
+
its native meaning. See the [v4 migration guide](https://github.com/santi020k/lumen/blob/main/docs/migrating-v3-to-v4.md#form-control-visual-sizing).
|
|
77
|
+
|
|
7
78
|
## Install
|
|
8
79
|
|
|
9
80
|
```bash
|
|
@@ -31,6 +102,10 @@ The stylesheet defaults `--ui-font` to `"Montserrat", "Avenir Next", "Segoe UI",
|
|
|
31
102
|
It declares the family stack but does not bundle or load font files. Load Montserrat once through
|
|
32
103
|
your preferred delivery path, or override `--ui-font` in application CSS.
|
|
33
104
|
|
|
105
|
+
## Appearance presets
|
|
106
|
+
|
|
107
|
+
Wrap registered components in `data-lumen-preset="studio"`; ThemeBuilder preset buttons use `data-ui-theme-preset`. See [appearance presets](https://github.com/santi020k/lumen/blob/main/docs/appearance-presets.md).
|
|
108
|
+
|
|
34
109
|
## Usage
|
|
35
110
|
|
|
36
111
|
Register the elements once per custom-element registry, then use `lumen-*` tags anywhere HTML is
|
|
@@ -45,8 +120,8 @@ independently from the document registry.
|
|
|
45
120
|
</script>
|
|
46
121
|
|
|
47
122
|
<lumen-card>
|
|
48
|
-
<label
|
|
49
|
-
<lumen-input id="email" type="email" placeholder="you@example.com"></lumen-input>
|
|
123
|
+
<lumen-label id="email-label">Email</lumen-label>
|
|
124
|
+
<lumen-input aria-labelledby="email-label" id="email" type="email" placeholder="you@example.com"></lumen-input>
|
|
50
125
|
<lumen-button>Subscribe</lumen-button>
|
|
51
126
|
</lumen-card>
|
|
52
127
|
```
|
|
@@ -85,6 +160,18 @@ selection, dismissal, and focus behavior without importing the complete catalog.
|
|
|
85
160
|
still use `defineLumenElements`; do not replace the full entrypoint when the application needs a
|
|
86
161
|
behavior-backed element that does not yet have a granular module.
|
|
87
162
|
|
|
163
|
+
VirtualList also has an independent registration entrypoint, including windowing and cleanup:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { defineLumenVirtualList } from '@santi020k/lumen-elements/components/virtual-list'
|
|
167
|
+
|
|
168
|
+
defineLumenVirtualList()
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
It registers the same constructor as the full catalog and accepts an optional registry. Pair it
|
|
172
|
+
with the matching stylesheet. `pnpm run measure:selective-imports` compares this entry with the
|
|
173
|
+
complete catalog using an equivalent single-component consumer.
|
|
174
|
+
|
|
88
175
|
Foundation-only pages can register fifteen layout, composition, and accessibility elements as one
|
|
89
176
|
small implementation-level bundle:
|
|
90
177
|
|
|
@@ -222,6 +309,19 @@ or `variant="glass"` for selective translucency.
|
|
|
222
309
|
|
|
223
310
|
## Data visualization
|
|
224
311
|
|
|
312
|
+
The web visualization milestone adds `WaterfallChart` for signed changes and explicit totals, and
|
|
313
|
+
`Histogram` for precomputed numeric bins (`frequency="density"` for unequal widths). Line charts
|
|
314
|
+
support explicit continuous axes, annotations, optional keyboard/pointer/touch inspection, and
|
|
315
|
+
synchronized cursors. Heatmaps show labeled axes, a color legend, and explicit missing cells.
|
|
316
|
+
Heatmap JSON is validated as a complete collection: a malformed row rejects the dataset instead
|
|
317
|
+
of displaying a partial result. Null and nonfinite numeric measurements remain missing cells.
|
|
318
|
+
See the [visualization contracts](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md) and
|
|
319
|
+
[interactive web example](https://lumen.santi020k.com/docs/web/data-visualization).
|
|
320
|
+
|
|
321
|
+
Use `<lumen-histogram bins="...">` and `<lumen-waterfall-chart data="...">` with JSON arrays.
|
|
322
|
+
`<lumen-line-chart interactive x-scale="time" sync-group="report">` emits
|
|
323
|
+
`ui:chart-cursor-change` with `{ x }`. Set `annotations` to a JSON array.
|
|
324
|
+
|
|
225
325
|
Set serializable `series` data through the JavaScript property for application data. The JSON
|
|
226
326
|
attribute form is useful for static HTML and server output.
|
|
227
327
|
|
|
@@ -276,6 +376,16 @@ document-level toast controller events.
|
|
|
276
376
|
|
|
277
377
|
## Interactive behavior
|
|
278
378
|
|
|
379
|
+
Internal behavior methods use native JavaScript private members. Public properties, events and
|
|
380
|
+
registration APIs retain their existing contracts.
|
|
381
|
+
|
|
382
|
+
Disclosure keyboard navigation skips hidden or inert regions, invisible controls, and native
|
|
383
|
+
disabled controls, including a disabled fieldset. Available controls in a fieldset's first legend
|
|
384
|
+
retain their native keyboard behavior; removing `inert` makes a region available again.
|
|
385
|
+
|
|
386
|
+
Mentions keeps suggestion navigation on the textarea through `aria-activedescendant`; suggestion
|
|
387
|
+
buttons are excluded from the Tab sequence while Enter and pointer selection still insert a mention.
|
|
388
|
+
|
|
279
389
|
Registering the elements wires behavior-heavy primitives without a framework runtime. DataTable,
|
|
280
390
|
Dialog, Popover, DropdownMenu, ContextMenu, Tabs, Select, ThemeBuilder, Toast, Tooltip, forms,
|
|
281
391
|
Calendar, InputOTP, DateRangePicker, RichTextEditor, Schedule, Resizable, and VirtualList track the
|
|
@@ -283,6 +393,10 @@ Astro runtime's data event, validation, calendar grids, OTP segmentation, date r
|
|
|
283
393
|
text command, context menu, schedule drag/drop, resizable pane sizing, theme export, ARIA,
|
|
284
394
|
keyboard, Escape, dismissal, and toast controller semantics while keeping markup declarative and
|
|
285
395
|
Declarative-Shadow-DOM friendly.
|
|
396
|
+
Calendar attribute updates preserve the focused day in the owning document after iframe adoption.
|
|
397
|
+
Calendar form resets restore the latest configured `value` attribute; interactive date selections
|
|
398
|
+
do not replace that reset baseline. Form error summaries can focus associated native controls
|
|
399
|
+
outside the form subtree when their `form` attribute names that form.
|
|
286
400
|
FileUpload, Tour, Anchor, CopyButton, Progress, ScrollProgress, Transfer, Mentions, Cascader, and TreeSelect also run directly through
|
|
287
401
|
their registered custom elements; no Astro runtime or host controller is required.
|
|
288
402
|
Rich text controls may provide `data-ui-editor-value` for commands such as `formatBlock` and
|
|
@@ -291,6 +405,9 @@ formatting shortcuts, and keep toggle controls synchronized through `aria-presse
|
|
|
291
405
|
`lumen-tabs` keeps the selected trigger visible in narrow horizontal lists and emits
|
|
292
406
|
`ui:tabs-change`; import `LumenTabsChangeDetail` or `LumenTabsChangeEvent` for its typed detail.
|
|
293
407
|
|
|
408
|
+
Nested `lumen-tabs` and `lumen-code-tabs` keep independent selection and panel state. Keyboard
|
|
409
|
+
navigation skips disabled triggers and stays within the active tab group.
|
|
410
|
+
|
|
294
411
|
`<lumen-kanban-board>` and `<lumen-kanban-column value="…">` provide the same controlled board
|
|
295
412
|
contract. Mark ordinary card items with `data-ui-kanban-item` and put `data-ui-kanban-handle` on a
|
|
296
413
|
dedicated button. The board emits the cancellable `ui:kanban-move-request` event for keyboard,
|
|
@@ -326,7 +443,7 @@ Use `<lumen-error-state>` when a region or page cannot show its primary content.
|
|
|
326
443
|
heading, explanation, optional safe reference, and recovery actions in light DOM, label the region
|
|
327
444
|
with `aria-labelledby`, and use the documented `data-slot="error-state-*"` hooks. Lumen presents the
|
|
328
445
|
failure; application code owns exception capture, logging, and retry policy. See the repository
|
|
329
|
-
[Web Components error-handling guide](
|
|
446
|
+
[Web Components error-handling guide](https://github.com/santi020k/lumen/blob/main/docs/error-handling.md#web-components) for the complete
|
|
330
447
|
light-DOM structure, ARIA ownership, retry boundary, and verification guidance.
|
|
331
448
|
|
|
332
449
|
## Motion
|
|
@@ -345,6 +462,36 @@ The elements adapter exposes the same motion vocabulary as Astro and React. Use
|
|
|
345
462
|
<lumen-animated-number decimals="1" suffix="%" value="99.8"></lumen-animated-number>
|
|
346
463
|
```
|
|
347
464
|
|
|
465
|
+
### Elements labels and dialogs
|
|
466
|
+
|
|
467
|
+
`lumen-input` owns an internal native control. Name that control using `aria-labelledby` pointing
|
|
468
|
+
at a visible label, or `aria-label` on the host. A native label's `for` pointing only at the custom
|
|
469
|
+
host does not name the internal input. Use the public `lumen-label` for visible label styling.
|
|
470
|
+
Do not type a custom host as `HTMLInputElement` or `HTMLButtonElement`; use its exported element
|
|
471
|
+
class and runtime narrowing when accessing element-specific methods.
|
|
472
|
+
|
|
473
|
+
Use `lumen-dialog` as the behavior owner. Its public methods are `show(trigger?)` and `close()`,
|
|
474
|
+
not `showModal()` on the custom host. A native `dialog` child is its documented modal contract:
|
|
475
|
+
|
|
476
|
+
```html
|
|
477
|
+
<lumen-button data-ui-dialog-trigger="profile-dialog">Edit profile</lumen-button>
|
|
478
|
+
<lumen-dialog id="profile-dialog">
|
|
479
|
+
<dialog aria-labelledby="profile-title">
|
|
480
|
+
<h2 id="profile-title">Profile settings</h2>
|
|
481
|
+
<lumen-field>
|
|
482
|
+
<lumen-label id="profile-name-label">Display name</lumen-label>
|
|
483
|
+
<lumen-input aria-labelledby="profile-name-label" value="Ada"></lumen-input>
|
|
484
|
+
</lumen-field>
|
|
485
|
+
<lumen-button data-ui-dialog-close type="button">Cancel</lumen-button>
|
|
486
|
+
</dialog>
|
|
487
|
+
</lumen-dialog>
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
Register `Button`, `Dialog`, `Field`, `Input`, and `Label` through `defineLumenElements` before use.
|
|
491
|
+
The dialog behavior focuses its first focusable control, handles Escape, traps focus, and returns
|
|
492
|
+
focus to its trigger. Keep draft state in the form control or application; closing does not reset it.
|
|
493
|
+
Do not replace this behavior with a parallel native-dialog controller.
|
|
494
|
+
|
|
348
495
|
## Glass surfaces
|
|
349
496
|
|
|
350
497
|
Load `@santi020k/lumen-elements/styles.css` once, register the elements, then use the shared glass
|
|
@@ -360,7 +507,352 @@ attributes.
|
|
|
360
507
|
|
|
361
508
|
## Consumer composition recipes
|
|
362
509
|
|
|
363
|
-
See [consumer UI recipes](
|
|
510
|
+
See [consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md) for static React icons, responsive
|
|
364
511
|
record tables, keyboard-aware native sheets, whole-unit amount fields, adaptive editors, and
|
|
365
512
|
asynchronous action states. Each recipe identifies the public primitives and the behavior that
|
|
366
513
|
remains owned by the application.
|
|
514
|
+
|
|
515
|
+
## Exact amount fields
|
|
516
|
+
|
|
517
|
+
`lumen-amount-field` creates a visible localized input and a named hidden input containing the
|
|
518
|
+
complete ASCII decimal string. `ui:amount-change` reports `{ draft, value }`; incomplete drafts
|
|
519
|
+
have an undefined value. A host ID `amount` gives its visible input ID `amount-input` for labels.
|
|
520
|
+
|
|
521
|
+
```html
|
|
522
|
+
<lumen-amount-field name="amount" locale="es-CO" default-value="1234.50"
|
|
523
|
+
aria-label="Amount COP"></lumen-amount-field>
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
See [consumer workflows](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#executable-consumer-workflows) for
|
|
527
|
+
validation, reset and `auto-scroll` activity feeds.
|
|
528
|
+
|
|
529
|
+
### Accessible code examples
|
|
530
|
+
|
|
531
|
+
`<lumen-code variant="block" copy>` enhances its native `pre > code` child with a
|
|
532
|
+
copy button. Use `copy-label`, `copied-label`, and `error-label` for clipboard
|
|
533
|
+
feedback, and `code-label` to name its keyboard-scrollable source region. The same
|
|
534
|
+
labels can be placed on an enclosing `<lumen-code-tabs>`; child labels take
|
|
535
|
+
precedence. Copy emits `ui:copy-success` or `ui:copy-error` and announces feedback
|
|
536
|
+
in a live region. Clipboard failures leave the source available for manual copy.
|
|
537
|
+
|
|
538
|
+
Unwrapped `pre` children receive `tabindex="0"` and region semantics while keeping
|
|
539
|
+
authored accessible names. Generated names follow `code-label` updates and the current enclosing code tabs after reconnecting. Changing to `wrap="true"` removes only enhancer-added focus attributes.
|
|
540
|
+
Disabling `copy` removes only generated copy controls and cancels pending feedback.
|
|
541
|
+
With `wrap="true"`, the component does not add an extra
|
|
542
|
+
tab stop. Keep native `pre` and `code` children rather than placing source text in
|
|
543
|
+
HTML attributes.
|
|
544
|
+
|
|
545
|
+
### Virtual list and editor ownership
|
|
546
|
+
|
|
547
|
+
`VirtualList` displays fixed-height rows with inert spacers that retain the full scroll extent.
|
|
548
|
+
Rows stay mounted; use pagination when the initial DOM cost matters. Scrolling, resizing, sizing
|
|
549
|
+
changes and direct row changes refresh the window. Focused rows remain available. See the
|
|
550
|
+
[fixed-height list contract](https://github.com/santi020k/lumen/blob/main/docs/ai-usage.md#fixed-height-virtual-lists).
|
|
551
|
+
|
|
552
|
+
External rich-text engines should handle the cancelable `ui:editor-command-request` event before
|
|
553
|
+
execution and use `ui:editor-command` only for completion notifications. React also supports
|
|
554
|
+
`useRichTextEditor({ commandHandler })`. Disable native toolbar state syncing when the external
|
|
555
|
+
engine owns it. See the [editor guidance](https://github.com/santi020k/lumen/blob/main/docs/ai-usage.md).
|
|
556
|
+
|
|
557
|
+
## Phone presentation in v4
|
|
558
|
+
|
|
559
|
+
Phone inputs bundle the same offline flag artwork on every platform. The selected country shows
|
|
560
|
+
its flag and calling code inside one continuous input border. Country names remain in the native
|
|
561
|
+
picker and its accessible name; flags are supplementary. Unknown flag codes fall back to text.
|
|
562
|
+
|
|
563
|
+
Astro and React `PhoneInput` accept `disabled`, `readOnly`, `required`, `errorMessage`,
|
|
564
|
+
`showValidationError`, and `inputProps`. Their `id` targets the number input in v4; React also
|
|
565
|
+
accepts `inputRef`. Web Components use `disabled`, `readonly`, `required`, `error-message`,
|
|
566
|
+
`show-validation-error="false"`, and `input-id`, with native input attributes on the host.
|
|
567
|
+
Both controls lock together and validation remains associated with the input.
|
|
568
|
+
|
|
569
|
+
The `phone-input`, `phone-country`, and `country-flag` styling parts plus `--ui-phone-height`,
|
|
570
|
+
`--ui-phone-padding`, and `--ui-phone-country-gap` replace consumer CSS overlays.
|
|
571
|
+
|
|
572
|
+
Astro and React also export `CountryFlag` (`regionCode`, optional `decorative`) and `PhoneNumber`
|
|
573
|
+
(`value: LumenPhoneNumber`, optional `link`). A telephone link is rendered only for a complete
|
|
574
|
+
E.164 value. Use the model returned by the phone normalizer; keep domain persistence in your app.
|
|
575
|
+
Artwork attribution is shipped with the core package in `PHONE_FLAG_LICENSE.txt`.
|
|
576
|
+
|
|
577
|
+
When `PhoneInput` is registered, `defineLumenElements` also registers `lumen-country-flag`
|
|
578
|
+
(`country`, `decorative`, optional `label`) and `lumen-phone-number` (`country`, `value`, `link`,
|
|
579
|
+
optional `locale`). The read-only element normalizes its string value before creating a tel link.
|
|
580
|
+
|
|
581
|
+
## Combobox keyboard behavior
|
|
582
|
+
|
|
583
|
+
In v4, Combobox retains input focus and exposes its active option through `aria-activedescendant`.
|
|
584
|
+
Enter commits an active option; text editing and composition remain native. Escape dismisses one
|
|
585
|
+
nested control at a time. See the [shared keyboard contract](https://github.com/santi020k/lumen/blob/main/docs/ai-usage.md#combobox-keyboard-behavior-in-v4)
|
|
586
|
+
for dynamic options, controlled inputs and migration guidance.
|
|
587
|
+
|
|
588
|
+
## Content flow
|
|
589
|
+
|
|
590
|
+
Stack and Grid own sibling spacing. Their gap accepts `related`, `group` (default) and `section`,
|
|
591
|
+
or canonical `none`, `xs`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl` sizes. Card owns the inset and gap
|
|
592
|
+
between its visible parts; use `density="compact"`, `"comfortable"` (default) or `"spacious"`.
|
|
593
|
+
Elements uses the same names as attributes. Use a nested Stack for CardContent groups and Field
|
|
594
|
+
for label/control/feedback. See [content flow](https://github.com/santi020k/lumen/blob/main/docs/content-flow.md) and the
|
|
595
|
+
[v4 migration guide](https://github.com/santi020k/lumen/blob/main/docs/migrating-to-lumen.md) for ownership and changed explicit gaps.
|
|
596
|
+
|
|
597
|
+
### Reading and complete compositions
|
|
598
|
+
|
|
599
|
+
Prose and Typography trim outer child margins and separate headings from preceding text.
|
|
600
|
+
Container gutters grow from 16px to 32px with viewport width; override `--ui-container-gutter`
|
|
601
|
+
when a product needs fixed gutters. Card allows interactive overflow; use AspectRatio to clip media.
|
|
602
|
+
Install `content-flow-header`, `content-flow-settings`, `content-flow-list` or `content-flow-actions`
|
|
603
|
+
with `lumen add <recipe> --target astro|react|elements`. MCP returns the same complete examples.
|
|
604
|
+
Connect application actions and replace sample IDs before reuse. See
|
|
605
|
+
[content flow](https://github.com/santi020k/lumen/blob/main/docs/content-flow.md) for composition and migration guidance.
|
|
606
|
+
|
|
607
|
+
### Large fixed-height collections
|
|
608
|
+
|
|
609
|
+
`VirtualList` supports an empty `mode="data"` root with the public Core data controller.
|
|
610
|
+
Only the visible window, overscan and focused neighbors mount. Stable keys retain row identity;
|
|
611
|
+
applications own offscreen editing state. See [data rendering](https://github.com/santi020k/lumen/blob/main/docs/virtual-list-data.md) for
|
|
612
|
+
setup, lifecycle, accessibility and the mounted-mode tradeoff.
|
|
613
|
+
|
|
614
|
+
Forms validate native controls associated through the `form` attribute even outside the form tree.
|
|
615
|
+
Timed toasts retain their remaining duration until both pointer hover and keyboard focus leave.
|
|
616
|
+
|
|
617
|
+
### Compound dialog tasks
|
|
618
|
+
|
|
619
|
+
Register `DialogHeader`, `DialogTitle`, `DialogBody`, `DialogFooter`, and
|
|
620
|
+
`DialogClose` with `Dialog`. Keep the body directly inside the dialog host for
|
|
621
|
+
independent scrolling. Use a native heading inside the title host and a native
|
|
622
|
+
button inside the close host; their semantics and disabled behavior remain native.
|
|
623
|
+
Associate the heading id with the dialog's `aria-labelledby`.
|
|
624
|
+
|
|
625
|
+
```html
|
|
626
|
+
<lumen-dialog aria-labelledby="record-title">
|
|
627
|
+
<lumen-dialog-header>
|
|
628
|
+
<lumen-dialog-title><h2 id="record-title">Edit record</h2></lumen-dialog-title>
|
|
629
|
+
</lumen-dialog-header>
|
|
630
|
+
<lumen-dialog-body><form id="record-form"><label>Name <input name="name"></label></form></lumen-dialog-body>
|
|
631
|
+
<lumen-dialog-footer>
|
|
632
|
+
<lumen-dialog-close><button type="button">Cancel</button></lumen-dialog-close>
|
|
633
|
+
<button type="submit" form="record-form">Save</button>
|
|
634
|
+
</lumen-dialog-footer>
|
|
635
|
+
</lumen-dialog>
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
A cancelled click or disabled native button does not dismiss the dialog. Nested
|
|
639
|
+
close actions only dismiss their own dialog. `lumen-file-upload` accepts
|
|
640
|
+
`selected-files-label` containing `{count}` for localized multiple-file feedback.
|
|
641
|
+
|
|
642
|
+
### Rich description rows
|
|
643
|
+
|
|
644
|
+
Register `Descriptions`, `DescriptionItem`, `DescriptionTerm`, and `DescriptionDetail`
|
|
645
|
+
to compose rich values. The item, term, and detail hosts expose `group`, `term`, and
|
|
646
|
+
`definition` roles respectively. Use a labeled group for the overall collection and
|
|
647
|
+
associate the detail with its term when the relationship needs an explicit label.
|
|
648
|
+
These custom hosts provide ARIA semantics; they are not native `dl`, `dt`, or `dd` tags.
|
|
649
|
+
|
|
650
|
+
```html
|
|
651
|
+
<lumen-descriptions role="group" aria-label="Record details">
|
|
652
|
+
<lumen-description-item>
|
|
653
|
+
<lumen-description-term id="status-label">Status</lumen-description-term>
|
|
654
|
+
<lumen-description-detail aria-labelledby="status-label"><strong>Active</strong></lumen-description-detail>
|
|
655
|
+
</lumen-description-item>
|
|
656
|
+
</lumen-descriptions>
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
For native definition-list markup, place a complete native `dl` with `div`, `dt`,
|
|
660
|
+
and `dd` children inside `lumen-descriptions` instead of nesting custom hosts inside the `dl`.
|
|
661
|
+
|
|
662
|
+
### Stepper progress
|
|
663
|
+
|
|
664
|
+
`lumen-stepper` is a generic element: it applies the shared `ui-stepper` presentation class and
|
|
665
|
+
`ui-stepper--vertical` for `orientation="vertical"`, but owns no step generation or current-step
|
|
666
|
+
behavior. Provide the complete step markup as light-DOM children:
|
|
667
|
+
|
|
668
|
+
```html
|
|
669
|
+
<lumen-stepper role="list" aria-label="Setup steps">
|
|
670
|
+
<div class="ui-stepper__step" data-state="complete" role="listitem">
|
|
671
|
+
<span class="ui-stepper__marker">1</span>
|
|
672
|
+
<span class="ui-stepper__content"><span class="ui-stepper__title">Account</span></span>
|
|
673
|
+
</div>
|
|
674
|
+
<div aria-current="step" class="ui-stepper__step" data-state="current" role="listitem">
|
|
675
|
+
<span class="ui-stepper__marker">2</span>
|
|
676
|
+
<span class="ui-stepper__content"><span class="ui-stepper__title">Workspace</span></span>
|
|
677
|
+
</div>
|
|
678
|
+
</lumen-stepper>
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
Each step is a native `div` with `role="listitem"`, `class="ui-stepper__step"`, and
|
|
682
|
+
`data-state="complete" | "current" | "upcoming"`. Give the current step `aria-current="step"`. Each
|
|
683
|
+
step holds a `ui-stepper__marker` span and a `ui-stepper__content` span containing a
|
|
684
|
+
`ui-stepper__title` span and an optional `ui-stepper__description` span.
|
|
685
|
+
|
|
686
|
+
## Attachment composition
|
|
687
|
+
|
|
688
|
+
Use `AttachmentList` to group native `li` children and `AttachmentPreview` for browser-owned images
|
|
689
|
+
with localized loading, error, and unsupported-file states. Compose independent actions rather
|
|
690
|
+
than nesting controls inside a linked Attachment. The application retains file validation,
|
|
691
|
+
authorization, persistence, and object URL cleanup. See the
|
|
692
|
+
[attachment composition recipe](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#attachment-previews-and-file-lists)
|
|
693
|
+
for adapter props, slots, child contracts, retry identity, and safe state events.
|
|
694
|
+
|
|
695
|
+
### Chart datum actions
|
|
696
|
+
|
|
697
|
+
All seven data charts accept the opt-in `drilldown` attribute. Each plotted observation gains a
|
|
698
|
+
pointer target and a matching native button under an accessible disclosure, even with
|
|
699
|
+
`show-table="false"`. Listen for the bubbling, composed `ui:chart-datum-activate` event on the chart
|
|
700
|
+
host; its validated `LumenChartDatumActivationDetail` contains original data identities and values.
|
|
701
|
+
Missing measurements and invalid ranges have no actions; pie charts expose only positive slices.
|
|
702
|
+
|
|
703
|
+
```html
|
|
704
|
+
<lumen-bar-chart drilldown explore-data-label="Explore chart data"
|
|
705
|
+
datum-action-prefix="Open details: " show-table="false"></lumen-bar-chart>
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
Use the `explore-data-label` and `datum-action-prefix` attributes for localized text, or assign a
|
|
709
|
+
`datumActionFormatter(context)` property for a contextual label formatter. The exported
|
|
710
|
+
`LumenChartDatumActionsElement` type describes this host property contract. Updating series, data,
|
|
711
|
+
or labels preserves the open disclosure and restores focus to the same available action. Chart
|
|
712
|
+
hosts own their controller lifecycle across disconnect/reconnect; mixed Astro pages leave these
|
|
713
|
+
hosts to Elements. Application code owns navigation, requests, and filtering.
|
|
714
|
+
|
|
715
|
+
## Dashboard composition
|
|
716
|
+
|
|
717
|
+
`FilterBar` groups host-owned filtering controls, active criteria, reset actions, and a polite
|
|
718
|
+
result announcement. `ChangeSummary` presents explicit before/after values and application-owned
|
|
719
|
+
changed state. Neither component owns requests, persistence, parsing, or financial policy.
|
|
720
|
+
|
|
721
|
+
ScatterChart supports independent X/Y formatting, explicit domains, logarithmic positive X values,
|
|
722
|
+
and labeled reference lines/regions. See [consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md)
|
|
723
|
+
for dashboard tables, freshness, import review, activity inbox, and persistent Kanban patterns.
|
|
724
|
+
|
|
725
|
+
### Actual-versus-target charts
|
|
726
|
+
|
|
727
|
+
`lumen-bullet-chart` compares a nullable actual `value` with a finite `target` and optional
|
|
728
|
+
labeled `ranges`. A strong actual bar, target marker, readable value labels, and expandable exact
|
|
729
|
+
data work together. Domains include zero and all measurements; invalid inputs fail closed.
|
|
730
|
+
Null values stay distinct from zero. See the [chart guide](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md#actual-values-and-targets)
|
|
731
|
+
for the input, localization, and domain contracts.
|
|
732
|
+
|
|
733
|
+
### Rankings and paired comparisons
|
|
734
|
+
|
|
735
|
+
Use `LollipopChart` for zero-based rankings and `DumbbellChart` for paired measurements (native
|
|
736
|
+
`LumenLollipopChart` and `LumenDumbbellChart`). Supply ordered comparison data with `id`, `label`,
|
|
737
|
+
nullable `value`, optional nullable `reference`, and optional `tone`. Both charts preserve missing
|
|
738
|
+
values and expose exact data. Set `value-label` to name the current measurement and `reference-label`
|
|
739
|
+
to name the paired measurement; `summary` supplies an escaped accessible interpretation that remains
|
|
740
|
+
available when `show-table="false"`. See the [shared visualization contract](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md#rankings-and-paired-comparisons).
|
|
741
|
+
|
|
742
|
+
### Calendar activity, ordered stages and distributions
|
|
743
|
+
|
|
744
|
+
`CalendarHeatmap`, `FunnelChart`, and `BoxPlot` share validated geometry with every Lumen adapter.
|
|
745
|
+
CalendarHeatmap takes date-only UTC `startDate`/`endDate`, nullable `{ date, value }` data and optional
|
|
746
|
+
`weekStartsOn` (0 for Sunday or 1 for Monday). It fills omitted dates as missing, keeps zero distinct,
|
|
747
|
+
and limits the inclusive range to 3,660 days. `weekdayLabels` always indexes Sunday through Saturday;
|
|
748
|
+
`dateFormatter` customizes readable dates without changing their identity.
|
|
749
|
+
|
|
750
|
+
FunnelChart takes ordered `{ id, label, value, tone? }` stages with nonnegative nullable values.
|
|
751
|
+
Stages retain the supplied order, including increasing values; the chart derives no conversion rates.
|
|
752
|
+
BoxPlot takes precomputed `{ id, label, min, q1, median, q3, max, outliers?, tone? }` statistics.
|
|
753
|
+
Statistics must be ordered and finite, or all five must be null for a missing row. Explicit domains
|
|
754
|
+
must contain all observations and outliers. `statisticLabels` localizes the six statistic names.
|
|
755
|
+
|
|
756
|
+
All three accept `valueFormatter`, chart `labels`, `summary`, and `show-table` (default true).
|
|
757
|
+
Exact data and missing measurements remain readable; invalid input fails closed instead of dropping
|
|
758
|
+
observations or clipping the domain. Prefer retaining the data table for complete visual inspection.
|
|
759
|
+
|
|
760
|
+
Register `CalendarHeatmap`, `FunnelChart`, and `BoxPlot` through `defineLumenElements`.
|
|
761
|
+
Use `<lumen-calendar-heatmap start-date="2026-01-01" end-date="2026-01-31">`,
|
|
762
|
+
`<lumen-funnel-chart>`, and `<lumen-box-plot>`. Assign typed `data`, `labels`, `valueFormatter`,
|
|
763
|
+
`dateFormatter`/`weekdayLabels` (calendar), and `statisticLabels` (box) properties, or supply JSON
|
|
764
|
+
`data` attributes. Calendar supports `week-starts-on` and JSON `weekday-labels`; domains use
|
|
765
|
+
`domain-min`/`domain-max`. Box statistic attributes are `min-label`, `q1-label`, `median-label`,
|
|
766
|
+
`q3-label`, `max-label`, and `outliers-label`. `show-table="false"` hides exact-data disclosure;
|
|
767
|
+
provide an application-owned summary when additional interpretation is useful.
|
|
768
|
+
|
|
769
|
+
## Combobox lifecycle
|
|
770
|
+
|
|
771
|
+
`lumen-combobox` enhances its input and listbox when both are available. Children can arrive after
|
|
772
|
+
connection or be replaced by an application renderer. Disconnecting the host releases listeners
|
|
773
|
+
and observers; reconnecting binds the current children.
|
|
774
|
+
|
|
775
|
+
Decoded chart annotations ignore invalid supplied axes; omitted axes use the shared `y` default.
|
|
776
|
+
|
|
777
|
+
React hosts can assign `type` on scalar input elements before or after connection. The input
|
|
778
|
+
reflects that property to its native control without clearing the value. Register elements once
|
|
779
|
+
at the app boundary and handle their bubbling native input events.
|
|
780
|
+
|
|
781
|
+
`lumen-button` activates with Enter or a Space release. Canceled Enter events, canceled Space
|
|
782
|
+
keydown events before the host handles them, and canceled Space releases suppress activation. The
|
|
783
|
+
host consumes Space keydown to prevent scrolling; cancel its key release or click to suppress activation
|
|
784
|
+
from an ancestor after that keydown has been consumed. It blocks
|
|
785
|
+
disabled/loading activation, including direct clicks. Blocking flags expose `aria-disabled`;
|
|
786
|
+
removing them restores any prior application-supplied ARIA value. Native nested controls retain
|
|
787
|
+
their own keyboard path.
|
|
788
|
+
|
|
789
|
+
## Device demonstrations
|
|
790
|
+
|
|
791
|
+
`DeviceFrame` presents slotted HTML, an image, or a titled iframe inside `macbook-pro`, `macbook-air`, `imac`,
|
|
792
|
+
`iphone`, `pixel`, and `ipad-pro` frames, with generic `laptop`, `desktop`, `android`, and `tablet` options.
|
|
793
|
+
Device-specific enclosures include a MacBook Pro display notch and full front edge, a tapered MacBook Air deck, curved iMac
|
|
794
|
+
stand and chin, rounded phone glass with separate rails and buttons, and an iPad Pro with even
|
|
795
|
+
bezels and no Home button. The generic `tablet` retains its classic enclosure.
|
|
796
|
+
Shells are decorative and do not emulate device hardware. `orientation` selects portrait or landscape; `tone` selects
|
|
797
|
+
light or dark chrome independently of the screen content. Use `color="white"`,
|
|
798
|
+
`color="black"`, or a 3-, 4-, 6-, or 8-digit hex color such as `color="#a9b8ac"` for
|
|
799
|
+
a custom hardware finish. `color` takes precedence over `tone`; invalid values fall back to the
|
|
800
|
+
tone. The finish stays independent of the content theme. For CSS-driven updates, set
|
|
801
|
+
`--ui-device-color` to a CSS color on the frame.
|
|
802
|
+
|
|
803
|
+
Iframe layouts use the preset screen viewport and scale to the available width. Override
|
|
804
|
+
`screenWidth` and `screenHeight` for a custom viewport (1–16384 CSS pixels). Slotted HTML shares
|
|
805
|
+
the host viewport; container queries can adapt it to the screen. Images preserve their proportions.
|
|
806
|
+
`scroll={false}` clips HTML overflow; an iframe manages its own scrolling.
|
|
807
|
+
|
|
808
|
+
Supply image alternative text and iframe titles. Consumers own iframe `sandbox`, `allow`,
|
|
809
|
+
loading, and referrer policies. Remote sites can refuse embedding through their response headers.
|
|
810
|
+
The decorative shell does not alter focus or intercept interactions. Camera and home-indicator
|
|
811
|
+
details occupy separate chrome outside live HTML and iframe viewports. Direct phone images extend
|
|
812
|
+
under the decorative camera and home indicator for a full-bleed presentation.
|
|
813
|
+
Screen presets are 1280 × 800 for MacBook Pro and MacBook Air, 1440 × 810 for iMac, 390 × 844 for iPhone,
|
|
814
|
+
412 × 915 for Pixel, and 834 × 1194 for iPad Pro (1194 × 834 in landscape). These are demonstration
|
|
815
|
+
viewports, not physical display specifications.
|
|
816
|
+
|
|
817
|
+
```html
|
|
818
|
+
<lumen-device-frame device="android" tone="light">
|
|
819
|
+
<iframe src="/demo" title="Mobile application demo" loading="lazy"></iframe>
|
|
820
|
+
</lumen-device-frame>
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
Register with `defineLumenElements(['DeviceFrame'])` or `defineLumenDeviceFrame()` from
|
|
824
|
+
`@santi020k/lumen-elements/components/device-frame`. Use `screen-width`, `screen-height`, and
|
|
825
|
+
`scroll="false"` attributes. The element preserves the initial child nodes inside its screen;
|
|
826
|
+
append later content to `.ui-device-frame__screen`.
|
|
827
|
+
## Visual interactions and product blocks
|
|
828
|
+
|
|
829
|
+
See [visual interactions](https://github.com/santi020k/lumen/blob/main/docs/visual-interactions.md) for keyed motion, semantic effects,
|
|
830
|
+
chart continuity, AI surfaces, optional SDK integrations, and the four installable product recipes.
|
|
831
|
+
|
|
832
|
+
## World map
|
|
833
|
+
|
|
834
|
+
WorldMap supports highlighted countries, location markers, dotted or solid styles, country selection,
|
|
835
|
+
and theme customization. Use `initialView="highlighted"` (Elements: `initial-view`) to start with a
|
|
836
|
+
regional view. Zoom toward the cursor with Ctrl/Cmd-scroll, or fit highlighted countries using the
|
|
837
|
+
map controls. Import geography explicitly from
|
|
838
|
+
`@santi020k/lumen-core/world-map-data`; it is excluded from root exports. See the
|
|
839
|
+
[WorldMap usage guide](https://github.com/santi020k/lumen/blob/main/docs/world-map.md) for adapter examples, events, localization,
|
|
840
|
+
accessibility, and customization.
|
|
841
|
+
|
|
842
|
+
## Studio media workspace
|
|
843
|
+
|
|
844
|
+
Compose MediaViewport, MediaThumbnail, MediaFilmstrip and ImageComparison modes with the
|
|
845
|
+
[Studio media workspace recipes](https://github.com/santi020k/lumen/blob/main/docs/studio-media-workspaces.md). Applications retain
|
|
846
|
+
media loading, selection, adjustment algorithms, processing, export and persistence.
|
|
847
|
+
|
|
848
|
+
## Resources
|
|
849
|
+
|
|
850
|
+
| Guide | What you will find |
|
|
851
|
+
| --- | --- |
|
|
852
|
+
| [Styling contract](https://github.com/santi020k/lumen/blob/main/docs/styling-contract.md) | Stable styling parts, component variables, and overrides. |
|
|
853
|
+
| [Error handling](https://github.com/santi020k/lumen/blob/main/docs/error-handling.md) | Field feedback, summaries, alerts, and recovery surfaces. |
|
|
854
|
+
| [Contributing](https://github.com/santi020k/lumen/blob/main/CONTRIBUTING.md) | Setup, checks, and contribution workflow. |
|
|
855
|
+
| [Release history](https://github.com/santi020k/lumen/releases) | Published releases and version notes. |
|
|
856
|
+
|
|
857
|
+
Part of [Lumen UI](https://lumen.santi020k.com), created by [Santiago Molina](https://santi020k.com).
|
|
858
|
+
Licensed under [MIT](https://github.com/santi020k/lumen/blob/main/LICENSE); third-party artwork retains its own notices.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type LumenBulletRange, type LumenChartLabels } from '@santi020k/lumen-core';
|
|
2
|
+
export declare const bulletNumberAttribute: (element: HTMLElement, name: string) => number;
|
|
3
|
+
export declare const parseBulletRanges: (source: string | null) => readonly LumenBulletRange[];
|
|
4
|
+
export declare const bulletChartHtml: (element: HTMLElement, ranges: readonly LumenBulletRange[], text: Readonly<LumenChartLabels>, format: (value: number) => string) => string;
|
|
5
|
+
//# sourceMappingURL=bullet-chart-html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bullet-chart-html.d.ts","sourceRoot":"","sources":["../src/bullet-chart-html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyD,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAmB,MAAM,uBAAuB,CAAA;AAI5J,eAAO,MAAM,qBAAqB,GAAI,SAAS,WAAW,EAAE,MAAM,MAAM,KAAG,MAI1E,CAAA;AAgBD,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,GAAG,IAAI,KAAG,SAAS,gBAAgB,EAelF,CAAA;AAED,eAAO,MAAM,eAAe,GAC1B,SAAS,WAAW,EAAE,QAAQ,SAAS,gBAAgB,EAAE,EAAE,MAAM,QAAQ,CAAC,gBAAgB,CAAC,EAC3F,QAAQ,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,KAChC,MA0BF,CAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createLumenBulletGeometry, getLumenChartToneClassName, lumenChartTones } from '@santi020k/lumen-core';
|
|
2
|
+
import { escapeChartHtml } from './chart-html.js';
|
|
3
|
+
export const bulletNumberAttribute = (element, name) => {
|
|
4
|
+
const value = element.getAttribute(name);
|
|
5
|
+
return value === null || value.trim() === '' ? NaN : Number(value);
|
|
6
|
+
};
|
|
7
|
+
const bulletRangeWithTone = (end, label, inputTone) => {
|
|
8
|
+
const tone = lumenChartTones.find(candidate => candidate === inputTone);
|
|
9
|
+
if (inputTone !== undefined && !tone)
|
|
10
|
+
return { end: NaN, label: '' };
|
|
11
|
+
return { end, label, ...(tone ? { tone } : {}) };
|
|
12
|
+
};
|
|
13
|
+
const bulletDomain = (element) => {
|
|
14
|
+
if (!element.hasAttribute('domain-min') && !element.hasAttribute('domain-max'))
|
|
15
|
+
return {};
|
|
16
|
+
return { domain: { min: bulletNumberAttribute(element, 'domain-min'), max: bulletNumberAttribute(element, 'domain-max') } };
|
|
17
|
+
};
|
|
18
|
+
export const parseBulletRanges = (source) => {
|
|
19
|
+
try {
|
|
20
|
+
const entries = JSON.parse(source ?? '[]');
|
|
21
|
+
if (!Array.isArray(entries))
|
|
22
|
+
return [{ end: NaN, label: '' }];
|
|
23
|
+
return entries.map((entry) => {
|
|
24
|
+
if (typeof entry !== 'object' || entry === null || !('end' in entry) || !('label' in entry) ||
|
|
25
|
+
typeof entry.end !== 'number' || typeof entry.label !== 'string')
|
|
26
|
+
return { end: NaN, label: '' };
|
|
27
|
+
return bulletRangeWithTone(entry.end, entry.label, 'tone' in entry ? entry.tone : undefined);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return [{ end: NaN, label: '' }];
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
export const bulletChartHtml = (element, ranges, text, format) => {
|
|
35
|
+
const value = element.hasAttribute('value') ? bulletNumberAttribute(element, 'value') : null;
|
|
36
|
+
const target = bulletNumberAttribute(element, 'target');
|
|
37
|
+
const model = createLumenBulletGeometry(value, target, { ranges, ...bulletDomain(element) });
|
|
38
|
+
if (!model.valid)
|
|
39
|
+
return `<p class="ui-chart__empty" role="status">${escapeChartHtml(text.invalidData)}</p>`;
|
|
40
|
+
const actual = value === null ? text.notAvailable : format(value);
|
|
41
|
+
const targetLabel = element.getAttribute('target-label') ?? 'Target';
|
|
42
|
+
const tone = lumenChartTones.find(candidate => candidate === element.getAttribute('tone')) ?? 'series-1';
|
|
43
|
+
const summary = element.getAttribute('summary') ?? `${text.value}: ${actual}. ${targetLabel}: ${format(target)}.`;
|
|
44
|
+
const bands = model.ranges.map((range, index) => `<span class="ui-bullet-chart__range ${getLumenChartToneClassName(range.tone ?? 'neutral')}" style="left:${range.startRatio * 100}%;width:${(range.endRatio - range.startRatio) * 100}%;opacity:${0.08 + index / Math.max(1, model.ranges.length - 1) * 0.14}"></span>`).join('');
|
|
45
|
+
const bar = value === null ? '' : `<span class="ui-bullet-chart__bar ${getLumenChartToneClassName(tone)}" style="left:${model.valueStartRatio * 100}%;width:${model.valueWidthRatio * 100}%"></span>`;
|
|
46
|
+
const ticks = model.ticks.map(tick => `<span>${escapeChartHtml(format(tick.value))}</span>`).join('');
|
|
47
|
+
const rangeValue = (range) => `${format(range.start)}–${format(range.end)}`;
|
|
48
|
+
const legend = model.ranges.length ? `<ul class="ui-bullet-chart__ranges">${model.ranges.map(range => `<li class="${getLumenChartToneClassName(range.tone ?? 'neutral')}"><span>${escapeChartHtml(range.label)}</span><span>${escapeChartHtml(rangeValue(range))}</span></li>`).join('')}</ul>` : '';
|
|
49
|
+
const rows = [
|
|
50
|
+
[text.value, actual],
|
|
51
|
+
[targetLabel, format(target)],
|
|
52
|
+
...model.ranges.map(range => [range.label, rangeValue(range)])
|
|
53
|
+
];
|
|
54
|
+
const table = element.getAttribute('show-table') === 'false' ? '' : `<details class="ui-chart__data"><summary>${escapeChartHtml(text.viewData)}</summary><div role="group" tabindex="0" aria-label="${escapeChartHtml(text.chartData)}"><table><thead><tr><th scope="col">${escapeChartHtml(text.category)}</th><th scope="col">${escapeChartHtml(text.value)}</th></tr></thead><tbody>${rows.map(([label, number]) => `<tr><th scope="row">${escapeChartHtml(label ?? '')}</th><td>${escapeChartHtml(number ?? '')}</td></tr>`).join('')}</tbody></table></div></details>`;
|
|
55
|
+
return `<p class="ui-sr-only" data-ui-chart-summary>${escapeChartHtml(summary)}</p><div class="ui-bullet-chart__values"><div><span>${escapeChartHtml(text.value)}</span><strong>${escapeChartHtml(actual)}</strong></div><p><span class="ui-bullet-chart__target-key" aria-hidden="true"></span>${escapeChartHtml(targetLabel)}<strong>${escapeChartHtml(format(target))}</strong></p></div><div class="ui-bullet-chart__plot" aria-hidden="true"><div class="ui-bullet-chart__track">${bands}${bar}<span class="ui-bullet-chart__target" style="left:${model.targetRatio * 100}%"></span></div><div class="ui-bullet-chart__ticks">${ticks}</div></div>${legend}${table}`;
|
|
56
|
+
};
|