@refraktor/dates 0.0.1
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/.turbo/turbo-build.log +4 -0
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/build/components/date-input/date-input.d.ts +4 -0
- package/build/components/date-input/date-input.d.ts.map +1 -0
- package/build/components/date-input/date-input.js +164 -0
- package/build/components/date-input/date-input.types.d.ts +96 -0
- package/build/components/date-input/date-input.types.d.ts.map +1 -0
- package/build/components/date-input/date-input.types.js +1 -0
- package/build/components/date-input/index.d.ts +3 -0
- package/build/components/date-input/index.d.ts.map +1 -0
- package/build/components/date-input/index.js +1 -0
- package/build/components/date-picker/date-picker.d.ts +4 -0
- package/build/components/date-picker/date-picker.d.ts.map +1 -0
- package/build/components/date-picker/date-picker.js +307 -0
- package/build/components/date-picker/date-picker.types.d.ts +86 -0
- package/build/components/date-picker/date-picker.types.d.ts.map +1 -0
- package/build/components/date-picker/date-picker.types.js +1 -0
- package/build/components/date-picker/index.d.ts +3 -0
- package/build/components/date-picker/index.d.ts.map +1 -0
- package/build/components/date-picker/index.js +1 -0
- package/build/components/dates-provider/context.d.ts +4 -0
- package/build/components/dates-provider/context.d.ts.map +1 -0
- package/build/components/dates-provider/context.js +10 -0
- package/build/components/dates-provider/dates-provider.d.ts +7 -0
- package/build/components/dates-provider/dates-provider.d.ts.map +1 -0
- package/build/components/dates-provider/dates-provider.js +65 -0
- package/build/components/dates-provider/index.d.ts +5 -0
- package/build/components/dates-provider/index.d.ts.map +1 -0
- package/build/components/dates-provider/index.js +3 -0
- package/build/components/dates-provider/types.d.ts +26 -0
- package/build/components/dates-provider/types.d.ts.map +1 -0
- package/build/components/dates-provider/types.js +1 -0
- package/build/components/dates-provider/use-dates.d.ts +2 -0
- package/build/components/dates-provider/use-dates.d.ts.map +1 -0
- package/build/components/dates-provider/use-dates.js +4 -0
- package/build/components/index.d.ts +8 -0
- package/build/components/index.d.ts.map +1 -0
- package/build/components/index.js +7 -0
- package/build/components/month-input/index.d.ts +3 -0
- package/build/components/month-input/index.d.ts.map +1 -0
- package/build/components/month-input/index.js +1 -0
- package/build/components/month-input/month-input.d.ts +4 -0
- package/build/components/month-input/month-input.d.ts.map +1 -0
- package/build/components/month-input/month-input.js +161 -0
- package/build/components/month-input/month-input.types.d.ts +85 -0
- package/build/components/month-input/month-input.types.d.ts.map +1 -0
- package/build/components/month-input/month-input.types.js +1 -0
- package/build/components/month-picker/index.d.ts +3 -0
- package/build/components/month-picker/index.d.ts.map +1 -0
- package/build/components/month-picker/index.js +1 -0
- package/build/components/month-picker/month-picker.d.ts +4 -0
- package/build/components/month-picker/month-picker.d.ts.map +1 -0
- package/build/components/month-picker/month-picker.js +229 -0
- package/build/components/month-picker/month-picker.types.d.ts +69 -0
- package/build/components/month-picker/month-picker.types.d.ts.map +1 -0
- package/build/components/month-picker/month-picker.types.js +1 -0
- package/build/components/picker-shared/index.d.ts +5 -0
- package/build/components/picker-shared/index.d.ts.map +1 -0
- package/build/components/picker-shared/index.js +2 -0
- package/build/components/picker-shared/picker-header.d.ts +4 -0
- package/build/components/picker-shared/picker-header.d.ts.map +1 -0
- package/build/components/picker-shared/picker-header.js +27 -0
- package/build/components/picker-shared/picker-header.types.d.ts +36 -0
- package/build/components/picker-shared/picker-header.types.d.ts.map +1 -0
- package/build/components/picker-shared/picker-header.types.js +1 -0
- package/build/components/picker-shared/picker.styles.d.ts +12 -0
- package/build/components/picker-shared/picker.styles.d.ts.map +1 -0
- package/build/components/picker-shared/picker.styles.js +53 -0
- package/build/components/picker-shared/picker.types.d.ts +4 -0
- package/build/components/picker-shared/picker.types.d.ts.map +1 -0
- package/build/components/picker-shared/picker.types.js +1 -0
- package/build/components/year-input/index.d.ts +3 -0
- package/build/components/year-input/index.d.ts.map +1 -0
- package/build/components/year-input/index.js +1 -0
- package/build/components/year-input/year-input.d.ts +4 -0
- package/build/components/year-input/year-input.d.ts.map +1 -0
- package/build/components/year-input/year-input.js +157 -0
- package/build/components/year-input/year-input.types.d.ts +74 -0
- package/build/components/year-input/year-input.types.d.ts.map +1 -0
- package/build/components/year-input/year-input.types.js +1 -0
- package/build/components/year-picker/index.d.ts +3 -0
- package/build/components/year-picker/index.d.ts.map +1 -0
- package/build/components/year-picker/index.js +1 -0
- package/build/components/year-picker/year-picker.d.ts +4 -0
- package/build/components/year-picker/year-picker.d.ts.map +1 -0
- package/build/components/year-picker/year-picker.js +236 -0
- package/build/components/year-picker/year-picker.types.d.ts +70 -0
- package/build/components/year-picker/year-picker.types.d.ts.map +1 -0
- package/build/components/year-picker/year-picker.types.js +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +2 -0
- package/build/style.css +2 -0
- package/package.json +38 -0
- package/refraktor-dates-0.0.1-alpha.0.tgz +0 -0
- package/src/components/date-input/date-input.tsx +376 -0
- package/src/components/date-input/date-input.types.ts +161 -0
- package/src/components/date-input/index.ts +13 -0
- package/src/components/date-picker/date-picker.tsx +649 -0
- package/src/components/date-picker/date-picker.types.ts +145 -0
- package/src/components/date-picker/index.ts +15 -0
- package/src/components/dates-provider/context.ts +18 -0
- package/src/components/dates-provider/dates-provider.tsx +136 -0
- package/src/components/dates-provider/index.ts +10 -0
- package/src/components/dates-provider/types.ts +33 -0
- package/src/components/dates-provider/use-dates.ts +5 -0
- package/src/components/index.ts +7 -0
- package/src/components/month-input/index.ts +13 -0
- package/src/components/month-input/month-input.tsx +363 -0
- package/src/components/month-input/month-input.types.ts +139 -0
- package/src/components/month-picker/index.ts +14 -0
- package/src/components/month-picker/month-picker.tsx +458 -0
- package/src/components/month-picker/month-picker.types.ts +117 -0
- package/src/components/picker-shared/index.ts +7 -0
- package/src/components/picker-shared/picker-header.tsx +178 -0
- package/src/components/picker-shared/picker-header.types.ts +49 -0
- package/src/components/picker-shared/picker.styles.ts +69 -0
- package/src/components/picker-shared/picker.types.ts +4 -0
- package/src/components/year-input/index.ts +13 -0
- package/src/components/year-input/year-input.tsx +347 -0
- package/src/components/year-input/year-input.types.ts +118 -0
- package/src/components/year-picker/index.ts +15 -0
- package/src/components/year-picker/year-picker.tsx +504 -0
- package/src/components/year-picker/year-picker.types.ts +108 -0
- package/src/index.ts +3 -0
- package/src/style.css +1 -0
- package/tsconfig.json +13 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ChillWoW
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @refraktor/dates
|
|
2
|
+
|
|
3
|
+
Date and time components for React.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# NPM
|
|
9
|
+
npm install @refraktor/dates dayjs
|
|
10
|
+
|
|
11
|
+
# Bun
|
|
12
|
+
bun add @refraktor/dates dayjs
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Documentation
|
|
16
|
+
|
|
17
|
+
Coming Soon...
|
|
18
|
+
|
|
19
|
+
## License
|
|
20
|
+
|
|
21
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-input.d.ts","sourceRoot":"","sources":["../../../src/components/date-input/date-input.tsx"],"names":[],"mappings":"AA6BA,OAAO,EAEH,uBAAuB,EAE1B,MAAM,oBAAoB,CAAC;AAyC5B,QAAA,MAAM,SAAS,uEAuSb,CAAC;AAMH,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId, useUncontrolled } from "@refraktor/utils";
|
|
3
|
+
import { useCallback, useMemo } from "react";
|
|
4
|
+
import { createClassNamesConfig, createComponentConfig, factory, Input, Transition, useClassNames, useProps, useTheme } from "@refraktor/core";
|
|
5
|
+
import { autoUpdate, flip, FloatingFocusManager, FloatingPortal, inline, offset, shift, useDismiss, useFloating, useFocus, useInteractions, useRole } from "@floating-ui/react";
|
|
6
|
+
import { useDates } from "../dates-provider";
|
|
7
|
+
import { DatePicker } from "../date-picker";
|
|
8
|
+
const DEFAULT_MONTH_PICKER_COLUMNS = 3;
|
|
9
|
+
const DEFAULT_YEAR_PICKER_YEARS_PER_PAGE = 9;
|
|
10
|
+
const DEFAULT_YEAR_PICKER_COLUMNS = 3;
|
|
11
|
+
const DEFAULT_VALUE_FORMAT = "MMMM D, YYYY";
|
|
12
|
+
const defaultProps = {
|
|
13
|
+
monthPickerColumns: DEFAULT_MONTH_PICKER_COLUMNS,
|
|
14
|
+
yearPickerYearsPerPage: DEFAULT_YEAR_PICKER_YEARS_PER_PAGE,
|
|
15
|
+
yearPickerColumns: DEFAULT_YEAR_PICKER_COLUMNS,
|
|
16
|
+
valueFormat: DEFAULT_VALUE_FORMAT,
|
|
17
|
+
disabled: false,
|
|
18
|
+
size: "md",
|
|
19
|
+
radius: "default",
|
|
20
|
+
positioning: {
|
|
21
|
+
placement: "bottom-start",
|
|
22
|
+
offset: 4
|
|
23
|
+
},
|
|
24
|
+
middlewares: {
|
|
25
|
+
flip: true,
|
|
26
|
+
shift: true
|
|
27
|
+
},
|
|
28
|
+
withinPortal: true,
|
|
29
|
+
closeOnClickOutside: true,
|
|
30
|
+
closeOnEscape: true
|
|
31
|
+
};
|
|
32
|
+
const isValidDate = (value) => value instanceof Date && !Number.isNaN(value.getTime());
|
|
33
|
+
const normalizeDateValue = (value) => {
|
|
34
|
+
if (!isValidDate(value)) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const normalizedValue = new Date(value);
|
|
38
|
+
normalizedValue.setHours(0, 0, 0, 0);
|
|
39
|
+
return normalizedValue;
|
|
40
|
+
};
|
|
41
|
+
const DateInput = factory((_props, ref) => {
|
|
42
|
+
const { cx, getRadius } = useTheme();
|
|
43
|
+
const { createDate } = useDates();
|
|
44
|
+
const { id, value, defaultValue, onChange, opened, defaultOpened, onOpenedChange, minDate, maxDate, monthPickerColumns, yearPickerYearsPerPage, yearPickerColumns, getWeekdayLabel, getDayLabel, getDayAriaLabel, getHeaderLabel, getNavigationAriaLabel, getMonthLabel, getMonthAriaLabel, getMonthHeaderLabel, getMonthNavigationAriaLabel, valueFormat, disabled, size, radius, positioning, middlewares, withinPortal, closeOnClickOutside, closeOnEscape, transitionProps, inputClassNames, className, classNames, onFocus, onBlur, onClick, onKeyDown, ...inputProps } = useProps("DateInput", defaultProps, _props);
|
|
45
|
+
const classes = useClassNames("DateInput", classNames);
|
|
46
|
+
const _id = useId(id);
|
|
47
|
+
const dropdownId = `${_id}-dropdown`;
|
|
48
|
+
const [selectedDateState, setSelectedDate] = useUncontrolled({
|
|
49
|
+
value,
|
|
50
|
+
defaultValue,
|
|
51
|
+
finalValue: undefined,
|
|
52
|
+
onChange: (nextDate) => {
|
|
53
|
+
if (nextDate !== undefined) {
|
|
54
|
+
onChange?.(nextDate);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const [isOpenState, setIsOpen] = useUncontrolled({
|
|
59
|
+
value: opened,
|
|
60
|
+
defaultValue: defaultOpened,
|
|
61
|
+
finalValue: false,
|
|
62
|
+
onChange: onOpenedChange
|
|
63
|
+
});
|
|
64
|
+
const isOpen = isOpenState && !disabled;
|
|
65
|
+
const middleware = useMemo(() => {
|
|
66
|
+
const middlewareList = [];
|
|
67
|
+
middlewareList.push(offset(positioning?.offset ?? 4));
|
|
68
|
+
if (middlewares?.flip ?? true) {
|
|
69
|
+
middlewareList.push(flip(typeof middlewares?.flip === "boolean"
|
|
70
|
+
? undefined
|
|
71
|
+
: middlewares.flip));
|
|
72
|
+
}
|
|
73
|
+
if (middlewares?.shift ?? true) {
|
|
74
|
+
middlewareList.push(shift(typeof middlewares?.shift === "boolean"
|
|
75
|
+
? undefined
|
|
76
|
+
: middlewares.shift));
|
|
77
|
+
}
|
|
78
|
+
if (middlewares?.inline) {
|
|
79
|
+
middlewareList.push(inline(typeof middlewares.inline === "boolean"
|
|
80
|
+
? undefined
|
|
81
|
+
: middlewares.inline));
|
|
82
|
+
}
|
|
83
|
+
return middlewareList;
|
|
84
|
+
}, [middlewares, positioning?.offset]);
|
|
85
|
+
const handleOpenChange = useCallback((nextOpen) => {
|
|
86
|
+
if (disabled && nextOpen) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
setIsOpen(nextOpen);
|
|
90
|
+
}, [disabled, setIsOpen]);
|
|
91
|
+
const floating = useFloating({
|
|
92
|
+
placement: positioning?.placement ?? "bottom-start",
|
|
93
|
+
open: isOpen,
|
|
94
|
+
onOpenChange: handleOpenChange,
|
|
95
|
+
middleware,
|
|
96
|
+
whileElementsMounted: autoUpdate
|
|
97
|
+
});
|
|
98
|
+
const focus = useFocus(floating.context, {
|
|
99
|
+
enabled: !disabled
|
|
100
|
+
});
|
|
101
|
+
const dismiss = useDismiss(floating.context, {
|
|
102
|
+
outsidePress: closeOnClickOutside,
|
|
103
|
+
escapeKey: closeOnEscape
|
|
104
|
+
});
|
|
105
|
+
const role = useRole(floating.context, {
|
|
106
|
+
role: "dialog"
|
|
107
|
+
});
|
|
108
|
+
const { getReferenceProps, getFloatingProps } = useInteractions([
|
|
109
|
+
focus,
|
|
110
|
+
dismiss,
|
|
111
|
+
role
|
|
112
|
+
]);
|
|
113
|
+
const setInputRef = useCallback((node) => {
|
|
114
|
+
floating.refs.setReference(node);
|
|
115
|
+
if (typeof ref === "function") {
|
|
116
|
+
ref(node);
|
|
117
|
+
}
|
|
118
|
+
else if (ref) {
|
|
119
|
+
ref.current = node;
|
|
120
|
+
}
|
|
121
|
+
}, [floating.refs, ref]);
|
|
122
|
+
const handleInputKeyDown = useCallback((event) => {
|
|
123
|
+
onKeyDown?.(event);
|
|
124
|
+
if (event.defaultPrevented || disabled) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (event.key === "ArrowDown" ||
|
|
128
|
+
event.key === "Enter" ||
|
|
129
|
+
event.key === " ") {
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
setIsOpen(true);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (event.key === "Escape") {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
setIsOpen(false);
|
|
137
|
+
}
|
|
138
|
+
}, [disabled, onKeyDown, setIsOpen]);
|
|
139
|
+
const handleDateChange = useCallback((nextDate) => {
|
|
140
|
+
const normalizedDate = normalizeDateValue(nextDate);
|
|
141
|
+
if (normalizedDate === undefined) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
setSelectedDate(normalizedDate);
|
|
145
|
+
setIsOpen(false);
|
|
146
|
+
}, [setIsOpen, setSelectedDate]);
|
|
147
|
+
const selectedDate = normalizeDateValue(selectedDateState);
|
|
148
|
+
const inputValue = selectedDate === undefined ? "" : createDate(selectedDate).format(valueFormat);
|
|
149
|
+
const mergedReferenceProps = getReferenceProps({
|
|
150
|
+
onFocus,
|
|
151
|
+
onBlur,
|
|
152
|
+
onClick,
|
|
153
|
+
onKeyDown: handleInputKeyDown
|
|
154
|
+
});
|
|
155
|
+
const dropdownContent = (_jsx(Transition, { transition: "fade", duration: 150, mounted: isOpen, ...transitionProps, children: _jsx("div", { ref: floating.refs.setFloating, id: dropdownId, style: {
|
|
156
|
+
...floating.floatingStyles
|
|
157
|
+
}, className: cx("w-80 z-50 border border-[var(--refraktor-border)] bg-[var(--refraktor-bg)] p-2 text-[var(--refraktor-text)] shadow-md", getRadius(radius), classes.dropdown), ...getFloatingProps(), children: _jsx(DatePicker, { value: selectedDate, onChange: handleDateChange, minDate: minDate, maxDate: maxDate, monthPickerColumns: monthPickerColumns, yearPickerYearsPerPage: yearPickerYearsPerPage, yearPickerColumns: yearPickerColumns, disabled: disabled, size: size, radius: radius, getWeekdayLabel: getWeekdayLabel, getDayLabel: getDayLabel, getDayAriaLabel: getDayAriaLabel, getHeaderLabel: getHeaderLabel, getNavigationAriaLabel: getNavigationAriaLabel, getMonthLabel: getMonthLabel, getMonthAriaLabel: getMonthAriaLabel, getMonthHeaderLabel: getMonthHeaderLabel, getMonthNavigationAriaLabel: getMonthNavigationAriaLabel, className: cx("bg-transparent p-0", classes.datePicker) }) }) }));
|
|
158
|
+
const wrappedContent = isOpen ? (_jsx(FloatingFocusManager, { context: floating.context, modal: false, initialFocus: -1, returnFocus: false, children: dropdownContent })) : (dropdownContent);
|
|
159
|
+
return (_jsxs(_Fragment, { children: [_jsx(Input, { ref: setInputRef, id: _id, readOnly: true, value: inputValue, disabled: disabled, size: size, radius: radius, role: "combobox", "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-controls": isOpen ? dropdownId : undefined, className: cx(classes.input, className), classNames: inputClassNames, ...inputProps, ...mergedReferenceProps }), withinPortal ? (_jsx(FloatingPortal, { children: wrappedContent })) : (wrappedContent)] }));
|
|
160
|
+
});
|
|
161
|
+
DateInput.displayName = "@refraktor/dates/DateInput";
|
|
162
|
+
DateInput.configure = createComponentConfig();
|
|
163
|
+
DateInput.classNames = createClassNamesConfig();
|
|
164
|
+
export default DateInput;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { FlipOptions, InlineOptions, Placement, ShiftOptions } from "@floating-ui/react";
|
|
2
|
+
import { createClassNamesConfig, createComponentConfig, FactoryPayload, InputFieldClassNames, InputProps, RefraktorRadius, RefraktorSize, TransitionProps } from "@refraktor/core";
|
|
3
|
+
import type { DatePickerGetDayAriaLabel, DatePickerGetDayLabel, DatePickerGetHeaderLabel, DatePickerGetNavigationAriaLabel, DatePickerGetWeekdayLabel } from "../date-picker";
|
|
4
|
+
import type { MonthPickerGetHeaderLabel, MonthPickerGetMonthAriaLabel, MonthPickerGetMonthLabel, MonthPickerGetNavigationAriaLabel } from "../month-picker";
|
|
5
|
+
export type DateInputValue = Date;
|
|
6
|
+
export type DateInputSize = RefraktorSize;
|
|
7
|
+
export type DateInputRadius = RefraktorRadius;
|
|
8
|
+
export type DateInputOnChange = (value: DateInputValue) => void;
|
|
9
|
+
export type DateInputValueFormat = string;
|
|
10
|
+
export type DateInputPositioning = {
|
|
11
|
+
/** The placement of the dropdown relative to the input @default `bottom-start` */
|
|
12
|
+
placement?: Placement;
|
|
13
|
+
/** Offset distance from the input in pixels @default `4` */
|
|
14
|
+
offset?: number;
|
|
15
|
+
};
|
|
16
|
+
export type DateInputMiddlewares = {
|
|
17
|
+
shift?: boolean | ShiftOptions;
|
|
18
|
+
flip?: boolean | FlipOptions;
|
|
19
|
+
inline?: boolean | InlineOptions;
|
|
20
|
+
};
|
|
21
|
+
export type DateInputClassNames = {
|
|
22
|
+
input?: string;
|
|
23
|
+
dropdown?: string;
|
|
24
|
+
datePicker?: string;
|
|
25
|
+
};
|
|
26
|
+
interface _DateInputProps {
|
|
27
|
+
/** Selected date (controlled). */
|
|
28
|
+
value?: DateInputValue;
|
|
29
|
+
/** Initial selected date (uncontrolled). */
|
|
30
|
+
defaultValue?: DateInputValue;
|
|
31
|
+
/** Callback called when selected date changes. */
|
|
32
|
+
onChange?: DateInputOnChange;
|
|
33
|
+
/** Dropdown open state (controlled). */
|
|
34
|
+
opened?: boolean;
|
|
35
|
+
/** Initial dropdown open state (uncontrolled). */
|
|
36
|
+
defaultOpened?: boolean;
|
|
37
|
+
/** Callback called when dropdown open state changes. */
|
|
38
|
+
onOpenedChange?: (opened: boolean) => void;
|
|
39
|
+
/** Minimum selectable date. */
|
|
40
|
+
minDate?: Date;
|
|
41
|
+
/** Maximum selectable date. */
|
|
42
|
+
maxDate?: Date;
|
|
43
|
+
/** Month picker columns @default `3` */
|
|
44
|
+
monthPickerColumns?: number;
|
|
45
|
+
/** Year picker years rendered in one page @default `9` */
|
|
46
|
+
yearPickerYearsPerPage?: number;
|
|
47
|
+
/** Year picker columns @default `3` */
|
|
48
|
+
yearPickerColumns?: number;
|
|
49
|
+
/** Custom weekday label renderer. */
|
|
50
|
+
getWeekdayLabel?: DatePickerGetWeekdayLabel;
|
|
51
|
+
/** Custom day label renderer. */
|
|
52
|
+
getDayLabel?: DatePickerGetDayLabel;
|
|
53
|
+
/** Custom aria-label generator for day buttons. */
|
|
54
|
+
getDayAriaLabel?: DatePickerGetDayAriaLabel;
|
|
55
|
+
/** Custom header label renderer for visible month. */
|
|
56
|
+
getHeaderLabel?: DatePickerGetHeaderLabel;
|
|
57
|
+
/** Custom aria-label generator for previous/next controls. */
|
|
58
|
+
getNavigationAriaLabel?: DatePickerGetNavigationAriaLabel;
|
|
59
|
+
/** Custom month label renderer in month view. */
|
|
60
|
+
getMonthLabel?: MonthPickerGetMonthLabel;
|
|
61
|
+
/** Custom aria-label generator for month buttons in month view. */
|
|
62
|
+
getMonthAriaLabel?: MonthPickerGetMonthAriaLabel;
|
|
63
|
+
/** Custom header label renderer in month view. */
|
|
64
|
+
getMonthHeaderLabel?: MonthPickerGetHeaderLabel;
|
|
65
|
+
/** Custom aria-label generator for month view navigation controls. */
|
|
66
|
+
getMonthNavigationAriaLabel?: MonthPickerGetNavigationAriaLabel;
|
|
67
|
+
/** Dayjs format used to render selected date in the input @default `MMMM D, YYYY` */
|
|
68
|
+
valueFormat?: DateInputValueFormat;
|
|
69
|
+
/** Positioning settings for the dropdown. */
|
|
70
|
+
positioning?: DateInputPositioning;
|
|
71
|
+
/** Floating middleware settings. */
|
|
72
|
+
middlewares?: DateInputMiddlewares;
|
|
73
|
+
/** Whether to render dropdown in a portal @default `true` */
|
|
74
|
+
withinPortal?: boolean;
|
|
75
|
+
/** Whether to close on click outside @default `true` */
|
|
76
|
+
closeOnClickOutside?: boolean;
|
|
77
|
+
/** Whether to close on Escape key @default `true` */
|
|
78
|
+
closeOnEscape?: boolean;
|
|
79
|
+
/** Transition props for dropdown, uses Transition internally */
|
|
80
|
+
transitionProps?: Omit<TransitionProps, "children" | "mounted">;
|
|
81
|
+
/** Used for styling the core Input field parts. */
|
|
82
|
+
inputClassNames?: InputFieldClassNames;
|
|
83
|
+
/** Used for styling DateInput parts. */
|
|
84
|
+
classNames?: DateInputClassNames;
|
|
85
|
+
}
|
|
86
|
+
export type DateInputProps = _DateInputProps & Omit<InputProps, "value" | "defaultValue" | "onChange" | "readOnly" | "classNames">;
|
|
87
|
+
export interface DateInputFactoryPayload extends FactoryPayload {
|
|
88
|
+
props: DateInputProps;
|
|
89
|
+
ref: HTMLInputElement;
|
|
90
|
+
compound: {
|
|
91
|
+
configure: ReturnType<typeof createComponentConfig<DateInputProps>>;
|
|
92
|
+
classNames: ReturnType<typeof createClassNamesConfig<DateInputClassNames>>;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export {};
|
|
96
|
+
//# sourceMappingURL=date-input.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-input.types.d.ts","sourceRoot":"","sources":["../../../src/components/date-input/date-input.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { default as DateInput } from "./date-input";
|
|
2
|
+
export type { DateInputClassNames, DateInputFactoryPayload, DateInputMiddlewares, DateInputOnChange, DateInputPositioning, DateInputProps, DateInputRadius, DateInputSize, DateInputValue, DateInputValueFormat } from "./date-input.types";
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/date-input/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EACR,mBAAmB,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,aAAa,EACb,cAAc,EACd,oBAAoB,EACvB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as DateInput } from "./date-input";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-picker.d.ts","sourceRoot":"","sources":["../../../src/components/date-picker/date-picker.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAEH,wBAAwB,EAG3B,MAAM,qBAAqB,CAAC;AAkK7B,QAAA,MAAM,UAAU,wEA0cd,CAAC;AAMH,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId, useUncontrolled } from "@refraktor/utils";
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { createClassNamesConfig, createComponentConfig, factory, useTheme, useClassNames, useProps } from "@refraktor/core";
|
|
5
|
+
import { getGridColumns, getPickerSizeStyles, PickerHeader } from "../picker-shared";
|
|
6
|
+
import { useDates } from "../dates-provider";
|
|
7
|
+
import { MonthPicker } from "../month-picker";
|
|
8
|
+
const DAYS_IN_WEEK = 7;
|
|
9
|
+
const CONSISTENT_WEEK_COUNT = 6;
|
|
10
|
+
const DEFAULT_MONTH_PICKER_COLUMNS = 3;
|
|
11
|
+
const DEFAULT_YEAR_PICKER_YEARS_PER_PAGE = 9;
|
|
12
|
+
const DEFAULT_YEAR_PICKER_COLUMNS = 3;
|
|
13
|
+
const defaultProps = {
|
|
14
|
+
monthPickerColumns: DEFAULT_MONTH_PICKER_COLUMNS,
|
|
15
|
+
yearPickerYearsPerPage: DEFAULT_YEAR_PICKER_YEARS_PER_PAGE,
|
|
16
|
+
yearPickerColumns: DEFAULT_YEAR_PICKER_COLUMNS,
|
|
17
|
+
disabled: false,
|
|
18
|
+
size: "md",
|
|
19
|
+
radius: "default"
|
|
20
|
+
};
|
|
21
|
+
const isValidDate = (value) => value instanceof Date && !Number.isNaN(value.getTime());
|
|
22
|
+
const toSafeInteger = (value, fallback) => {
|
|
23
|
+
if (!Number.isFinite(value)) {
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
return Math.trunc(value);
|
|
27
|
+
};
|
|
28
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
29
|
+
const startOfDay = (value) => {
|
|
30
|
+
const next = new Date(value);
|
|
31
|
+
next.setHours(0, 0, 0, 0);
|
|
32
|
+
return next;
|
|
33
|
+
};
|
|
34
|
+
const startOfMonth = (value) => {
|
|
35
|
+
const next = startOfDay(value);
|
|
36
|
+
next.setDate(1);
|
|
37
|
+
return next;
|
|
38
|
+
};
|
|
39
|
+
const isSameDay = (first, second) => first.getFullYear() === second.getFullYear() &&
|
|
40
|
+
first.getMonth() === second.getMonth() &&
|
|
41
|
+
first.getDate() === second.getDate();
|
|
42
|
+
const isSameMonth = (first, second) => first.getFullYear() === second.getFullYear() &&
|
|
43
|
+
first.getMonth() === second.getMonth();
|
|
44
|
+
const addDays = (value, amount) => {
|
|
45
|
+
const next = startOfDay(value);
|
|
46
|
+
next.setDate(next.getDate() + amount);
|
|
47
|
+
return next;
|
|
48
|
+
};
|
|
49
|
+
const addMonthsPreservingDay = (value, amount) => {
|
|
50
|
+
const originalDay = value.getDate();
|
|
51
|
+
const next = startOfDay(value);
|
|
52
|
+
next.setDate(1);
|
|
53
|
+
next.setMonth(next.getMonth() + amount);
|
|
54
|
+
const lastDayInTargetMonth = new Date(next.getFullYear(), next.getMonth() + 1, 0).getDate();
|
|
55
|
+
next.setDate(Math.min(originalDay, lastDayInTargetMonth));
|
|
56
|
+
return next;
|
|
57
|
+
};
|
|
58
|
+
const getDateBounds = (minDate, maxDate) => {
|
|
59
|
+
const normalizedMin = isValidDate(minDate) ? startOfDay(minDate) : undefined;
|
|
60
|
+
const normalizedMax = isValidDate(maxDate) ? startOfDay(maxDate) : undefined;
|
|
61
|
+
if (normalizedMin && normalizedMax && normalizedMin > normalizedMax) {
|
|
62
|
+
return {
|
|
63
|
+
minDate: normalizedMax,
|
|
64
|
+
maxDate: normalizedMin,
|
|
65
|
+
minTimestamp: normalizedMax.getTime(),
|
|
66
|
+
maxTimestamp: normalizedMin.getTime(),
|
|
67
|
+
hasMin: true,
|
|
68
|
+
hasMax: true
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
minDate: normalizedMin,
|
|
73
|
+
maxDate: normalizedMax,
|
|
74
|
+
minTimestamp: normalizedMin?.getTime() ?? Number.NEGATIVE_INFINITY,
|
|
75
|
+
maxTimestamp: normalizedMax?.getTime() ?? Number.POSITIVE_INFINITY,
|
|
76
|
+
hasMin: normalizedMin !== undefined,
|
|
77
|
+
hasMax: normalizedMax !== undefined
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
const clampDate = (value, bounds) => {
|
|
81
|
+
const normalizedValue = startOfDay(value);
|
|
82
|
+
const timestamp = normalizedValue.getTime();
|
|
83
|
+
if (timestamp < bounds.minTimestamp && bounds.minDate) {
|
|
84
|
+
return bounds.minDate;
|
|
85
|
+
}
|
|
86
|
+
if (timestamp > bounds.maxTimestamp && bounds.maxDate) {
|
|
87
|
+
return bounds.maxDate;
|
|
88
|
+
}
|
|
89
|
+
return normalizedValue;
|
|
90
|
+
};
|
|
91
|
+
const isDateDisabled = (value, disabled, bounds) => disabled ||
|
|
92
|
+
value.getTime() < bounds.minTimestamp ||
|
|
93
|
+
value.getTime() > bounds.maxTimestamp;
|
|
94
|
+
const toMonthIndex = (value) => value.getFullYear() * 12 + value.getMonth();
|
|
95
|
+
const resolveMonthStart = (value, bounds) => startOfMonth(clampDate(value, bounds));
|
|
96
|
+
const getVisibleDays = (displayedMonth, firstDayOfWeek, consistentWeeks) => {
|
|
97
|
+
const monthStart = startOfMonth(displayedMonth);
|
|
98
|
+
const leadingDays = (monthStart.getDay() - firstDayOfWeek + DAYS_IN_WEEK) % DAYS_IN_WEEK;
|
|
99
|
+
const daysInMonth = new Date(monthStart.getFullYear(), monthStart.getMonth() + 1, 0).getDate();
|
|
100
|
+
const visibleWithoutTrailing = leadingDays + daysInMonth;
|
|
101
|
+
const trailingDays = consistentWeeks
|
|
102
|
+
? DAYS_IN_WEEK * CONSISTENT_WEEK_COUNT - visibleWithoutTrailing
|
|
103
|
+
: (DAYS_IN_WEEK - (visibleWithoutTrailing % DAYS_IN_WEEK)) % DAYS_IN_WEEK;
|
|
104
|
+
const totalDays = visibleWithoutTrailing + trailingDays;
|
|
105
|
+
const firstVisibleDate = addDays(monthStart, -leadingDays);
|
|
106
|
+
return Array.from({ length: totalDays }, (_, index) => addDays(firstVisibleDate, index));
|
|
107
|
+
};
|
|
108
|
+
const DatePicker = factory((_props, ref) => {
|
|
109
|
+
const { cx, getRadius } = useTheme();
|
|
110
|
+
const { createDate, firstDayOfWeek, consistentWeeks, weekdays } = useDates();
|
|
111
|
+
const { id, value, defaultValue, onChange, minDate, maxDate, monthPickerColumns, yearPickerYearsPerPage, yearPickerColumns, disabled, size, radius, getWeekdayLabel, getDayLabel, getDayAriaLabel, getHeaderLabel, getNavigationAriaLabel, getMonthLabel, getMonthAriaLabel, getMonthHeaderLabel, getMonthNavigationAriaLabel, className, classNames, ...props } = useProps("DatePicker", defaultProps, _props);
|
|
112
|
+
const classes = useClassNames("DatePicker", classNames);
|
|
113
|
+
const _id = useId(id);
|
|
114
|
+
const today = useMemo(() => startOfDay(new Date()), []);
|
|
115
|
+
const bounds = useMemo(() => getDateBounds(minDate, maxDate), [minDate, maxDate]);
|
|
116
|
+
const safeMonthPickerColumns = clamp(toSafeInteger(monthPickerColumns, DEFAULT_MONTH_PICKER_COLUMNS), 1, 6);
|
|
117
|
+
const safeYearPickerYearsPerPage = Math.max(1, toSafeInteger(yearPickerYearsPerPage, DEFAULT_YEAR_PICKER_YEARS_PER_PAGE));
|
|
118
|
+
const safeYearPickerColumns = clamp(toSafeInteger(yearPickerColumns, DEFAULT_YEAR_PICKER_COLUMNS), 1, Math.min(6, safeYearPickerYearsPerPage));
|
|
119
|
+
const [selectedDateState, setSelectedDate] = useUncontrolled({
|
|
120
|
+
value,
|
|
121
|
+
defaultValue,
|
|
122
|
+
finalValue: undefined,
|
|
123
|
+
onChange: (nextDate) => {
|
|
124
|
+
if (nextDate !== undefined) {
|
|
125
|
+
onChange?.(nextDate);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
const selectedDateTimestamp = isValidDate(selectedDateState)
|
|
130
|
+
? startOfDay(selectedDateState).getTime()
|
|
131
|
+
: undefined;
|
|
132
|
+
const selectedDate = selectedDateTimestamp !== undefined
|
|
133
|
+
? new Date(selectedDateTimestamp)
|
|
134
|
+
: undefined;
|
|
135
|
+
const [displayedMonth, setDisplayedMonth] = useState(() => resolveMonthStart(selectedDate ?? today, bounds));
|
|
136
|
+
const [view, setView] = useState("day");
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
setDisplayedMonth((previousMonth) => resolveMonthStart(previousMonth, bounds));
|
|
139
|
+
}, [bounds]);
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
if (selectedDate === undefined) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const normalizedMonth = resolveMonthStart(selectedDate, bounds);
|
|
145
|
+
setDisplayedMonth((previousMonth) => {
|
|
146
|
+
if (isSameMonth(previousMonth, normalizedMonth)) {
|
|
147
|
+
return previousMonth;
|
|
148
|
+
}
|
|
149
|
+
return normalizedMonth;
|
|
150
|
+
});
|
|
151
|
+
}, [bounds, selectedDateTimestamp]);
|
|
152
|
+
const visibleDays = useMemo(() => getVisibleDays(displayedMonth, firstDayOfWeek, consistentWeeks), [consistentWeeks, displayedMonth, firstDayOfWeek]);
|
|
153
|
+
const hasVisibleSelection = selectedDate !== undefined &&
|
|
154
|
+
visibleDays.some((day) => isSameDay(day, selectedDate));
|
|
155
|
+
const firstAvailableDay = useMemo(() => visibleDays.find((day) => !isDateDisabled(day, disabled ?? false, bounds)), [bounds, disabled, visibleDays]);
|
|
156
|
+
const canGoPrevious = !disabled &&
|
|
157
|
+
(!bounds.hasMin ||
|
|
158
|
+
(bounds.minDate !== undefined &&
|
|
159
|
+
toMonthIndex(displayedMonth) > toMonthIndex(bounds.minDate)));
|
|
160
|
+
const canGoNext = !disabled &&
|
|
161
|
+
(!bounds.hasMax ||
|
|
162
|
+
(bounds.maxDate !== undefined &&
|
|
163
|
+
toMonthIndex(displayedMonth) < toMonthIndex(bounds.maxDate)));
|
|
164
|
+
const sizeStyles = getPickerSizeStyles(size);
|
|
165
|
+
const resolveNavigationLabel = (direction) => {
|
|
166
|
+
if (getNavigationAriaLabel) {
|
|
167
|
+
return getNavigationAriaLabel(direction, displayedMonth);
|
|
168
|
+
}
|
|
169
|
+
const nextVisibleMonth = addMonthsPreservingDay(displayedMonth, direction === "previous" ? -1 : 1);
|
|
170
|
+
return direction === "previous"
|
|
171
|
+
? `Show previous month (${createDate(nextVisibleMonth).format("MMMM YYYY")})`
|
|
172
|
+
: `Show next month (${createDate(nextVisibleMonth).format("MMMM YYYY")})`;
|
|
173
|
+
};
|
|
174
|
+
const resolveDayAriaLabel = (day, isSelected, isOutside, isDisabled) => {
|
|
175
|
+
if (getDayAriaLabel) {
|
|
176
|
+
return getDayAriaLabel(day, isSelected);
|
|
177
|
+
}
|
|
178
|
+
const dayLabel = createDate(day).format("dddd, MMMM D, YYYY");
|
|
179
|
+
if (isSelected) {
|
|
180
|
+
return `${dayLabel}, selected`;
|
|
181
|
+
}
|
|
182
|
+
const suffix = [
|
|
183
|
+
isOutside ? "outside current month" : undefined,
|
|
184
|
+
isDisabled ? "unavailable" : undefined
|
|
185
|
+
]
|
|
186
|
+
.filter(Boolean)
|
|
187
|
+
.join(", ");
|
|
188
|
+
return suffix ? `Choose ${dayLabel}, ${suffix}` : `Choose ${dayLabel}`;
|
|
189
|
+
};
|
|
190
|
+
const setDay = (day) => {
|
|
191
|
+
const normalizedDay = startOfDay(day);
|
|
192
|
+
if (isDateDisabled(normalizedDay, disabled ?? false, bounds)) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (selectedDate !== undefined && isSameDay(selectedDate, normalizedDay)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
setDisplayedMonth(startOfMonth(normalizedDay));
|
|
199
|
+
setSelectedDate(normalizedDay);
|
|
200
|
+
};
|
|
201
|
+
const shiftDisplayedMonth = (direction) => {
|
|
202
|
+
setDisplayedMonth((previousMonth) => resolveMonthStart(addMonthsPreservingDay(previousMonth, direction), bounds));
|
|
203
|
+
};
|
|
204
|
+
const handlePrevious = () => {
|
|
205
|
+
if (!canGoPrevious) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
shiftDisplayedMonth(-1);
|
|
209
|
+
};
|
|
210
|
+
const handleNext = () => {
|
|
211
|
+
if (!canGoNext) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
shiftDisplayedMonth(1);
|
|
215
|
+
};
|
|
216
|
+
const handleGridKeyDown = (event) => {
|
|
217
|
+
if (disabled) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const keyboardBaseDay = selectedDate && visibleDays.some((day) => isSameDay(day, selectedDate))
|
|
221
|
+
? selectedDate
|
|
222
|
+
: firstAvailableDay ?? clampDate(displayedMonth, bounds);
|
|
223
|
+
const keyboardSteps = {
|
|
224
|
+
ArrowLeft: -1,
|
|
225
|
+
ArrowRight: 1,
|
|
226
|
+
ArrowUp: -DAYS_IN_WEEK,
|
|
227
|
+
ArrowDown: DAYS_IN_WEEK
|
|
228
|
+
};
|
|
229
|
+
const step = keyboardSteps[event.key];
|
|
230
|
+
if (step !== undefined) {
|
|
231
|
+
event.preventDefault();
|
|
232
|
+
setDay(addDays(keyboardBaseDay, step));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (event.key === "Home") {
|
|
236
|
+
event.preventDefault();
|
|
237
|
+
setDay(startOfMonth(displayedMonth));
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (event.key === "End") {
|
|
241
|
+
event.preventDefault();
|
|
242
|
+
setDay(new Date(displayedMonth.getFullYear(), displayedMonth.getMonth() + 1, 0));
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (event.key === "PageUp") {
|
|
246
|
+
event.preventDefault();
|
|
247
|
+
setDay(addMonthsPreservingDay(keyboardBaseDay, -1));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (event.key === "PageDown") {
|
|
251
|
+
event.preventDefault();
|
|
252
|
+
setDay(addMonthsPreservingDay(keyboardBaseDay, 1));
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
const handleHeaderLabelClick = () => {
|
|
256
|
+
if (disabled) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
setView("month");
|
|
260
|
+
};
|
|
261
|
+
const handleMonthChange = (nextMonth) => {
|
|
262
|
+
if (disabled) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
setDisplayedMonth(resolveMonthStart(nextMonth, bounds));
|
|
266
|
+
setView("day");
|
|
267
|
+
};
|
|
268
|
+
const monthViewMinYear = bounds.minDate?.getFullYear();
|
|
269
|
+
const monthViewMaxYear = bounds.maxDate?.getFullYear();
|
|
270
|
+
return (_jsx("div", { ref: ref, id: _id, className: cx("inline-flex w-full flex-col gap-2 bg-[var(--refraktor-bg)] p-2", getRadius(radius), classes.root, className), ...props, children: view === "month" ? (_jsx(MonthPicker, { value: displayedMonth, onChange: handleMonthChange, minYear: monthViewMinYear, maxYear: monthViewMaxYear, columns: safeMonthPickerColumns, yearPickerYearsPerPage: safeYearPickerYearsPerPage, yearPickerColumns: safeYearPickerColumns, disabled: disabled, size: size, radius: radius, getMonthLabel: getMonthLabel, getMonthAriaLabel: getMonthAriaLabel, getHeaderLabel: getMonthHeaderLabel, getNavigationAriaLabel: getMonthNavigationAriaLabel, className: cx("bg-transparent p-0", classes.monthPicker) })) : (_jsxs(_Fragment, { children: [_jsx(PickerHeader, { label: getHeaderLabel
|
|
271
|
+
? getHeaderLabel(displayedMonth.getMonth(), displayedMonth.getFullYear(), displayedMonth)
|
|
272
|
+
: createDate(displayedMonth).format("MMMM YYYY"), onPrevious: handlePrevious, onNext: handleNext, onLabelClick: disabled ? undefined : handleHeaderLabelClick, previousDisabled: !canGoPrevious, nextDisabled: !canGoNext, previousLabel: resolveNavigationLabel("previous"), nextLabel: resolveNavigationLabel("next"), size: size, radius: radius, classNames: {
|
|
273
|
+
root: classes.header,
|
|
274
|
+
controls: classes.headerControls,
|
|
275
|
+
control: classes.headerControl,
|
|
276
|
+
previousControl: classes.headerPreviousControl,
|
|
277
|
+
nextControl: classes.headerNextControl,
|
|
278
|
+
label: classes.headerLabel
|
|
279
|
+
} }), _jsx("div", { role: "row", className: cx("grid grid-cols-7 text-center text-[var(--refraktor-text-secondary)]", sizeStyles.label, sizeStyles.gridGap, classes.weekdays), children: weekdays.map((weekday, index) => {
|
|
280
|
+
const dayOfWeek = (firstDayOfWeek + index) % DAYS_IN_WEEK;
|
|
281
|
+
return (_jsx("span", { className: cx("truncate", classes.weekday), children: getWeekdayLabel
|
|
282
|
+
? getWeekdayLabel(dayOfWeek, weekday)
|
|
283
|
+
: weekday }, `${weekday}-${index}`));
|
|
284
|
+
}) }), _jsx("div", { role: "grid", "aria-label": `Date picker, ${createDate(displayedMonth).format("MMMM YYYY")}`, className: cx("grid", getGridColumns(DAYS_IN_WEEK), sizeStyles.gridGap, classes.grid), onKeyDown: handleGridKeyDown, children: visibleDays.map((day) => {
|
|
285
|
+
const isOutside = !isSameMonth(day, displayedMonth);
|
|
286
|
+
const isSelected = selectedDate !== undefined &&
|
|
287
|
+
isSameDay(day, selectedDate);
|
|
288
|
+
const isToday = isSameDay(day, today);
|
|
289
|
+
const isDayDisabled = isDateDisabled(day, disabled ?? false, bounds);
|
|
290
|
+
const tabIndex = isSelected ||
|
|
291
|
+
(!hasVisibleSelection &&
|
|
292
|
+
firstAvailableDay !== undefined &&
|
|
293
|
+
isSameDay(day, firstAvailableDay))
|
|
294
|
+
? 0
|
|
295
|
+
: -1;
|
|
296
|
+
return (_jsx("button", { type: "button", role: "gridcell", "aria-selected": isSelected, "aria-current": isToday ? "date" : undefined, "aria-label": resolveDayAriaLabel(day, isSelected, isOutside, isDayDisabled), tabIndex: tabIndex, "data-active": isSelected, "data-outside": isOutside, "data-today": isToday, "data-disabled": isDayDisabled, disabled: isDayDisabled, className: cx("inline-flex items-center justify-center font-medium text-[var(--refraktor-text)] transition-colors", "hover:bg-[var(--refraktor-bg-hover)]", isSelected &&
|
|
297
|
+
"bg-[var(--refraktor-primary)] text-[var(--refraktor-primary-text)] hover:bg-[var(--refraktor-primary)]", isOutside &&
|
|
298
|
+
"text-[var(--refraktor-text-secondary)]", isToday &&
|
|
299
|
+
!isSelected &&
|
|
300
|
+
"ring-1 ring-[var(--refraktor-primary)]", isDayDisabled &&
|
|
301
|
+
"pointer-events-none cursor-not-allowed opacity-50", sizeStyles.cell, getRadius(radius), classes.day, isOutside && classes.dayOutside, isToday && classes.dayToday, isSelected && classes.daySelected, isDayDisabled && classes.dayDisabled), onClick: () => setDay(day), children: getDayLabel ? getDayLabel(day) : day.getDate() }, day.toISOString()));
|
|
302
|
+
}) })] })) }));
|
|
303
|
+
});
|
|
304
|
+
DatePicker.displayName = "@refraktor/dates/DatePicker";
|
|
305
|
+
DatePicker.configure = createComponentConfig();
|
|
306
|
+
DatePicker.classNames = createClassNamesConfig();
|
|
307
|
+
export default DatePicker;
|