@dreamstack-us/kaal 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/lib/module/components/CalendarGrid/CalendarGrid.js +112 -0
- package/lib/module/components/CalendarGrid/CalendarGrid.js.map +1 -0
- package/lib/module/components/CalendarGrid/CalendarGrid.styles.js +46 -0
- package/lib/module/components/CalendarGrid/CalendarGrid.styles.js.map +1 -0
- package/lib/module/components/CalendarGrid/DayCell.js +96 -0
- package/lib/module/components/CalendarGrid/DayCell.js.map +1 -0
- package/lib/module/components/CalendarGrid/index.js +4 -0
- package/lib/module/components/CalendarGrid/index.js.map +1 -0
- package/lib/module/components/DatePicker/DatePicker.android.js +66 -0
- package/lib/module/components/DatePicker/DatePicker.android.js.map +1 -0
- package/lib/module/components/DatePicker/DatePicker.ios.js +74 -0
- package/lib/module/components/DatePicker/DatePicker.ios.js.map +1 -0
- package/lib/module/components/DatePicker/DatePicker.js +9 -0
- package/lib/module/components/DatePicker/DatePicker.js.map +1 -0
- package/lib/module/components/DatePicker/DatePicker.styles.js +35 -0
- package/lib/module/components/DatePicker/DatePicker.styles.js.map +1 -0
- package/lib/module/components/DatePicker/DatePicker.web.js +32 -0
- package/lib/module/components/DatePicker/DatePicker.web.js.map +1 -0
- package/lib/module/components/DatePicker/index.js +4 -0
- package/lib/module/components/DatePicker/index.js.map +1 -0
- package/lib/module/components/TimePicker/ClockFace.js +194 -0
- package/lib/module/components/TimePicker/ClockFace.js.map +1 -0
- package/lib/module/components/TimePicker/MaterialTimePicker.js +122 -0
- package/lib/module/components/TimePicker/MaterialTimePicker.js.map +1 -0
- package/lib/module/components/TimePicker/TimePicker.android.js +77 -0
- package/lib/module/components/TimePicker/TimePicker.android.js.map +1 -0
- package/lib/module/components/TimePicker/TimePicker.ios.js +83 -0
- package/lib/module/components/TimePicker/TimePicker.ios.js.map +1 -0
- package/lib/module/components/TimePicker/TimePicker.js +34 -0
- package/lib/module/components/TimePicker/TimePicker.js.map +1 -0
- package/lib/module/components/TimePicker/TimePicker.styles.js +180 -0
- package/lib/module/components/TimePicker/TimePicker.styles.js.map +1 -0
- package/lib/module/components/TimePicker/TimePicker.web.js +37 -0
- package/lib/module/components/TimePicker/TimePicker.web.js.map +1 -0
- package/lib/module/components/TimePicker/TimeWheelPicker.js +178 -0
- package/lib/module/components/TimePicker/TimeWheelPicker.js.map +1 -0
- package/lib/module/components/TimePicker/index.js +7 -0
- package/lib/module/components/TimePicker/index.js.map +1 -0
- package/lib/module/components/WheelPicker/WheelPicker.js +5 -0
- package/lib/module/components/WheelPicker/WheelPicker.js.map +1 -0
- package/lib/module/components/WheelPicker/WheelPicker.styles.js +41 -0
- package/lib/module/components/WheelPicker/WheelPicker.styles.js.map +1 -0
- package/lib/module/components/WheelPicker/WheelPicker.web.js +190 -0
- package/lib/module/components/WheelPicker/WheelPicker.web.js.map +1 -0
- package/lib/module/components/WheelPicker/index.js +4 -0
- package/lib/module/components/WheelPicker/index.js.map +1 -0
- package/lib/module/components/index.js +7 -0
- package/lib/module/components/index.js.map +1 -0
- package/lib/module/hooks/index.js +6 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/useCalendar.js +44 -0
- package/lib/module/hooks/useCalendar.js.map +1 -0
- package/lib/module/hooks/useDatePicker.js +30 -0
- package/lib/module/hooks/useDatePicker.js.map +1 -0
- package/lib/module/hooks/useTimePicker.js +125 -0
- package/lib/module/hooks/useTimePicker.js.map +1 -0
- package/lib/module/index.js +22 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/types/datepicker.js +2 -0
- package/lib/module/types/datepicker.js.map +1 -0
- package/lib/module/types/index.js +5 -0
- package/lib/module/types/index.js.map +1 -0
- package/lib/module/types/timepicker.js +2 -0
- package/lib/module/types/timepicker.js.map +1 -0
- package/lib/module/unistyles.js +9 -0
- package/lib/module/unistyles.js.map +1 -0
- package/lib/module/utils/date.js +205 -0
- package/lib/module/utils/date.js.map +1 -0
- package/lib/module/utils/index.js +5 -0
- package/lib/module/utils/index.js.map +1 -0
- package/lib/module/utils/validation.js +61 -0
- package/lib/module/utils/validation.js.map +1 -0
- package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts +12 -0
- package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts.map +1 -0
- package/lib/typescript/components/CalendarGrid/CalendarGrid.styles.d.ts +45 -0
- package/lib/typescript/components/CalendarGrid/CalendarGrid.styles.d.ts.map +1 -0
- package/lib/typescript/components/CalendarGrid/DayCell.d.ts +12 -0
- package/lib/typescript/components/CalendarGrid/DayCell.d.ts.map +1 -0
- package/lib/typescript/components/CalendarGrid/index.d.ts +2 -0
- package/lib/typescript/components/CalendarGrid/index.d.ts.map +1 -0
- package/lib/typescript/components/DatePicker/DatePicker.android.d.ts +4 -0
- package/lib/typescript/components/DatePicker/DatePicker.android.d.ts.map +1 -0
- package/lib/typescript/components/DatePicker/DatePicker.d.ts +15 -0
- package/lib/typescript/components/DatePicker/DatePicker.d.ts.map +1 -0
- package/lib/typescript/components/DatePicker/DatePicker.ios.d.ts +4 -0
- package/lib/typescript/components/DatePicker/DatePicker.ios.d.ts.map +1 -0
- package/lib/typescript/components/DatePicker/DatePicker.styles.d.ts +29 -0
- package/lib/typescript/components/DatePicker/DatePicker.styles.d.ts.map +1 -0
- package/lib/typescript/components/DatePicker/DatePicker.web.d.ts +4 -0
- package/lib/typescript/components/DatePicker/DatePicker.web.d.ts.map +1 -0
- package/lib/typescript/components/DatePicker/index.d.ts +3 -0
- package/lib/typescript/components/DatePicker/index.d.ts.map +1 -0
- package/lib/typescript/components/TimePicker/ClockFace.d.ts +12 -0
- package/lib/typescript/components/TimePicker/ClockFace.d.ts.map +1 -0
- package/lib/typescript/components/TimePicker/MaterialTimePicker.d.ts +12 -0
- package/lib/typescript/components/TimePicker/MaterialTimePicker.d.ts.map +1 -0
- package/lib/typescript/components/TimePicker/TimePicker.android.d.ts +4 -0
- package/lib/typescript/components/TimePicker/TimePicker.android.d.ts.map +1 -0
- package/lib/typescript/components/TimePicker/TimePicker.d.ts +29 -0
- package/lib/typescript/components/TimePicker/TimePicker.d.ts.map +1 -0
- package/lib/typescript/components/TimePicker/TimePicker.ios.d.ts +4 -0
- package/lib/typescript/components/TimePicker/TimePicker.ios.d.ts.map +1 -0
- package/lib/typescript/components/TimePicker/TimePicker.styles.d.ts +168 -0
- package/lib/typescript/components/TimePicker/TimePicker.styles.d.ts.map +1 -0
- package/lib/typescript/components/TimePicker/TimePicker.web.d.ts +10 -0
- package/lib/typescript/components/TimePicker/TimePicker.web.d.ts.map +1 -0
- package/lib/typescript/components/TimePicker/TimeWheelPicker.d.ts +11 -0
- package/lib/typescript/components/TimePicker/TimeWheelPicker.d.ts.map +1 -0
- package/lib/typescript/components/TimePicker/index.d.ts +6 -0
- package/lib/typescript/components/TimePicker/index.d.ts.map +1 -0
- package/lib/typescript/components/WheelPicker/WheelPicker.d.ts +2 -0
- package/lib/typescript/components/WheelPicker/WheelPicker.d.ts.map +1 -0
- package/lib/typescript/components/WheelPicker/WheelPicker.styles.d.ts +40 -0
- package/lib/typescript/components/WheelPicker/WheelPicker.styles.d.ts.map +1 -0
- package/lib/typescript/components/WheelPicker/WheelPicker.web.d.ts +10 -0
- package/lib/typescript/components/WheelPicker/WheelPicker.web.d.ts.map +1 -0
- package/lib/typescript/components/WheelPicker/index.d.ts +2 -0
- package/lib/typescript/components/WheelPicker/index.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +5 -0
- package/lib/typescript/components/index.d.ts.map +1 -0
- package/lib/typescript/hooks/index.d.ts +4 -0
- package/lib/typescript/hooks/index.d.ts.map +1 -0
- package/lib/typescript/hooks/useCalendar.d.ts +10 -0
- package/lib/typescript/hooks/useCalendar.d.ts.map +1 -0
- package/lib/typescript/hooks/useDatePicker.d.ts +15 -0
- package/lib/typescript/hooks/useDatePicker.d.ts.map +1 -0
- package/lib/typescript/hooks/useTimePicker.d.ts +52 -0
- package/lib/typescript/hooks/useTimePicker.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +12 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/types/datepicker.d.ts +15 -0
- package/lib/typescript/types/datepicker.d.ts.map +1 -0
- package/lib/typescript/types/index.d.ts +3 -0
- package/lib/typescript/types/index.d.ts.map +1 -0
- package/lib/typescript/types/timepicker.d.ts +54 -0
- package/lib/typescript/types/timepicker.d.ts.map +1 -0
- package/lib/typescript/unistyles.d.ts +3 -0
- package/lib/typescript/unistyles.d.ts.map +1 -0
- package/lib/typescript/utils/date.d.ts +94 -0
- package/lib/typescript/utils/date.d.ts.map +1 -0
- package/lib/typescript/utils/index.d.ts +3 -0
- package/lib/typescript/utils/index.d.ts.map +1 -0
- package/lib/typescript/utils/validation.d.ts +40 -0
- package/lib/typescript/utils/validation.d.ts.map +1 -0
- package/package.json +101 -0
- package/src/components/CalendarGrid/CalendarGrid.styles.ts +44 -0
- package/src/components/CalendarGrid/CalendarGrid.tsx +151 -0
- package/src/components/CalendarGrid/DayCell.tsx +108 -0
- package/src/components/CalendarGrid/index.ts +1 -0
- package/src/components/DatePicker/DatePicker.android.tsx +69 -0
- package/src/components/DatePicker/DatePicker.ios.tsx +78 -0
- package/src/components/DatePicker/DatePicker.styles.ts +35 -0
- package/src/components/DatePicker/DatePicker.tsx +21 -0
- package/src/components/DatePicker/DatePicker.web.tsx +36 -0
- package/src/components/DatePicker/index.ts +2 -0
- package/src/components/TimePicker/ClockFace.tsx +233 -0
- package/src/components/TimePicker/MaterialTimePicker.tsx +169 -0
- package/src/components/TimePicker/TimePicker.android.tsx +80 -0
- package/src/components/TimePicker/TimePicker.ios.tsx +88 -0
- package/src/components/TimePicker/TimePicker.styles.ts +209 -0
- package/src/components/TimePicker/TimePicker.tsx +35 -0
- package/src/components/TimePicker/TimePicker.web.tsx +35 -0
- package/src/components/TimePicker/TimeWheelPicker.tsx +211 -0
- package/src/components/TimePicker/index.ts +5 -0
- package/src/components/WheelPicker/WheelPicker.styles.ts +39 -0
- package/src/components/WheelPicker/WheelPicker.tsx +2 -0
- package/src/components/WheelPicker/WheelPicker.web.tsx +237 -0
- package/src/components/WheelPicker/index.ts +1 -0
- package/src/components/index.ts +11 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/useCalendar.ts +59 -0
- package/src/hooks/useDatePicker.ts +40 -0
- package/src/hooks/useTimePicker.ts +152 -0
- package/src/index.ts +77 -0
- package/src/types/datepicker.ts +17 -0
- package/src/types/index.ts +2 -0
- package/src/types/timepicker.ts +59 -0
- package/src/unistyles.ts +6 -0
- package/src/utils/date.ts +217 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/validation.ts +76 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
import { StyleSheet as RNStyleSheet, Text, View } from 'react-native';
|
|
4
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
5
|
+
import Animated, {
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
useSharedValue,
|
|
8
|
+
withSpring,
|
|
9
|
+
} from 'react-native-reanimated';
|
|
10
|
+
|
|
11
|
+
const ITEM_HEIGHT = 44;
|
|
12
|
+
const VISIBLE_ITEMS = 5;
|
|
13
|
+
const CONTAINER_HEIGHT = ITEM_HEIGHT * VISIBLE_ITEMS;
|
|
14
|
+
|
|
15
|
+
interface WheelPickerProps {
|
|
16
|
+
value: Date;
|
|
17
|
+
onChange: (date: Date) => void;
|
|
18
|
+
minDate?: Date;
|
|
19
|
+
maxDate?: Date;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const getDaysInMonth = (year: number, month: number): number => {
|
|
23
|
+
return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const generateDateItems = (
|
|
27
|
+
type: 'day' | 'month' | 'year',
|
|
28
|
+
currentDate: Date,
|
|
29
|
+
minDate?: Date,
|
|
30
|
+
maxDate?: Date,
|
|
31
|
+
) => {
|
|
32
|
+
if (type === 'day') {
|
|
33
|
+
const daysInMonth = getDaysInMonth(
|
|
34
|
+
currentDate.getUTCFullYear(),
|
|
35
|
+
currentDate.getUTCMonth(),
|
|
36
|
+
);
|
|
37
|
+
return Array.from({ length: daysInMonth }, (_, i) => ({
|
|
38
|
+
value: i + 1,
|
|
39
|
+
label: String(i + 1).padStart(2, '0'),
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
if (type === 'month') {
|
|
43
|
+
return Array.from({ length: 12 }, (_, i) => ({
|
|
44
|
+
value: i,
|
|
45
|
+
label: new Date(2000, i).toLocaleString('en-US', { month: 'short' }),
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
const minYear =
|
|
49
|
+
minDate?.getUTCFullYear() ?? currentDate.getUTCFullYear() - 100;
|
|
50
|
+
const maxYear =
|
|
51
|
+
maxDate?.getUTCFullYear() ?? currentDate.getUTCFullYear() + 10;
|
|
52
|
+
return Array.from({ length: maxYear - minYear + 1 }, (_, i) => ({
|
|
53
|
+
value: minYear + i,
|
|
54
|
+
label: String(minYear + i),
|
|
55
|
+
}));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const WheelColumn: React.FC<{
|
|
59
|
+
items: { value: number; label: string }[];
|
|
60
|
+
selectedIndex: number;
|
|
61
|
+
onSelect: (index: number) => void;
|
|
62
|
+
}> = ({ items, selectedIndex, onSelect }) => {
|
|
63
|
+
const translateY = useSharedValue(-selectedIndex * ITEM_HEIGHT);
|
|
64
|
+
const velocity = useSharedValue(0);
|
|
65
|
+
|
|
66
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: Reanimated shared values are stable refs
|
|
67
|
+
const panGesture = useMemo(
|
|
68
|
+
() =>
|
|
69
|
+
Gesture.Pan()
|
|
70
|
+
.onUpdate((e) => {
|
|
71
|
+
'worklet';
|
|
72
|
+
translateY.value = e.translationY + -selectedIndex * ITEM_HEIGHT;
|
|
73
|
+
velocity.value = e.velocityY;
|
|
74
|
+
})
|
|
75
|
+
.onEnd(() => {
|
|
76
|
+
'worklet';
|
|
77
|
+
const targetIndex = Math.round(-translateY.value / ITEM_HEIGHT);
|
|
78
|
+
const clampedIndex = Math.max(
|
|
79
|
+
0,
|
|
80
|
+
Math.min(items.length - 1, targetIndex),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
translateY.value = withSpring(-clampedIndex * ITEM_HEIGHT, {
|
|
84
|
+
velocity: velocity.value,
|
|
85
|
+
damping: 20,
|
|
86
|
+
stiffness: 200,
|
|
87
|
+
});
|
|
88
|
+
}),
|
|
89
|
+
[selectedIndex, items.length],
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
93
|
+
transform: [{ translateY: translateY.value }],
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<View style={webStyles.column}>
|
|
98
|
+
<View style={webStyles.selectionHighlight} />
|
|
99
|
+
|
|
100
|
+
<GestureDetector gesture={panGesture}>
|
|
101
|
+
<Animated.View style={[webStyles.itemsContainer, animatedStyle]}>
|
|
102
|
+
<View style={{ height: ITEM_HEIGHT * 2 }} />
|
|
103
|
+
|
|
104
|
+
{items.map((item) => (
|
|
105
|
+
<View key={item.value} style={webStyles.item}>
|
|
106
|
+
<Text style={webStyles.itemText}>{item.label}</Text>
|
|
107
|
+
</View>
|
|
108
|
+
))}
|
|
109
|
+
|
|
110
|
+
<View style={{ height: ITEM_HEIGHT * 2 }} />
|
|
111
|
+
</Animated.View>
|
|
112
|
+
</GestureDetector>
|
|
113
|
+
</View>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const WheelPicker: React.FC<WheelPickerProps> = ({
|
|
118
|
+
value,
|
|
119
|
+
onChange,
|
|
120
|
+
minDate,
|
|
121
|
+
maxDate,
|
|
122
|
+
}) => {
|
|
123
|
+
const days = useMemo(
|
|
124
|
+
() => generateDateItems('day', value, minDate, maxDate),
|
|
125
|
+
[value, minDate, maxDate],
|
|
126
|
+
);
|
|
127
|
+
const months = useMemo(() => generateDateItems('month', value), [value]);
|
|
128
|
+
const years = useMemo(
|
|
129
|
+
() => generateDateItems('year', value, minDate, maxDate),
|
|
130
|
+
[value, minDate, maxDate],
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const handleDayChange = useCallback(
|
|
134
|
+
(index: number) => {
|
|
135
|
+
const newDay = days[index]?.value;
|
|
136
|
+
if (newDay !== undefined) {
|
|
137
|
+
const newDate = new Date(
|
|
138
|
+
Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), newDay),
|
|
139
|
+
);
|
|
140
|
+
onChange(newDate);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
[value, days, onChange],
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const handleMonthChange = useCallback(
|
|
147
|
+
(index: number) => {
|
|
148
|
+
const newMonth = months[index]?.value;
|
|
149
|
+
if (newMonth !== undefined) {
|
|
150
|
+
const daysInNewMonth = getDaysInMonth(value.getUTCFullYear(), newMonth);
|
|
151
|
+
const newDay = Math.min(value.getUTCDate(), daysInNewMonth);
|
|
152
|
+
const newDate = new Date(
|
|
153
|
+
Date.UTC(value.getUTCFullYear(), newMonth, newDay),
|
|
154
|
+
);
|
|
155
|
+
onChange(newDate);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
[value, months, onChange],
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const handleYearChange = useCallback(
|
|
162
|
+
(index: number) => {
|
|
163
|
+
const newYear = years[index]?.value;
|
|
164
|
+
if (newYear !== undefined) {
|
|
165
|
+
const daysInNewMonth = getDaysInMonth(newYear, value.getUTCMonth());
|
|
166
|
+
const newDay = Math.min(value.getUTCDate(), daysInNewMonth);
|
|
167
|
+
const newDate = new Date(
|
|
168
|
+
Date.UTC(newYear, value.getUTCMonth(), newDay),
|
|
169
|
+
);
|
|
170
|
+
onChange(newDate);
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
[value, years, onChange],
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<View style={webStyles.container}>
|
|
178
|
+
<WheelColumn
|
|
179
|
+
items={months}
|
|
180
|
+
selectedIndex={value.getUTCMonth()}
|
|
181
|
+
onSelect={handleMonthChange}
|
|
182
|
+
/>
|
|
183
|
+
<WheelColumn
|
|
184
|
+
items={days}
|
|
185
|
+
selectedIndex={value.getUTCDate() - 1}
|
|
186
|
+
onSelect={handleDayChange}
|
|
187
|
+
/>
|
|
188
|
+
<WheelColumn
|
|
189
|
+
items={years}
|
|
190
|
+
selectedIndex={years.findIndex(
|
|
191
|
+
(y) => y.value === value.getUTCFullYear(),
|
|
192
|
+
)}
|
|
193
|
+
onSelect={handleYearChange}
|
|
194
|
+
/>
|
|
195
|
+
</View>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const webStyles = RNStyleSheet.create({
|
|
200
|
+
container: {
|
|
201
|
+
flexDirection: 'row',
|
|
202
|
+
height: CONTAINER_HEIGHT,
|
|
203
|
+
backgroundColor: 'rgba(255, 255, 255, 0.8)',
|
|
204
|
+
// @ts-ignore - web-only property
|
|
205
|
+
backdropFilter: 'blur(20px)',
|
|
206
|
+
borderRadius: 14,
|
|
207
|
+
overflow: 'hidden',
|
|
208
|
+
},
|
|
209
|
+
column: {
|
|
210
|
+
flex: 1,
|
|
211
|
+
height: CONTAINER_HEIGHT,
|
|
212
|
+
overflow: 'hidden',
|
|
213
|
+
},
|
|
214
|
+
selectionHighlight: {
|
|
215
|
+
position: 'absolute',
|
|
216
|
+
top: ITEM_HEIGHT * 2,
|
|
217
|
+
left: 4,
|
|
218
|
+
right: 4,
|
|
219
|
+
height: ITEM_HEIGHT,
|
|
220
|
+
backgroundColor: 'rgba(0, 0, 0, 0.04)',
|
|
221
|
+
borderRadius: 8,
|
|
222
|
+
zIndex: 0,
|
|
223
|
+
},
|
|
224
|
+
itemsContainer: {
|
|
225
|
+
zIndex: 1,
|
|
226
|
+
},
|
|
227
|
+
item: {
|
|
228
|
+
height: ITEM_HEIGHT,
|
|
229
|
+
justifyContent: 'center',
|
|
230
|
+
alignItems: 'center',
|
|
231
|
+
},
|
|
232
|
+
itemText: {
|
|
233
|
+
fontSize: 21,
|
|
234
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif',
|
|
235
|
+
fontWeight: '400',
|
|
236
|
+
},
|
|
237
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WheelPicker } from './WheelPicker';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { DatePicker, type KaalDatePickerProps } from './DatePicker';
|
|
2
|
+
export { CalendarGrid } from './CalendarGrid';
|
|
3
|
+
export { WheelPicker } from './WheelPicker';
|
|
4
|
+
export {
|
|
5
|
+
TimePicker,
|
|
6
|
+
TimeWheelPicker,
|
|
7
|
+
ClockFace,
|
|
8
|
+
MaterialTimePicker,
|
|
9
|
+
type TimePickerProps,
|
|
10
|
+
type TimeValue,
|
|
11
|
+
} from './TimePicker';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
addMonths,
|
|
4
|
+
getDayOfWeek,
|
|
5
|
+
getFirstDayOfMonth,
|
|
6
|
+
getMonthDays,
|
|
7
|
+
today,
|
|
8
|
+
} from '../utils/date';
|
|
9
|
+
|
|
10
|
+
export const useCalendar = (initialDate?: Date) => {
|
|
11
|
+
const [currentMonth, setCurrentMonth] = useState(() => {
|
|
12
|
+
const date = initialDate ?? today();
|
|
13
|
+
return getFirstDayOfMonth(date);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const navigateMonth = useCallback((direction: 1 | -1) => {
|
|
17
|
+
setCurrentMonth((prev) => addMonths(prev, direction));
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
const goToMonth = useCallback((date: Date) => {
|
|
21
|
+
setCurrentMonth(getFirstDayOfMonth(date));
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
const goToDate = useCallback((date: Date) => {
|
|
25
|
+
setCurrentMonth(getFirstDayOfMonth(date));
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
const daysInMonth = useMemo(() => {
|
|
29
|
+
const days: (Date | null)[] = [];
|
|
30
|
+
const firstDay = getFirstDayOfMonth(currentMonth);
|
|
31
|
+
// getDayOfWeek returns 0 for Sunday, we want Monday = 0
|
|
32
|
+
const dayOfWeek = getDayOfWeek(firstDay);
|
|
33
|
+
const paddingDays = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < paddingDays; i++) {
|
|
36
|
+
days.push(null);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const monthDays = getMonthDays(
|
|
40
|
+
currentMonth.getUTCFullYear(),
|
|
41
|
+
currentMonth.getUTCMonth(),
|
|
42
|
+
);
|
|
43
|
+
for (const day of monthDays) {
|
|
44
|
+
days.push(day);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return days;
|
|
48
|
+
}, [currentMonth]);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
currentMonth,
|
|
52
|
+
daysInMonth,
|
|
53
|
+
navigateMonth,
|
|
54
|
+
goToMonth,
|
|
55
|
+
goToDate,
|
|
56
|
+
nextMonth: () => navigateMonth(1),
|
|
57
|
+
prevMonth: () => navigateMonth(-1),
|
|
58
|
+
};
|
|
59
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { compareDates, today } from '../utils/date';
|
|
3
|
+
|
|
4
|
+
interface UseDatePickerOptions {
|
|
5
|
+
initialDate?: Date;
|
|
6
|
+
minDate?: Date;
|
|
7
|
+
maxDate?: Date;
|
|
8
|
+
onChange?: (date: Date) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useDatePicker = (options: UseDatePickerOptions = {}) => {
|
|
12
|
+
const { initialDate = today(), minDate, maxDate, onChange } = options;
|
|
13
|
+
|
|
14
|
+
const [selectedDate, setSelectedDate] = useState(initialDate);
|
|
15
|
+
|
|
16
|
+
const handleDateChange = useCallback(
|
|
17
|
+
(date: Date) => {
|
|
18
|
+
setSelectedDate(date);
|
|
19
|
+
onChange?.(date);
|
|
20
|
+
},
|
|
21
|
+
[onChange],
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const isDateDisabled = useCallback(
|
|
25
|
+
(date: Date) => {
|
|
26
|
+
if (minDate && compareDates(date, minDate) < 0) return true;
|
|
27
|
+
if (maxDate && compareDates(date, maxDate) > 0) return true;
|
|
28
|
+
return false;
|
|
29
|
+
},
|
|
30
|
+
[minDate, maxDate],
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
selectedDate,
|
|
35
|
+
setSelectedDate: handleDateChange,
|
|
36
|
+
isDateDisabled,
|
|
37
|
+
minDate,
|
|
38
|
+
maxDate,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
ClockMode,
|
|
4
|
+
Time12Hour,
|
|
5
|
+
TimePeriod,
|
|
6
|
+
TimeValue,
|
|
7
|
+
} from '../types/timepicker';
|
|
8
|
+
|
|
9
|
+
interface UseTimePickerOptions {
|
|
10
|
+
/** Initial hours (0-23) */
|
|
11
|
+
initialHours?: number;
|
|
12
|
+
/** Initial minutes (0-59) */
|
|
13
|
+
initialMinutes?: number;
|
|
14
|
+
/** Callback when time changes */
|
|
15
|
+
onChange?: (time: TimeValue) => void;
|
|
16
|
+
/** Use 24-hour format */
|
|
17
|
+
is24Hour?: boolean;
|
|
18
|
+
/** Minute interval for snapping */
|
|
19
|
+
minuteInterval?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Converts 24-hour time to 12-hour format
|
|
24
|
+
*/
|
|
25
|
+
export const to12Hour = (hours: number): Time12Hour => {
|
|
26
|
+
const period: TimePeriod = hours >= 12 ? 'PM' : 'AM';
|
|
27
|
+
let hour = hours % 12;
|
|
28
|
+
if (hour === 0) hour = 12;
|
|
29
|
+
return { hour, period };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Converts 12-hour time to 24-hour format
|
|
34
|
+
*/
|
|
35
|
+
export const to24Hour = (hour: number, period: TimePeriod): number => {
|
|
36
|
+
if (period === 'AM') {
|
|
37
|
+
return hour === 12 ? 0 : hour;
|
|
38
|
+
}
|
|
39
|
+
return hour === 12 ? 12 : hour + 12;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Formats time as HH:MM string
|
|
44
|
+
*/
|
|
45
|
+
export const formatTime = (
|
|
46
|
+
hours: number,
|
|
47
|
+
minutes: number,
|
|
48
|
+
is24Hour = false,
|
|
49
|
+
): string => {
|
|
50
|
+
if (is24Hour) {
|
|
51
|
+
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
|
|
52
|
+
}
|
|
53
|
+
const { hour, period } = to12Hour(hours);
|
|
54
|
+
return `${hour}:${minutes.toString().padStart(2, '0')} ${period}`;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Snaps minutes to the nearest interval
|
|
59
|
+
*/
|
|
60
|
+
export const snapToInterval = (minutes: number, interval: number): number => {
|
|
61
|
+
return Math.round(minutes / interval) * interval;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Hook for managing time picker state
|
|
66
|
+
*/
|
|
67
|
+
export const useTimePicker = (options: UseTimePickerOptions = {}) => {
|
|
68
|
+
const {
|
|
69
|
+
initialHours = 0,
|
|
70
|
+
initialMinutes = 0,
|
|
71
|
+
onChange,
|
|
72
|
+
is24Hour = false,
|
|
73
|
+
minuteInterval = 1,
|
|
74
|
+
} = options;
|
|
75
|
+
|
|
76
|
+
const [hours, setHoursState] = useState(initialHours);
|
|
77
|
+
const [minutes, setMinutesState] = useState(initialMinutes);
|
|
78
|
+
const [clockMode, setClockMode] = useState<ClockMode>('hours');
|
|
79
|
+
|
|
80
|
+
const setHours = useCallback(
|
|
81
|
+
(newHours: number) => {
|
|
82
|
+
const clampedHours = Math.max(0, Math.min(23, newHours));
|
|
83
|
+
setHoursState(clampedHours);
|
|
84
|
+
onChange?.({ hours: clampedHours, minutes });
|
|
85
|
+
},
|
|
86
|
+
[minutes, onChange],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const setMinutes = useCallback(
|
|
90
|
+
(newMinutes: number) => {
|
|
91
|
+
const snapped = snapToInterval(newMinutes, minuteInterval);
|
|
92
|
+
const clampedMinutes = Math.max(0, Math.min(59, snapped));
|
|
93
|
+
setMinutesState(clampedMinutes);
|
|
94
|
+
onChange?.({ hours, minutes: clampedMinutes });
|
|
95
|
+
},
|
|
96
|
+
[hours, onChange, minuteInterval],
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const setTime = useCallback(
|
|
100
|
+
(time: TimeValue) => {
|
|
101
|
+
const clampedHours = Math.max(0, Math.min(23, time.hours));
|
|
102
|
+
const snapped = snapToInterval(time.minutes, minuteInterval);
|
|
103
|
+
const clampedMinutes = Math.max(0, Math.min(59, snapped));
|
|
104
|
+
setHoursState(clampedHours);
|
|
105
|
+
setMinutesState(clampedMinutes);
|
|
106
|
+
onChange?.({ hours: clampedHours, minutes: clampedMinutes });
|
|
107
|
+
},
|
|
108
|
+
[onChange, minuteInterval],
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const setHours12 = useCallback(
|
|
112
|
+
(hour: number, period: TimePeriod) => {
|
|
113
|
+
const hours24 = to24Hour(hour, period);
|
|
114
|
+
setHours(hours24);
|
|
115
|
+
},
|
|
116
|
+
[setHours],
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const setPeriod = useCallback(
|
|
120
|
+
(period: TimePeriod) => {
|
|
121
|
+
const { hour } = to12Hour(hours);
|
|
122
|
+
setHours(to24Hour(hour, period));
|
|
123
|
+
},
|
|
124
|
+
[hours, setHours],
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const time12Hour = to12Hour(hours);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
// State
|
|
131
|
+
hours,
|
|
132
|
+
minutes,
|
|
133
|
+
clockMode,
|
|
134
|
+
// 12-hour format helpers
|
|
135
|
+
hour12: time12Hour.hour,
|
|
136
|
+
period: time12Hour.period,
|
|
137
|
+
// Setters
|
|
138
|
+
setHours,
|
|
139
|
+
setMinutes,
|
|
140
|
+
setTime,
|
|
141
|
+
setHours12,
|
|
142
|
+
setPeriod,
|
|
143
|
+
setClockMode,
|
|
144
|
+
// Formatting
|
|
145
|
+
formatted: formatTime(hours, minutes, is24Hour),
|
|
146
|
+
formatted12: formatTime(hours, minutes, false),
|
|
147
|
+
formatted24: formatTime(hours, minutes, true),
|
|
148
|
+
// Config
|
|
149
|
+
is24Hour,
|
|
150
|
+
minuteInterval,
|
|
151
|
+
};
|
|
152
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Date picker components
|
|
2
|
+
export { DatePicker, type KaalDatePickerProps } from './components';
|
|
3
|
+
export { CalendarGrid } from './components';
|
|
4
|
+
export { WheelPicker } from './components';
|
|
5
|
+
|
|
6
|
+
// Time picker components
|
|
7
|
+
export {
|
|
8
|
+
TimePicker,
|
|
9
|
+
TimeWheelPicker,
|
|
10
|
+
ClockFace,
|
|
11
|
+
MaterialTimePicker,
|
|
12
|
+
type TimePickerProps,
|
|
13
|
+
type TimeValue,
|
|
14
|
+
} from './components';
|
|
15
|
+
|
|
16
|
+
// Hooks
|
|
17
|
+
export { useDatePicker, useCalendar } from './hooks';
|
|
18
|
+
export {
|
|
19
|
+
useTimePicker,
|
|
20
|
+
to12Hour,
|
|
21
|
+
to24Hour,
|
|
22
|
+
formatTime,
|
|
23
|
+
snapToInterval,
|
|
24
|
+
} from './hooks';
|
|
25
|
+
|
|
26
|
+
// Date utilities
|
|
27
|
+
export {
|
|
28
|
+
toISODateString,
|
|
29
|
+
toISODateTimeString,
|
|
30
|
+
parseISODate,
|
|
31
|
+
parseISODateTime,
|
|
32
|
+
fromISODateString,
|
|
33
|
+
fromISODateTimeString,
|
|
34
|
+
getDateRange,
|
|
35
|
+
isDateInRange,
|
|
36
|
+
getUserTimezone,
|
|
37
|
+
addDays,
|
|
38
|
+
addMonths,
|
|
39
|
+
compareDates,
|
|
40
|
+
isSameDay,
|
|
41
|
+
isSameMonth,
|
|
42
|
+
getMonthDays,
|
|
43
|
+
getFirstDayOfMonth,
|
|
44
|
+
getLastDayOfMonth,
|
|
45
|
+
getDayOfWeek,
|
|
46
|
+
formatMonth,
|
|
47
|
+
formatWeekday,
|
|
48
|
+
formatYearMonth,
|
|
49
|
+
today,
|
|
50
|
+
} from './utils';
|
|
51
|
+
|
|
52
|
+
// Validation schemas
|
|
53
|
+
export {
|
|
54
|
+
isoDateSchema,
|
|
55
|
+
isoDateTimeSchema,
|
|
56
|
+
dateRangeSchema,
|
|
57
|
+
datePickerValueSchema,
|
|
58
|
+
dateSchema,
|
|
59
|
+
temporalDateSchema,
|
|
60
|
+
} from './utils';
|
|
61
|
+
|
|
62
|
+
export type { DatePickerValue, DateRange } from './utils';
|
|
63
|
+
export type {
|
|
64
|
+
DatePickerMode,
|
|
65
|
+
DatePickerTheme,
|
|
66
|
+
DatePickerVariant,
|
|
67
|
+
DatePickerProps,
|
|
68
|
+
} from './types';
|
|
69
|
+
|
|
70
|
+
// Time picker types
|
|
71
|
+
export type {
|
|
72
|
+
TimePeriod,
|
|
73
|
+
ClockMode,
|
|
74
|
+
Time12Hour,
|
|
75
|
+
MinuteInterval,
|
|
76
|
+
TimePickerTheme,
|
|
77
|
+
} from './types';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type DatePickerMode = 'date' | 'time' | 'datetime';
|
|
2
|
+
|
|
3
|
+
export type DatePickerTheme = 'native' | 'ios' | 'android' | 'custom';
|
|
4
|
+
|
|
5
|
+
export type DatePickerVariant = 'wheel' | 'calendar' | 'compact';
|
|
6
|
+
|
|
7
|
+
export interface DatePickerProps {
|
|
8
|
+
value: Date;
|
|
9
|
+
onChange: (date: Date) => void;
|
|
10
|
+
mode?: DatePickerMode;
|
|
11
|
+
theme?: DatePickerTheme;
|
|
12
|
+
variant?: DatePickerVariant;
|
|
13
|
+
minDate?: Date;
|
|
14
|
+
maxDate?: Date;
|
|
15
|
+
disabledDates?: Date[];
|
|
16
|
+
locale?: string;
|
|
17
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a time value in 24-hour format
|
|
3
|
+
*/
|
|
4
|
+
export interface TimeValue {
|
|
5
|
+
/** Hours in 24-hour format (0-23) */
|
|
6
|
+
hours: number;
|
|
7
|
+
/** Minutes (0-59) */
|
|
8
|
+
minutes: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Theme options for TimePicker appearance
|
|
13
|
+
*/
|
|
14
|
+
export type TimePickerTheme = 'native' | 'ios' | 'android';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Minute interval options for time selection
|
|
18
|
+
*/
|
|
19
|
+
export type MinuteInterval = 1 | 5 | 10 | 15 | 30;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Props for the TimePicker component
|
|
23
|
+
*/
|
|
24
|
+
export interface TimePickerProps {
|
|
25
|
+
/** Current time value */
|
|
26
|
+
value: TimeValue;
|
|
27
|
+
/** Callback when time changes */
|
|
28
|
+
onChange: (time: TimeValue) => void;
|
|
29
|
+
/** Visual theme - 'native' uses platform defaults */
|
|
30
|
+
theme?: TimePickerTheme;
|
|
31
|
+
/** Interval for minute selection */
|
|
32
|
+
minuteInterval?: MinuteInterval;
|
|
33
|
+
/** Use 24-hour format instead of 12-hour with AM/PM */
|
|
34
|
+
is24Hour?: boolean;
|
|
35
|
+
/** Minimum selectable time */
|
|
36
|
+
minTime?: TimeValue;
|
|
37
|
+
/** Maximum selectable time */
|
|
38
|
+
maxTime?: TimeValue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Period indicator for 12-hour format
|
|
43
|
+
*/
|
|
44
|
+
export type TimePeriod = 'AM' | 'PM';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Clock mode for Material-style picker
|
|
48
|
+
*/
|
|
49
|
+
export type ClockMode = 'hours' | 'minutes';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 12-hour time representation
|
|
53
|
+
*/
|
|
54
|
+
export interface Time12Hour {
|
|
55
|
+
/** Hour in 12-hour format (1-12) */
|
|
56
|
+
hour: number;
|
|
57
|
+
/** AM or PM */
|
|
58
|
+
period: TimePeriod;
|
|
59
|
+
}
|
package/src/unistyles.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Re-export theme configuration from @dreamstack-us/kaal-themes
|
|
2
|
+
// This ensures the module augmentation is applied
|
|
3
|
+
export { configureKaalThemes } from '@dreamstack-us/kaal-themes';
|
|
4
|
+
|
|
5
|
+
// Import to ensure type augmentation is applied during build
|
|
6
|
+
import '@dreamstack-us/kaal-themes';
|