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.
Files changed (275) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/CONTRIBUTING.md +44 -0
  3. package/README.md +65 -13
  4. package/SECURITY.md +15 -0
  5. package/banhaten.config.example.json +0 -2
  6. package/docs/design-system/README.md +9 -0
  7. package/docs/design-system/adoption-quality-remediation.md +87 -0
  8. package/docs/design-system/agent-usage.md +13 -0
  9. package/docs/design-system/appearance-presets.md +6 -1
  10. package/docs/design-system/component-docs-pattern.md +81 -0
  11. package/docs/design-system/component-implementation-ownership-audit.md +244 -0
  12. package/docs/design-system/component-quality-audit-2026-08-20.md +223 -0
  13. package/docs/design-system/component-showcase-consistency-report.md +2 -0
  14. package/docs/design-system/component-token-consistency-audit.md +13 -3
  15. package/docs/design-system/components/README.md +4 -2
  16. package/docs/design-system/components/accordion.md +16 -0
  17. package/docs/design-system/components/activity-feed.md +17 -1
  18. package/docs/design-system/components/activity-heatmap.md +73 -0
  19. package/docs/design-system/components/alert-dialog.md +17 -0
  20. package/docs/design-system/components/alert.md +46 -14
  21. package/docs/design-system/components/aspect-ratio.md +15 -0
  22. package/docs/design-system/components/attribute.md +16 -0
  23. package/docs/design-system/components/autocomplete.md +19 -0
  24. package/docs/design-system/components/avatar.md +18 -0
  25. package/docs/design-system/components/badge.md +19 -0
  26. package/docs/design-system/components/banner.md +19 -1
  27. package/docs/design-system/components/breadcrumbs.md +17 -0
  28. package/docs/design-system/components/button-group.md +35 -0
  29. package/docs/design-system/components/button.md +18 -0
  30. package/docs/design-system/components/card.md +26 -1
  31. package/docs/design-system/components/carousel.md +20 -3
  32. package/docs/design-system/components/catalog-components.md +18 -1
  33. package/docs/design-system/components/chart.md +15 -0
  34. package/docs/design-system/components/checkbox.md +16 -0
  35. package/docs/design-system/components/collapsible.md +15 -0
  36. package/docs/design-system/components/command.md +15 -0
  37. package/docs/design-system/components/context-menu.md +15 -0
  38. package/docs/design-system/components/date-picker.md +21 -1
  39. package/docs/design-system/components/divider.md +16 -0
  40. package/docs/design-system/components/empty-state.md +17 -0
  41. package/docs/design-system/components/field.md +17 -0
  42. package/docs/design-system/components/file-upload.md +18 -0
  43. package/docs/design-system/components/hover-card.md +15 -0
  44. package/docs/design-system/components/input-group.md +17 -0
  45. package/docs/design-system/components/input-otp.md +18 -0
  46. package/docs/design-system/components/input.md +43 -7
  47. package/docs/design-system/components/item.md +72 -0
  48. package/docs/design-system/components/kbd.md +18 -0
  49. package/docs/design-system/components/label.md +16 -0
  50. package/docs/design-system/components/menu.md +17 -0
  51. package/docs/design-system/components/menubar.md +15 -0
  52. package/docs/design-system/components/modal.md +25 -4
  53. package/docs/design-system/components/native-select.md +18 -0
  54. package/docs/design-system/components/navigation-menu.md +15 -0
  55. package/docs/design-system/components/onboarding-step-list-item.md +16 -1
  56. package/docs/design-system/components/page-header.md +17 -1
  57. package/docs/design-system/components/pagination.md +31 -8
  58. package/docs/design-system/components/popover.md +19 -0
  59. package/docs/design-system/components/progress-slider.md +18 -0
  60. package/docs/design-system/components/progress.md +17 -0
  61. package/docs/design-system/components/radio-card.md +16 -0
  62. package/docs/design-system/components/radio-group.md +17 -0
  63. package/docs/design-system/components/resizable.md +15 -0
  64. package/docs/design-system/components/scroll-area.md +20 -1
  65. package/docs/design-system/components/segmented-control.md +71 -0
  66. package/docs/design-system/components/select.md +44 -7
  67. package/docs/design-system/components/sheet.md +17 -0
  68. package/docs/design-system/components/sidebar.md +17 -0
  69. package/docs/design-system/components/skeleton.md +18 -0
  70. package/docs/design-system/components/slideout.md +17 -0
  71. package/docs/design-system/components/slider.md +20 -0
  72. package/docs/design-system/components/social-button.md +21 -3
  73. package/docs/design-system/components/spinner.md +16 -0
  74. package/docs/design-system/components/steps.md +19 -0
  75. package/docs/design-system/components/table.md +36 -4
  76. package/docs/design-system/components/tabs.md +17 -0
  77. package/docs/design-system/components/tag.md +17 -0
  78. package/docs/design-system/components/textarea.md +16 -0
  79. package/docs/design-system/components/timeline.md +17 -0
  80. package/docs/design-system/components/toast.md +17 -0
  81. package/docs/design-system/components/toggle.md +17 -0
  82. package/docs/design-system/components/toolbar.md +21 -1
  83. package/docs/design-system/components/tooltip.md +18 -1
  84. package/docs/design-system/density-and-elevation.md +61 -0
  85. package/docs/design-system/direction-and-localization.md +23 -0
  86. package/docs/design-system/form-recipes.md +73 -0
  87. package/docs/design-system/gap-promotion-policy.md +21 -0
  88. package/docs/design-system/gap-promotion-report.md +100 -0
  89. package/docs/design-system/installation.md +68 -0
  90. package/docs/design-system/launch-comparison-shadcn.md +98 -0
  91. package/docs/design-system/registry-and-cli.md +30 -7
  92. package/docs/design-system/release-policy.md +30 -0
  93. package/docs/design-system/roadmap.md +14 -0
  94. package/docs/design-system/support-matrix.md +23 -0
  95. package/docs/design-system/tabs.md +1 -1
  96. package/docs/design-system/token-governance.md +23 -1
  97. package/package.json +30 -15
  98. package/registry/component-contract.json +40 -0
  99. package/registry/components/alert-dialog.tsx +2 -2
  100. package/registry/components/alert.tsx +101 -76
  101. package/registry/components/autocomplete.tsx +23 -9
  102. package/registry/components/avatar.tsx +14 -10
  103. package/registry/components/badge.tsx +18 -18
  104. package/registry/components/button-group.tsx +324 -5
  105. package/registry/components/button.tsx +38 -17
  106. package/registry/components/card.tsx +87 -62
  107. package/registry/components/carousel.tsx +67 -5
  108. package/registry/components/chart.tsx +36 -3
  109. package/registry/components/checkbox.tsx +3 -3
  110. package/registry/components/collapsible.tsx +1 -1
  111. package/registry/components/command.tsx +38 -4
  112. package/registry/components/context-menu.tsx +1 -1
  113. package/registry/components/date-picker.tsx +312 -135
  114. package/registry/components/expanded/ActivityFeed.tsx +9 -22
  115. package/registry/components/expanded/ActivityHeatmap.tsx +64 -0
  116. package/registry/components/expanded/Banner.tsx +3 -4
  117. package/registry/components/expanded/Breadcrumbs.tsx +8 -5
  118. package/registry/components/expanded/CatalogComponentsShowcase.tsx +16 -17
  119. package/registry/components/expanded/CatalogDivider.tsx +4 -3
  120. package/registry/components/expanded/EmptyState.tsx +1 -1
  121. package/registry/components/expanded/FileUpload.tsx +16 -7
  122. package/registry/components/expanded/OnboardingStepListItem.tsx +2 -6
  123. package/registry/components/expanded/PageHeader.tsx +47 -56
  124. package/registry/components/expanded/Slideout.tsx +29 -424
  125. package/registry/components/expanded/Steps.tsx +151 -13
  126. package/registry/components/expanded/Table.tsx +161 -82
  127. package/registry/components/expanded/Timeline.tsx +14 -27
  128. package/registry/components/expanded/activityFeed.css +9 -9
  129. package/registry/components/expanded/activityHeatmap.css +66 -0
  130. package/registry/components/expanded/banner.css +21 -11
  131. package/registry/components/expanded/breadcrumbs.css +2 -2
  132. package/registry/components/expanded/divider.css +1 -1
  133. package/registry/components/expanded/emptyState.css +1 -1
  134. package/registry/components/expanded/fileUpload.css +7 -7
  135. package/registry/components/expanded/onboardingStepListItem.css +2 -2
  136. package/registry/components/expanded/pageHeader.css +14 -11
  137. package/registry/components/expanded/slideout.css +10 -690
  138. package/registry/components/expanded/steps.css +78 -15
  139. package/registry/components/expanded/table.css +60 -69
  140. package/registry/components/expanded/timeline.css +19 -18
  141. package/registry/components/field.tsx +61 -8
  142. package/registry/components/input-content.ts +1 -0
  143. package/registry/components/input-otp.tsx +13 -3
  144. package/registry/components/input.tsx +68 -43
  145. package/registry/components/item.tsx +189 -0
  146. package/registry/components/kbd.tsx +111 -4
  147. package/registry/components/menu.tsx +87 -251
  148. package/registry/components/menubar.tsx +1 -1
  149. package/registry/components/modal.tsx +143 -103
  150. package/registry/components/native-select.tsx +2 -2
  151. package/registry/components/navigation-menu.tsx +1 -1
  152. package/registry/components/pagination.css +36 -0
  153. package/registry/components/pagination.tsx +88 -64
  154. package/registry/components/popover.tsx +162 -664
  155. package/registry/components/progress-slider.tsx +1 -1
  156. package/registry/components/radio-card.tsx +116 -87
  157. package/registry/components/resizable.tsx +36 -2
  158. package/registry/components/scroll-area.tsx +30 -6
  159. package/registry/components/segmented-control.tsx +87 -0
  160. package/registry/components/select-content.tsx +2 -2
  161. package/registry/components/select.tsx +86 -27
  162. package/registry/components/sheet.tsx +1 -1
  163. package/registry/components/sidebar.tsx +12 -6
  164. package/registry/components/skeleton.css +0 -3
  165. package/registry/components/social-button.tsx +59 -30
  166. package/registry/components/spinner.tsx +18 -55
  167. package/registry/components/table-elements.tsx +128 -0
  168. package/registry/components/tabs.css +1 -1
  169. package/registry/components/tabs.tsx +13 -14
  170. package/registry/components/tag.tsx +3 -3
  171. package/registry/components/textarea.tsx +3 -3
  172. package/registry/components/toast.tsx +16 -11
  173. package/registry/components/toggle.tsx +4 -4
  174. package/registry/components/toolbar.tsx +94 -22
  175. package/registry/components/tooltip.tsx +2 -2
  176. package/registry/examples/alert-demo.tsx +25 -5
  177. package/registry/examples/attribute-demo.tsx +1 -1
  178. package/registry/examples/autocomplete-demo.tsx +3 -3
  179. package/registry/examples/avatar-demo.tsx +14 -11
  180. package/registry/examples/badge-demo.tsx +1 -1
  181. package/registry/examples/button-demo.tsx +6 -6
  182. package/registry/examples/button-group-demo.tsx +23 -2
  183. package/registry/examples/card-demo.tsx +46 -8
  184. package/registry/examples/carousel-demo.tsx +20 -2
  185. package/registry/examples/chart-demo.tsx +1 -1
  186. package/registry/examples/checkbox-demo.tsx +5 -5
  187. package/registry/examples/collection-filter-bar-demo.tsx +135 -0
  188. package/registry/examples/command-demo.tsx +3 -3
  189. package/registry/examples/{expanded/command-bar-demo.tsx → command-palette-demo.tsx} +5 -15
  190. package/registry/examples/context-menu-demo.tsx +1 -1
  191. package/registry/examples/control-density-contract-demo.tsx +61 -0
  192. package/registry/examples/date-picker-demo.tsx +12 -12
  193. package/registry/examples/divider-demo.tsx +3 -3
  194. package/registry/examples/expanded/activity-heatmap-demo.tsx +29 -0
  195. package/registry/examples/expanded/banner-demo.tsx +1 -1
  196. package/registry/examples/expanded/breadcrumbs-demo.tsx +4 -4
  197. package/registry/examples/expanded/empty-state-demo.tsx +1 -1
  198. package/registry/examples/expanded/file-upload-demo.tsx +1 -1
  199. package/registry/examples/expanded/onboarding-step-demo.tsx +1 -1
  200. package/registry/examples/expanded/slideout-demo.tsx +2 -2
  201. package/registry/examples/expanded/steps-demo.tsx +1 -1
  202. package/registry/examples/expanded/tabs-demo.tsx +1 -1
  203. package/registry/examples/expanded/timeline-demo.tsx +1 -1
  204. package/registry/examples/hover-card-demo.tsx +2 -2
  205. package/registry/examples/input-demo.tsx +6 -6
  206. package/registry/examples/input-otp-demo.tsx +4 -4
  207. package/registry/examples/item-demo.tsx +46 -0
  208. package/registry/examples/kbd-demo.tsx +1 -1
  209. package/registry/examples/label-demo.tsx +1 -1
  210. package/registry/examples/menu-demo.tsx +1 -1
  211. package/registry/examples/modal-demo.tsx +67 -9
  212. package/registry/examples/navigation-menu-demo.tsx +1 -1
  213. package/registry/examples/pagination-demo.tsx +10 -7
  214. package/registry/examples/popover-demo.tsx +19 -19
  215. package/registry/examples/progress-demo.tsx +1 -1
  216. package/registry/examples/progress-slider-demo.tsx +1 -1
  217. package/registry/examples/radio-card-demo.tsx +4 -4
  218. package/registry/examples/radio-group-demo.tsx +2 -2
  219. package/registry/examples/resizable-demo.tsx +1 -1
  220. package/registry/examples/results-toolbar-demo.tsx +44 -0
  221. package/registry/examples/scroll-area-demo.tsx +3 -3
  222. package/registry/examples/segmented-control-demo.tsx +39 -0
  223. package/registry/examples/select-demo.tsx +1 -1
  224. package/registry/examples/sidebar-demo.tsx +5 -3
  225. package/registry/examples/slider-demo.tsx +3 -3
  226. package/registry/examples/social-button-demo.tsx +18 -8
  227. package/registry/examples/spinner-demo.tsx +13 -13
  228. package/registry/examples/tabs-demo.tsx +1 -1
  229. package/registry/examples/tag-demo.tsx +1 -1
  230. package/registry/examples/textarea-demo.tsx +4 -4
  231. package/registry/examples/toast-demo.tsx +1 -1
  232. package/registry/examples/toggle-demo.tsx +6 -6
  233. package/registry/examples/tooltip-demo.tsx +4 -4
  234. package/registry/guidance/USAGE.md +27 -0
  235. package/registry/hooks/use-direction.ts +193 -13
  236. package/registry/index.json +5668 -375
  237. package/registry/styles/globals.css +2404 -3999
  238. package/registry.json +156 -149
  239. package/schema/config.schema.json +11 -8
  240. package/schema/registry.schema.json +150 -45
  241. package/schema/tokens.schema.json +134 -34
  242. package/src/cli/command-line.js +194 -0
  243. package/src/cli/config-editors.js +9 -0
  244. package/src/cli/doctor.js +399 -0
  245. package/src/cli/file-transaction.js +295 -0
  246. package/src/cli/global-css.js +153 -0
  247. package/src/cli/index.js +567 -1613
  248. package/src/cli/install-plan.js +592 -0
  249. package/src/cli/install-receipt.js +171 -0
  250. package/src/cli/jsonc.js +203 -0
  251. package/src/cli/package-install-state.js +254 -0
  252. package/src/cli/package-manager.js +238 -0
  253. package/src/cli/package-plan.js +185 -0
  254. package/src/cli/package-version.js +97 -0
  255. package/src/cli/registry-query.js +207 -0
  256. package/src/cli/reporting.js +55 -0
  257. package/src/cli/schema-validation.js +19 -0
  258. package/src/cli/source-scanner.js +686 -0
  259. package/src/cli/vite-config.js +478 -0
  260. package/tokens/banhaten.tokens.json +4444 -331
  261. package/docs/design-system/components/command-bar.md +0 -57
  262. package/registry/components/expanded/BannerBoard.tsx +0 -225
  263. package/registry/components/expanded/CatalogTag.tsx +0 -85
  264. package/registry/components/expanded/CommandBar.tsx +0 -386
  265. package/registry/components/expanded/IconExplorer.tsx +0 -612
  266. package/registry/components/expanded/TimelineShowcase.tsx +0 -158
  267. package/registry/components/expanded/commandBar.css +0 -470
  268. package/registry/components/expanded/iconExplorer.css +0 -764
  269. package/registry/components/expanded/iconPacks.ts +0 -866
  270. package/registry/components/expanded/tag.css +0 -148
  271. package/registry/examples/expanded/icons-demo.tsx +0 -5
  272. package/tokens/Color mode.zip +0 -0
  273. package/tokens/Numbers.zip +0 -0
  274. package/tokens/Radius.zip +0 -0
  275. 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 defaultDisplayMonth = new Date(2050, 6, 1)
