@santi020k/lumen-astro 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 (103) hide show
  1. package/README.md +403 -7
  2. package/components/AmountField.astro +21 -0
  3. package/components/AnimatedNumber.astro +1 -1
  4. package/components/ApprovalCard.astro +30 -0
  5. package/components/AttachmentList.astro +15 -0
  6. package/components/AttachmentPreview.astro +51 -0
  7. package/components/Avatar.astro +2 -0
  8. package/components/BarChart.astro +95 -31
  9. package/components/BoxPlot.astro +77 -0
  10. package/components/Breadcrumb.astro +2 -1
  11. package/components/BulletChart.astro +64 -0
  12. package/components/Button.astro +1 -1
  13. package/components/Calendar.astro +54 -65
  14. package/components/CalendarHeatmap.astro +76 -0
  15. package/components/Card.astro +4 -0
  16. package/components/ChangeSummary.astro +36 -0
  17. package/components/ChartDatumActions.astro +24 -0
  18. package/components/ChartMotion.astro +11 -0
  19. package/components/Code.astro +18 -3
  20. package/components/CodeTabs.astro +12 -0
  21. package/components/ComboChart.astro +93 -11
  22. package/components/CountryFlag.astro +23 -0
  23. package/components/DataTable.astro +15 -7
  24. package/components/DatePicker.astro +24 -17
  25. package/components/DescriptionDetail.astro +18 -0
  26. package/components/DescriptionItem.astro +18 -0
  27. package/components/DescriptionTerm.astro +18 -0
  28. package/components/DeviceFrame.astro +79 -0
  29. package/components/DialogBody.astro +20 -0
  30. package/components/DialogClose.astro +21 -0
  31. package/components/DialogFooter.astro +20 -0
  32. package/components/DialogHeader.astro +20 -0
  33. package/components/DialogTitle.astro +21 -0
  34. package/components/DumbbellChart.astro +8 -0
  35. package/components/FileUpload.astro +3 -1
  36. package/components/FilterBar.astro +25 -0
  37. package/components/FunnelChart.astro +50 -0
  38. package/components/Grid.astro +2 -2
  39. package/components/Heatmap.astro +61 -12
  40. package/components/Histogram.astro +30 -0
  41. package/components/Icon.astro +7 -3
  42. package/components/ImageComparison.astro +74 -0
  43. package/components/Input.astro +2 -2
  44. package/components/LineChart.astro +122 -66
  45. package/components/LollipopChart.astro +8 -0
  46. package/components/MediaFilmstrip.astro +19 -0
  47. package/components/MediaThumbnail.astro +29 -0
  48. package/components/MediaViewport.astro +38 -0
  49. package/components/Mentions.astro +2 -1
  50. package/components/MessageScroller.astro +6 -0
  51. package/components/MotionGroup.astro +19 -0
  52. package/components/NativeSelect.astro +2 -2
  53. package/components/Pagination.astro +2 -1
  54. package/components/PasswordField.astro +1 -1
  55. package/components/PhoneInput.astro +60 -9
  56. package/components/PhoneNumber.astro +19 -0
  57. package/components/PieChart.astro +26 -3
  58. package/components/PromptComposer.astro +36 -0
  59. package/components/RangeChart.astro +38 -8
  60. package/components/ScatterChart.astro +62 -14
  61. package/components/Segmented.astro +5 -5
  62. package/components/Select.astro +8 -8
  63. package/components/SourceCitation.astro +18 -0
  64. package/components/Sparkline.astro +9 -10
  65. package/components/Stack.astro +2 -2
  66. package/components/StreamMessage.astro +26 -0
  67. package/components/Tabs.astro +3 -0
  68. package/components/ToolActivity.astro +17 -0
  69. package/components/TreeSelect.astro +1 -0
  70. package/components/VirtualList.astro +5 -2
  71. package/components/VisualEffect.astro +21 -0
  72. package/components/WaterfallChart.astro +27 -0
  73. package/components/WorldMap.astro +213 -0
  74. package/forms.ts +17 -4
  75. package/index.ts +37 -0
  76. package/internal/ChartInspection.astro +27 -0
  77. package/internal/ComparisonChart.astro +69 -0
  78. package/internal/IntervalChart.astro +66 -0
  79. package/package.json +6 -4
  80. package/runtime/UIPrimitives.astro +670 -647
  81. package/runtime/chart-actions.ts +19 -0
  82. package/runtime/controllers/amount-field.ts +12 -0
  83. package/runtime/controllers/attachments.ts +13 -0
  84. package/runtime/controllers/chart-activation.ts +33 -0
  85. package/runtime/controllers/charts.ts +33 -0
  86. package/runtime/controllers/combobox.ts +39 -0
  87. package/runtime/controllers/comparison-reset.ts +20 -0
  88. package/runtime/controllers/data-table.ts +142 -0
  89. package/runtime/controllers/dialogs.ts +32 -32
  90. package/runtime/controllers/document-navigation.ts +54 -22
  91. package/runtime/controllers/file-upload.ts +89 -0
  92. package/runtime/controllers/image-comparison.ts +53 -0
  93. package/runtime/controllers/media-selection.ts +20 -0
  94. package/runtime/controllers/media-viewport.ts +54 -0
  95. package/runtime/controllers/media-workspace.ts +8 -0
  96. package/runtime/controllers/message-scroller.ts +13 -0
  97. package/runtime/controllers/motion.ts +15 -16
  98. package/runtime/controllers/optional-media.ts +29 -0
  99. package/runtime/controllers/phone-input.ts +128 -39
  100. package/runtime/controllers/visual-interactions.ts +75 -0
  101. package/runtime/controllers/world-map.ts +154 -0
  102. package/styles/critical.css +634 -61
  103. package/styles/lumen.css +101 -0
