@santi020k/lumen-react 3.0.1 → 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.
Files changed (145) hide show
  1. package/README.md +666 -13
  2. package/dist/ai-surfaces.d.ts +49 -0
  3. package/dist/ai-surfaces.d.ts.map +1 -0
  4. package/dist/ai-surfaces.js +88 -0
  5. package/dist/amount-field.d.ts +11 -0
  6. package/dist/amount-field.d.ts.map +1 -0
  7. package/dist/amount-field.js +37 -0
  8. package/dist/attachments.d.ts +17 -0
  9. package/dist/attachments.d.ts.map +1 -0
  10. package/dist/attachments.js +23 -0
  11. package/dist/bullet-chart.d.ts +14 -0
  12. package/dist/bullet-chart.d.ts.map +1 -0
  13. package/dist/bullet-chart.js +22 -0
  14. package/dist/change-summary.d.ts +14 -0
  15. package/dist/change-summary.d.ts.map +1 -0
  16. package/dist/change-summary.js +4 -0
  17. package/dist/chart-inspection.d.ts +10 -0
  18. package/dist/chart-inspection.d.ts.map +1 -0
  19. package/dist/chart-inspection.js +11 -0
  20. package/dist/chart-interaction.d.ts +17 -0
  21. package/dist/chart-interaction.d.ts.map +1 -0
  22. package/dist/chart-interaction.js +50 -0
  23. package/dist/chart-label.d.ts +3 -0
  24. package/dist/chart-label.d.ts.map +1 -0
  25. package/dist/chart-label.js +1 -0
  26. package/dist/chart-recipes.d.ts +8 -0
  27. package/dist/chart-recipes.d.ts.map +1 -1
  28. package/dist/chart-recipes.js +35 -0
  29. package/dist/combobox.d.ts +9 -0
  30. package/dist/combobox.d.ts.map +1 -0
  31. package/dist/combobox.js +137 -0
  32. package/dist/comparison-chart.d.ts +13 -0
  33. package/dist/comparison-chart.d.ts.map +1 -0
  34. package/dist/comparison-chart.js +18 -0
  35. package/dist/components.d.ts +106 -70
  36. package/dist/components.d.ts.map +1 -1
  37. package/dist/components.js +4 -1949
  38. package/dist/dashboard.d.ts +20 -0
  39. package/dist/dashboard.d.ts.map +1 -0
  40. package/dist/dashboard.js +19 -0
  41. package/dist/data-table-range-model.d.ts +8 -0
  42. package/dist/data-table-range-model.d.ts.map +1 -0
  43. package/dist/data-table-range-model.js +31 -0
  44. package/dist/data-table-range.d.ts +17 -0
  45. package/dist/data-table-range.d.ts.map +1 -0
  46. package/dist/data-table-range.js +8 -0
  47. package/dist/data-table-saved-views.d.ts +30 -0
  48. package/dist/data-table-saved-views.d.ts.map +1 -0
  49. package/dist/data-table-saved-views.js +49 -0
  50. package/dist/data-table-selection-model.d.ts +23 -0
  51. package/dist/data-table-selection-model.d.ts.map +1 -0
  52. package/dist/data-table-selection-model.js +36 -0
  53. package/dist/data-table-selection.d.ts +11 -0
  54. package/dist/data-table-selection.d.ts.map +1 -0
  55. package/dist/data-table-selection.js +10 -0
  56. package/dist/data-table-state.d.ts +6 -0
  57. package/dist/data-table-state.d.ts.map +1 -0
  58. package/dist/data-table-state.js +38 -0
  59. package/dist/data-table-view.d.ts +88 -0
  60. package/dist/data-table-view.d.ts.map +1 -0
  61. package/dist/data-table-view.js +1 -0
  62. package/dist/data-table.d.ts +60 -0
  63. package/dist/data-table.d.ts.map +1 -0
  64. package/dist/data-table.js +1 -0
  65. package/dist/date-range-calendar.d.ts +29 -0
  66. package/dist/date-range-calendar.d.ts.map +1 -0
  67. package/dist/date-range-calendar.js +69 -0
  68. package/dist/date-range-input.d.ts +25 -0
  69. package/dist/date-range-input.d.ts.map +1 -0
  70. package/dist/date-range-input.js +195 -0
  71. package/dist/device-frame.d.ts +13 -0
  72. package/dist/device-frame.d.ts.map +1 -0
  73. package/dist/device-frame.js +23 -0
  74. package/dist/dialog-lifecycle.d.ts +3 -0
  75. package/dist/dialog-lifecycle.d.ts.map +1 -0
  76. package/dist/dialog-lifecycle.js +70 -0
  77. package/dist/expanded-charts.d.ts +26 -0
  78. package/dist/expanded-charts.d.ts.map +1 -0
  79. package/dist/expanded-charts.js +46 -0
  80. package/dist/floating-panel.d.ts +11 -0
  81. package/dist/floating-panel.d.ts.map +1 -0
  82. package/dist/floating-panel.js +1 -0
  83. package/dist/form-workflow.d.ts +77 -0
  84. package/dist/form-workflow.d.ts.map +1 -0
  85. package/dist/form-workflow.js +337 -0
  86. package/dist/hooks.d.ts +24 -21
  87. package/dist/hooks.d.ts.map +1 -1
  88. package/dist/hooks.js +1 -2468
  89. package/dist/image-comparison.d.ts +20 -0
  90. package/dist/image-comparison.d.ts.map +1 -0
  91. package/dist/image-comparison.js +55 -0
  92. package/dist/index.d.ts +25 -1
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +24 -0
  95. package/dist/interval-charts.d.ts +21 -0
  96. package/dist/interval-charts.d.ts.map +1 -0
  97. package/dist/interval-charts.js +23 -0
  98. package/dist/media-selection.d.ts +18 -0
  99. package/dist/media-selection.d.ts.map +1 -0
  100. package/dist/media-selection.js +12 -0
  101. package/dist/media-viewport.d.ts +16 -0
  102. package/dist/media-viewport.d.ts.map +1 -0
  103. package/dist/media-viewport.js +37 -0
  104. package/dist/motion.d.ts +597 -0
  105. package/dist/motion.d.ts.map +1 -0
  106. package/dist/motion.js +22 -0
  107. package/dist/review-workflow.d.ts +32 -0
  108. package/dist/review-workflow.d.ts.map +1 -0
  109. package/dist/review-workflow.js +76 -0
  110. package/dist/rich-text-editor.d.ts +21 -0
  111. package/dist/rich-text-editor.d.ts.map +1 -0
  112. package/dist/rich-text-editor.js +143 -0
  113. package/dist/select-form.d.ts +3 -0
  114. package/dist/select-form.d.ts.map +1 -0
  115. package/dist/select-form.js +1 -0
  116. package/dist/server-components.d.ts +24 -4
  117. package/dist/server-components.d.ts.map +1 -1
  118. package/dist/server-components.js +11 -3
  119. package/dist/server.d.ts +1 -0
  120. package/dist/server.d.ts.map +1 -1
  121. package/dist/server.js +1 -0
  122. package/dist/toast-context.d.ts +4 -0
  123. package/dist/toast-context.d.ts.map +1 -0
  124. package/dist/toast-context.js +10 -0
  125. package/dist/toast-provider.d.ts +3 -0
  126. package/dist/toast-provider.d.ts.map +1 -0
  127. package/dist/toast-provider.js +3 -0
  128. package/dist/use-copy-feedback.d.ts +17 -0
  129. package/dist/use-copy-feedback.d.ts.map +1 -0
  130. package/dist/use-copy-feedback.js +54 -0
  131. package/dist/virtual-list-data.d.ts +13 -0
  132. package/dist/virtual-list-data.d.ts.map +1 -0
  133. package/dist/virtual-list-data.js +87 -0
  134. package/dist/virtual-list.d.ts +14 -0
  135. package/dist/virtual-list.d.ts.map +1 -0
  136. package/dist/virtual-list.js +24 -0
  137. package/dist/visual-interactions.d.ts +16 -0
  138. package/dist/visual-interactions.d.ts.map +1 -0
  139. package/dist/visual-interactions.js +30 -0
  140. package/dist/world-map.d.ts +26 -0
  141. package/dist/world-map.d.ts.map +1 -0
  142. package/dist/world-map.js +81 -0
  143. package/package.json +99 -11
  144. package/styles/critical.css +634 -61
  145. package/styles/data-table-view.css +36 -0