148
- const defaultSelectedDate = new Date(2050, 6, 18)
149
- const defaultRange: CalendarRange = {
150
- from: new Date(2050, 6, 15),
151
- to: new Date(2050, 7, 23),
152
- }
153
- const defaultTimeValue: Required<CalendarTimeValue> = {
154
- hours: "05",
155
- minutes: "21",
156
- period: "AM",
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 h-[var(--bh-date-trigger-height)] w-[var(--bh-date-trigger-width)] max-w-full items-center gap-[var(--bh-space-md-8)] rounded-[var(--bh-radius-lg-8)]",
210
- "bg-[var(--bh-interactive-input-default)] px-[var(--bh-space-lg-10)] text-start outline-none",
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-default)] text-[var(--bh-content-on-color)] hover:bg-[var(--bh-bg-brand-default)]",
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 = defaultSelectedDate,
294
+ defaultValue,
292
295
  dir,
293
296
  displayMonth,
294
297
  events,
295
- monthYearItems = defaultMonthYearItems,
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 = true,
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 ?? [new Date(2050, 6, 15)]
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 items={monthYearItems} />
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 = defaultRange,
396
+ defaultValue = {},
385
397
  dir,
386
398
  displayMonth,
387
399
  nextLabel = "Next month",
388
400
  onDisplayMonthChange,
