@santi020k/lumen-astro 0.4.0 → 1.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 (158) hide show
  1. package/README.md +44 -6
  2. package/components/Accordion.astro +4 -4
  3. package/components/Affix.astro +2 -2
  4. package/components/Agenda.astro +3 -2
  5. package/components/Alert.astro +9 -9
  6. package/components/AlertDialog.astro +7 -13
  7. package/components/Anchor.astro +18 -4
  8. package/components/AnimatedLogo.astro +2 -1
  9. package/components/AnimatedNumber.astro +5 -2
  10. package/components/AnimatedPortrait.astro +46 -36
  11. package/components/AspectRatio.astro +10 -8
  12. package/components/Attachment.astro +24 -24
  13. package/components/Autocomplete.astro +3 -2
  14. package/components/Avatar.astro +4 -3
  15. package/components/BackToTop.astro +2 -2
  16. package/components/Badge.astro +9 -9
  17. package/components/BarChart.astro +76 -50
  18. package/components/Breadcrumb.astro +3 -3
  19. package/components/Bubble.astro +7 -7
  20. package/components/Button.astro +18 -16
  21. package/components/ButtonGroup.astro +3 -3
  22. package/components/ButtonLink.astro +9 -3
  23. package/components/Calendar.astro +43 -31
  24. package/components/Callout.astro +2 -2
  25. package/components/Card.astro +12 -10
  26. package/components/CardContent.astro +26 -0
  27. package/components/CardDescription.astro +26 -0
  28. package/components/CardFooter.astro +26 -0
  29. package/components/CardHeader.astro +26 -0
  30. package/components/CardTitle.astro +26 -0
  31. package/components/Carousel.astro +6 -6
  32. package/components/Cascader.astro +10 -4
  33. package/components/Chart.astro +9 -6
  34. package/components/Checkbox.astro +3 -2
  35. package/components/CheckboxGroup.astro +39 -0
  36. package/components/Code.astro +55 -52
  37. package/components/CodeTabs.astro +6 -6
  38. package/components/Collapsible.astro +3 -3
  39. package/components/ColorPicker.astro +3 -2
  40. package/components/Combobox.astro +4 -5
  41. package/components/Command.astro +6 -6
  42. package/components/Container.astro +29 -0
  43. package/components/ContextMenu.astro +7 -13
  44. package/components/CopyButton.astro +55 -0
  45. package/components/CoverImage.astro +19 -9
  46. package/components/DataTable.astro +50 -49
  47. package/components/DatePicker.astro +20 -17
  48. package/components/DateRangePicker.astro +3 -2
  49. package/components/Descriptions.astro +3 -3
  50. package/components/Dialog.astro +8 -14
  51. package/components/Direction.astro +3 -3
  52. package/components/Drawer.astro +7 -13
  53. package/components/DropdownMenu.astro +7 -13
  54. package/components/Empty.astro +6 -6
  55. package/components/ErrorSummary.astro +54 -0
  56. package/components/Eyebrow.astro +2 -2
  57. package/components/Field.astro +7 -6
  58. package/components/FieldError.astro +29 -0
  59. package/components/FileUpload.astro +2 -2
  60. package/components/FloatingBadge.astro +2 -2
  61. package/components/Form.astro +33 -0
  62. package/components/FormattedDate.astro +2 -2
  63. package/components/GradientDivider.astro +2 -2
  64. package/components/Grid.astro +46 -0
  65. package/components/HoverCard.astro +7 -13
  66. package/components/Icon.astro +8 -6
  67. package/components/Image.astro +2 -1
  68. package/components/Input.astro +6 -5
  69. package/components/InputGroup.astro +3 -3
  70. package/components/InputOTP.astro +5 -3
  71. package/components/Item.astro +11 -8
  72. package/components/Kbd.astro +3 -3
  73. package/components/Label.astro +3 -2
  74. package/components/LanguageToggle.astro +2 -2
  75. package/components/LineChart.astro +52 -35
  76. package/components/Link.astro +6 -5
  77. package/components/ListBox.astro +154 -0
  78. package/components/Marker.astro +6 -6
  79. package/components/Mentions.astro +30 -10
  80. package/components/Menubar.astro +7 -13
  81. package/components/Message.astro +5 -5
  82. package/components/MessageScroller.astro +6 -6
  83. package/components/Meter.astro +1 -0
  84. package/components/NativeSelect.astro +4 -5
  85. package/components/NavigationMenu.astro +9 -14
  86. package/components/Note.astro +1 -1
  87. package/components/NumberField.astro +3 -2
  88. package/components/Pagination.astro +3 -3
  89. package/components/Particles.astro +13 -3
  90. package/components/PasswordField.astro +73 -0
  91. package/components/PhoneInput.astro +5 -7
  92. package/components/PieChart.astro +16 -13
  93. package/components/Pill.astro +11 -8
  94. package/components/Popconfirm.astro +8 -3
  95. package/components/Popover.astro +7 -13
  96. package/components/Progress.astro +12 -4
  97. package/components/Prose.astro +2 -2
  98. package/components/QRCode.astro +13 -5
  99. package/components/RadioGroup.astro +3 -3
  100. package/components/Rating.astro +3 -1
  101. package/components/Resizable.astro +8 -7
  102. package/components/RevealGroup.astro +4 -2
  103. package/components/RichTextEditor.astro +3 -2
  104. package/components/Schedule.astro +3 -2
  105. package/components/ScrollArea.astro +6 -6
  106. package/components/ScrollProgress.astro +2 -2
  107. package/components/ScrollReveal.astro +4 -2
  108. package/components/SearchField.astro +3 -2
  109. package/components/Segmented.astro +4 -6
  110. package/components/Select.astro +23 -14
  111. package/components/Separator.astro +5 -5
  112. package/components/Sheet.astro +7 -13
  113. package/components/Sidebar.astro +9 -14
  114. package/components/Skeleton.astro +4 -3
  115. package/components/SkipLink.astro +2 -2
  116. package/components/Slider.astro +3 -2
  117. package/components/Sonner.astro +3 -3
  118. package/components/Sparkline.astro +9 -9
  119. package/components/SpeedDial.astro +3 -3
  120. package/components/Spinner.astro +4 -3
  121. package/components/Stack.astro +46 -0
  122. package/components/Stat.astro +6 -5
  123. package/components/StatDescription.astro +16 -0
  124. package/components/StatIcon.astro +15 -0
  125. package/components/StatLabel.astro +16 -0
  126. package/components/StatTrend.astro +28 -0
  127. package/components/StatValue.astro +16 -0
  128. package/components/Stepper.astro +11 -8
  129. package/components/Switch.astro +3 -2
  130. package/components/Table.astro +7 -6
  131. package/components/Tabs.astro +6 -6
  132. package/components/TagGroup.astro +3 -2
  133. package/components/Textarea.astro +3 -2
  134. package/components/ThemeBuilder.astro +3 -2
  135. package/components/ThemeToggle.astro +5 -3
  136. package/components/TimeField.astro +3 -2
  137. package/components/Timeline.astro +2 -1
  138. package/components/TimelineItem.astro +2 -1
  139. package/components/Toast.astro +10 -16
  140. package/components/Toggle.astro +3 -2
  141. package/components/ToggleGroup.astro +3 -3
  142. package/components/Toolbar.astro +2 -2
  143. package/components/Tooltip.astro +3 -3
  144. package/components/Tour.astro +3 -3
  145. package/components/Transfer.astro +16 -5
  146. package/components/Tree.astro +3 -2
  147. package/components/TreeGrid.astro +3 -2
  148. package/components/TreeSelect.astro +10 -4
  149. package/components/Typography.astro +3 -3
  150. package/components/VirtualList.astro +3 -2
  151. package/components/VisuallyHidden.astro +27 -0
  152. package/components/Watermark.astro +10 -6
  153. package/forms.ts +46 -0
  154. package/index.ts +26 -0
  155. package/package.json +12 -8
  156. package/runtime/UIPrimitives.astro +3755 -3176
  157. package/runtime/controllers/motion.ts +13 -16
  158. package/index.d.ts +0 -131