@@ -2,30 +2,37 @@
2
2
  import type { HTMLAttributes } from 'astro/types'
3
3
 
4
4
  import {
5
- alignLumenChartSeries,
6
- createLumenLineGeometry,
5
+ createLumenChartDatumActivation,
6
+ createLumenLineChartModel,
7
7
  formatLumenChartSummary,
8
- getLumenChartAxisPadding,
9
- getLumenChartCategories,
10
- getLumenChartDomain,
11
- getLumenChartTicks,
8
+ getLumenChartCategoryLabel,
9
+ getLumenChartMotionKey,
12
10
  getLumenChartToneClassName,
13
11
  hasLumenChartData,
14
12
  type LumenChartLabels,
15
13
  type LumenChartSeries,
14
+ type LumenLineChartOptions,
16
15
  resolveLumenChartLabels,
17
16
  resolveLumenChartTone,
18
17
  scaleLumenChartValue
19
18
  } from '@santi020k/lumen-core'
20
19
 
20
+ import ChartInspection from '../internal/ChartInspection.astro'
21
+ import { createChartDatumAction } from '../runtime/chart-actions'
22
+
23
+ import Button from './Button.astro'
21
24
  import Chart from './Chart.astro'
25
+ import ChartDatumActions from './ChartDatumActions.astro'
22
26
 
