azamat-ui-kit-cli 0.2.2 → 0.3.4
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 +11 -0
- package/dist/index.cjs +452 -0
- package/package.json +2 -2
- package/vendor/src/components/actions/action-menu.tsx +21 -18
- package/vendor/src/components/calendar/calendar.tsx +153 -102
- package/vendor/src/components/calendar/date-picker.tsx +24 -14
- package/vendor/src/components/calendar/date-range-picker.tsx +137 -58
- package/vendor/src/components/charts/charts.tsx +32 -21
- package/vendor/src/components/command/command-palette.tsx +68 -57
- package/vendor/src/components/data-table/data-table-bulk-actions.tsx +23 -20
- package/vendor/src/components/data-table/data-table-column-visibility-menu.tsx +21 -10
- package/vendor/src/components/data-table/data-table-pagination.tsx +6 -6
- package/vendor/src/components/data-table/data-table-toolbar.tsx +72 -44
- package/vendor/src/components/data-table/data-table.tsx +15 -11
- package/vendor/src/components/data-table/table-export-menu.tsx +1 -1
- package/vendor/src/components/data-table/table-import-button.tsx +1 -1
- package/vendor/src/components/display/data-state.tsx +20 -8
- package/vendor/src/components/display/index.ts +19 -15
- package/vendor/src/components/display/metric-card.tsx +35 -0
- package/vendor/src/components/display/progress-circle.tsx +24 -0
- package/vendor/src/components/display/smart-card.tsx +49 -27
- package/vendor/src/components/display/status-dot.tsx +45 -0
- package/vendor/src/components/display/user-card.tsx +30 -0
- package/vendor/src/components/feedback/alert.tsx +21 -11
- package/vendor/src/components/feedback/empty-state.tsx +2 -2
- package/vendor/src/components/feedback/loading-state.tsx +2 -2
- package/vendor/src/components/feedback/page-state.tsx +19 -15
- package/vendor/src/components/feedback/status-badge.tsx +43 -43
- package/vendor/src/components/form/form-app-input.tsx +147 -0
- package/vendor/src/components/form/form-date-input.tsx +16 -19
- package/vendor/src/components/form/form-field-shell.tsx +11 -8
- package/vendor/src/components/form/form-field-utils.ts +76 -0
- package/vendor/src/components/form/form-input.tsx +423 -44
- package/vendor/src/components/form/form-number-input.tsx +16 -15
- package/vendor/src/components/form/form-phone-input.tsx +15 -9
- package/vendor/src/components/form/form-search-input.tsx +16 -19
- package/vendor/src/components/form/form-select.tsx +4 -3
- package/vendor/src/components/form/public.ts +16 -14
- package/vendor/src/components/form/smart-form-shell.tsx +13 -12
- package/vendor/src/components/inputs/app-input.tsx +27 -0
- package/vendor/src/components/inputs/async-select.tsx +113 -84
- package/vendor/src/components/inputs/clearable-input.tsx +81 -61
- package/vendor/src/components/inputs/date-input.tsx +21 -17
- package/vendor/src/components/inputs/date-range-input.tsx +10 -10
- package/vendor/src/components/inputs/index.ts +1 -0
- package/vendor/src/components/inputs/input-decorator.tsx +101 -57
- package/vendor/src/components/inputs/masked-input.tsx +20 -20
- package/vendor/src/components/inputs/money-input.tsx +2 -2
- package/vendor/src/components/inputs/number-input.tsx +29 -19
- package/vendor/src/components/inputs/password-input.tsx +82 -45
- package/vendor/src/components/inputs/phone-input.tsx +24 -2
- package/vendor/src/components/inputs/quantity-input.tsx +2 -2
- package/vendor/src/components/inputs/search-input.tsx +54 -3
- package/vendor/src/components/inputs/simple-select.tsx +110 -22
- package/vendor/src/components/layout/app-shell.tsx +2 -2
- package/vendor/src/components/layout/index.ts +5 -4
- package/vendor/src/components/layout/page-header.tsx +79 -35
- package/vendor/src/components/layout/public.ts +12 -10
- package/vendor/src/components/layout/section-header.tsx +56 -0
- package/vendor/src/components/layout/stack.tsx +106 -0
- package/vendor/src/components/layout/stat-card.tsx +66 -29
- package/vendor/src/components/navigation/index.ts +1 -0
- package/vendor/src/components/navigation/nav-tabs.tsx +60 -0
- package/vendor/src/components/navigation/page-tabs.tsx +41 -26
- package/vendor/src/components/navigation/pagination.tsx +14 -10
- package/vendor/src/components/overlay/alert-dialog.tsx +65 -0
- package/vendor/src/components/overlay/drawer.tsx +71 -0
- package/vendor/src/components/overlay/index.ts +4 -2
- package/vendor/src/components/patterns/data-view.tsx +13 -8
- package/vendor/src/components/ui/badge.tsx +96 -52
- package/vendor/src/components/ui/button.tsx +99 -61
- package/vendor/src/components/ui/card.tsx +84 -25
- package/vendor/src/components/ui/checkbox.tsx +68 -68
- package/vendor/src/components/ui/command.tsx +32 -32
- package/vendor/src/components/ui/dialog.tsx +135 -138
- package/vendor/src/components/ui/dropdown-menu.tsx +21 -21
- package/vendor/src/components/ui/hover-card.tsx +49 -0
- package/vendor/src/components/ui/input-primitive.tsx +24 -0
- package/vendor/src/components/ui/input.tsx +191 -20
- package/vendor/src/components/ui/kbd.tsx +33 -0
- package/vendor/src/components/ui/popover.tsx +11 -11
- package/vendor/src/components/ui/radio-group.tsx +102 -0
- package/vendor/src/components/ui/right-click-menu.tsx +60 -0
- package/vendor/src/components/ui/scroll-box.tsx +27 -0
- package/vendor/src/components/ui/segmented-control.tsx +21 -17
- package/vendor/src/components/ui/select.tsx +187 -189
- package/vendor/src/components/ui/skeleton.tsx +2 -2
- package/vendor/src/components/ui/switch.tsx +60 -60
- package/vendor/src/components/ui/table.tsx +114 -114
- package/vendor/src/components/ui/tabs.tsx +2 -2
- package/vendor/src/components/ui/textarea.tsx +1 -1
- package/vendor/src/components/upload/file-dropzone.tsx +38 -0
- package/vendor/src/components/upload/file-upload.tsx +4 -4
- package/vendor/src/components/upload/image-upload.tsx +22 -19
- package/vendor/src/components/upload/index.ts +2 -0
- package/vendor/src/families/catalog.ts +1 -0
- package/vendor/src/families/docs-groups.ts +10 -1
- package/vendor/src/families/member-metadata.ts +24 -0
- package/vendor/src/families/member-snippets.ts +41 -2
- package/vendor/src/families/migration-map.ts +3 -0
- package/vendor/src/index.ts +23 -18
- package/vendor/templates/styles/globals.css +253 -0
- package/dist/index.js +0 -432
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azamat-ui-kit-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "CLI for Azamat UI Kit source-copy, presets, and theme setup.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"azamat-ui-kit": "dist/index.
|
|
8
|
+
"azamat-ui-kit": "dist/index.cjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
@@ -66,17 +66,20 @@ function ActionMenu({
|
|
|
66
66
|
return (
|
|
67
67
|
<DropdownMenu>
|
|
68
68
|
<DropdownMenuTrigger
|
|
69
|
-
render={
|
|
70
|
-
trigger ?? (
|
|
71
|
-
<Button
|
|
72
|
-
type="button"
|
|
73
|
-
variant="ghost"
|
|
74
|
-
size="icon-sm"
|
|
75
|
-
disabled={disabled}
|
|
76
|
-
className={
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
69
|
+
render={
|
|
70
|
+
trigger ?? (
|
|
71
|
+
<Button
|
|
72
|
+
type="button"
|
|
73
|
+
variant="ghost"
|
|
74
|
+
size="icon-sm"
|
|
75
|
+
disabled={disabled}
|
|
76
|
+
className={cn(
|
|
77
|
+
"rounded-full border border-transparent text-muted-foreground hover:border-border/70 hover:bg-muted/50 hover:text-foreground",
|
|
78
|
+
triggerClassName
|
|
79
|
+
)}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
80
83
|
>
|
|
81
84
|
{!trigger && <MoreHorizontalIcon />}
|
|
82
85
|
<span className="sr-only">Open actions</span>
|
|
@@ -104,13 +107,13 @@ function ActionMenu({
|
|
|
104
107
|
variant={action.destructive ? "destructive" : "default"}
|
|
105
108
|
onClick={() => void handleSelect(action)}
|
|
106
109
|
>
|
|
107
|
-
{isLoading ? <Loader2Icon className="animate-spin" /> : action.icon}
|
|
108
|
-
<span className="min-w-0 flex-1 truncate">{action.label}</span>
|
|
109
|
-
{action.shortcut && (
|
|
110
|
-
<span className="ml-auto text-
|
|
111
|
-
)}
|
|
112
|
-
</DropdownMenuItem>
|
|
113
|
-
)
|
|
110
|
+
{isLoading ? <Loader2Icon className="animate-spin" /> : action.icon}
|
|
111
|
+
<span className="min-w-0 flex-1 truncate">{action.label}</span>
|
|
112
|
+
{action.shortcut && (
|
|
113
|
+
<span className="ml-auto text-[11px] tracking-[0.14em] text-muted-foreground">{action.shortcut}</span>
|
|
114
|
+
)}
|
|
115
|
+
</DropdownMenuItem>
|
|
116
|
+
)
|
|
114
117
|
})}
|
|
115
118
|
</DropdownMenuContent>
|
|
116
119
|
</DropdownMenu>
|
|
@@ -31,10 +31,10 @@ export type CalendarLabels = {
|
|
|
31
31
|
disabledDate?: (date: string, reason: CalendarDisabledReason) => string
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export type CalendarProps = React.ComponentProps<"div"> & {
|
|
35
|
-
value?: string | null
|
|
36
|
-
range?: CalendarDateRange
|
|
37
|
-
onValueChange?: (value: string) => void
|
|
34
|
+
export type CalendarProps = React.ComponentProps<"div"> & {
|
|
35
|
+
value?: string | null
|
|
36
|
+
range?: CalendarDateRange
|
|
37
|
+
onValueChange?: (value: string) => void
|
|
38
38
|
onRangeChange?: (range: CalendarDateRange) => void
|
|
39
39
|
mode?: "single" | "range"
|
|
40
40
|
month?: Date
|
|
@@ -42,11 +42,14 @@ export type CalendarProps = React.ComponentProps<"div"> & {
|
|
|
42
42
|
onMonthChange?: (month: Date) => void
|
|
43
43
|
min?: string
|
|
44
44
|
max?: string
|
|
45
|
-
disabledDates?: string[]
|
|
46
|
-
locale?: string
|
|
47
|
-
weekStartsOn?: 0 | 1
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
disabledDates?: string[]
|
|
46
|
+
locale?: string
|
|
47
|
+
weekStartsOn?: 0 | 1
|
|
48
|
+
numberOfMonths?: number
|
|
49
|
+
showMonthHeaders?: boolean
|
|
50
|
+
pagedNavigation?: boolean
|
|
51
|
+
labels?: CalendarLabels
|
|
52
|
+
}
|
|
50
53
|
|
|
51
54
|
function getInitialMonth(defaultMonth?: Date | string | null, value?: string | null, range?: CalendarDateRange) {
|
|
52
55
|
if (defaultMonth instanceof Date) return startOfMonth(defaultMonth)
|
|
@@ -103,20 +106,41 @@ function Calendar({
|
|
|
103
106
|
onMonthChange,
|
|
104
107
|
min,
|
|
105
108
|
max,
|
|
106
|
-
disabledDates,
|
|
107
|
-
locale = "en-US",
|
|
108
|
-
weekStartsOn = 1,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
const
|
|
109
|
+
disabledDates,
|
|
110
|
+
locale = "en-US",
|
|
111
|
+
weekStartsOn = 1,
|
|
112
|
+
numberOfMonths = 1,
|
|
113
|
+
showMonthHeaders,
|
|
114
|
+
pagedNavigation = false,
|
|
115
|
+
labels,
|
|
116
|
+
...props
|
|
117
|
+
}: CalendarProps) {
|
|
118
|
+
const [internalMonth, setInternalMonth] = React.useState(() => getInitialMonth(defaultMonth, value, range))
|
|
119
|
+
const currentMonth = month ?? internalMonth
|
|
120
|
+
const resolvedNumberOfMonths = Math.max(numberOfMonths, 1)
|
|
121
|
+
const shouldShowMonthHeaders = showMonthHeaders ?? resolvedNumberOfMonths > 1
|
|
122
|
+
const navigationStep = pagedNavigation ? resolvedNumberOfMonths : 1
|
|
123
|
+
const todayKey = toDateKey(new Date())
|
|
124
|
+
const disabledSet = React.useMemo(() => new Set(disabledDates ?? []), [disabledDates])
|
|
125
|
+
const weekdayLabels = React.useMemo(() => getWeekdayLabels(locale, weekStartsOn), [locale, weekStartsOn])
|
|
126
|
+
const buttonRefs = React.useRef(new Map<string, HTMLButtonElement>())
|
|
127
|
+
const [focusedDateKey, setFocusedDateKey] = React.useState(() => value ?? range?.from ?? todayKey)
|
|
128
|
+
const visibleMonths = React.useMemo(
|
|
129
|
+
() => Array.from({ length: resolvedNumberOfMonths }, (_, index) => addMonths(currentMonth, index)),
|
|
130
|
+
[currentMonth, resolvedNumberOfMonths]
|
|
131
|
+
)
|
|
132
|
+
const monthDaysByMonth = React.useMemo(
|
|
133
|
+
() =>
|
|
134
|
+
visibleMonths.map((visibleMonth) => ({
|
|
135
|
+
month: visibleMonth,
|
|
136
|
+
days: getMonthDays(visibleMonth, weekStartsOn),
|
|
137
|
+
})),
|
|
138
|
+
[visibleMonths, weekStartsOn]
|
|
139
|
+
)
|
|
140
|
+
const allMonthDays = React.useMemo(
|
|
141
|
+
() => monthDaysByMonth.flatMap((entry) => entry.days),
|
|
142
|
+
[monthDaysByMonth]
|
|
143
|
+
)
|
|
120
144
|
|
|
121
145
|
const getDisabledReason = React.useCallback(
|
|
122
146
|
(dateKey: string): CalendarDisabledReason | undefined => {
|
|
@@ -128,12 +152,12 @@ function Calendar({
|
|
|
128
152
|
[disabledSet, max, min]
|
|
129
153
|
)
|
|
130
154
|
|
|
131
|
-
const isDateDisabled = React.useCallback((dateKey: string) => Boolean(getDisabledReason(dateKey)), [getDisabledReason])
|
|
132
|
-
|
|
133
|
-
const visibleEnabledKeys = React.useMemo(
|
|
134
|
-
() =>
|
|
135
|
-
[
|
|
136
|
-
)
|
|
155
|
+
const isDateDisabled = React.useCallback((dateKey: string) => Boolean(getDisabledReason(dateKey)), [getDisabledReason])
|
|
156
|
+
|
|
157
|
+
const visibleEnabledKeys = React.useMemo(
|
|
158
|
+
() => allMonthDays.map(toDateKey).filter((dateKey) => !isDateDisabled(dateKey)),
|
|
159
|
+
[allMonthDays, isDateDisabled]
|
|
160
|
+
)
|
|
137
161
|
|
|
138
162
|
const tabbableDateKey = React.useMemo(() => {
|
|
139
163
|
const preferred = value ?? range?.from ?? todayKey
|
|
@@ -153,9 +177,9 @@ function Calendar({
|
|
|
153
177
|
onMonthChange?.(next)
|
|
154
178
|
}
|
|
155
179
|
|
|
156
|
-
const moveFocus = (date: Date) => {
|
|
157
|
-
let nextDate = date
|
|
158
|
-
let nextKey = toDateKey(nextDate)
|
|
180
|
+
const moveFocus = (date: Date) => {
|
|
181
|
+
let nextDate = date
|
|
182
|
+
let nextKey = toDateKey(nextDate)
|
|
159
183
|
let guard = 0
|
|
160
184
|
|
|
161
185
|
while (isDateDisabled(nextKey) && guard < 370) {
|
|
@@ -163,13 +187,15 @@ function Calendar({
|
|
|
163
187
|
nextKey = toDateKey(nextDate)
|
|
164
188
|
guard += 1
|
|
165
189
|
}
|
|
166
|
-
|
|
167
|
-
setFocusedDateKey(nextKey)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
190
|
+
|
|
191
|
+
setFocusedDateKey(nextKey)
|
|
192
|
+
|
|
193
|
+
const isVisibleInCurrentViewport = visibleMonths.some((visibleMonth) => isSameMonth(nextDate, visibleMonth))
|
|
194
|
+
|
|
195
|
+
if (!isVisibleInCurrentViewport) {
|
|
196
|
+
setMonth(startOfMonth(nextDate))
|
|
197
|
+
}
|
|
198
|
+
}
|
|
173
199
|
|
|
174
200
|
const handleDateSelect = (dateKey: string) => {
|
|
175
201
|
if (isDateDisabled(dateKey)) return
|
|
@@ -197,10 +223,12 @@ function Calendar({
|
|
|
197
223
|
onRangeChange?.({ from, to: dateKey })
|
|
198
224
|
}
|
|
199
225
|
|
|
200
|
-
const handleDateKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, date: Date) => {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
226
|
+
const handleDateKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, date: Date) => {
|
|
227
|
+
const visibleMonthEntry = monthDaysByMonth.find((entry) => isSameMonth(date, entry.month))
|
|
228
|
+
const columnIndex =
|
|
229
|
+
((visibleMonthEntry?.days.findIndex((item) => toDateKey(item) === toDateKey(date)) ?? 0) + 7) % 7
|
|
230
|
+
|
|
231
|
+
switch (event.key) {
|
|
204
232
|
case "ArrowRight":
|
|
205
233
|
event.preventDefault()
|
|
206
234
|
moveFocus(addDays(date, 1))
|
|
@@ -239,90 +267,113 @@ function Calendar({
|
|
|
239
267
|
return (
|
|
240
268
|
<div
|
|
241
269
|
data-slot="calendar"
|
|
270
|
+
data-months={resolvedNumberOfMonths}
|
|
242
271
|
className={cn(
|
|
243
|
-
"w-
|
|
272
|
+
"w-fit rounded-[calc(var(--radius-2xl)+4px)] border border-border/80 bg-popover/98 p-3.5 text-popover-foreground shadow-[0_24px_70px_rgba(15,23,42,0.16)] ring-1 ring-foreground/6 backdrop-blur",
|
|
244
273
|
className
|
|
245
274
|
)}
|
|
246
275
|
{...props}
|
|
247
276
|
>
|
|
248
|
-
<div className="mb-
|
|
277
|
+
<div className="mb-4 flex items-center justify-between gap-3">
|
|
249
278
|
<Button
|
|
250
279
|
type="button"
|
|
251
280
|
variant="outline"
|
|
252
281
|
size="icon-sm"
|
|
253
|
-
className="rounded-full border-border/
|
|
282
|
+
className="rounded-full border-border/90 bg-background/88 text-foreground shadow-[0_1px_0_rgba(255,255,255,0.08)] hover:border-ring/30 hover:bg-accent hover:text-accent-foreground"
|
|
254
283
|
aria-label={labels?.previousMonth ?? "Previous month"}
|
|
255
|
-
onClick={() => setMonth(addMonths(currentMonth, -
|
|
284
|
+
onClick={() => setMonth(addMonths(currentMonth, -navigationStep))}
|
|
256
285
|
>
|
|
257
286
|
<ChevronLeftIcon />
|
|
258
287
|
</Button>
|
|
259
|
-
<div className="
|
|
288
|
+
<div className="flex flex-1 flex-col items-center text-center">
|
|
289
|
+
<span className="text-[11px] font-semibold uppercase tracking-[0.24em] text-muted-foreground">Calendar</span>
|
|
290
|
+
<div className="text-base font-semibold capitalize tracking-tight text-foreground">{getMonthLabel(currentMonth, locale)}</div>
|
|
291
|
+
</div>
|
|
260
292
|
<Button
|
|
261
293
|
type="button"
|
|
262
294
|
variant="outline"
|
|
263
295
|
size="icon-sm"
|
|
264
|
-
className="rounded-full border-border/
|
|
296
|
+
className="rounded-full border-border/90 bg-background/88 text-foreground shadow-[0_1px_0_rgba(255,255,255,0.08)] hover:border-ring/30 hover:bg-accent hover:text-accent-foreground"
|
|
265
297
|
aria-label={labels?.nextMonth ?? "Next month"}
|
|
266
|
-
onClick={() => setMonth(addMonths(currentMonth,
|
|
298
|
+
onClick={() => setMonth(addMonths(currentMonth, navigationStep))}
|
|
267
299
|
>
|
|
268
300
|
<ChevronRightIcon />
|
|
269
301
|
</Button>
|
|
270
302
|
</div>
|
|
271
303
|
|
|
272
|
-
<div
|
|
273
|
-
{
|
|
274
|
-
|
|
275
|
-
|
|
304
|
+
<div
|
|
305
|
+
className={cn("grid gap-3.5", resolvedNumberOfMonths > 1 && "sm:grid-cols-2")}
|
|
306
|
+
style={
|
|
307
|
+
resolvedNumberOfMonths > 2
|
|
308
|
+
? { gridTemplateColumns: `repeat(${resolvedNumberOfMonths}, minmax(0, 1fr))` }
|
|
309
|
+
: undefined
|
|
310
|
+
}
|
|
311
|
+
>
|
|
312
|
+
{monthDaysByMonth.map(({ month: visibleMonth, days }) => (
|
|
313
|
+
<div key={toDateKey(visibleMonth)} className="min-w-[17rem] rounded-[var(--radius-2xl)] border border-border/60 bg-background/58 p-2.5 shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]">
|
|
314
|
+
{shouldShowMonthHeaders && (
|
|
315
|
+
<div className="mb-3 text-center text-sm font-semibold capitalize tracking-tight text-foreground">
|
|
316
|
+
{getMonthLabel(visibleMonth, locale)}
|
|
317
|
+
</div>
|
|
318
|
+
)}
|
|
319
|
+
|
|
320
|
+
<div className="grid grid-cols-7 gap-1 text-center text-[0.68rem] font-semibold uppercase tracking-[0.18em] text-muted-foreground">
|
|
321
|
+
{weekdayLabels.map((weekday) => (
|
|
322
|
+
<div key={`${toDateKey(visibleMonth)}-${weekday}`} className="py-1.5">
|
|
323
|
+
{weekday}
|
|
324
|
+
</div>
|
|
325
|
+
))}
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<div className="mt-2 grid grid-cols-7 gap-1.5">
|
|
329
|
+
{days.map((date) => {
|
|
330
|
+
const dateKey = toDateKey(date)
|
|
331
|
+
const outside = !isSameMonth(date, visibleMonth)
|
|
332
|
+
const selected = mode === "single" ? value === dateKey : dateKey === range?.from || dateKey === range?.to
|
|
333
|
+
const inRange = mode === "range" && isWithinRange(dateKey, range?.from, range?.to)
|
|
334
|
+
const disabledReason = getDisabledReason(dateKey)
|
|
335
|
+
const disabled = Boolean(disabledReason)
|
|
336
|
+
const disabledLabel = disabledReason ? labels?.disabledDate?.(dateKey, disabledReason) : undefined
|
|
337
|
+
|
|
338
|
+
return (
|
|
339
|
+
<button
|
|
340
|
+
key={dateKey}
|
|
341
|
+
ref={(node) => {
|
|
342
|
+
if (node) buttonRefs.current.set(dateKey, node)
|
|
343
|
+
else buttonRefs.current.delete(dateKey)
|
|
344
|
+
}}
|
|
345
|
+
type="button"
|
|
346
|
+
disabled={disabled}
|
|
347
|
+
aria-label={disabledLabel ?? labels?.selectDate?.(dateKey) ?? dateKey}
|
|
348
|
+
aria-current={dateKey === todayKey ? "date" : undefined}
|
|
349
|
+
tabIndex={dateKey === tabbableDateKey ? 0 : -1}
|
|
350
|
+
title={disabledLabel}
|
|
351
|
+
data-selected={selected || undefined}
|
|
352
|
+
data-today={dateKey === todayKey || undefined}
|
|
353
|
+
data-outside={outside || undefined}
|
|
354
|
+
data-in-range={inRange || undefined}
|
|
355
|
+
data-disabled-reason={disabledReason}
|
|
356
|
+
className={cn(
|
|
357
|
+
"flex h-10 items-center justify-center rounded-[min(var(--radius-xl),16px)] border border-transparent text-sm font-medium outline-none transition-[background-color,color,border-color,box-shadow,transform] hover:border-border/70 hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-35",
|
|
358
|
+
outside && "text-muted-foreground/42",
|
|
359
|
+
dateKey === todayKey && "border-primary/25 bg-accent/42 text-foreground",
|
|
360
|
+
inRange && "border-primary/12 bg-primary/10 text-foreground",
|
|
361
|
+
selected && "border-primary/85 bg-primary text-primary-foreground shadow-[0_10px_24px_color-mix(in_oklch,var(--primary),transparent_76%)] hover:bg-primary/92 hover:text-primary-foreground"
|
|
362
|
+
)}
|
|
363
|
+
onFocus={() => setFocusedDateKey(dateKey)}
|
|
364
|
+
onKeyDown={(event) => handleDateKeyDown(event, date)}
|
|
365
|
+
onClick={() => handleDateSelect(dateKey)}
|
|
366
|
+
>
|
|
367
|
+
{date.getDate()}
|
|
368
|
+
</button>
|
|
369
|
+
)
|
|
370
|
+
})}
|
|
371
|
+
</div>
|
|
276
372
|
</div>
|
|
277
373
|
))}
|
|
278
374
|
</div>
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const dateKey = toDateKey(date)
|
|
283
|
-
const outside = !isSameMonth(date, currentMonth)
|
|
284
|
-
const selected = mode === "single" ? value === dateKey : dateKey === range?.from || dateKey === range?.to
|
|
285
|
-
const inRange = mode === "range" && isWithinRange(dateKey, range?.from, range?.to)
|
|
286
|
-
const disabledReason = getDisabledReason(dateKey)
|
|
287
|
-
const disabled = Boolean(disabledReason)
|
|
288
|
-
const disabledLabel = disabledReason ? labels?.disabledDate?.(dateKey, disabledReason) : undefined
|
|
289
|
-
|
|
290
|
-
return (
|
|
291
|
-
<button
|
|
292
|
-
key={dateKey}
|
|
293
|
-
ref={(node) => {
|
|
294
|
-
if (node) buttonRefs.current.set(dateKey, node)
|
|
295
|
-
else buttonRefs.current.delete(dateKey)
|
|
296
|
-
}}
|
|
297
|
-
type="button"
|
|
298
|
-
disabled={disabled}
|
|
299
|
-
aria-label={disabledLabel ?? labels?.selectDate?.(dateKey) ?? dateKey}
|
|
300
|
-
aria-current={dateKey === todayKey ? "date" : undefined}
|
|
301
|
-
tabIndex={dateKey === tabbableDateKey ? 0 : -1}
|
|
302
|
-
title={disabledLabel}
|
|
303
|
-
data-selected={selected || undefined}
|
|
304
|
-
data-today={dateKey === todayKey || undefined}
|
|
305
|
-
data-outside={outside || undefined}
|
|
306
|
-
data-in-range={inRange || undefined}
|
|
307
|
-
data-disabled-reason={disabledReason}
|
|
308
|
-
className={cn(
|
|
309
|
-
"flex h-9 items-center justify-center rounded-xl border border-transparent text-sm font-medium outline-none transition-[background-color,color,border-color,box-shadow] hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-35",
|
|
310
|
-
outside && "text-muted-foreground/45",
|
|
311
|
-
dateKey === todayKey && "border-primary/30 bg-accent/25 text-foreground",
|
|
312
|
-
inRange && "border-accent/60 bg-accent/75 text-accent-foreground",
|
|
313
|
-
selected && "border-primary/80 bg-primary text-primary-foreground shadow-[0_10px_24px_color-mix(in_oklch,var(--primary),transparent_76%)] hover:bg-primary/92 hover:text-primary-foreground"
|
|
314
|
-
)}
|
|
315
|
-
onFocus={() => setFocusedDateKey(dateKey)}
|
|
316
|
-
onKeyDown={(event) => handleDateKeyDown(event, date)}
|
|
317
|
-
onClick={() => handleDateSelect(dateKey)}
|
|
318
|
-
>
|
|
319
|
-
{date.getDate()}
|
|
320
|
-
</button>
|
|
321
|
-
)
|
|
322
|
-
})}
|
|
323
|
-
</div>
|
|
324
|
-
</div>
|
|
325
|
-
)
|
|
326
|
-
}
|
|
375
|
+
</div>
|
|
376
|
+
)
|
|
377
|
+
}
|
|
327
378
|
|
|
328
379
|
export { Calendar }
|
|
@@ -52,25 +52,35 @@ function DatePicker({
|
|
|
52
52
|
return (
|
|
53
53
|
<div data-slot="date-picker" className={cn("w-full", className)}>
|
|
54
54
|
<Popover open={open} onOpenChange={setOpen}>
|
|
55
|
-
<PopoverTrigger
|
|
56
|
-
render={
|
|
57
|
-
<Button
|
|
58
|
-
type="button"
|
|
59
|
-
variant="outline"
|
|
60
|
-
disabled={disabled}
|
|
61
|
-
className={cn(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
<PopoverTrigger
|
|
56
|
+
render={
|
|
57
|
+
<Button
|
|
58
|
+
type="button"
|
|
59
|
+
variant="outline"
|
|
60
|
+
disabled={disabled}
|
|
61
|
+
className={cn(
|
|
62
|
+
"min-h-11 w-full justify-start rounded-[min(var(--radius-xl),16px)] border-border/80 bg-background/96 text-left font-normal shadow-[0_1px_0_rgba(255,255,255,0.06)]",
|
|
63
|
+
!hasValue && "text-muted-foreground",
|
|
64
|
+
triggerClassName
|
|
65
|
+
)}
|
|
66
|
+
/>
|
|
67
|
+
}
|
|
68
|
+
>
|
|
65
69
|
<CalendarIcon data-icon="inline-start" />
|
|
66
70
|
<span className="min-w-0 flex-1 truncate">
|
|
67
71
|
{hasValue ? formatValue(String(value)) : placeholder ?? labels?.placeholder ?? "Select date"}
|
|
68
72
|
</span>
|
|
69
73
|
</PopoverTrigger>
|
|
70
|
-
<PopoverContent
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
<PopoverContent
|
|
75
|
+
align="start"
|
|
76
|
+
className={cn(
|
|
77
|
+
"w-auto rounded-[var(--radius-2xl)] border-border/80 bg-popover/98 p-0 shadow-[0_24px_80px_rgba(15,23,42,0.18)] backdrop-blur",
|
|
78
|
+
contentClassName
|
|
79
|
+
)}
|
|
80
|
+
>
|
|
81
|
+
<Calendar value={value} onValueChange={handleSelect} labels={labels} {...calendarProps} />
|
|
82
|
+
</PopoverContent>
|
|
83
|
+
</Popover>
|
|
74
84
|
</div>
|
|
75
85
|
)
|
|
76
86
|
}
|