@dreamstack-us/kaal 0.0.4 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/components/CalendarGrid/CalendarGrid.js +1 -1
- package/lib/module/components/CalendarGrid/CalendarGrid.js.map +1 -1
- package/lib/module/components/CalendarGrid/CalendarGrid.web.js +1 -1
- package/lib/module/components/CalendarGrid/CalendarGrid.web.js.map +1 -1
- package/lib/module/components/CalendarGrid/DayCell.js +81 -36
- package/lib/module/components/CalendarGrid/DayCell.js.map +1 -1
- package/lib/module/components/CalendarGrid/DayCell.web.js +80 -36
- package/lib/module/components/CalendarGrid/DayCell.web.js.map +1 -1
- package/lib/module/components/TimePicker/MaterialTimePicker.web.js +67 -15
- package/lib/module/components/TimePicker/MaterialTimePicker.web.js.map +1 -1
- package/lib/module/components/WheelPicker/WheelPicker.web.js +14 -14
- package/lib/module/components/WheelPicker/WheelPicker.web.js.map +1 -1
- package/lib/module/hooks/useCalendar.js +1 -1
- package/lib/module/hooks/useCalendar.js.map +1 -1
- package/lib/module/utils/date.js +25 -21
- package/lib/module/utils/date.js.map +1 -1
- package/lib/module/utils/validation.js +3 -3
- package/lib/module/utils/validation.js.map +1 -1
- package/lib/typescript/components/CalendarGrid/DayCell.d.ts.map +1 -1
- package/lib/typescript/components/CalendarGrid/DayCell.web.d.ts.map +1 -1
- package/lib/typescript/components/TimePicker/MaterialTimePicker.web.d.ts.map +1 -1
- package/lib/typescript/components/WheelPicker/WheelPicker.web.d.ts.map +1 -1
- package/lib/typescript/utils/date.d.ts +7 -3
- package/lib/typescript/utils/date.d.ts.map +1 -1
- package/package.json +8 -9
- package/src/components/CalendarGrid/CalendarGrid.tsx +2 -2
- package/src/components/CalendarGrid/CalendarGrid.web.tsx +2 -2
- package/src/components/CalendarGrid/DayCell.tsx +98 -47
- package/src/components/CalendarGrid/DayCell.web.tsx +105 -47
- package/src/components/TimePicker/MaterialTimePicker.web.tsx +150 -13
- package/src/components/WheelPicker/WheelPicker.web.tsx +15 -25
- package/src/hooks/useCalendar.ts +2 -2
- package/src/utils/date.ts +34 -27
- package/src/utils/validation.ts +5 -5
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
import type React from 'react';
|
|
3
|
-
import { memo, useCallback, useState } from 'react';
|
|
3
|
+
import { memo, useCallback, useMemo, useState } from 'react';
|
|
4
4
|
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
|
+
import { useTimePickerOverrides } from '../../context/ThemeOverrideContext';
|
|
5
6
|
import { to12Hour, to24Hour } from '../../hooks/useTimePicker';
|
|
6
7
|
import type { ClockMode, TimePeriod, TimeValue } from '../../types/timepicker';
|
|
7
8
|
import { ClockFace } from './ClockFace';
|
|
8
9
|
|
|
10
|
+
// Default colors (dark theme)
|
|
11
|
+
const DEFAULT_COLORS = {
|
|
12
|
+
containerBackground: '#2C2C2E',
|
|
13
|
+
headerColor: '#8E8E93',
|
|
14
|
+
timeFieldBackground: '#3A3A3C',
|
|
15
|
+
timeFieldActiveBackground: '#007AFF',
|
|
16
|
+
textColor: '#FFFFFF',
|
|
17
|
+
separatorColor: '#FFFFFF',
|
|
18
|
+
borderColor: '#48484A',
|
|
19
|
+
periodActiveBackground: 'rgba(0, 122, 255, 0.2)',
|
|
20
|
+
periodTextColor: '#8E8E93',
|
|
21
|
+
periodTextActiveColor: '#007AFF',
|
|
22
|
+
actionButtonColor: '#007AFF',
|
|
23
|
+
};
|
|
24
|
+
|
|
9
25
|
interface MaterialTimePickerProps {
|
|
10
26
|
value: TimeValue;
|
|
11
27
|
onChange: (time: TimeValue) => void;
|
|
@@ -127,6 +143,7 @@ const webStyles = StyleSheet.create({
|
|
|
127
143
|
|
|
128
144
|
export const MaterialTimePicker: React.FC<MaterialTimePickerProps> = memo(
|
|
129
145
|
({ value, onChange, is24Hour = false, onCancel, onConfirm }) => {
|
|
146
|
+
const overrides = useTimePickerOverrides();
|
|
130
147
|
const [mode, setMode] = useState<ClockMode>('hours');
|
|
131
148
|
const { hour: hour12, period } = to12Hour(value.hours);
|
|
132
149
|
|
|
@@ -155,9 +172,95 @@ export const MaterialTimePicker: React.FC<MaterialTimePickerProps> = memo(
|
|
|
155
172
|
: hour12.toString().padStart(2, '0');
|
|
156
173
|
const displayMinute = value.minutes.toString().padStart(2, '0');
|
|
157
174
|
|
|
175
|
+
// Build override styles from themeOverrides
|
|
176
|
+
const containerStyle = useMemo(
|
|
177
|
+
() => ({
|
|
178
|
+
backgroundColor:
|
|
179
|
+
overrides?.containerBackground ?? DEFAULT_COLORS.containerBackground,
|
|
180
|
+
}),
|
|
181
|
+
[overrides],
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const headerStyle = useMemo(
|
|
185
|
+
() => ({
|
|
186
|
+
color: overrides?.headerColor ?? DEFAULT_COLORS.headerColor,
|
|
187
|
+
}),
|
|
188
|
+
[overrides],
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const timeFieldStyle = useMemo(
|
|
192
|
+
() => ({
|
|
193
|
+
backgroundColor:
|
|
194
|
+
overrides?.timeFieldBackground ?? DEFAULT_COLORS.timeFieldBackground,
|
|
195
|
+
}),
|
|
196
|
+
[overrides],
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const timeFieldActiveStyle = useMemo(
|
|
200
|
+
() => ({
|
|
201
|
+
backgroundColor:
|
|
202
|
+
overrides?.timeFieldActiveBackground ??
|
|
203
|
+
DEFAULT_COLORS.timeFieldActiveBackground,
|
|
204
|
+
}),
|
|
205
|
+
[overrides],
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const textStyle = useMemo(
|
|
209
|
+
() => ({
|
|
210
|
+
color: overrides?.textColor ?? DEFAULT_COLORS.textColor,
|
|
211
|
+
}),
|
|
212
|
+
[overrides],
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const separatorStyle = useMemo(
|
|
216
|
+
() => ({
|
|
217
|
+
color: overrides?.separatorColor ?? DEFAULT_COLORS.separatorColor,
|
|
218
|
+
}),
|
|
219
|
+
[overrides],
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const periodContainerStyle = useMemo(
|
|
223
|
+
() => ({
|
|
224
|
+
borderColor: overrides?.periodBorderColor ?? DEFAULT_COLORS.borderColor,
|
|
225
|
+
}),
|
|
226
|
+
[overrides],
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
const periodActiveStyle = useMemo(
|
|
230
|
+
() => ({
|
|
231
|
+
backgroundColor:
|
|
232
|
+
overrides?.periodActiveBackground ??
|
|
233
|
+
DEFAULT_COLORS.periodActiveBackground,
|
|
234
|
+
}),
|
|
235
|
+
[overrides],
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
const periodTextStyle = useMemo(
|
|
239
|
+
() => ({
|
|
240
|
+
color: overrides?.periodTextColor ?? DEFAULT_COLORS.periodTextColor,
|
|
241
|
+
}),
|
|
242
|
+
[overrides],
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const periodTextActiveStyle = useMemo(
|
|
246
|
+
() => ({
|
|
247
|
+
color:
|
|
248
|
+
overrides?.periodTextActiveColor ??
|
|
249
|
+
DEFAULT_COLORS.periodTextActiveColor,
|
|
250
|
+
}),
|
|
251
|
+
[overrides],
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const actionButtonTextStyle = useMemo(
|
|
255
|
+
() => ({
|
|
256
|
+
color: overrides?.actionButtonColor ?? DEFAULT_COLORS.actionButtonColor,
|
|
257
|
+
}),
|
|
258
|
+
[overrides],
|
|
259
|
+
);
|
|
260
|
+
|
|
158
261
|
return (
|
|
159
|
-
<View style={webStyles.materialContainer}>
|
|
160
|
-
<Text style={webStyles.materialHeader}>Select time</Text>
|
|
262
|
+
<View style={[webStyles.materialContainer, containerStyle]}>
|
|
263
|
+
<Text style={[webStyles.materialHeader, headerStyle]}>Select time</Text>
|
|
161
264
|
|
|
162
265
|
<View style={webStyles.timeInputContainer}>
|
|
163
266
|
<View style={webStyles.timeFieldsContainer}>
|
|
@@ -165,12 +268,17 @@ export const MaterialTimePicker: React.FC<MaterialTimePickerProps> = memo(
|
|
|
165
268
|
onPress={handleHourPress}
|
|
166
269
|
style={[
|
|
167
270
|
webStyles.timeField,
|
|
168
|
-
|
|
271
|
+
timeFieldStyle,
|
|
272
|
+
mode === 'hours' && [
|
|
273
|
+
webStyles.timeFieldActive,
|
|
274
|
+
timeFieldActiveStyle,
|
|
275
|
+
],
|
|
169
276
|
]}
|
|
170
277
|
>
|
|
171
278
|
<Text
|
|
172
279
|
style={[
|
|
173
280
|
webStyles.timeFieldText,
|
|
281
|
+
textStyle,
|
|
174
282
|
mode === 'hours' && webStyles.timeFieldTextActive,
|
|
175
283
|
]}
|
|
176
284
|
>
|
|
@@ -178,18 +286,23 @@ export const MaterialTimePicker: React.FC<MaterialTimePickerProps> = memo(
|
|
|
178
286
|
</Text>
|
|
179
287
|
</Pressable>
|
|
180
288
|
|
|
181
|
-
<Text style={webStyles.timeSeparator}>:</Text>
|
|
289
|
+
<Text style={[webStyles.timeSeparator, separatorStyle]}>:</Text>
|
|
182
290
|
|
|
183
291
|
<Pressable
|
|
184
292
|
onPress={handleMinutePress}
|
|
185
293
|
style={[
|
|
186
294
|
webStyles.timeField,
|
|
187
|
-
|
|
295
|
+
timeFieldStyle,
|
|
296
|
+
mode === 'minutes' && [
|
|
297
|
+
webStyles.timeFieldActive,
|
|
298
|
+
timeFieldActiveStyle,
|
|
299
|
+
],
|
|
188
300
|
]}
|
|
189
301
|
>
|
|
190
302
|
<Text
|
|
191
303
|
style={[
|
|
192
304
|
webStyles.timeFieldText,
|
|
305
|
+
textStyle,
|
|
193
306
|
mode === 'minutes' && webStyles.timeFieldTextActive,
|
|
194
307
|
]}
|
|
195
308
|
>
|
|
@@ -199,19 +312,28 @@ export const MaterialTimePicker: React.FC<MaterialTimePickerProps> = memo(
|
|
|
199
312
|
</View>
|
|
200
313
|
|
|
201
314
|
{!is24Hour && (
|
|
202
|
-
<View
|
|
315
|
+
<View
|
|
316
|
+
style={[webStyles.periodToggleContainer, periodContainerStyle]}
|
|
317
|
+
>
|
|
203
318
|
<Pressable
|
|
204
319
|
onPress={() => handlePeriodChange('AM')}
|
|
205
320
|
style={[
|
|
206
321
|
webStyles.periodButton,
|
|
207
322
|
webStyles.periodButtonTop,
|
|
208
|
-
period === 'AM' &&
|
|
323
|
+
period === 'AM' && [
|
|
324
|
+
webStyles.periodButtonActive,
|
|
325
|
+
periodActiveStyle,
|
|
326
|
+
],
|
|
209
327
|
]}
|
|
210
328
|
>
|
|
211
329
|
<Text
|
|
212
330
|
style={[
|
|
213
331
|
webStyles.periodButtonText,
|
|
214
|
-
|
|
332
|
+
periodTextStyle,
|
|
333
|
+
period === 'AM' && [
|
|
334
|
+
webStyles.periodButtonTextActive,
|
|
335
|
+
periodTextActiveStyle,
|
|
336
|
+
],
|
|
215
337
|
]}
|
|
216
338
|
>
|
|
217
339
|
AM
|
|
@@ -221,13 +343,20 @@ export const MaterialTimePicker: React.FC<MaterialTimePickerProps> = memo(
|
|
|
221
343
|
onPress={() => handlePeriodChange('PM')}
|
|
222
344
|
style={[
|
|
223
345
|
webStyles.periodButton,
|
|
224
|
-
period === 'PM' &&
|
|
346
|
+
period === 'PM' && [
|
|
347
|
+
webStyles.periodButtonActive,
|
|
348
|
+
periodActiveStyle,
|
|
349
|
+
],
|
|
225
350
|
]}
|
|
226
351
|
>
|
|
227
352
|
<Text
|
|
228
353
|
style={[
|
|
229
354
|
webStyles.periodButtonText,
|
|
230
|
-
|
|
355
|
+
periodTextStyle,
|
|
356
|
+
period === 'PM' && [
|
|
357
|
+
webStyles.periodButtonTextActive,
|
|
358
|
+
periodTextActiveStyle,
|
|
359
|
+
],
|
|
231
360
|
]}
|
|
232
361
|
>
|
|
233
362
|
PM
|
|
@@ -253,12 +382,20 @@ export const MaterialTimePicker: React.FC<MaterialTimePickerProps> = memo(
|
|
|
253
382
|
<View style={webStyles.actionButtonsContainer}>
|
|
254
383
|
{onCancel && (
|
|
255
384
|
<Pressable style={webStyles.actionButton} onPress={onCancel}>
|
|
256
|
-
<Text
|
|
385
|
+
<Text
|
|
386
|
+
style={[webStyles.actionButtonText, actionButtonTextStyle]}
|
|
387
|
+
>
|
|
388
|
+
Cancel
|
|
389
|
+
</Text>
|
|
257
390
|
</Pressable>
|
|
258
391
|
)}
|
|
259
392
|
{onConfirm && (
|
|
260
393
|
<Pressable style={webStyles.actionButton} onPress={onConfirm}>
|
|
261
|
-
<Text
|
|
394
|
+
<Text
|
|
395
|
+
style={[webStyles.actionButtonText, actionButtonTextStyle]}
|
|
396
|
+
>
|
|
397
|
+
OK
|
|
398
|
+
</Text>
|
|
262
399
|
</Pressable>
|
|
263
400
|
)}
|
|
264
401
|
</View>
|
|
@@ -15,7 +15,7 @@ interface WheelPickerProps {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const getDaysInMonth = (year: number, month: number): number => {
|
|
18
|
-
return new Date(
|
|
18
|
+
return new Date(year, month + 1, 0).getDate();
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
const generateDateItems = (
|
|
@@ -26,8 +26,8 @@ const generateDateItems = (
|
|
|
26
26
|
) => {
|
|
27
27
|
if (type === 'day') {
|
|
28
28
|
const daysInMonth = getDaysInMonth(
|
|
29
|
-
currentDate.
|
|
30
|
-
currentDate.
|
|
29
|
+
currentDate.getFullYear(),
|
|
30
|
+
currentDate.getMonth(),
|
|
31
31
|
);
|
|
32
32
|
return Array.from({ length: daysInMonth }, (_, i) => ({
|
|
33
33
|
value: i + 1,
|
|
@@ -40,10 +40,8 @@ const generateDateItems = (
|
|
|
40
40
|
label: new Date(2000, i).toLocaleString('en-US', { month: 'short' }),
|
|
41
41
|
}));
|
|
42
42
|
}
|
|
43
|
-
const minYear =
|
|
44
|
-
|
|
45
|
-
const maxYear =
|
|
46
|
-
maxDate?.getUTCFullYear() ?? currentDate.getUTCFullYear() + 10;
|
|
43
|
+
const minYear = minDate?.getFullYear() ?? currentDate.getFullYear() - 100;
|
|
44
|
+
const maxYear = maxDate?.getFullYear() ?? currentDate.getFullYear() + 10;
|
|
47
45
|
return Array.from({ length: maxYear - minYear + 1 }, (_, i) => ({
|
|
48
46
|
value: minYear + i,
|
|
49
47
|
label: String(minYear + i),
|
|
@@ -234,9 +232,7 @@ export const WheelPicker: React.FC<WheelPickerProps> = ({
|
|
|
234
232
|
(index: number) => {
|
|
235
233
|
const newDay = days[index]?.value;
|
|
236
234
|
if (newDay !== undefined) {
|
|
237
|
-
const newDate = new Date(
|
|
238
|
-
Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), newDay),
|
|
239
|
-
);
|
|
235
|
+
const newDate = new Date(value.getFullYear(), value.getMonth(), newDay);
|
|
240
236
|
onChange(newDate);
|
|
241
237
|
}
|
|
242
238
|
},
|
|
@@ -247,11 +243,9 @@ export const WheelPicker: React.FC<WheelPickerProps> = ({
|
|
|
247
243
|
(index: number) => {
|
|
248
244
|
const newMonth = months[index]?.value;
|
|
249
245
|
if (newMonth !== undefined) {
|
|
250
|
-
const daysInNewMonth = getDaysInMonth(value.
|
|
251
|
-
const newDay = Math.min(value.
|
|
252
|
-
const newDate = new Date(
|
|
253
|
-
Date.UTC(value.getUTCFullYear(), newMonth, newDay),
|
|
254
|
-
);
|
|
246
|
+
const daysInNewMonth = getDaysInMonth(value.getFullYear(), newMonth);
|
|
247
|
+
const newDay = Math.min(value.getDate(), daysInNewMonth);
|
|
248
|
+
const newDate = new Date(value.getFullYear(), newMonth, newDay);
|
|
255
249
|
onChange(newDate);
|
|
256
250
|
}
|
|
257
251
|
},
|
|
@@ -262,11 +256,9 @@ export const WheelPicker: React.FC<WheelPickerProps> = ({
|
|
|
262
256
|
(index: number) => {
|
|
263
257
|
const newYear = years[index]?.value;
|
|
264
258
|
if (newYear !== undefined) {
|
|
265
|
-
const daysInNewMonth = getDaysInMonth(newYear, value.
|
|
266
|
-
const newDay = Math.min(value.
|
|
267
|
-
const newDate = new Date(
|
|
268
|
-
Date.UTC(newYear, value.getUTCMonth(), newDay),
|
|
269
|
-
);
|
|
259
|
+
const daysInNewMonth = getDaysInMonth(newYear, value.getMonth());
|
|
260
|
+
const newDay = Math.min(value.getDate(), daysInNewMonth);
|
|
261
|
+
const newDate = new Date(newYear, value.getMonth(), newDay);
|
|
270
262
|
onChange(newDate);
|
|
271
263
|
}
|
|
272
264
|
},
|
|
@@ -277,21 +269,19 @@ export const WheelPicker: React.FC<WheelPickerProps> = ({
|
|
|
277
269
|
<View style={webStyles.container}>
|
|
278
270
|
<WheelColumn
|
|
279
271
|
items={months}
|
|
280
|
-
selectedIndex={value.
|
|
272
|
+
selectedIndex={value.getMonth()}
|
|
281
273
|
onSelect={handleMonthChange}
|
|
282
274
|
label="Month"
|
|
283
275
|
/>
|
|
284
276
|
<WheelColumn
|
|
285
277
|
items={days}
|
|
286
|
-
selectedIndex={value.
|
|
278
|
+
selectedIndex={value.getDate() - 1}
|
|
287
279
|
onSelect={handleDayChange}
|
|
288
280
|
label="Day"
|
|
289
281
|
/>
|
|
290
282
|
<WheelColumn
|
|
291
283
|
items={years}
|
|
292
|
-
selectedIndex={years.findIndex(
|
|
293
|
-
(y) => y.value === value.getUTCFullYear(),
|
|
294
|
-
)}
|
|
284
|
+
selectedIndex={years.findIndex((y) => y.value === value.getFullYear())}
|
|
295
285
|
onSelect={handleYearChange}
|
|
296
286
|
label="Year"
|
|
297
287
|
/>
|
package/src/hooks/useCalendar.ts
CHANGED
|
@@ -37,8 +37,8 @@ export const useCalendar = (initialDate?: Date) => {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const monthDays = getMonthDays(
|
|
40
|
-
currentMonth.
|
|
41
|
-
currentMonth.
|
|
40
|
+
currentMonth.getFullYear(),
|
|
41
|
+
currentMonth.getMonth(),
|
|
42
42
|
);
|
|
43
43
|
for (const day of monthDays) {
|
|
44
44
|
days.push(day);
|
package/src/utils/date.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Date utilities using native Date and Intl APIs
|
|
3
3
|
* Replaces @js-temporal/polyfill for lighter bundle size
|
|
4
|
+
*
|
|
5
|
+
* All date-only values use LOCAL time (not UTC). This ensures that
|
|
6
|
+
* dates displayed in the calendar match what consumers read with
|
|
7
|
+
* standard .getDate(), .getMonth(), .getFullYear() methods.
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
|
-
* Converts a Date to ISO date string (YYYY-MM-DD)
|
|
11
|
+
* Converts a Date to ISO date string (YYYY-MM-DD) using local time
|
|
8
12
|
*/
|
|
9
13
|
export const toISODateString = (date: Date): string => {
|
|
10
|
-
const year = date.
|
|
11
|
-
const month = String(date.
|
|
12
|
-
const day = String(date.
|
|
14
|
+
const year = date.getFullYear();
|
|
15
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
16
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
13
17
|
return `${year}-${month}-${day}`;
|
|
14
18
|
};
|
|
15
19
|
|
|
@@ -34,11 +38,11 @@ export const toISODateTimeString = (date: Date, timeZone?: string): string => {
|
|
|
34
38
|
};
|
|
35
39
|
|
|
36
40
|
/**
|
|
37
|
-
* Parses an ISO date string (YYYY-MM-DD) to Date
|
|
41
|
+
* Parses an ISO date string (YYYY-MM-DD) to a local Date
|
|
38
42
|
*/
|
|
39
43
|
export const parseISODate = (iso: string): Date => {
|
|
40
44
|
const parts = iso.split('-').map(Number) as [number, number, number];
|
|
41
|
-
return new Date(
|
|
45
|
+
return new Date(parts[0], parts[1] - 1, parts[2]);
|
|
42
46
|
};
|
|
43
47
|
|
|
44
48
|
/**
|
|
@@ -67,7 +71,7 @@ export const getDateRange = (start: Date, end: Date): Date[] => {
|
|
|
67
71
|
|
|
68
72
|
while (current.getTime() <= end.getTime()) {
|
|
69
73
|
dates.push(new Date(current.getTime()));
|
|
70
|
-
current.
|
|
74
|
+
current.setDate(current.getDate() + 1);
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
return dates;
|
|
@@ -99,7 +103,7 @@ export const getUserTimezone = (): string => {
|
|
|
99
103
|
*/
|
|
100
104
|
export const addDays = (date: Date, days: number): Date => {
|
|
101
105
|
const result = new Date(date.getTime());
|
|
102
|
-
result.
|
|
106
|
+
result.setDate(result.getDate() + days);
|
|
103
107
|
return result;
|
|
104
108
|
};
|
|
105
109
|
|
|
@@ -108,13 +112,15 @@ export const addDays = (date: Date, days: number): Date => {
|
|
|
108
112
|
*/
|
|
109
113
|
export const addMonths = (date: Date, months: number): Date => {
|
|
110
114
|
const result = new Date(date.getTime());
|
|
111
|
-
const dayOfMonth = result.
|
|
112
|
-
result.
|
|
113
|
-
result.
|
|
115
|
+
const dayOfMonth = result.getDate();
|
|
116
|
+
result.setDate(1);
|
|
117
|
+
result.setMonth(result.getMonth() + months);
|
|
114
118
|
const daysInMonth = new Date(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
result.getFullYear(),
|
|
120
|
+
result.getMonth() + 1,
|
|
121
|
+
0,
|
|
122
|
+
).getDate();
|
|
123
|
+
result.setDate(Math.min(dayOfMonth, daysInMonth));
|
|
118
124
|
return result;
|
|
119
125
|
};
|
|
120
126
|
|
|
@@ -137,10 +143,7 @@ export const isSameDay = (a: Date, b: Date): boolean => {
|
|
|
137
143
|
* Checks if two dates are the same month
|
|
138
144
|
*/
|
|
139
145
|
export const isSameMonth = (a: Date, b: Date): boolean => {
|
|
140
|
-
return (
|
|
141
|
-
a.getUTCFullYear() === b.getUTCFullYear() &&
|
|
142
|
-
a.getUTCMonth() === b.getUTCMonth()
|
|
143
|
-
);
|
|
146
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
144
147
|
};
|
|
145
148
|
|
|
146
149
|
/**
|
|
@@ -148,9 +151,9 @@ export const isSameMonth = (a: Date, b: Date): boolean => {
|
|
|
148
151
|
*/
|
|
149
152
|
export const getMonthDays = (year: number, month: number): Date[] => {
|
|
150
153
|
const days: Date[] = [];
|
|
151
|
-
const daysInMonth = new Date(
|
|
154
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
152
155
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
153
|
-
days.push(new Date(
|
|
156
|
+
days.push(new Date(year, month, day));
|
|
154
157
|
}
|
|
155
158
|
return days;
|
|
156
159
|
};
|
|
@@ -161,21 +164,21 @@ export const getMonthDays = (year: number, month: number): Date[] => {
|
|
|
161
164
|
*/
|
|
162
165
|
export const getFirstDayOfMonth = (date: Date | null | undefined): Date => {
|
|
163
166
|
const d = date ?? today();
|
|
164
|
-
return new Date(
|
|
167
|
+
return new Date(d.getFullYear(), d.getMonth(), 1);
|
|
165
168
|
};
|
|
166
169
|
|
|
167
170
|
/**
|
|
168
171
|
* Gets the last day of the month
|
|
169
172
|
*/
|
|
170
173
|
export const getLastDayOfMonth = (date: Date): Date => {
|
|
171
|
-
return new Date(
|
|
174
|
+
return new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
172
175
|
};
|
|
173
176
|
|
|
174
177
|
/**
|
|
175
178
|
* Gets the day of week (0 = Sunday, 6 = Saturday)
|
|
176
179
|
*/
|
|
177
180
|
export const getDayOfWeek = (date: Date): number => {
|
|
178
|
-
return date.
|
|
181
|
+
return date.getDay();
|
|
179
182
|
};
|
|
180
183
|
|
|
181
184
|
/**
|
|
@@ -186,7 +189,9 @@ export const formatMonth = (
|
|
|
186
189
|
locale = 'en-US',
|
|
187
190
|
style: 'long' | 'short' | 'narrow' = 'long',
|
|
188
191
|
): string => {
|
|
189
|
-
return new Intl.DateTimeFormat(locale, {
|
|
192
|
+
return new Intl.DateTimeFormat(locale, {
|
|
193
|
+
month: style,
|
|
194
|
+
}).format(date);
|
|
190
195
|
};
|
|
191
196
|
|
|
192
197
|
/**
|
|
@@ -197,7 +202,9 @@ export const formatWeekday = (
|
|
|
197
202
|
locale = 'en-US',
|
|
198
203
|
style: 'long' | 'short' | 'narrow' = 'short',
|
|
199
204
|
): string => {
|
|
200
|
-
return new Intl.DateTimeFormat(locale, {
|
|
205
|
+
return new Intl.DateTimeFormat(locale, {
|
|
206
|
+
weekday: style,
|
|
207
|
+
}).format(date);
|
|
201
208
|
};
|
|
202
209
|
|
|
203
210
|
/**
|
|
@@ -211,9 +218,9 @@ export const formatYearMonth = (date: Date, locale = 'en-US'): string => {
|
|
|
211
218
|
};
|
|
212
219
|
|
|
213
220
|
/**
|
|
214
|
-
* Gets today's date at midnight
|
|
221
|
+
* Gets today's date at local midnight
|
|
215
222
|
*/
|
|
216
223
|
export const today = (): Date => {
|
|
217
224
|
const now = new Date();
|
|
218
|
-
return new Date(
|
|
225
|
+
return new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
219
226
|
};
|
package/src/utils/validation.ts
CHANGED
|
@@ -19,12 +19,12 @@ export function isValidISODate(value: string): boolean {
|
|
|
19
19
|
number,
|
|
20
20
|
number,
|
|
21
21
|
];
|
|
22
|
-
const date = new Date(
|
|
22
|
+
const date = new Date(year, month - 1, day);
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
|
-
date.
|
|
26
|
-
date.
|
|
27
|
-
date.
|
|
25
|
+
date.getFullYear() === year &&
|
|
26
|
+
date.getMonth() === month - 1 &&
|
|
27
|
+
date.getDate() === day
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -70,7 +70,7 @@ export function parseISODateSafe(value: string): Date | null {
|
|
|
70
70
|
number,
|
|
71
71
|
number,
|
|
72
72
|
];
|
|
73
|
-
return new Date(
|
|
73
|
+
return new Date(year, month - 1, day);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// Types for date picker values
|