23
- interface Props extends HTMLAttributes<'figure'> {
27
+ interface Props extends HTMLAttributes<'figure'>, LumenLineChartOptions {
28
+ interactive?: boolean
29
+ syncGroup?: string
24
30
  area?: boolean
25
31
  caption?: string
26
32
  class?: string | null
27
33
  className?: string
28
34
  description?: string
35
+ drilldown?: boolean
29
36
  emptyLabel?: string
30
37
  formatCategory?: (category: number | string) => string
31
38
  formatValue?: (value: number) => string
@@ -43,12 +50,21 @@ interface Props extends HTMLAttributes<'figure'> {
43
50
 
44
51
  const {
45
52
  area = false,
53
+ annotations = [],
54
+ domain: requestedDomain,
55
+ height: requestedHeight,
56
+ width: requestedWidth,
57
+ xDomain,
58
+ xScale = 'categorical',
59
+ interactive = false,
60
+ syncGroup,
46
61
  caption,
47
62
  class: classProp = '',
48
63
  className = '',
49
64
  description,
65
+ drilldown = false,
50
66
  emptyLabel,
51
- formatCategory = String,
67
+ formatCategory,
52
68
  formatValue = String,
53
69
  glass = false,
54
70
  heading,
@@ -63,32 +79,22 @@ const {
63
79
  } = Astro.props
64
80
 
65
81
  const resolvedLabels = resolveLumenChartLabels(labels)
66
- const summary = summaryProp ?? formatLumenChartSummary(series, formatValue, resolvedLabels)
67
- const width = 640
68
- const height = 320
69
- const padding = 44
70
- const categories = getLumenChartCategories(series)
71
- const hasData = hasLumenChartData(series)
72
- const alignedSeries = series.map(item => alignLumenChartSeries(item, categories))
73
-
74
- const domain = getLumenChartDomain([
75
- ...alignedSeries.flatMap(item => item.data.map(datum => datum.y)),
76
- referenceValue ?? null
77
- ], false)
78
-
79
- const ticks = getLumenChartTicks(domain)
80
- const paddingLeft = getLumenChartAxisPadding(ticks.map(tick => formatValue(tick)))
81
82
 
82
- const geometries = alignedSeries.map(item => createLumenLineGeometry(item.data, {
83
- domain,
84
- height,
85
- includeZero: false,
86
- padding,
87
- paddingLeft,
88
- width
89
- }))
83
+ const model = createLumenLineChartModel(series, {
84
+ annotations,
85
+ xScale,
86
+ ...(requestedDomain ? { domain: requestedDomain } : {}),
87
+ ...(requestedHeight === undefined ? {} : { height: requestedHeight }),
88
+ ...(requestedWidth === undefined ? {} : { width: requestedWidth }),
89
+ ...(xDomain ? { xDomain } : {}),
90
+ ...(referenceValue === undefined ? {} : { referenceValue }),
91
+ ...(formatCategory ? { formatCategory } : {}),
92
+ formatValue
93
+ })
90
94
 
91
- const labelStep = Math.max(1, Math.ceil(categories.length / 8))
95
+ const { width, height, padding, paddingLeft, categories, categoryTicks, domain, geometries, ticks, series: alignedSeries } = model
96
+ const hasData = hasLumenChartData(alignedSeries)
97
+ const summary = summaryProp ?? formatLumenChartSummary(alignedSeries, formatValue, resolvedLabels)
92
98
  let markerStep = 1
93
99
 
94
100
  if (markers === false || markers === 'none') markerStep = Number.POSITIVE_INFINITY
@@ -96,14 +102,29 @@ else if (typeof markers === 'number') markerStep = Math.max(1, Math.round(marker
96
102
  else if (markers === 'auto') markerStep = Math.max(1, Math.ceil(categories.length / 24))
97
103
 
98
104
  const valueFor = (item: LumenChartSeries, category: number | string) => item.data.find(datum => datum.x === category)
99
-
100
- const categoryLabelFor = (category: number | string) => series.flatMap(item => item.data)
101
- .find(datum => datum.x === category)?.xLabel ?? formatCategory(category)
105
+ const categoryLabelFor = (category: number | string) => getLumenChartCategoryLabel(alignedSeries, category, formatCategory, 'detail')
102
106
 
103
107
  const referenceY =
104
108
  referenceValue === undefined ?
105
109
  undefined :
106
110
  scaleLumenChartValue(referenceValue, domain, height - padding, padding)
111
+
112
+ const pointActions = drilldown ?
113
+ geometries.map((geometry, index) => {
114
+ const item = alignedSeries[index]
115
+
116
+ return geometry.points.map(point => item ?
117
+ createChartDatumAction(
118
+ createLumenChartDatumActivation(item.id, point),
119
+ `${categoryLabelFor(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}`,
120
+ resolvedLabels
121
+ ) :
122
+ null)
123
+ }) :
124
+ []
125
+
126
+ const datumActions = pointActions.flat()
127
+
107
128
  ---
108
129
 
109
130
  <Chart
@@ -113,22 +134,31 @@ const referenceY =
113
134
  {glass}
114
135
  {heading}
115
136
  {summary}
137
+ data-ui-chart-interactive={interactive || undefined}
138
+ data-ui-chart-sync={syncGroup}
116
139
  {...rest}
140
+ data-ui-chart-activation={drilldown || undefined}
117
141
  >
118
142
  {
119
143
  showLegend && hasData && (
120
144
  <ul class="ui-chart__legend" aria-label={resolvedLabels.chartLegend}>
121
145
  {series.map((item, index) => (
122
146
  <li class={getLumenChartToneClassName(item.tone, index)}>
123
- <span aria-hidden="true" />
124
- {item.label}
147
+ {interactive ? <Button aria-pressed="true" data-ui-chart-toggle={item.id} disabled size="sm" variant="ghost">{item.label}</Button> : <><span aria-hidden="true" />{item.label}</>}
125
148
  </li>
126
149
  ))}
127
150
  </ul>
128
151
  )
129
152
  }
130
153
  {!hasData && <p class="ui-chart__empty" role="status">{emptyLabel ?? resolvedLabels.empty}</p>}
131
- <div class="ui-chart__plot" hidden={!hasData}>
154
+ <div
155
+ aria-label={rest['aria-label'] ?? heading ?? resolvedLabels.chartData}
156
+ class="ui-chart__plot"
157
+ data-ui-chart-interaction-plot={interactive || undefined}
158
+ role="region"
159
+ tabindex="0"
160
+ hidden={!hasData}
161
+ >
132
162
  <svg
133
163
  aria-hidden="true"
134
164
  preserveAspectRatio="xMidYMid meet"
@@ -154,26 +184,21 @@ const referenceY =
154
184
  </g>
155
185
  <g class="ui-chart__axis-labels">
156
186
  {
157
- categories.map((category, index) => {
158
- if (
159
- index % labelStep !== 0 &&
160
- index !== categories.length - 1
161
- )
162
- return null
163
-
164
- const denominator = Math.max(1, categories.length - 1)
165
-
166
- const x =
167
- paddingLeft + (index / denominator) * (width - paddingLeft - padding)
168
-
169
- return (
170
- <text text-anchor="middle" x={x} y={height - 14}>
171
- {categoryLabelFor(category)}
172
- </text>
173
- )
174
- })
187
+ categoryTicks.map(tick => (
188
+ <text text-anchor={tick.textAnchor} x={tick.position} y={height - 14}>
189
+ {tick.label}
190
+ </text>
191
+ ))
175
192
  }
176
193
  </g>
194
+ <svg
195
+ x={paddingLeft}
196
+ y={padding}
197
+ width={width - padding - paddingLeft}
198
+ height={height - 2 * padding}
199
+ viewBox={`${paddingLeft} ${padding} ${width - padding - paddingLeft} ${height - 2 * padding}`}
200
+ overflow="hidden"
201
+ >
177
202
  {
178
203
  referenceY !== undefined && (
179
204
  <line
@@ -187,43 +212,73 @@ const referenceY =
187
212
  }
188
213
  {
189
214
  geometries.map((geometry, index) => {
190
- const item = series[index]
215
+ const item = alignedSeries[index]
191
216
 
192
217
  if (!item) return null
193
218
 
194
219
  const tone = resolveLumenChartTone(item.tone, index)
195
220
 
196
221
  return (
197
- <g class:list={['ui-line-chart__series', getLumenChartToneClassName(tone)]}>
198
- {area &&
199
- geometry.areaPaths.map(path => (
200
- <path class="ui-line-chart__area" d={path} />
201
- ))}
202
- <path class="ui-line-chart__line" d={geometry.path} />
222
+ <g
223
+ data-ui-chart-series={item.id}
224
+ class:list={['ui-line-chart__series', getLumenChartToneClassName(tone)]}
225
+ >
226
+ {area && geometry.areaPaths.length > 0 && (
227
+ <path
228
+ data-ui-chart-motion-key={JSON.stringify(['area', item.id])}
229
+ class="ui-line-chart__area"
230
+ d={geometry.areaPaths.join(' ')}
231
+ />
232
+ )}
233
+ <path
234
+ data-ui-chart-motion-key={JSON.stringify(['path', item.id])}
235
+ class="ui-line-chart__line"
236
+ d={geometry.path}
237
+ />
203
238
  {Number.isFinite(markerStep) &&
204
239
  geometry.points.map((point, pointIndex) => pointIndex % markerStep === 0 && (
205
240
  <circle
241
+ data-ui-chart-datum={pointActions[index]?.[pointIndex]?.serialized}
242
+ data-ui-chart-motion-key={getLumenChartMotionKey(item.id, point)}
206
243
  class="ui-line-chart__point"
207
244
  cx={point.xCoordinate}
208
245
  cy={point.yCoordinate}
209
246
  r="3"
210
247
  >
211
248
  <title>
212
- {`${point.xLabel ?? formatCategory(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}`}
249
+ {`${categoryLabelFor(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}`}
213
250
  </title>
214
251
  </circle>
215
252
  ))}
253
+ {drilldown && geometry.points.map((point, pointIndex) => (
254
+ <circle
255
+ class="ui-chart__datum-hit"
256
+ cx={point.xCoordinate}
257
+ cy={point.yCoordinate}
258
+ r="10"
259
+ data-ui-chart-datum={pointActions[index]?.[pointIndex]?.serialized}
260
+ />
261
+ ))}
216
262
  </g>
217
263
  )
218
264
  })
219
265
  }
266
+ </svg>
267
+ {model.annotationMarks.map(mark => (
268
+ <g class:list={['ui-chart__annotation', getLumenChartToneClassName(mark.tone)]}>
269
+ {mark.axis === 'x' ? <><line x1={mark.coordinate} x2={mark.coordinate} y1={padding} y2={height - padding} /><text x={mark.coordinate + 4} y={padding - 8}>{mark.label}</text></> : <><line x1={paddingLeft} x2={width - padding} y1={mark.coordinate} y2={mark.coordinate} /><text text-anchor="end" x={width - padding} y={mark.coordinate - 8}>{mark.label}</text></>}
270
+ </g>
271
+ ))}
272
+ {interactive && <line class="ui-chart__crosshair" data-ui-chart-crosshair style="display: none" y1={padding} y2={height - padding} />}
220
273
  </svg>
221
274
  </div>
275
+ {model.annotationMarks.length > 0 && <ul class="ui-sr-only">{model.annotationMarks.map(mark => <li>{mark.label}: {mark.axis === 'x' ? categoryLabelFor(mark.value) : formatValue(Number(mark.value))}</li>)}</ul>}
276
+ {interactive && hasData && <ChartInspection {model} {formatCategory} {formatValue} labels={resolvedLabels} />}
222
277
  {
223
278
  showTable && hasData && (
224
279
  <details class="ui-chart__data">
225
280
  <summary>{resolvedLabels.viewData}</summary>
226
- <div>
281
+ <div role="group" tabindex="0" aria-label={resolvedLabels.chartData}>
227
282
  <table>
228
283
  <thead>
229
284
  <tr>
@@ -235,7 +290,7 @@ const referenceY =
235
290
  {categories.map(category => (
236
291
  <tr>
237
292
  <th scope="row">{categoryLabelFor(category)}</th>
238
- {series.map(item => {
293
+ {alignedSeries.map(item => {
239
294
  const datum = valueFor(item, category)
240
295
 
241
296
  return (
@@ -255,4 +310,5 @@ const referenceY =
255
310
  </details>
256
311
  )
257
312
  }
313
+ {drilldown && <ChartDatumActions actions={datumActions} label={resolvedLabels.exploreData} />}
258
314
  </Chart>
@@ -0,0 +1,8 @@
1
+ ---
2
+ import type { ComponentProps } from 'astro/types'
3
+
4
+ import ComparisonChart from '../internal/ComparisonChart.astro'
5
+
6
+ type Props = Omit<ComponentProps<typeof ComparisonChart>, 'paired'>
7
+ ---
8
+ <ComparisonChart {...Astro.props} class:list={['ui-lollipop-chart', Astro.props.class, Astro.props.className]} paired={false} />
@@ -0,0 +1,19 @@
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
+ interface Props extends HTMLAttributes<'section'> {
7
+ className?: string
8
+ label: string
9
+ selectionLabel: string
10
+ }
11
+
12
+ const { class: classProp, className, label, selectionLabel, ...rest } = Astro.props
13
+ const { classList, passthrough } = resolveLumenAstroProps(rest, ['ui-media-filmstrip'], classProp, className)
14
+ ---
15
+
16
+ <section {...passthrough} class:list={classList} aria-label={label}>
17
+ <div class="ui-media-filmstrip__heading"><span>{label}</span><span role="status" data-ui-media-filmstrip-selection>{selectionLabel}</span></div>
18
+ <ol class="ui-media-filmstrip__items"><slot /></ol>
19
+ </section>
@@ -0,0 +1,29 @@
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
+ import Button from './Button.astro'
7
+
8
+ interface Props extends HTMLAttributes<'button'> {
9
+ className?: string
10
+ label: string
11
+ mediaId: string
12
+ order?: number
13
+ selected?: boolean
14
+ state?: 'ready' | 'loading' | 'error'
15
+ stateLabel?: string
16
+ }
17
+
18
+ const { class: classProp, className, disabled, label, mediaId, order, selected = false, state = 'ready', stateLabel, ...rest } = Astro.props
19
+ const position = order !== undefined && Number.isSafeInteger(order) && order > 0 ? order : undefined
20
+ const status = stateLabel ?? (state === 'loading' ? 'Loading' : 'Unavailable')
21
+ const { classList, passthrough } = resolveLumenAstroProps(rest, ['ui-media-thumbnail'], classProp, className)
22
+ ---
23
+
24
+ <Button {...passthrough} class:list={classList} variant="outline" data-ui-media-thumbnail data-media-id={mediaId} aria-busy={state === 'loading' || undefined} aria-pressed={selected} disabled={Boolean(disabled) || state !== 'ready'} data-state={state}>
25
+ <span class="ui-media-thumbnail__media" aria-hidden="true"><slot /></span>
26
+ <span class="ui-media-thumbnail__label">{label}</span>
27
+ {position !== undefined && <span class="ui-media-thumbnail__order">{position}</span>}
28
+ {state !== 'ready' && <span class="ui-media-thumbnail__state">{status}</span>}
29
+ </Button>
@@ -0,0 +1,38 @@
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
4
+ import { formatLumenMediaZoom, lumenMediaViewportActions, type LumenMediaViewportLabels, lumenMediaViewportLabels, type LumenMediaViewportValue, normalizeLumenMediaViewport, resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
+ import Button from './Button.astro'
7
+
8
+ interface Props extends HTMLAttributes<'figure'> {
9
+ className?: string
10
+ disabled?: boolean
11
+ label: string
12
+ labels?: Partial<LumenMediaViewportLabels>
13
+ locale?: string
14
+ maxZoom?: number
15
+ ratio?: number
16
+ value?: Partial<LumenMediaViewportValue>
17
+ }
18
+
19
+ const { class: classProp, className, disabled = false, label, labels, locale, maxZoom = 4, ratio = 16 / 9, value, ...rest } = Astro.props
20
+ const current = normalizeLumenMediaViewport(value, maxZoom)
21
+ const copy = { ...lumenMediaViewportLabels, ...labels }
22
+ const safeRatio = Number.isFinite(ratio) && ratio >= 0.1 && ratio <= 10 ? ratio : 16 / 9
23
+ const statusId = `media-viewport-${crypto.randomUUID()}`
24
+ const { classList, passthrough } = resolveLumenAstroProps(rest, ['ui-media-viewport'], classProp, className)
25
+ ---
26
+
27
+ <figure class:list={classList} {...passthrough} data-ui-media-viewport data-disabled={String(disabled)} data-max-zoom={maxZoom} data-zoom={current.zoom} data-pan-x={current.x} data-pan-y={current.y} data-locale={locale}>
28
+ <div class="ui-media-viewport__stage" data-ui-media-viewport-stage role="group" aria-label={label} aria-disabled={disabled} aria-describedby={statusId} tabindex="0" style={`aspect-ratio: ${safeRatio}`}>
29
+ <div class="ui-media-viewport__content" data-ui-media-viewport-content style={`transform: translate(${current.x * (current.zoom - 1) * 50}%, ${current.y * (current.zoom - 1) * 50}%) scale(${current.zoom})`}><slot /></div>
30
+ </div>
31
+ <figcaption>
32
+ <span>{label}</span>
33
+ <span id={statusId} role="status" aria-live="polite" data-ui-media-viewport-status>{formatLumenMediaZoom(current.zoom, locale)}</span>
34
+ <div class="ui-media-viewport__actions" role="group" aria-label={label}>
35
+ {lumenMediaViewportActions.map(action => <Button variant="outline" data-ui-media-viewport-action={action} disabled>{copy[action]}</Button>)}
36
+ </div>
37
+ </figcaption>
38
+ </figure>
@@ -49,7 +49,7 @@ const { classList, passthrough } = resolveLumenAstroProps(rest, [
49
49
  />
50
50
  <ul class="ui-mentions__list" data-ui-mentions-list hidden id={listId} role="listbox">
51
51
  {normalized.map((option, index) => (
52
- <li>
52
+ <li role="presentation">
53
53
  <button
54
54
  aria-selected="false"
55
55
  class="ui-mentions__option"
@@ -57,6 +57,7 @@ const { classList, passthrough } = resolveLumenAstroProps(rest, [
57
57
  data-value={option.value}
58
58
  id={`${listId}-option-${index}`}
59
59
  role="option"
60
+ tabindex={-1}
60
61
  type="button"
61
62
  >
62
63
  {option.label}
@@ -7,12 +7,16 @@ interface Props extends HTMLAttributes<'div'> {
7
7
  class?: string | null
8
8
  className?: string
9
9
  glass?: boolean | 'subtle' | 'strong'
10
+ autoScroll?: boolean
11
+ scrollThreshold?: number
10
12
  }
11
13
 
12
14
  const {
13
15
  class: classProp = '',
14
16
  className = '',
15
17
  glass = false,
18
+ autoScroll = false,
19
+ scrollThreshold = 32,
16
20
  ...rest
17
21
  } = Astro.props
18
22
 
@@ -25,6 +29,8 @@ const { classList, passthrough } = resolveLumenAstroProps(rest, [
25
29
  ---
26
30
 
27
31
  <div
32
+ data-ui-message-scroller={autoScroll ? '' : undefined}
33
+ scroll-threshold={scrollThreshold}
28
34
  class:list={classList}
29
35
  {...passthrough}
30
36
  >
@@ -0,0 +1,19 @@
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
4
+ import { type LumenWorkflowDuration, resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
+ interface Props extends HTMLAttributes<'div'> {
7
+ class?: string | null
8
+ className?: string
9
+ duration?: LumenWorkflowDuration
10
+ enterExit?: boolean
11
+ }
12
+
13
+ const { class: classProp, className, duration = 'standard', enterExit = true, ...rest } = Astro.props
14
+ const { classList, passthrough } = resolveLumenAstroProps(rest, ['ui-motion-group'], classProp, className)
15
+ ---
16
+
17
+ <div class:list={classList} data-ui-motion-group data-ui-motion-duration={duration} data-ui-motion-enter-exit={String(enterExit)} {...passthrough}>
18
+ <slot />
19
+ </div>
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from 'astro/types'
3
3
 
4
- import { resolveLumenAstroProps } from '@santi020k/lumen-core'
4
+ import { type LumenControlVisualSize, resolveLumenAstroProps } from '@santi020k/lumen-core'
5
5
 
6
6
  interface Option {
7
7
  disabled?: boolean
@@ -17,7 +17,7 @@ interface Props extends Omit<HTMLAttributes<'select'>, 'size'> {
17
17
  size?: HTMLAttributes<'select'>['size']
18
18
  options?: SelectOption[]
19
19
  placeholder?: string
20
- visualSize?: 'default' | 'lg' | 'sm'
20
+ visualSize?: LumenControlVisualSize
21
21
  }
22
22
 
23
23
  const {
@@ -9,6 +9,7 @@ interface Props extends HTMLAttributes<'nav'> {
9
9
  }
10
10
 
11
11
  const {
12
+ 'aria-label': ariaLabel = 'Pagination',
12
13
  class: classProp = '',
13
14
  className = '',
14
15
  ...rest
@@ -20,7 +21,7 @@ const { classList, passthrough } = resolveLumenAstroProps(rest, [
20
21
  ---
21
22
 
22
23
  <nav
23
- aria-label="Pagination"
24
+ aria-label={ariaLabel}
24
25
  class:list={classList}
25
26
  {...passthrough}
26
27
  >
@@ -65,7 +65,7 @@ const { classList } = resolveLumenAstroProps({}, ['ui-password-field'], classPro
65
65
  type="button"
66
66
  variant="ghost"
67
67
  >
68
- <span aria-hidden="true" data-ui-password-toggle-label>Show</span>
68
+ <span aria-hidden="true" data-ui-password-toggle-label>{showLabel}</span>
69
69
  </Button>
70
70
  </div>
71
71
  {hint && <p class="ui-field__hint" data-ui-field-hint id={hintId}>{hint}</p>}