@santi020k/lumen-core 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.
- package/PHONE_FLAG_LICENSE.txt +21 -0
- package/README.md +363 -3
- package/THIRD_PARTY_NOTICES.md +16 -2
- package/dist/ai-surfaces.d.ts +25 -0
- package/dist/ai-surfaces.d.ts.map +1 -0
- package/dist/ai-surfaces.js +162 -0
- package/dist/amount-field.d.ts +22 -0
- package/dist/amount-field.d.ts.map +1 -0
- package/dist/amount-field.js +207 -0
- package/dist/attachments.d.ts +15 -0
- package/dist/attachments.d.ts.map +1 -0
- package/dist/attachments.js +123 -0
- package/dist/bullet-chart.d.ts +33 -0
- package/dist/bullet-chart.d.ts.map +1 -0
- package/dist/bullet-chart.js +80 -0
- package/dist/chart-activation.d.ts +32 -0
- package/dist/chart-activation.d.ts.map +1 -0
- package/dist/chart-activation.js +97 -0
- package/dist/chart-interaction.d.ts +12 -0
- package/dist/chart-interaction.d.ts.map +1 -0
- package/dist/chart-interaction.js +274 -0
- package/dist/chart-models.d.ts +156 -0
- package/dist/chart-models.d.ts.map +1 -0
- package/dist/chart-models.js +348 -0
- package/dist/chart-motion.d.ts +6 -0
- package/dist/chart-motion.d.ts.map +1 -0
- package/dist/chart-motion.js +107 -0
- package/dist/charts.d.ts +58 -3
- package/dist/charts.d.ts.map +1 -1
- package/dist/charts.js +247 -18
- package/dist/code.js +1 -1
- package/dist/combobox.d.ts +7 -0
- package/dist/combobox.d.ts.map +1 -0
- package/dist/combobox.js +253 -0
- package/dist/comparison-chart.d.ts +32 -0
- package/dist/comparison-chart.d.ts.map +1 -0
- package/dist/comparison-chart.js +64 -0
- package/dist/components.d.ts +210 -25
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +102 -26
- package/dist/dashboard.d.ts +16 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +21 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +21 -5
- package/dist/dates.d.ts +13 -0
- package/dist/dates.d.ts.map +1 -0
- package/dist/dates.js +37 -0
- package/dist/device-frame.d.ts +25 -0
- package/dist/device-frame.d.ts.map +1 -0
- package/dist/device-frame.js +50 -0
- package/dist/direction.d.ts +3 -0
- package/dist/direction.d.ts.map +1 -0
- package/dist/direction.js +10 -0
- package/dist/extended-charts.d.ts +95 -0
- package/dist/extended-charts.d.ts.map +1 -0
- package/dist/extended-charts.js +175 -0
- package/dist/figma.d.ts.map +1 -1
- package/dist/figma.js +10 -1
- package/dist/forms.d.ts.map +1 -1
- package/dist/forms.js +18 -4
- package/dist/icon-data.generated.d.ts +1 -1
- package/dist/icon-data.generated.d.ts.map +1 -1
- package/dist/icon-data.generated.js +1 -1
- package/dist/icons.js +3 -4
- package/dist/illustrations.generated.d.ts +1 -1
- package/dist/image-comparison.d.ts +14 -0
- package/dist/image-comparison.d.ts.map +1 -0
- package/dist/image-comparison.js +48 -0
- package/dist/index.d.ts +35 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -3
- package/dist/media-viewport.d.ts +24 -0
- package/dist/media-viewport.d.ts.map +1 -0
- package/dist/media-viewport.js +119 -0
- package/dist/media-workspace.d.ts +27 -0
- package/dist/media-workspace.d.ts.map +1 -0
- package/dist/media-workspace.js +83 -0
- package/dist/message-scroller.d.ts +14 -0
- package/dist/message-scroller.d.ts.map +1 -0
- package/dist/message-scroller.js +113 -0
- package/dist/motion-workflows.d.ts +24 -0
- package/dist/motion-workflows.d.ts.map +1 -0
- package/dist/motion-workflows.js +220 -0
- package/dist/motion.d.ts +12 -0
- package/dist/motion.d.ts.map +1 -0
- package/dist/motion.js +96 -0
- package/dist/native-input.d.ts +25 -0
- package/dist/native-input.d.ts.map +1 -0
- package/dist/native-input.js +188 -0
- package/dist/phone-flags.generated.d.ts +2 -0
- package/dist/phone-flags.generated.d.ts.map +1 -0
- package/dist/phone-flags.generated.js +263 -0
- package/dist/phone.d.ts +4 -0
- package/dist/phone.d.ts.map +1 -1
- package/dist/phone.js +6 -1
- package/dist/rich-text.d.ts +7 -0
- package/dist/rich-text.d.ts.map +1 -1
- package/dist/rich-text.js +13 -0
- package/dist/rive.d.ts +37 -0
- package/dist/rive.d.ts.map +1 -0
- package/dist/rive.js +129 -0
- package/dist/schedule.d.ts.map +1 -1
- package/dist/schedule.js +63 -23
- package/dist/tab-indicator.d.ts +3 -0
- package/dist/tab-indicator.d.ts.map +1 -0
- package/dist/tab-indicator.js +57 -0
- package/dist/theme-audit.d.ts +16 -0
- package/dist/theme-audit.d.ts.map +1 -0
- package/dist/theme-audit.js +57 -0
- package/dist/theme-builder.d.ts +7 -1
- package/dist/theme-builder.d.ts.map +1 -1
- package/dist/theme-builder.js +45 -23
- package/dist/theme-presets.generated.d.ts +217 -0
- package/dist/theme-presets.generated.d.ts.map +1 -0
- package/dist/theme-presets.generated.js +231 -0
- package/dist/theme.d.ts +11 -3
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +56 -6
- package/dist/virtual-collection.d.ts +14 -0
- package/dist/virtual-collection.d.ts.map +1 -0
- package/dist/virtual-collection.js +132 -0
- package/dist/virtual-list.d.ts +7 -0
- package/dist/virtual-list.d.ts.map +1 -0
- package/dist/virtual-list.js +135 -0
- package/dist/virtual-window.d.ts +19 -0
- package/dist/virtual-window.d.ts.map +1 -0
- package/dist/virtual-window.js +84 -0
- package/dist/visual-effects.d.ts +6 -0
- package/dist/visual-effects.d.ts.map +1 -0
- package/dist/visual-effects.js +44 -0
- package/dist/world-map-data.generated.d.ts +9 -0
- package/dist/world-map-data.generated.d.ts.map +1 -0
- package/dist/world-map-data.generated.js +240 -0
- package/dist/world-map-navigation.d.ts +16 -0
- package/dist/world-map-navigation.d.ts.map +1 -0
- package/dist/world-map-navigation.js +53 -0
- package/dist/world-map-pan.d.ts +3 -0
- package/dist/world-map-pan.d.ts.map +1 -0
- package/dist/world-map-pan.js +66 -0
- package/dist/world-map-zoom.d.ts +13 -0
- package/dist/world-map-zoom.d.ts.map +1 -0
- package/dist/world-map-zoom.js +61 -0
- package/dist/world-map.d.ts +71 -0
- package/dist/world-map.d.ts.map +1 -0
- package/dist/world-map.js +143 -0
- package/package.json +51 -4
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013 Panayiotis Lipiridis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
10
|
+
so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,4 +1,66 @@
|
|
|
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 · Core</h1>
|
|
11
|
+
|
|
12
|
+
<p align="center">Shared contracts · Metadata · Framework-neutral helpers</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://www.npmjs.com/package/@santi020k/lumen-core"><img src="https://img.shields.io/npm/v/@santi020k/lumen-core?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/foundations">Documentation</a>
|
|
21
|
+
·
|
|
22
|
+
<a href="https://www.npmjs.com/package/@santi020k/lumen-core">npm</a>
|
|
23
|
+
·
|
|
24
|
+
<a href="https://github.com/santi020k/lumen/tree/main/packages/core">Source</a>
|
|
25
|
+
·
|
|
26
|
+
<a href="https://github.com/santi020k/lumen/issues">Issues</a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
**Package:** `@santi020k/lumen-core`
|
|
30
|
+
|
|
31
|
+
**On this page:** [Install](#install) · [Quick start](#quick-start) · [Appearance presets](#appearance-presets) · [Resources](#resources)
|
|
32
|
+
|
|
33
|
+
<details>
|
|
34
|
+
<summary>Browse all sections</summary>
|
|
35
|
+
|
|
36
|
+
- [Install](#install)
|
|
37
|
+
- [Quick start](#quick-start)
|
|
38
|
+
- [Data view state](#data-view-state)
|
|
39
|
+
- [Schedule helpers](#schedule-helpers)
|
|
40
|
+
- [Appearance presets](#appearance-presets)
|
|
41
|
+
- [Language Helpers](#language-helpers)
|
|
42
|
+
- [Date Helpers](#date-helpers)
|
|
43
|
+
- [Chart Helpers](#chart-helpers)
|
|
44
|
+
- [Phone Helpers](#phone-helpers)
|
|
45
|
+
- [Icon Credits](#icon-credits)
|
|
46
|
+
- [Theme Builder Helpers](#theme-builder-helpers)
|
|
47
|
+
- [Figma and Design Tokens](#figma-and-design-tokens)
|
|
48
|
+
- [Static interface icon data](#static-interface-icon-data)
|
|
49
|
+
- [Consumer diagnostics and exact amounts](#consumer-diagnostics-and-exact-amounts)
|
|
50
|
+
- [Combobox DOM controller](#combobox-dom-controller)
|
|
51
|
+
- [Virtual collections and direction](#virtual-collections-and-direction)
|
|
52
|
+
- [Exact localized input drafts](#exact-localized-input-drafts)
|
|
53
|
+
- [Attachment preview state](#attachment-preview-state)
|
|
54
|
+
- [Chart datum activation](#chart-datum-activation)
|
|
55
|
+
- [Dashboard contracts](#dashboard-contracts)
|
|
56
|
+
- [Extended chart models](#extended-chart-models)
|
|
57
|
+
- [Optional visual integrations](#optional-visual-integrations)
|
|
58
|
+
- [Presence motion](#presence-motion)
|
|
59
|
+
- [World map](#world-map)
|
|
60
|
+
- [Studio media workspace](#studio-media-workspace)
|
|
61
|
+
- [Resources](#resources)
|
|
62
|
+
|
|
63
|
+
</details>
|
|
2
64
|
|
|
3
65
|
Shared metadata, token constants, and tiny utilities used by the Lumen package family.
|
|
4
66
|
|
|
@@ -16,6 +78,59 @@ Cross-platform foundations originate in `tokens/lumen.tokens.json` and are publi
|
|
|
16
78
|
numeric spacing, radius, typography, duration, easing, and elevation values. The legacy
|
|
17
79
|
`lumenColors` export retains CSS-ready HSL values.
|
|
18
80
|
|
|
81
|
+
## Install
|
|
82
|
+
|
|
83
|
+
Install core directly when building framework-neutral tooling or a custom adapter:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pnpm add @santi020k/lumen-core
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Core provides data and helpers; it does not register elements, render components, or load CSS.
|
|
90
|
+
Use documented subpath exports such as `/charts`, `/phone`, and `/icon-data` for focused imports.
|
|
91
|
+
Image-comparison helpers use `/image-comparison`; DOM windowing uses
|
|
92
|
+
`/virtual-list`, and data-renderer window calculations use `/virtual-window`.
|
|
93
|
+
|
|
94
|
+
## Quick start
|
|
95
|
+
|
|
96
|
+
Read semantic foundations through the focused token entry:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { lumenColors } from '@santi020k/lumen-core/tokens'
|
|
100
|
+
|
|
101
|
+
const brandChannels = lumenColors.brand
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`lumenColors` contains CSS-ready HSL channels. For portable JSON and native generated palettes,
|
|
105
|
+
use the [design tokens package](https://github.com/santi020k/lumen/blob/main/packages/tokens/README.md).
|
|
106
|
+
|
|
107
|
+
## Data view state
|
|
108
|
+
|
|
109
|
+
`serializeDataViewState` and `parseDataViewState` preserve named filters, including names that
|
|
110
|
+
match built-in object properties, as ordinary own properties. Repeated filter parameters use
|
|
111
|
+
the last value. `createDataViewRequestUrl` and `createDataViewServerRequest` append state to
|
|
112
|
+
an endpoint's query before its fragment, preserving existing query parameters and fragment text.
|
|
113
|
+
Import these helpers from the root or `@santi020k/lumen-core/data`.
|
|
114
|
+
|
|
115
|
+
## Schedule helpers
|
|
116
|
+
|
|
117
|
+
`canPlaceScheduleEvent` rejects malformed or reversed candidate intervals, including in an empty
|
|
118
|
+
schedule. `scheduleEventsOverlap` conservatively treats an invalid interval as a conflict with
|
|
119
|
+
a different event sharing its resource; distinct resources and identical event IDs retain their
|
|
120
|
+
existing behavior. Adjacent valid intervals remain available.
|
|
121
|
+
|
|
122
|
+
`resizeScheduleEvent` and `expandRecurringScheduleEvent` reject invalid date-times with `TypeError`.
|
|
123
|
+
Recurrence advances UTC calendar days and preserves UTC time-of-day independently of the host time zone. Applications own civil-time recurrence policies and DST conversion.
|
|
124
|
+
|
|
125
|
+
Invalid bounds, non-finite snapping/recurrence options, unsupported resulting date-times, and
|
|
126
|
+
non-integer or negative recurrence counts produce `RangeError`. Applications own validation and
|
|
127
|
+
recovery feedback. `parseScheduleEvents` preserves structurally valid records with malformed
|
|
128
|
+
date-times so applications can repair the original data; these helpers do not rewrite storage.
|
|
129
|
+
|
|
130
|
+
## Appearance presets
|
|
131
|
+
|
|
132
|
+
Use `createThemePreset('studio', { scheme: 'dark', overrides: { 'ui-radius': '0.75rem' } })` for a named starting point. Default, Studio and Glass share the [appearance contract](https://github.com/santi020k/lumen/blob/main/docs/appearance-presets.md).
|
|
133
|
+
|
|
19
134
|
## Language Helpers
|
|
20
135
|
|
|
21
136
|
The root entry exports the shared locale contract used by the Astro, React, and Elements language
|
|
@@ -45,12 +160,43 @@ const label = formatLumenLanguageLabel(
|
|
|
45
160
|
)
|
|
46
161
|
```
|
|
47
162
|
|
|
163
|
+
## Date Helpers
|
|
164
|
+
|
|
165
|
+
Calendar adapters share `parseLumenDate`, `isLumenDateBoundsValid`, and
|
|
166
|
+
`isLumenDateRangeValid` from the core root entry. They accept complete Gregorian
|
|
167
|
+
`YYYY-MM-DD` dates in years 0001–9999, reject overflow dates, and require ordered,
|
|
168
|
+
inclusive bounds. `resolveLumenDateLocale` normalizes an explicit locale or the
|
|
169
|
+
browser locale, falling back to English for invalid locale tags.
|
|
170
|
+
`resolveLumenDateLabels` supplies English and Spanish navigation, picker, and
|
|
171
|
+
invalid-range defaults through the `LumenDateLabels` contract. Components allow
|
|
172
|
+
label overrides for other languages; pass an explicit locale during SSR.
|
|
173
|
+
|
|
174
|
+
`isLumenDateRangeValid` accepts unknown restored data and returns `false` for a missing record
|
|
175
|
+
or non-string range fields instead of dereferencing malformed input.
|
|
176
|
+
|
|
48
177
|
## Chart Helpers
|
|
49
178
|
|
|
179
|
+
The package root also exports `createLumenLineChartModel`, `createLumenWaterfallGeometry`,
|
|
180
|
+
`createLumenHistogramGeometry`, and `createLumenHeatmapModel`. These pure models share web geometry,
|
|
181
|
+
validation, ticks, and annotations. The optional `createLumenChartInteractionController` owns only
|
|
182
|
+
DOM listeners and cursor/legend state; call `destroy()` when removing its surface.
|
|
183
|
+
Line chart models ignore malformed annotation entries and containers before reading overlay fields.
|
|
184
|
+
When annotation IDs repeat, the first valid entry wins so overlay identities stay unique.
|
|
185
|
+
Destroying a chart controller restores series marks, inspection values, and legend pressed state
|
|
186
|
+
so rebinding starts with all series visible.
|
|
187
|
+
Waterfall and histogram geometry accept arrays of unknown decoded rows, validate their complete
|
|
188
|
+
shape before accumulation or sorting, and return `valid: false` with empty marks when a row is
|
|
189
|
+
malformed. Typed component props continue to use `LumenWaterfallDatum` and `LumenHistogramBin`.
|
|
190
|
+
`normalizeLumenHeatmapData` validates decoded cell arrays before adapters read coordinates or
|
|
191
|
+
format labels. It returns an empty array when any row has malformed coordinates, measurement
|
|
192
|
+
types, labels, identity, or tone. Heatmap geometry and models use it internally. Explicit `null`
|
|
193
|
+
and non-finite numeric measurements retain the existing missing-cell behavior; numeric coordinates
|
|
194
|
+
must be finite.
|
|
195
|
+
|
|
50
196
|
`@santi020k/lumen-core/charts` exports the shared `LumenChartSeries` contract plus deterministic
|
|
51
197
|
domain, tick, scaling, line/area, grouped/stacked bar, and pie/donut geometry helpers. They render
|
|
52
198
|
no DOM and perform no statistical analysis; framework packages use them to keep chart output
|
|
53
|
-
aligned.
|
|
199
|
+
aligned. Linear x coordinates ignore blank strings rather than treating them as zero.
|
|
54
200
|
|
|
55
201
|
```ts
|
|
56
202
|
import {
|
|
@@ -89,6 +235,8 @@ as-you-type formatting, validation, pasted international-number detection, and E
|
|
|
89
235
|
web and React Native adapters.
|
|
90
236
|
|
|
91
237
|
```ts
|
|
238
|
+
import { getLumenPhoneCountry, resolveLumenPhoneNumber } from '@santi020k/lumen-core/phone'
|
|
239
|
+
|
|
92
240
|
const colombia = getLumenPhoneCountry('CO', { locale: 'en-US' })
|
|
93
241
|
if (!colombia) throw new Error('Missing Colombia metadata')
|
|
94
242
|
|
|
@@ -98,6 +246,8 @@ const phone = resolveLumenPhoneNumber(colombia, '6015550123')
|
|
|
98
246
|
It also exports the Lucide-backed icon map (`lumenIcons`, `lumenIconNames`) and helpers such as
|
|
99
247
|
`renderLumenIconSvg` so framework adapters can render icons by name.
|
|
100
248
|
|
|
249
|
+
Icon resolution only accepts dictionary-owned names; inherited object properties are rejected.
|
|
250
|
+
|
|
101
251
|
## Icon Credits
|
|
102
252
|
|
|
103
253
|
Icons are provided by [Lucide](https://lucide.dev/) and its open-source contributors. Lucide is
|
|
@@ -162,4 +312,214 @@ React consumers can import both the renderer and definitions from
|
|
|
162
312
|
`@santi020k/lumen-react/icons`. Existing `getLumenIcon`, runtime names, and registered icon packs
|
|
163
313
|
remain supported through the original API. The interface selection in `icons/lumen.icons.json`
|
|
164
314
|
controls the generated exports; run `pnpm run generate:platform-icons` after catalog changes.
|
|
165
|
-
See [consumer UI recipes](
|
|
315
|
+
See [consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#static-react-icons).
|
|
316
|
+
|
|
317
|
+
## Consumer diagnostics and exact amounts
|
|
318
|
+
|
|
319
|
+
`auditLumenTheme` requires every semantic color mapping and checks normal-text contrast for
|
|
320
|
+
resolved opaque HSL channels. `inspectLumenTheme(element)` reads computed mappings in each
|
|
321
|
+
representative light, dark and nested scope. Unsupported color syntax produces a finding rather
|
|
322
|
+
than an assumed pass; these checks supplement rendered accessibility verification.
|
|
323
|
+
|
|
324
|
+
`formatLumenAmountDraft`, `parseLumenAmountDraft` and `getLumenAmountValue` preserve exact ASCII
|
|
325
|
+
decimal strings across localized display and editing. Invalid locale tags fall back to `en-US`
|
|
326
|
+
consistently in server rendering, parsing and browser controllers. Call the amount controller’s
|
|
327
|
+
`refresh()` after moving it to a different document or shadow root; this rebinds native form resets
|
|
328
|
+
without replacing its draft. The shared amount and message-scroller DOM controllers power the web
|
|
329
|
+
adapters. Currency policy and message state remain consumer-owned.
|
|
330
|
+
|
|
331
|
+
## Combobox DOM controller
|
|
332
|
+
|
|
333
|
+
`createLumenComboboxController(root)` enhances a client-side root containing an
|
|
334
|
+
`input[role="combobox"]` and a `[role="listbox"]` with `[role="option"]` children.
|
|
335
|
+
It preserves editing focus, observes option changes and supports delegated selection. It returns
|
|
336
|
+
`close()` and `destroy()`; call `destroy()` when the owner disconnects. Astro and Elements manage
|
|
337
|
+
that lifecycle automatically. React uses its state-driven component with the same keyboard contract.
|
|
338
|
+
An accepted native form reset closes options, clears the active descendant, and refilters against
|
|
339
|
+
the restored input value without emitting change events. Canceled resets preserve editing state;
|
|
340
|
+
`destroy()` cancels pending reset work. Pointer selection cancels native option-button submission.
|
|
341
|
+
Filtering preserves application-hidden options; cleanup restores options hidden by the filter.
|
|
342
|
+
The controller respects disabled fieldset ancestors and uses the input's current form association
|
|
343
|
+
when handling resets.
|
|
344
|
+
|
|
345
|
+
The mounted VirtualList controller supports roots and rows created in another document, including
|
|
346
|
+
same-origin iframe documents.
|
|
347
|
+
Data-mode collections preserve iframe keyboard focus across distant scrolling and keyed updates,
|
|
348
|
+
and return focus to the list when the focused record is removed.
|
|
349
|
+
|
|
350
|
+
## Virtual collections and direction
|
|
351
|
+
|
|
352
|
+
`createLumenVirtualCollectionController(root, { items, getKey, renderItem, itemSize, overscan })`
|
|
353
|
+
owns an empty data-mode VirtualList root. It mounts only visible rows and focused neighbors, retains
|
|
354
|
+
stable keyed wrappers, and returns `update(items)` and `destroy()`. Declare data mode before the
|
|
355
|
+
Astro or Elements runtime initializes. See [data rendering](https://github.com/santi020k/lumen/blob/main/docs/virtual-list-data.md) for
|
|
356
|
+
setup, state ownership and lifecycle examples.
|
|
357
|
+
|
|
358
|
+
`getLumenVirtualWindow` calculates a fixed-height window with optional disjoint focus retention.
|
|
359
|
+
`observeLumenVirtualWindow` observes scroll, resize and focus; its handle provides `update()` and
|
|
360
|
+
`destroy()`. Applications using these lower-level helpers own row rendering and cleanup.
|
|
361
|
+
Data-mode controllers also preserve focus and reusable keyed content in same-origin iframe
|
|
362
|
+
documents, including elements adopted from another document. Range events and resize observation
|
|
363
|
+
use the root's document.
|
|
364
|
+
|
|
365
|
+
`getLumenDirectionalKey(element, key)` resolves the element's current inherited CSS direction and
|
|
366
|
+
swaps horizontal arrows in RTL. Other keys are unchanged. Web adapters use it for visual keyboard
|
|
367
|
+
navigation; native range inputs retain browser-owned behavior.
|
|
368
|
+
|
|
369
|
+
## Exact localized input drafts
|
|
370
|
+
|
|
371
|
+
`parseLumenDecimalDraft(value, locale)` distinguishes empty, incomplete, invalid and valid decimal
|
|
372
|
+
input. `isLumenDecimalInBounds(value, { locale, min, max, step })` validates complete values;
|
|
373
|
+
`stepLumenDecimalDraft(value, direction, options)` performs exact steps and inclusive clamping without
|
|
374
|
+
floating-point conversion. Bounds and steps use ASCII decimal strings; drafts use localized decimal
|
|
375
|
+
separators, the locale's numbering-system digits, and Unicode decimal digits. This includes Chinese
|
|
376
|
+
decimal numerals (`hanidec`), so formatted steps remain editable on subsequent steps.
|
|
377
|
+
Grouping, exponents, whitespace and inputs exceeding 128
|
|
378
|
+
characters are rejected. Empty drafts stay distinct from zero; unfinished drafts cannot step.
|
|
379
|
+
Malformed locale tags safely use English decimal symbols.
|
|
380
|
+
Applications own units, currency policy, required validation and submission serialization.
|
|
381
|
+
|
|
382
|
+
`normalizeLumenNumericOTP(proposal, length)` normalizes Unicode decimal digits, whitespace and hyphens
|
|
383
|
+
into ASCII, rejects unrelated text and excess digits, and bounds input to 128 characters. Length must
|
|
384
|
+
be 1–12. `LumenTimeSelection`, `isLumenTimeSelection` and `isLumenTimeInBounds` describe wall-clock
|
|
385
|
+
hours/minutes with inclusive same-day bounds, leaving dates and time zones to the application.
|
|
386
|
+
## Attachment preview state
|
|
387
|
+
|
|
388
|
+
`resolveLumenAttachmentPreviewState` resolves explicit fallback states, unsupported MIME types,
|
|
389
|
+
and image failures. `createLumenAttachmentPreviewController` enhances the documented DOM child
|
|
390
|
+
contract with safe state events and lifecycle cleanup. Use the framework components for product UI;
|
|
391
|
+
see the [attachment recipe](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md#attachment-previews-and-file-lists).
|
|
392
|
+
|
|
393
|
+
Non-string source or MIME metadata resolves to `unavailable`; explicit loading and error states
|
|
394
|
+
still take precedence.
|
|
395
|
+
|
|
396
|
+
## Chart datum activation
|
|
397
|
+
|
|
398
|
+
`createLumenChartDatumActivation(seriesId, datum)`, `createLumenHeatmapDatumActivation(datum)`,
|
|
399
|
+
and `createLumenRangeDatumActivation(datum)` return a validated `LumenChartDatumActivationDetail`
|
|
400
|
+
or `null` for an unavailable observation. The discriminated payload preserves raw axes and optional
|
|
401
|
+
datum IDs. `parseLumenChartDatumActivation(unknown)` validates event data and removes unrelated
|
|
402
|
+
fields. These helpers are also available from `@santi020k/lumen-core/charts`.
|
|
403
|
+
|
|
404
|
+
For custom browser charts, `createLumenChartActivationController(root)` delegates clicks from
|
|
405
|
+
`[data-ui-chart-datum]` descendants owned by a `[data-ui-chart-activation]` root. Each target's
|
|
406
|
+
attribute contains a JSON payload from the builders. Use native buttons with descriptive labels
|
|
407
|
+
as keyboard equivalents for decorative SVG marks; the controller does not create that UI.
|
|
408
|
+
It emits the bubbling, composed `ui:chart-datum-activate` event and returns `destroy()` for cleanup.
|
|
409
|
+
Applications own navigation, filtering, detail views, and server authorization.
|
|
410
|
+
|
|
411
|
+
## Dashboard contracts
|
|
412
|
+
|
|
413
|
+
`LumenChangeSummaryItem` carries explicit display values and caller-owned changed state;
|
|
414
|
+
`LumenActiveFilter` identifies an active criterion. `readLumenChangeSummaryItems` and
|
|
415
|
+
`readLumenActiveFilters` validate unknown input, reject duplicate/empty IDs, and copy records.
|
|
416
|
+
|
|
417
|
+
Scatter geometry supports positive logarithmic X coordinates, explicit domains, and shared
|
|
418
|
+
reference projection. `createLumenScatterReferences`, `scaleLumenScatterX`, and
|
|
419
|
+
`getLumenScatterXTicks` use the same coordinate contract as `createLumenScatterGeometry`.
|
|
420
|
+
See [consumer UI recipes](https://github.com/santi020k/lumen/blob/main/docs/consumer-ui-recipes.md) for application ownership boundaries.
|
|
421
|
+
|
|
422
|
+
## Extended chart models
|
|
423
|
+
|
|
424
|
+
`createLumenCalendarHeatmapGeometry`, `createLumenFunnelGeometry`, and
|
|
425
|
+
`createLumenBoxPlotGeometry` validate complete datasets and provide normalized geometry.
|
|
426
|
+
Sparse datasets and sparse box-plot outlier arrays are invalid, including empty array slots.
|
|
427
|
+
Comparison chart geometry follows the same rule. Use explicit `null` measurements for missing
|
|
428
|
+
observations; invalid datasets return no plotted rows or cells.
|
|
429
|
+
See [data visualization](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md) for date-only identities, ordered
|
|
430
|
+
stages, precomputed quartiles, missing values, and domain rules.
|
|
431
|
+
|
|
432
|
+
`isLumenTimeSelection` narrows unknown decoded values to valid same-day wall-clock selections.
|
|
433
|
+
`isLumenTimeInBounds` returns false for malformed selections and preserves explicit bounds validation.
|
|
434
|
+
|
|
435
|
+
## Optional visual integrations
|
|
436
|
+
|
|
437
|
+
Coordinated DOM motion, chart continuity, safe citation URLs, and prompt/approval controllers are
|
|
438
|
+
exported from the main package without an animation SDK. See [visual interactions](https://github.com/santi020k/lumen/blob/main/docs/visual-interactions.md).
|
|
439
|
+
The optional `@santi020k/lumen-core/rive` entry requires the optional peer `@rive-app/canvas@^2.44.0`.
|
|
440
|
+
It owns playback, data bindings, reduced motion, visibility, and cleanup for a consumer-owned asset.
|
|
441
|
+
## Presence motion
|
|
442
|
+
|
|
443
|
+
`animateLumenPresence(element, options)` animates a mounted DOM reference using the browser's Web
|
|
444
|
+
Animations API. It is also exported from `@santi020k/lumen`. No animation dependency is required.
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
import { animateLumenPresence } from '@santi020k/lumen-core'
|
|
448
|
+
|
|
449
|
+
const controller = new AbortController()
|
|
450
|
+
await animateLumenPresence(element, {
|
|
451
|
+
duration: 'standard',
|
|
452
|
+
preset: 'slide-up',
|
|
453
|
+
signal: controller.signal
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
// Before exiting, move focus out of the item and prevent further interaction.
|
|
457
|
+
element.inert = true
|
|
458
|
+
const result = await animateLumenPresence(element, {
|
|
459
|
+
phase: 'exit',
|
|
460
|
+
preset: 'fade',
|
|
461
|
+
signal: controller.signal
|
|
462
|
+
})
|
|
463
|
+
if (result !== 'cancelled') element.remove()
|
|
464
|
+
else element.inert = false
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Presets are `fade`, `slide-up`, and `scale`. Durations are `fast`, `standard`, and `slow`; the helper
|
|
468
|
+
reads the matching `--ui-duration-fast`, `--ui-duration`, or `--ui-duration-slow` and
|
|
469
|
+
`--ui-ease-emphasized` CSS tokens, with shared-token fallbacks. Existing transforms and opacity are
|
|
470
|
+
preserved. Use a wrapper when another animation already owns those properties.
|
|
471
|
+
|
|
472
|
+
The promise resolves to `finished`, `cancelled`, or `skipped`. Reduced motion, zero duration,
|
|
473
|
+
detached elements, and missing browser animation support skip the effect. Abort on unmount or when
|
|
474
|
+
an operation becomes stale. A new presence request cancels the previous one on the same element.
|
|
475
|
+
System reduced-motion changes cancel a running effect and resolve it as `skipped`. DOM removal,
|
|
476
|
+
framework state, focus restoration, inertness, and announcements belong to the consumer; an exit
|
|
477
|
+
animation does not hide or remove the element itself. Unexpected animation failures reject the
|
|
478
|
+
promise and should use the application's error handling.
|
|
479
|
+
|
|
480
|
+
Use `data-ui-motion="reduce"` on a container to skip new presence requests and, with the optional
|
|
481
|
+
stylesheet, disable descendant CSS motion. Abort existing requests when changing this local preference. An application must never
|
|
482
|
+
override the user's system reduced-motion preference to force animations.
|
|
483
|
+
|
|
484
|
+
Import `@santi020k/lumen/styles/motion.css` alongside the base stylesheet to opt into native
|
|
485
|
+
disclosure height transitions and the CSS reduction scope. This small optional stylesheet works
|
|
486
|
+
with Astro, React, and Elements and keeps those effects out of the default stylesheet. The presence
|
|
487
|
+
helper's system and local reduced-motion checks work without this CSS import.
|
|
488
|
+
|
|
489
|
+
When using Tailwind, import Lumen's layer order before either stylesheet:
|
|
490
|
+
|
|
491
|
+
```css
|
|
492
|
+
@import "@santi020k/lumen/layers.css";
|
|
493
|
+
@import "tailwindcss";
|
|
494
|
+
@import "@santi020k/lumen/styles.css";
|
|
495
|
+
@import "@santi020k/lumen/styles/motion.css";
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## World map
|
|
499
|
+
|
|
500
|
+
WorldMap supports highlighted countries, location markers, dotted or solid styles, country selection,
|
|
501
|
+
and theme customization. Use `initialView="highlighted"` (Elements: `initial-view`) to start with a
|
|
502
|
+
regional view. Zoom toward the cursor with Ctrl/Cmd-scroll, or fit highlighted countries using the
|
|
503
|
+
map controls. Import geography explicitly from
|
|
504
|
+
`@santi020k/lumen-core/world-map-data`; it is excluded from root exports. See the
|
|
505
|
+
[WorldMap usage guide](https://github.com/santi020k/lumen/blob/main/docs/world-map.md) for adapter examples, events, localization,
|
|
506
|
+
accessibility, and customization.
|
|
507
|
+
|
|
508
|
+
## Studio media workspace
|
|
509
|
+
|
|
510
|
+
Compose MediaViewport, MediaThumbnail, MediaFilmstrip and ImageComparison modes with the
|
|
511
|
+
[Studio media workspace recipes](https://github.com/santi020k/lumen/blob/main/docs/studio-media-workspaces.md). Applications retain
|
|
512
|
+
media loading, selection, adjustment algorithms, processing, export and persistence.
|
|
513
|
+
|
|
514
|
+
## Resources
|
|
515
|
+
|
|
516
|
+
| Guide | What you will find |
|
|
517
|
+
| --- | --- |
|
|
518
|
+
| [Cross-platform architecture](https://github.com/santi020k/lumen/blob/main/docs/cross-platform.md) | Shared foundations, adapter boundaries, and support tiers. |
|
|
519
|
+
| [Data visualization](https://github.com/santi020k/lumen/blob/main/docs/data-visualization.md) | Data visualization guidance and examples. |
|
|
520
|
+
| [Figma token export](https://github.com/santi020k/lumen/blob/main/docs/figma.md) | Variables, token export, and Code Connect workflows. |
|
|
521
|
+
| [Contributing](https://github.com/santi020k/lumen/blob/main/CONTRIBUTING.md) | Setup, checks, and contribution workflow. |
|
|
522
|
+
| [Release history](https://github.com/santi020k/lumen/releases) | Published releases and version notes. |
|
|
523
|
+
|
|
524
|
+
Part of [Lumen UI](https://lumen.santi020k.com), created by [Santiago Molina](https://santi020k.com).
|
|
525
|
+
Licensed under [MIT](https://github.com/santi020k/lumen/blob/main/LICENSE); third-party artwork retains its own notices.
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
# Third-Party Notices
|
|
2
2
|
|
|
3
|
-
`@santi020k/lumen-core` uses libphonenumber-js
|
|
4
|
-
are reproduced from their licenses.
|
|
3
|
+
`@santi020k/lumen-core` uses libphonenumber-js, the Lucide icon library, and Natural Earth map data.
|
|
4
|
+
The following notices are reproduced from their licenses.
|
|
5
|
+
|
|
6
|
+
## Natural Earth
|
|
7
|
+
|
|
8
|
+
The `@santi020k/lumen-core/world-map-data` country geometry is generated from
|
|
9
|
+
[Natural Earth](https://www.naturalearthdata.com/) 1:50m Cultural Vectors, Admin 0 Countries.
|
|
10
|
+
|
|
11
|
+
Per Natural Earth's [terms of use](https://www.naturalearthdata.com/about/terms-of-use/):
|
|
12
|
+
|
|
13
|
+
Natural Earth map data is public domain and may be modified and redistributed without permission
|
|
14
|
+
or required attribution.
|
|
15
|
+
|
|
16
|
+
See [`maps/README.md`](../../maps/README.md) for the exact source release, checksum, retrieval
|
|
17
|
+
date, projection, and precision notes, and `scripts/import-natural-earth-world-map.mjs` for the
|
|
18
|
+
reproducible import step.
|
|
5
19
|
|
|
6
20
|
## libphonenumber-js
|
|
7
21
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type LumenStreamStatus = 'idle' | 'streaming' | 'complete' | 'error' | 'canceled';
|
|
2
|
+
export type LumenToolStatus = 'queued' | 'running' | 'success' | 'error' | 'canceled';
|
|
3
|
+
export type LumenApprovalStatus = 'pending' | 'approved' | 'rejected';
|
|
4
|
+
export interface LumenPromptSubmitDetail {
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LumenApprovalResponseDetail {
|
|
8
|
+
requestId: string;
|
|
9
|
+
response: 'approve' | 'reject';
|
|
10
|
+
}
|
|
11
|
+
export declare const readLumenPromptSubmitDetail: (value: unknown) => LumenPromptSubmitDetail | undefined;
|
|
12
|
+
export declare const readLumenApprovalResponseDetail: (value: unknown) => LumenApprovalResponseDetail | undefined;
|
|
13
|
+
export declare const normalizeLumenPromptLimit: (value: unknown) => number;
|
|
14
|
+
export declare const canSubmitLumenPrompt: (text: string, limit?: number) => boolean;
|
|
15
|
+
/** Citations accept safe web destinations and local absolute paths, never executable schemes. */
|
|
16
|
+
export declare const resolveLumenCitationHref: (value: unknown) => string | undefined;
|
|
17
|
+
export interface LumenPromptComposerController {
|
|
18
|
+
destroy(): void;
|
|
19
|
+
sync(): void;
|
|
20
|
+
}
|
|
21
|
+
/** Bind an event bridge. A supplied native action keeps its normal POST behavior. */
|
|
22
|
+
export declare const createLumenPromptComposerController: (root: HTMLElement) => LumenPromptComposerController;
|
|
23
|
+
/** This event requests a decision; the application must still authorize the actual operation. */
|
|
24
|
+
export declare const bindLumenApprovalCard: (root: HTMLElement) => (() => void);
|
|
25
|
+
//# sourceMappingURL=ai-surfaces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-surfaces.d.ts","sourceRoot":"","sources":["../src/ai-surfaces.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,CAAA;AACxF,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,CAAA;AACrF,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAA;AAErE,MAAM,WAAW,uBAAuB;IAAG,IAAI,EAAE,MAAM,CAAA;CAAE;AACzD,MAAM,WAAW,2BAA2B;IAAG,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,GAAG,QAAQ,CAAA;CAAE;AAElG,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,uBAAuB,GAAG,SAItF,CAAA;AAED,eAAO,MAAM,+BAA+B,GAAI,OAAO,OAAO,KAAG,2BAA2B,GAAG,SAM9F,CAAA;AAED,eAAO,MAAM,yBAAyB,GAAI,OAAO,OAAO,KAAG,MAI1D,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,cAAY,KAAG,OAEjE,CAAA;AAwBD,iGAAiG;AACjG,eAAO,MAAM,wBAAwB,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAQlE,CAAA;AAKD,MAAM,WAAW,6BAA6B;IAC5C,OAAO,IAAI,IAAI,CAAA;IAEf,IAAI,IAAI,IAAI,CAAA;CACb;AAED,qFAAqF;AACrF,eAAO,MAAM,mCAAmC,GAAI,MAAM,WAAW,KAAG,6BA2GvE,CAAA;AAED,iGAAiG;AACjG,eAAO,MAAM,qBAAqB,GAAI,MAAM,WAAW,KAAG,CAAC,MAAM,IAAI,CA2CpE,CAAA"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export const readLumenPromptSubmitDetail = (value) => {
|
|
2
|
+
if (typeof value === 'object' && value !== null && 'text' in value && typeof value.text === 'string')
|
|
3
|
+
return { text: value.text };
|
|
4
|
+
return undefined;
|
|
5
|
+
};
|
|
6
|
+
export const readLumenApprovalResponseDetail = (value) => {
|
|
7
|
+
if (typeof value !== 'object' || value === null || !('requestId' in value) || typeof value.requestId !== 'string' || !('response' in value))
|
|
8
|
+
return undefined;
|
|
9
|
+
if (value.response === 'approve' || value.response === 'reject')
|
|
10
|
+
return { requestId: value.requestId, response: value.response };
|
|
11
|
+
return undefined;
|
|
12
|
+
};
|
|
13
|
+
export const normalizeLumenPromptLimit = (value) => {
|
|
14
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 1)
|
|
15
|
+
return 4000;
|
|
16
|
+
return Math.min(2147483647, Math.floor(value));
|
|
17
|
+
};
|
|
18
|
+
export const canSubmitLumenPrompt = (text, limit = 4000) => (text.trim().length > 0 && text.length <= normalizeLumenPromptLimit(limit));
|
|
19
|
+
const hasUnsafeCitationCharacter = (value) => {
|
|
20
|
+
for (let index = 0; index < value.length; index++) {
|
|
21
|
+
const code = value.charCodeAt(index);
|
|
22
|
+
if (code <= 32 || code === 127 || code === 92)
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
};
|
|
27
|
+
const webCitationHref = (value) => {
|
|
28
|
+
try {
|
|
29
|
+
const url = new URL(value);
|
|
30
|
+
if ((url.protocol === 'https:' || url.protocol === 'http:') && !url.username && !url.password)
|
|
31
|
+
return url.href;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
};
|
|
38
|
+
/** Citations accept safe web destinations and local absolute paths, never executable schemes. */
|
|
39
|
+
export const resolveLumenCitationHref = (value) => {
|
|
40
|
+
if (typeof value !== 'string')
|
|
41
|
+
return undefined;
|
|
42
|
+
if (hasUnsafeCitationCharacter(value))
|
|
43
|
+
return undefined;
|
|
44
|
+
if (value.startsWith('/') && !value.startsWith('//'))
|
|
45
|
+
return value;
|
|
46
|
+
return webCitationHref(value);
|
|
47
|
+
};
|
|
48
|
+
const isPlainSubmitKey = (event) => event.key === 'Enter' && !event.defaultPrevented && !event.isComposing &&
|
|
49
|
+
!event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
50
|
+
/** Bind an event bridge. A supplied native action keeps its normal POST behavior. */
|
|
51
|
+
export const createLumenPromptComposerController = (root) => {
|
|
52
|
+
const form = root.matches('form') ? root : root.querySelector('form');
|
|
53
|
+
const input = root.querySelector('[data-ui-prompt-input]');
|
|
54
|
+
const submit = root.querySelector('[data-ui-prompt-send]');
|
|
55
|
+
const stop = root.querySelector('[data-ui-prompt-stop]');
|
|
56
|
+
const view = root.ownerDocument.defaultView;
|
|
57
|
+
if (!form || !input || !submit || !view)
|
|
58
|
+
return { destroy: () => undefined, sync: () => undefined };
|
|
59
|
+
const sync = () => {
|
|
60
|
+
const pending = root.dataset.pending === 'true';
|
|
61
|
+
const disabled = root.dataset.disabled === 'true';
|
|
62
|
+
input.disabled = disabled;
|
|
63
|
+
const limit = input.maxLength > 0 ? input.maxLength : 4000;
|
|
64
|
+
submit.disabled = disabled || pending || !canSubmitLumenPrompt(input.value, limit);
|
|
65
|
+
if (stop) {
|
|
66
|
+
stop.hidden = !pending;
|
|
67
|
+
stop.disabled = disabled;
|
|
68
|
+
}
|
|
69
|
+
form.setAttribute('aria-busy', String(pending));
|
|
70
|
+
};
|
|
71
|
+
const send = (event) => {
|
|
72
|
+
sync();
|
|
73
|
+
if (submit.disabled) {
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const detail = { text: input.value.trim() };
|
|
78
|
+
const accepted = root.dispatchEvent(new view.CustomEvent('ui:prompt-submit', { bubbles: true, cancelable: true, detail }));
|
|
79
|
+
if (!accepted || !form.hasAttribute('action'))
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
};
|
|
82
|
+
const keydown = (event) => {
|
|
83
|
+
if (!isPlainSubmitKey(event))
|
|
84
|
+
return;
|
|
85
|
+
if (root.dataset.submitOnEnter !== 'true')
|
|
86
|
+
return;
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
sync();
|
|
89
|
+
if (!submit.disabled && form instanceof view.HTMLFormElement)
|
|
90
|
+
form.requestSubmit(submit);
|
|
91
|
+
};
|
|
92
|
+
const stopRequest = () => {
|
|
93
|
+
if (root.dataset.pending !== 'true' || root.dataset.disabled === 'true')
|
|
94
|
+
return;
|
|
95
|
+
root.dispatchEvent(new view.CustomEvent('ui:prompt-stop', { bubbles: true }));
|
|
96
|
+
};
|
|
97
|
+
input.addEventListener('input', sync);
|
|
98
|
+
let resetTimer;
|
|
99
|
+
const reset = (event) => {
|
|
100
|
+
if (resetTimer !== undefined)
|
|
101
|
+
view.clearTimeout(resetTimer);
|
|
102
|
+
resetTimer = view.setTimeout(() => {
|
|
103
|
+
resetTimer = undefined;
|
|
104
|
+
if (!event.defaultPrevented)
|
|
105
|
+
sync();
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
input.addEventListener('keydown', keydown);
|
|
109
|
+
form.addEventListener('submit', send);
|
|
110
|
+
form.addEventListener('reset', reset);
|
|
111
|
+
stop?.addEventListener('click', stopRequest);
|
|
112
|
+
const observer = new view.MutationObserver(sync);
|
|
113
|
+
observer.observe(root, { attributeFilter: ['data-pending', 'data-disabled'], attributes: true });
|
|
114
|
+
sync();
|
|
115
|
+
return {
|
|
116
|
+
destroy: () => {
|
|
117
|
+
observer.disconnect();
|
|
118
|
+
input.removeEventListener('input', sync);
|
|
119
|
+
input.removeEventListener('keydown', keydown);
|
|
120
|
+
form.removeEventListener('submit', send);
|
|
121
|
+
form.removeEventListener('reset', reset);
|
|
122
|
+
if (resetTimer !== undefined)
|
|
123
|
+
view.clearTimeout(resetTimer);
|
|
124
|
+
stop?.removeEventListener('click', stopRequest);
|
|
125
|
+
},
|
|
126
|
+
sync
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
/** This event requests a decision; the application must still authorize the actual operation. */
|
|
130
|
+
export const bindLumenApprovalCard = (root) => {
|
|
131
|
+
const view = root.ownerDocument.defaultView;
|
|
132
|
+
if (!view)
|
|
133
|
+
return () => undefined;
|
|
134
|
+
const sync = () => {
|
|
135
|
+
const disabled = root.dataset.disabled === 'true' || root.dataset.status !== 'pending' || !root.dataset.requestId;
|
|
136
|
+
for (const button of root.querySelectorAll('button[data-ui-approval-response]')) {
|
|
137
|
+
if (button.closest('[data-ui-approval-card]') === root)
|
|
138
|
+
button.disabled = disabled;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const observer = new view.MutationObserver(sync);
|
|
142
|
+
observer.observe(root, { attributeFilter: ['data-disabled', 'data-status', 'data-request-id'], attributes: true });
|
|
143
|
+
sync();
|
|
144
|
+
const click = (event) => {
|
|
145
|
+
if (!(event.target instanceof view.Element) || event.defaultPrevented || root.dataset.status !== 'pending')
|
|
146
|
+
return;
|
|
147
|
+
const button = event.target.closest('button[data-ui-approval-response]');
|
|
148
|
+
if (!button || button.disabled || button.closest('[data-ui-approval-card]') !== root)
|
|
149
|
+
return;
|
|
150
|
+
const requestId = root.dataset.requestId;
|
|
151
|
+
const response = button.dataset.uiApprovalResponse;
|
|
152
|
+
if (!requestId || (response !== 'approve' && response !== 'reject'))
|
|
153
|
+
return;
|
|
154
|
+
const detail = { requestId, response };
|
|
155
|
+
root.dispatchEvent(new view.CustomEvent('ui:approval-response', { bubbles: true, cancelable: true, detail }));
|
|
156
|
+
};
|
|
157
|
+
root.addEventListener('click', click);
|
|
158
|
+
return () => {
|
|
159
|
+
observer.disconnect();
|
|
160
|
+
root.removeEventListener('click', click);
|
|
161
|
+
};
|
|
162
|
+
};
|