banhaten 0.1.3 → 0.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/CHANGELOG.md +73 -0
- package/CONTRIBUTING.md +44 -0
- package/README.md +65 -13
- package/SECURITY.md +15 -0
- package/banhaten.config.example.json +0 -2
- package/docs/design-system/README.md +9 -0
- package/docs/design-system/adoption-quality-remediation.md +87 -0
- package/docs/design-system/agent-usage.md +13 -0
- package/docs/design-system/appearance-presets.md +6 -1
- package/docs/design-system/component-docs-pattern.md +81 -0
- package/docs/design-system/component-implementation-ownership-audit.md +244 -0
- package/docs/design-system/component-quality-audit-2026-08-20.md +223 -0
- package/docs/design-system/component-showcase-consistency-report.md +2 -0
- package/docs/design-system/component-token-consistency-audit.md +13 -3
- package/docs/design-system/components/README.md +4 -2
- package/docs/design-system/components/accordion.md +16 -0
- package/docs/design-system/components/activity-feed.md +17 -1
- package/docs/design-system/components/activity-heatmap.md +73 -0
- package/docs/design-system/components/alert-dialog.md +17 -0
- package/docs/design-system/components/alert.md +46 -14
- package/docs/design-system/components/aspect-ratio.md +15 -0
- package/docs/design-system/components/attribute.md +16 -0
- package/docs/design-system/components/autocomplete.md +19 -0
- package/docs/design-system/components/avatar.md +18 -0
- package/docs/design-system/components/badge.md +19 -0
- package/docs/design-system/components/banner.md +19 -1
- package/docs/design-system/components/breadcrumbs.md +17 -0
- package/docs/design-system/components/button-group.md +35 -0
- package/docs/design-system/components/button.md +18 -0
- package/docs/design-system/components/card.md +26 -1
- package/docs/design-system/components/carousel.md +20 -3
- package/docs/design-system/components/catalog-components.md +18 -1
- package/docs/design-system/components/chart.md +15 -0
- package/docs/design-system/components/checkbox.md +16 -0
- package/docs/design-system/components/collapsible.md +15 -0
- package/docs/design-system/components/command.md +15 -0
- package/docs/design-system/components/context-menu.md +15 -0
- package/docs/design-system/components/date-picker.md +21 -1
- package/docs/design-system/components/divider.md +16 -0
- package/docs/design-system/components/empty-state.md +17 -0
- package/docs/design-system/components/field.md +17 -0
- package/docs/design-system/components/file-upload.md +18 -0
- package/docs/design-system/components/hover-card.md +15 -0
- package/docs/design-system/components/input-group.md +17 -0
- package/docs/design-system/components/input-otp.md +18 -0
- package/docs/design-system/components/input.md +43 -7
- package/docs/design-system/components/item.md +72 -0
- package/docs/design-system/components/kbd.md +18 -0
- package/docs/design-system/components/label.md +16 -0
- package/docs/design-system/components/menu.md +17 -0
- package/docs/design-system/components/menubar.md +15 -0
- package/docs/design-system/components/modal.md +25 -4
- package/docs/design-system/components/native-select.md +18 -0
- package/docs/design-system/components/navigation-menu.md +15 -0
- package/docs/design-system/components/onboarding-step-list-item.md +16 -1
- package/docs/design-system/components/page-header.md +17 -1
- package/docs/design-system/components/pagination.md +31 -8
- package/docs/design-system/components/popover.md +19 -0
- package/docs/design-system/components/progress-slider.md +18 -0
- package/docs/design-system/components/progress.md +17 -0
- package/docs/design-system/components/radio-card.md +16 -0
- package/docs/design-system/components/radio-group.md +17 -0
- package/docs/design-system/components/resizable.md +15 -0
- package/docs/design-system/components/scroll-area.md +20 -1
- package/docs/design-system/components/segmented-control.md +71 -0
- package/docs/design-system/components/select.md +44 -7
- package/docs/design-system/components/sheet.md +17 -0
- package/docs/design-system/components/sidebar.md +17 -0
- package/docs/design-system/components/skeleton.md +18 -0
- package/docs/design-system/components/slideout.md +17 -0
- package/docs/design-system/components/slider.md +20 -0
- package/docs/design-system/components/social-button.md +21 -3
- package/docs/design-system/components/spinner.md +16 -0
- package/docs/design-system/components/steps.md +19 -0
- package/docs/design-system/components/table.md +36 -4
- package/docs/design-system/components/tabs.md +17 -0
- package/docs/design-system/components/tag.md +17 -0
- package/docs/design-system/components/textarea.md +16 -0
- package/docs/design-system/components/timeline.md +17 -0
- package/docs/design-system/components/toast.md +17 -0
- package/docs/design-system/components/toggle.md +17 -0
- package/docs/design-system/components/toolbar.md +21 -1
- package/docs/design-system/components/tooltip.md +18 -1
- package/docs/design-system/density-and-elevation.md +61 -0
- package/docs/design-system/direction-and-localization.md +23 -0
- package/docs/design-system/form-recipes.md +73 -0
- package/docs/design-system/gap-promotion-policy.md +21 -0
- package/docs/design-system/gap-promotion-report.md +100 -0
- package/docs/design-system/installation.md +68 -0
- package/docs/design-system/launch-comparison-shadcn.md +98 -0
- package/docs/design-system/registry-and-cli.md +30 -7
- package/docs/design-system/release-policy.md +30 -0
- package/docs/design-system/roadmap.md +14 -0
- package/docs/design-system/support-matrix.md +23 -0
- package/docs/design-system/tabs.md +1 -1
- package/docs/design-system/token-governance.md +23 -1
- package/package.json +30 -15
- package/registry/component-contract.json +40 -0
- package/registry/components/alert-dialog.tsx +2 -2
- package/registry/components/alert.tsx +101 -76
- package/registry/components/autocomplete.tsx +23 -9
- package/registry/components/avatar.tsx +14 -10
- package/registry/components/badge.tsx +18 -18
- package/registry/components/button-group.tsx +324 -5
- package/registry/components/button.tsx +38 -17
- package/registry/components/card.tsx +87 -62
- package/registry/components/carousel.tsx +67 -5
- package/registry/components/chart.tsx +36 -3
- package/registry/components/checkbox.tsx +3 -3
- package/registry/components/collapsible.tsx +1 -1
- package/registry/components/command.tsx +38 -4
- package/registry/components/context-menu.tsx +1 -1
- package/registry/components/date-picker.tsx +312 -135
- package/registry/components/expanded/ActivityFeed.tsx +9 -22
- package/registry/components/expanded/ActivityHeatmap.tsx +64 -0
- package/registry/components/expanded/Banner.tsx +3 -4
- package/registry/components/expanded/Breadcrumbs.tsx +8 -5
- package/registry/components/expanded/CatalogComponentsShowcase.tsx +16 -17
- package/registry/components/expanded/CatalogDivider.tsx +4 -3
- package/registry/components/expanded/EmptyState.tsx +1 -1
- package/registry/components/expanded/FileUpload.tsx +16 -7
- package/registry/components/expanded/OnboardingStepListItem.tsx +2 -6
- package/registry/components/expanded/PageHeader.tsx +47 -56
- package/registry/components/expanded/Slideout.tsx +29 -424
- package/registry/components/expanded/Steps.tsx +151 -13
- package/registry/components/expanded/Table.tsx +161 -82
- package/registry/components/expanded/Timeline.tsx +14 -27
- package/registry/components/expanded/activityFeed.css +9 -9
- package/registry/components/expanded/activityHeatmap.css +66 -0
- package/registry/components/expanded/banner.css +21 -11
- package/registry/components/expanded/breadcrumbs.css +2 -2
- package/registry/components/expanded/divider.css +1 -1
- package/registry/components/expanded/emptyState.css +1 -1
- package/registry/components/expanded/fileUpload.css +7 -7
- package/registry/components/expanded/onboardingStepListItem.css +2 -2
- package/registry/components/expanded/pageHeader.css +14 -11
- package/registry/components/expanded/slideout.css +10 -690
- package/registry/components/expanded/steps.css +78 -15
- package/registry/components/expanded/table.css +60 -69
- package/registry/components/expanded/timeline.css +19 -18
- package/registry/components/field.tsx +61 -8
- package/registry/components/input-content.ts +1 -0
- package/registry/components/input-otp.tsx +13 -3
- package/registry/components/input.tsx +68 -43
- package/registry/components/item.tsx +189 -0
- package/registry/components/kbd.tsx +111 -4
- package/registry/components/menu.tsx +87 -251
- package/registry/components/menubar.tsx +1 -1
- package/registry/components/modal.tsx +143 -103
- package/registry/components/native-select.tsx +2 -2
- package/registry/components/navigation-menu.tsx +1 -1
- package/registry/components/pagination.css +36 -0
- package/registry/components/pagination.tsx +88 -64
- package/registry/components/popover.tsx +162 -664
- package/registry/components/progress-slider.tsx +1 -1
- package/registry/components/radio-card.tsx +116 -87
- package/registry/components/resizable.tsx +36 -2
- package/registry/components/scroll-area.tsx +30 -6
- package/registry/components/segmented-control.tsx +87 -0
- package/registry/components/select-content.tsx +2 -2
- package/registry/components/select.tsx +86 -27
- package/registry/components/sheet.tsx +1 -1
- package/registry/components/sidebar.tsx +12 -6
- package/registry/components/skeleton.css +0 -3
- package/registry/components/social-button.tsx +59 -30
- package/registry/components/spinner.tsx +18 -55
- package/registry/components/table-elements.tsx +128 -0
- package/registry/components/tabs.css +1 -1
- package/registry/components/tabs.tsx +13 -14
- package/registry/components/tag.tsx +3 -3
- package/registry/components/textarea.tsx +3 -3
- package/registry/components/toast.tsx +16 -11
- package/registry/components/toggle.tsx +4 -4
- package/registry/components/toolbar.tsx +94 -22
- package/registry/components/tooltip.tsx +2 -2
- package/registry/examples/alert-demo.tsx +25 -5
- package/registry/examples/attribute-demo.tsx +1 -1
- package/registry/examples/autocomplete-demo.tsx +3 -3
- package/registry/examples/avatar-demo.tsx +14 -11
- package/registry/examples/badge-demo.tsx +1 -1
- package/registry/examples/button-demo.tsx +6 -6
- package/registry/examples/button-group-demo.tsx +23 -2
- package/registry/examples/card-demo.tsx +46 -8
- package/registry/examples/carousel-demo.tsx +20 -2
- package/registry/examples/chart-demo.tsx +1 -1
- package/registry/examples/checkbox-demo.tsx +5 -5
- package/registry/examples/collection-filter-bar-demo.tsx +135 -0
- package/registry/examples/command-demo.tsx +3 -3
- package/registry/examples/{expanded/command-bar-demo.tsx → command-palette-demo.tsx} +5 -15
- package/registry/examples/context-menu-demo.tsx +1 -1
- package/registry/examples/control-density-contract-demo.tsx +61 -0
- package/registry/examples/date-picker-demo.tsx +12 -12
- package/registry/examples/divider-demo.tsx +3 -3
- package/registry/examples/expanded/activity-heatmap-demo.tsx +29 -0
- package/registry/examples/expanded/banner-demo.tsx +1 -1
- package/registry/examples/expanded/breadcrumbs-demo.tsx +4 -4
- package/registry/examples/expanded/empty-state-demo.tsx +1 -1
- package/registry/examples/expanded/file-upload-demo.tsx +1 -1
- package/registry/examples/expanded/onboarding-step-demo.tsx +1 -1
- package/registry/examples/expanded/slideout-demo.tsx +2 -2
- package/registry/examples/expanded/steps-demo.tsx +1 -1
- package/registry/examples/expanded/tabs-demo.tsx +1 -1
- package/registry/examples/expanded/timeline-demo.tsx +1 -1
- package/registry/examples/hover-card-demo.tsx +2 -2
- package/registry/examples/input-demo.tsx +6 -6
- package/registry/examples/input-otp-demo.tsx +4 -4
- package/registry/examples/item-demo.tsx +46 -0
- package/registry/examples/kbd-demo.tsx +1 -1
- package/registry/examples/label-demo.tsx +1 -1
- package/registry/examples/menu-demo.tsx +1 -1
- package/registry/examples/modal-demo.tsx +67 -9
- package/registry/examples/navigation-menu-demo.tsx +1 -1
- package/registry/examples/pagination-demo.tsx +10 -7
- package/registry/examples/popover-demo.tsx +19 -19
- package/registry/examples/progress-demo.tsx +1 -1
- package/registry/examples/progress-slider-demo.tsx +1 -1
- package/registry/examples/radio-card-demo.tsx +4 -4
- package/registry/examples/radio-group-demo.tsx +2 -2
- package/registry/examples/resizable-demo.tsx +1 -1
- package/registry/examples/results-toolbar-demo.tsx +44 -0
- package/registry/examples/scroll-area-demo.tsx +3 -3
- package/registry/examples/segmented-control-demo.tsx +39 -0
- package/registry/examples/select-demo.tsx +1 -1
- package/registry/examples/sidebar-demo.tsx +5 -3
- package/registry/examples/slider-demo.tsx +3 -3
- package/registry/examples/social-button-demo.tsx +18 -8
- package/registry/examples/spinner-demo.tsx +13 -13
- package/registry/examples/tabs-demo.tsx +1 -1
- package/registry/examples/tag-demo.tsx +1 -1
- package/registry/examples/textarea-demo.tsx +4 -4
- package/registry/examples/toast-demo.tsx +1 -1
- package/registry/examples/toggle-demo.tsx +6 -6
- package/registry/examples/tooltip-demo.tsx +4 -4
- package/registry/guidance/USAGE.md +27 -0
- package/registry/hooks/use-direction.ts +193 -13
- package/registry/index.json +5668 -375
- package/registry/styles/globals.css +2404 -3999
- package/registry.json +156 -149
- package/schema/config.schema.json +11 -8
- package/schema/registry.schema.json +150 -45
- package/schema/tokens.schema.json +134 -34
- package/src/cli/command-line.js +194 -0
- package/src/cli/config-editors.js +9 -0
- package/src/cli/doctor.js +399 -0
- package/src/cli/file-transaction.js +295 -0
- package/src/cli/global-css.js +153 -0
- package/src/cli/index.js +567 -1613
- package/src/cli/install-plan.js +592 -0
- package/src/cli/install-receipt.js +171 -0
- package/src/cli/jsonc.js +203 -0
- package/src/cli/package-install-state.js +254 -0
- package/src/cli/package-manager.js +238 -0
- package/src/cli/package-plan.js +185 -0
- package/src/cli/package-version.js +97 -0
- package/src/cli/registry-query.js +207 -0
- package/src/cli/reporting.js +55 -0
- package/src/cli/schema-validation.js +19 -0
- package/src/cli/source-scanner.js +686 -0
- package/src/cli/vite-config.js +478 -0
- package/tokens/banhaten.tokens.json +4444 -331
- package/docs/design-system/components/command-bar.md +0 -57
- package/registry/components/expanded/BannerBoard.tsx +0 -225
- package/registry/components/expanded/CatalogTag.tsx +0 -85
- package/registry/components/expanded/CommandBar.tsx +0 -386
- package/registry/components/expanded/IconExplorer.tsx +0 -612
- package/registry/components/expanded/TimelineShowcase.tsx +0 -158
- package/registry/components/expanded/commandBar.css +0 -470
- package/registry/components/expanded/iconExplorer.css +0 -764
- package/registry/components/expanded/iconPacks.ts +0 -866
- package/registry/components/expanded/tag.css +0 -148
- package/registry/examples/expanded/icons-demo.tsx +0 -5
- package/tokens/Color mode.zip +0 -0
- package/tokens/Numbers.zip +0 -0
- package/tokens/Radius.zip +0 -0
- package/tokens/Theme.zip +0 -0
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { cva } from "class-variance-authority"
|
|
12
12
|
|
|
13
13
|
import { cn } from "@/lib/utils"
|
|
14
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover"
|
|
14
15
|
import {
|
|
15
16
|
addMonths,
|
|
16
17
|
formatCalendarDate,
|
|
@@ -34,6 +35,8 @@ import type {
|
|
|
34
35
|
RangeCalendarType,
|
|
35
36
|
} from "./date-picker-state"
|
|
36
37
|
|
|
38
|
+
type ControlDensity = "compact" | "default" | "comfortable"
|
|
39
|
+
|
|
37
40
|
type CalendarTimeValue = {
|
|
38
41
|
hours?: string
|
|
39
42
|
minutes?: string
|
|
@@ -45,6 +48,7 @@ type CalendarMonthYearItem = {
|
|
|
45
48
|
id?: string
|
|
46
49
|
month: React.ReactNode
|
|
47
50
|
year: React.ReactNode
|
|
51
|
+
value?: Date
|
|
48
52
|
selected?: boolean
|
|
49
53
|
disabled?: boolean
|
|
50
54
|
}
|
|
@@ -111,6 +115,7 @@ type DatePickerProps = Omit<
|
|
|
111
115
|
>
|
|
112
116
|
defaultOpen?: boolean
|
|
113
117
|
defaultValue?: Date
|
|
118
|
+
density?: ControlDensity
|
|
114
119
|
dir?: DatePickerDirection
|
|
115
120
|
disabled?: boolean
|
|
116
121
|
icon?: React.ReactNode
|
|
@@ -119,6 +124,10 @@ type DatePickerProps = Omit<
|
|
|
119
124
|
open?: boolean
|
|
120
125
|
placeholder?: React.ReactNode
|
|
121
126
|
triggerClassName?: string
|
|
127
|
+
triggerProps?: Omit<
|
|
128
|
+
DatePickerTriggerProps,
|
|
129
|
+
"density" | "icon" | "open" | "placeholder" | "value"
|
|
130
|
+
>
|
|
122
131
|
value?: Date
|
|
123
132
|
}
|
|
124
133
|
|
|
@@ -132,6 +141,7 @@ type DateRangePickerProps = Omit<
|
|
|
132
141
|
>
|
|
133
142
|
defaultOpen?: boolean
|
|
134
143
|
defaultValue?: CalendarRange
|
|
144
|
+
density?: ControlDensity
|
|
135
145
|
dir?: DatePickerDirection
|
|
136
146
|
disabled?: boolean
|
|
137
147
|
icon?: React.ReactNode
|
|
@@ -140,43 +150,24 @@ type DateRangePickerProps = Omit<
|
|
|
140
150
|
open?: boolean
|
|
141
151
|
placeholder?: React.ReactNode
|
|
142
152
|
triggerClassName?: string
|
|
153
|
+
triggerProps?: Omit<
|
|
154
|
+
DatePickerTriggerProps,
|
|
155
|
+
"density" | "icon" | "open" | "placeholder" | "value"
|
|
156
|
+
>
|
|
143
157
|
type?: RangeCalendarType
|
|
144
158
|
value?: CalendarRange
|
|
145
159
|
}
|
|
146
160
|
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
zone: "GTM-3",
|
|
158
|
-
}
|
|
159
|
-
const defaultDayLabels = ["S", "M", "T", "W", "T", "F", "S"]
|
|
160
|
-
const defaultMonthYearItems: CalendarMonthYearItem[] = [
|
|
161
|
-
{ month: "April", year: "2023" },
|
|
162
|
-
{ month: "May", year: "2022" },
|
|
163
|
-
{ month: "June", year: "2023" },
|
|
164
|
-
{ month: "July", year: "2024", selected: true },
|
|
165
|
-
{ month: "August", year: "2025" },
|
|
166
|
-
{ month: "September", year: "2026" },
|
|
167
|
-
{ month: "October", year: "2027" },
|
|
168
|
-
]
|
|
169
|
-
const defaultPresets: CalendarPreset[] = [
|
|
170
|
-
{ label: "Today" },
|
|
171
|
-
{ label: "Last 7 days" },
|
|
172
|
-
{ label: "Last 14 days" },
|
|
173
|
-
{ label: "Last 30 days" },
|
|
174
|
-
{ label: "Last 3 months" },
|
|
175
|
-
{ label: "Last 12 months" },
|
|
176
|
-
{ label: "Quarter to date" },
|
|
177
|
-
{ label: "Year to date" },
|
|
178
|
-
{ label: "All time" },
|
|
179
|
-
]
|
|
161
|
+
const defaultTimeValue = getCurrentTimeValue()
|
|
162
|
+
const defaultDayLabels = [
|
|
163
|
+
{ full: "Sunday", short: "S" },
|
|
164
|
+
{ full: "Monday", short: "M" },
|
|
165
|
+
{ full: "Tuesday", short: "T" },
|
|
166
|
+
{ full: "Wednesday", short: "W" },
|
|
167
|
+
{ full: "Thursday", short: "T" },
|
|
168
|
+
{ full: "Friday", short: "F" },
|
|
169
|
+
{ full: "Saturday", short: "S" },
|
|
170
|
+
] as const
|
|
180
171
|
|
|
181
172
|
const calendarSurface = cva(
|
|
182
173
|
[
|
|
@@ -206,14 +197,26 @@ const calendarSurface = cva(
|
|
|
206
197
|
|
|
207
198
|
const pickerTrigger = cva(
|
|
208
199
|
[
|
|
209
|
-
"group/date-trigger relative flex
|
|
210
|
-
"bg-[var(--bh-interactive-input-default)]
|
|
200
|
+
"group/date-trigger relative flex w-[var(--bh-date-trigger-width)] max-w-full items-center gap-[var(--bh-space-md-8)] rounded-[var(--bh-radius-lg-8)]",
|
|
201
|
+
"bg-[var(--bh-interactive-input-default)] text-start outline-none",
|
|
211
202
|
"[--bh-date-trigger-border:var(--bh-border-input)] [--bh-date-trigger-shadow:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-date-trigger-border),var(--shadow-component-default)]",
|
|
212
203
|
"text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)]",
|
|
213
204
|
"text-[var(--bh-content-default)] shadow-[var(--bh-date-trigger-shadow)] transition-[background-color,box-shadow]",
|
|
214
205
|
"hover:bg-[var(--bh-bg-default-hover)] focus-visible:shadow-[var(--shadow-input-focus-ring)]",
|
|
215
206
|
"disabled:pointer-events-none disabled:bg-[var(--bh-interactive-input-disabled)] disabled:text-[var(--bh-content-disabled)] disabled:shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]",
|
|
216
|
-
]
|
|
207
|
+
],
|
|
208
|
+
{
|
|
209
|
+
variants: {
|
|
210
|
+
density: {
|
|
211
|
+
compact: "h-[var(--bh-button-xs-height)] px-[var(--bh-space-md-8)]",
|
|
212
|
+
default: "h-[var(--bh-button-sm-height)] px-[var(--bh-space-lg-10)]",
|
|
213
|
+
comfortable: "h-[var(--bh-date-trigger-height)] px-[var(--bh-space-xl-12)]",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
defaultVariants: {
|
|
217
|
+
density: "comfortable",
|
|
218
|
+
},
|
|
219
|
+
}
|
|
217
220
|
)
|
|
218
221
|
|
|
219
222
|
const monthButton = cva(
|
|
@@ -267,7 +270,7 @@ const dayCellButton = cva(
|
|
|
267
270
|
event:
|
|
268
271
|
"rounded-[var(--bh-control-default)] text-[var(--bh-content-brand-default)] after:absolute after:bottom-[var(--bh-date-event-dot-offset-bottom)] after:left-1/2 after:size-[var(--bh-date-event-dot-size)] after:-translate-x-1/2 after:rounded-[var(--bh-radius-full)] after:bg-[var(--bh-content-brand-default)] rtl:after:translate-x-1/2",
|
|
269
272
|
selected:
|
|
270
|
-
"rounded-[var(--bh-control-default)] bg-[var(--bh-bg-brand-
|
|
273
|
+
"rounded-[var(--bh-control-default)] bg-[var(--bh-bg-brand-bold)] text-[var(--bh-content-on-brand)] hover:bg-[var(--bh-bg-brand-bold)] hover:shadow-[inset_0_0_0_var(--bh-border-width-default)_var(--bh-border-focus)]",
|
|
271
274
|
range:
|
|
272
275
|
"rounded-none bg-[var(--bh-bg-brand-subtle)] text-[var(--bh-content-brand-strong)] hover:bg-[var(--bh-bg-brand-subtle)]",
|
|
273
276
|
},
|
|
@@ -288,18 +291,18 @@ const dateListItem = cva(
|
|
|
288
291
|
|
|
289
292
|
function Calendar({
|
|
290
293
|
className,
|
|
291
|
-
defaultValue
|
|
294
|
+
defaultValue,
|
|
292
295
|
dir,
|
|
293
296
|
displayMonth,
|
|
294
297
|
events,
|
|
295
|
-
monthYearItems
|
|
298
|
+
monthYearItems,
|
|
296
299
|
nextLabel = "Next month",
|
|
297
300
|
onDisplayMonthChange,
|
|
298
301
|
onSelect,
|
|
299
302
|
onViewChange,
|
|
300
303
|
previousLabel = "Previous month",
|
|
301
304
|
selectedDate,
|
|
302
|
-
showTime =
|
|
305
|
+
showTime = false,
|
|
303
306
|
surfaceSize = "calendar",
|
|
304
307
|
timeLabel = "Time",
|
|
305
308
|
timeValue = defaultTimeValue,
|
|
@@ -310,14 +313,16 @@ function Calendar({
|
|
|
310
313
|
}: CalendarProps) {
|
|
311
314
|
const controlledValue = value ?? selectedDate
|
|
312
315
|
const [internalValue, setInternalValue] = React.useState(defaultValue)
|
|
313
|
-
const [internalDisplayMonth, setInternalDisplayMonth] = React.useState(
|
|
314
|
-
startOfMonth(displayMonth ?? controlledValue ?? defaultValue)
|
|
316
|
+
const [internalDisplayMonth, setInternalDisplayMonth] = React.useState(() =>
|
|
317
|
+
startOfMonth(displayMonth ?? controlledValue ?? defaultValue ?? new Date())
|
|
315
318
|
)
|
|
316
319
|
const [internalView, setInternalView] = React.useState<CalendarView>("days")
|
|
317
320
|
const selected = controlledValue ?? internalValue
|
|
318
321
|
const activeDisplayMonth = startOfMonth(displayMonth ?? internalDisplayMonth)
|
|
319
322
|
const activeView = view ?? internalView
|
|
320
|
-
const eventDates = events ?? [
|
|
323
|
+
const eventDates = events ?? []
|
|
324
|
+
const resolvedMonthYearItems =
|
|
325
|
+
monthYearItems ?? createMonthYearItems(activeDisplayMonth)
|
|
321
326
|
|
|
322
327
|
function updateDisplayMonth(nextMonth: Date) {
|
|
323
328
|
const normalized = startOfMonth(nextMonth)
|
|
@@ -372,7 +377,14 @@ function Calendar({
|
|
|
372
377
|
{activeView === "month-year" ? (
|
|
373
378
|
<>
|
|
374
379
|
<div aria-hidden="true" className="h-[var(--bh-date-month-year-spacer-height)] w-full" />
|
|
375
|
-
<MonthYearList
|
|
380
|
+
<MonthYearList
|
|
381
|
+
items={resolvedMonthYearItems}
|
|
382
|
+
onSelect={(item) => {
|
|
383
|
+
if (!item.value) return
|
|
384
|
+
updateDisplayMonth(item.value)
|
|
385
|
+
updateView("days")
|
|
386
|
+
}}
|
|
387
|
+
/>
|
|
376
388
|
</>
|
|
377
389
|
) : null}
|
|
378
390
|
</div>
|
|
@@ -381,17 +393,17 @@ function Calendar({
|
|
|
381
393
|
|
|
382
394
|
function RangeCalendar({
|
|
383
395
|
className,
|
|
384
|
-
defaultValue =
|
|
396
|
+
defaultValue = {},
|
|
385
397
|
dir,
|
|
386
398
|
displayMonth,
|
|
387
399
|
nextLabel = "Next month",
|
|
388
400
|
onDisplayMonthChange,
|
|
389
401
|
onPresetSelect,
|
|
390
402
|
onSelect,
|
|
391
|
-
presets
|
|
403
|
+
presets,
|
|
392
404
|
previousLabel = "Previous month",
|
|
393
405
|
showPresets,
|
|
394
|
-
showTime =
|
|
406
|
+
showTime = false,
|
|
395
407
|
timeLabel = "Time",
|
|
396
408
|
timeValue = defaultTimeValue,
|
|
397
409
|
type = "double",
|
|
@@ -399,13 +411,15 @@ function RangeCalendar({
|
|
|
399
411
|
...props
|
|
400
412
|
}: RangeCalendarProps) {
|
|
401
413
|
const [internalRange, setInternalRange] = React.useState(defaultValue)
|
|
402
|
-
const [internalDisplayMonth, setInternalDisplayMonth] = React.useState(
|
|
403
|
-
startOfMonth(displayMonth ?? defaultValue.from ??
|
|
414
|
+
const [internalDisplayMonth, setInternalDisplayMonth] = React.useState(() =>
|
|
415
|
+
startOfMonth(displayMonth ?? defaultValue.from ?? new Date())
|
|
404
416
|
)
|
|
417
|
+
const [presetReferenceDate] = React.useState(() => startOfDay(new Date()))
|
|
405
418
|
const selectedRange = normalizeRange(value ?? internalRange)
|
|
406
419
|
const activeDisplayMonth = startOfMonth(displayMonth ?? internalDisplayMonth)
|
|
407
420
|
const monthsToRender = type === "single" ? 1 : 2
|
|
408
421
|
const shouldShowPresets = showPresets ?? type === "double-with-presets"
|
|
422
|
+
const presetItems = presets ?? createDefaultPresets(presetReferenceDate)
|
|
409
423
|
const monthNodes = Array.from({ length: monthsToRender }, (_, index) =>
|
|
410
424
|
addMonths(activeDisplayMonth, index)
|
|
411
425
|
)
|
|
@@ -451,12 +465,17 @@ function RangeCalendar({
|
|
|
451
465
|
data-slot="range-calendar-presets"
|
|
452
466
|
className="grid w-[var(--bh-date-presets-width)] shrink-0 border-e border-[var(--bh-border-subtle)] pe-[var(--bh-space-md-8)]"
|
|
453
467
|
>
|
|
454
|
-
{
|
|
468
|
+
{presetItems.map((preset, index) => {
|
|
469
|
+
const disabled =
|
|
470
|
+
Boolean(preset.disabled) ||
|
|
471
|
+
(!preset.range && onPresetSelect === undefined)
|
|
472
|
+
|
|
473
|
+
return (
|
|
455
474
|
<button
|
|
456
|
-
aria-disabled={
|
|
475
|
+
aria-disabled={disabled || undefined}
|
|
457
476
|
className={dateListItem()}
|
|
458
477
|
data-slot="range-calendar-preset"
|
|
459
|
-
disabled={
|
|
478
|
+
disabled={disabled}
|
|
460
479
|
key={preset.id || index}
|
|
461
480
|
type="button"
|
|
462
481
|
onClick={() => selectPreset(preset)}
|
|
@@ -465,7 +484,8 @@ function RangeCalendar({
|
|
|
465
484
|
{preset.label}
|
|
466
485
|
</span>
|
|
467
486
|
</button>
|
|
468
|
-
|
|
487
|
+
)
|
|
488
|
+
})}
|
|
469
489
|
</div>
|
|
470
490
|
) : null}
|
|
471
491
|
|
|
@@ -501,6 +521,7 @@ function DatePicker({
|
|
|
501
521
|
className,
|
|
502
522
|
defaultOpen = false,
|
|
503
523
|
defaultValue,
|
|
524
|
+
density,
|
|
504
525
|
dir,
|
|
505
526
|
disabled,
|
|
506
527
|
icon,
|
|
@@ -509,13 +530,14 @@ function DatePicker({
|
|
|
509
530
|
open,
|
|
510
531
|
placeholder = "Select date",
|
|
511
532
|
triggerClassName,
|
|
533
|
+
triggerProps,
|
|
512
534
|
value,
|
|
513
535
|
...props
|
|
514
536
|
}: DatePickerProps) {
|
|
515
537
|
const [internalOpen, setInternalOpen] = React.useState(defaultOpen)
|
|
516
538
|
const [internalValue, setInternalValue] = React.useState(defaultValue)
|
|
517
539
|
const [internalDisplayMonth, setInternalDisplayMonth] = React.useState(() =>
|
|
518
|
-
startOfMonth(value ?? defaultValue ??
|
|
540
|
+
startOfMonth(value ?? defaultValue ?? new Date())
|
|
519
541
|
)
|
|
520
542
|
const selected = value ?? internalValue
|
|
521
543
|
const isOpen = open ?? internalOpen
|
|
@@ -550,34 +572,41 @@ function DatePicker({
|
|
|
550
572
|
}
|
|
551
573
|
|
|
552
574
|
return (
|
|
553
|
-
<
|
|
575
|
+
<Popover
|
|
576
|
+
open={isOpen}
|
|
577
|
+
onOpenChange={updateOpen}
|
|
554
578
|
data-open={isOpen ? "true" : "false"}
|
|
555
579
|
data-slot="date-picker"
|
|
556
580
|
dir={dir}
|
|
557
581
|
className={cn("relative w-[var(--bh-date-picker-width)] max-w-full", className)}
|
|
558
582
|
{...props}
|
|
559
583
|
>
|
|
560
|
-
<
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
584
|
+
<PopoverTrigger asChild>
|
|
585
|
+
<DatePickerTrigger
|
|
586
|
+
{...triggerProps}
|
|
587
|
+
disabled={disabled ?? triggerProps?.disabled}
|
|
588
|
+
density={density}
|
|
589
|
+
icon={icon}
|
|
590
|
+
open={isOpen}
|
|
591
|
+
placeholder={placeholder}
|
|
592
|
+
value={selected ? formatCalendarDate(selected, { month: "long" }) : undefined}
|
|
593
|
+
className={cn(triggerClassName, triggerProps?.className)}
|
|
594
|
+
/>
|
|
595
|
+
</PopoverTrigger>
|
|
569
596
|
|
|
570
|
-
|
|
597
|
+
<PopoverContent
|
|
598
|
+
align="center"
|
|
599
|
+
showArrow={false}
|
|
600
|
+
size="auto"
|
|
601
|
+
className="w-auto bg-transparent p-0 shadow-none"
|
|
602
|
+
>
|
|
571
603
|
<Calendar
|
|
572
604
|
{...calendarProps}
|
|
573
|
-
defaultValue={defaultValue
|
|
605
|
+
defaultValue={defaultValue}
|
|
574
606
|
displayMonth={calendarProps?.displayMonth ?? internalDisplayMonth}
|
|
575
607
|
selectedDate={selected}
|
|
576
608
|
surfaceSize="picker"
|
|
577
|
-
className={
|
|
578
|
-
"absolute left-1/2 top-[calc(100%+var(--bh-space-md-8))] z-[var(--bh-z-popover)] -translate-x-1/2",
|
|
579
|
-
calendarProps?.className
|
|
580
|
-
)}
|
|
609
|
+
className={calendarProps?.className}
|
|
581
610
|
dir={calendarProps?.dir ?? dir}
|
|
582
611
|
onDisplayMonthChange={updateDisplayMonth}
|
|
583
612
|
onSelect={(nextDate) => {
|
|
@@ -585,8 +614,8 @@ function DatePicker({
|
|
|
585
614
|
calendarProps?.onSelect?.(nextDate)
|
|
586
615
|
}}
|
|
587
616
|
/>
|
|
588
|
-
|
|
589
|
-
</
|
|
617
|
+
</PopoverContent>
|
|
618
|
+
</Popover>
|
|
590
619
|
)
|
|
591
620
|
}
|
|
592
621
|
|
|
@@ -595,6 +624,7 @@ function DateRangePicker({
|
|
|
595
624
|
className,
|
|
596
625
|
defaultOpen = false,
|
|
597
626
|
defaultValue,
|
|
627
|
+
density,
|
|
598
628
|
dir,
|
|
599
629
|
disabled,
|
|
600
630
|
icon,
|
|
@@ -603,6 +633,7 @@ function DateRangePicker({
|
|
|
603
633
|
open,
|
|
604
634
|
placeholder = "Select date range",
|
|
605
635
|
triggerClassName,
|
|
636
|
+
triggerProps,
|
|
606
637
|
type = "double",
|
|
607
638
|
value,
|
|
608
639
|
...props
|
|
@@ -612,7 +643,7 @@ function DateRangePicker({
|
|
|
612
643
|
const selectedRange = normalizeRange(value ?? internalValue ?? {})
|
|
613
644
|
const isOpen = open ?? internalOpen
|
|
614
645
|
const [internalDisplayMonth, setInternalDisplayMonth] = React.useState(() =>
|
|
615
|
-
startOfMonth(selectedRange.from ?? defaultValue?.from ??
|
|
646
|
+
startOfMonth(selectedRange.from ?? defaultValue?.from ?? new Date())
|
|
616
647
|
)
|
|
617
648
|
const selectedRangeFromTime = selectedRange.from?.getTime()
|
|
618
649
|
|
|
@@ -645,7 +676,9 @@ function DateRangePicker({
|
|
|
645
676
|
}
|
|
646
677
|
|
|
647
678
|
return (
|
|
648
|
-
<
|
|
679
|
+
<Popover
|
|
680
|
+
open={isOpen}
|
|
681
|
+
onOpenChange={updateOpen}
|
|
649
682
|
data-open={isOpen ? "true" : "false"}
|
|
650
683
|
data-slot="date-range-picker"
|
|
651
684
|
data-type={type}
|
|
@@ -653,27 +686,32 @@ function DateRangePicker({
|
|
|
653
686
|
className={cn("relative w-[var(--bh-date-picker-width)] max-w-full", className)}
|
|
654
687
|
{...props}
|
|
655
688
|
>
|
|
656
|
-
<
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
689
|
+
<PopoverTrigger asChild>
|
|
690
|
+
<DatePickerTrigger
|
|
691
|
+
{...triggerProps}
|
|
692
|
+
disabled={disabled ?? triggerProps?.disabled}
|
|
693
|
+
density={density}
|
|
694
|
+
icon={icon}
|
|
695
|
+
open={isOpen}
|
|
696
|
+
placeholder={placeholder}
|
|
697
|
+
value={formatCalendarRange(selectedRange)}
|
|
698
|
+
className={cn(triggerClassName, triggerProps?.className)}
|
|
699
|
+
/>
|
|
700
|
+
</PopoverTrigger>
|
|
665
701
|
|
|
666
|
-
|
|
702
|
+
<PopoverContent
|
|
703
|
+
align="center"
|
|
704
|
+
showArrow={false}
|
|
705
|
+
size="auto"
|
|
706
|
+
className="w-auto bg-transparent p-0 shadow-none"
|
|
707
|
+
>
|
|
667
708
|
<RangeCalendar
|
|
668
709
|
{...calendarProps}
|
|
669
|
-
defaultValue={defaultValue ??
|
|
710
|
+
defaultValue={defaultValue ?? {}}
|
|
670
711
|
displayMonth={calendarProps?.displayMonth ?? internalDisplayMonth}
|
|
671
712
|
type={type}
|
|
672
713
|
value={selectedRange}
|
|
673
|
-
className={
|
|
674
|
-
"absolute left-1/2 top-[calc(100%+var(--bh-space-md-8))] z-[var(--bh-z-popover)] -translate-x-1/2",
|
|
675
|
-
calendarProps?.className
|
|
676
|
-
)}
|
|
714
|
+
className={calendarProps?.className}
|
|
677
715
|
dir={calendarProps?.dir ?? dir}
|
|
678
716
|
onDisplayMonthChange={updateDisplayMonth}
|
|
679
717
|
onSelect={(nextRange) => {
|
|
@@ -681,45 +719,54 @@ function DateRangePicker({
|
|
|
681
719
|
calendarProps?.onSelect?.(nextRange)
|
|
682
720
|
}}
|
|
683
721
|
/>
|
|
684
|
-
|
|
685
|
-
</
|
|
722
|
+
</PopoverContent>
|
|
723
|
+
</Popover>
|
|
686
724
|
)
|
|
687
725
|
}
|
|
688
726
|
|
|
727
|
+
type DatePickerTriggerProps = Omit<
|
|
728
|
+
React.ComponentProps<"button">,
|
|
729
|
+
"children" | "value"
|
|
730
|
+
> & {
|
|
731
|
+
density?: ControlDensity
|
|
732
|
+
icon?: React.ReactNode
|
|
733
|
+
open?: boolean
|
|
734
|
+
placeholder: React.ReactNode
|
|
735
|
+
value?: React.ReactNode
|
|
736
|
+
}
|
|
737
|
+
|
|
689
738
|
function DatePickerTrigger({
|
|
690
739
|
className,
|
|
740
|
+
density,
|
|
691
741
|
disabled,
|
|
692
742
|
icon,
|
|
693
743
|
onClick,
|
|
694
744
|
open,
|
|
695
745
|
placeholder,
|
|
746
|
+
type = "button",
|
|
696
747
|
value,
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
disabled?: boolean
|
|
700
|
-
icon?: React.ReactNode
|
|
701
|
-
onClick?: React.MouseEventHandler<HTMLButtonElement>
|
|
702
|
-
open?: boolean
|
|
703
|
-
placeholder: React.ReactNode
|
|
704
|
-
value?: React.ReactNode
|
|
705
|
-
}) {
|
|
748
|
+
...props
|
|
749
|
+
}: DatePickerTriggerProps) {
|
|
706
750
|
const hasValue = hasRenderableContent(value)
|
|
707
751
|
|
|
708
752
|
return (
|
|
709
753
|
<button
|
|
710
754
|
aria-expanded={open || undefined}
|
|
755
|
+
data-date-picker-trigger=""
|
|
756
|
+
data-density={density ?? "comfortable"}
|
|
711
757
|
data-slot="date-picker-trigger"
|
|
712
758
|
disabled={disabled}
|
|
713
|
-
type=
|
|
714
|
-
className={cn(pickerTrigger(), className)}
|
|
759
|
+
type={type}
|
|
760
|
+
className={cn(pickerTrigger({ density }), className)}
|
|
715
761
|
onClick={onClick}
|
|
762
|
+
{...props}
|
|
716
763
|
>
|
|
717
764
|
<span
|
|
718
765
|
aria-hidden="true"
|
|
719
766
|
data-slot="date-picker-trigger-icon"
|
|
720
767
|
className="flex size-[var(--bh-date-trigger-icon-size)] shrink-0 items-center justify-center text-[var(--bh-content-muted)] group-disabled/date-trigger:text-[var(--bh-content-disabled)] [&_svg:not([class*='size-'])]:size-[var(--bh-date-trigger-icon-size)]"
|
|
721
768
|
>
|
|
722
|
-
{icon ?? <CalendarDaysIcon strokeWidth=
|
|
769
|
+
{icon ?? <CalendarDaysIcon strokeWidth="var(--bh-icon-stroke-180)" />}
|
|
723
770
|
</span>
|
|
724
771
|
<span
|
|
725
772
|
data-slot="date-picker-trigger-value"
|
|
@@ -777,6 +824,7 @@ function CalendarMonth({
|
|
|
777
824
|
}) {
|
|
778
825
|
const cells = React.useMemo(() => getCalendarCells(displayMonth), [displayMonth])
|
|
779
826
|
const gridRef = React.useRef<HTMLDivElement>(null)
|
|
827
|
+
const titleId = React.useId()
|
|
780
828
|
const pendingFocusDateRef = React.useRef<Date | undefined>(undefined)
|
|
781
829
|
const monthTitle = title ?? formatMonthTitle(displayMonth)
|
|
782
830
|
const hasMonthToggle = mode === "single"
|
|
@@ -832,6 +880,7 @@ function CalendarMonth({
|
|
|
832
880
|
<button
|
|
833
881
|
aria-expanded={view === "month-year"}
|
|
834
882
|
data-slot="calendar-title"
|
|
883
|
+
id={titleId}
|
|
835
884
|
type="button"
|
|
836
885
|
className={calendarTitleButton({
|
|
837
886
|
active: view === "month-year",
|
|
@@ -849,6 +898,7 @@ function CalendarMonth({
|
|
|
849
898
|
) : (
|
|
850
899
|
<div
|
|
851
900
|
data-slot="calendar-title"
|
|
901
|
+
id={titleId}
|
|
852
902
|
className={calendarTitleButton({ framed: false })}
|
|
853
903
|
>
|
|
854
904
|
<span className="min-w-0 truncate">{monthTitle}</span>
|
|
@@ -869,39 +919,54 @@ function CalendarMonth({
|
|
|
869
919
|
{view === "days" ? (
|
|
870
920
|
<>
|
|
871
921
|
<div
|
|
872
|
-
aria-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
>
|
|
876
|
-
{defaultDayLabels.map((day, index) => (
|
|
877
|
-
<div
|
|
878
|
-
className="flex h-[var(--bh-date-day-cell-height)] min-w-0 items-center justify-center rounded-[var(--bh-radius-lg-8)] text-center text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
|
|
879
|
-
key={`${day}-${index}`}
|
|
880
|
-
>
|
|
881
|
-
{day}
|
|
882
|
-
</div>
|
|
883
|
-
))}
|
|
884
|
-
</div>
|
|
885
|
-
|
|
886
|
-
<div
|
|
922
|
+
aria-colcount={7}
|
|
923
|
+
aria-labelledby={titleId}
|
|
924
|
+
aria-rowcount={Math.ceil(cells.length / 7) + 1}
|
|
887
925
|
data-slot="calendar-grid"
|
|
888
926
|
role="grid"
|
|
889
927
|
ref={gridRef}
|
|
890
|
-
className="grid w-full
|
|
928
|
+
className="grid w-full gap-[var(--bh-space-md-8)]"
|
|
891
929
|
>
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
930
|
+
<div
|
|
931
|
+
data-slot="calendar-weekdays"
|
|
932
|
+
role="row"
|
|
933
|
+
className="grid w-full grid-cols-7"
|
|
934
|
+
>
|
|
935
|
+
{defaultDayLabels.map((day) => (
|
|
936
|
+
<div
|
|
937
|
+
aria-label={day.full}
|
|
938
|
+
className="flex h-[var(--bh-date-day-cell-height)] min-w-0 items-center justify-center rounded-[var(--bh-radius-lg-8)] text-center text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
|
|
939
|
+
key={day.full}
|
|
940
|
+
role="columnheader"
|
|
941
|
+
>
|
|
942
|
+
{day.short}
|
|
943
|
+
</div>
|
|
944
|
+
))}
|
|
945
|
+
</div>
|
|
946
|
+
|
|
947
|
+
<div role="rowgroup" className="grid w-full">
|
|
948
|
+
{chunkCalendarRows(cells).map((row, rowIndex) => (
|
|
949
|
+
<div
|
|
950
|
+
className="grid w-full grid-cols-7"
|
|
951
|
+
key={row[0]?.date.toISOString() ?? rowIndex}
|
|
952
|
+
role="row"
|
|
953
|
+
>
|
|
954
|
+
{row.map((cell) => (
|
|
955
|
+
<CalendarDayCell
|
|
956
|
+
cell={cell}
|
|
957
|
+
displayMonth={displayMonth}
|
|
958
|
+
eventDates={eventDates}
|
|
959
|
+
key={cell.date.toISOString()}
|
|
960
|
+
mode={mode}
|
|
961
|
+
onKeyboardNavigateDate={handleKeyboardNavigateDate}
|
|
962
|
+
onSelectDate={onSelectDate}
|
|
963
|
+
range={range}
|
|
964
|
+
selectedDate={selectedDate}
|
|
965
|
+
/>
|
|
966
|
+
))}
|
|
967
|
+
</div>
|
|
968
|
+
))}
|
|
969
|
+
</div>
|
|
905
970
|
</div>
|
|
906
971
|
|
|
907
972
|
{showTime ? (
|
|
@@ -916,6 +981,16 @@ function CalendarMonth({
|
|
|
916
981
|
)
|
|
917
982
|
}
|
|
918
983
|
|
|
984
|
+
function chunkCalendarRows(cells: CalendarCell[]) {
|
|
985
|
+
const rows: CalendarCell[][] = []
|
|
986
|
+
|
|
987
|
+
for (let index = 0; index < cells.length; index += 7) {
|
|
988
|
+
rows.push(cells.slice(index, index + 7))
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
return rows
|
|
992
|
+
}
|
|
993
|
+
|
|
919
994
|
function CalendarDayCell({
|
|
920
995
|
cell,
|
|
921
996
|
displayMonth,
|
|
@@ -1003,7 +1078,7 @@ function CalendarTime({
|
|
|
1003
1078
|
{label}
|
|
1004
1079
|
</span>
|
|
1005
1080
|
<span
|
|
1006
|
-
data-slot="calendar-time-
|
|
1081
|
+
data-slot="calendar-time-value"
|
|
1007
1082
|
className="flex min-h-[var(--bh-date-time-input-min-height)] min-w-0 flex-1 items-center gap-[var(--bh-space-xs-4)] rounded-[var(--bh-radius-lg-8)] bg-[var(--bh-interactive-input-default)] px-[var(--bh-space-md-8)] py-[var(--bh-space-sm-6)] shadow-[var(--shadow-date-time-input)]"
|
|
1008
1083
|
>
|
|
1009
1084
|
<CalendarTimeSegment active>{resolved.hours}</CalendarTimeSegment>
|
|
@@ -1039,7 +1114,13 @@ function CalendarTimeSegment({
|
|
|
1039
1114
|
)
|
|
1040
1115
|
}
|
|
1041
1116
|
|
|
1042
|
-
function MonthYearList({
|
|
1117
|
+
function MonthYearList({
|
|
1118
|
+
items,
|
|
1119
|
+
onSelect,
|
|
1120
|
+
}: {
|
|
1121
|
+
items: CalendarMonthYearItem[]
|
|
1122
|
+
onSelect: (item: CalendarMonthYearItem) => void
|
|
1123
|
+
}) {
|
|
1043
1124
|
return (
|
|
1044
1125
|
<div
|
|
1045
1126
|
data-slot="calendar-month-year-list"
|
|
@@ -1055,8 +1136,9 @@ function MonthYearList({ items }: { items: CalendarMonthYearItem[] }) {
|
|
|
1055
1136
|
)}
|
|
1056
1137
|
data-selected={item.selected ? "true" : undefined}
|
|
1057
1138
|
data-slot="calendar-month-year-item"
|
|
1058
|
-
disabled={item.disabled}
|
|
1139
|
+
disabled={item.disabled || !item.value}
|
|
1059
1140
|
key={item.id || index}
|
|
1141
|
+
onClick={() => onSelect(item)}
|
|
1060
1142
|
type="button"
|
|
1061
1143
|
>
|
|
1062
1144
|
<span className="truncate">{item.month}</span>
|
|
@@ -1067,6 +1149,99 @@ function MonthYearList({ items }: { items: CalendarMonthYearItem[] }) {
|
|
|
1067
1149
|
)
|
|
1068
1150
|
}
|
|
1069
1151
|
|
|
1152
|
+
function startOfDay(date: Date) {
|
|
1153
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function addDays(date: Date, amount: number) {
|
|
1157
|
+
const next = startOfDay(date)
|
|
1158
|
+
next.setDate(next.getDate() + amount)
|
|
1159
|
+
return next
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
function createDefaultPresets(today: Date): CalendarPreset[] {
|
|
1163
|
+
const quarterStartMonth = Math.floor(today.getMonth() / 3) * 3
|
|
1164
|
+
|
|
1165
|
+
return [
|
|
1166
|
+
{ id: "today", label: "Today", range: { from: today, to: today } },
|
|
1167
|
+
{
|
|
1168
|
+
id: "last-7-days",
|
|
1169
|
+
label: "Last 7 days",
|
|
1170
|
+
range: { from: addDays(today, -6), to: today },
|
|
1171
|
+
},
|
|
1172
|
+
{
|
|
1173
|
+
id: "last-14-days",
|
|
1174
|
+
label: "Last 14 days",
|
|
1175
|
+
range: { from: addDays(today, -13), to: today },
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
id: "last-30-days",
|
|
1179
|
+
label: "Last 30 days",
|
|
1180
|
+
range: { from: addDays(today, -29), to: today },
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
id: "last-3-months",
|
|
1184
|
+
label: "Last 3 months",
|
|
1185
|
+
range: { from: addMonths(today, -3), to: today },
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
id: "last-12-months",
|
|
1189
|
+
label: "Last 12 months",
|
|
1190
|
+
range: { from: addMonths(today, -12), to: today },
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
id: "quarter-to-date",
|
|
1194
|
+
label: "Quarter to date",
|
|
1195
|
+
range: {
|
|
1196
|
+
from: new Date(today.getFullYear(), quarterStartMonth, 1),
|
|
1197
|
+
to: today,
|
|
1198
|
+
},
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
id: "year-to-date",
|
|
1202
|
+
label: "Year to date",
|
|
1203
|
+
range: { from: new Date(today.getFullYear(), 0, 1), to: today },
|
|
1204
|
+
},
|
|
1205
|
+
]
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
function createMonthYearItems(
|
|
1209
|
+
displayMonth: Date
|
|
1210
|
+
): CalendarMonthYearItem[] {
|
|
1211
|
+
return Array.from({ length: 7 }, (_, index) => {
|
|
1212
|
+
const value = addMonths(displayMonth, index - 3)
|
|
1213
|
+
|
|
1214
|
+
return {
|
|
1215
|
+
id: `${value.getFullYear()}-${value.getMonth()}`,
|
|
1216
|
+
month: new Intl.DateTimeFormat(undefined, { month: "long" }).format(value),
|
|
1217
|
+
selected: index === 3,
|
|
1218
|
+
value,
|
|
1219
|
+
year: String(value.getFullYear()),
|
|
1220
|
+
}
|
|
1221
|
+
})
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
function getCurrentTimeValue(): Required<CalendarTimeValue> {
|
|
1225
|
+
const now = new Date()
|
|
1226
|
+
const timeParts = new Intl.DateTimeFormat("en-US", {
|
|
1227
|
+
hour: "2-digit",
|
|
1228
|
+
hour12: true,
|
|
1229
|
+
minute: "2-digit",
|
|
1230
|
+
}).formatToParts(now)
|
|
1231
|
+
const zoneParts = new Intl.DateTimeFormat("en-US", {
|
|
1232
|
+
timeZoneName: "short",
|
|
1233
|
+
}).formatToParts(now)
|
|
1234
|
+
const getPart = (parts: Intl.DateTimeFormatPart[], type: string) =>
|
|
1235
|
+
parts.find((part) => part.type === type)?.value ?? ""
|
|
1236
|
+
|
|
1237
|
+
return {
|
|
1238
|
+
hours: getPart(timeParts, "hour"),
|
|
1239
|
+
minutes: getPart(timeParts, "minute"),
|
|
1240
|
+
period: getPart(timeParts, "dayPeriod"),
|
|
1241
|
+
zone: getPart(zoneParts, "timeZoneName") || "UTC",
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1070
1245
|
function hasRenderableContent(content: React.ReactNode) {
|
|
1071
1246
|
return (
|
|
1072
1247
|
content !== undefined &&
|
|
@@ -1094,7 +1269,9 @@ export type {
|
|
|
1094
1269
|
CalendarView,
|
|
1095
1270
|
DatePickerDirection,
|
|
1096
1271
|
DatePickerProps,
|
|
1272
|
+
DatePickerTriggerProps,
|
|
1097
1273
|
DateRangePickerProps,
|
|
1274
|
+
ControlDensity,
|
|
1098
1275
|
RangeCalendarProps,
|
|
1099
1276
|
RangeCalendarType,
|
|
1100
1277
|
}
|