@react-fabric/date 1.0.0-beta.8 → 1.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/README.md +2 -9
- package/{src/components/Date.module.css → css/_date.css} +5 -5
- package/{src/superdate/SuperDate.module.css → css/styles.css} +2 -8
- package/dist/cjs/components/DateCell.js +5 -7
- package/dist/cjs/components/DateHeader.js +27 -20
- package/dist/cjs/components/DatePage.js +10 -12
- package/dist/cjs/components/DecadePage.js +3 -3
- package/dist/cjs/components/MonthPage.js +3 -3
- package/dist/cjs/components/TimeSliders.js +36 -35
- package/dist/cjs/components/YearPage.js +3 -3
- package/dist/cjs/display/DateDisplay.js +25 -8
- package/dist/cjs/display/DateDuration.js +155 -0
- package/dist/cjs/hijri/DateConverter.js +154 -0
- package/dist/cjs/hijri/FormatDate.js +116 -0
- package/dist/cjs/hijri/HijriDate.js +531 -0
- package/dist/cjs/hijri/StringDateParser.js +74 -0
- package/dist/cjs/hijri/index.js +15 -1025
- package/dist/cjs/hijri/initializer.js +113 -0
- package/dist/cjs/hijri/locales.js +19 -0
- package/dist/cjs/hijri/safe.js +17 -0
- package/dist/cjs/i18n/ar/date.json.js +213 -0
- package/dist/cjs/i18n/en/date.json.js +83 -0
- package/dist/cjs/i18n/index.js +4 -10
- package/dist/cjs/index.js +41 -2
- package/dist/cjs/panels/BasePanel.js +6 -5
- package/dist/cjs/panels/DatePanel.js +45 -21
- package/dist/cjs/{superdate → panels}/EventPanel.js +28 -12
- package/dist/cjs/panels/RangePanel.js +49 -19
- package/dist/cjs/utils/dateUtil.js +3 -5
- package/dist/esm/components/DateCell.js +6 -8
- package/dist/esm/components/DateHeader.js +28 -21
- package/dist/esm/components/DatePage.js +11 -13
- package/dist/esm/components/DecadePage.js +3 -3
- package/dist/esm/components/MonthPage.js +3 -3
- package/dist/esm/components/TimeSliders.js +38 -37
- package/dist/esm/components/YearPage.js +3 -3
- package/dist/esm/display/DateDisplay.js +26 -9
- package/dist/esm/display/DateDuration.js +155 -0
- package/dist/esm/hijri/DateConverter.js +152 -0
- package/dist/esm/hijri/FormatDate.js +113 -0
- package/dist/esm/hijri/HijriDate.js +528 -0
- package/dist/esm/hijri/StringDateParser.js +74 -0
- package/dist/esm/hijri/index.js +15 -1025
- package/dist/esm/hijri/initializer.js +113 -0
- package/dist/esm/hijri/locales.js +18 -0
- package/dist/esm/hijri/safe.js +14 -0
- package/dist/esm/i18n/ar/date.json.js +209 -0
- package/dist/esm/i18n/en/date.json.js +79 -0
- package/dist/esm/i18n/index.js +2 -8
- package/dist/esm/index.js +36 -1
- package/dist/esm/panels/BasePanel.js +6 -5
- package/dist/esm/panels/DatePanel.js +48 -24
- package/dist/esm/{superdate → panels}/EventPanel.js +29 -13
- package/dist/esm/panels/RangePanel.js +52 -22
- package/dist/esm/utils/dateUtil.js +4 -6
- package/dist/types/components/DateCell.d.ts +1 -1
- package/dist/types/components/DateHeader.d.ts +1 -1
- package/dist/types/components/DatePage.d.ts +1 -1
- package/dist/types/components/DecadePage.d.ts +1 -1
- package/dist/types/components/MonthPage.d.ts +1 -1
- package/dist/types/components/TimeSliders.d.ts +1 -1
- package/dist/types/components/YearPage.d.ts +1 -1
- package/dist/types/display/DateDisplay.d.ts +17 -4
- package/dist/types/display/DateDuration.d.ts +25 -0
- package/dist/types/hijri/DateConverter.d.ts +13 -0
- package/dist/types/hijri/FormatDate.d.ts +19 -0
- package/dist/types/hijri/HijriDate.d.ts +214 -0
- package/dist/types/hijri/StringDateParser.d.ts +11 -0
- package/dist/types/hijri/index.d.ts +2 -2
- package/dist/types/hijri/initializer.d.ts +129 -0
- package/dist/types/hijri/locales.d.ts +19 -0
- package/dist/types/hijri/safe.d.ts +3 -0
- package/dist/types/index.d.ts +32 -1
- package/dist/types/panels/BasePanel.d.ts +1 -1
- package/dist/types/panels/DatePanel.d.ts +22 -1
- package/dist/types/panels/EventPanel.d.ts +17 -0
- package/dist/types/panels/RangePanel.d.ts +19 -1
- package/dist/types/types/index.d.ts +8 -40
- package/package.json +14 -21
- package/src/components/DateCell.tsx +20 -75
- package/src/components/DateHeader.tsx +27 -78
- package/src/components/DatePage.tsx +11 -36
- package/src/components/DecadePage.tsx +2 -2
- package/src/components/MonthPage.tsx +2 -2
- package/src/components/TimeSliders.tsx +20 -60
- package/src/components/YearPage.tsx +2 -2
- package/src/display/DateDisplay.tsx +26 -38
- package/src/display/DateDuration.tsx +170 -0
- package/src/hijri/DateConverter.js +175 -0
- package/src/hijri/FormatDate.js +134 -0
- package/src/hijri/HijriDate.js +477 -0
- package/src/hijri/StringDateParser.js +68 -0
- package/src/hijri/index.js +16 -1535
- package/src/hijri/initializer.js +96 -0
- package/src/hijri/locales.js +109 -0
- package/src/hijri/safe.js +16 -0
- package/src/i18n/ar/date.json +203 -0
- package/src/i18n/en/date.json +73 -0
- package/src/i18n/index.ts +2 -5
- package/src/index.ts +29 -18
- package/src/panels/BasePanel.tsx +7 -10
- package/src/panels/DatePanel.tsx +41 -33
- package/src/{superdate → panels}/EventPanel.tsx +52 -53
- package/src/panels/RangePanel.tsx +54 -60
- package/src/types/index.ts +8 -43
- package/src/utils/dateUtil.ts +26 -82
- package/dist/cjs/components/Date.module.css +0 -1
- package/dist/cjs/i18n/ar.json.js +0 -25
- package/dist/cjs/i18n/en.json.js +0 -21
- package/dist/cjs/i18n/superdate/ar.json.js +0 -86
- package/dist/cjs/i18n/superdate/en.json.js +0 -93
- package/dist/cjs/superdate/QuickSelect.js +0 -134
- package/dist/cjs/superdate/RelativeInput.js +0 -79
- package/dist/cjs/superdate/RelativePanel.js +0 -91
- package/dist/cjs/superdate/SuperDate.module.css +0 -1
- package/dist/cjs/superdate/Superdate.js +0 -177
- package/dist/esm/components/Date.module.css +0 -1
- package/dist/esm/i18n/ar.json.js +0 -21
- package/dist/esm/i18n/en.json.js +0 -18
- package/dist/esm/i18n/superdate/ar.json.js +0 -79
- package/dist/esm/i18n/superdate/en.json.js +0 -85
- package/dist/esm/superdate/QuickSelect.js +0 -134
- package/dist/esm/superdate/RelativeInput.js +0 -79
- package/dist/esm/superdate/RelativePanel.js +0 -91
- package/dist/esm/superdate/SuperDate.module.css +0 -1
- package/dist/esm/superdate/Superdate.js +0 -176
- package/dist/types/superdate/EventPanel.d.ts +0 -4
- package/dist/types/superdate/QuickSelect.d.ts +0 -1
- package/dist/types/superdate/RelativeInput.d.ts +0 -1
- package/dist/types/superdate/RelativePanel.d.ts +0 -1
- package/dist/types/superdate/Superdate.d.ts +0 -17
- package/src/i18n/ar.json +0 -14
- package/src/i18n/en.json +0 -13
- package/src/i18n/superdate/ar.json +0 -70
- package/src/i18n/superdate/en.json +0 -75
- package/src/superdate/QuickSelect.tsx +0 -121
- package/src/superdate/RelativeInput.tsx +0 -64
- package/src/superdate/RelativePanel.tsx +0 -103
- package/src/superdate/Superdate.tsx +0 -198
package/src/panels/DatePanel.tsx
CHANGED
|
@@ -21,9 +21,8 @@
|
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { Button, useDebounce,
|
|
25
|
-
import { isArray, isEmpty } from "@react-fabric/utilities";
|
|
26
|
-
import classNames from "classnames";
|
|
24
|
+
import { Button, useApplicationContext, useDebounce, usePropState } from "@react-fabric/core";
|
|
25
|
+
import { cn, isArray, isEmpty } from "@react-fabric/utilities";
|
|
27
26
|
import { isAfter, isBefore, isSameDay } from "date-fns";
|
|
28
27
|
import { useCallback, useEffect, useReducer, useState } from "react";
|
|
29
28
|
import { useTranslation } from "react-i18next";
|
|
@@ -46,7 +45,28 @@ type PanelActions =
|
|
|
46
45
|
| { type: "changePage"; page: PageType }
|
|
47
46
|
| { type: "changePageDate"; date: Date; pageChange: boolean };
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* DatePanel component to display a date picker with options for selecting a date and time.
|
|
50
|
+
* It allows switching between Gregorian and Hijri calendars, and includes buttons for selecting today or canceling the selection.
|
|
51
|
+
* It uses the current calendar and locale from the global context.
|
|
52
|
+
* It handles date selection, time changes, and page navigation within the date picker.
|
|
53
|
+
* It also supports disabling specific dates and setting minimum and maximum date limits.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```jsx
|
|
57
|
+
* <DatePanel
|
|
58
|
+
* className="my-date-panel"
|
|
59
|
+
* max="2025-12-31"
|
|
60
|
+
* min="2020-01-01"
|
|
61
|
+
* dateDisabled={["2023-01-01", "2023-12-25"]}
|
|
62
|
+
* onChange={(date) => console.log("Selected date:", date)}
|
|
63
|
+
* showHijriToggle={true}
|
|
64
|
+
* value={new Date()}
|
|
65
|
+
* withTime={true}
|
|
66
|
+
* />
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export function DatePanel({
|
|
50
70
|
className,
|
|
51
71
|
max,
|
|
52
72
|
min,
|
|
@@ -56,18 +76,14 @@ export const DatePanel = ({
|
|
|
56
76
|
value,
|
|
57
77
|
withTime,
|
|
58
78
|
...rest
|
|
59
|
-
}: DateProps)
|
|
79
|
+
}: DateProps) {
|
|
60
80
|
const { t } = useTranslation("date");
|
|
61
|
-
const { currentCalendar } =
|
|
62
|
-
const [isHijri,
|
|
81
|
+
const { currentCalendar } = useApplicationContext();
|
|
82
|
+
const [isHijri, setIsHijri] = usePropState(currentCalendar === "hijri");
|
|
63
83
|
const [canSelectToday, setCanSelectToday] = useState(true);
|
|
64
84
|
|
|
65
85
|
const fireChange = useDebounce(onChange, [onChange]);
|
|
66
86
|
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
setHijri(currentCalendar === "hijri");
|
|
69
|
-
}, [currentCalendar]);
|
|
70
|
-
|
|
71
87
|
const [state, dispatch] = useReducer(
|
|
72
88
|
(state: PanelState, action: PanelActions) => {
|
|
73
89
|
if (action.type === "reset") {
|
|
@@ -101,7 +117,7 @@ export const DatePanel = ({
|
|
|
101
117
|
}
|
|
102
118
|
if (action.type === "changePageDate") {
|
|
103
119
|
state.pageDate = action.date;
|
|
104
|
-
action.pageChange
|
|
120
|
+
if (action.pageChange) state.page = Math.max(state.page - 1, 0);
|
|
105
121
|
}
|
|
106
122
|
if (action.type === "changeDate" || action.type === "changeTime") {
|
|
107
123
|
if (min && state.selected && isBefore(state.selected, new Date(min))) {
|
|
@@ -114,16 +130,21 @@ export const DatePanel = ({
|
|
|
114
130
|
state.hour = state.selected.getHours();
|
|
115
131
|
state.minute = state.selected.getMinutes();
|
|
116
132
|
}
|
|
117
|
-
state.selected
|
|
133
|
+
if (state.selected) fireChange?.(state.selected.toISOString());
|
|
118
134
|
}
|
|
119
135
|
return { ...state };
|
|
120
136
|
},
|
|
121
137
|
{
|
|
122
138
|
page: PageType.DATE,
|
|
123
|
-
pageDate: new Date(),
|
|
124
139
|
hour: 0,
|
|
125
140
|
minute: 0,
|
|
126
141
|
} as PanelState,
|
|
142
|
+
() => ({
|
|
143
|
+
page: PageType.DATE,
|
|
144
|
+
pageDate: new Date(),
|
|
145
|
+
hour: 0,
|
|
146
|
+
minute: 0,
|
|
147
|
+
}),
|
|
127
148
|
);
|
|
128
149
|
|
|
129
150
|
useEffect(() => {
|
|
@@ -134,6 +155,7 @@ export const DatePanel = ({
|
|
|
134
155
|
if (max && canSelect) {
|
|
135
156
|
canSelect = isBefore(new Date(), max);
|
|
136
157
|
}
|
|
158
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
137
159
|
setCanSelectToday(canSelect);
|
|
138
160
|
}, [min, max]);
|
|
139
161
|
|
|
@@ -170,13 +192,7 @@ export const DatePanel = ({
|
|
|
170
192
|
}, []);
|
|
171
193
|
|
|
172
194
|
return (
|
|
173
|
-
<div
|
|
174
|
-
className={classNames(
|
|
175
|
-
"block overflow-hidden bg-base rounded-capped",
|
|
176
|
-
className,
|
|
177
|
-
)}
|
|
178
|
-
{...rest}
|
|
179
|
-
>
|
|
195
|
+
<div className={cn("block overflow-hidden bg-default rounded-capped", className)} {...rest}>
|
|
180
196
|
<BasePanel
|
|
181
197
|
{...state}
|
|
182
198
|
max={max}
|
|
@@ -198,7 +214,7 @@ export const DatePanel = ({
|
|
|
198
214
|
color="primary"
|
|
199
215
|
className="my-1"
|
|
200
216
|
onClick={() => {
|
|
201
|
-
|
|
217
|
+
setIsHijri(!isHijri);
|
|
202
218
|
}}
|
|
203
219
|
>
|
|
204
220
|
{t(isHijri ? "label.gregorian" : "label.hijri")}
|
|
@@ -206,13 +222,7 @@ export const DatePanel = ({
|
|
|
206
222
|
)}
|
|
207
223
|
</span>
|
|
208
224
|
{state.page === PageType.DATE && canSelectToday && (
|
|
209
|
-
<Button
|
|
210
|
-
size="sm"
|
|
211
|
-
variant="link"
|
|
212
|
-
color="primary"
|
|
213
|
-
className="my-1"
|
|
214
|
-
onClick={() => selectDate(new Date())}
|
|
215
|
-
>
|
|
225
|
+
<Button size="sm" variant="link" color="primary" className="my-1" onClick={() => selectDate(new Date())}>
|
|
216
226
|
{t("label.today")}
|
|
217
227
|
</Button>
|
|
218
228
|
)}
|
|
@@ -222,9 +232,7 @@ export const DatePanel = ({
|
|
|
222
232
|
variant="link"
|
|
223
233
|
color="primary"
|
|
224
234
|
className="my-1"
|
|
225
|
-
onClick={() =>
|
|
226
|
-
dispatch({ type: "changePage", page: PageType.DATE })
|
|
227
|
-
}
|
|
235
|
+
onClick={() => dispatch({ type: "changePage", page: PageType.DATE })}
|
|
228
236
|
>
|
|
229
237
|
{t("core:action.cancel")}
|
|
230
238
|
</Button>
|
|
@@ -232,4 +240,4 @@ export const DatePanel = ({
|
|
|
232
240
|
</div>
|
|
233
241
|
</div>
|
|
234
242
|
);
|
|
235
|
-
}
|
|
243
|
+
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { Content,
|
|
24
|
+
import { Content, Layout } from "@react-fabric/core";
|
|
25
25
|
import { EMPTY_ARRAY } from "@react-fabric/utilities";
|
|
26
26
|
import { endOfDay, isSameMonth, setYear, startOfDay } from "date-fns";
|
|
27
27
|
import i18next from "i18next";
|
|
@@ -30,7 +30,7 @@ import { useTranslation } from "react-i18next";
|
|
|
30
30
|
import { DateHeader } from "../components/DateHeader";
|
|
31
31
|
import { DecadePage } from "../components/DecadePage";
|
|
32
32
|
import { YearPage } from "../components/YearPage";
|
|
33
|
-
import { type EventType, PageType
|
|
33
|
+
import { type EventType, PageType } from "../types";
|
|
34
34
|
import { DateUtil } from "../utils/dateUtil";
|
|
35
35
|
|
|
36
36
|
interface EventState {
|
|
@@ -44,11 +44,7 @@ type EventActions =
|
|
|
44
44
|
| { type: "changePage"; page: PageType }
|
|
45
45
|
| { type: "changePageDate"; date: Date; changePage?: boolean };
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
year: number,
|
|
49
|
-
recurring: EventType[] = [],
|
|
50
|
-
predefined: Array<EventType<Date>> = [],
|
|
51
|
-
) => {
|
|
47
|
+
function makeList(year: number, recurring: EventType[] = [], predefined: Array<EventType<Date>> = []) {
|
|
52
48
|
const list = [...predefined];
|
|
53
49
|
recurring.forEach((evt) =>
|
|
54
50
|
list.push({
|
|
@@ -73,11 +69,7 @@ const makeList = (
|
|
|
73
69
|
let hijriStart = DateUtil.startOfYear(new Date(`${year - 1}-12-31`), true);
|
|
74
70
|
hijriStart = DateUtil.addMonths(hijriStart, 8, true);
|
|
75
71
|
if (hijriStart.getFullYear() !== year) {
|
|
76
|
-
hijriStart = DateUtil.addYears(
|
|
77
|
-
hijriStart,
|
|
78
|
-
hijriStart.getFullYear() > year ? -1 : 1,
|
|
79
|
-
true,
|
|
80
|
-
);
|
|
72
|
+
hijriStart = DateUtil.addYears(hijriStart, hijriStart.getFullYear() > year ? -1 : 1, true);
|
|
81
73
|
}
|
|
82
74
|
const hijriEnd = DateUtil.endOfMonth(hijriStart, true);
|
|
83
75
|
list.push({
|
|
@@ -88,18 +80,28 @@ const makeList = (
|
|
|
88
80
|
return list.sort((a, b) => {
|
|
89
81
|
return a.start.getTime() < b.start.getTime() ? -1 : 1;
|
|
90
82
|
});
|
|
91
|
-
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface EventProps {
|
|
86
|
+
isHijri?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* list of calendar events
|
|
89
|
+
*/
|
|
90
|
+
events?: EventType[];
|
|
91
|
+
/**
|
|
92
|
+
* list of calendar events
|
|
93
|
+
*/
|
|
94
|
+
recurringEvents?: EventType[];
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
/**
|
|
97
|
+
* change handler
|
|
98
|
+
*/
|
|
99
|
+
onChange?: (value: string, dates: [string, string]) => void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function EventPanel({ isHijri, onChange, events = EMPTY_ARRAY, recurringEvents = EMPTY_ARRAY }: EventProps) {
|
|
99
103
|
const { t } = useTranslation();
|
|
100
|
-
const [predefined, setPredefined] = useState<
|
|
101
|
-
KeyValue<Array<EventType<Date>>>
|
|
102
|
-
>({});
|
|
104
|
+
const [predefined, setPredefined] = useState<KeyValue<EventType<Date>[]>>(() => ({}));
|
|
103
105
|
|
|
104
106
|
const [state, dispatch] = useReducer(
|
|
105
107
|
(state: EventState, action: EventActions) => {
|
|
@@ -114,25 +116,25 @@ export const EventPanel = ({
|
|
|
114
116
|
if (action.type === "changePageDate") {
|
|
115
117
|
resetList = true;
|
|
116
118
|
state.pageDate = action.date;
|
|
117
|
-
if (action.changePage
|
|
119
|
+
if (action.changePage) {
|
|
118
120
|
state.page = Math.max(state.page - 1, PageType.MONTH);
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
if (resetList) {
|
|
122
124
|
const year = state.pageDate.getFullYear();
|
|
123
|
-
state.events = makeList(
|
|
124
|
-
year,
|
|
125
|
-
recurringEvents ?? [],
|
|
126
|
-
predefined[year] ?? [],
|
|
127
|
-
);
|
|
125
|
+
state.events = makeList(year, recurringEvents ?? [], predefined[year] ?? []);
|
|
128
126
|
}
|
|
129
127
|
return { ...state };
|
|
130
128
|
},
|
|
131
129
|
{
|
|
130
|
+
page: PageType.MONTH,
|
|
131
|
+
events: [],
|
|
132
|
+
} as unknown as EventState,
|
|
133
|
+
() => ({
|
|
132
134
|
pageDate: new Date(),
|
|
133
135
|
page: PageType.MONTH,
|
|
134
136
|
events: [],
|
|
135
|
-
}
|
|
137
|
+
}),
|
|
136
138
|
);
|
|
137
139
|
|
|
138
140
|
useEffect(() => {
|
|
@@ -140,31 +142,34 @@ export const EventPanel = ({
|
|
|
140
142
|
}, []);
|
|
141
143
|
|
|
142
144
|
useEffect(() => {
|
|
143
|
-
const predefined: KeyValue = {};
|
|
145
|
+
const predefined: KeyValue<EventType<Date>[]> = {};
|
|
144
146
|
events.forEach((evt) => {
|
|
145
147
|
const start = startOfDay(new Date(evt.start));
|
|
146
148
|
const end = endOfDay(new Date(evt.end));
|
|
147
149
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
predefined[
|
|
150
|
+
const startYear = start.getFullYear();
|
|
151
|
+
if (!predefined[startYear]) predefined[startYear] = [];
|
|
152
|
+
predefined[startYear].push({
|
|
153
|
+
...evt,
|
|
154
|
+
start,
|
|
155
|
+
end,
|
|
156
|
+
});
|
|
151
157
|
if (start.getFullYear() !== end.getFullYear()) {
|
|
152
158
|
if (!predefined[end.getFullYear()]) predefined[end.getFullYear()] = [];
|
|
153
|
-
predefined[end.getFullYear()].push(evt);
|
|
159
|
+
predefined[end.getFullYear()].push({ ...evt, start, end });
|
|
154
160
|
}
|
|
155
161
|
});
|
|
162
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
156
163
|
setPredefined(predefined);
|
|
157
164
|
}, [events]);
|
|
158
165
|
|
|
159
166
|
return (
|
|
160
|
-
<
|
|
167
|
+
<Layout>
|
|
161
168
|
<DateHeader
|
|
162
169
|
page={state.page}
|
|
163
170
|
pageDate={state.pageDate}
|
|
164
171
|
changePage={(page) => dispatch({ type: "changePage", page })}
|
|
165
|
-
changePageDate={(date) =>
|
|
166
|
-
dispatch({ type: "changePageDate", date, changePage: false })
|
|
167
|
-
}
|
|
172
|
+
changePageDate={(date) => dispatch({ type: "changePageDate", date, changePage: false })}
|
|
168
173
|
/>
|
|
169
174
|
{state.page === PageType.MONTH && (
|
|
170
175
|
<Content className="p-2 max-h-48">
|
|
@@ -172,24 +177,18 @@ export const EventPanel = ({
|
|
|
172
177
|
<div
|
|
173
178
|
role="none"
|
|
174
179
|
key={evt.label}
|
|
180
|
+
data-dropdown-dismiss="true"
|
|
175
181
|
onClick={() => {
|
|
176
|
-
onChange?.(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
);
|
|
182
|
+
onChange?.(`${evt.start.toISOString()}|${evt.end.toISOString()}`, [
|
|
183
|
+
evt.start.toISOString(),
|
|
184
|
+
evt.end.toISOString(),
|
|
185
|
+
]);
|
|
180
186
|
}}
|
|
181
187
|
className="text-sm my-1 py-1 px-2 flex flex-nowrap items-center cursor-pointer rounded hover:bg-primary-50"
|
|
182
188
|
>
|
|
183
|
-
<div className="flex-1 truncate">
|
|
184
|
-
{t(evt.label, { defaultValue: evt.label })}
|
|
185
|
-
</div>
|
|
189
|
+
<div className="flex-1 truncate">{t(evt.label, { defaultValue: evt.label })}</div>
|
|
186
190
|
<div className="text-muted text-xs">
|
|
187
|
-
{DateUtil.format(
|
|
188
|
-
evt.start,
|
|
189
|
-
isSameMonth(evt.start, evt.end) ? "dd" : "dd/MMM",
|
|
190
|
-
i18next.language,
|
|
191
|
-
isHijri,
|
|
192
|
-
)}
|
|
191
|
+
{DateUtil.format(evt.start, isSameMonth(evt.start, evt.end) ? "dd" : "dd/MMM", i18next.language, isHijri)}
|
|
193
192
|
<span> - </span>
|
|
194
193
|
{DateUtil.format(evt.end, "dd/MMM", i18next.language, isHijri)}
|
|
195
194
|
</div>
|
|
@@ -213,6 +212,6 @@ export const EventPanel = ({
|
|
|
213
212
|
changePageDate={(date) => dispatch({ type: "changePageDate", date })}
|
|
214
213
|
/>
|
|
215
214
|
)}
|
|
216
|
-
</
|
|
215
|
+
</Layout>
|
|
217
216
|
);
|
|
218
|
-
}
|
|
217
|
+
}
|
|
@@ -21,11 +21,10 @@
|
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { Button, useDebounce,
|
|
25
|
-
import { isArray, isEmpty } from "@react-fabric/utilities";
|
|
26
|
-
import classNames from "classnames";
|
|
24
|
+
import { Button, useApplicationContext, useDebounce, usePropState } from "@react-fabric/core";
|
|
25
|
+
import { cn, isArray, isEmpty } from "@react-fabric/utilities";
|
|
27
26
|
import { addMonths, isAfter, isBefore, isSameDay } from "date-fns";
|
|
28
|
-
import { useCallback, useEffect, useReducer
|
|
27
|
+
import { useCallback, useEffect, useReducer } from "react";
|
|
29
28
|
import { useTranslation } from "react-i18next";
|
|
30
29
|
import { PageType, type DateLike, type RangeProps } from "../types";
|
|
31
30
|
import { DateUtil } from "../utils/dateUtil";
|
|
@@ -58,7 +57,25 @@ type PanelActions =
|
|
|
58
57
|
pageChange?: boolean;
|
|
59
58
|
};
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
/**
|
|
61
|
+
* RangePanel component to display a date range picker with options for selecting a start and end date.
|
|
62
|
+
* It allows switching between Gregorian and Hijri calendars, and includes buttons for applying or canceling the selection.
|
|
63
|
+
* It uses the current calendar and locale from the global context.
|
|
64
|
+
* It handles date selection, time changes, and page navigation within the date picker.
|
|
65
|
+
* It also supports disabling specific dates and setting minimum and maximum date limits.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```jsx
|
|
69
|
+
* <RangePanel
|
|
70
|
+
* className="my-range-panel"
|
|
71
|
+
* max="2025-12-31"
|
|
72
|
+
* min="2020-01-01"
|
|
73
|
+
* dateDisabled={["2023-01-01", "2023-12-25"]}
|
|
74
|
+
* onChange={(dateRange) => console.log("Selected date range:", dateRange)}
|
|
75
|
+
* />
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function RangePanel({
|
|
62
79
|
className,
|
|
63
80
|
max,
|
|
64
81
|
min,
|
|
@@ -70,17 +87,13 @@ export const RangePanel = ({
|
|
|
70
87
|
// @ts-expect-error ignore
|
|
71
88
|
showApply = false,
|
|
72
89
|
...rest
|
|
73
|
-
}: RangeProps)
|
|
90
|
+
}: RangeProps) {
|
|
74
91
|
const { t } = useTranslation("date");
|
|
75
|
-
const { currentCalendar } =
|
|
76
|
-
const [isHijri,
|
|
92
|
+
const { currentCalendar } = useApplicationContext();
|
|
93
|
+
const [isHijri, setIsHijri] = usePropState(currentCalendar === "hijri");
|
|
77
94
|
|
|
78
95
|
const fireChange = useDebounce(onChange, [onChange]);
|
|
79
96
|
|
|
80
|
-
useEffect(() => {
|
|
81
|
-
setHijri(currentCalendar === "hijri");
|
|
82
|
-
}, [currentCalendar]);
|
|
83
|
-
|
|
84
97
|
const [state, dispatch] = useReducer(
|
|
85
98
|
(state: RangeState, action: PanelActions) => {
|
|
86
99
|
if (action.type === "reset") {
|
|
@@ -155,42 +168,39 @@ export const RangePanel = ({
|
|
|
155
168
|
state[action.range].pageDate = action.date;
|
|
156
169
|
if (action.pageChange) {
|
|
157
170
|
state[action.range].page = Math.max(state[action.range].page - 1, 0);
|
|
158
|
-
action.range === "start" &&
|
|
159
|
-
state.start.page === PageType.DATE &&
|
|
160
|
-
(state.end.pageDate = addMonths(action.date, 1));
|
|
171
|
+
if (action.range === "start" && state.start.page === PageType.DATE) state.end.pageDate = addMonths(action.date, 1);
|
|
161
172
|
}
|
|
162
173
|
}
|
|
163
174
|
|
|
164
175
|
if (action.type === "changeDate" || action.type === "changeTime") {
|
|
165
|
-
if (
|
|
166
|
-
min &&
|
|
167
|
-
state.selected &&
|
|
168
|
-
isBefore(state.selected[0], new Date(min))
|
|
169
|
-
) {
|
|
176
|
+
if (min && state.selected && isBefore(state.selected[0], new Date(min))) {
|
|
170
177
|
state.selected[0] = new Date(min);
|
|
171
178
|
state.start.hour = state.selected[0].getHours();
|
|
172
179
|
state.start.minute = state.selected[0].getMinutes();
|
|
173
180
|
}
|
|
174
|
-
if (
|
|
175
|
-
max &&
|
|
176
|
-
state.selected &&
|
|
177
|
-
isAfter(state.selected[1], new Date(max))
|
|
178
|
-
) {
|
|
181
|
+
if (max && state.selected && isAfter(state.selected[1], new Date(max))) {
|
|
179
182
|
state.selected[1] = new Date(max);
|
|
180
183
|
state.end.hour = state.selected[1].getHours();
|
|
181
184
|
state.end.minute = state.selected[1].getMinutes();
|
|
182
185
|
}
|
|
183
|
-
!showApply &&
|
|
184
|
-
state.selected
|
|
185
|
-
!state.hilight &&
|
|
186
|
-
fireChange?.([
|
|
187
|
-
state.selected[0].toISOString(),
|
|
188
|
-
state.selected[1].toISOString(),
|
|
189
|
-
]);
|
|
186
|
+
if (!showApply && state.selected && !state.hilight)
|
|
187
|
+
fireChange?.([state.selected[0].toISOString(), state.selected[1].toISOString()]);
|
|
190
188
|
}
|
|
191
189
|
return { ...state };
|
|
192
190
|
},
|
|
193
191
|
{
|
|
192
|
+
start: {
|
|
193
|
+
page: PageType.DATE,
|
|
194
|
+
hour: 0,
|
|
195
|
+
minute: 0,
|
|
196
|
+
},
|
|
197
|
+
end: {
|
|
198
|
+
page: PageType.DATE,
|
|
199
|
+
hour: 0,
|
|
200
|
+
minute: 0,
|
|
201
|
+
},
|
|
202
|
+
} as RangeState,
|
|
203
|
+
() => ({
|
|
194
204
|
start: {
|
|
195
205
|
page: PageType.DATE,
|
|
196
206
|
pageDate: new Date(),
|
|
@@ -203,7 +213,9 @@ export const RangePanel = ({
|
|
|
203
213
|
hour: 0,
|
|
204
214
|
minute: 0,
|
|
205
215
|
},
|
|
206
|
-
|
|
216
|
+
hilight: undefined,
|
|
217
|
+
selected: undefined,
|
|
218
|
+
}),
|
|
207
219
|
);
|
|
208
220
|
|
|
209
221
|
useEffect(() => {
|
|
@@ -262,13 +274,7 @@ export const RangePanel = ({
|
|
|
262
274
|
}, []);
|
|
263
275
|
|
|
264
276
|
return (
|
|
265
|
-
<div
|
|
266
|
-
className={classNames(
|
|
267
|
-
"block overflow-hidden bg-base rounded-capped",
|
|
268
|
-
className,
|
|
269
|
-
)}
|
|
270
|
-
{...rest}
|
|
271
|
-
>
|
|
277
|
+
<div className={cn("block overflow-hidden bg-default rounded-capped", className)} {...rest}>
|
|
272
278
|
<div className="flex flex-nowrap">
|
|
273
279
|
<BasePanel
|
|
274
280
|
{...state.start}
|
|
@@ -281,12 +287,8 @@ export const RangePanel = ({
|
|
|
281
287
|
selected={state.selected}
|
|
282
288
|
selectDate={selectDate}
|
|
283
289
|
isDateDisabled={isStartDateDisabled}
|
|
284
|
-
changeTime={(time) =>
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
changePage={(page) =>
|
|
288
|
-
dispatch({ type: "changePage", range: "start", page })
|
|
289
|
-
}
|
|
290
|
+
changeTime={(time) => dispatch({ type: "changeTime", range: "start", ...time })}
|
|
291
|
+
changePage={(page) => dispatch({ type: "changePage", range: "start", page })}
|
|
290
292
|
mouseOver={(date) => dispatch({ type: "changeHilight", date })}
|
|
291
293
|
changePageDate={changeStartPageDate}
|
|
292
294
|
/>
|
|
@@ -302,12 +304,8 @@ export const RangePanel = ({
|
|
|
302
304
|
selected={state.selected}
|
|
303
305
|
selectDate={selectDate}
|
|
304
306
|
isDateDisabled={isEndDateDisabled}
|
|
305
|
-
changeTime={(time) =>
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
changePage={(page) =>
|
|
309
|
-
dispatch({ type: "changePage", range: "end", page })
|
|
310
|
-
}
|
|
307
|
+
changeTime={(time) => dispatch({ type: "changeTime", range: "end", ...time })}
|
|
308
|
+
changePage={(page) => dispatch({ type: "changePage", range: "end", page })}
|
|
311
309
|
mouseOver={(date) => dispatch({ type: "changeHilight", date })}
|
|
312
310
|
changePageDate={changeEndPageDate}
|
|
313
311
|
/>
|
|
@@ -321,7 +319,7 @@ export const RangePanel = ({
|
|
|
321
319
|
color="primary"
|
|
322
320
|
className="my-1"
|
|
323
321
|
onClick={() => {
|
|
324
|
-
|
|
322
|
+
setIsHijri(!isHijri);
|
|
325
323
|
}}
|
|
326
324
|
>
|
|
327
325
|
{t(isHijri ? "label.gregorian" : "label.hijri")}
|
|
@@ -383,13 +381,9 @@ export const RangePanel = ({
|
|
|
383
381
|
variant="solid"
|
|
384
382
|
color="primary"
|
|
385
383
|
disabled={!state.selected}
|
|
384
|
+
data-dropdown-dismiss="true"
|
|
386
385
|
className="my-1"
|
|
387
|
-
onClick={() =>
|
|
388
|
-
fireChange?.([
|
|
389
|
-
state.selected?.[0].toISOString(),
|
|
390
|
-
state.selected?.[1].toISOString(),
|
|
391
|
-
])
|
|
392
|
-
}
|
|
386
|
+
onClick={() => fireChange?.([state.selected?.[0].toISOString(), state.selected?.[1].toISOString()])}
|
|
393
387
|
>
|
|
394
388
|
{t("core:action.apply")}
|
|
395
389
|
</Button>
|
|
@@ -397,4 +391,4 @@ export const RangePanel = ({
|
|
|
397
391
|
</div>
|
|
398
392
|
</div>
|
|
399
393
|
);
|
|
400
|
-
}
|
|
394
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import {
|
|
25
|
-
type ColorType,
|
|
26
25
|
type CssProp,
|
|
27
26
|
type TestProps,
|
|
28
27
|
} from "@react-fabric/core/dist/types/types";
|
|
@@ -63,6 +62,14 @@ format?:
|
|
|
63
62
|
| string;
|
|
64
63
|
*/
|
|
65
64
|
|
|
65
|
+
export interface EventType<D = DateLike> {
|
|
66
|
+
label: string;
|
|
67
|
+
icon?: string;
|
|
68
|
+
start: D;
|
|
69
|
+
end: D;
|
|
70
|
+
hijri?: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
66
73
|
export interface BaseProps {
|
|
67
74
|
min?: DateLike;
|
|
68
75
|
max?: DateLike;
|
|
@@ -82,45 +89,3 @@ export interface RangeProps extends BaseProps, CssProp, TestProps {
|
|
|
82
89
|
onChange?: (dates: [start: string, end: string]) => void;
|
|
83
90
|
presets?: Record<string, [DateLike, DateLike]>;
|
|
84
91
|
}
|
|
85
|
-
|
|
86
|
-
export interface EventType<D = DateLike> {
|
|
87
|
-
label: string;
|
|
88
|
-
icon?: string;
|
|
89
|
-
start: D;
|
|
90
|
-
end: D;
|
|
91
|
-
hijri?: boolean;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface PresetType {
|
|
95
|
-
value: string;
|
|
96
|
-
label: string;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface SuperDateProps extends TestProps {
|
|
100
|
-
as?: "button" | "chip";
|
|
101
|
-
color?: ColorType;
|
|
102
|
-
variant?: "solid" | "outline" | "link";
|
|
103
|
-
disabled?: boolean;
|
|
104
|
-
/**
|
|
105
|
-
* relative date value
|
|
106
|
-
*/
|
|
107
|
-
value?: string;
|
|
108
|
-
/**
|
|
109
|
-
* change handler
|
|
110
|
-
*/
|
|
111
|
-
onChange?: (value: string, dates: [string, string]) => void;
|
|
112
|
-
/**
|
|
113
|
-
* list of calendar events
|
|
114
|
-
*/
|
|
115
|
-
events?: EventType[];
|
|
116
|
-
/**
|
|
117
|
-
* list of calendar events
|
|
118
|
-
*/
|
|
119
|
-
recurringEvents?: EventType[];
|
|
120
|
-
/**
|
|
121
|
-
* list of relative presets
|
|
122
|
-
*
|
|
123
|
-
* {value:'$year-5|$year', label:'last 5 years'}
|
|
124
|
-
*/
|
|
125
|
-
presets?: PresetType[];
|
|
126
|
-
}
|