@santi020k/lumen-astro 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +403 -7
- package/components/AmountField.astro +21 -0
- package/components/AnimatedNumber.astro +1 -1
- package/components/ApprovalCard.astro +30 -0
- package/components/AttachmentList.astro +15 -0
- package/components/AttachmentPreview.astro +51 -0
- package/components/Avatar.astro +2 -0
- package/components/BarChart.astro +95 -31
- package/components/BoxPlot.astro +77 -0
- package/components/Breadcrumb.astro +2 -1
- package/components/BulletChart.astro +64 -0
- package/components/Button.astro +1 -1
- package/components/Calendar.astro +54 -65
- package/components/CalendarHeatmap.astro +76 -0
- package/components/Card.astro +4 -0
- package/components/ChangeSummary.astro +36 -0
- package/components/ChartDatumActions.astro +24 -0
- package/components/ChartMotion.astro +11 -0
- package/components/Code.astro +18 -3
- package/components/CodeTabs.astro +12 -0
- package/components/ComboChart.astro +93 -11
- package/components/CountryFlag.astro +23 -0
- package/components/DataTable.astro +15 -7
- package/components/DatePicker.astro +24 -17
- package/components/DescriptionDetail.astro +18 -0
- package/components/DescriptionItem.astro +18 -0
- package/components/DescriptionTerm.astro +18 -0
- package/components/DeviceFrame.astro +79 -0
- package/components/DialogBody.astro +20 -0
- package/components/DialogClose.astro +21 -0
- package/components/DialogFooter.astro +20 -0
- package/components/DialogHeader.astro +20 -0
- package/components/DialogTitle.astro +21 -0
- package/components/DumbbellChart.astro +8 -0
- package/components/FileUpload.astro +3 -1
- package/components/FilterBar.astro +25 -0
- package/components/FunnelChart.astro +50 -0
- package/components/Grid.astro +2 -2
- package/components/Heatmap.astro +61 -12
- package/components/Histogram.astro +30 -0
- package/components/Icon.astro +7 -3
- package/components/ImageComparison.astro +74 -0
- package/components/Input.astro +2 -2
- package/components/LineChart.astro +127 -67
- package/components/LollipopChart.astro +8 -0
- package/components/MediaFilmstrip.astro +19 -0
- package/components/MediaThumbnail.astro +29 -0
- package/components/MediaViewport.astro +38 -0
- package/components/Mentions.astro +2 -1
- package/components/MessageScroller.astro +6 -0
- package/components/MotionGroup.astro +19 -0
- package/components/NativeSelect.astro +2 -2
- package/components/Pagination.astro +2 -1
- package/components/PasswordField.astro +1 -1
- package/components/PhoneInput.astro +60 -9
- package/components/PhoneNumber.astro +19 -0
- package/components/PieChart.astro +26 -3
- package/components/PromptComposer.astro +36 -0
- package/components/RangeChart.astro +38 -8
- package/components/ScatterChart.astro +62 -14
- package/components/Segmented.astro +5 -5
- package/components/Select.astro +8 -8
- package/components/SourceCitation.astro +18 -0
- package/components/Sparkline.astro +9 -10
- package/components/Stack.astro +2 -2
- package/components/StreamMessage.astro +26 -0
- package/components/Tabs.astro +3 -0
- package/components/ToolActivity.astro +17 -0
- package/components/TreeSelect.astro +1 -0
- package/components/VirtualList.astro +5 -2
- package/components/VisualEffect.astro +21 -0
- package/components/WaterfallChart.astro +27 -0
- package/components/WorldMap.astro +213 -0
- package/forms.ts +17 -4
- package/index.ts +37 -0
- package/internal/ChartInspection.astro +27 -0
- package/internal/ComparisonChart.astro +69 -0
- package/internal/IntervalChart.astro +66 -0
- package/package.json +6 -4
- package/runtime/UIPrimitives.astro +670 -647
- package/runtime/chart-actions.ts +19 -0
- package/runtime/controllers/amount-field.ts +12 -0
- package/runtime/controllers/attachments.ts +13 -0
- package/runtime/controllers/chart-activation.ts +33 -0
- package/runtime/controllers/charts.ts +33 -0
- package/runtime/controllers/combobox.ts +39 -0
- package/runtime/controllers/comparison-reset.ts +20 -0
- package/runtime/controllers/data-table.ts +142 -0
- package/runtime/controllers/dialogs.ts +32 -32
- package/runtime/controllers/document-navigation.ts +54 -22
- package/runtime/controllers/file-upload.ts +89 -0
- package/runtime/controllers/image-comparison.ts +53 -0
- package/runtime/controllers/media-selection.ts +20 -0
- package/runtime/controllers/media-viewport.ts +54 -0
- package/runtime/controllers/media-workspace.ts +8 -0
- package/runtime/controllers/message-scroller.ts +13 -0
- package/runtime/controllers/motion.ts +15 -16
- package/runtime/controllers/optional-media.ts +29 -0
- package/runtime/controllers/phone-input.ts +128 -39
- package/runtime/controllers/visual-interactions.ts +75 -0
- package/runtime/controllers/world-map.ts +154 -0
- package/styles/critical.css +634 -61
- package/styles/lumen.css +101 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { createLumenCalendarHeatmapGeometry, type LumenCalendarHeatmapDatum, type LumenCalendarHeatmapOptions, type LumenChartLabels, resolveLumenChartLabels } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
import Chart from './Chart.astro'
|
|
7
|
+
|
|
8
|
+
interface Props extends HTMLAttributes<'figure'>, LumenCalendarHeatmapOptions {
|
|
9
|
+
data: readonly LumenCalendarHeatmapDatum[]
|
|
10
|
+
className?: string
|
|
11
|
+
heading?: string
|
|
12
|
+
description?: string
|
|
13
|
+
caption?: string
|
|
14
|
+
presentation?: 'bare' | 'default'
|
|
15
|
+
summary?: string
|
|
16
|
+
formatValue?: (value: number) => string
|
|
17
|
+
formatDate?: (date: string) => string
|
|
18
|
+
weekdayLabels?: readonly string[]
|
|
19
|
+
labels?: Partial<LumenChartLabels>
|
|
20
|
+
showTable?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { data, startDate, endDate, weekStartsOn = 0, domain, formatValue = String, formatDate = String,
|
|
24
|
+
weekdayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], labels, showTable = true, summary,
|
|
25
|
+
class: classProp, className, ...props } = Astro.props
|
|
26
|
+
|
|
27
|
+
const text = resolveLumenChartLabels(labels)
|
|
28
|
+
const model = createLumenCalendarHeatmapGeometry(data, { startDate, endDate, weekStartsOn, domain })
|
|
29
|
+
const observed = model.cells.filter(cell => cell.value !== null).length
|
|
30
|
+
const format = (value: number | null) => value === null ? text.notAvailable : formatValue(value)
|
|
31
|
+
const width = Math.max(240, model.weekCount * 18 + 52)
|
|
32
|
+
const factual = model.valid ? `${formatDate(startDate)} – ${formatDate(endDate)}. ${text.value}: ${observed}/${model.cells.length}. ${text.notAvailable}: ${model.cells.length - observed}.` : text.invalidData
|
|
33
|
+
---
|
|
34
|
+
<Chart {...props} class:list={['ui-calendar-heatmap', classProp, className]} summary={summary ?? factual}>
|
|
35
|
+
{model.valid ?
|
|
36
|
+
(
|
|
37
|
+
<>
|
|
38
|
+
<p class="ui-calendar-heatmap__range">{formatDate(startDate)} – {formatDate(endDate)}</p>
|
|
39
|
+
<div class="ui-calendar-heatmap__plot" role="group" tabindex="0" aria-label={text.chartData}>
|
|
40
|
+
<svg viewBox={`0 0 ${width} 152`} width={width} height="152" aria-hidden="true">
|
|
41
|
+
{Array.from({ length: 7 }, (_, day) => (
|
|
42
|
+
<text
|
|
43
|
+
x="0"
|
|
44
|
+
y={day * 18 + 15}
|
|
45
|
+
class="ui-calendar-heatmap__weekday"
|
|
46
|
+
>{weekdayLabels[(day + weekStartsOn) % 7] ?? ''}</text>
|
|
47
|
+
))}
|
|
48
|
+
{model.cells.map(cell => (
|
|
49
|
+
<g><rect class="ui-calendar-heatmap__cell" data-missing={cell.value === null ? '' : undefined} x={cell.week * 18 + 46} y={cell.day * 18 + 2} width="15" height="15" rx="3" style={{ opacity: cell.ratio === null ? 1 : 0.15 + cell.ratio * 0.85 }} /><title>{formatDate(cell.date)}: {format(cell.value)}</title>{cell.value === null && (
|
|
50
|
+
<text
|
|
51
|
+
x={cell.week * 18 + 53.5}
|
|
52
|
+
y={cell.day * 18 + 14}
|
|
53
|
+
text-anchor="middle"
|
|
54
|
+
class="ui-calendar-heatmap__missing"
|
|
55
|
+
>×</text>
|
|
56
|
+
)}</g>
|
|
57
|
+
))}
|
|
58
|
+
{model.cells.filter(cell => cell.day === 0 && cell.week % 4 === 0).map(cell => (
|
|
59
|
+
<text
|
|
60
|
+
x={cell.week * 18 + 46}
|
|
61
|
+
y="145"
|
|
62
|
+
class="ui-calendar-heatmap__weekday"
|
|
63
|
+
>{cell.date.slice(5)}</text>
|
|
64
|
+
))}
|
|
65
|
+
</svg>
|
|
66
|
+
</div>
|
|
67
|
+
<div class="ui-calendar-heatmap__legend"><span>{formatValue(model.domain.min)}</span><span
|
|
68
|
+
class="ui-calendar-heatmap__scale"
|
|
69
|
+
aria-hidden="true"
|
|
70
|
+
/><span>{formatValue(model.domain.max)}</span><span>× {text.notAvailable}</span></div>
|
|
71
|
+
{!showTable && <ul class="ui-sr-only">{model.cells.map(cell => <li>{formatDate(cell.date)}: {format(cell.value)}</li>)}</ul>}
|
|
72
|
+
{showTable && <details class="ui-chart__data"><summary>{text.viewData}</summary><div role="group" tabindex="0" aria-label={text.chartData}><table><thead><tr><th scope="col">{text.category}</th><th scope="col">{text.value}</th></tr></thead><tbody>{model.cells.map(cell => <tr><th scope="row">{formatDate(cell.date)}</th><td>{format(cell.value)}</td></tr>)}</tbody></table></div></details>}
|
|
73
|
+
</>
|
|
74
|
+
) :
|
|
75
|
+
<p class="ui-chart__empty" role="status">{text.invalidData}</p>}
|
|
76
|
+
</Chart>
|
package/components/Card.astro
CHANGED
|
@@ -7,6 +7,7 @@ interface Props extends HTMLAttributes<'div'> {
|
|
|
7
7
|
as?: 'article' | 'div' | 'section'
|
|
8
8
|
class?: string | null
|
|
9
9
|
className?: string
|
|
10
|
+
density?: 'comfortable' | 'compact' | 'spacious'
|
|
10
11
|
glass?: boolean | 'subtle' | 'strong'
|
|
11
12
|
variant?: 'default' | 'glass' | 'interactive' | 'muted' | 'unstyled'
|
|
12
13
|
}
|
|
@@ -15,6 +16,7 @@ const {
|
|
|
15
16
|
as: Tag = 'div',
|
|
16
17
|
class: classProp = '',
|
|
17
18
|
className = '',
|
|
19
|
+
density = 'comfortable',
|
|
18
20
|
glass = false,
|
|
19
21
|
variant = 'default',
|
|
20
22
|
...rest
|
|
@@ -24,6 +26,7 @@ const isGlass = glass || variant === 'glass'
|
|
|
24
26
|
|
|
25
27
|
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
26
28
|
'ui-card',
|
|
29
|
+
`ui-card--${density}`,
|
|
27
30
|
variant === 'unstyled' && 'ui-card--unstyled',
|
|
28
31
|
variant === 'muted' && 'ui-card--muted',
|
|
29
32
|
variant === 'interactive' && 'ui-card--interactive',
|
|
@@ -35,6 +38,7 @@ const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
|
35
38
|
|
|
36
39
|
<Tag
|
|
37
40
|
class:list={classList}
|
|
41
|
+
data-density={density}
|
|
38
42
|
data-slot="card"
|
|
39
43
|
data-variant={variant}
|
|
40
44
|
{...passthrough}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { type LumenChangeSummaryItem, resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'section'> {
|
|
7
|
+
items?: readonly LumenChangeSummaryItem[]
|
|
8
|
+
label?: string
|
|
9
|
+
beforeLabel?: string
|
|
10
|
+
afterLabel?: string
|
|
11
|
+
changedLabel?: string
|
|
12
|
+
unchangedLabel?: string
|
|
13
|
+
summary?: string
|
|
14
|
+
class?: string | null
|
|
15
|
+
className?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { items = [], label = 'Changes', beforeLabel = 'Before', afterLabel = 'After', changedLabel = 'Changed',
|
|
19
|
+
unchangedLabel = 'Unchanged', summary, class: classProp, className, ...rest } = Astro.props
|
|
20
|
+
|
|
21
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, ['ui-change-summary'], classProp, className)
|
|
22
|
+
---
|
|
23
|
+
<section class:list={classList} aria-label={label} {...passthrough}>
|
|
24
|
+
<h3 class="ui-change-summary__heading">{label}</h3>
|
|
25
|
+
{summary && <p class="ui-change-summary__summary">{summary}</p>}
|
|
26
|
+
<dl class="ui-change-summary__list">
|
|
27
|
+
{items.map(item => (
|
|
28
|
+
<div class="ui-change-summary__item" data-changed={item.changed}>
|
|
29
|
+
<dt>{item.label}<span class="ui-change-summary__state">{item.changed ? changedLabel : unchangedLabel}</span></dt>
|
|
30
|
+
<dd><span class="ui-change-summary__value-label">{beforeLabel}</span>{item.before}</dd>
|
|
31
|
+
<dd><span class="ui-change-summary__value-label">{afterLabel}</span>{item.after}</dd>
|
|
32
|
+
</div>
|
|
33
|
+
))}
|
|
34
|
+
</dl>
|
|
35
|
+
<slot />
|
|
36
|
+
</section>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ChartDatumAction } from '../runtime/chart-actions'
|
|
3
|
+
|
|
4
|
+
import Button from './Button.astro'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
actions: readonly (ChartDatumAction | null)[]
|
|
8
|
+
label: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { actions, label } = Astro.props
|
|
12
|
+
const available = actions.filter(action => action !== null)
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{available.length > 0 && (
|
|
16
|
+
<details class="ui-chart__actions" data-ui-chart-actions>
|
|
17
|
+
<summary>{label}</summary>
|
|
18
|
+
<ul>
|
|
19
|
+
{available.map(action => (
|
|
20
|
+
<li><Button variant="ghost" data-ui-chart-datum={action.serialized}>{action.label}</Button></li>
|
|
21
|
+
))}
|
|
22
|
+
</ul>
|
|
23
|
+
</details>
|
|
24
|
+
)}
|
|
@@ -0,0 +1,11 @@
|
|
|
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'> { duration?: LumenWorkflowDuration, className?: string }
|
|
7
|
+
|
|
8
|
+
const { duration = 'standard', class: classProp = '', className = '', ...rest } = Astro.props
|
|
9
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [], classProp, className)
|
|
10
|
+
---
|
|
11
|
+
<div class:list={classList} data-ui-chart-motion data-ui-motion-duration={duration} {...passthrough}><slot /></div>
|
package/components/Code.astro
CHANGED
|
@@ -12,6 +12,10 @@ interface Props extends HTMLAttributes<'code'> {
|
|
|
12
12
|
className?: string
|
|
13
13
|
code?: string
|
|
14
14
|
copy?: boolean
|
|
15
|
+
codeLabel?: string
|
|
16
|
+
copyLabel?: string
|
|
17
|
+
copiedLabel?: string
|
|
18
|
+
errorLabel?: string
|
|
15
19
|
highlighted?: boolean
|
|
16
20
|
label?: string
|
|
17
21
|
language?: string
|
|
@@ -25,6 +29,10 @@ const {
|
|
|
25
29
|
className = '',
|
|
26
30
|
code,
|
|
27
31
|
copy = false,
|
|
32
|
+
codeLabel = 'Code example',
|
|
33
|
+
copyLabel = 'Copy code to clipboard',
|
|
34
|
+
copiedLabel = 'Code copied to clipboard',
|
|
35
|
+
errorLabel = 'Could not copy code. Select and copy it manually.',
|
|
28
36
|
highlighted = false,
|
|
29
37
|
label,
|
|
30
38
|
language,
|
|
@@ -54,6 +62,7 @@ const renderedCode = hasCode ? renderLumenCodeHtml(code, language) : ''
|
|
|
54
62
|
(
|
|
55
63
|
<figure
|
|
56
64
|
class:list={blockClassList}
|
|
65
|
+
data-code-label={codeLabel}
|
|
57
66
|
data-code-theme={theme}
|
|
58
67
|
data-language={language}
|
|
59
68
|
data-slot="code"
|
|
@@ -73,8 +82,14 @@ const renderedCode = hasCode ? renderLumenCodeHtml(code, language) : ''
|
|
|
73
82
|
</span>
|
|
74
83
|
{copy && (
|
|
75
84
|
<button
|
|
76
|
-
aria-label=
|
|
85
|
+
aria-label={copyLabel}
|
|
77
86
|
class="ui-code__copy"
|
|
87
|
+
data-copy-value={highlighted ? code : undefined}
|
|
88
|
+
data-copy-label={copyLabel}
|
|
89
|
+
data-copy-copied-label={copiedLabel}
|
|
90
|
+
data-copy-error-label={errorLabel}
|
|
91
|
+
data-state="idle"
|
|
92
|
+
title={copyLabel}
|
|
78
93
|
data-ui-code-copy
|
|
79
94
|
type="button"
|
|
80
95
|
>
|
|
@@ -85,8 +100,8 @@ const renderedCode = hasCode ? renderLumenCodeHtml(code, language) : ''
|
|
|
85
100
|
</figcaption>
|
|
86
101
|
)}
|
|
87
102
|
{highlighted && <slot />}
|
|
88
|
-
{!highlighted && hasCode && <pre><code set:html={renderedCode}></code></pre>}
|
|
89
|
-
{!highlighted && !hasCode && <pre><code><slot /></code></pre>}
|
|
103
|
+
{!highlighted && hasCode && <pre aria-label={!wrap ? codeLabel : undefined} role={!wrap ? 'region' : undefined} tabindex={!wrap ? 0 : undefined}><code set:html={renderedCode}></code></pre>}
|
|
104
|
+
{!highlighted && !hasCode && <pre aria-label={!wrap ? codeLabel : undefined} role={!wrap ? 'region' : undefined} tabindex={!wrap ? 0 : undefined}><code><slot /></code></pre>}
|
|
90
105
|
</figure>
|
|
91
106
|
) :
|
|
92
107
|
(
|
|
@@ -20,6 +20,10 @@ interface Props extends HTMLAttributes<'div'> {
|
|
|
20
20
|
class?: string | null
|
|
21
21
|
className?: string
|
|
22
22
|
copy?: boolean
|
|
23
|
+
codeLabel?: string
|
|
24
|
+
copyLabel?: string
|
|
25
|
+
copiedLabel?: string
|
|
26
|
+
errorLabel?: string
|
|
23
27
|
initialValue?: string
|
|
24
28
|
items: readonly CodeTabItem[]
|
|
25
29
|
storageKey?: string
|
|
@@ -32,6 +36,10 @@ const {
|
|
|
32
36
|
class: classProp = '',
|
|
33
37
|
className = '',
|
|
34
38
|
copy = true,
|
|
39
|
+
codeLabel = 'Code example',
|
|
40
|
+
copyLabel = 'Copy code to clipboard',
|
|
41
|
+
copiedLabel = 'Code copied to clipboard',
|
|
42
|
+
errorLabel = 'Could not copy code. Select and copy it manually.',
|
|
35
43
|
initialValue,
|
|
36
44
|
items,
|
|
37
45
|
storageKey,
|
|
@@ -82,6 +90,10 @@ const { classList, passthrough } = resolveLumenAstroProps(
|
|
|
82
90
|
<Code
|
|
83
91
|
code={item.code}
|
|
84
92
|
copy={copy}
|
|
93
|
+
codeLabel={codeLabel}
|
|
94
|
+
copyLabel={copyLabel}
|
|
95
|
+
copiedLabel={copiedLabel}
|
|
96
|
+
errorLabel={errorLabel}
|
|
85
97
|
{...(item.language === undefined ? {} : { language: item.language })}
|
|
86
98
|
theme={theme}
|
|
87
99
|
variant="block"
|
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { HTMLAttributes } from 'astro/types'
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
alignLumenChartSeries,
|
|
4
|
+
import { alignLumenChartSeries,
|
|
6
5
|
createLumenBarGeometry,
|
|
6
|
+
createLumenChartDatumActivation,
|
|
7
7
|
createLumenLineGeometry,
|
|
8
8
|
formatLumenChartSummary,
|
|
9
9
|
getLumenChartCategories,
|
|
10
|
+
getLumenChartCategoryLabel,
|
|
11
|
+
getLumenChartCategoryTicks,
|
|
10
12
|
getLumenChartDomain,
|
|
13
|
+
getLumenChartTicks,
|
|
11
14
|
getLumenChartToneClassName,
|
|
12
15
|
hasLumenChartData,
|
|
13
16
|
type LumenChartDatum,
|
|
14
17
|
type LumenChartLabels,
|
|
15
18
|
type LumenComboSeries,
|
|
16
19
|
resolveLumenChartLabels,
|
|
17
|
-
resolveLumenChartTone } from '@santi020k/lumen-core'
|
|
20
|
+
resolveLumenChartTone, scaleLumenChartValue } from '@santi020k/lumen-core'
|
|
21
|
+
|
|
22
|
+
import { createChartDatumAction } from '../runtime/chart-actions'
|
|
18
23
|
|
|
19
24
|
import Chart from './Chart.astro'
|
|
25
|
+
import ChartDatumActions from './ChartDatumActions.astro'
|
|
20
26
|
|
|
21
27
|
interface Props extends HTMLAttributes<'figure'> {
|
|
22
28
|
caption?: string
|
|
23
29
|
class?: string | null
|
|
24
30
|
className?: string
|
|
25
31
|
description?: string
|
|
32
|
+
drilldown?: boolean
|
|
26
33
|
formatValue?: (value: number) => string
|
|
27
34
|
heading?: string
|
|
28
35
|
labels?: Partial<LumenChartLabels>
|
|
@@ -32,7 +39,7 @@ interface Props extends HTMLAttributes<'figure'> {
|
|
|
32
39
|
summary?: string
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
const { caption, class: classProp = '', className = '', description, formatValue = String, heading, labels, series, showLegend = true, showTable = true, summary: summaryProp, ...rest } = Astro.props
|
|
42
|
+
const { caption, class: classProp = '', className = '', description, drilldown = false, formatValue = String, heading, labels, series, showLegend = true, showTable = true, summary: summaryProp, ...rest } = Astro.props
|
|
36
43
|
const resolvedLabels = resolveLumenChartLabels(labels)
|
|
37
44
|
const summary = summaryProp ?? formatLumenChartSummary(series, formatValue, resolvedLabels)
|
|
38
45
|
const width = 640
|
|
@@ -73,6 +80,8 @@ const lineGeometryOptions = {
|
|
|
73
80
|
...(barSeries.length === 0 ?
|
|
74
81
|
{} :
|
|
75
82
|
{
|
|
83
|
+
paddingBottom: bars.margin.bottom,
|
|
84
|
+
paddingTop: bars.margin.top,
|
|
76
85
|
xDomain: { max: 1, min: 0 },
|
|
77
86
|
xScale: 'linear' as const
|
|
78
87
|
})
|
|
@@ -83,16 +92,80 @@ const lines = lineSeries.map(item => createLumenLineGeometry(
|
|
|
83
92
|
lineGeometryOptions
|
|
84
93
|
))
|
|
85
94
|
|
|
95
|
+
const plotTop = barSeries.length > 0 ? bars.margin.top : padding
|
|
96
|
+
const plotBottom = height - (barSeries.length > 0 ? bars.margin.bottom : padding)
|
|
97
|
+
const plotLeft = barSeries.length > 0 ? bars.margin.left : padding
|
|
98
|
+
const plotRight = width - (barSeries.length > 0 ? bars.margin.right : padding)
|
|
99
|
+
const ticks = getLumenChartTicks(domain)
|
|
100
|
+
|
|
101
|
+
const categoryTicks = getLumenChartCategoryTicks(categories.map(category => getLumenChartCategoryLabel(series, category)), {
|
|
102
|
+
start: plotLeft,
|
|
103
|
+
end: plotRight,
|
|
104
|
+
...(barSeries.length > 0 ? { positions: bars.categories.map(category => category.x) } : {})
|
|
105
|
+
})
|
|
106
|
+
|
|
86
107
|
const hasData = hasLumenChartData(series)
|
|
108
|
+
|
|
109
|
+
const barActions = drilldown ?
|
|
110
|
+
bars.marks.map(mark => {
|
|
111
|
+
const item = barSeries.find(candidate => candidate.id === mark.seriesId)
|
|
112
|
+
const datum = item?.data.find(candidate => candidate.x === mark.category)
|
|
113
|
+
|
|
114
|
+
return item && datum ?
|
|
115
|
+
createChartDatumAction(
|
|
116
|
+
createLumenChartDatumActivation(item.id, datum),
|
|
117
|
+
`${datum.xLabel ?? datum.x} · ${item.label}: ${datum.label ?? formatValue(mark.value)}`,
|
|
118
|
+
resolvedLabels
|
|
119
|
+
) :
|
|
120
|
+
null
|
|
121
|
+
}) :
|
|
122
|
+
[]
|
|
123
|
+
|
|
124
|
+
const pointActions = drilldown ?
|
|
125
|
+
lines.map((geometry, index) => {
|
|
126
|
+
const item = lineSeries[index]
|
|
127
|
+
|
|
128
|
+
return geometry.points.map(point => {
|
|
129
|
+
const datum = item?.data.find(candidate => alignComboLineDatum(candidate).x === point.x)
|
|
130
|
+
|
|
131
|
+
return item && datum ?
|
|
132
|
+
createChartDatumAction(
|
|
133
|
+
createLumenChartDatumActivation(item.id, datum),
|
|
134
|
+
`${datum.xLabel ?? datum.x} · ${item.label}: ${datum.label ?? formatValue(point.y ?? 0)}`,
|
|
135
|
+
resolvedLabels
|
|
136
|
+
) :
|
|
137
|
+
null
|
|
138
|
+
})
|
|
139
|
+
}) :
|
|
140
|
+
[]
|
|
141
|
+
|
|
142
|
+
const datumActions = [...barActions, ...pointActions.flat()]
|
|
143
|
+
|
|
87
144
|
---
|
|
88
145
|
|
|
89
|
-
<Chart class:list={['ui-combo-chart', classProp, className]} {caption} {description} {heading} {summary} {...rest}>
|
|
146
|
+
<Chart class:list={['ui-combo-chart', classProp, className]} {caption} {description} {heading} {summary} {...rest} data-ui-chart-activation={drilldown || undefined}>
|
|
90
147
|
{showLegend && hasData && <ul class="ui-chart__legend" aria-label={resolvedLabels.chartLegend}>{series.map((item, index) => <li class={getLumenChartToneClassName(item.tone, index)}><span aria-hidden="true" />{item.label}</li>)}</ul>}
|
|
91
148
|
{!hasData && <p class="ui-chart__empty" role="status">{resolvedLabels.empty}</p>}
|
|
92
|
-
<div class="ui-chart__plot" hidden={!hasData}><svg aria-hidden="true" viewBox={`0 0 ${width} ${height}`}><g class="
|
|
93
|
-
ui-
|
|
149
|
+
<div aria-label={resolvedLabels.chartData} class="ui-chart__plot" role="region" tabindex="0" hidden={!hasData}><svg aria-hidden="true" viewBox={`0 0 ${width} ${height}`}><g class="
|
|
150
|
+
ui-chart__grid
|
|
94
151
|
"
|
|
95
|
-
|
|
152
|
+
>{ticks.map(tick => {
|
|
153
|
+
const y = scaleLumenChartValue(tick, domain, plotBottom, plotTop)
|
|
154
|
+
|
|
155
|
+
return <><line x1={plotLeft} x2={plotRight} y1={y} y2={y} /><text x={plotLeft - 8} y={y}>{formatValue(tick)}</text></>
|
|
156
|
+
})}</g><g class="ui-chart__axis-labels">{categoryTicks.map(tick => <text x={tick.position} y={height - 12} text-anchor={tick.textAnchor}>{tick.label}</text>)}</g><g class="
|
|
157
|
+
ui-bar-chart__marks
|
|
158
|
+
"
|
|
159
|
+
>{bars.marks.map((mark, index) => <rect data-ui-chart-datum={barActions[index]?.serialized} class={getLumenChartToneClassName(mark.tone)} height={mark.height} rx="4" width={mark.width} x={mark.x} y={mark.y}><title>{`${mark.seriesLabel}: ${formatValue(mark.value)}`}</title></rect>)}{drilldown && bars.marks.map((mark, index) => (
|
|
160
|
+
<rect
|
|
161
|
+
class="ui-chart__datum-hit"
|
|
162
|
+
width={Math.max(12, mark.width)}
|
|
163
|
+
height={Math.max(12, mark.height)}
|
|
164
|
+
x={mark.x - Math.max(0, 12 - mark.width) / 2}
|
|
165
|
+
y={mark.y - Math.max(0, 12 - mark.height) / 2}
|
|
166
|
+
data-ui-chart-datum={barActions[index]?.serialized}
|
|
167
|
+
/>
|
|
168
|
+
))}</g>{lines.map((geometry, index) => {
|
|
96
169
|
const item = lineSeries[index]
|
|
97
170
|
|
|
98
171
|
if (!item) return null
|
|
@@ -108,16 +181,25 @@ d={path}
|
|
|
108
181
|
)) }<path
|
|
109
182
|
class="ui-line-chart__line"
|
|
110
183
|
d={geometry.path}
|
|
111
|
-
|
|
184
|
+
/>{geometry.points.map(point => <circle class="ui-line-chart__point" cx={point.xCoordinate} cy={point.yCoordinate} r="3.5" />)}{drilldown && geometry.points.map((point, pointIndex) => (
|
|
185
|
+
<circle
|
|
186
|
+
class="ui-chart__datum-hit"
|
|
187
|
+
cx={point.xCoordinate}
|
|
188
|
+
cy={point.yCoordinate}
|
|
189
|
+
r="10"
|
|
190
|
+
data-ui-chart-datum={pointActions[index]?.[pointIndex]?.serialized}
|
|
191
|
+
/>
|
|
192
|
+
))}</g>
|
|
112
193
|
)
|
|
113
194
|
})}</svg></div>
|
|
114
195
|
{showTable && hasData && (
|
|
115
|
-
<details class="ui-chart__data"><summary>{resolvedLabels.viewData}</summary><div><table><thead><tr><th scope="col">{resolvedLabels.category}</th>{series.map(item => <th scope="col">{item.label}</th>)}</tr></thead><tbody>{categories.map(category => (
|
|
116
|
-
<tr><th scope="row">{String(category)}</th>{
|
|
196
|
+
<details class="ui-chart__data"><summary>{resolvedLabels.viewData}</summary><div role="group" tabindex="0" aria-label={resolvedLabels.chartData}><table><thead><tr><th scope="col">{resolvedLabels.category}</th>{series.map(item => <th scope="col">{item.label}</th>)}</tr></thead><tbody>{categories.map(category => (
|
|
197
|
+
<tr><th scope="row">{String(category)}</th>{alignedSeries.map(item => {
|
|
117
198
|
const datum = item.data.find(point => point.x === category)
|
|
118
199
|
|
|
119
200
|
return <td>{datum?.label ?? (datum?.y === undefined || datum.y === null || !Number.isFinite(datum.y) ? resolvedLabels.notAvailable : formatValue(datum.y))}</td>
|
|
120
201
|
})}</tr>
|
|
121
202
|
))}</tbody></table></div></details>
|
|
122
203
|
)}
|
|
204
|
+
{drilldown && <ChartDatumActions actions={datumActions} label={resolvedLabels.exploreData} />}
|
|
123
205
|
</Chart>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { getLumenPhoneFlagSource } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'span'> {
|
|
7
|
+
regionCode: string
|
|
8
|
+
decorative?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { regionCode, decorative = false, class: className, ...props } = Astro.props
|
|
12
|
+
const source = getLumenPhoneFlagSource(regionCode)
|
|
13
|
+
---
|
|
14
|
+
<span
|
|
15
|
+
{...props}
|
|
16
|
+
aria-hidden={decorative || undefined}
|
|
17
|
+
aria-label={decorative ? undefined : props['aria-label'] ?? regionCode.toUpperCase()}
|
|
18
|
+
class:list={['ui-country-flag', className]}
|
|
19
|
+
data-slot="country-flag"
|
|
20
|
+
role={decorative ? undefined : 'img'}
|
|
21
|
+
>
|
|
22
|
+
{source ? <img alt="" height="18" src={source} width="24" /> : regionCode.toUpperCase().slice(0, 2)}
|
|
23
|
+
</span>
|
|
@@ -33,6 +33,8 @@ interface Props extends HTMLAttributes<'div'> {
|
|
|
33
33
|
class?: string | null
|
|
34
34
|
className?: string
|
|
35
35
|
columns?: DataTableColumn[]
|
|
36
|
+
layout?: 'records' | 'scroll'
|
|
37
|
+
sortMode?: 'client' | 'manual'
|
|
36
38
|
glass?: boolean | 'subtle' | 'strong'
|
|
37
39
|
name?: string
|
|
38
40
|
rows?: DataTableRow[]
|
|
@@ -44,6 +46,8 @@ const {
|
|
|
44
46
|
className = '',
|
|
45
47
|
columns = [],
|
|
46
48
|
glass = false,
|
|
49
|
+
layout = 'scroll',
|
|
50
|
+
sortMode = 'client',
|
|
47
51
|
name,
|
|
48
52
|
rows = [],
|
|
49
53
|
selectable = false,
|
|
@@ -80,17 +84,20 @@ const getRowValue = (row: DataTableRow, index: number): string => String(row.row
|
|
|
80
84
|
class:list={classList}
|
|
81
85
|
data-ui-datatable
|
|
82
86
|
data-ui-datatable-name={name}
|
|
87
|
+
data-ui-datatable-sort-mode={sortMode}
|
|
88
|
+
data-layout={layout}
|
|
83
89
|
data-ui-datatable-selectable={selectable ? 'true' : undefined}
|
|
84
90
|
data-ui-glass-track={glass ? true : undefined}
|
|
85
91
|
{...passthrough}
|
|
86
92
|
>
|
|
87
93
|
{hasData ?
|
|
88
94
|
(
|
|
89
|
-
<table>
|
|
90
|
-
<thead>
|
|
91
|
-
<tr>
|
|
95
|
+
<table class="ui-table" data-layout={layout} role="table">
|
|
96
|
+
<thead role="rowgroup">
|
|
97
|
+
<tr role="row">
|
|
92
98
|
{columns.map(column => (
|
|
93
99
|
<th
|
|
100
|
+
data-ui-datatable-sort-key={column.key}
|
|
94
101
|
data-ui-datatable-sort-type={column.sort}
|
|
95
102
|
data-ui-datatable-sortable={column.sortable ? 'true' : undefined}
|
|
96
103
|
scope="col"
|
|
@@ -100,15 +107,16 @@ const getRowValue = (row: DataTableRow, index: number): string => String(row.row
|
|
|
100
107
|
))}
|
|
101
108
|
</tr>
|
|
102
109
|
</thead>
|
|
103
|
-
<tbody>
|
|
110
|
+
<tbody role="rowgroup">
|
|
104
111
|
{rows.map((row, rowIndex) => (
|
|
105
|
-
<tr data-ui-datatable-row data-value={getRowValue(row, rowIndex)}>
|
|
112
|
+
<tr role="row" data-ui-datatable-row data-value={getRowValue(row, rowIndex)}>
|
|
106
113
|
{columns.map(column => {
|
|
107
114
|
const cell = row[column.key]
|
|
108
115
|
|
|
109
116
|
return (
|
|
110
|
-
<td data-sort-value={getSortValue(cell)}>
|
|
111
|
-
{
|
|
117
|
+
<td role="cell" data-sort-value={getSortValue(cell)}>
|
|
118
|
+
{layout === 'records' && <span class="ui-table__label" aria-hidden="true">{column.header ?? column.label ?? column.key}</span>}
|
|
119
|
+
<div class="ui-table__value">{formatCell(cell)}</div>
|
|
112
120
|
</td>
|
|
113
121
|
)
|
|
114
122
|
})}
|
|
@@ -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 LumenDateLabels, parseLumenDate, resolveLumenAstroProps, resolveLumenDateLabels, resolveLumenDateLocale } from '@santi020k/lumen-core'
|
|
5
5
|
|
|
6
6
|
import Calendar from './Calendar.astro'
|
|
7
7
|
|
|
@@ -9,6 +9,8 @@ interface Props extends HTMLAttributes<'input'> {
|
|
|
9
9
|
class?: string | null
|
|
10
10
|
className?: string
|
|
11
11
|
glass?: boolean | 'subtle' | 'strong'
|
|
12
|
+
labels?: Partial<Pick<LumenDateLabels, 'previousMonth' | 'nextMonth' | 'chooseDate'>>
|
|
13
|
+
locale?: string
|
|
12
14
|
placeholder?: string
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -18,6 +20,9 @@ const {
|
|
|
18
20
|
disabled = false,
|
|
19
21
|
glass = false,
|
|
20
22
|
id,
|
|
23
|
+
labels,
|
|
24
|
+
locale,
|
|
25
|
+
readonly = false,
|
|
21
26
|
max,
|
|
22
27
|
min,
|
|
23
28
|
name,
|
|
@@ -39,28 +44,28 @@ const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
|
39
44
|
glass === 'strong' && 'ui-glass-strong'
|
|
40
45
|
], classProp, className)
|
|
41
46
|
|
|
47
|
+
const resolvedLocale = resolveLumenDateLocale(locale)
|
|
48
|
+
const resolvedLabels = { ...resolveLumenDateLabels(resolvedLocale), ...labels }
|
|
42
49
|
const selectedValue = value === undefined || value === null ? undefined : String(value)
|
|
43
|
-
const
|
|
44
|
-
const
|
|
50
|
+
const selectedDate = parseLumenDate(selectedValue)
|
|
51
|
+
const hasSelectedValue = selectedDate !== null
|
|
52
|
+
const placeholderText = placeholder ?? resolvedLabels.chooseDate
|
|
45
53
|
|
|
46
|
-
const
|
|
47
|
-
new
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const displayValue = selectedDate && !Number.isNaN(selectedDate.getTime()) ?
|
|
51
|
-
new Intl.DateTimeFormat('en', {
|
|
52
|
-
day: 'numeric',
|
|
53
|
-
month: 'short',
|
|
54
|
-
timeZone: 'UTC',
|
|
55
|
-
year: 'numeric'
|
|
54
|
+
const displayValue = selectedDate ?
|
|
55
|
+
new Intl.DateTimeFormat(resolvedLocale, {
|
|
56
|
+
day: 'numeric', month: 'short', timeZone: 'UTC', year: 'numeric'
|
|
56
57
|
}).format(selectedDate) :
|
|
57
|
-
|
|
58
|
+
placeholderText
|
|
59
|
+
|
|
58
60
|
---
|
|
59
61
|
|
|
60
62
|
<div
|
|
61
63
|
class:list={classList}
|
|
62
64
|
data-placeholder={hasSelectedValue ? undefined : 'true'}
|
|
63
65
|
data-ui-date-picker
|
|
66
|
+
data-locale={locale}
|
|
67
|
+
data-choose-date-label={labels?.chooseDate}
|
|
68
|
+
data-placeholder-default={placeholder === undefined ? 'true' : undefined}
|
|
64
69
|
data-ui-glass-track={glass ? true : undefined}
|
|
65
70
|
>
|
|
66
71
|
<input
|
|
@@ -71,7 +76,8 @@ const displayValue = selectedDate && !Number.isNaN(selectedDate.getTime()) ?
|
|
|
71
76
|
max={max}
|
|
72
77
|
min={min}
|
|
73
78
|
name={name}
|
|
74
|
-
placeholder={
|
|
79
|
+
placeholder={placeholderText}
|
|
80
|
+
readonly={readonly}
|
|
75
81
|
required={required}
|
|
76
82
|
type={type}
|
|
77
83
|
value={value}
|
|
@@ -83,6 +89,7 @@ const displayValue = selectedDate && !Number.isNaN(selectedDate.getTime()) ?
|
|
|
83
89
|
aria-controls={popoverId}
|
|
84
90
|
aria-expanded="false"
|
|
85
91
|
aria-haspopup="dialog"
|
|
92
|
+
aria-disabled={readonly ? 'true' : undefined}
|
|
86
93
|
aria-required={required ? 'true' : undefined}
|
|
87
94
|
class="ui-input ui-date-picker ui-date-picker__trigger"
|
|
88
95
|
data-ui-date-picker-trigger
|
|
@@ -97,7 +104,7 @@ const displayValue = selectedDate && !Number.isNaN(selectedDate.getTime()) ?
|
|
|
97
104
|
</button>
|
|
98
105
|
|
|
99
106
|
<div
|
|
100
|
-
aria-label=
|
|
107
|
+
aria-label={resolvedLabels.chooseDate}
|
|
101
108
|
class="ui-date-picker__popover"
|
|
102
109
|
data-state="closed"
|
|
103
110
|
data-ui-date-picker-popover
|
|
@@ -105,7 +112,7 @@ const displayValue = selectedDate && !Number.isNaN(selectedDate.getTime()) ?
|
|
|
105
112
|
id={popoverId}
|
|
106
113
|
role="dialog"
|
|
107
114
|
>
|
|
108
|
-
<Calendar max={typeof max === 'string' ? max : undefined} min={typeof min === 'string' ? min : undefined} value={selectedValue} />
|
|
115
|
+
<Calendar locale={locale} labels={labels} readOnly={readonly} max={typeof max === 'string' ? max : undefined} min={typeof min === 'string' ? min : undefined} value={selectedValue} />
|
|
109
116
|
</div>
|
|
110
117
|
</div>
|
|
111
118
|
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'dd'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { class: classProp = '', className = '', ...rest } = Astro.props
|
|
12
|
+
|
|
13
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
14
|
+
rest, ['ui-description-detail', 'ui-descriptions__detail'], classProp, className
|
|
15
|
+
)
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<dd class:list={classList} data-slot="description-detail" {...passthrough}><slot /></dd>
|