torch-glare 2.5.6 → 2.6.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/README.md +4 -3
- package/dist/bin/index.js +9 -24
- package/dist/bin/index.js.map +1 -1
- package/dist/src/commands/add.d.ts +2 -2
- package/dist/src/commands/add.d.ts.map +1 -1
- package/dist/src/commands/add.js +4 -70
- package/dist/src/commands/add.js.map +1 -1
- package/dist/src/commands/hook.d.ts +5 -4
- package/dist/src/commands/hook.d.ts.map +1 -1
- package/dist/src/commands/hook.js +7 -75
- package/dist/src/commands/hook.js.map +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/layout.d.ts +5 -4
- package/dist/src/commands/layout.d.ts.map +1 -1
- package/dist/src/commands/layout.js +7 -75
- package/dist/src/commands/layout.js.map +1 -1
- package/dist/src/commands/provider.d.ts +5 -4
- package/dist/src/commands/provider.d.ts.map +1 -1
- package/dist/src/commands/provider.js +7 -75
- package/dist/src/commands/provider.js.map +1 -1
- package/dist/src/commands/update.d.ts.map +1 -1
- package/dist/src/commands/update.js +78 -43
- package/dist/src/commands/update.js.map +1 -1
- package/dist/src/commands/utils.d.ts +5 -4
- package/dist/src/commands/utils.d.ts.map +1 -1
- package/dist/src/commands/utils.js +7 -80
- package/dist/src/commands/utils.js.map +1 -1
- package/dist/src/shared/addFromRegistry.d.ts +16 -0
- package/dist/src/shared/addFromRegistry.d.ts.map +1 -0
- package/dist/src/shared/addFromRegistry.js +88 -0
- package/dist/src/shared/addFromRegistry.js.map +1 -0
- package/dist/src/shared/getInstallCommand.d.ts.map +1 -1
- package/dist/src/shared/getInstallCommand.js +5 -0
- package/dist/src/shared/getInstallCommand.js.map +1 -1
- package/dist/src/shared/installDependencies.d.ts +0 -10
- package/dist/src/shared/installDependencies.d.ts.map +1 -1
- package/dist/src/shared/installDependencies.js +0 -17
- package/dist/src/shared/installDependencies.js.map +1 -1
- package/dist/src/shared/installFromPlan.d.ts +7 -5
- package/dist/src/shared/installFromPlan.d.ts.map +1 -1
- package/dist/src/shared/installFromPlan.js +103 -36
- package/dist/src/shared/installFromPlan.js.map +1 -1
- package/dist/src/shared/loadRegistry.d.ts +12 -3
- package/dist/src/shared/loadRegistry.d.ts.map +1 -1
- package/dist/src/shared/loadRegistry.js +19 -20
- package/dist/src/shared/loadRegistry.js.map +1 -1
- package/dist/src/shared/registryClient.d.ts +19 -0
- package/dist/src/shared/registryClient.d.ts.map +1 -0
- package/dist/src/shared/registryClient.js +128 -0
- package/dist/src/shared/registryClient.js.map +1 -0
- package/dist/src/shared/resolveEntry.d.ts +10 -7
- package/dist/src/shared/resolveEntry.d.ts.map +1 -1
- package/dist/src/shared/resolveEntry.js +18 -13
- package/dist/src/shared/resolveEntry.js.map +1 -1
- package/dist/src/shared/suggestOtherCommand.d.ts +7 -1
- package/dist/src/shared/suggestOtherCommand.d.ts.map +1 -1
- package/dist/src/shared/suggestOtherCommand.js +29 -23
- package/dist/src/shared/suggestOtherCommand.js.map +1 -1
- package/dist/src/types/main.d.ts +31 -2
- package/dist/src/types/main.d.ts.map +1 -1
- package/package.json +6 -11
- package/apps/lib/components/ActionButton.tsx +0 -93
- package/apps/lib/components/ActionsGroup.tsx +0 -27
- package/apps/lib/components/AlertDialog.tsx +0 -204
- package/apps/lib/components/Avatar.tsx +0 -46
- package/apps/lib/components/Badge.tsx +0 -250
- package/apps/lib/components/BadgeField.tsx +0 -403
- package/apps/lib/components/Breadcrumb.tsx +0 -280
- package/apps/lib/components/Button.tsx +0 -325
- package/apps/lib/components/ButtonGroup.tsx +0 -190
- package/apps/lib/components/Calendar.tsx +0 -115
- package/apps/lib/components/Card.tsx +0 -95
- package/apps/lib/components/Checkbox.tsx +0 -42
- package/apps/lib/components/ColorPicker.tsx +0 -441
- package/apps/lib/components/ConclusionHeader.tsx +0 -148
- package/apps/lib/components/ContextMenu.tsx +0 -571
- package/apps/lib/components/CountBadge.tsx +0 -52
- package/apps/lib/components/DataTable.tsx +0 -213
- package/apps/lib/components/DataViews/badge.ts +0 -49
- package/apps/lib/components/DataViews/cell.tsx +0 -324
- package/apps/lib/components/DataViews/context.ts +0 -144
- package/apps/lib/components/DataViews/data-views.tsx +0 -395
- package/apps/lib/components/DataViews/filters/children.tsx +0 -98
- package/apps/lib/components/DataViews/filters/custom.tsx +0 -34
- package/apps/lib/components/DataViews/filters/filters.tsx +0 -157
- package/apps/lib/components/DataViews/filters/index.ts +0 -4
- package/apps/lib/components/DataViews/filters/labelled.tsx +0 -20
- package/apps/lib/components/DataViews/filters/presets.tsx +0 -65
- package/apps/lib/components/DataViews/filters/sync.tsx +0 -35
- package/apps/lib/components/DataViews/filters/values.ts +0 -173
- package/apps/lib/components/DataViews/header.tsx +0 -220
- package/apps/lib/components/DataViews/hooks/index.ts +0 -5
- package/apps/lib/components/DataViews/hooks/useActiveRow.ts +0 -22
- package/apps/lib/components/DataViews/hooks/useControllable.ts +0 -52
- package/apps/lib/components/DataViews/index.ts +0 -88
- package/apps/lib/components/DataViews/panel/columns.tsx +0 -153
- package/apps/lib/components/DataViews/panel/controls.tsx +0 -106
- package/apps/lib/components/DataViews/panel/index.ts +0 -3
- package/apps/lib/components/DataViews/panel/panel.tsx +0 -164
- package/apps/lib/components/DataViews/panel/saved-views.tsx +0 -67
- package/apps/lib/components/DataViews/panel/section.tsx +0 -81
- package/apps/lib/components/DataViews/panel/sort.tsx +0 -42
- package/apps/lib/components/DataViews/panel/tab.tsx +0 -31
- package/apps/lib/components/DataViews/slots.ts +0 -72
- package/apps/lib/components/DataViews/states.tsx +0 -73
- package/apps/lib/components/DataViews/types.ts +0 -510
- package/apps/lib/components/DataViews/views/board-view.tsx +0 -381
- package/apps/lib/components/DataViews/views/card-rows.tsx +0 -36
- package/apps/lib/components/DataViews/views/inbox-view.tsx +0 -260
- package/apps/lib/components/DataViews/views/pane-views.tsx +0 -196
- package/apps/lib/components/DataViews/views/table-view.tsx +0 -493
- package/apps/lib/components/DataViews/views/tree-view.tsx +0 -367
- package/apps/lib/components/DatePicker.tsx +0 -232
- package/apps/lib/components/Dialog.tsx +0 -116
- package/apps/lib/components/Divider.tsx +0 -28
- package/apps/lib/components/Drawer.tsx +0 -451
- package/apps/lib/components/DropdownMenu.tsx +0 -554
- package/apps/lib/components/FieldHint.tsx +0 -69
- package/apps/lib/components/Form.tsx +0 -182
- package/apps/lib/components/FormBuilder/context.ts +0 -85
- package/apps/lib/components/FormBuilder/field-kind.ts +0 -28
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +0 -60
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +0 -59
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +0 -11
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +0 -32
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +0 -74
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +0 -168
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +0 -39
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +0 -132
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +0 -109
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +0 -31
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +0 -114
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +0 -157
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +0 -67
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +0 -41
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +0 -336
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +0 -213
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +0 -43
- package/apps/lib/components/FormBuilder/fields/countries.ts +0 -303
- package/apps/lib/components/FormBuilder/fields/index.ts +0 -25
- package/apps/lib/components/FormBuilder/form-builder.tsx +0 -198
- package/apps/lib/components/FormBuilder/index.ts +0 -31
- package/apps/lib/components/FormBuilder/numberFormat.ts +0 -16
- package/apps/lib/components/FormBuilder/submit.tsx +0 -67
- package/apps/lib/components/FormBuilder/types.ts +0 -316
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -250
- package/apps/lib/components/FormRenderer/detail.tsx +0 -256
- package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -358
- package/apps/lib/components/FormRenderer/header.tsx +0 -88
- package/apps/lib/components/FormRenderer/index.ts +0 -17
- package/apps/lib/components/FormRenderer/notch-action.tsx +0 -64
- package/apps/lib/components/FormRenderer/section.tsx +0 -39
- package/apps/lib/components/FormRenderer/stepper.tsx +0 -393
- package/apps/lib/components/FormRenderer/types.ts +0 -115
- package/apps/lib/components/FormSummary.tsx +0 -282
- package/apps/lib/components/HeaderBar.tsx +0 -117
- package/apps/lib/components/ImageAttachment.tsx +0 -209
- package/apps/lib/components/InnerLabelField.tsx +0 -154
- package/apps/lib/components/Input.tsx +0 -224
- package/apps/lib/components/InputField.tsx +0 -147
- package/apps/lib/components/InputOTP.tsx +0 -84
- package/apps/lib/components/Label.tsx +0 -129
- package/apps/lib/components/LabelField.tsx +0 -76
- package/apps/lib/components/LabeledCheckBox.tsx +0 -51
- package/apps/lib/components/LabeledRadio.tsx +0 -51
- package/apps/lib/components/LinkButton.tsx +0 -94
- package/apps/lib/components/LoginButton.tsx +0 -51
- package/apps/lib/components/PasswordLevel.tsx +0 -58
- package/apps/lib/components/Popover.tsx +0 -312
- package/apps/lib/components/ProfileMenu.tsx +0 -85
- package/apps/lib/components/Radio.tsx +0 -59
- package/apps/lib/components/RadioCard.tsx +0 -50
- package/apps/lib/components/ScrollArea.tsx +0 -54
- package/apps/lib/components/SearchField.tsx +0 -62
- package/apps/lib/components/SearchableSelect.tsx +0 -318
- package/apps/lib/components/SearchableTable.tsx +0 -344
- package/apps/lib/components/SearchableTree.tsx +0 -485
- package/apps/lib/components/SearchableTreeDialog.tsx +0 -502
- package/apps/lib/components/SectionBlock.tsx +0 -168
- package/apps/lib/components/Select.tsx +0 -347
- package/apps/lib/components/SimpleSelect.tsx +0 -225
- package/apps/lib/components/Skeleton.tsx +0 -12
- package/apps/lib/components/SlideDatePicker.tsx +0 -255
- package/apps/lib/components/SpinLoading.tsx +0 -174
- package/apps/lib/components/Stepper.tsx +0 -480
- package/apps/lib/components/Switch.tsx +0 -82
- package/apps/lib/components/TabFormItem.tsx +0 -153
- package/apps/lib/components/TabSwitch.tsx +0 -172
- package/apps/lib/components/Table.tsx +0 -597
- package/apps/lib/components/TextEditor/ChartBlockTool.ts +0 -676
- package/apps/lib/components/TextEditor/RichTextField.tsx +0 -46
- package/apps/lib/components/TextEditor/TableDnDWrapper.ts +0 -462
- package/apps/lib/components/TextEditor/TextEditor.tsx +0 -1164
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +0 -429
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +0 -70
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +0 -50
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +0 -48
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +0 -98
- package/apps/lib/components/TextEditor/editorjs.d.ts +0 -60
- package/apps/lib/components/TextEditor/index.ts +0 -7
- package/apps/lib/components/TextEditor/markdownParser.ts +0 -346
- package/apps/lib/components/Textarea.tsx +0 -117
- package/apps/lib/components/Timeline.tsx +0 -263
- package/apps/lib/components/Toast.tsx +0 -87
- package/apps/lib/components/Toggle.tsx +0 -125
- package/apps/lib/components/ToggleButton.tsx +0 -140
- package/apps/lib/components/Tooltip.tsx +0 -116
- package/apps/lib/components/TransparentLabel.tsx +0 -71
- package/apps/lib/components/TreeDropDown.tsx +0 -115
- package/apps/lib/components/TreeFolder/TreeFolder.tsx +0 -380
- package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +0 -75
- package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +0 -354
- package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +0 -60
- package/apps/lib/components/TreeFolder/icons.tsx +0 -60
- package/apps/lib/components/TreeFolder/index.ts +0 -24
- package/apps/lib/components/TreeFolder/treeFolderUtils.ts +0 -109
- package/apps/lib/components/TreeFolder/types.ts +0 -77
- package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +0 -118
- package/apps/lib/hooks/useActiveTreeItem.ts +0 -61
- package/apps/lib/hooks/useClickOutside.ts +0 -27
- package/apps/lib/hooks/useDragDrop.tsx +0 -365
- package/apps/lib/hooks/useHtmlDir.ts +0 -31
- package/apps/lib/hooks/useInfiniteScroll.ts +0 -108
- package/apps/lib/hooks/useIsMobile.ts +0 -21
- package/apps/lib/hooks/useResize.ts +0 -67
- package/apps/lib/hooks/useTagSelection.ts +0 -300
- package/apps/lib/layouts/CNLayout.tsx +0 -372
- package/apps/lib/layouts/DataViewCard.tsx +0 -62
- package/apps/lib/layouts/FieldSection.tsx +0 -91
- package/apps/lib/layouts/TreeSubLayout.tsx +0 -173
- package/apps/lib/providers/ThemeProvider.tsx +0 -84
- package/apps/lib/registry.json +0 -1217
- package/apps/lib/tsconfig.json +0 -9
- package/apps/lib/tsconfig.tsbuildinfo +0 -1
- package/apps/lib/utils/cn.ts +0 -6
- package/apps/lib/utils/color.ts +0 -175
- package/apps/lib/utils/dataViews/path.ts +0 -67
- package/apps/lib/utils/dataViews/query.ts +0 -73
- package/apps/lib/utils/dataViews/types.ts +0 -187
- package/apps/lib/utils/dateFormat.ts +0 -108
- package/apps/lib/utils/resize.ts +0 -31
- package/apps/lib/utils/scroller.ts +0 -26
- package/apps/lib/utils/types.ts +0 -16
- package/docs/BLOCKS.md +0 -129
- package/docs/CHANGELOG-1.1.16.md +0 -108
- package/docs/Cover.png +0 -0
- package/docs/README.md +0 -215
- package/docs/components/action-button.md +0 -638
- package/docs/components/actions-group.md +0 -708
- package/docs/components/alert-dialog.md +0 -920
- package/docs/components/avatar.md +0 -709
- package/docs/components/badge-field.md +0 -738
- package/docs/components/badge.md +0 -301
- package/docs/components/breadcrumb.md +0 -930
- package/docs/components/button-group.md +0 -650
- package/docs/components/button.md +0 -639
- package/docs/components/calendar.md +0 -555
- package/docs/components/card.md +0 -622
- package/docs/components/chart-block-tool.md +0 -129
- package/docs/components/checkbox.md +0 -699
- package/docs/components/cn-layout.md +0 -854
- package/docs/components/color-picker.md +0 -101
- package/docs/components/conclusion-header.md +0 -80
- package/docs/components/context-menu.md +0 -483
- package/docs/components/count-badge.md +0 -560
- package/docs/components/data-table.md +0 -854
- package/docs/components/data-views/backend-response.md +0 -324
- package/docs/components/data-views/examples/a11y-rtl.md +0 -250
- package/docs/components/data-views/examples/api-orders-route.md +0 -130
- package/docs/components/data-views/examples/fields.md +0 -362
- package/docs/components/data-views/examples/filters.md +0 -307
- package/docs/components/data-views/examples/inbox-routing.md +0 -218
- package/docs/components/data-views/examples/index.md +0 -29
- package/docs/components/data-views/examples/overview.md +0 -244
- package/docs/components/data-views/examples/panel.md +0 -212
- package/docs/components/data-views/examples/scale.md +0 -231
- package/docs/components/data-views/examples/server-side.md +0 -210
- package/docs/components/data-views/examples/state.md +0 -250
- package/docs/components/data-views/examples/tree-custom.md +0 -388
- package/docs/components/data-views/examples/view-registry.md +0 -313
- package/docs/components/data-views/examples/views.md +0 -534
- package/docs/components/data-views/guide.md +0 -405
- package/docs/components/data-views/index.md +0 -1521
- package/docs/components/data-views/migration.md +0 -81
- package/docs/components/date-picker.md +0 -857
- package/docs/components/dialog.md +0 -929
- package/docs/components/divider.md +0 -766
- package/docs/components/drawer.md +0 -654
- package/docs/components/dropdown-menu.md +0 -668
- package/docs/components/field-hint.md +0 -898
- package/docs/components/field-section.md +0 -781
- package/docs/components/form-builder.md +0 -284
- package/docs/components/form-renderer.md +0 -396
- package/docs/components/form-summary.md +0 -138
- package/docs/components/form.md +0 -785
- package/docs/components/header-bar.md +0 -188
- package/docs/components/image-attachment.md +0 -906
- package/docs/components/inner-label-field.md +0 -819
- package/docs/components/input-field.md +0 -623
- package/docs/components/input-otp.md +0 -756
- package/docs/components/input.md +0 -634
- package/docs/components/label-field.md +0 -770
- package/docs/components/label.md +0 -761
- package/docs/components/labeled-check-box.md +0 -642
- package/docs/components/labeled-radio.md +0 -796
- package/docs/components/link-button.md +0 -685
- package/docs/components/login-button.md +0 -872
- package/docs/components/password-level.md +0 -803
- package/docs/components/popover.md +0 -649
- package/docs/components/profile-menu.md +0 -656
- package/docs/components/radio-card.md +0 -801
- package/docs/components/radio.md +0 -735
- package/docs/components/scroll-area.md +0 -691
- package/docs/components/search-field.md +0 -702
- package/docs/components/searchable-select.md +0 -368
- package/docs/components/searchable-table.md +0 -438
- package/docs/components/searchable-tree-dialog.md +0 -190
- package/docs/components/searchable-tree.md +0 -200
- package/docs/components/section-block.md +0 -483
- package/docs/components/select.md +0 -709
- package/docs/components/simple-select.md +0 -741
- package/docs/components/skeleton.md +0 -706
- package/docs/components/slide-date-picker.md +0 -837
- package/docs/components/spin-loading.md +0 -696
- package/docs/components/stepper.md +0 -316
- package/docs/components/switch.md +0 -770
- package/docs/components/tab-form-item.md +0 -773
- package/docs/components/tab-switch.md +0 -170
- package/docs/components/table-dnd-wrapper.md +0 -107
- package/docs/components/table.md +0 -1088
- package/docs/components/text-editor.md +0 -735
- package/docs/components/textarea.md +0 -681
- package/docs/components/timeline.md +0 -254
- package/docs/components/toast.md +0 -773
- package/docs/components/toggle-button.md +0 -643
- package/docs/components/toggle.md +0 -882
- package/docs/components/tooltip.md +0 -513
- package/docs/components/transparent-label.md +0 -721
- package/docs/components/tree-drop-down.md +0 -740
- package/docs/components/tree-folder.md +0 -110
- package/docs/components/tree-sub-layout.md +0 -707
- package/docs/explanation/architecture.md +0 -54
- package/docs/explanation/design-system.md +0 -52
- package/docs/how-to/form-and-list-recipes.md +0 -386
- package/docs/how-to/forms-with-form-builder.md +0 -472
- package/docs/how-to/guides.md +0 -1027
- package/docs/migration/changelog.md +0 -60
- package/docs/migration/form-builder-2.5.2.md +0 -113
- package/docs/reference/cli.md +0 -112
- package/docs/reference/components.md +0 -572
- package/docs/reference/hooks.md +0 -1523
- package/docs/reference/providers.md +0 -866
- package/docs/reference/tailwind-plugins.md +0 -942
- package/docs/reference/theme.md +0 -85
- package/docs/reference/types.md +0 -1016
- package/docs/reference/utilities.md +0 -726
- package/docs/tutorials/building-first-form.md +0 -846
- package/docs/tutorials/component-composition.md +0 -882
- package/docs/tutorials/getting-started.md +0 -302
- package/docs/tutorials/theming-basics.md +0 -785
|
@@ -1,857 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: DatePicker
|
|
3
|
-
description: Complete date and time picker with calendar dropdown, optional time selection, and input field integration
|
|
4
|
-
group: Date & Time
|
|
5
|
-
keywords: [date-picker, calendar, time, datetime, input, popover]
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# DatePicker
|
|
9
|
-
|
|
10
|
-
> A comprehensive date picker component that combines a calendar dropdown with optional time selection and seamless input field integration. Built on top of the Calendar component with Popover for elegant date and time selection.
|
|
11
|
-
|
|
12
|
-
## Installation
|
|
13
|
-
|
|
14
|
-
TORCH Glare is a copy-in library: the CLI copies this component's source into your project
|
|
15
|
-
(you do **not** install it from the npm package). Run `init` once, then `add`:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npx torch-glare@latest init
|
|
19
|
-
npx torch-glare@latest add DatePicker
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
`add` also copies any components, hooks, and utilities that `DatePicker` depends on.
|
|
23
|
-
|
|
24
|
-
## Import
|
|
25
|
-
|
|
26
|
-
Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
|
|
27
|
-
|
|
28
|
-
```tsx
|
|
29
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Quick Examples
|
|
33
|
-
|
|
34
|
-
### Basic Usage
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
38
|
-
import { useState } from 'react'
|
|
39
|
-
|
|
40
|
-
function Example() {
|
|
41
|
-
const [date, setDate] = useState<Date>(new Date())
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<DatePicker
|
|
45
|
-
mode="single"
|
|
46
|
-
value={date}
|
|
47
|
-
onChange={(e) => setDate(e.target.value)}
|
|
48
|
-
/>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### With Time Picker
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
57
|
-
import { useState } from 'react'
|
|
58
|
-
|
|
59
|
-
function DateTimePicker() {
|
|
60
|
-
const [datetime, setDatetime] = useState<Date>(new Date())
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<DatePicker
|
|
64
|
-
mode="single"
|
|
65
|
-
value={datetime}
|
|
66
|
-
onChange={(e) => setDatetime(e.target.value)}
|
|
67
|
-
timePicker
|
|
68
|
-
/>
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Range Selection
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
77
|
-
import { useState } from 'react'
|
|
78
|
-
import { DateRange } from 'react-day-picker'
|
|
79
|
-
|
|
80
|
-
function RangePicker() {
|
|
81
|
-
const [range, setRange] = useState<DateRange>({
|
|
82
|
-
from: new Date(),
|
|
83
|
-
to: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
<DatePicker
|
|
88
|
-
mode="range"
|
|
89
|
-
selected={range}
|
|
90
|
-
onChange={(e) => setRange(e.target.value)}
|
|
91
|
-
min={1}
|
|
92
|
-
max={30}
|
|
93
|
-
/>
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Multiple Date Selection
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
102
|
-
import { useState } from 'react'
|
|
103
|
-
|
|
104
|
-
function MultipleDates() {
|
|
105
|
-
const [dates, setDates] = useState<Date[]>([])
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<DatePicker
|
|
109
|
-
mode="multiple"
|
|
110
|
-
selected={dates}
|
|
111
|
-
onChange={(e) => setDates(e.target.value)}
|
|
112
|
-
max={5}
|
|
113
|
-
/>
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Custom Date Format
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
122
|
-
|
|
123
|
-
function CustomFormat() {
|
|
124
|
-
const [date, setDate] = useState<Date>(new Date())
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<DatePicker
|
|
128
|
-
value={date}
|
|
129
|
-
onChange={(e) => setDate(e.target.value)}
|
|
130
|
-
dateFormat="MMM dd, yyyy" // "Jan 15, 2024"
|
|
131
|
-
/>
|
|
132
|
-
)
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Custom Trigger Element
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
140
|
-
import { Button } from "@/components/Button";
|
|
141
|
-
|
|
142
|
-
function CustomTrigger() {
|
|
143
|
-
const [date, setDate] = useState<Date>(new Date())
|
|
144
|
-
|
|
145
|
-
return (
|
|
146
|
-
<DatePicker
|
|
147
|
-
value={date}
|
|
148
|
-
onChange={(e) => setDate(e.target.value)}
|
|
149
|
-
>
|
|
150
|
-
<Button variant="BluSecStyle">
|
|
151
|
-
<i className="ri-calendar-line" />
|
|
152
|
-
Select Date
|
|
153
|
-
</Button>
|
|
154
|
-
</DatePicker>
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### With Controlled Input
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
163
|
-
import { InputField } from "@/components/InputField";
|
|
164
|
-
import { useState } from 'react'
|
|
165
|
-
|
|
166
|
-
function ControlledInput() {
|
|
167
|
-
const [date, setDate] = useState<Date>(new Date())
|
|
168
|
-
|
|
169
|
-
return (
|
|
170
|
-
<DatePicker
|
|
171
|
-
value={date}
|
|
172
|
-
onChange={(e) => setDate(e.target.value)}
|
|
173
|
-
>
|
|
174
|
-
<InputField
|
|
175
|
-
label="Appointment Date"
|
|
176
|
-
placeholder="Select a date"
|
|
177
|
-
/>
|
|
178
|
-
</DatePicker>
|
|
179
|
-
)
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### Date Restrictions
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
187
|
-
|
|
188
|
-
function RestrictedDates() {
|
|
189
|
-
const [date, setDate] = useState<Date>()
|
|
190
|
-
|
|
191
|
-
return (
|
|
192
|
-
<DatePicker
|
|
193
|
-
value={date}
|
|
194
|
-
onChange={(e) => setDate(e.target.value)}
|
|
195
|
-
calendarProps={{
|
|
196
|
-
disabled: [
|
|
197
|
-
{ before: new Date() }, // No past dates
|
|
198
|
-
{ dayOfWeek: [0, 6] }, // No weekends
|
|
199
|
-
],
|
|
200
|
-
fromDate: new Date(),
|
|
201
|
-
toDate: new Date(2025, 11, 31),
|
|
202
|
-
}}
|
|
203
|
-
/>
|
|
204
|
-
)
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
### Booking System
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
212
|
-
import { useState } from 'react'
|
|
213
|
-
import { DateRange } from 'react-day-picker'
|
|
214
|
-
|
|
215
|
-
function BookingPicker() {
|
|
216
|
-
const [range, setRange] = useState<DateRange>()
|
|
217
|
-
|
|
218
|
-
const bookedDates = [
|
|
219
|
-
new Date(2024, 0, 15),
|
|
220
|
-
new Date(2024, 0, 16),
|
|
221
|
-
{ from: new Date(2024, 0, 20), to: new Date(2024, 0, 22) },
|
|
222
|
-
]
|
|
223
|
-
|
|
224
|
-
const nightsCount = range?.from && range?.to
|
|
225
|
-
? Math.ceil((range.to.getTime() - range.from.getTime()) / (1000 * 60 * 60 * 24))
|
|
226
|
-
: 0
|
|
227
|
-
|
|
228
|
-
return (
|
|
229
|
-
<div>
|
|
230
|
-
<DatePicker
|
|
231
|
-
mode="range"
|
|
232
|
-
selected={range}
|
|
233
|
-
onChange={(e) => setRange(e.target.value)}
|
|
234
|
-
min={1}
|
|
235
|
-
max={30}
|
|
236
|
-
calendarProps={{
|
|
237
|
-
disabled: bookedDates,
|
|
238
|
-
}}
|
|
239
|
-
/>
|
|
240
|
-
{nightsCount > 0 && (
|
|
241
|
-
<p className="mt-2 text-sm">
|
|
242
|
-
{nightsCount} night{nightsCount > 1 ? 's' : ''} selected
|
|
243
|
-
</p>
|
|
244
|
-
)}
|
|
245
|
-
</div>
|
|
246
|
-
)
|
|
247
|
-
}
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Deadline Picker with Time
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
254
|
-
import { useState } from 'react'
|
|
255
|
-
|
|
256
|
-
function DeadlinePicker() {
|
|
257
|
-
const [deadline, setDeadline] = useState<Date>()
|
|
258
|
-
|
|
259
|
-
return (
|
|
260
|
-
<DatePicker
|
|
261
|
-
value={deadline}
|
|
262
|
-
onChange={(e) => setDeadline(e.target.value)}
|
|
263
|
-
timePicker
|
|
264
|
-
calendarProps={{
|
|
265
|
-
disabled: { before: new Date() },
|
|
266
|
-
}}
|
|
267
|
-
dateFormat="MMM dd, yyyy 'at' hh:mm a"
|
|
268
|
-
>
|
|
269
|
-
<InputField
|
|
270
|
-
label="Project Deadline"
|
|
271
|
-
placeholder="Select deadline with time"
|
|
272
|
-
childrenSide={<i className="ri-time-line" />}
|
|
273
|
-
/>
|
|
274
|
-
</DatePicker>
|
|
275
|
-
)
|
|
276
|
-
}
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
## API Reference
|
|
280
|
-
|
|
281
|
-
### DatePicker Props
|
|
282
|
-
|
|
283
|
-
Extends all standard input attributes (`HTMLAttributes<HTMLInputElement>`).
|
|
284
|
-
|
|
285
|
-
| Prop | Type | Default | Description |
|
|
286
|
-
|------|------|---------|-------------|
|
|
287
|
-
| `mode` | `'single' \| 'multiple' \| 'range'` | `'single'` | Selection mode |
|
|
288
|
-
| `selected` | `Date \| Date[] \| DateRange` | - | Selected date(s) for Calendar |
|
|
289
|
-
| `value` | `Date \| Date[] \| DateRange` | - | Input field value |
|
|
290
|
-
| `onChange` | `(e: Event) => void` | - | Change handler (event with target.value) |
|
|
291
|
-
| `timePicker` | `boolean` | `false` | Show time picker |
|
|
292
|
-
| `dateFormat` | `string` | `'yyyy/MM/dd'` | Date display format |
|
|
293
|
-
| `size` | `'M' \| 'S'` | `'M'` | Input field size |
|
|
294
|
-
| `min` | `number` | - | Minimum range length (range mode) |
|
|
295
|
-
| `max` | `number` | - | Maximum range/multiple count |
|
|
296
|
-
| `showWeekNumber` | `boolean` | `false` | Show week numbers in calendar |
|
|
297
|
-
| `captionLayout` | `'dropdown' \| 'label' \| 'dropdown-months' \| 'dropdown-years'` | `'dropdown'` | Caption style |
|
|
298
|
-
| `calendarProps` | `CalendarProps` | - | Additional Calendar props |
|
|
299
|
-
| `children` | `ReactElement` | - | Custom trigger element |
|
|
300
|
-
| `className` | `string` | - | Additional CSS classes |
|
|
301
|
-
|
|
302
|
-
### TimePickerValue Interface
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
interface TimePickerValue {
|
|
306
|
-
hour: number // 1-12
|
|
307
|
-
minute: number // 0-59
|
|
308
|
-
time: 'AM' | 'PM'
|
|
309
|
-
}
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
## TypeScript
|
|
313
|
-
|
|
314
|
-
### Full Type Definitions
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
import { DateRange } from 'react-day-picker'
|
|
318
|
-
|
|
319
|
-
interface DatePickerProps extends HTMLAttributes<HTMLInputElement> {
|
|
320
|
-
mode?: "single" | "multiple" | "range"
|
|
321
|
-
selected?: Date | Date[] | DateRange | undefined
|
|
322
|
-
min?: number
|
|
323
|
-
max?: number
|
|
324
|
-
size?: "M" | "S"
|
|
325
|
-
showWeekNumber?: boolean
|
|
326
|
-
captionLayout?: "dropdown" | "label" | "dropdown-months" | "dropdown-years"
|
|
327
|
-
dateFormat?: string
|
|
328
|
-
calendarProps?: CalendarProps
|
|
329
|
-
timePicker?: boolean
|
|
330
|
-
value?: Date | Date[] | DateRange
|
|
331
|
-
onChange?: (e: { target: { value: Date | Date[] | DateRange } }) => void
|
|
332
|
-
children?: ReactElement
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Usage with different modes
|
|
336
|
-
type SingleDatePicker = DatePickerProps & {
|
|
337
|
-
mode: 'single'
|
|
338
|
-
value?: Date
|
|
339
|
-
selected?: Date
|
|
340
|
-
onChange?: (e: { target: { value: Date | undefined } }) => void
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
type MultipleDatePicker = DatePickerProps & {
|
|
344
|
-
mode: 'multiple'
|
|
345
|
-
value?: Date[]
|
|
346
|
-
selected?: Date[]
|
|
347
|
-
onChange?: (e: { target: { value: Date[] | undefined } }) => void
|
|
348
|
-
max?: number
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
type RangeDatePicker = DatePickerProps & {
|
|
352
|
-
mode: 'range'
|
|
353
|
-
value?: DateRange
|
|
354
|
-
selected?: DateRange
|
|
355
|
-
onChange?: (e: { target: { value: DateRange | undefined } }) => void
|
|
356
|
-
min?: number
|
|
357
|
-
max?: number
|
|
358
|
-
}
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
## Common Patterns
|
|
362
|
-
|
|
363
|
-
### Event Scheduler
|
|
364
|
-
|
|
365
|
-
```typescript
|
|
366
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
367
|
-
import { useState } from 'react'
|
|
368
|
-
|
|
369
|
-
function EventScheduler() {
|
|
370
|
-
const [startDate, setStartDate] = useState<Date>(new Date())
|
|
371
|
-
const [endDate, setEndDate] = useState<Date>()
|
|
372
|
-
|
|
373
|
-
return (
|
|
374
|
-
<div className="space-y-4">
|
|
375
|
-
<DatePicker
|
|
376
|
-
value={startDate}
|
|
377
|
-
onChange={(e) => {
|
|
378
|
-
setStartDate(e.target.value)
|
|
379
|
-
// Ensure end date is after start date
|
|
380
|
-
if (endDate && endDate < e.target.value) {
|
|
381
|
-
setEndDate(e.target.value)
|
|
382
|
-
}
|
|
383
|
-
}}
|
|
384
|
-
timePicker
|
|
385
|
-
dateFormat="MMM dd, yyyy hh:mm a"
|
|
386
|
-
>
|
|
387
|
-
<InputField label="Event Start" />
|
|
388
|
-
</DatePicker>
|
|
389
|
-
|
|
390
|
-
<DatePicker
|
|
391
|
-
value={endDate}
|
|
392
|
-
onChange={(e) => setEndDate(e.target.value)}
|
|
393
|
-
timePicker
|
|
394
|
-
dateFormat="MMM dd, yyyy hh:mm a"
|
|
395
|
-
calendarProps={{
|
|
396
|
-
disabled: { before: startDate },
|
|
397
|
-
}}
|
|
398
|
-
>
|
|
399
|
-
<InputField label="Event End" />
|
|
400
|
-
</DatePicker>
|
|
401
|
-
</div>
|
|
402
|
-
)
|
|
403
|
-
}
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### Birthday Picker
|
|
407
|
-
|
|
408
|
-
```typescript
|
|
409
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
410
|
-
import { useState } from 'react'
|
|
411
|
-
|
|
412
|
-
function BirthdayPicker() {
|
|
413
|
-
const [birthday, setBirthday] = useState<Date>()
|
|
414
|
-
|
|
415
|
-
const maxDate = new Date()
|
|
416
|
-
maxDate.setFullYear(maxDate.getFullYear() - 13) // Minimum age 13
|
|
417
|
-
|
|
418
|
-
return (
|
|
419
|
-
<DatePicker
|
|
420
|
-
value={birthday}
|
|
421
|
-
onChange={(e) => setBirthday(e.target.value)}
|
|
422
|
-
dateFormat="MMMM dd, yyyy"
|
|
423
|
-
calendarProps={{
|
|
424
|
-
captionLayout: 'dropdown',
|
|
425
|
-
toDate: maxDate,
|
|
426
|
-
fromDate: new Date(1900, 0, 1),
|
|
427
|
-
}}
|
|
428
|
-
>
|
|
429
|
-
<InputField
|
|
430
|
-
label="Date of Birth"
|
|
431
|
-
placeholder="Select your birthday"
|
|
432
|
-
/>
|
|
433
|
-
</DatePicker>
|
|
434
|
-
)
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
### Work Schedule Picker
|
|
439
|
-
|
|
440
|
-
```typescript
|
|
441
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
442
|
-
import { useState } from 'react'
|
|
443
|
-
|
|
444
|
-
function WorkSchedule() {
|
|
445
|
-
const [workDays, setWorkDays] = useState<Date[]>([])
|
|
446
|
-
|
|
447
|
-
return (
|
|
448
|
-
<DatePicker
|
|
449
|
-
mode="multiple"
|
|
450
|
-
selected={workDays}
|
|
451
|
-
onChange={(e) => setWorkDays(e.target.value)}
|
|
452
|
-
max={5} // Max 5 work days
|
|
453
|
-
calendarProps={{
|
|
454
|
-
disabled: { dayOfWeek: [0, 6] }, // Disable weekends
|
|
455
|
-
}}
|
|
456
|
-
>
|
|
457
|
-
<InputField
|
|
458
|
-
label="Select Work Days"
|
|
459
|
-
placeholder={`${workDays.length} day${workDays.length !== 1 ? 's' : ''} selected`}
|
|
460
|
-
/>
|
|
461
|
-
</DatePicker>
|
|
462
|
-
)
|
|
463
|
-
}
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
## Testing
|
|
467
|
-
|
|
468
|
-
### Unit Test Examples
|
|
469
|
-
|
|
470
|
-
```typescript
|
|
471
|
-
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
472
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
473
|
-
import userEvent from '@testing-library/user-event'
|
|
474
|
-
|
|
475
|
-
describe('DatePicker', () => {
|
|
476
|
-
it('renders with default input', () => {
|
|
477
|
-
render(<DatePicker mode="single" />)
|
|
478
|
-
|
|
479
|
-
const input = screen.getByRole('textbox')
|
|
480
|
-
expect(input).toBeInTheDocument()
|
|
481
|
-
expect(input).toHaveAttribute('readonly')
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
it('opens calendar on input click', async () => {
|
|
485
|
-
render(<DatePicker mode="single" />)
|
|
486
|
-
|
|
487
|
-
const input = screen.getByRole('textbox')
|
|
488
|
-
await userEvent.click(input)
|
|
489
|
-
|
|
490
|
-
await waitFor(() => {
|
|
491
|
-
expect(screen.getByRole('grid')).toBeInTheDocument() // Calendar grid
|
|
492
|
-
})
|
|
493
|
-
})
|
|
494
|
-
|
|
495
|
-
it('selects a date', async () => {
|
|
496
|
-
const handleChange = jest.fn()
|
|
497
|
-
|
|
498
|
-
render(
|
|
499
|
-
<DatePicker
|
|
500
|
-
mode="single"
|
|
501
|
-
onChange={handleChange}
|
|
502
|
-
/>
|
|
503
|
-
)
|
|
504
|
-
|
|
505
|
-
const input = screen.getByRole('textbox')
|
|
506
|
-
await userEvent.click(input)
|
|
507
|
-
|
|
508
|
-
const day15 = await screen.findByRole('button', { name: /15/ })
|
|
509
|
-
await userEvent.click(day15)
|
|
510
|
-
|
|
511
|
-
expect(handleChange).toHaveBeenCalled()
|
|
512
|
-
const selectedDate = handleChange.mock.calls[0][0].target.value
|
|
513
|
-
expect(selectedDate.getDate()).toBe(15)
|
|
514
|
-
})
|
|
515
|
-
|
|
516
|
-
it('displays time picker when enabled', async () => {
|
|
517
|
-
render(
|
|
518
|
-
<DatePicker
|
|
519
|
-
mode="single"
|
|
520
|
-
timePicker
|
|
521
|
-
/>
|
|
522
|
-
)
|
|
523
|
-
|
|
524
|
-
const input = screen.getByRole('textbox')
|
|
525
|
-
await userEvent.click(input)
|
|
526
|
-
|
|
527
|
-
await waitFor(() => {
|
|
528
|
-
expect(screen.getByText(/AM/i)).toBeInTheDocument()
|
|
529
|
-
expect(screen.getByText(/PM/i)).toBeInTheDocument()
|
|
530
|
-
})
|
|
531
|
-
})
|
|
532
|
-
|
|
533
|
-
it('formats date according to dateFormat prop', () => {
|
|
534
|
-
const date = new Date(2024, 0, 15)
|
|
535
|
-
|
|
536
|
-
render(
|
|
537
|
-
<DatePicker
|
|
538
|
-
mode="single"
|
|
539
|
-
value={date}
|
|
540
|
-
dateFormat="MMM dd, yyyy"
|
|
541
|
-
/>
|
|
542
|
-
)
|
|
543
|
-
|
|
544
|
-
const input = screen.getByRole('textbox')
|
|
545
|
-
expect(input).toHaveValue('Jan 15, 2024')
|
|
546
|
-
})
|
|
547
|
-
|
|
548
|
-
it('renders custom trigger element', () => {
|
|
549
|
-
const CustomButton = <button>Pick Date</button>
|
|
550
|
-
|
|
551
|
-
render(
|
|
552
|
-
<DatePicker mode="single">
|
|
553
|
-
{CustomButton}
|
|
554
|
-
</DatePicker>
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
expect(screen.getByText('Pick Date')).toBeInTheDocument()
|
|
558
|
-
})
|
|
559
|
-
|
|
560
|
-
it('disables body scroll when open', async () => {
|
|
561
|
-
render(<DatePicker mode="single" />)
|
|
562
|
-
|
|
563
|
-
const input = screen.getByRole('textbox')
|
|
564
|
-
await userEvent.click(input)
|
|
565
|
-
|
|
566
|
-
await waitFor(() => {
|
|
567
|
-
expect(document.body.style.overflow).toBe('hidden')
|
|
568
|
-
})
|
|
569
|
-
})
|
|
570
|
-
|
|
571
|
-
it('respects min/max range constraints', async () => {
|
|
572
|
-
const handleChange = jest.fn()
|
|
573
|
-
|
|
574
|
-
render(
|
|
575
|
-
<DatePicker
|
|
576
|
-
mode="range"
|
|
577
|
-
onChange={handleChange}
|
|
578
|
-
min={2}
|
|
579
|
-
max={7}
|
|
580
|
-
/>
|
|
581
|
-
)
|
|
582
|
-
|
|
583
|
-
// Test that constraints are applied
|
|
584
|
-
// This would require more complex interaction testing
|
|
585
|
-
})
|
|
586
|
-
})
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
## Known Limitations & Frontend Patterns
|
|
590
|
-
|
|
591
|
-
### `onChange` payload type does not match what TypeScript thinks
|
|
592
|
-
|
|
593
|
-
`DatePicker` props extend `HTMLAttributes<HTMLInputElement>`, which makes `onChange` look like `(e: ChangeEvent<HTMLInputElement>) => void` where `e.target.value: string`. **At runtime the value is a `Date | Date[] | DateRange`**, not a string — the component dispatches a hand-rolled pseudo-event with the typed payload behind a `string` type assertion.
|
|
594
|
-
|
|
595
|
-
**Workaround — cast through `unknown` on every call site:**
|
|
596
|
-
|
|
597
|
-
```tsx
|
|
598
|
-
<DatePicker
|
|
599
|
-
mode="single"
|
|
600
|
-
value={startDate}
|
|
601
|
-
onChange={(e) =>
|
|
602
|
-
setStartDate(e.target.value as unknown as Date | undefined)
|
|
603
|
-
}
|
|
604
|
-
/>
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
For `mode="multiple"`:
|
|
608
|
-
|
|
609
|
-
```tsx
|
|
610
|
-
onChange={(e) => setDates(e.target.value as unknown as Date[] | undefined)}
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
For `mode="range"`:
|
|
614
|
-
|
|
615
|
-
```tsx
|
|
616
|
-
onChange={(e) => setRange(e.target.value as unknown as DateRange | undefined)}
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
The `as unknown as Date` shape is required — TypeScript will reject `as Date` directly because `string` and `Date` don't overlap.
|
|
620
|
-
|
|
621
|
-
### `TimePickerValue` interface — `hour`/`minute` are strings, not numbers
|
|
622
|
-
|
|
623
|
-
The shipped runtime uses `string` for `hour`, `minute`, and `time` (`"AM" | "PM"`). If you derive your own state from `TimePickerValue`, type those fields as `string`, not `number`, regardless of what older docs claim.
|
|
624
|
-
|
|
625
|
-
### Internal `<Picker>` value/onChange types are incompatible with `TimePickerValue` — `tsc` fails out of the box
|
|
626
|
-
|
|
627
|
-
`DatePicker.tsx` wraps `torch-react-mobile-picker`'s `<Picker>` and passes a concrete `TimePickerValue` (`{ hour, minute, time }`) where the picker's generic `PickerValue` (`Record<string, string>`) is expected. Strict TypeScript builds fail immediately:
|
|
628
|
-
|
|
629
|
-
```
|
|
630
|
-
DatePicker.tsx: Type 'TimePickerValue' is not assignable to type 'PickerValue'.
|
|
631
|
-
Index signature for type 'string' is missing in type 'TimePickerValue'.
|
|
632
|
-
DatePicker.tsx: Type '(e: TimePickerValue) => void' is not assignable to type
|
|
633
|
-
'(value: PickerValue, key: string) => void'.
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
**Workaround — add `as any` casts at the `<Picker>` boundary** (and only there, so consumer types stay honest):
|
|
637
|
-
|
|
638
|
-
```tsx
|
|
639
|
-
<Picker
|
|
640
|
-
value={value as any}
|
|
641
|
-
onChange={((e: TimePickerValue) => {
|
|
642
|
-
onChange(e);
|
|
643
|
-
}) as any}
|
|
644
|
-
wheelMode="normal"
|
|
645
|
-
>
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
Patch this manually after `npx torch-glare add DatePicker` — otherwise `pnpm build` (which runs `tsc -b`) will fail. The same `Ref` import in the file is also unused and trips `noUnusedLocals`; remove it while you're in there.
|
|
649
|
-
|
|
650
|
-
### `npx torch-glare add DatePicker` does not install `utils/dateFormat.ts`
|
|
651
|
-
|
|
652
|
-
The CLI ships `DatePicker.tsx` without copying the `dateFormat.ts` utility it imports, so the component fails to build immediately after install:
|
|
653
|
-
|
|
654
|
-
```
|
|
655
|
-
[plugin:vite:import-analysis] Failed to resolve import "../utils/dateFormat"
|
|
656
|
-
from "DatePicker.tsx".
|
|
657
|
-
```
|
|
658
|
-
|
|
659
|
-
**Workaround until the CLI is fixed:** create `utils/dateFormat.ts` manually with the three exports the component needs:
|
|
660
|
-
|
|
661
|
-
- `TimePickerValue` (`{ hour: string; minute: string; time: "AM" | "PM" }`)
|
|
662
|
-
- `applyTimeToDateValue(value, timePickerValue)` — applies the time picker value to the date value
|
|
663
|
-
- `formatDateValueToString(value, timePickerValue, dateFormat)` — formats the date value to a display string
|
|
664
|
-
|
|
665
|
-
Reverse-engineer the implementation from the call sites in `DatePicker.tsx` until the CLI copies it automatically.
|
|
666
|
-
|
|
667
|
-
## Accessibility
|
|
668
|
-
|
|
669
|
-
- **Keyboard Navigation**:
|
|
670
|
-
- Tab: Focus input field
|
|
671
|
-
- Enter/Space: Open calendar dropdown
|
|
672
|
-
- Arrow keys: Navigate calendar (when open)
|
|
673
|
-
- Escape: Close dropdown
|
|
674
|
-
- **ARIA Attributes**: Inherited from Calendar and Popover
|
|
675
|
-
- **Screen Readers**: Date announced on selection
|
|
676
|
-
- **Focus Management**: Focus returns to trigger after selection
|
|
677
|
-
- **Body Scroll Lock**: Prevents background scroll when picker open
|
|
678
|
-
|
|
679
|
-
### Accessibility Best Practices
|
|
680
|
-
|
|
681
|
-
```typescript
|
|
682
|
-
// Provide labels for inputs
|
|
683
|
-
<DatePicker>
|
|
684
|
-
<InputField
|
|
685
|
-
label="Appointment Date"
|
|
686
|
-
aria-describedby="date-hint"
|
|
687
|
-
/>
|
|
688
|
-
</DatePicker>
|
|
689
|
-
<p id="date-hint">Select a date for your appointment</p>
|
|
690
|
-
|
|
691
|
-
// Describe restrictions
|
|
692
|
-
<DatePicker
|
|
693
|
-
calendarProps={{
|
|
694
|
-
disabled: { before: new Date() },
|
|
695
|
-
'aria-label': 'Select a future date',
|
|
696
|
-
}}
|
|
697
|
-
/>
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
## Performance
|
|
701
|
-
|
|
702
|
-
| Metric | Value |
|
|
703
|
-
|--------|-------|
|
|
704
|
-
| Bundle size (minified) | ~45kb |
|
|
705
|
-
| Bundle size (gzipped) | ~12kb |
|
|
706
|
-
| Dependencies | react-day-picker, torch-react-mobile-picker |
|
|
707
|
-
| Initial render | <25ms |
|
|
708
|
-
| Dropdown open | <15ms |
|
|
709
|
-
| Tree-shakeable | ✅ |
|
|
710
|
-
|
|
711
|
-
### Performance Tips
|
|
712
|
-
|
|
713
|
-
1. **Memoize callbacks**:
|
|
714
|
-
```typescript
|
|
715
|
-
const handleChange = useCallback((e) => {
|
|
716
|
-
setDate(e.target.value)
|
|
717
|
-
}, [])
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
2. **Use value prop sparingly**: Only use when you need controlled state
|
|
721
|
-
|
|
722
|
-
3. **Lazy load time picker**: Only enable when needed
|
|
723
|
-
|
|
724
|
-
4. **Limit calendarProps modifiers**: Complex modifiers can slow rendering
|
|
725
|
-
|
|
726
|
-
## Styling
|
|
727
|
-
|
|
728
|
-
### Custom Styles
|
|
729
|
-
|
|
730
|
-
```typescript
|
|
731
|
-
// Style the input field
|
|
732
|
-
<DatePicker
|
|
733
|
-
className="custom-date-input"
|
|
734
|
-
size="M"
|
|
735
|
-
>
|
|
736
|
-
<InputField className="custom-field" />
|
|
737
|
-
</DatePicker>
|
|
738
|
-
|
|
739
|
-
// Style the calendar
|
|
740
|
-
<DatePicker
|
|
741
|
-
calendarProps={{
|
|
742
|
-
className: "custom-calendar",
|
|
743
|
-
classNames: {
|
|
744
|
-
day: "custom-day",
|
|
745
|
-
selected: "custom-selected",
|
|
746
|
-
},
|
|
747
|
-
}}
|
|
748
|
-
/>
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
### Default Styling
|
|
752
|
-
|
|
753
|
-
- Input: Read-only field with calendar icon
|
|
754
|
-
- Popover: Dropdown with calendar and optional time picker
|
|
755
|
-
- Calendar: Themed calendar with dropdown navigation
|
|
756
|
-
- Time Picker: Wheel interface with hour/minute/AM-PM columns
|
|
757
|
-
|
|
758
|
-
## Integration Examples
|
|
759
|
-
|
|
760
|
-
### With Form Libraries
|
|
761
|
-
|
|
762
|
-
```typescript
|
|
763
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
764
|
-
import { useForm, Controller } from 'react-hook-form'
|
|
765
|
-
|
|
766
|
-
function FormExample() {
|
|
767
|
-
const { control, handleSubmit } = useForm()
|
|
768
|
-
|
|
769
|
-
return (
|
|
770
|
-
<form onSubmit={handleSubmit(console.log)}>
|
|
771
|
-
<Controller
|
|
772
|
-
name="appointmentDate"
|
|
773
|
-
control={control}
|
|
774
|
-
rules={{ required: 'Date is required' }}
|
|
775
|
-
render={({ field, fieldState: { error } }) => (
|
|
776
|
-
<div>
|
|
777
|
-
<DatePicker
|
|
778
|
-
value={field.value}
|
|
779
|
-
onChange={(e) => field.onChange(e.target.value)}
|
|
780
|
-
timePicker
|
|
781
|
-
>
|
|
782
|
-
<InputField
|
|
783
|
-
label="Appointment Date & Time"
|
|
784
|
-
error={error?.message}
|
|
785
|
-
/>
|
|
786
|
-
</DatePicker>
|
|
787
|
-
</div>
|
|
788
|
-
)}
|
|
789
|
-
/>
|
|
790
|
-
</form>
|
|
791
|
-
)
|
|
792
|
-
}
|
|
793
|
-
```
|
|
794
|
-
|
|
795
|
-
### With State Management
|
|
796
|
-
|
|
797
|
-
```typescript
|
|
798
|
-
import { DatePicker } from "@/components/DatePicker";
|
|
799
|
-
import { useStore } from './store'
|
|
800
|
-
|
|
801
|
-
function DatePickerWithStore() {
|
|
802
|
-
const { selectedDate, setSelectedDate } = useStore()
|
|
803
|
-
|
|
804
|
-
return (
|
|
805
|
-
<DatePicker
|
|
806
|
-
value={selectedDate}
|
|
807
|
-
onChange={(e) => setSelectedDate(e.target.value)}
|
|
808
|
-
mode="single"
|
|
809
|
-
/>
|
|
810
|
-
)
|
|
811
|
-
}
|
|
812
|
-
```
|
|
813
|
-
|
|
814
|
-
## Best Practices
|
|
815
|
-
|
|
816
|
-
1. **Always specify mode**: Be explicit about selection type
|
|
817
|
-
```typescript
|
|
818
|
-
<DatePicker mode="single" />
|
|
819
|
-
```
|
|
820
|
-
|
|
821
|
-
2. **Use controlled components**: Manage state explicitly
|
|
822
|
-
```typescript
|
|
823
|
-
const [date, setDate] = useState<Date>()
|
|
824
|
-
<DatePicker value={date} onChange={(e) => setDate(e.target.value)} />
|
|
825
|
-
```
|
|
826
|
-
|
|
827
|
-
3. **Provide clear labels**: Help users understand the input
|
|
828
|
-
```typescript
|
|
829
|
-
<DatePicker>
|
|
830
|
-
<InputField label="Select Date" />
|
|
831
|
-
</DatePicker>
|
|
832
|
-
```
|
|
833
|
-
|
|
834
|
-
4. **Handle undefined values**: User can clear selection
|
|
835
|
-
```typescript
|
|
836
|
-
onChange={(e) => {
|
|
837
|
-
setDate(e.target.value || new Date())
|
|
838
|
-
}}
|
|
839
|
-
```
|
|
840
|
-
|
|
841
|
-
5. **Use timePicker selectively**: Only when time is necessary
|
|
842
|
-
|
|
843
|
-
6. **Disable invalid dates**: Use calendarProps.disabled
|
|
844
|
-
|
|
845
|
-
7. **Format dates appropriately**: Match user's locale and context
|
|
846
|
-
```typescript
|
|
847
|
-
<DatePicker dateFormat="MMM dd, yyyy" />
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
8. **Test keyboard navigation**: Ensure accessibility
|
|
851
|
-
|
|
852
|
-
## Related Components
|
|
853
|
-
|
|
854
|
-
- [Calendar](./calendar.md) - Base calendar component
|
|
855
|
-
- [SlideDatePicker](./slide-date-picker.md) - Mobile-optimized slide picker
|
|
856
|
-
- [InputField](./input-field.md) - Input field used as default trigger
|
|
857
|
-
- [Popover](./popover.md) - Dropdown container
|