@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.
Files changed (181) hide show
  1. package/lib/module/components/CalendarGrid/CalendarGrid.js +112 -0
  2. package/lib/module/components/CalendarGrid/CalendarGrid.js.map +1 -0
  3. package/lib/module/components/CalendarGrid/CalendarGrid.styles.js +46 -0
  4. package/lib/module/components/CalendarGrid/CalendarGrid.styles.js.map +1 -0
  5. package/lib/module/components/CalendarGrid/DayCell.js +96 -0
  6. package/lib/module/components/CalendarGrid/DayCell.js.map +1 -0
  7. package/lib/module/components/CalendarGrid/index.js +4 -0
  8. package/lib/module/components/CalendarGrid/index.js.map +1 -0
  9. package/lib/module/components/DatePicker/DatePicker.android.js +66 -0
  10. package/lib/module/components/DatePicker/DatePicker.android.js.map +1 -0
  11. package/lib/module/components/DatePicker/DatePicker.ios.js +74 -0
  12. package/lib/module/components/DatePicker/DatePicker.ios.js.map +1 -0
  13. package/lib/module/components/DatePicker/DatePicker.js +9 -0
  14. package/lib/module/components/DatePicker/DatePicker.js.map +1 -0
  15. package/lib/module/components/DatePicker/DatePicker.styles.js +35 -0
  16. package/lib/module/components/DatePicker/DatePicker.styles.js.map +1 -0
  17. package/lib/module/components/DatePicker/DatePicker.web.js +32 -0
  18. package/lib/module/components/DatePicker/DatePicker.web.js.map +1 -0
  19. package/lib/module/components/DatePicker/index.js +4 -0
  20. package/lib/module/components/DatePicker/index.js.map +1 -0
  21. package/lib/module/components/TimePicker/ClockFace.js +194 -0
  22. package/lib/module/components/TimePicker/ClockFace.js.map +1 -0
  23. package/lib/module/components/TimePicker/MaterialTimePicker.js +122 -0
  24. package/lib/module/components/TimePicker/MaterialTimePicker.js.map +1 -0
  25. package/lib/module/components/TimePicker/TimePicker.android.js +77 -0
  26. package/lib/module/components/TimePicker/TimePicker.android.js.map +1 -0
  27. package/lib/module/components/TimePicker/TimePicker.ios.js +83 -0
  28. package/lib/module/components/TimePicker/TimePicker.ios.js.map +1 -0
  29. package/lib/module/components/TimePicker/TimePicker.js +34 -0
  30. package/lib/module/components/TimePicker/TimePicker.js.map +1 -0
  31. package/lib/module/components/TimePicker/TimePicker.styles.js +180 -0
  32. package/lib/module/components/TimePicker/TimePicker.styles.js.map +1 -0
  33. package/lib/module/components/TimePicker/TimePicker.web.js +37 -0
  34. package/lib/module/components/TimePicker/TimePicker.web.js.map +1 -0
  35. package/lib/module/components/TimePicker/TimeWheelPicker.js +178 -0
  36. package/lib/module/components/TimePicker/TimeWheelPicker.js.map +1 -0
  37. package/lib/module/components/TimePicker/index.js +7 -0
  38. package/lib/module/components/TimePicker/index.js.map +1 -0
  39. package/lib/module/components/WheelPicker/WheelPicker.js +5 -0
  40. package/lib/module/components/WheelPicker/WheelPicker.js.map +1 -0
  41. package/lib/module/components/WheelPicker/WheelPicker.styles.js +41 -0
  42. package/lib/module/components/WheelPicker/WheelPicker.styles.js.map +1 -0
  43. package/lib/module/components/WheelPicker/WheelPicker.web.js +190 -0
  44. package/lib/module/components/WheelPicker/WheelPicker.web.js.map +1 -0
  45. package/lib/module/components/WheelPicker/index.js +4 -0
  46. package/lib/module/components/WheelPicker/index.js.map +1 -0
  47. package/lib/module/components/index.js +7 -0
  48. package/lib/module/components/index.js.map +1 -0
  49. package/lib/module/hooks/index.js +6 -0
  50. package/lib/module/hooks/index.js.map +1 -0
  51. package/lib/module/hooks/useCalendar.js +44 -0
  52. package/lib/module/hooks/useCalendar.js.map +1 -0
  53. package/lib/module/hooks/useDatePicker.js +30 -0
  54. package/lib/module/hooks/useDatePicker.js.map +1 -0
  55. package/lib/module/hooks/useTimePicker.js +125 -0
  56. package/lib/module/hooks/useTimePicker.js.map +1 -0
  57. package/lib/module/index.js +22 -0
  58. package/lib/module/index.js.map +1 -0
  59. package/lib/module/types/datepicker.js +2 -0
  60. package/lib/module/types/datepicker.js.map +1 -0
  61. package/lib/module/types/index.js +5 -0
  62. package/lib/module/types/index.js.map +1 -0
  63. package/lib/module/types/timepicker.js +2 -0
  64. package/lib/module/types/timepicker.js.map +1 -0
  65. package/lib/module/unistyles.js +9 -0
  66. package/lib/module/unistyles.js.map +1 -0
  67. package/lib/module/utils/date.js +205 -0
  68. package/lib/module/utils/date.js.map +1 -0
  69. package/lib/module/utils/index.js +5 -0
  70. package/lib/module/utils/index.js.map +1 -0
  71. package/lib/module/utils/validation.js +61 -0
  72. package/lib/module/utils/validation.js.map +1 -0
  73. package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts +12 -0
  74. package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts.map +1 -0
  75. package/lib/typescript/components/CalendarGrid/CalendarGrid.styles.d.ts +45 -0
  76. package/lib/typescript/components/CalendarGrid/CalendarGrid.styles.d.ts.map +1 -0
  77. package/lib/typescript/components/CalendarGrid/DayCell.d.ts +12 -0
  78. package/lib/typescript/components/CalendarGrid/DayCell.d.ts.map +1 -0
  79. package/lib/typescript/components/CalendarGrid/index.d.ts +2 -0
  80. package/lib/typescript/components/CalendarGrid/index.d.ts.map +1 -0
  81. package/lib/typescript/components/DatePicker/DatePicker.android.d.ts +4 -0
  82. package/lib/typescript/components/DatePicker/DatePicker.android.d.ts.map +1 -0
  83. package/lib/typescript/components/DatePicker/DatePicker.d.ts +15 -0
  84. package/lib/typescript/components/DatePicker/DatePicker.d.ts.map +1 -0
  85. package/lib/typescript/components/DatePicker/DatePicker.ios.d.ts +4 -0
  86. package/lib/typescript/components/DatePicker/DatePicker.ios.d.ts.map +1 -0
  87. package/lib/typescript/components/DatePicker/DatePicker.styles.d.ts +29 -0
  88. package/lib/typescript/components/DatePicker/DatePicker.styles.d.ts.map +1 -0
  89. package/lib/typescript/components/DatePicker/DatePicker.web.d.ts +4 -0
  90. package/lib/typescript/components/DatePicker/DatePicker.web.d.ts.map +1 -0
  91. package/lib/typescript/components/DatePicker/index.d.ts +3 -0
  92. package/lib/typescript/components/DatePicker/index.d.ts.map +1 -0
  93. package/lib/typescript/components/TimePicker/ClockFace.d.ts +12 -0
  94. package/lib/typescript/components/TimePicker/ClockFace.d.ts.map +1 -0
  95. package/lib/typescript/components/TimePicker/MaterialTimePicker.d.ts +12 -0
  96. package/lib/typescript/components/TimePicker/MaterialTimePicker.d.ts.map +1 -0
  97. package/lib/typescript/components/TimePicker/TimePicker.android.d.ts +4 -0
  98. package/lib/typescript/components/TimePicker/TimePicker.android.d.ts.map +1 -0
  99. package/lib/typescript/components/TimePicker/TimePicker.d.ts +29 -0
  100. package/lib/typescript/components/TimePicker/TimePicker.d.ts.map +1 -0
  101. package/lib/typescript/components/TimePicker/TimePicker.ios.d.ts +4 -0
  102. package/lib/typescript/components/TimePicker/TimePicker.ios.d.ts.map +1 -0
  103. package/lib/typescript/components/TimePicker/TimePicker.styles.d.ts +168 -0
  104. package/lib/typescript/components/TimePicker/TimePicker.styles.d.ts.map +1 -0
  105. package/lib/typescript/components/TimePicker/TimePicker.web.d.ts +10 -0
  106. package/lib/typescript/components/TimePicker/TimePicker.web.d.ts.map +1 -0
  107. package/lib/typescript/components/TimePicker/TimeWheelPicker.d.ts +11 -0
  108. package/lib/typescript/components/TimePicker/TimeWheelPicker.d.ts.map +1 -0
  109. package/lib/typescript/components/TimePicker/index.d.ts +6 -0
  110. package/lib/typescript/components/TimePicker/index.d.ts.map +1 -0
  111. package/lib/typescript/components/WheelPicker/WheelPicker.d.ts +2 -0
  112. package/lib/typescript/components/WheelPicker/WheelPicker.d.ts.map +1 -0
  113. package/lib/typescript/components/WheelPicker/WheelPicker.styles.d.ts +40 -0
  114. package/lib/typescript/components/WheelPicker/WheelPicker.styles.d.ts.map +1 -0
  115. package/lib/typescript/components/WheelPicker/WheelPicker.web.d.ts +10 -0
  116. package/lib/typescript/components/WheelPicker/WheelPicker.web.d.ts.map +1 -0
  117. package/lib/typescript/components/WheelPicker/index.d.ts +2 -0
  118. package/lib/typescript/components/WheelPicker/index.d.ts.map +1 -0
  119. package/lib/typescript/components/index.d.ts +5 -0
  120. package/lib/typescript/components/index.d.ts.map +1 -0
  121. package/lib/typescript/hooks/index.d.ts +4 -0
  122. package/lib/typescript/hooks/index.d.ts.map +1 -0
  123. package/lib/typescript/hooks/useCalendar.d.ts +10 -0
  124. package/lib/typescript/hooks/useCalendar.d.ts.map +1 -0
  125. package/lib/typescript/hooks/useDatePicker.d.ts +15 -0
  126. package/lib/typescript/hooks/useDatePicker.d.ts.map +1 -0
  127. package/lib/typescript/hooks/useTimePicker.d.ts +52 -0
  128. package/lib/typescript/hooks/useTimePicker.d.ts.map +1 -0
  129. package/lib/typescript/index.d.ts +12 -0
  130. package/lib/typescript/index.d.ts.map +1 -0
  131. package/lib/typescript/types/datepicker.d.ts +15 -0
  132. package/lib/typescript/types/datepicker.d.ts.map +1 -0
  133. package/lib/typescript/types/index.d.ts +3 -0
  134. package/lib/typescript/types/index.d.ts.map +1 -0
  135. package/lib/typescript/types/timepicker.d.ts +54 -0
  136. package/lib/typescript/types/timepicker.d.ts.map +1 -0
  137. package/lib/typescript/unistyles.d.ts +3 -0
  138. package/lib/typescript/unistyles.d.ts.map +1 -0
  139. package/lib/typescript/utils/date.d.ts +94 -0
  140. package/lib/typescript/utils/date.d.ts.map +1 -0
  141. package/lib/typescript/utils/index.d.ts +3 -0
  142. package/lib/typescript/utils/index.d.ts.map +1 -0
  143. package/lib/typescript/utils/validation.d.ts +40 -0
  144. package/lib/typescript/utils/validation.d.ts.map +1 -0
  145. package/package.json +101 -0
  146. package/src/components/CalendarGrid/CalendarGrid.styles.ts +44 -0
  147. package/src/components/CalendarGrid/CalendarGrid.tsx +151 -0
  148. package/src/components/CalendarGrid/DayCell.tsx +108 -0
  149. package/src/components/CalendarGrid/index.ts +1 -0
  150. package/src/components/DatePicker/DatePicker.android.tsx +69 -0
  151. package/src/components/DatePicker/DatePicker.ios.tsx +78 -0
  152. package/src/components/DatePicker/DatePicker.styles.ts +35 -0
  153. package/src/components/DatePicker/DatePicker.tsx +21 -0
  154. package/src/components/DatePicker/DatePicker.web.tsx +36 -0
  155. package/src/components/DatePicker/index.ts +2 -0
  156. package/src/components/TimePicker/ClockFace.tsx +233 -0
  157. package/src/components/TimePicker/MaterialTimePicker.tsx +169 -0
  158. package/src/components/TimePicker/TimePicker.android.tsx +80 -0
  159. package/src/components/TimePicker/TimePicker.ios.tsx +88 -0
  160. package/src/components/TimePicker/TimePicker.styles.ts +209 -0
  161. package/src/components/TimePicker/TimePicker.tsx +35 -0
  162. package/src/components/TimePicker/TimePicker.web.tsx +35 -0
  163. package/src/components/TimePicker/TimeWheelPicker.tsx +211 -0
  164. package/src/components/TimePicker/index.ts +5 -0
  165. package/src/components/WheelPicker/WheelPicker.styles.ts +39 -0
  166. package/src/components/WheelPicker/WheelPicker.tsx +2 -0
  167. package/src/components/WheelPicker/WheelPicker.web.tsx +237 -0
  168. package/src/components/WheelPicker/index.ts +1 -0
  169. package/src/components/index.ts +11 -0
  170. package/src/hooks/index.ts +9 -0
  171. package/src/hooks/useCalendar.ts +59 -0
  172. package/src/hooks/useDatePicker.ts +40 -0
  173. package/src/hooks/useTimePicker.ts +152 -0
  174. package/src/index.ts +77 -0
  175. package/src/types/datepicker.ts +17 -0
  176. package/src/types/index.ts +2 -0
  177. package/src/types/timepicker.ts +59 -0
  178. package/src/unistyles.ts +6 -0
  179. package/src/utils/date.ts +217 -0
  180. package/src/utils/index.ts +2 -0
  181. 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,9 @@
1
+ export { useDatePicker } from './useDatePicker';
2
+ export { useCalendar } from './useCalendar';
3
+ export {
4
+ useTimePicker,
5
+ to12Hour,
6
+ to24Hour,
7
+ formatTime,
8
+ snapToInterval,
9
+ } from './useTimePicker';
@@ -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,2 @@
1
+ export * from './datepicker';
2
+ export * from './timepicker';
@@ -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
+ }
@@ -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';