@santi020k/lumen-astro 1.2.0 → 1.3.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 +57 -0
- package/components/DropdownMenuContent.astro +28 -0
- package/components/DropdownMenuItem.astro +39 -0
- package/components/DropdownMenuSeparator.astro +25 -0
- package/components/DropdownMenuTrigger.astro +39 -0
- package/components/LanguageToggle.astro +35 -2
- package/components/PopoverPanel.astro +27 -0
- package/components/PopoverTrigger.astro +34 -0
- package/components/TabsList.astro +34 -0
- package/components/TabsPanel.astro +35 -0
- package/components/TabsTrigger.astro +36 -0
- package/components/TooltipContent.astro +27 -0
- package/index.ts +10 -0
- package/package.json +3 -3
- package/runtime/UIPrimitives.astro +139 -4
package/README.md
CHANGED
|
@@ -60,6 +60,63 @@ incremental migrations, but new code should keep native behavior and visual styl
|
|
|
60
60
|
<NativeSelect size={8} visualSize="lg" />
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
## Compound interactive components
|
|
64
|
+
|
|
65
|
+
Popover, dropdown menu, tabs, and tooltip parts expose the DOM and ARIA contracts consumed by the
|
|
66
|
+
shared progressive-enhancement runtime. Prefer these parts over recreating roles and `data-ui-*`
|
|
67
|
+
attributes by hand.
|
|
68
|
+
|
|
69
|
+
```astro
|
|
70
|
+
---
|
|
71
|
+
import {
|
|
72
|
+
Button,
|
|
73
|
+
DropdownMenu,
|
|
74
|
+
DropdownMenuContent,
|
|
75
|
+
DropdownMenuItem,
|
|
76
|
+
DropdownMenuSeparator,
|
|
77
|
+
DropdownMenuTrigger,
|
|
78
|
+
Popover,
|
|
79
|
+
PopoverPanel,
|
|
80
|
+
PopoverTrigger,
|
|
81
|
+
Tabs,
|
|
82
|
+
TabsList,
|
|
83
|
+
TabsPanel,
|
|
84
|
+
TabsTrigger,
|
|
85
|
+
Tooltip,
|
|
86
|
+
TooltipContent
|
|
87
|
+
} from '@santi020k/lumen-astro'
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
<DropdownMenu>
|
|
91
|
+
<DropdownMenuTrigger>Download</DropdownMenuTrigger>
|
|
92
|
+
<DropdownMenuContent>
|
|
93
|
+
<DropdownMenuItem>macOS</DropdownMenuItem>
|
|
94
|
+
<DropdownMenuItem>Windows</DropdownMenuItem>
|
|
95
|
+
<DropdownMenuSeparator />
|
|
96
|
+
<DropdownMenuItem disabled status="Soon">Android</DropdownMenuItem>
|
|
97
|
+
</DropdownMenuContent>
|
|
98
|
+
</DropdownMenu>
|
|
99
|
+
|
|
100
|
+
<Popover>
|
|
101
|
+
<PopoverTrigger>Filters</PopoverTrigger>
|
|
102
|
+
<PopoverPanel>Filter controls</PopoverPanel>
|
|
103
|
+
</Popover>
|
|
104
|
+
|
|
105
|
+
<Tabs initialValue="preview">
|
|
106
|
+
<TabsList aria-label="Workspace view">
|
|
107
|
+
<TabsTrigger value="preview">Preview</TabsTrigger>
|
|
108
|
+
<TabsTrigger value="code">Code</TabsTrigger>
|
|
109
|
+
</TabsList>
|
|
110
|
+
<TabsPanel value="preview">Rendered result</TabsPanel>
|
|
111
|
+
<TabsPanel value="code">Source code</TabsPanel>
|
|
112
|
+
</Tabs>
|
|
113
|
+
|
|
114
|
+
<Tooltip>
|
|
115
|
+
<Button aria-label="Save changes">Save</Button>
|
|
116
|
+
<TooltipContent>Save changes</TooltipContent>
|
|
117
|
+
</Tooltip>
|
|
118
|
+
```
|
|
119
|
+
|
|
63
120
|
## Context navigation
|
|
64
121
|
|
|
65
122
|
Use `ContextNavigation` below a primary header when the available links change with a selected
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'div'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: classProp = '',
|
|
13
|
+
className = '',
|
|
14
|
+
role = 'menu',
|
|
15
|
+
...rest
|
|
16
|
+
} = Astro.props
|
|
17
|
+
|
|
18
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
19
|
+
rest,
|
|
20
|
+
['ui-menu__content'],
|
|
21
|
+
classProp,
|
|
22
|
+
className
|
|
23
|
+
)
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<div role={role} class:list={classList} {...passthrough}>
|
|
27
|
+
<slot />
|
|
28
|
+
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'button'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
status?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
class: classProp = '',
|
|
14
|
+
className = '',
|
|
15
|
+
disabled = false,
|
|
16
|
+
status,
|
|
17
|
+
type = 'button',
|
|
18
|
+
...rest
|
|
19
|
+
} = Astro.props
|
|
20
|
+
|
|
21
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
22
|
+
rest,
|
|
23
|
+
['ui-menu__item'],
|
|
24
|
+
classProp,
|
|
25
|
+
className
|
|
26
|
+
)
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<button
|
|
30
|
+
type={type}
|
|
31
|
+
role="menuitem"
|
|
32
|
+
class:list={classList}
|
|
33
|
+
disabled={disabled}
|
|
34
|
+
aria-disabled={disabled ? 'true' : undefined}
|
|
35
|
+
{...passthrough}
|
|
36
|
+
>
|
|
37
|
+
<span class="ui-menu__item-label"><slot /></span>
|
|
38
|
+
{status && <span class="ui-menu__item-status">{status}</span>}
|
|
39
|
+
</button>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'div'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: classProp = '',
|
|
13
|
+
className = '',
|
|
14
|
+
...rest
|
|
15
|
+
} = Astro.props
|
|
16
|
+
|
|
17
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
18
|
+
rest,
|
|
19
|
+
['ui-menu__separator'],
|
|
20
|
+
classProp,
|
|
21
|
+
className
|
|
22
|
+
)
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<div role="separator" class:list={classList} {...passthrough} />
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'button'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: classProp = '',
|
|
13
|
+
className = '',
|
|
14
|
+
type = 'button',
|
|
15
|
+
...rest
|
|
16
|
+
} = Astro.props
|
|
17
|
+
|
|
18
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
19
|
+
rest,
|
|
20
|
+
[
|
|
21
|
+
'ui-button',
|
|
22
|
+
'ui-button--outline',
|
|
23
|
+
'ui-button--default-size',
|
|
24
|
+
'ui-menu__trigger'
|
|
25
|
+
],
|
|
26
|
+
classProp,
|
|
27
|
+
className
|
|
28
|
+
)
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<button
|
|
32
|
+
type={type}
|
|
33
|
+
class:list={classList}
|
|
34
|
+
data-ui-trigger
|
|
35
|
+
aria-haspopup="menu"
|
|
36
|
+
{...passthrough}
|
|
37
|
+
>
|
|
38
|
+
<slot />
|
|
39
|
+
</button>
|
|
@@ -1,20 +1,41 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { HTMLAttributes } from 'astro/types'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
formatLumenLanguageLabel,
|
|
6
|
+
getLumenLocalePair,
|
|
7
|
+
type LumenLocaleOption,
|
|
8
|
+
normalizeLumenLocales,
|
|
9
|
+
resolveLumenAstroProps
|
|
10
|
+
} from '@santi020k/lumen-core'
|
|
5
11
|
|
|
6
12
|
interface Props extends HTMLAttributes<'button'> {
|
|
7
13
|
class?: string | null
|
|
8
14
|
className?: string
|
|
15
|
+
defaultValue?: string
|
|
16
|
+
labelTemplate?: string
|
|
17
|
+
locales?: readonly LumenLocaleOption[]
|
|
18
|
+
storageKey?: string
|
|
19
|
+
value?: string
|
|
9
20
|
}
|
|
10
21
|
|
|
11
22
|
const {
|
|
12
23
|
class: classProp = '',
|
|
13
24
|
className = '',
|
|
25
|
+
defaultValue,
|
|
26
|
+
labelTemplate = 'Change language from {current} to {next}',
|
|
27
|
+
locales: localeOptions,
|
|
28
|
+
storageKey,
|
|
14
29
|
type = 'button',
|
|
30
|
+
value,
|
|
15
31
|
...rest
|
|
16
32
|
} = Astro.props
|
|
17
33
|
|
|
34
|
+
const locales = normalizeLumenLocales(localeOptions)
|
|
35
|
+
const initialValue = value ?? defaultValue ?? locales[0]?.value
|
|
36
|
+
const { current, next } = getLumenLocalePair(locales, initialValue)
|
|
37
|
+
const accessibleLabel = formatLumenLanguageLabel(labelTemplate, current, next)
|
|
38
|
+
|
|
18
39
|
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
19
40
|
'ui-language-toggle'
|
|
20
41
|
], classProp, className)
|
|
@@ -24,7 +45,19 @@ const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
|
24
45
|
type={type}
|
|
25
46
|
class:list={classList}
|
|
26
47
|
data-ui-language-toggle
|
|
48
|
+
data-ui-language-controlled={value === undefined ? undefined : ''}
|
|
49
|
+
data-ui-language-initial-value={initialValue}
|
|
50
|
+
data-ui-language-label-template={labelTemplate}
|
|
51
|
+
data-ui-language-locales={JSON.stringify(locales)}
|
|
52
|
+
data-ui-language-value={current.value}
|
|
53
|
+
data-ui-language-next-value={next.value}
|
|
54
|
+
data-ui-language-storage-key={storageKey}
|
|
55
|
+
aria-label={accessibleLabel}
|
|
27
56
|
{...passthrough}
|
|
28
57
|
>
|
|
29
|
-
|
|
58
|
+
{
|
|
59
|
+
Astro.slots.has('default') ?
|
|
60
|
+
<slot /> :
|
|
61
|
+
<span data-ui-language-label lang={next.value}>{next.label}</span>
|
|
62
|
+
}
|
|
30
63
|
</button>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'div'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: classProp = '',
|
|
13
|
+
className = '',
|
|
14
|
+
...rest
|
|
15
|
+
} = Astro.props
|
|
16
|
+
|
|
17
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
18
|
+
rest,
|
|
19
|
+
['ui-popover__panel'],
|
|
20
|
+
classProp,
|
|
21
|
+
className
|
|
22
|
+
)
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<div class:list={classList} {...passthrough}>
|
|
26
|
+
<slot />
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'button'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: classProp = '',
|
|
13
|
+
className = '',
|
|
14
|
+
type = 'button',
|
|
15
|
+
...rest
|
|
16
|
+
} = Astro.props
|
|
17
|
+
|
|
18
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
19
|
+
rest,
|
|
20
|
+
['ui-button', 'ui-button--outline', 'ui-button--default-size'],
|
|
21
|
+
classProp,
|
|
22
|
+
className
|
|
23
|
+
)
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<button
|
|
27
|
+
type={type}
|
|
28
|
+
class:list={classList}
|
|
29
|
+
data-ui-trigger
|
|
30
|
+
aria-haspopup="menu"
|
|
31
|
+
{...passthrough}
|
|
32
|
+
>
|
|
33
|
+
<slot />
|
|
34
|
+
</button>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'div'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
orientation?: 'horizontal' | 'vertical'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
class: classProp = '',
|
|
14
|
+
className = '',
|
|
15
|
+
orientation = 'horizontal',
|
|
16
|
+
...rest
|
|
17
|
+
} = Astro.props
|
|
18
|
+
|
|
19
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
20
|
+
rest,
|
|
21
|
+
[],
|
|
22
|
+
classProp,
|
|
23
|
+
className
|
|
24
|
+
)
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<div
|
|
28
|
+
role="tablist"
|
|
29
|
+
aria-orientation={orientation}
|
|
30
|
+
class:list={classList}
|
|
31
|
+
{...passthrough}
|
|
32
|
+
>
|
|
33
|
+
<slot />
|
|
34
|
+
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'div'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
value: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
class: classProp = '',
|
|
14
|
+
className = '',
|
|
15
|
+
value,
|
|
16
|
+
...rest
|
|
17
|
+
} = Astro.props
|
|
18
|
+
|
|
19
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
20
|
+
rest,
|
|
21
|
+
[],
|
|
22
|
+
classProp,
|
|
23
|
+
className
|
|
24
|
+
)
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<div
|
|
28
|
+
role="tabpanel"
|
|
29
|
+
data-value={value}
|
|
30
|
+
tabindex="0"
|
|
31
|
+
class:list={classList}
|
|
32
|
+
{...passthrough}
|
|
33
|
+
>
|
|
34
|
+
<slot />
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'button'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
value: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
class: classProp = '',
|
|
14
|
+
className = '',
|
|
15
|
+
type = 'button',
|
|
16
|
+
value,
|
|
17
|
+
...rest
|
|
18
|
+
} = Astro.props
|
|
19
|
+
|
|
20
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
21
|
+
rest,
|
|
22
|
+
[],
|
|
23
|
+
classProp,
|
|
24
|
+
className
|
|
25
|
+
)
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<button
|
|
29
|
+
type={type}
|
|
30
|
+
role="tab"
|
|
31
|
+
data-value={value}
|
|
32
|
+
class:list={classList}
|
|
33
|
+
{...passthrough}
|
|
34
|
+
>
|
|
35
|
+
<slot />
|
|
36
|
+
</button>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
3
|
+
|
|
4
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'span'> {
|
|
7
|
+
class?: string | null
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: classProp = '',
|
|
13
|
+
className = '',
|
|
14
|
+
...rest
|
|
15
|
+
} = Astro.props
|
|
16
|
+
|
|
17
|
+
const { classList, passthrough } = resolveLumenAstroProps(
|
|
18
|
+
rest,
|
|
19
|
+
[],
|
|
20
|
+
classProp,
|
|
21
|
+
className
|
|
22
|
+
)
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<span role="tooltip" class:list={classList} {...passthrough}>
|
|
26
|
+
<slot />
|
|
27
|
+
</span>
|
package/index.ts
CHANGED
|
@@ -51,6 +51,10 @@ export { default as Dialog } from './components/Dialog.astro'
|
|
|
51
51
|
export { default as Direction } from './components/Direction.astro'
|
|
52
52
|
export { default as Drawer } from './components/Drawer.astro'
|
|
53
53
|
export { default as DropdownMenu } from './components/DropdownMenu.astro'
|
|
54
|
+
export { default as DropdownMenuContent } from './components/DropdownMenuContent.astro'
|
|
55
|
+
export { default as DropdownMenuItem } from './components/DropdownMenuItem.astro'
|
|
56
|
+
export { default as DropdownMenuSeparator } from './components/DropdownMenuSeparator.astro'
|
|
57
|
+
export { default as DropdownMenuTrigger } from './components/DropdownMenuTrigger.astro'
|
|
54
58
|
export { default as Empty } from './components/Empty.astro'
|
|
55
59
|
export { default as ErrorSummary } from './components/ErrorSummary.astro'
|
|
56
60
|
export { default as Eyebrow } from './components/Eyebrow.astro'
|
|
@@ -95,6 +99,8 @@ export { default as PieChart } from './components/PieChart.astro'
|
|
|
95
99
|
export { default as Pill } from './components/Pill.astro'
|
|
96
100
|
export { default as Popconfirm } from './components/Popconfirm.astro'
|
|
97
101
|
export { default as Popover } from './components/Popover.astro'
|
|
102
|
+
export { default as PopoverPanel } from './components/PopoverPanel.astro'
|
|
103
|
+
export { default as PopoverTrigger } from './components/PopoverTrigger.astro'
|
|
98
104
|
export { default as Progress } from './components/Progress.astro'
|
|
99
105
|
export { default as Prose } from './components/Prose.astro'
|
|
100
106
|
export { default as QRCode } from './components/QRCode.astro'
|
|
@@ -131,6 +137,9 @@ export { default as Stepper } from './components/Stepper.astro'
|
|
|
131
137
|
export { default as Switch } from './components/Switch.astro'
|
|
132
138
|
export { default as Table } from './components/Table.astro'
|
|
133
139
|
export { default as Tabs } from './components/Tabs.astro'
|
|
140
|
+
export { default as TabsList } from './components/TabsList.astro'
|
|
141
|
+
export { default as TabsPanel } from './components/TabsPanel.astro'
|
|
142
|
+
export { default as TabsTrigger } from './components/TabsTrigger.astro'
|
|
134
143
|
export { default as TagGroup } from './components/TagGroup.astro'
|
|
135
144
|
export { default as Textarea } from './components/Textarea.astro'
|
|
136
145
|
export { default as ThemeBuilder } from './components/ThemeBuilder.astro'
|
|
@@ -143,6 +152,7 @@ export { default as Toggle } from './components/Toggle.astro'
|
|
|
143
152
|
export { default as ToggleGroup } from './components/ToggleGroup.astro'
|
|
144
153
|
export { default as Toolbar } from './components/Toolbar.astro'
|
|
145
154
|
export { default as Tooltip } from './components/Tooltip.astro'
|
|
155
|
+
export { default as TooltipContent } from './components/TooltipContent.astro'
|
|
146
156
|
export { default as Tour } from './components/Tour.astro'
|
|
147
157
|
export { default as Transfer } from './components/Transfer.astro'
|
|
148
158
|
export { default as Tree } from './components/Tree.astro'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@santi020k/lumen-astro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"uqr": "^0.1.3",
|
|
40
|
-
"@santi020k/lumen": "^1.
|
|
41
|
-
"@santi020k/lumen-core": "^1.
|
|
40
|
+
"@santi020k/lumen": "^1.3.0",
|
|
41
|
+
"@santi020k/lumen-core": "^1.3.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@astrojs/ts-plugin": "^1.10.10",
|
|
@@ -15,13 +15,16 @@ import {
|
|
|
15
15
|
exportThemeCss,
|
|
16
16
|
exportThemeDesignTokens,
|
|
17
17
|
exportThemeFigmaVariables,
|
|
18
|
+
formatLumenLanguageLabel,
|
|
19
|
+
getLumenLocalePair,
|
|
18
20
|
getLumenRichTextShortcut,
|
|
19
21
|
isLumenRichTextToggleCommand,
|
|
22
|
+
type LumenLocaleOption,
|
|
20
23
|
type LumenRichTextChangeDetail,
|
|
21
24
|
type LumenRichTextCommandDetail,
|
|
22
25
|
type LumenThemeTokens,
|
|
23
|
-
lumenTokenNames
|
|
24
|
-
} from '@santi020k/lumen-core'
|
|
26
|
+
lumenTokenNames,
|
|
27
|
+
normalizeLumenLocales } from '@santi020k/lumen-core'
|
|
25
28
|
|
|
26
29
|
/* eslint-disable complexity */
|
|
27
30
|
/* Stateful DOM handlers stay local so Astro consumers do not need a framework runtime. */
|
|
@@ -237,6 +240,18 @@ const initDisclosures = (scope: ParentNode): void => {
|
|
|
237
240
|
nextItem?.focus()
|
|
238
241
|
})
|
|
239
242
|
|
|
243
|
+
if (root.hasAttribute('data-ui-dropdown-menu')) {
|
|
244
|
+
panel.addEventListener('click', event => {
|
|
245
|
+
const item = event.target instanceof Element ?
|
|
246
|
+
event.target.closest<HTMLElement>('[role="menuitem"]') :
|
|
247
|
+
null
|
|
248
|
+
|
|
249
|
+
if (!item || item.matches(':disabled, [aria-disabled="true"]')) return
|
|
250
|
+
|
|
251
|
+
setOpen(trigger, panel, false)
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
|
|
240
255
|
if (root.hasAttribute('data-ui-hover-card')) {
|
|
241
256
|
root.addEventListener('mouseenter', () => setOpen(trigger, panel, true))
|
|
242
257
|
|
|
@@ -933,6 +948,12 @@ const initTabs = (scope: ParentNode): void => {
|
|
|
933
948
|
const panels = [...root.querySelectorAll<HTMLElement>('[role="tabpanel"]')]
|
|
934
949
|
.filter(panel => panel.closest('[data-ui-tabs]') === root)
|
|
935
950
|
|
|
951
|
+
const tabList = tabs[0]?.closest<HTMLElement>('[role="tablist"]')
|
|
952
|
+
|
|
953
|
+
const orientation = tabList?.getAttribute('aria-orientation') === 'vertical' ?
|
|
954
|
+
'vertical' :
|
|
955
|
+
'horizontal'
|
|
956
|
+
|
|
936
957
|
if (!tabs.length || !panels.length) continue
|
|
937
958
|
|
|
938
959
|
const rootId = root.id || `ui-tabs-${++uiTabsCount}`
|
|
@@ -1034,13 +1055,22 @@ const initTabs = (scope: ParentNode): void => {
|
|
|
1034
1055
|
tab.addEventListener('click', () => activate(tab))
|
|
1035
1056
|
|
|
1036
1057
|
tab.addEventListener('keydown', event => {
|
|
1037
|
-
const keys =
|
|
1058
|
+
const keys = orientation === 'vertical' ?
|
|
1059
|
+
['ArrowUp', 'ArrowDown', 'Home', 'End'] :
|
|
1060
|
+
['ArrowLeft', 'ArrowRight', 'Home', 'End']
|
|
1038
1061
|
|
|
1039
1062
|
if (!keys.includes(event.key)) return
|
|
1040
1063
|
|
|
1041
1064
|
event.preventDefault()
|
|
1042
1065
|
|
|
1043
|
-
const nextTab = tabs[
|
|
1066
|
+
const nextTab = tabs[
|
|
1067
|
+
getLoopedIndex(
|
|
1068
|
+
event.key,
|
|
1069
|
+
index,
|
|
1070
|
+
tabs.length,
|
|
1071
|
+
orientation === 'vertical' ? ['ArrowDown'] : ['ArrowRight']
|
|
1072
|
+
)
|
|
1073
|
+
]
|
|
1044
1074
|
|
|
1045
1075
|
if (nextTab) {
|
|
1046
1076
|
activate(nextTab)
|
|
@@ -1282,6 +1312,109 @@ const initThemeToggles = (scope: ParentNode): void => {
|
|
|
1282
1312
|
}
|
|
1283
1313
|
}
|
|
1284
1314
|
|
|
1315
|
+
const isUnknownArray = (value: unknown): value is unknown[] => Array.isArray(value)
|
|
1316
|
+
|
|
1317
|
+
const parseLanguageToggleLocales = (value: string | undefined): readonly LumenLocaleOption[] => {
|
|
1318
|
+
if (!value) return normalizeLumenLocales(undefined)
|
|
1319
|
+
|
|
1320
|
+
try {
|
|
1321
|
+
const parsed: unknown = JSON.parse(value)
|
|
1322
|
+
|
|
1323
|
+
if (!isUnknownArray(parsed)) return normalizeLumenLocales(undefined)
|
|
1324
|
+
|
|
1325
|
+
const locales = parsed.flatMap(item => {
|
|
1326
|
+
if (!item || typeof item !== 'object' || !('label' in item) || !('value' in item)) return []
|
|
1327
|
+
|
|
1328
|
+
const { label, value: localeValue } = item
|
|
1329
|
+
|
|
1330
|
+
return typeof label === 'string' && typeof localeValue === 'string' ?
|
|
1331
|
+
[{ label, value: localeValue }] :
|
|
1332
|
+
[]
|
|
1333
|
+
})
|
|
1334
|
+
|
|
1335
|
+
return normalizeLumenLocales(locales)
|
|
1336
|
+
} catch {
|
|
1337
|
+
return normalizeLumenLocales(undefined)
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
const initLanguageToggles = (scope: ParentNode): void => {
|
|
1342
|
+
for (const control of scope.querySelectorAll<HTMLButtonElement>('[data-ui-language-toggle]')) {
|
|
1343
|
+
if (control.dataset.uiBound === 'true') continue
|
|
1344
|
+
|
|
1345
|
+
control.dataset.uiBound = 'true'
|
|
1346
|
+
|
|
1347
|
+
const locales = parseLanguageToggleLocales(control.dataset.uiLanguageLocales)
|
|
1348
|
+
const storageKey = control.dataset.uiLanguageStorageKey
|
|
1349
|
+
const controlled = control.hasAttribute('data-ui-language-controlled')
|
|
1350
|
+
|
|
1351
|
+
const labelTemplate = control.dataset.uiLanguageLabelTemplate ??
|
|
1352
|
+
'Change language from {current} to {next}'
|
|
1353
|
+
|
|
1354
|
+
const sync = (value: string): void => {
|
|
1355
|
+
const { current, next } = getLumenLocalePair(locales, value)
|
|
1356
|
+
const label = control.querySelector<HTMLElement>('[data-ui-language-label]')
|
|
1357
|
+
|
|
1358
|
+
control.dataset.uiLanguageValue = current.value
|
|
1359
|
+
|
|
1360
|
+
control.dataset.uiLanguageNextValue = next.value
|
|
1361
|
+
|
|
1362
|
+
control.setAttribute('aria-label', formatLumenLanguageLabel(labelTemplate, current, next))
|
|
1363
|
+
|
|
1364
|
+
if (label) {
|
|
1365
|
+
label.lang = next.value
|
|
1366
|
+
|
|
1367
|
+
label.textContent = next.label
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
let storedValue: string | null = null
|
|
1372
|
+
|
|
1373
|
+
if (!controlled && storageKey) {
|
|
1374
|
+
try {
|
|
1375
|
+
storedValue = localStorage.getItem(storageKey)
|
|
1376
|
+
} catch {
|
|
1377
|
+
// Storage can be unavailable in privacy modes.
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
const documentValue = document.documentElement.lang || undefined
|
|
1382
|
+
|
|
1383
|
+
const initialValue = controlled ?
|
|
1384
|
+
control.dataset.uiLanguageValue :
|
|
1385
|
+
storedValue ?? control.dataset.uiLanguageInitialValue ?? documentValue
|
|
1386
|
+
|
|
1387
|
+
const initialPair = getLumenLocalePair(locales, initialValue)
|
|
1388
|
+
|
|
1389
|
+
sync(initialPair.current.value)
|
|
1390
|
+
|
|
1391
|
+
if (!controlled) document.documentElement.lang = initialPair.current.value
|
|
1392
|
+
|
|
1393
|
+
control.addEventListener('click', () => {
|
|
1394
|
+
const { current, next } = getLumenLocalePair(locales, control.dataset.uiLanguageValue)
|
|
1395
|
+
|
|
1396
|
+
if (!controlled) {
|
|
1397
|
+
document.documentElement.lang = next.value
|
|
1398
|
+
|
|
1399
|
+
sync(next.value)
|
|
1400
|
+
|
|
1401
|
+
if (storageKey) {
|
|
1402
|
+
try {
|
|
1403
|
+
localStorage.setItem(storageKey, next.value)
|
|
1404
|
+
} catch {
|
|
1405
|
+
// Storage can be unavailable in privacy modes.
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
control.dispatchEvent(new CustomEvent('ui:language-change', {
|
|
1411
|
+
bubbles: true,
|
|
1412
|
+
detail: { previousValue: current.value, value: next.value }
|
|
1413
|
+
}))
|
|
1414
|
+
})
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1285
1418
|
const getVisibleItems = (items: HTMLElement[]): HTMLElement[] => items.filter(item => !item.hidden &&
|
|
1286
1419
|
item.offsetParent !== null &&
|
|
1287
1420
|
!item.hasAttribute('disabled') &&
|
|
@@ -5460,6 +5593,8 @@ const initUiPrimitives = (scope: ParentNode = document): void => {
|
|
|
5460
5593
|
|
|
5461
5594
|
initThemeToggles(scope)
|
|
5462
5595
|
|
|
5596
|
+
initLanguageToggles(scope)
|
|
5597
|
+
|
|
5463
5598
|
initCommand(scope)
|
|
5464
5599
|
|
|
5465
5600
|
initComboboxes(scope)
|