@react-fabric/date 1.0.0-beta.9 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +2 -9
  2. package/{src/components/Date.module.css → css/_date.css} +5 -5
  3. package/{src/superdate/SuperDate.module.css → css/styles.css} +2 -8
  4. package/dist/cjs/components/DateCell.js +5 -7
  5. package/dist/cjs/components/DateHeader.js +27 -20
  6. package/dist/cjs/components/DatePage.js +10 -12
  7. package/dist/cjs/components/DecadePage.js +3 -3
  8. package/dist/cjs/components/MonthPage.js +3 -3
  9. package/dist/cjs/components/TimeSliders.js +36 -35
  10. package/dist/cjs/components/YearPage.js +3 -3
  11. package/dist/cjs/display/DateDisplay.js +25 -8
  12. package/dist/cjs/display/DateDuration.js +155 -0
  13. package/dist/cjs/hijri/DateConverter.js +154 -0
  14. package/dist/cjs/hijri/FormatDate.js +116 -0
  15. package/dist/cjs/hijri/HijriDate.js +531 -0
  16. package/dist/cjs/hijri/StringDateParser.js +74 -0
  17. package/dist/cjs/hijri/index.js +15 -1025
  18. package/dist/cjs/hijri/initializer.js +113 -0
  19. package/dist/cjs/hijri/locales.js +19 -0
  20. package/dist/cjs/hijri/safe.js +17 -0
  21. package/dist/cjs/i18n/ar/date.json.js +213 -0
  22. package/dist/cjs/i18n/en/date.json.js +83 -0
  23. package/dist/cjs/i18n/index.js +4 -10
  24. package/dist/cjs/index.js +41 -2
  25. package/dist/cjs/panels/BasePanel.js +6 -5
  26. package/dist/cjs/panels/DatePanel.js +45 -21
  27. package/dist/cjs/{superdate → panels}/EventPanel.js +28 -12
  28. package/dist/cjs/panels/RangePanel.js +49 -19
  29. package/dist/cjs/utils/dateUtil.js +3 -5
  30. package/dist/esm/components/DateCell.js +6 -8
  31. package/dist/esm/components/DateHeader.js +28 -21
  32. package/dist/esm/components/DatePage.js +11 -13
  33. package/dist/esm/components/DecadePage.js +3 -3
  34. package/dist/esm/components/MonthPage.js +3 -3
  35. package/dist/esm/components/TimeSliders.js +38 -37
  36. package/dist/esm/components/YearPage.js +3 -3
  37. package/dist/esm/display/DateDisplay.js +26 -9
  38. package/dist/esm/display/DateDuration.js +155 -0
  39. package/dist/esm/hijri/DateConverter.js +152 -0
  40. package/dist/esm/hijri/FormatDate.js +113 -0
  41. package/dist/esm/hijri/HijriDate.js +528 -0
  42. package/dist/esm/hijri/StringDateParser.js +74 -0
  43. package/dist/esm/hijri/index.js +15 -1025
  44. package/dist/esm/hijri/initializer.js +113 -0
  45. package/dist/esm/hijri/locales.js +18 -0
  46. package/dist/esm/hijri/safe.js +14 -0
  47. package/dist/esm/i18n/ar/date.json.js +209 -0
  48. package/dist/esm/i18n/en/date.json.js +79 -0
  49. package/dist/esm/i18n/index.js +2 -8
  50. package/dist/esm/index.js +36 -1
  51. package/dist/esm/panels/BasePanel.js +6 -5
  52. package/dist/esm/panels/DatePanel.js +48 -24
  53. package/dist/esm/{superdate → panels}/EventPanel.js +29 -13
  54. package/dist/esm/panels/RangePanel.js +52 -22
  55. package/dist/esm/utils/dateUtil.js +4 -6
  56. package/dist/types/components/DateCell.d.ts +1 -1
  57. package/dist/types/components/DateHeader.d.ts +1 -1
  58. package/dist/types/components/DatePage.d.ts +1 -1
  59. package/dist/types/components/DecadePage.d.ts +1 -1
  60. package/dist/types/components/MonthPage.d.ts +1 -1
  61. package/dist/types/components/TimeSliders.d.ts +1 -1
  62. package/dist/types/components/YearPage.d.ts +1 -1
  63. package/dist/types/display/DateDisplay.d.ts +17 -4
  64. package/dist/types/display/DateDuration.d.ts +25 -0
  65. package/dist/types/hijri/DateConverter.d.ts +13 -0
  66. package/dist/types/hijri/FormatDate.d.ts +19 -0
  67. package/dist/types/hijri/HijriDate.d.ts +214 -0
  68. package/dist/types/hijri/StringDateParser.d.ts +11 -0
  69. package/dist/types/hijri/index.d.ts +2 -2
  70. package/dist/types/hijri/initializer.d.ts +129 -0
  71. package/dist/types/hijri/locales.d.ts +19 -0
  72. package/dist/types/hijri/safe.d.ts +3 -0
  73. package/dist/types/index.d.ts +32 -1
  74. package/dist/types/panels/BasePanel.d.ts +1 -1
  75. package/dist/types/panels/DatePanel.d.ts +22 -1
  76. package/dist/types/panels/EventPanel.d.ts +17 -0
  77. package/dist/types/panels/RangePanel.d.ts +19 -1
  78. package/dist/types/types/index.d.ts +8 -40
  79. package/package.json +14 -21
  80. package/src/components/DateCell.tsx +20 -75
  81. package/src/components/DateHeader.tsx +27 -78
  82. package/src/components/DatePage.tsx +11 -36
  83. package/src/components/DecadePage.tsx +2 -2
  84. package/src/components/MonthPage.tsx +2 -2
  85. package/src/components/TimeSliders.tsx +20 -60
  86. package/src/components/YearPage.tsx +2 -2
  87. package/src/display/DateDisplay.tsx +26 -38
  88. package/src/display/DateDuration.tsx +170 -0
  89. package/src/hijri/DateConverter.js +175 -0
  90. package/src/hijri/FormatDate.js +134 -0
  91. package/src/hijri/HijriDate.js +477 -0
  92. package/src/hijri/StringDateParser.js +68 -0
  93. package/src/hijri/index.js +16 -1535
  94. package/src/hijri/initializer.js +96 -0
  95. package/src/hijri/locales.js +109 -0
  96. package/src/hijri/safe.js +16 -0
  97. package/src/i18n/ar/date.json +203 -0
  98. package/src/i18n/en/date.json +73 -0
  99. package/src/i18n/index.ts +2 -5
  100. package/src/index.ts +29 -18
  101. package/src/panels/BasePanel.tsx +7 -10
  102. package/src/panels/DatePanel.tsx +41 -33
  103. package/src/{superdate → panels}/EventPanel.tsx +52 -53
  104. package/src/panels/RangePanel.tsx +54 -60
  105. package/src/types/index.ts +8 -43
  106. package/src/utils/dateUtil.ts +26 -82
  107. package/dist/cjs/components/Date.module.css +0 -1
  108. package/dist/cjs/i18n/ar.json.js +0 -25
  109. package/dist/cjs/i18n/en.json.js +0 -21
  110. package/dist/cjs/i18n/superdate/ar.json.js +0 -86
  111. package/dist/cjs/i18n/superdate/en.json.js +0 -93
  112. package/dist/cjs/superdate/QuickSelect.js +0 -134
  113. package/dist/cjs/superdate/RelativeInput.js +0 -79
  114. package/dist/cjs/superdate/RelativePanel.js +0 -91
  115. package/dist/cjs/superdate/SuperDate.module.css +0 -1
  116. package/dist/cjs/superdate/Superdate.js +0 -177
  117. package/dist/esm/components/Date.module.css +0 -1
  118. package/dist/esm/i18n/ar.json.js +0 -21
  119. package/dist/esm/i18n/en.json.js +0 -18
  120. package/dist/esm/i18n/superdate/ar.json.js +0 -79
  121. package/dist/esm/i18n/superdate/en.json.js +0 -85
  122. package/dist/esm/superdate/QuickSelect.js +0 -134
  123. package/dist/esm/superdate/RelativeInput.js +0 -79
  124. package/dist/esm/superdate/RelativePanel.js +0 -91
  125. package/dist/esm/superdate/SuperDate.module.css +0 -1
  126. package/dist/esm/superdate/Superdate.js +0 -176
  127. package/dist/types/superdate/EventPanel.d.ts +0 -4
  128. package/dist/types/superdate/QuickSelect.d.ts +0 -1
  129. package/dist/types/superdate/RelativeInput.d.ts +0 -1
  130. package/dist/types/superdate/RelativePanel.d.ts +0 -1
  131. package/dist/types/superdate/Superdate.d.ts +0 -17
  132. package/src/i18n/ar.json +0 -14
  133. package/src/i18n/en.json +0 -13
  134. package/src/i18n/superdate/ar.json +0 -70
  135. package/src/i18n/superdate/en.json +0 -75
  136. package/src/superdate/QuickSelect.tsx +0 -121
  137. package/src/superdate/RelativeInput.tsx +0 -64
  138. package/src/superdate/RelativePanel.tsx +0 -103
  139. package/src/superdate/Superdate.tsx +0 -198