389
401
  onPresetSelect,
390
402
  onSelect,
391
- presets = defaultPresets,
403
+ presets,
392
404
  previousLabel = "Previous month",
393
405
  showPresets,
394
- showTime = true,
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 ?? defaultDisplayMonth)
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
- {presets.map((preset, index) => (
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={preset.disabled || undefined}
475
+ aria-disabled={disabled || undefined}
457
476
  className={dateListItem()}
458
477
  data-slot="range-calendar-preset"
459
- disabled={preset.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 ?? defaultDisplayMonth)
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
- <div
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
- <DatePickerTrigger
561
- disabled={disabled}
562
- icon={icon}
563
- open={isOpen}
564
- placeholder={placeholder}
565
- value={selected ? formatCalendarDate(selected, { month: "long" }) : undefined}
566
- className={triggerClassName}
567
- onClick={() => updateOpen(!isOpen)}
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
- {isOpen ? (
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 ?? defaultSelectedDate}
605
+ defaultValue={defaultValue}
574
606
  displayMonth={calendarProps?.displayMonth ?? internalDisplayMonth}
575
607
  selectedDate={selected}
576
608
  surfaceSize="picker"
577
- className={cn(
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
- ) : null}
589
- </div>
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 ?? defaultDisplayMonth)
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
- <div
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
- <DatePickerTrigger
657
- disabled={disabled}
658
- icon={icon}
659
- open={isOpen}
660
- placeholder={placeholder}
661
- value={formatCalendarRange(selectedRange)}
662
- className={triggerClassName}
663
- onClick={() => updateOpen(!isOpen)}
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
- {isOpen ? (
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 ?? defaultRange}
710
+ defaultValue={defaultValue ?? {}}
670
711
  displayMonth={calendarProps?.displayMonth ?? internalDisplayMonth}
671
712
  type={type}
672
713
  value={selectedRange}
673
- className={cn(
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
- ) : null}
685
- </div>
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
- className?: string
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="button"
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={1.8} />}
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-hidden="true"
873
- data-slot="calendar-weekdays"
874
- className="grid w-full grid-cols-7"
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 grid-cols-7"
928
+ className="grid w-full gap-[var(--bh-space-md-8)]"
891
929
  >
892
- {cells.map((cell) => (
893
- <CalendarDayCell
894
- cell={cell}
895
- displayMonth={displayMonth}
896
- eventDates={eventDates}
897
- key={cell.date.toISOString()}
898
- mode={mode}
899
- onKeyboardNavigateDate={handleKeyboardNavigateDate}
900
- onSelectDate={onSelectDate}
901
- range={range}
902
- selectedDate={selectedDate}
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-input"
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({ items }: { items: CalendarMonthYearItem[] }) {
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
  }