package/README.md CHANGED
@@ -1,12 +1,91 @@
1
- # @santi020k/lumen-react
2
-
3
- React primitives for Lumen UI.
4
-
5
- This package provides React components for the shared Lumen primitive catalog using the standalone
6
- Lumen stylesheet.
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 · React</h1>
11
+
12
+ <p align="center">React primitives · Shared styles · Typed component APIs</p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.npmjs.com/package/@santi020k/lumen-react"><img src="https://img.shields.io/npm/v/@santi020k/lumen-react?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/react">Documentation</a>
21
+ ·
22
+ <a href="https://www.npmjs.com/package/@santi020k/lumen-react">npm</a>
23
+ ·
24
+ <a href="https://github.com/santi020k/lumen/tree/main/packages/react">Source</a>
25
+ ·
26
+ <a href="https://github.com/santi020k/lumen/issues">Issues</a>
27
+ </p>
28
+
29
+ **Package:** `@santi020k/lumen-react`
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
+ - [Selective imports](#selective-imports)
38
+ - [Appearance presets](#appearance-presets)
39
+ - [Usage](#usage)
40
+ - [Dropdown menus](#dropdown-menus)
41
+ - [Language selection](#language-selection)
42
+ - [Forms](#forms)
43
+ - [Error states](#error-states)
44
+ - [Data visualization](#data-visualization)
45
+ - [Next.js and server components](#nextjs-and-server-components)
46
+ - [Compatibility wrappers](#compatibility-wrappers)
47
+ - [Context navigation](#context-navigation)
48
+ - [Optimized images](#optimized-images)
49
+ - [Kanban boards](#kanban-boards)
50
+ - [Motion](#motion)
51
+ - [Glass surfaces](#glass-surfaces)
52
+ - [Semantic stat roots](#semantic-stat-roots)
53
+ - [Consumer composition recipes](#consumer-composition-recipes)
54
+ - [Exact amount fields](#exact-amount-fields)
55
+ - [Inline date range calendar](#inline-date-range-calendar)
56
+ - [Input-attached date range selection](#input-attached-date-range-selection)
57
+ - [Phone presentation in v4](#phone-presentation-in-v4)
58
+ - [Combobox keyboard behavior](#combobox-keyboard-behavior)
59
+ - [Content flow](#content-flow)
60
+ - [Attachment composition](#attachment-composition)
61
+ - [Chart datum actions](#chart-datum-actions)
62
+ - [Dashboard composition](#dashboard-composition)
63
+ - [Hook state updates](#hook-state-updates)
64
+ - [Advanced table views](#advanced-table-views)
65
+ - [Advanced form workflows](#advanced-form-workflows)
66
+ - [Device demonstrations](#device-demonstrations)
67
+ - [Visual interactions](#visual-interactions)
68
+ - [World map](#world-map)
69
+ - [Operational workflows](#operational-workflows)
70
+ - [Studio media workspace](#studio-media-workspace)
71
+ - [Resources](#resources)
72
+
73
+ </details>
74
+
75
+ React components and behavior hooks for Lumen UI, using the shared standalone stylesheet.
76
+ Start with one CSS import, then compose public primitives in your existing React application.
77
+
78
+ See the [shared web form contracts](https://github.com/santi020k/lumen/blob/main/docs/form-controls.md) for value ownership, reset,
79
+ submission, disabled state, and event behavior.
80
+
81
+ Form controls use `visualSize` (`visual-size` in Elements) with `default`, `sm` and `lg`.
82
+ Select, PhoneInput and Segmented follow Input and NativeSelect; numeric input/select `size` keeps
83
+ 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).
7
84
 
8
85
  ## Install
9
86
 
87
+ Requires React 19 or newer in the consuming application.
88
+
10
89
  ```bash
11
90
  pnpm add @santi020k/lumen-react
12
91
  ```
@@ -32,15 +111,41 @@ The stylesheet defaults `--ui-font` to `"Montserrat", "Avenir Next", "Segoe UI",
32
111
  It declares the family stack but does not bundle or load font files. Load Montserrat once through
33
112
  your preferred delivery path, or override `--ui-font` in application CSS.
34
113
 
114
+ ## Selective imports
115
+
116
+ Production builds compact component JavaScript without renaming identifiers or changing the
117
+ ES2022 target. Declaration files and public imports retain their existing contracts.
118
+
119
+ Root imports remain supported. Standalone component entrypoints include `/components/attachments`,
120
+ `/components/bullet-chart`, `/components/comparison-chart`, `/components/data-table`,
121
+ `/components/date-range-calendar`, `/components/date-range-input`, `/components/expanded-charts`,
122
+ `/components/image-comparison`, `/components/interval-charts`, and `/components/virtual-list`.
123
+ Import behavior hooks from `/hooks`; use `/server` for the server-safe primitive catalog.
124
+ Load the existing stylesheet once, regardless of the import path.
125
+
126
+ ```tsx
127
+ import { ImageComparison } from '@santi020k/lumen-react/components/image-comparison'
128
+ import { VirtualList } from '@santi020k/lumen-react/components/virtual-list'
129
+ ```
130
+
131
+ These entries use the same implementation and types as root imports. Interactive entries retain
132
+ `use client` for React Server Component consumers. A smaller module graph does not guarantee a
133
+ smaller final bundle: a bundler can already remove unused root exports. Run the repository's
134
+ `pnpm run measure:selective-imports` benchmark for the measured comparison.
135
+
136
+ ## Appearance presets
137
+
138
+ Use a scoped `data-lumen-preset="studio"` container, or select a preset with `useThemeBuilder`. See [appearance presets](https://github.com/santi020k/lumen/blob/main/docs/appearance-presets.md) for theme overrides and explicit glass surfaces.
139
+
35
140
  ## Usage
36
141
 
37
142
  ```tsx
38
- import { Button, Card, Input } from "@santi020k/lumen-react";
143
+ import { Button, Card, Input, Label } from "@santi020k/lumen-react";
39
144
 
40
145
  export function SubscribeForm() {
41
146
  return (
42
147
  <Card>
43
- <label htmlFor="email">Email</label>
148
+ <Label htmlFor="email">Email</Label>
44
149
  <Input id="email" type="email" placeholder="you@example.com" />
45
150
  <Button>Subscribe</Button>
46
151
  </Card>
@@ -66,6 +171,13 @@ Compose dropdown menus from the public trigger, content, item, and separator par
66
171
  menu after a successful selection; preventing the item's click event keeps it open. Disabled items
67
172
  remain unavailable, and `status` adds short trailing context to the item.
68
173
 
174
+ Disclosure keyboard navigation skips hidden or inert regions, invisible controls, and native
175
+ disabled controls, including a disabled fieldset. Available controls in a fieldset's first legend
176
+ retain their native keyboard behavior; removing `inert` makes a region available again.
177
+
178
+ Mentions keeps suggestion navigation on the textarea through `aria-activedescendant`; suggestion
179
+ buttons are excluded from the Tab sequence while Enter and pointer selection still insert a mention.
180
+
69
181
  ```tsx
70
182
  import {
71
183
  DropdownMenu,
@@ -122,6 +234,13 @@ and Spanish defaults when `locales` is omitted.
122
234
  accessibility contract without replacing form state. Native-backed controls forward refs to their
123
235
  submitted DOM controls and work directly with React Hook Form's `register()`.
124
236
 
237
+ Native form resets defer past the browser's default action and honor a cancelled `reset` event:
238
+ `DatePicker`, `DateRangeInput`, `PhoneInput`, and `Combobox` only restore uncontrolled defaults when
239
+ the reset is not prevented, and leave a controlled `value` untouched. `DatePicker` calendar
240
+ selection fires the native `onChange` exactly once, matching typed input. `DateRangeInput` attaches
241
+ its reset listener even without `name`, honoring an explicit `form` id as well as the nearest
242
+ ancestor form.
243
+
125
244
  For controlled composites, install the optional adapter:
126
245
 
127
246
  ```bash
@@ -152,7 +271,7 @@ diagnostics, or decide whether retrying is safe.
152
271
  />
153
272
  ```
154
273
 
155
- See the repository [React error-handling guide](../../docs/error-handling.md#react) for the complete
274
+ See the repository [React error-handling guide](https://github.com/santi020k/lumen/blob/main/docs/error-handling.md#react) for the complete
156
275
  example, error-boundary ownership, announcement behavior, and verification guidance.
157
276
 
158
277
  For long articles, `Anchor` accepts optional `depth`, `index`, and `description` metadata plus an
@@ -188,15 +307,29 @@ either a `value` or a `target` selector; `toast` opts into Lumen Toast feedback.
188
307
  customize its visible feedback while the localized label props remain the accessible announcement.
189
308
  Stable `data-slot` hooks expose the idle, copied, and error parts.
190
309
 
310
+ Wrap React copy actions in `ToastProvider` to display their optional toast feedback. Target selectors
311
+ resolve in the clicked button's owning document, including iframe portals. Updating a closing or moved
312
+ toast reapplies the destination viewport limit without dismissing toasts in another placement.
313
+
191
314
  ## Data visualization
192
315
 
316
+ The web visualization milestone adds `WaterfallChart` for signed changes and explicit totals, and
317
+ `Histogram` for precomputed numeric bins (`frequency="density"` for unequal widths). Line charts
318
+ support explicit continuous axes, annotations, optional keyboard/pointer/touch inspection, and
319
+ synchronized cursors. Heatmaps show labeled axes, a color legend, and explicit missing cells.
320
+ See the [visualization contracts](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md) and
321
+ [interactive web example](https://lumen.santi020k.com/docs/web/data-visualization).
322
+
323
+ Use `interactive`, `syncGroup`, and optional `cursor`/`onCursorChange` on `LineChart`. A supplied
324
+ `cursor` is controlled; the owner must accept requests before the selection changes.
325
+
193
326
  `Sparkline`, `BarChart`, `LineChart`, `PieChart`, `ScatterChart`, `Heatmap`, `RangeChart`, and
194
327
  `ComboChart` use the shared chart contracts and
195
328
  render without an external charting dependency. Data charts expose a revealable semantic table by
196
329
  default. `PieChart` accepts one series and defaults to a donut presentation.
197
330
 
198
331
  Every chart includes a factual screen-reader summary by default and accepts `summary` for more
199
- useful domain context. See [data visualization](../../docs/data-visualization.md) for selection,
332
+ useful domain context. See [data visualization](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md) for selection,
200
333
  missing-data, live-data, and accessibility guidance.
201
334
 
202
335
  ```tsx
@@ -384,16 +517,22 @@ ARIA, keyboard, Escape, dismissal, context menu, form
384
517
  validation, calendar grids, OTP segmentation, date range syncing, rich text command, schedule
385
518
  drag/drop, controlled Kanban move requests, theme export and switching, resizable pane sizing, and
386
519
  toast controller semantics for React applications.
387
- `useRichTextEditor` also provides `getEditableProps`, value-bearing commands, common formatting
520
+ Toast timeouts pause while hovered or focused and resume only after both interactions end;
521
+ moving focus between a toast's controls preserves its remaining duration. Updating a closing toast
522
+ reopens it with a fresh lifetime using its current duration.
523
+ `useRichTextEditor` honors consumer event cancellation before toolbar and shortcut commands.
524
+ It also provides `getEditableProps`, value-bearing commands, common formatting
388
525
  shortcuts, active toolbar state, and `{ html, text }` change details.
389
526
  `DataTable` can render structured `columns` and `rows`; sortable columns use native header buttons,
390
527
  update `aria-sort`, and order string or numeric values without mutating the supplied rows. The
391
528
  shared selectable/sortable data attributes remain available for app-level adapters, and
392
- `VirtualList` exposes the shared range sizing attributes.
529
+ `VirtualList` provides built-in fixed-height windowing with the shared sizing attributes.
393
530
  `useTabs` keeps the selected trigger visible when a narrow horizontal list scrolls. The package
394
531
  also exports `LumenTabsChangeDetail` and `LumenTabsChangeEvent` for integrations that consume the
395
532
  shared `ui:tabs-change` contract.
396
533
 
534
+ Keyboard navigation stays within the current tab group when tabs are nested and skips disabled triggers.
535
+
397
536
  ## Kanban boards
398
537
 
399
538
  Use `KanbanBoard`, `KanbanColumn`, and ordinary `Card` items for status-based workspaces. The
@@ -446,7 +585,7 @@ or `variant="glass"` for selective translucency.
446
585
 
447
586
  ## Consumer composition recipes
448
587
 
449
- See [consumer UI recipes](../../docs/consumer-ui-recipes.md) for static React icons, responsive
588
+ See [consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md) for static React icons, responsive
450
589
  record tables, keyboard-aware native sheets, whole-unit amount fields, adaptive editors, and
451
590
  asynchronous action states. Each recipe identifies the public primitives and the behavior that
452
591
  remains owned by the application.
@@ -462,3 +601,517 @@ import { Icon, Search } from '@santi020k/lumen-react/icons'
462
601
  The existing root `Icon name="search"` remains supported for runtime-selected names and icon packs.
463
602
  `Table layout="records"` opts into the responsive record recipe; its semantic child markup and
464
603
  shared mobile labels are documented in the consumer recipe linked above.
604
+
605
+ ## Exact amount fields
606
+
607
+ `AmountField` accepts editable ASCII decimal strings, formats a requested locale, and submits a
608
+ complete decimal string through its named hidden input. Use `defaultValue` for an uncontrolled
609
+ field or `value` with `onValueChange` for application-owned drafts. It preserves precision and
610
+ never rounds through a JavaScript number. Updating `defaultValue` changes the next reset baseline
611
+ without replacing the active draft. Currency rules and limits remain application-owned.
612
+
613
+ ```tsx
614
+ <AmountField name="amount" locale="es-CO" defaultValue="1234.50" aria-label="Amount COP" />
615
+ ```
616
+
617
+ See [consumer workflows](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#executable-consumer-workflows) for
618
+ validation, reset, operational-record actions and opt-in MessageScroller following.
619
+
620
+ ## Inline date range calendar
621
+
622
+ `DateRangeCalendar` is a controlled React range editor with two visible calendars,
623
+ inclusive range highlighting, a preset sidebar and the keyboard behavior of `useCalendar`,
624
+ including inherited RTL arrow navigation and focus movement in iframe documents.
625
+ On narrow screens the presets scroll horizontally and the calendars stack. It uses the shared Lumen stylesheet.
626
+
627
+ ```tsx
628
+ const [range, setRange] = useState({ start: '2026-09-01', end: '2026-09-30' })
629
+
630
+ <DateRangeCalendar
631
+ value={range}
632
+ onValueChange={setRange}
633
+ locale="en-US"
634
+ min="2000-01-01"
635
+ max="2100-12-31"
636
+ labels={{ start: 'From', end: 'To', presets: 'Quick range' }}
637
+ presets={[{ label: 'September', value: { start: '2026-09-01', end: '2026-09-30' } }]}
638
+ />
639
+ ```
640
+
641
+ Supply valid ISO date endpoints in ascending order. Choosing a start after the end,
642
+ or an end before the start, moves the opposite endpoint to the chosen day.
643
+ Presets outside `min`/`max` or in descending order are disabled. Only one matching
644
+ preset is highlighted, including when multiple presets resolve to the same range. `formatDate` can customize
645
+ the endpoint summaries without changing ISO values. The consumer owns draft state,
646
+ Apply/Cancel actions and domain limits such as maximum report duration. Labels are required;
647
+ `locale` controls month, weekday, navigation and day announcements. React date controls default
648
+ to English on both the server and browser; pass the application locale explicitly for localized
649
+ SSR and hydration.
650
+
651
+ ## Input-attached date range selection
652
+
653
+ `DateRangeInput` wraps `DateRangeCalendar` in an anchored, non-modal popover.
654
+ It measures and observes its owning viewport, including iframe documents. Apply and Cancel restore
655
+ trigger focus, and native reset follows the current associated form. Outside-focus dismissal
656
+ follows the owning document, including same-origin iframe and portal rendering. Use it
657
+ when the range should be edited directly from an input-like control. It keeps draft
658
+ changes internal and calls `onValueChange` only when the user chooses Apply.
659
+ The existing `DateRangePicker` and inline `DateRangeCalendar` remain available.
660
+
661
+ ```tsx
662
+ import { useState } from 'react'
663
+ import { DateRangeInput } from '@santi020k/lumen-react'
664
+ import '@santi020k/lumen-react/styles.css'
665
+
666
+ export function ReportPeriod() {
667
+ const [range, setRange] = useState({ start: '2026-09-01', end: '2026-09-30' })
668
+
669
+ return (
670
+ <DateRangeInput
671
+ value={range}
672
+ onValueChange={setRange}
673
+ label="Report period"
674
+ locale="en-US"
675
+ labels={{ start: 'From', end: 'To', presets: 'Quick ranges', apply: 'Apply', cancel: 'Cancel' }}
676
+ presets={[{ label: 'September', value: { start: '2026-09-01', end: '2026-09-30' } }]}
677
+ name={{ start: 'from', end: 'to' }}
678
+ validate={draft => draft.start < '2026-01-01' ? 'Choose dates in 2026 or later.' : undefined}
679
+ renderSummary={draft => `${draft.start} through ${draft.end}`}
680
+ />
681
+ )
682
+ }
683
+ ```
684
+
685
+ - `value` must contain real, ascending ISO dates (`YYYY-MM-DD`). `min`, `max`,
686
+ `presets`, `locale`, and `formatDate` follow the inline calendar contract.
687
+ - `label` names both the trigger and dialog. Supply localized start, end, presets,
688
+ apply, and cancel labels. For Spanish, use `Desde`, `Hasta`, `Períodos`, `Aplicar`,
689
+ and `Cancelar` with `locale="es-CO"`. Navigation announcements follow `locale`.
690
+ - `validate` runs on the draft; return a localized error to disable Apply. The error
691
+ is announced politely and associated with the Apply button. Keep validation pure.
692
+ - `renderSummary` optionally renders localized draft details. `formatDate` changes
693
+ visible dates, while optional hidden form fields always submit the applied ISO values.
694
+ - Cancel, Escape, outside pointer interaction, or moving focus outside discard the
695
+ draft. Apply, Cancel, and Escape return focus to the trigger. Opening again starts
696
+ from the latest controlled value. Calendar arrows retain their date-navigation behavior.
697
+ - The panel uses the browser Popover API top layer without a modal backdrop or focus
698
+ trap. A fixed-position fallback works where the API is unavailable; ancestor clipping
699
+ can affect that fallback. The page remains interactive.
700
+ - The panel tracks viewport changes, scroll, and trigger size. Narrow screens scroll
701
+ the trigger into view, stack the calendars, and scroll presets horizontally.
702
+ Only the body scrolls vertically; confirmation actions stay visible.
703
+ - `disabled` disables the trigger and optional form entries. `className` styles the
704
+ outer container. Import the shared stylesheet once at the application boundary.
705
+
706
+ This component is currently React-only. Publication is separate from local implementation.
707
+
708
+ ### Accessible code examples
709
+
710
+ `Code` and `CodeTabs` accept `copyLabel`, `copiedLabel`, `errorLabel`, and
711
+ `codeLabel`. Copy controls work directly in React, emit `ui:copy-success` or
712
+ `ui:copy-error`, and announce localized success or recovery guidance. Repeated
713
+ clicks restart feedback; unmounting clears its timer. No Astro runtime is needed.
714
+
715
+ Unwrapped code uses a named, keyboard-focusable region. `codeLabel` defaults to
716
+ `Code example`; set a descriptive localized name when several examples are
717
+ present. Highlighted `pre` children receive the same behavior while retaining
718
+ authored names and tab order. Wrapped code does not add an extra tab stop.
719
+
720
+ ### Server-sorted tables
721
+
722
+ `DataTable` accepts `sort` and `onSortChange` for controlled sorting, or
723
+ `defaultSort` for an initial uncontrolled sort. A `DataTableSort` contains a
724
+ column `key` and `direction: 'ascending' | 'descending'`; `null` means unsorted.
725
+ Header buttons request the next direction, and `aria-sort` describes the applied
726
+ state. Replacing `rows` does not reset that state.
727
+
728
+ Use `sortMode="manual"` with server pagination. In this mode the table preserves
729
+ the supplied row order, even when a header is activated. Apply the requested sort
730
+ to the complete dataset on the server before selecting the page, then supply the
731
+ returned rows and controlled sort. Keep authentication, query validation, loading
732
+ state, and network errors in the application. The default `sortMode="client"`
733
+ sorts a copy of the supplied rows.
734
+
735
+ ### Dialog dismissal and focus
736
+
737
+ `Dialog`, `AlertDialog`, and `useDialog` accept `dismissOnOutsidePress` and
738
+ `dismissOnEscape`. Ordinary dialogs allow both by default; alert dialogs ignore
739
+ outside presses by default. Set both to `false` while an application requires an
740
+ explicit decision or is completing a pending mutation. These policies govern
741
+ implicit dismissal; explicit close actions remain under application control.
742
+
743
+ Native Escape requests `onOpenChange(false)` without overriding a controlled
744
+ `open` value. Content padding and a drag that begins inside the dialog do not
745
+ count as backdrop dismissal. Consumer `onCancel` or `onClick` handlers can prevent
746
+ the corresponding default action.
747
+
748
+ A controlled dialog can open without hook trigger props: it captures the focused
749
+ opener before opening and returns focus on close or unmount, including when rendered into an iframe.
750
+ Focus capture and restoration use the dialog's own document. StrictMode replay
751
+ preserves that opener, and cleanup does not steal focus from a nested or
752
+ replacement dialog. Hook trigger props remain useful when the same component
753
+ owns the opener and dialog. Keep an accessible dialog name and logical initial
754
+ focus; native `autoFocus` can select the initial control.
755
+
756
+ ### Virtual list and editor ownership
757
+
758
+ `VirtualList` displays fixed-height rows with inert spacers that retain the full scroll extent.
759
+ Rows stay mounted; use pagination when the initial DOM cost matters. Scrolling, resizing, sizing
760
+ changes and direct row changes refresh the window. Focused rows remain available. See the
761
+ [fixed-height list contract](https://github.com/santi020k/lumen/blob/main/docs/ai-usage.md#fixed-height-virtual-lists).
762
+
763
+ External rich-text engines should handle the cancelable `ui:editor-command-request` event before
764
+ execution and use `ui:editor-command` only for completion notifications. React also supports
765
+ `useRichTextEditor({ commandHandler })`. Disable native toolbar state syncing when the external
766
+ engine owns it. See the [editor guidance](https://github.com/santi020k/lumen/blob/main/docs/ai-usage.md).
767
+
768
+ With native state enabled, `useRichTextEditor` initializes toolbar toggle states on mount,
769
+ including `aria-pressed`, before the first editing interaction. External command handlers
770
+ disable this synchronization by default so the application can own toolbar state.
771
+ Native toolbar and keyboard commands execute in the editor root's owning document, including
772
+ editors portaled into a same-origin iframe.
773
+
774
+ ## Phone presentation in v4
775
+
776
+ Phone inputs bundle the same offline flag artwork on every platform. The selected country shows
777
+ its flag and calling code inside one continuous input border. Country names remain in the native
778
+ picker and its accessible name; flags are supplementary. Unknown flag codes fall back to text.
779
+
780
+ Astro and React `PhoneInput` accept `disabled`, `readOnly`, `required`, `errorMessage`,
781
+ `showValidationError`, and `inputProps`. Their `id` targets the number input in v4; React also
782
+ accepts `inputRef`. Web Components use `disabled`, `readonly`, `required`, `error-message`,
783
+ `show-validation-error="false"`, and `input-id`, with native input attributes on the host.
784
+ Both controls lock together and validation remains associated with the input. React forwards
785
+ `inputProps.form` to the country picker and read-only country value as well as the number input,
786
+ so an external form receives both values. Legacy `countries` pickers also retain their selected
787
+ country while read-only and restore it after an accepted native form reset.
788
+
789
+ The `phone-input`, `phone-country`, and `country-flag` styling parts plus `--ui-phone-height`,
790
+ `--ui-phone-padding`, and `--ui-phone-country-gap` replace consumer CSS overlays.
791
+
792
+ Astro and React also export `CountryFlag` (`regionCode`, optional `decorative`) and `PhoneNumber`
793
+ (`value: LumenPhoneNumber`, optional `link`). A telephone link is rendered only for a complete
794
+ E.164 value. Use the model returned by the phone normalizer; keep domain persistence in your app.
795
+ Artwork attribution is shipped with the core package in `PHONE_FLAG_LICENSE.txt`.
796
+
797
+ ## Combobox keyboard behavior
798
+
799
+ Decoded Combobox options must be a dense string collection; invalid collections expose no options.
800
+ In v4, Combobox retains input focus and exposes its active option through `aria-activedescendant`.
801
+ Enter commits an active option; text editing and composition remain native. Escape dismisses one
802
+ 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)
803
+ for dynamic options, controlled inputs and migration guidance.
804
+
805
+ Combobox also supports native form reset: resetting the owning form restores an uncontrolled
806
+ `defaultValue`, closes the open option list, and clears the active selection without emitting
807
+ `onChange`. A controlled `value` is left unchanged.
808
+
809
+ ## Content flow
810
+
811
+ Stack and Grid own sibling spacing. Their gap accepts `related`, `group` (default) and `section`,
812
+ or canonical `none`, `xs`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl` sizes. Card owns the inset and gap
813
+ between its visible parts; use `density="compact"`, `"comfortable"` (default) or `"spacious"`.
814
+ Elements uses the same names as attributes. Use a nested Stack for CardContent groups and Field
815
+ for label/control/feedback. See [content flow](https://github.com/santi020k/lumen/blob/main/docs/content-flow.md) and the
816
+ [v4 migration guide](https://github.com/santi020k/lumen/blob/main/docs/migrating-to-lumen.md) for ownership and changed explicit gaps.
817
+
818
+ ### Reading and complete compositions
819
+
820
+ Prose and Typography trim outer child margins and separate headings from preceding text.
821
+ Container gutters grow from 16px to 32px with viewport width; override `--ui-container-gutter`
822
+ when a product needs fixed gutters. Card allows interactive overflow; use AspectRatio to clip media.
823
+ Install `content-flow-header`, `content-flow-settings`, `content-flow-list` or `content-flow-actions`
824
+ with `lumen add <recipe> --target astro|react|elements`. MCP returns the same complete examples.
825
+ Connect application actions and replace sample IDs before reuse. See
826
+ [content flow](https://github.com/santi020k/lumen/blob/main/docs/content-flow.md) for composition and migration guidance.
827
+
828
+ ### Large fixed-height collections
829
+
830
+ React `VirtualList` accepts typed `items`, `getKey` and `renderItem` for data mode.
831
+ Only the visible window, overscan and focused neighbors mount. Stable keys retain row identity;
832
+ applications own offscreen editing state. See [data rendering](https://github.com/santi020k/lumen/blob/main/docs/virtual-list-data.md) for
833
+ setup, lifecycle, accessibility and the mounted-mode tradeoff.
834
+
835
+ ### Compound dialog tasks
836
+
837
+ `DialogHeader`, `DialogTitle`, `DialogBody`, `DialogFooter`, and `DialogClose`
838
+ compose long forms with fixed actions and an independently scrolling body. Keep
839
+ `DialogBody` directly inside `Dialog`. Give `DialogTitle` an `id` and reference it
840
+ from `Dialog aria-labelledby`; `as` supports `h2`, `h3`, and `h4`.
841
+ `DialogClose` accepts Button props, honors `onClick` cancellation, and requests
842
+ closure through the enclosing Dialog controller, including controlled dialogs.
843
+ The four static structural parts are also exported from `@santi020k/lumen-react/server`.
844
+
845
+ ```tsx
846
+ <Dialog open={open} onOpenChange={setOpen} aria-labelledby="record-title">
847
+ <DialogHeader><DialogTitle id="record-title">Edit record</DialogTitle></DialogHeader>
848
+ <DialogBody><Form id="record-form"><Input name="name" aria-label="Name" /></Form></DialogBody>
849
+ <DialogFooter>
850
+ <DialogClose variant="outline">Cancel</DialogClose>
851
+ <Button type="submit" form="record-form">Save</Button>
852
+ </DialogFooter>
853
+ </Dialog>
854
+ ```
855
+
856
+ `FileUpload selectedFilesLabel` accepts localized text containing `{count}`.
857
+ FileUpload keeps its native file input uncontrolled: `value` and `defaultValue` are excluded from its props and ignored in decoded JavaScript input.
858
+ Reset feedback follows the current native form owner, including replaced external forms.
859
+ Accepted native form resets clear selected-file feedback; cancelled resets preserve it.
860
+
861
+ ### Rich description rows
862
+
863
+ `DescriptionItem`, `DescriptionTerm`, and `DescriptionDetail` accept native props
864
+ and refs and render `div`, `dt`, and `dd`. Compose them inside `Descriptions` for
865
+ rich values, alongside the existing `items` array when needed. The three static
866
+ parts are also available from `@santi020k/lumen-react/server` for use inside a native `dl`.
867
+
868
+ ```tsx
869
+ <Descriptions>
870
+ <DescriptionItem>
871
+ <DescriptionTerm>Status</DescriptionTerm>
872
+ <DescriptionDetail><Badge variant="success">Active</Badge></DescriptionDetail>
873
+ </DescriptionItem>
874
+ </Descriptions>
875
+ ```
876
+
877
+ ## Attachment composition
878
+
879
+ Use `AttachmentList` to group native `li` children and `AttachmentPreview` for browser-owned images
880
+ with localized loading, error, and unsupported-file states. Compose independent actions rather
881
+ than nesting controls inside a linked Attachment. The application retains file validation,
882
+ authorization, persistence, and object URL cleanup. See the
883
+ [attachment composition recipe](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#attachment-previews-and-file-lists)
884
+ for adapter props, slots, child contracts, retry identity, and safe state events.
885
+
886
+ ## Chart datum actions
887
+
888
+ Pass `onDatumActivate(detail)` to BarChart, LineChart, PieChart, ScatterChart, ComboChart, Heatmap,
889
+ or RangeChart to enable drilldown. The callback receives `LumenChartDatumActivationDetail`,
890
+ exported from this package, with raw axes and optional datum IDs. Applications own navigation,
891
+ filtering, detail views, and authorization. React charts use their own event handling and do not
892
+ require `UIPrimitives`.
893
+
894
+ Each available plotted datum has an equivalent native button in the actions disclosure, even
895
+ with `showTable={false}` or hidden line markers. Translate `labels.exploreData` and
896
+ `labels.formatDatumAction(context)` alongside the chart's existing labels and value formatters.
897
+ Missing observations have no action and never reach datum-action value formatters; pie actions cover only positive slices. Updated values and
898
+ callbacks take effect on rerender, while stable datum identities retain focused action buttons.
899
+ Chart activation uses the owning document, including iframe rendering.
900
+ The chart's native `onClick` can cancel activation with `event.preventDefault()`.
901
+
902
+ ## Dashboard composition
903
+
904
+ `FilterBar` groups host-owned filtering controls, active criteria, reset actions, and a polite
905
+ result announcement. `ChangeSummary` presents explicit before/after values and application-owned
906
+ changed state. Neither component owns requests, persistence, parsing, or financial policy.
907
+ Omit `FilterBar.open` for native disclosure ownership. Unrelated rerenders preserve native toggles
908
+ when `defaultOpen` is unchanged. Pass `open` and `onOpenChange` for application-controlled disclosure.
909
+
910
+ ScatterChart supports independent X/Y formatting, explicit domains, logarithmic positive X values,
911
+ and labeled reference lines/regions. See [consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md)
912
+ for dashboard tables, freshness, import review, activity inbox, and persistent Kanban patterns.
913
+
914
+ React DataTable adds `layout="records"`, rich `column.render`, and expandable `renderDetails`.
915
+ Use stable record IDs and controlled `expandedRowIds` across pages. Row identity uses `rowValue`,
916
+ then `id`, then a legacy `value` fallback; ordinary value cells do not override explicit IDs.
917
+ Rows without an explicit identity keep their original input index across client sorting.
918
+ Decoded row and column collections are validated before sorting or rendering. Invalid rows, columns, identities,
919
+ cell shapes, empty identities, and duplicate resolved identities fail closed to an empty table while
920
+ the column headers remain available. Client sorting only applies to columns marked `sortable`.
921
+ `DataTableSortControls` shares
922
+ `sort`/`onSortChange` with table headers; manual sorting preserves server page order.
923
+ Rows without an explicit `rowValue`, `value`, or `id` use their original input index as identity
924
+ during client sorting, preserving expanded details and local edits. Supply stable IDs when replacing,
925
+ filtering, or paginating the input rows.
926
+
927
+ Popover and DropdownMenu support anchored top-layer placement, viewport collision handling, logical
928
+ start/end alignment, and focus handoff in their owning document, including iframes.
929
+ Set `positioning="none"` for application-owned placement. Context menus dismiss outside pointer
930
+ presses; tooltips close when focus leaves their owner and cancel pending opening timers.
931
+
932
+ ### Actual-versus-target charts
933
+
934
+ `BulletChart` compares a nullable actual `value` with a finite `target` and optional
935
+ labeled `ranges`. A strong actual bar, target marker, readable value labels, and expandable exact
936
+ data work together. Domains include zero and all measurements; invalid inputs fail closed.
937
+ Invalid measurements do not reach `formatValue`.
938
+ 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)
939
+ for the input, localization, and domain contracts.
940
+
941
+ ### Rankings and paired comparisons
942
+
943
+ Use `LollipopChart` for zero-based rankings and `DumbbellChart` for paired measurements (native
944
+ `LumenLollipopChart` and `LumenDumbbellChart`). Supply ordered comparison data with `id`, `label`,
945
+ nullable `value`, optional nullable `reference`, and optional `tone`. Both charts preserve missing
946
+ values and expose exact data. See the [shared visualization contract](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md#rankings-and-paired-comparisons).
947
+
948
+ ### Calendar activity, ordered stages and distributions
949
+
950
+ `CalendarHeatmap`, `FunnelChart`, and `BoxPlot` share validated geometry with every Lumen adapter.
951
+ CalendarHeatmap takes date-only UTC `startDate`/`endDate`, nullable `{ date, value }` data and optional
952
+ `weekStartsOn` (0 for Sunday or 1 for Monday). It fills omitted dates as missing, keeps zero distinct,
953
+ and limits the inclusive range to 3,660 days. `weekdayLabels` always indexes Sunday through Saturday;
954
+ `formatDate` customizes readable dates without changing their identity.
955
+
956
+ FunnelChart takes ordered `{ id, label, value, tone? }` stages with nonnegative nullable values.
957
+ Stages retain the supplied order, including increasing values; the chart derives no conversion rates.
958
+ BoxPlot takes precomputed `{ id, label, min, q1, median, q3, max, outliers?, tone? }` statistics.
959
+ Statistics must be ordered and finite, or all five must be null for a missing row. Explicit domains
960
+ must contain all observations and outliers. `statisticLabels` localizes the six statistic names.
961
+
962
+ All three accept `formatValue`, chart `labels`, `summary`, and `showTable` (default true).
963
+ Exact data and missing measurements remain readable; invalid input fails closed instead of dropping
964
+ observations or clipping the domain. Prefer retaining the data table for complete visual inspection.
965
+
966
+ ```tsx
967
+ import { BoxPlot, CalendarHeatmap, FunnelChart } from '@santi020k/lumen-react'
968
+
969
+ <CalendarHeatmap heading="Daily visits" startDate="2026-01-01" endDate="2026-01-31"
970
+ data={[{ date: '2026-01-01', value: 42 }]} />
971
+ <FunnelChart heading="Checkout stages" data={[
972
+ { id: 'view', label: 'Viewed', value: 120 },
973
+ { id: 'paid', label: 'Paid', value: 32 }
974
+ ]} />
975
+ <BoxPlot heading="Response times" data={[
976
+ { id: 'api', label: 'API', min: 20, q1: 45, median: 60, q3: 90, max: 140, outliers: [210] }
977
+ ]} />
978
+ ```
979
+
980
+ ## Hook state updates
981
+
982
+ Public hook setters accept React functional updates. In uncontrolled mode, consecutive calls in
983
+ one event compose against the latest pending value. Change callbacks run once per setter call,
984
+ including under Strict Mode. Controlled values remain owned by the application.
985
+
986
+ `useToast().dismiss()` closes only the newest open toast. Pass an ID to dismiss a specific toast.
987
+
988
+ Highlighted Code updates generated region labels and removes its generated focus attributes when
989
+ wrapping is enabled, while preserving consumer-owned accessibility attributes.
990
+
991
+ ## Advanced table views
992
+
993
+ `DataTableView` is an opt-in TanStack Table companion for application-owned record tables. It adds
994
+ search, exact column filters, column visibility, density, pagination and controlled sorting state
995
+ while preserving your cells, details, actions and query ownership. Column keys must be nonempty
996
+ and unique; column labels, accessors, boolean flags and dense filter-option arrays are validated before
997
+ controls are constructed. Restored controlled and default snapshots require string search/filter values,
998
+ boolean visibility/sort directions, valid density and integer pagination; malformed state is rejected
999
+ before table processing. Import it from
1000
+ `@santi020k/lumen-react/components/data-table-view` or the package root. Load
1001
+ `@santi020k/lumen-react/styles/data-table-view.css` after the base stylesheet. See the
1002
+ [composition and server-mode guide](https://github.com/santi020k/lumen/blob/main/docs/data-table-views.md).
1003
+
1004
+ Existing `Table` and `DataTable` APIs remain available. This integration is React-specific and does
1005
+ not change the Astro or Elements runtime.
1006
+ ## Advanced form workflows
1007
+
1008
+ Use the optional `@santi020k/lumen-react/forms` entry point for validation, dependent fields,
1009
+ unsaved edit tracking, step navigation, stable repeatable rows, and cancelable asynchronous checks.
1010
+ Controls associated from outside the form through `form="id"` participate in edit tracking,
1011
+ blur validation and dependent-field validation. Portaled controls use React form callbacks without
1012
+ repeating validation through native document listeners. Each native edit is processed once, and
1013
+ controls associated with another form are ignored even when React bubbles their portal events.
1014
+ Adopted native controls retain checkbox, multiple-selection and file details in dirty-state tracking.
1015
+
1016
+ Accepted native reset buttons refresh dirty state after the browser restores defaults; canceled resets
1017
+ preserve the draft, errors, and attempted state, including cancellation by a native ancestor after
1018
+ React dispatch. `PromptComposer` keeps its standard `id` on the form for external submit controls
1019
+ and derives a separate labelled textarea ID.
1020
+
1021
+ See [composable form workflows](https://github.com/santi020k/lumen/blob/main/docs/powerful-forms.md). Applications retain schemas, financial
1022
+ rules, requests, draft storage, and authorization. Do not combine validation owners on one form.
1023
+
1024
+ `useResizable` accepts finite `panelCount` values up to 1,000. Fractions round down and negative values
1025
+ normalize to zero; non-finite or larger counts throw `RangeError` before allocating panel state.
1026
+ Context-menu item activation and keyboard focus follow the menu's owning document, including iframe
1027
+ portals. Date-range synchronization clamps an earlier end date before updating both input constraints.
1028
+
1029
+ ## Device demonstrations
1030
+
1031
+ `DeviceFrame` presents slotted HTML, an image, or a titled iframe inside `macbook-pro`, `macbook-air`, `imac`,
1032
+ `iphone`, `pixel`, and `ipad-pro` frames, with generic `laptop`, `desktop`, `android`, and `tablet` options.
1033
+ Device-specific enclosures include a MacBook Pro display notch and full front edge, a tapered MacBook Air deck, curved iMac
1034
+ stand and chin, rounded phone glass with separate rails and buttons, and an iPad Pro with even
1035
+ bezels and no Home button. The generic `tablet` retains its classic enclosure.
1036
+ Shells are decorative and do not emulate device hardware. `orientation` selects portrait or landscape; `tone` selects
1037
+ light or dark chrome independently of the screen content. Use `color="white"`,
1038
+ `color="black"`, or a 3-, 4-, 6-, or 8-digit hex color such as `color="#a9b8ac"` for
1039
+ a custom hardware finish. `color` takes precedence over `tone`; invalid values fall back to the
1040
+ tone. The finish stays independent of the content theme. For CSS-driven updates, set
1041
+ `--ui-device-color` to a CSS color on the frame.
1042
+
1043
+ Iframe layouts use the preset screen viewport and scale to the available width. Override
1044
+ `screenWidth` and `screenHeight` for a custom viewport (1–16384 CSS pixels). Slotted HTML shares
1045
+ the host viewport; container queries can adapt it to the screen. Images preserve their proportions.
1046
+ `scroll={false}` clips HTML overflow; an iframe manages its own scrolling.
1047
+
1048
+ Supply image alternative text and iframe titles. Consumers own iframe `sandbox`, `allow`,
1049
+ loading, and referrer policies. Remote sites can refuse embedding through their response headers.
1050
+ The decorative shell does not alter focus or intercept interactions. Camera and home-indicator
1051
+ details occupy separate chrome outside live HTML and iframe viewports. Direct phone images extend
1052
+ under the decorative camera and home indicator for a full-bleed presentation.
1053
+ Screen presets are 1280 × 800 for MacBook Pro and MacBook Air, 1440 × 810 for iMac, 390 × 844 for iPhone,
1054
+ 412 × 915 for Pixel, and 834 × 1194 for iPad Pro (1194 × 834 in landscape). These are demonstration
1055
+ viewports, not physical display specifications.
1056
+
1057
+ ```tsx
1058
+ import { DeviceFrame } from '@santi020k/lumen-react/components/device-frame'
1059
+
1060
+ <DeviceFrame device="laptop" screenWidth={1440} screenHeight={900}>
1061
+ <iframe src="/demo" title="Application demo" loading="lazy" />
1062
+ </DeviceFrame>
1063
+ ```
1064
+
1065
+ This component uses a client effect to maintain the scaled iframe viewport.
1066
+ ## Visual interactions
1067
+
1068
+ `MotionGroup`, `ChartMotion`, `VisualEffect`, `PromptComposer`, `StreamMessage`, `SourceCitation`,
1069
+ `ToolActivity`, and `ApprovalCard` are exported by the main package. `Tabs` accepts `indicator` for
1070
+ an optional moving selection underline. See [visual interactions](https://github.com/santi020k/lumen/blob/main/docs/visual-interactions.md).
1071
+ Install `motion@^14.0.0` only when using `@santi020k/lumen-react/motion`; its Motion wrappers remain
1072
+ separate from normal imports.
1073
+
1074
+ ## World map
1075
+
1076
+ WorldMap supports highlighted countries, location markers, dotted or solid styles, country selection,
1077
+ and theme customization. Use `initialView="highlighted"` (Elements: `initial-view`) to start with a
1078
+ regional view. Zoom toward the cursor with Ctrl/Cmd-scroll, or fit highlighted countries using the
1079
+ map controls. Import geography explicitly from
1080
+ `@santi020k/lumen-core/world-map-data`; it is excluded from root exports. See the
1081
+ [WorldMap usage guide](https://github.com/santi020k/lumen/blob/main/docs/world-map.md) for adapter examples, events, localization,
1082
+ accessibility, and customization.
1083
+
1084
+ For controlled selection, pair `selectedCountry` with `onSelectedCountryChange(countryId)` and
1085
+ store the requested value in application state. An empty string clears the selection while keeping
1086
+ the map controlled. `onCountrySelect(detail)` remains the country-detail callback for nonempty selections.
1087
+
1088
+ ## Operational workflows
1089
+
1090
+ Extend `DataTableView` with inclusive numeric/date ranges and explicit page-scoped selection.
1091
+ Use `DataTableSavedViews` for named preferences through application-owned persistence callbacks;
1092
+ `parseDataTableViewState` validates external preferences without retaining records or selection.
1093
+ See [table views](https://github.com/santi020k/lumen/blob/main/docs/data-table-views.md) for server pagination and selection boundaries.
1094
+
1095
+ `useLumenReviewWorkflow` from the root or `/forms` guards review, confirmation, stale revisions,
1096
+ duplicate submission and uncertain outcomes. Compose it with the installable React
1097
+ `review-workflow`, `import-review` and `record-workspace` recipes.
1098
+ See [operational recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#installable-operational-workflows).
1099
+
1100
+ ## Studio media workspace
1101
+
1102
+ Compose MediaViewport, MediaThumbnail, MediaFilmstrip and ImageComparison modes with the
1103
+ [Studio media workspace recipes](https://github.com/santi020k/lumen/blob/main/docs/studio-media-workspaces.md). Applications retain
1104
+ media loading, selection, adjustment algorithms, processing, export and persistence.
1105
+
1106
+ ## Resources
1107
+
1108
+ | Guide | What you will find |
1109
+ | --- | --- |
1110
+ | [Consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md) | Complete product compositions and application-owned behavior. |
1111
+ | [React Hook Form adapters](https://github.com/santi020k/lumen/blob/main/packages/react-hook-form/README.md) | Managed fields, composite controls, and schema validation. |
1112
+ | [Import and icon performance](https://github.com/santi020k/lumen/blob/main/docs/import-and-icon-performance.md) | Selective imports and reproducible bundle measurements. |
1113
+ | [Contributing](https://github.com/santi020k/lumen/blob/main/CONTRIBUTING.md) | Setup, checks, and contribution workflow. |
1114
+ | [Release history](https://github.com/santi020k/lumen/releases) | Published releases and version notes. |
1115
+
1116
+ Part of [Lumen UI](https://lumen.santi020k.com), created by [Santiago Molina](https://santi020k.com).
1117
+ Licensed under [MIT](https://github.com/santi020k/lumen/blob/main/LICENSE); third-party artwork retains its own notices.