@@ -1,28 +1,33 @@
1
1
  ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
2
4
  import {
3
5
  createLumenBarGeometry,
4
6
  getLumenChartTicks,
7
+ getLumenChartToneClassName,
5
8
  hasLumenChartData,
6
9
  type LumenBarChartLayout,
7
10
  type LumenChartOrientation,
8
11
  type LumenChartSeries,
9
- resolveLumenChartTone,
10
12
  scaleLumenChartValue
11
13
  } from '@santi020k/lumen-core'
12
14
 
13
- import type { HTMLAttributes } from 'astro/types'
14
-
15
15
  import Chart from './Chart.astro'
16
16
 
17
17
  interface Props extends HTMLAttributes<'figure'> {
18
18
  caption?: string
19
+ categoryWidth?: number
19
20
  class?: string | null
20
21
  className?: string
21
22
  description?: string
23
+ emptyLabel?: string
24
+ formatCategory?: (category: number | string) => string
25
+ formatValue?: (value: number) => string
22
26
  glass?: boolean | 'subtle' | 'strong'
23
27
  heading?: string
24
28
  layout?: LumenBarChartLayout
25
29
  orientation?: LumenChartOrientation
30
+ presentation?: 'bare' | 'default'
26
31
  series: readonly LumenChartSeries[]
27
32
  showLegend?: boolean
28
33
  showTable?: boolean
@@ -30,9 +35,13 @@ interface Props extends HTMLAttributes<'figure'> {
30
35
 
31
36
  const {
32
37
  caption,
38
+ categoryWidth,
33
39
  class: classProp = '',
34
40
  className = '',
35
41
  description,
42
+ emptyLabel = 'No chart data available.',
43
+ formatCategory = String,
44
+ formatValue = String,
36
45
  glass = false,
37
46
  heading,
38
47
  layout = 'grouped',
@@ -42,17 +51,39 @@ const {
42
51
  showTable = true,
43
52
  ...rest
44
53
  } = Astro.props
45
- const geometry = createLumenBarGeometry(series, { layout, orientation })
54
+
55
+ const geometry = createLumenBarGeometry(series, { categoryWidth, layout, orientation })
46
56
  const hasData = hasLumenChartData(series)
47
57
  const ticks = getLumenChartTicks(geometry.domain)
48
- const categories = geometry.categories.map(category => category.label)
49
- const valueFor = (item: LumenChartSeries, category: number | string) =>
50
- item.data.find(datum => datum.x === category)?.y ?? null
51
- const labelFor = (item: LumenChartSeries, category: number | string) =>
52
- item.data.find(datum => datum.x === category)?.label
53
- const margin = orientation === 'horizontal'
54
- ? { bottom: 24, left: 112, right: 20, top: 16 }
55
- : { bottom: 52, left: 52, right: 16, top: 16 }
58
+ const categories = geometry.categories.map(category => category.category)
59
+
60
+ const datumFor = (item: LumenChartSeries, category: number | string) => (
61
+ item.data.find(datum => datum.x === category)
62
+ )
63
+
64
+ const valueFor = (item: LumenChartSeries, category: number | string) => (
65
+ datumFor(item, category)?.y ?? null
66
+ )
67
+
68
+ const labelFor = (item: LumenChartSeries, category: number | string) => (
69
+ datumFor(item, category)?.label
70
+ )
71
+
72
+ const formattedValueFor = (item: LumenChartSeries, category: number | string) => {
73
+ const value = valueFor(item, category)
74
+
75
+ return value === null ? 'Not available' : formatValue(value)
76
+ }
77
+
78
+ const categoryLabelFor = (category: number | string) => {
79
+ const label = series.flatMap(item => item.data).find(datum => datum.x === category)?.xLabel
80
+
81
+ return label ?? formatCategory(category)
82
+ }
83
+
84
+ const margin = orientation === 'horizontal' ?
85
+ { bottom: 24, left: Math.max(64, Math.min(240, categoryWidth ?? 112)), right: 20, top: 16 } :
86
+ { bottom: 52, left: 52, right: 16, top: 16 }
56
87
  ---
57
88
 
58
89
  <Chart
@@ -67,7 +98,7 @@ const margin = orientation === 'horizontal'
67
98
  showLegend && hasData && (
68
99
  <ul class="ui-chart__legend" aria-label="Chart legend">
69
100
  {series.map((item, index) => (
70
- <li class={`ui-chart-tone--${resolveLumenChartTone(item.tone, index)}`}>
101
+ <li class={getLumenChartToneClassName(item.tone, index)}>
71
102
  <span aria-hidden="true" />
72
103
  {item.label}
73
104
  </li>
@@ -75,7 +106,7 @@ const margin = orientation === 'horizontal'
75
106
  </ul>
76
107
  )
77
108
  }
78
- {!hasData && <p class="ui-chart__empty" role="status">No chart data available.</p>}
109
+ {!hasData && <p class="ui-chart__empty" role="status">{emptyLabel}</p>}
79
110
  <div class="ui-chart__plot" hidden={!hasData}>
80
111
  <svg
81
112
  aria-hidden="true"
@@ -84,43 +115,39 @@ const margin = orientation === 'horizontal'
84
115
  >
85
116
  <g class="ui-chart__grid">
86
117
  {
87
- ticks.map((tick) => {
118
+ ticks.map(tick => {
88
119
  const coordinate =
89
- orientation === 'horizontal'
90
- ? scaleLumenChartValue(
91
- tick,
92
- geometry.domain,
93
- margin.left,
94
- geometry.width - margin.right
95
- )
96
- : scaleLumenChartValue(
97
- tick,
98
- geometry.domain,
99
- geometry.height - margin.bottom,
100
- margin.top
101
- )
120
+ orientation === 'horizontal' ?
121
+ scaleLumenChartValue(
122
+ tick, geometry.domain, margin.left, geometry.width - margin.right
123
+ ) :
124
+ scaleLumenChartValue(
125
+ tick, geometry.domain, geometry.height - margin.bottom, margin.top
126
+ )
102
127
 
103
- return orientation === 'horizontal' ? (
104
- <line
105
- x1={coordinate}
106
- x2={coordinate}
107
- y1={margin.top}
108
- y2={geometry.height - margin.bottom}
109
- />
110
- ) : (
111
- <line
112
- x1={margin.left}
113
- x2={geometry.width - margin.right}
114
- y1={coordinate}
115
- y2={coordinate}
116
- />
117
- )
128
+ return orientation === 'horizontal' ?
129
+ (
130
+ <line
131
+ x1={coordinate}
132
+ x2={coordinate}
133
+ y1={margin.top}
134
+ y2={geometry.height - margin.bottom}
135
+ />
136
+ ) :
137
+ (
138
+ <line
139
+ x1={margin.left}
140
+ x2={geometry.width - margin.right}
141
+ y1={coordinate}
142
+ y2={coordinate}
143
+ />
144
+ )
118
145
  })
119
146
  }
120
147
  </g>
121
148
  <g class="ui-chart__axis-labels">
122
149
  {
123
- geometry.categories.map((category) => (
150
+ geometry.categories.map(category => (
124
151
  <text
125
152
  dominant-baseline={orientation === 'horizontal' ? 'middle' : undefined}
126
153
  text-anchor={orientation === 'horizontal' ? 'end' : 'middle'}
@@ -134,9 +161,9 @@ const margin = orientation === 'horizontal'
134
161
  </g>
135
162
  <g class="ui-bar-chart__marks">
136
163
  {
137
- geometry.marks.map((mark) => (
164
+ geometry.marks.map(mark => (
138
165
  <rect
139
- class={`ui-chart-tone--${mark.tone}`}
166
+ class={getLumenChartToneClassName(mark.tone)}
140
167
  height={mark.height}
141
168
  rx="4"
142
169
  width={mark.width}
@@ -144,7 +171,7 @@ const margin = orientation === 'horizontal'
144
171
  y={mark.y}
145
172
  >
146
173
  <title>
147
- {`${String(mark.category)} · ${mark.seriesLabel}: ${mark.value}`}
174
+ {`${categoryLabelFor(mark.category)} · ${mark.seriesLabel}: ${formatValue(mark.value)}`}
148
175
  </title>
149
176
  </rect>
150
177
  ))
@@ -167,11 +194,10 @@ const margin = orientation === 'horizontal'
167
194
  <tbody>
168
195
  {categories.map(category => (
169
196
  <tr>
170
- <th scope="row">{String(category)}</th>
197
+ <th scope="row">{categoryLabelFor(category)}</th>
171
198
  {series.map(item => (
172
199
  <td>
173
- {labelFor(item, category) ??
174
- String(valueFor(item, category) ?? 'Not available')}
200
+ {labelFor(item, category) ?? formattedValueFor(item, category)}
175
201
  </td>
176
202
  ))}
177
203
  </tr>
@@ -1,8 +1,8 @@
1
1
  ---
2
- import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
-
4
2
  import type { HTMLAttributes } from 'astro/types'
5
3
 
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
6
  interface Props extends HTMLAttributes<'nav'> {
7
7
  class?: string | null
8
8
  className?: string
@@ -15,7 +15,7 @@ const {
15
15
  } = Astro.props
16
16
 
17
17
  const { classList, passthrough } = resolveLumenAstroProps(rest, [
18
- 'ui-breadcrumb'
18
+ 'ui-breadcrumb'
19
19
  ], classProp, className)
20
20
  ---
21
21
 
@@ -1,8 +1,8 @@
1
1
  ---
2
- import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
-
4
2
  import type { HTMLAttributes } from 'astro/types'
5
3
 
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
6
  interface Props extends HTMLAttributes<'article'> {
7
7
  class?: string | null
8
8
  className?: string
@@ -19,11 +19,11 @@ const {
19
19
  } = Astro.props
20
20
 
21
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'
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
27
  ], classProp, className)
28
28
  ---
29
29
 
@@ -1,8 +1,8 @@
1
1
  ---
2
- import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
-
4
2
  import type { HTMLAttributes } from 'astro/types'
5
3
 
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
6
  interface Props extends HTMLAttributes<'button'> {
7
7
  class?: string | null
8
8
  className?: string
@@ -23,26 +23,28 @@ const {
23
23
  } = Astro.props
24
24
 
25
25
  const isDisabled = Boolean(disabled) || loading
26
+
26
27
  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'
28
+ 'ui-button',
29
+ variant === 'default' && 'ui-button--default',
30
+ variant === 'destructive' && 'ui-button--destructive',
31
+ variant === 'ghost' && 'ui-button--ghost',
32
+ variant === 'link' && 'ui-button--link',
33
+ variant === 'outline' && 'ui-button--outline',
34
+ variant === 'secondary' && 'ui-button--secondary',
35
+ size === 'default' && 'ui-button--default-size',
36
+ size === 'sm' && 'ui-button--sm',
37
+ size === 'lg' && 'ui-button--lg',
38
+ size === 'icon' && 'ui-button--icon',
39
+ isDisabled && 'ui-button--disabled',
40
+ loading && 'ui-button--loading'
40
41
  ], classProp, className)
41
42
  ---
42
43
 
43
44
  <button
44
- class:list={classList}
45
45
  aria-busy={loading || undefined}
46
+ class:list={classList}
47
+ data-slot="button"
46
48
  disabled={isDisabled || undefined}
47
49
  type={type}
48
50
  {...passthrough}
@@ -1,8 +1,8 @@
1
1
  ---
2
- import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
-
4
2
  import type { HTMLAttributes } from 'astro/types'
5
3
 
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
6
  interface Props extends HTMLAttributes<'div'> {
7
7
  class?: string | null
8
8
  className?: string
@@ -15,7 +15,7 @@ const {
15
15
  } = Astro.props
16
16
 
17
17
  const { classList, passthrough } = resolveLumenAstroProps(rest, [
18
- 'ui-button-group'
18
+ 'ui-button-group'
19
19
  ], classProp, className)
20
20
  ---
21
21
 
@@ -1,8 +1,9 @@
1
1
  ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
2
4
  import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
- import Icon from './Icon.astro'
4
5
 
5
- import type { HTMLAttributes } from 'astro/types'
6
+ import Icon from './Icon.astro'
6
7
 
7
8
  interface Props extends HTMLAttributes<'a'> {
8
9
  ariaLabel?: string
@@ -43,6 +44,7 @@ const { classList, passthrough } = resolveLumenAstroProps(rest, [
43
44
  <a
44
45
  aria-label={ariaLabel}
45
46
  class:list={classList}
47
+ data-slot="button-link"
46
48
  href={href}
47
49
  rel={newTab ? 'noreferrer noopener' : undefined}
48
50
  target={newTab ? '_blank' : undefined}
@@ -52,7 +54,11 @@ const { classList, passthrough } = resolveLumenAstroProps(rest, [
52
54
  {showArrow && (
53
55
  <Icon
54
56
  aria-hidden="true"
55
- class="size-4 shrink-0 transition-transform duration-200 motion-safe:group-hover:translate-x-0.5 motion-reduce:transition-none"
57
+ class="
58
+ size-4 shrink-0 transition-transform duration-200
59
+ motion-safe:group-hover:translate-x-0.5
60
+ motion-reduce:transition-none
61
+ "
56
62
  name={newTab ? 'arrow-up-right' : 'arrow-right'}
57
63
  />
58
64
  )}
@@ -1,8 +1,8 @@
1
1
  ---
2
- import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
-
4
2
  import type { HTMLAttributes } from 'astro/types'
5
3
 
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
6
  interface Props extends Omit<HTMLAttributes<'div'>, 'value'> {
7
7
  class?: string | null
8
8
  className?: string
@@ -19,6 +19,7 @@ const isoDatePattern = /^\d{4}-\d{2}-\d{2}$/
19
19
  const isoMonthPattern = /^\d{4}-\d{2}$/
20
20
  const weekdayFormatter = new Intl.DateTimeFormat('en', { timeZone: 'UTC', weekday: 'short' })
21
21
  const monthFormatter = new Intl.DateTimeFormat('en', { month: 'long', timeZone: 'UTC', year: 'numeric' })
22
+
22
23
  const dayFormatter = new Intl.DateTimeFormat('en', {
23
24
  day: 'numeric',
24
25
  month: 'long',
@@ -35,9 +36,9 @@ const parseIsoDate = (date: string | undefined): Date | undefined => {
35
36
 
36
37
  return parsed.getUTCFullYear() === year &&
37
38
  parsed.getUTCMonth() === month - 1 &&
38
- parsed.getUTCDate() === day
39
- ? parsed
40
- : undefined
39
+ parsed.getUTCDate() === day ?
40
+ parsed :
41
+ undefined
41
42
  }
42
43
 
43
44
  const parseIsoMonth = (date: string | undefined): Date | undefined => {
@@ -46,21 +47,27 @@ const parseIsoMonth = (date: string | undefined): Date | undefined => {
46
47
  const [year, month] = date.split('-').map(Number)
47
48
  const parsed = new Date(Date.UTC(year, month - 1, 1))
48
49
 
49
- return parsed.getUTCFullYear() === year && parsed.getUTCMonth() === month - 1
50
- ? parsed
51
- : undefined
50
+ return parsed.getUTCFullYear() === year && parsed.getUTCMonth() === month - 1 ?
51
+ parsed :
52
+ undefined
52
53
  }
53
54
 
54
55
  const formatIsoDate = (date: Date): string => date.toISOString().slice(0, 10)
55
56
  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 =>
57
+
58
+ const addDays = (date: Date, days: number): Date => {
59
+ const utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days)
60
+
61
+ return new Date(utc)
62
+ }
63
+
64
+ const isBefore = (date: Date, limit: Date | undefined): boolean => (
61
65
  Boolean(limit && formatIsoDate(date) < formatIsoDate(limit))
62
- const isAfter = (date: Date, limit: Date | undefined): boolean =>
66
+ )
67
+
68
+ const isAfter = (date: Date, limit: Date | undefined): boolean => (
63
69
  Boolean(limit && formatIsoDate(date) > formatIsoDate(limit))
70
+ )
64
71
 
65
72
  const {
66
73
  class: classProp = '',
@@ -82,9 +89,11 @@ const selectedDate = parseIsoDate(value)
82
89
  const minDate = parseIsoDate(min)
83
90
  const maxDate = parseIsoDate(max)
84
91
  const selectedIso = selectedDate ? formatIsoDate(selectedDate) : ''
92
+
85
93
  const monthDate = parseIsoMonth(month) ??
86
94
  (selectedDate ? new Date(Date.UTC(selectedDate.getUTCFullYear(), selectedDate.getUTCMonth(), 1)) : undefined) ??
87
95
  new Date(Date.UTC(todayUtc.getUTCFullYear(), todayUtc.getUTCMonth(), 1))
96
+
88
97
  const calendarId = id ?? `ui-calendar-${Math.random().toString(36).slice(2)}`
89
98
  const labelId = `${calendarId}-label`
90
99
  const monthIso = formatIsoMonth(monthDate)
@@ -92,26 +101,29 @@ const todayIso = formatIsoDate(todayUtc)
92
101
  const firstWeekday = (monthDate.getUTCDay() + 6) % 7
93
102
  const firstCell = addDays(monthDate, -firstWeekday)
94
103
  const visibleDates = Array.from({ length: 42 }, (_, index) => addDays(firstCell, index))
95
- const firstSelectableDate = visibleDates.find(date =>
96
- date.getUTCMonth() === monthDate.getUTCMonth() &&
104
+
105
+ const firstSelectableDate = visibleDates.find(date => date.getUTCMonth() === monthDate.getUTCMonth() &&
97
106
  !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)))
107
+ !isAfter(date, maxDate))
108
+
109
+ let focusIso = formatIsoDate(monthDate)
110
+
111
+ if (selectedDate && formatIsoMonth(selectedDate) === monthIso) focusIso = selectedIso
112
+ else if (
113
+ todayUtc.getUTCMonth() === monthDate.getUTCMonth() &&
114
+ todayUtc.getUTCFullYear() === monthDate.getUTCFullYear()
115
+ ) focusIso = todayIso
116
+ else if (firstSelectableDate) focusIso = formatIsoDate(firstSelectableDate)
117
+
118
+ const weekdays = Array.from(
119
+ { length: 7 }, (_, index) => weekdayFormatter.format(new Date(Date.UTC(2026, 0, 5 + index)))
109
120
  )
121
+
110
122
  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'
123
+ 'ui-calendar',
124
+ glass && 'ui-calendar--glass',
125
+ glass === 'subtle' && 'ui-glass-subtle',
126
+ glass === 'strong' && 'ui-glass-strong'
115
127
  ], classProp, className)
116
128
  ---
117
129
 
@@ -1,8 +1,8 @@
1
1
  ---
2
- import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
-
4
2
  import type { HTMLAttributes } from 'astro/types'
5
3
 
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
6
  interface Props extends HTMLAttributes<'aside'> {
7
7
  class?: string | null
8
8
  className?: string
@@ -1,8 +1,8 @@
1
1
  ---
2
- import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
-
4
2
  import type { HTMLAttributes } from 'astro/types'
5
3
 
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
6
  interface Props extends HTMLAttributes<'div'> {
7
7
  as?: 'article' | 'div' | 'section'
8
8
  class?: string | null
@@ -21,20 +21,22 @@ const {
21
21
  } = Astro.props
22
22
 
23
23
  const isGlass = glass || variant === 'glass'
24
+
24
25
  const { classList, passthrough } = resolveLumenAstroProps(rest, [
25
- 'ui-card',
26
- variant === 'unstyled' && 'ui-card--unstyled',
27
- variant === 'muted' && 'ui-card--muted',
28
- variant === 'interactive' && 'ui-card--interactive',
29
- isGlass && 'ui-card--glass',
30
- glass === 'subtle' && 'ui-glass-subtle',
31
- glass === 'strong' && 'ui-glass-strong'
26
+ 'ui-card',
27
+ variant === 'unstyled' && 'ui-card--unstyled',
28
+ variant === 'muted' && 'ui-card--muted',
29
+ variant === 'interactive' && 'ui-card--interactive',
30
+ isGlass && 'ui-card--glass',
31
+ glass === 'subtle' && 'ui-glass-subtle',
32
+ glass === 'strong' && 'ui-glass-strong'
32
33
  ], classProp, className)
33
34
  ---
34
35
 
35
36
  <Tag
36
- data-variant={variant}
37
37
  class:list={classList}
38
+ data-slot="card"
39
+ data-variant={variant}
38
40
  {...passthrough}
39
41
  >
40
42
  <slot />
@@ -0,0 +1,26 @@
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
+ interface Props extends HTMLAttributes<'div'> {
7
+ as?: 'div' | 'section'
8
+ class?: string | null
9
+ className?: string
10
+ }
11
+
12
+ const {
13
+ as: Tag = 'div',
14
+ class: classProp = '',
15
+ className = '',
16
+ ...rest
17
+ } = Astro.props
18
+
19
+ const { classList, passthrough } = resolveLumenAstroProps(
20
+ rest, ['ui-card__content'], classProp, className
21
+ )
22
+ ---
23
+
24
+ <Tag class:list={classList} data-slot="card-content" {...passthrough}>
25
+ <slot />
26
+ </Tag>
@@ -0,0 +1,26 @@
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
+ interface Props extends HTMLAttributes<'div'> {
7
+ as?: 'div' | 'p'
8
+ class?: string | null
9
+ className?: string
10
+ }
11
+
12
+ const {
13
+ as: Tag = 'div',
14
+ class: classProp = '',
15
+ className = '',
16
+ ...rest
17
+ } = Astro.props
18
+
19
+ const { classList, passthrough } = resolveLumenAstroProps(
20
+ rest, ['ui-card__description'], classProp, className
21
+ )
22
+ ---
23
+
24
+ <Tag class:list={classList} data-slot="card-description" {...passthrough}>
25
+ <slot />
26
+ </Tag>
@@ -0,0 +1,26 @@
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
+ interface Props extends HTMLAttributes<'div'> {
7
+ as?: 'div' | 'footer'
8
+ class?: string | null
9
+ className?: string
10
+ }
11
+
12
+ const {
13
+ as: Tag = 'div',
14
+ class: classProp = '',
15
+ className = '',
16
+ ...rest
17
+ } = Astro.props
18
+
19
+ const { classList, passthrough } = resolveLumenAstroProps(
20
+ rest, ['ui-card__footer'], classProp, className
21
+ )
22
+ ---
23
+
24
+ <Tag class:list={classList} data-slot="card-footer" {...passthrough}>
25
+ <slot />
26
+ </Tag>
@@ -0,0 +1,26 @@
1
+ ---
2
+ import type { HTMLAttributes } from 'astro/types'
3
+
4
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
5
+
6
+ interface Props extends HTMLAttributes<'div'> {
7
+ as?: 'div' | 'header'
8
+ class?: string | null
9
+ className?: string
10
+ }
11
+
12
+ const {
13
+ as: Tag = 'div',
14
+ class: classProp = '',
15
+ className = '',
16
+ ...rest
17
+ } = Astro.props
18
+
19
+ const { classList, passthrough } = resolveLumenAstroProps(
20
+ rest, ['ui-card__header'], classProp, className
21
+ )
22
+ ---
23
+
24
+ <Tag class:list={classList} data-slot="card-header" {...passthrough}>
25
+ <slot />
26
+ </Tag>