@santi020k/lumen-astro 0.1.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/LICENSE +21 -0
- package/README.md +142 -0
- package/components/Accordion.astro +27 -0
- package/components/Affix.astro +41 -0
- package/components/Agenda.astro +19 -0
- package/components/Alert.astro +38 -0
- package/components/AlertDialog.astro +41 -0
- package/components/Anchor.astro +43 -0
- package/components/AnimatedLogo.astro +30 -0
- package/components/AnimatedNumber.astro +56 -0
- package/components/AnimatedPortrait.astro +124 -0
- package/components/AspectRatio.astro +39 -0
- package/components/Attachment.astro +46 -0
- package/components/Autocomplete.astro +24 -0
- package/components/Avatar.astro +36 -0
- package/components/BackToTop.astro +30 -0
- package/components/Badge.astro +36 -0
- package/components/Breadcrumb.astro +28 -0
- package/components/Bubble.astro +36 -0
- package/components/Button.astro +52 -0
- package/components/ButtonGroup.astro +27 -0
- package/components/ButtonLink.astro +58 -0
- package/components/Calendar.astro +197 -0
- package/components/Callout.astro +27 -0
- package/components/Card.astro +40 -0
- package/components/Carousel.astro +33 -0
- package/components/Cascader.astro +109 -0
- package/components/Chart.astro +32 -0
- package/components/Checkbox.astro +22 -0
- package/components/Code.astro +96 -0
- package/components/Collapsible.astro +28 -0
- package/components/ColorPicker.astro +16 -0
- package/components/Combobox.astro +67 -0
- package/components/Command.astro +33 -0
- package/components/ContextMenu.astro +44 -0
- package/components/CoverImage.astro +180 -0
- package/components/DataTable.astro +122 -0
- package/components/DatePicker.astro +108 -0
- package/components/DateRangePicker.astro +18 -0
- package/components/Descriptions.astro +41 -0
- package/components/Dialog.astro +40 -0
- package/components/Direction.astro +30 -0
- package/components/Drawer.astro +40 -0
- package/components/DropdownMenu.astro +40 -0
- package/components/Empty.astro +32 -0
- package/components/Eyebrow.astro +27 -0
- package/components/Field.astro +42 -0
- package/components/FileUpload.astro +44 -0
- package/components/FloatingBadge.astro +27 -0
- package/components/FormattedDate.astro +27 -0
- package/components/GradientDivider.astro +24 -0
- package/components/HoverCard.astro +40 -0
- package/components/Icon.astro +50 -0
- package/components/Image.astro +33 -0
- package/components/Input.astro +24 -0
- package/components/InputGroup.astro +27 -0
- package/components/InputOTP.astro +60 -0
- package/components/Item.astro +32 -0
- package/components/Kbd.astro +27 -0
- package/components/Label.astro +18 -0
- package/components/LanguageToggle.astro +30 -0
- package/components/Link.astro +27 -0
- package/components/Marker.astro +33 -0
- package/components/Mentions.astro +47 -0
- package/components/Menubar.astro +40 -0
- package/components/Message.astro +32 -0
- package/components/MessageScroller.astro +32 -0
- package/components/Meter.astro +31 -0
- package/components/NativeSelect.astro +37 -0
- package/components/NavigationMenu.astro +40 -0
- package/components/Note.astro +23 -0
- package/components/NumberField.astro +16 -0
- package/components/Pagination.astro +28 -0
- package/components/Particles.astro +31 -0
- package/components/PhoneInput.astro +86 -0
- package/components/Pill.astro +30 -0
- package/components/Popconfirm.astro +40 -0
- package/components/Popover.astro +40 -0
- package/components/Progress.astro +30 -0
- package/components/Prose.astro +27 -0
- package/components/QRCode.astro +44 -0
- package/components/RadioGroup.astro +28 -0
- package/components/Rating.astro +42 -0
- package/components/Resizable.astro +55 -0
- package/components/RevealGroup.astro +54 -0
- package/components/RichTextEditor.astro +19 -0
- package/components/Schedule.astro +19 -0
- package/components/ScrollArea.astro +32 -0
- package/components/ScrollReveal.astro +50 -0
- package/components/SearchField.astro +16 -0
- package/components/Segmented.astro +56 -0
- package/components/Select.astro +120 -0
- package/components/Separator.astro +31 -0
- package/components/Sheet.astro +40 -0
- package/components/Sidebar.astro +39 -0
- package/components/Skeleton.astro +36 -0
- package/components/SkipLink.astro +27 -0
- package/components/Slider.astro +20 -0
- package/components/Sonner.astro +38 -0
- package/components/SpeedDial.astro +77 -0
- package/components/Spinner.astro +36 -0
- package/components/Stat.astro +34 -0
- package/components/Stepper.astro +57 -0
- package/components/Switch.astro +23 -0
- package/components/Table.astro +32 -0
- package/components/Tabs.astro +33 -0
- package/components/TagGroup.astro +18 -0
- package/components/Textarea.astro +17 -0
- package/components/ThemeBuilder.astro +19 -0
- package/components/ThemeToggle.astro +41 -0
- package/components/TimeField.astro +16 -0
- package/components/Timeline.astro +12 -0
- package/components/TimelineItem.astro +19 -0
- package/components/Toast.astro +50 -0
- package/components/Toggle.astro +25 -0
- package/components/ToggleGroup.astro +28 -0
- package/components/Toolbar.astro +36 -0
- package/components/Tooltip.astro +27 -0
- package/components/Tour.astro +52 -0
- package/components/Transfer.astro +70 -0
- package/components/Tree.astro +19 -0
- package/components/TreeGrid.astro +19 -0
- package/components/TreeSelect.astro +69 -0
- package/components/Typography.astro +27 -0
- package/components/VirtualList.astro +19 -0
- package/components/Watermark.astro +60 -0
- package/index.d.ts +125 -0
- package/index.ts +125 -0
- package/package.json +74 -0
- package/runtime/UIPrimitives.astro +4649 -0
- package/styles/lumen.css +5 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'input'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
list: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { class: classProp = '', className = '', type = 'search', ...rest } = Astro.props
|
|
13
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, ['ui-input', 'ui-autocomplete'
|
|
14
|
+
], classProp, className)
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
{/* No explicit role: an input with a `list` attribute already has native combobox
|
|
18
|
+
semantics, and the browser manages aria-expanded for the datalist popup. */}
|
|
19
|
+
<input
|
|
20
|
+
aria-autocomplete="list"
|
|
21
|
+
class:list={classList}
|
|
22
|
+
type={type}
|
|
23
|
+
{...passthrough}
|
|
24
|
+
/>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'span'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
alt?: string
|
|
10
|
+
fallback?: string
|
|
11
|
+
src?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
class: classProp = '',
|
|
16
|
+
className = '',
|
|
17
|
+
alt = '',
|
|
18
|
+
fallback,
|
|
19
|
+
src,
|
|
20
|
+
...rest
|
|
21
|
+
} = Astro.props
|
|
22
|
+
|
|
23
|
+
const hasDefaultSlot = Astro.slots.has('default')
|
|
24
|
+
const fallbackText = fallback ?? (!src && !hasDefaultSlot ? '?' : undefined)
|
|
25
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
26
|
+
'ui-avatar'
|
|
27
|
+
], classProp, className)
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<span
|
|
31
|
+
class:list={classList}
|
|
32
|
+
{...passthrough}
|
|
33
|
+
>
|
|
34
|
+
{src ? <img alt={alt} src={src} /> : fallbackText && <span aria-hidden={fallback ? undefined : 'true'}>{fallbackText}</span>}
|
|
35
|
+
<slot />
|
|
36
|
+
</span>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'button'> {
|
|
7
|
+
class?: string
|
|
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(rest, [
|
|
19
|
+
'ui-back-to-top'
|
|
20
|
+
], classProp, className)
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
<button
|
|
24
|
+
type={type}
|
|
25
|
+
class:list={classList}
|
|
26
|
+
data-ui-back-to-top
|
|
27
|
+
{...passthrough}
|
|
28
|
+
>
|
|
29
|
+
<slot />
|
|
30
|
+
</button>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'span'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
variant?: 'default' | 'secondary' | 'outline' | 'destructive' | 'success' | 'warning'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
class: classProp = '',
|
|
14
|
+
className = '',
|
|
15
|
+
variant = 'default',
|
|
16
|
+
...rest
|
|
17
|
+
} = Astro.props
|
|
18
|
+
|
|
19
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
20
|
+
'ui-badge',
|
|
21
|
+
variant === 'default' && 'ui-badge--default',
|
|
22
|
+
variant === 'secondary' && 'ui-badge--secondary',
|
|
23
|
+
variant === 'outline' && 'ui-badge--outline',
|
|
24
|
+
variant === 'destructive' && 'ui-badge--destructive',
|
|
25
|
+
variant === 'success' && 'ui-badge--success',
|
|
26
|
+
variant === 'warning' && 'ui-badge--warning'
|
|
27
|
+
], classProp, className)
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<span
|
|
31
|
+
data-variant={variant}
|
|
32
|
+
class:list={classList}
|
|
33
|
+
{...passthrough}
|
|
34
|
+
>
|
|
35
|
+
<slot />
|
|
36
|
+
</span>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'nav'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: classProp = '',
|
|
13
|
+
className = '',
|
|
14
|
+
...rest
|
|
15
|
+
} = Astro.props
|
|
16
|
+
|
|
17
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
18
|
+
'ui-breadcrumb'
|
|
19
|
+
], classProp, className)
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<nav
|
|
23
|
+
aria-label="Breadcrumb"
|
|
24
|
+
class:list={classList}
|
|
25
|
+
{...passthrough}
|
|
26
|
+
>
|
|
27
|
+
<slot />
|
|
28
|
+
</nav>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'article'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
from?: 'user' | 'assistant'
|
|
10
|
+
glass?: boolean | 'subtle' | 'strong'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
class: classProp = '',
|
|
15
|
+
className = '',
|
|
16
|
+
from = 'assistant',
|
|
17
|
+
glass = false,
|
|
18
|
+
...rest
|
|
19
|
+
} = Astro.props
|
|
20
|
+
|
|
21
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
22
|
+
'ui-bubble',
|
|
23
|
+
from === 'user' && 'ui-bubble--user',
|
|
24
|
+
glass && 'ui-bubble--glass',
|
|
25
|
+
glass === 'subtle' && 'ui-glass-subtle',
|
|
26
|
+
glass === 'strong' && 'ui-glass-strong'
|
|
27
|
+
], classProp, className)
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<article
|
|
31
|
+
data-from={from}
|
|
32
|
+
class:list={classList}
|
|
33
|
+
{...passthrough}
|
|
34
|
+
>
|
|
35
|
+
<slot />
|
|
36
|
+
</article>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'button'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
loading?: boolean
|
|
10
|
+
size?: 'default' | 'sm' | 'lg' | 'icon'
|
|
11
|
+
variant?: 'default' | 'destructive' | 'ghost' | 'link' | 'outline' | 'secondary'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
class: classProp = '',
|
|
16
|
+
className = '',
|
|
17
|
+
disabled,
|
|
18
|
+
loading = false,
|
|
19
|
+
size = 'default',
|
|
20
|
+
type = 'button',
|
|
21
|
+
variant = 'default',
|
|
22
|
+
...rest
|
|
23
|
+
} = Astro.props
|
|
24
|
+
|
|
25
|
+
const isDisabled = Boolean(disabled) || loading
|
|
26
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
27
|
+
'ui-button',
|
|
28
|
+
variant === 'default' && 'ui-button--default',
|
|
29
|
+
variant === 'destructive' && 'ui-button--destructive',
|
|
30
|
+
variant === 'ghost' && 'ui-button--ghost',
|
|
31
|
+
variant === 'link' && 'ui-button--link',
|
|
32
|
+
variant === 'outline' && 'ui-button--outline',
|
|
33
|
+
variant === 'secondary' && 'ui-button--secondary',
|
|
34
|
+
size === 'default' && 'ui-button--default-size',
|
|
35
|
+
size === 'sm' && 'ui-button--sm',
|
|
36
|
+
size === 'lg' && 'ui-button--lg',
|
|
37
|
+
size === 'icon' && 'ui-button--icon',
|
|
38
|
+
isDisabled && 'ui-button--disabled',
|
|
39
|
+
loading && 'ui-button--loading'
|
|
40
|
+
], classProp, className)
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
<button
|
|
44
|
+
class:list={classList}
|
|
45
|
+
aria-busy={loading || undefined}
|
|
46
|
+
disabled={isDisabled || undefined}
|
|
47
|
+
type={type}
|
|
48
|
+
{...passthrough}
|
|
49
|
+
>
|
|
50
|
+
{loading && <span aria-hidden="true" class="ui-spinner"></span>}
|
|
51
|
+
<slot />
|
|
52
|
+
</button>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'div'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: classProp = '',
|
|
13
|
+
className = '',
|
|
14
|
+
...rest
|
|
15
|
+
} = Astro.props
|
|
16
|
+
|
|
17
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
18
|
+
'ui-button-group'
|
|
19
|
+
], classProp, className)
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<div
|
|
23
|
+
class:list={classList}
|
|
24
|
+
{...passthrough}
|
|
25
|
+
>
|
|
26
|
+
<slot />
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
import Icon from './Icon.astro'
|
|
4
|
+
|
|
5
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
6
|
+
|
|
7
|
+
interface Props extends HTMLAttributes<'a'> {
|
|
8
|
+
ariaLabel?: string
|
|
9
|
+
class?: string
|
|
10
|
+
className?: string
|
|
11
|
+
href: string
|
|
12
|
+
newTab?: boolean
|
|
13
|
+
shape?: 'default' | 'icon'
|
|
14
|
+
showArrow?: boolean
|
|
15
|
+
variant?: 'ghost' | 'inline' | 'primary' | 'secondary'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
ariaLabel,
|
|
20
|
+
class: classProp = '',
|
|
21
|
+
className = '',
|
|
22
|
+
href,
|
|
23
|
+
newTab = false,
|
|
24
|
+
shape = 'default',
|
|
25
|
+
showArrow = false,
|
|
26
|
+
variant = 'primary',
|
|
27
|
+
...rest
|
|
28
|
+
} = Astro.props
|
|
29
|
+
|
|
30
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
31
|
+
'ui-button-link',
|
|
32
|
+
'group inline-flex cursor-pointer items-center justify-center gap-2 text-sm font-semibold tracking-[0.01em]',
|
|
33
|
+
variant !== 'inline' && 'min-h-11 rounded-full border',
|
|
34
|
+
variant === 'primary' && 'ui-button ui-button--primary',
|
|
35
|
+
variant === 'secondary' && 'ui-button ui-button--secondary',
|
|
36
|
+
variant === 'ghost' && 'ui-button ui-button--ghost',
|
|
37
|
+
variant === 'inline' && 'ui-button ui-button--inline',
|
|
38
|
+
shape === 'icon' && variant !== 'inline' && 'ui-button-link--icon'
|
|
39
|
+
], classProp, className)
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
<a
|
|
43
|
+
aria-label={ariaLabel}
|
|
44
|
+
class:list={classList}
|
|
45
|
+
href={href}
|
|
46
|
+
rel={newTab ? 'noreferrer noopener' : undefined}
|
|
47
|
+
target={newTab ? '_blank' : undefined}
|
|
48
|
+
{...passthrough}
|
|
49
|
+
>
|
|
50
|
+
<slot />
|
|
51
|
+
{showArrow && (
|
|
52
|
+
<Icon
|
|
53
|
+
aria-hidden="true"
|
|
54
|
+
class="size-4 shrink-0 transition-transform duration-200 motion-safe:group-hover:translate-x-0.5 motion-reduce:transition-none"
|
|
55
|
+
name={newTab ? 'arrow-up-right' : 'arrow-right'}
|
|
56
|
+
/>
|
|
57
|
+
)}
|
|
58
|
+
</a>
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends Omit<HTMLAttributes<'div'>, 'value'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
glass?: boolean | 'subtle' | 'strong'
|
|
11
|
+
max?: string
|
|
12
|
+
min?: string
|
|
13
|
+
month?: string
|
|
14
|
+
name?: string
|
|
15
|
+
value?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const isoDatePattern = /^\d{4}-\d{2}-\d{2}$/
|
|
19
|
+
const isoMonthPattern = /^\d{4}-\d{2}$/
|
|
20
|
+
const weekdayFormatter = new Intl.DateTimeFormat('en', { timeZone: 'UTC', weekday: 'short' })
|
|
21
|
+
const monthFormatter = new Intl.DateTimeFormat('en', { month: 'long', timeZone: 'UTC', year: 'numeric' })
|
|
22
|
+
const dayFormatter = new Intl.DateTimeFormat('en', {
|
|
23
|
+
day: 'numeric',
|
|
24
|
+
month: 'long',
|
|
25
|
+
timeZone: 'UTC',
|
|
26
|
+
weekday: 'long',
|
|
27
|
+
year: 'numeric'
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const parseIsoDate = (date: string | undefined): Date | undefined => {
|
|
31
|
+
if (!date || !isoDatePattern.test(date)) return undefined
|
|
32
|
+
|
|
33
|
+
const [year, month, day] = date.split('-').map(Number)
|
|
34
|
+
const parsed = new Date(Date.UTC(year, month - 1, day))
|
|
35
|
+
|
|
36
|
+
return parsed.getUTCFullYear() === year &&
|
|
37
|
+
parsed.getUTCMonth() === month - 1 &&
|
|
38
|
+
parsed.getUTCDate() === day
|
|
39
|
+
? parsed
|
|
40
|
+
: undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const parseIsoMonth = (date: string | undefined): Date | undefined => {
|
|
44
|
+
if (!date || !isoMonthPattern.test(date)) return undefined
|
|
45
|
+
|
|
46
|
+
const [year, month] = date.split('-').map(Number)
|
|
47
|
+
const parsed = new Date(Date.UTC(year, month - 1, 1))
|
|
48
|
+
|
|
49
|
+
return parsed.getUTCFullYear() === year && parsed.getUTCMonth() === month - 1
|
|
50
|
+
? parsed
|
|
51
|
+
: undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const formatIsoDate = (date: Date): string => date.toISOString().slice(0, 10)
|
|
55
|
+
const formatIsoMonth = (date: Date): string => date.toISOString().slice(0, 7)
|
|
56
|
+
const addDays = (date: Date, days: number): Date =>
|
|
57
|
+
new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days))
|
|
58
|
+
const getDaysInMonth = (date: Date): number =>
|
|
59
|
+
new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 0)).getUTCDate()
|
|
60
|
+
const isBefore = (date: Date, limit: Date | undefined): boolean =>
|
|
61
|
+
Boolean(limit && formatIsoDate(date) < formatIsoDate(limit))
|
|
62
|
+
const isAfter = (date: Date, limit: Date | undefined): boolean =>
|
|
63
|
+
Boolean(limit && formatIsoDate(date) > formatIsoDate(limit))
|
|
64
|
+
|
|
65
|
+
const {
|
|
66
|
+
class: classProp = '',
|
|
67
|
+
className = '',
|
|
68
|
+
disabled = false,
|
|
69
|
+
glass = false,
|
|
70
|
+
id,
|
|
71
|
+
max,
|
|
72
|
+
min,
|
|
73
|
+
month,
|
|
74
|
+
name,
|
|
75
|
+
value,
|
|
76
|
+
...rest
|
|
77
|
+
} = Astro.props
|
|
78
|
+
|
|
79
|
+
const today = new Date()
|
|
80
|
+
const todayUtc = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate()))
|
|
81
|
+
const selectedDate = parseIsoDate(value)
|
|
82
|
+
const minDate = parseIsoDate(min)
|
|
83
|
+
const maxDate = parseIsoDate(max)
|
|
84
|
+
const selectedIso = selectedDate ? formatIsoDate(selectedDate) : ''
|
|
85
|
+
const monthDate = parseIsoMonth(month) ??
|
|
86
|
+
(selectedDate ? new Date(Date.UTC(selectedDate.getUTCFullYear(), selectedDate.getUTCMonth(), 1)) : undefined) ??
|
|
87
|
+
new Date(Date.UTC(todayUtc.getUTCFullYear(), todayUtc.getUTCMonth(), 1))
|
|
88
|
+
const calendarId = id ?? `ui-calendar-${Math.random().toString(36).slice(2)}`
|
|
89
|
+
const labelId = `${calendarId}-label`
|
|
90
|
+
const monthIso = formatIsoMonth(monthDate)
|
|
91
|
+
const todayIso = formatIsoDate(todayUtc)
|
|
92
|
+
const firstWeekday = (monthDate.getUTCDay() + 6) % 7
|
|
93
|
+
const firstCell = addDays(monthDate, -firstWeekday)
|
|
94
|
+
const visibleDates = Array.from({ length: 42 }, (_, index) => addDays(firstCell, index))
|
|
95
|
+
const firstSelectableDate = visibleDates.find(date =>
|
|
96
|
+
date.getUTCMonth() === monthDate.getUTCMonth() &&
|
|
97
|
+
!isBefore(date, minDate) &&
|
|
98
|
+
!isAfter(date, maxDate)
|
|
99
|
+
)
|
|
100
|
+
const focusIso = selectedDate && formatIsoMonth(selectedDate) === monthIso
|
|
101
|
+
? selectedIso
|
|
102
|
+
: todayUtc.getUTCMonth() === monthDate.getUTCMonth() && todayUtc.getUTCFullYear() === monthDate.getUTCFullYear()
|
|
103
|
+
? todayIso
|
|
104
|
+
: firstSelectableDate
|
|
105
|
+
? formatIsoDate(firstSelectableDate)
|
|
106
|
+
: formatIsoDate(monthDate)
|
|
107
|
+
const weekdays = Array.from({ length: 7 }, (_, index) =>
|
|
108
|
+
weekdayFormatter.format(new Date(Date.UTC(2026, 0, 5 + index)))
|
|
109
|
+
)
|
|
110
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
111
|
+
'ui-calendar',
|
|
112
|
+
glass && 'ui-calendar--glass',
|
|
113
|
+
glass === 'subtle' && 'ui-glass-subtle',
|
|
114
|
+
glass === 'strong' && 'ui-glass-strong'
|
|
115
|
+
], classProp, className)
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
<div
|
|
119
|
+
aria-disabled={disabled ? 'true' : undefined}
|
|
120
|
+
class:list={classList}
|
|
121
|
+
data-ui-calendar
|
|
122
|
+
data-ui-calendar-initial-month={monthIso}
|
|
123
|
+
data-ui-calendar-max={maxDate ? formatIsoDate(maxDate) : undefined}
|
|
124
|
+
data-ui-calendar-min={minDate ? formatIsoDate(minDate) : undefined}
|
|
125
|
+
data-ui-calendar-month={monthIso}
|
|
126
|
+
data-ui-calendar-value={selectedIso || undefined}
|
|
127
|
+
data-disabled={disabled ? 'true' : undefined}
|
|
128
|
+
data-ui-glass-track={glass ? true : undefined}
|
|
129
|
+
id={calendarId}
|
|
130
|
+
{...passthrough}
|
|
131
|
+
>
|
|
132
|
+
<input
|
|
133
|
+
data-ui-calendar-input
|
|
134
|
+
disabled={disabled || undefined}
|
|
135
|
+
name={name}
|
|
136
|
+
type="hidden"
|
|
137
|
+
value={selectedIso}
|
|
138
|
+
/>
|
|
139
|
+
|
|
140
|
+
<div class="ui-calendar__header">
|
|
141
|
+
<button
|
|
142
|
+
aria-label="Previous month"
|
|
143
|
+
class="ui-calendar__nav"
|
|
144
|
+
data-ui-calendar-prev
|
|
145
|
+
disabled={disabled || undefined}
|
|
146
|
+
type="button"
|
|
147
|
+
>
|
|
148
|
+
<span aria-hidden="true">‹</span>
|
|
149
|
+
</button>
|
|
150
|
+
<strong class="ui-calendar__label" data-ui-calendar-label id={labelId}>{monthFormatter.format(monthDate)}</strong>
|
|
151
|
+
<button
|
|
152
|
+
aria-label="Next month"
|
|
153
|
+
class="ui-calendar__nav"
|
|
154
|
+
data-ui-calendar-next
|
|
155
|
+
disabled={disabled || undefined}
|
|
156
|
+
type="button"
|
|
157
|
+
>
|
|
158
|
+
<span aria-hidden="true">›</span>
|
|
159
|
+
</button>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<table aria-labelledby={labelId} class="ui-calendar__grid" data-ui-calendar-grid role="grid">
|
|
163
|
+
<thead>
|
|
164
|
+
<tr role="row">
|
|
165
|
+
{weekdays.map(weekday => <th role="columnheader" scope="col">{weekday}</th>)}
|
|
166
|
+
</tr>
|
|
167
|
+
</thead>
|
|
168
|
+
<tbody>
|
|
169
|
+
{Array.from({ length: 6 }, (_, rowIndex) => (
|
|
170
|
+
<tr role="row">
|
|
171
|
+
{visibleDates.slice(rowIndex * 7, rowIndex * 7 + 7).map(date => {
|
|
172
|
+
const dateIso = formatIsoDate(date)
|
|
173
|
+
const outside = date.getUTCMonth() !== monthDate.getUTCMonth()
|
|
174
|
+
const unavailable = disabled || isBefore(date, minDate) || isAfter(date, maxDate)
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<td
|
|
178
|
+
aria-disabled={unavailable ? 'true' : undefined}
|
|
179
|
+
aria-label={dayFormatter.format(date)}
|
|
180
|
+
aria-selected={selectedIso === dateIso ? 'true' : 'false'}
|
|
181
|
+
data-date={dateIso}
|
|
182
|
+
data-outside={outside ? 'true' : undefined}
|
|
183
|
+
data-selected={selectedIso === dateIso ? 'true' : undefined}
|
|
184
|
+
data-today={todayIso === dateIso ? 'true' : undefined}
|
|
185
|
+
data-ui-calendar-day
|
|
186
|
+
role="gridcell"
|
|
187
|
+
tabindex={!unavailable && focusIso === dateIso ? '0' : '-1'}
|
|
188
|
+
>
|
|
189
|
+
{date.getUTCDate()}
|
|
190
|
+
</td>
|
|
191
|
+
)
|
|
192
|
+
})}
|
|
193
|
+
</tr>
|
|
194
|
+
))}
|
|
195
|
+
</tbody>
|
|
196
|
+
</table>
|
|
197
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'aside'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: classProp = '',
|
|
13
|
+
className = '',
|
|
14
|
+
...rest
|
|
15
|
+
} = Astro.props
|
|
16
|
+
|
|
17
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
18
|
+
'ui-callout'
|
|
19
|
+
], classProp, className)
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<aside
|
|
23
|
+
class:list={classList}
|
|
24
|
+
{...passthrough}
|
|
25
|
+
>
|
|
26
|
+
<slot />
|
|
27
|
+
</aside>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'div'> {
|
|
7
|
+
as?: 'article' | 'div' | 'section'
|
|
8
|
+
class?: string
|
|
9
|
+
className?: string
|
|
10
|
+
glass?: boolean | 'subtle' | 'strong'
|
|
11
|
+
variant?: 'default' | 'glass' | 'interactive' | 'muted'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
as: Tag = 'div',
|
|
16
|
+
class: classProp = '',
|
|
17
|
+
className = '',
|
|
18
|
+
glass = false,
|
|
19
|
+
variant = 'default',
|
|
20
|
+
...rest
|
|
21
|
+
} = Astro.props
|
|
22
|
+
|
|
23
|
+
const isGlass = glass || variant === 'glass'
|
|
24
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
25
|
+
'ui-card',
|
|
26
|
+
variant === 'muted' && 'ui-card--muted',
|
|
27
|
+
variant === 'interactive' && 'ui-card--interactive',
|
|
28
|
+
isGlass && 'ui-card--glass',
|
|
29
|
+
glass === 'subtle' && 'ui-glass-subtle',
|
|
30
|
+
glass === 'strong' && 'ui-glass-strong'
|
|
31
|
+
], classProp, className)
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
<Tag
|
|
35
|
+
data-variant={variant}
|
|
36
|
+
class:list={classList}
|
|
37
|
+
{...passthrough}
|
|
38
|
+
>
|
|
39
|
+
<slot />
|
|
40
|
+
</Tag>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { resolveLumenAstroProps } from '@santi020k/lumen-core'
|
|
3
|
+
|
|
4
|
+
import type { HTMLAttributes } from 'astro/types'
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<'section'> {
|
|
7
|
+
class?: string
|
|
8
|
+
className?: string
|
|
9
|
+
glass?: boolean | 'subtle' | 'strong'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
class: classProp = '',
|
|
14
|
+
className = '',
|
|
15
|
+
glass = false,
|
|
16
|
+
...rest
|
|
17
|
+
} = Astro.props
|
|
18
|
+
|
|
19
|
+
const { classList, passthrough } = resolveLumenAstroProps(rest, [
|
|
20
|
+
'ui-carousel',
|
|
21
|
+
glass && 'ui-carousel--glass',
|
|
22
|
+
glass === 'subtle' && 'ui-glass-subtle',
|
|
23
|
+
glass === 'strong' && 'ui-glass-strong'
|
|
24
|
+
], classProp, className)
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<section
|
|
28
|
+
data-ui-carousel
|
|
29
|
+
class:list={classList}
|
|
30
|
+
{...passthrough}
|
|
31
|
+
>
|
|
32
|
+
<slot />
|
|
33
|
+
</section>
|