@@ -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, useGlobals } from "@react-fabric/core";
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
- export const DatePanel = ({
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 } = useGlobals();
62
- const [isHijri, setHijri] = useState(currentCalendar === "hijri");
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 && (state.page = Math.max(state.page - 1, 0));
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 && fireChange?.(state.selected.toISOString());
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
- setHijri(!isHijri);
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, Section } from "@react-fabric/core";
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, type SuperDateProps } from "../types";
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
- const makeList = (
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
- export const EventPanel = ({
94
- isHijri,
95
- onChange,
96
- events = EMPTY_ARRAY,
97
- recurringEvents = EMPTY_ARRAY,
98
- }: { isHijri: boolean } & Partial<SuperDateProps>) => {
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 !== false) {
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
- } as EventState,
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
- if (!predefined[start.getFullYear()])
149
- predefined[start.getFullYear()] = [];
150
- predefined[start.getFullYear()].push(evt);
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
- <Section>
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
- `${evt.start.toISOString()}|${evt.end.toISOString()}`,
178
- [evt.start.toISOString(), evt.end.toISOString()],
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
- </Section>
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, useGlobals } from "@react-fabric/core";
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, useState } from "react";
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
- export const RangePanel = ({
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 } = useGlobals();
76
- const [isHijri, setHijri] = useState(currentCalendar === "hijri");
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
- } as RangeState,
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
- dispatch({ type: "changeTime", range: "start", ...time })
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
- dispatch({ type: "changeTime", range: "end", ...time })
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
- setHijri(!isHijri);
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
+ }
@@ -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
- }