@dreamstack-us/kaal 0.0.1 → 0.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 (134) hide show
  1. package/lib/module/components/CalendarGrid/CalendarGrid.js +47 -13
  2. package/lib/module/components/CalendarGrid/CalendarGrid.js.map +1 -1
  3. package/lib/module/components/CalendarGrid/CalendarGrid.styles.js +22 -17
  4. package/lib/module/components/CalendarGrid/CalendarGrid.styles.js.map +1 -1
  5. package/lib/module/components/CalendarGrid/CalendarGrid.web.js +203 -0
  6. package/lib/module/components/CalendarGrid/CalendarGrid.web.js.map +1 -0
  7. package/lib/module/components/CalendarGrid/DayCell.js +64 -52
  8. package/lib/module/components/CalendarGrid/DayCell.js.map +1 -1
  9. package/lib/module/components/CalendarGrid/DayCell.web.js +112 -0
  10. package/lib/module/components/CalendarGrid/DayCell.web.js.map +1 -0
  11. package/lib/module/components/CalendarGrid/index.js +1 -1
  12. package/lib/module/components/CalendarGrid/index.js.map +1 -1
  13. package/lib/module/components/DatePicker/DatePicker.android.js +17 -8
  14. package/lib/module/components/DatePicker/DatePicker.android.js.map +1 -1
  15. package/lib/module/components/DatePicker/DatePicker.ios.js +17 -8
  16. package/lib/module/components/DatePicker/DatePicker.ios.js.map +1 -1
  17. package/lib/module/components/DatePicker/DatePicker.js.map +1 -1
  18. package/lib/module/components/DatePicker/DatePicker.styles.js +19 -20
  19. package/lib/module/components/DatePicker/DatePicker.styles.js.map +1 -1
  20. package/lib/module/components/DatePicker/DatePicker.web.js +26 -12
  21. package/lib/module/components/DatePicker/DatePicker.web.js.map +1 -1
  22. package/lib/module/components/TimePicker/ClockFace.js +27 -7
  23. package/lib/module/components/TimePicker/ClockFace.js.map +1 -1
  24. package/lib/module/components/TimePicker/ClockFace.web.js +253 -0
  25. package/lib/module/components/TimePicker/ClockFace.web.js.map +1 -0
  26. package/lib/module/components/TimePicker/MaterialTimePicker.js +68 -16
  27. package/lib/module/components/TimePicker/MaterialTimePicker.js.map +1 -1
  28. package/lib/module/components/TimePicker/MaterialTimePicker.web.js +231 -0
  29. package/lib/module/components/TimePicker/MaterialTimePicker.web.js.map +1 -0
  30. package/lib/module/components/TimePicker/TimePicker.android.js +13 -6
  31. package/lib/module/components/TimePicker/TimePicker.android.js.map +1 -1
  32. package/lib/module/components/TimePicker/TimePicker.ios.js +14 -7
  33. package/lib/module/components/TimePicker/TimePicker.ios.js.map +1 -1
  34. package/lib/module/components/TimePicker/TimePicker.styles.js +53 -45
  35. package/lib/module/components/TimePicker/TimePicker.styles.js.map +1 -1
  36. package/lib/module/components/TimePicker/TimePicker.web.js +24 -12
  37. package/lib/module/components/TimePicker/TimePicker.web.js.map +1 -1
  38. package/lib/module/components/TimePicker/TimeWheelPicker.js +45 -10
  39. package/lib/module/components/TimePicker/TimeWheelPicker.js.map +1 -1
  40. package/lib/module/components/TimePicker/TimeWheelPicker.web.js +339 -0
  41. package/lib/module/components/TimePicker/TimeWheelPicker.web.js.map +1 -0
  42. package/lib/module/components/TimePicker/index.js +3 -3
  43. package/lib/module/components/TimePicker/index.js.map +1 -1
  44. package/lib/module/components/WheelPicker/WheelPicker.js +21 -2
  45. package/lib/module/components/WheelPicker/WheelPicker.js.map +1 -1
  46. package/lib/module/components/WheelPicker/WheelPicker.styles.js +13 -8
  47. package/lib/module/components/WheelPicker/WheelPicker.styles.js.map +1 -1
  48. package/lib/module/components/WheelPicker/WheelPicker.web.js +146 -57
  49. package/lib/module/components/WheelPicker/WheelPicker.web.js.map +1 -1
  50. package/lib/module/context/ThemeOverrideContext.js +34 -0
  51. package/lib/module/context/ThemeOverrideContext.js.map +1 -0
  52. package/lib/module/index.js +3 -0
  53. package/lib/module/index.js.map +1 -1
  54. package/lib/module/utils/validation.js +74 -34
  55. package/lib/module/utils/validation.js.map +1 -1
  56. package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts +9 -0
  57. package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts.map +1 -1
  58. package/lib/typescript/components/CalendarGrid/CalendarGrid.styles.d.ts +12 -10
  59. package/lib/typescript/components/CalendarGrid/CalendarGrid.styles.d.ts.map +1 -1
  60. package/lib/typescript/components/CalendarGrid/CalendarGrid.web.d.ts +21 -0
  61. package/lib/typescript/components/CalendarGrid/CalendarGrid.web.d.ts.map +1 -0
  62. package/lib/typescript/components/CalendarGrid/DayCell.d.ts.map +1 -1
  63. package/lib/typescript/components/CalendarGrid/DayCell.web.d.ts +12 -0
  64. package/lib/typescript/components/CalendarGrid/DayCell.web.d.ts.map +1 -0
  65. package/lib/typescript/components/DatePicker/DatePicker.android.d.ts.map +1 -1
  66. package/lib/typescript/components/DatePicker/DatePicker.d.ts +12 -1
  67. package/lib/typescript/components/DatePicker/DatePicker.d.ts.map +1 -1
  68. package/lib/typescript/components/DatePicker/DatePicker.ios.d.ts.map +1 -1
  69. package/lib/typescript/components/DatePicker/DatePicker.styles.d.ts +12 -13
  70. package/lib/typescript/components/DatePicker/DatePicker.styles.d.ts.map +1 -1
  71. package/lib/typescript/components/DatePicker/DatePicker.web.d.ts.map +1 -1
  72. package/lib/typescript/components/TimePicker/ClockFace.d.ts.map +1 -1
  73. package/lib/typescript/components/TimePicker/ClockFace.web.d.ts +12 -0
  74. package/lib/typescript/components/TimePicker/ClockFace.web.d.ts.map +1 -0
  75. package/lib/typescript/components/TimePicker/MaterialTimePicker.d.ts.map +1 -1
  76. package/lib/typescript/components/TimePicker/MaterialTimePicker.web.d.ts +12 -0
  77. package/lib/typescript/components/TimePicker/MaterialTimePicker.web.d.ts.map +1 -0
  78. package/lib/typescript/components/TimePicker/TimePicker.android.d.ts.map +1 -1
  79. package/lib/typescript/components/TimePicker/TimePicker.ios.d.ts.map +1 -1
  80. package/lib/typescript/components/TimePicker/TimePicker.styles.d.ts +29 -25
  81. package/lib/typescript/components/TimePicker/TimePicker.styles.d.ts.map +1 -1
  82. package/lib/typescript/components/TimePicker/TimePicker.web.d.ts.map +1 -1
  83. package/lib/typescript/components/TimePicker/TimeWheelPicker.d.ts.map +1 -1
  84. package/lib/typescript/components/TimePicker/TimeWheelPicker.web.d.ts +11 -0
  85. package/lib/typescript/components/TimePicker/TimeWheelPicker.web.d.ts.map +1 -0
  86. package/lib/typescript/components/WheelPicker/WheelPicker.d.ts +14 -1
  87. package/lib/typescript/components/WheelPicker/WheelPicker.d.ts.map +1 -1
  88. package/lib/typescript/components/WheelPicker/WheelPicker.styles.d.ts +9 -7
  89. package/lib/typescript/components/WheelPicker/WheelPicker.styles.d.ts.map +1 -1
  90. package/lib/typescript/components/WheelPicker/WheelPicker.web.d.ts.map +1 -1
  91. package/lib/typescript/context/ThemeOverrideContext.d.ts +23 -0
  92. package/lib/typescript/context/ThemeOverrideContext.d.ts.map +1 -0
  93. package/lib/typescript/index.d.ts +4 -2
  94. package/lib/typescript/index.d.ts.map +1 -1
  95. package/lib/typescript/types/datepicker.d.ts +41 -0
  96. package/lib/typescript/types/datepicker.d.ts.map +1 -1
  97. package/lib/typescript/types/timepicker.d.ts +62 -0
  98. package/lib/typescript/types/timepicker.d.ts.map +1 -1
  99. package/lib/typescript/utils/validation.d.ts +47 -27
  100. package/lib/typescript/utils/validation.d.ts.map +1 -1
  101. package/package.json +8 -8
  102. package/src/components/CalendarGrid/CalendarGrid.styles.ts +21 -17
  103. package/src/components/CalendarGrid/CalendarGrid.tsx +98 -12
  104. package/src/components/CalendarGrid/CalendarGrid.web.tsx +305 -0
  105. package/src/components/CalendarGrid/DayCell.tsx +78 -59
  106. package/src/components/CalendarGrid/DayCell.web.tsx +129 -0
  107. package/src/components/DatePicker/DatePicker.android.tsx +14 -8
  108. package/src/components/DatePicker/DatePicker.ios.tsx +14 -8
  109. package/src/components/DatePicker/DatePicker.styles.ts +18 -22
  110. package/src/components/DatePicker/DatePicker.tsx +12 -1
  111. package/src/components/DatePicker/DatePicker.web.tsx +21 -13
  112. package/src/components/TimePicker/ClockFace.tsx +34 -8
  113. package/src/components/TimePicker/ClockFace.web.tsx +303 -0
  114. package/src/components/TimePicker/MaterialTimePicker.tsx +144 -13
  115. package/src/components/TimePicker/MaterialTimePicker.web.tsx +271 -0
  116. package/src/components/TimePicker/TimePicker.android.tsx +9 -1
  117. package/src/components/TimePicker/TimePicker.ios.tsx +10 -6
  118. package/src/components/TimePicker/TimePicker.styles.ts +52 -45
  119. package/src/components/TimePicker/TimePicker.web.tsx +17 -7
  120. package/src/components/TimePicker/TimeWheelPicker.tsx +60 -6
  121. package/src/components/TimePicker/TimeWheelPicker.web.tsx +401 -0
  122. package/src/components/WheelPicker/WheelPicker.styles.ts +12 -8
  123. package/src/components/WheelPicker/WheelPicker.tsx +24 -2
  124. package/src/components/WheelPicker/WheelPicker.web.tsx +153 -57
  125. package/src/context/ThemeOverrideContext.tsx +38 -0
  126. package/src/index.ts +11 -0
  127. package/src/types/datepicker.ts +44 -0
  128. package/src/types/timepicker.ts +74 -0
  129. package/src/utils/validation.ts +111 -55
  130. package/lib/module/unistyles.js +0 -9
  131. package/lib/module/unistyles.js.map +0 -1
  132. package/lib/typescript/unistyles.d.ts +0 -3
  133. package/lib/typescript/unistyles.d.ts.map +0 -1
  134. package/src/unistyles.ts +0 -6
@@ -1,12 +1,7 @@
1
+ /// <reference lib="dom" />
1
2
  import type React from 'react';
2
- import { useCallback, useMemo } from 'react';
3
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
3
4
  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
5
 
11
6
  const ITEM_HEIGHT = 44;
12
7
  const VISIBLE_ITEMS = 5;
@@ -59,57 +54,162 @@ const WheelColumn: React.FC<{
59
54
  items: { value: number; label: string }[];
60
55
  selectedIndex: number;
61
56
  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],
57
+ label?: string;
58
+ }> = ({ items, selectedIndex, onSelect, label = 'Select value' }) => {
59
+ const scrollRef = useRef<HTMLDivElement>(null);
60
+ const isScrolling = useRef(false);
61
+ const scrollTimeout = useRef<ReturnType<typeof setTimeout>>();
62
+
63
+ // Scroll to selected item on mount and when selection changes externally
64
+ useEffect(() => {
65
+ if (scrollRef.current && !isScrolling.current) {
66
+ scrollRef.current.scrollTop = selectedIndex * ITEM_HEIGHT;
67
+ }
68
+ }, [selectedIndex]);
69
+
70
+ const handleScroll = useCallback(() => {
71
+ if (!scrollRef.current) return;
72
+
73
+ isScrolling.current = true;
74
+
75
+ // Clear existing timeout
76
+ if (scrollTimeout.current) {
77
+ clearTimeout(scrollTimeout.current);
78
+ }
79
+
80
+ // Debounce scroll end detection
81
+ scrollTimeout.current = setTimeout(() => {
82
+ if (!scrollRef.current) return;
83
+
84
+ const scrollTop = scrollRef.current.scrollTop;
85
+ const newIndex = Math.round(scrollTop / ITEM_HEIGHT);
86
+ const clampedIndex = Math.max(0, Math.min(items.length - 1, newIndex));
87
+
88
+ // Snap to nearest item
89
+ scrollRef.current.scrollTop = clampedIndex * ITEM_HEIGHT;
90
+
91
+ if (clampedIndex !== selectedIndex) {
92
+ onSelect(clampedIndex);
93
+ }
94
+
95
+ isScrolling.current = false;
96
+ }, 100);
97
+ }, [items.length, selectedIndex, onSelect]);
98
+
99
+ // Keyboard navigation for spinbutton behavior (like native input[type="time"])
100
+ const handleKeyDown = useCallback(
101
+ (e: React.KeyboardEvent) => {
102
+ let newIndex = selectedIndex;
103
+
104
+ switch (e.key) {
105
+ case 'ArrowUp':
106
+ e.preventDefault();
107
+ newIndex = Math.max(0, selectedIndex - 1);
108
+ break;
109
+ case 'ArrowDown':
110
+ e.preventDefault();
111
+ newIndex = Math.min(items.length - 1, selectedIndex + 1);
112
+ break;
113
+ case 'Home':
114
+ e.preventDefault();
115
+ newIndex = 0;
116
+ break;
117
+ case 'End':
118
+ e.preventDefault();
119
+ newIndex = items.length - 1;
120
+ break;
121
+ case 'PageUp':
122
+ e.preventDefault();
123
+ newIndex = Math.max(0, selectedIndex - 5);
124
+ break;
125
+ case 'PageDown':
126
+ e.preventDefault();
127
+ newIndex = Math.min(items.length - 1, selectedIndex + 5);
128
+ break;
129
+ default:
130
+ return;
131
+ }
132
+
133
+ if (newIndex !== selectedIndex) {
134
+ if (scrollRef.current) {
135
+ scrollRef.current.scrollTop = newIndex * ITEM_HEIGHT;
136
+ }
137
+ onSelect(newIndex);
138
+ }
139
+ },
140
+ [selectedIndex, items.length, onSelect],
90
141
  );
91
142
 
92
- const animatedStyle = useAnimatedStyle(() => ({
93
- transform: [{ translateY: translateY.value }],
94
- }));
143
+ const currentItem = items[selectedIndex];
95
144
 
96
145
  return (
97
146
  <View style={webStyles.column}>
98
147
  <View style={webStyles.selectionHighlight} />
99
148
 
100
- <GestureDetector gesture={panGesture}>
101
- <Animated.View style={[webStyles.itemsContainer, animatedStyle]}>
102
- <View style={{ height: ITEM_HEIGHT * 2 }} />
149
+ {/* Spinbutton container - mimics input[type="time"] accessibility */}
150
+ <div
151
+ ref={scrollRef}
152
+ role="spinbutton"
153
+ tabIndex={0}
154
+ aria-label={label}
155
+ aria-valuenow={currentItem?.value}
156
+ aria-valuemin={items[0]?.value}
157
+ aria-valuemax={items[items.length - 1]?.value}
158
+ aria-valuetext={currentItem?.label}
159
+ onScroll={handleScroll}
160
+ onKeyDown={handleKeyDown}
161
+ onClick={(e) => {
162
+ const target = e.target as HTMLElement;
163
+ const item = target.closest('[data-index]') as HTMLElement | null;
164
+ if (item && scrollRef.current) {
165
+ const index = Number(item.dataset.index);
166
+ scrollRef.current.scrollTop = index * ITEM_HEIGHT;
167
+ onSelect(index);
168
+ }
169
+ }}
170
+ style={{
171
+ height: CONTAINER_HEIGHT,
172
+ overflowY: 'auto',
173
+ scrollSnapType: 'y mandatory',
174
+ scrollBehavior: 'smooth',
175
+ position: 'relative',
176
+ zIndex: 1,
177
+ outline: 'none',
178
+ // Hide scrollbar
179
+ scrollbarWidth: 'none',
180
+ msOverflowStyle: 'none',
181
+ }}
182
+ >
183
+ {/* @ts-ignore - webkit scrollbar hiding */}
184
+ <style>{`
185
+ div::-webkit-scrollbar {
186
+ display: none;
187
+ }
188
+ `}</style>
189
+
190
+ {/* Top padding */}
191
+ <div style={{ height: ITEM_HEIGHT * 2, flexShrink: 0 }} />
103
192
 
104
- {items.map((item) => (
105
- <View key={item.value} style={webStyles.item}>
106
- <Text style={webStyles.itemText}>{item.label}</Text>
107
- </View>
108
- ))}
193
+ {items.map((item, index) => (
194
+ <div
195
+ key={item.value}
196
+ data-index={index}
197
+ style={{
198
+ height: ITEM_HEIGHT,
199
+ display: 'flex',
200
+ justifyContent: 'center',
201
+ alignItems: 'center',
202
+ scrollSnapAlign: 'center',
203
+ cursor: 'pointer',
204
+ }}
205
+ >
206
+ <Text style={webStyles.itemText}>{item.label}</Text>
207
+ </div>
208
+ ))}
109
209
 
110
- <View style={{ height: ITEM_HEIGHT * 2 }} />
111
- </Animated.View>
112
- </GestureDetector>
210
+ {/* Bottom padding */}
211
+ <div style={{ height: ITEM_HEIGHT * 2, flexShrink: 0 }} />
212
+ </div>
113
213
  </View>
114
214
  );
115
215
  };
@@ -179,11 +279,13 @@ export const WheelPicker: React.FC<WheelPickerProps> = ({
179
279
  items={months}
180
280
  selectedIndex={value.getUTCMonth()}
181
281
  onSelect={handleMonthChange}
282
+ label="Month"
182
283
  />
183
284
  <WheelColumn
184
285
  items={days}
185
286
  selectedIndex={value.getUTCDate() - 1}
186
287
  onSelect={handleDayChange}
288
+ label="Day"
187
289
  />
188
290
  <WheelColumn
189
291
  items={years}
@@ -191,6 +293,7 @@ export const WheelPicker: React.FC<WheelPickerProps> = ({
191
293
  (y) => y.value === value.getUTCFullYear(),
192
294
  )}
193
295
  onSelect={handleYearChange}
296
+ label="Year"
194
297
  />
195
298
  </View>
196
299
  );
@@ -210,6 +313,7 @@ const webStyles = RNStyleSheet.create({
210
313
  flex: 1,
211
314
  height: CONTAINER_HEIGHT,
212
315
  overflow: 'hidden',
316
+ position: 'relative',
213
317
  },
214
318
  selectionHighlight: {
215
319
  position: 'absolute',
@@ -221,14 +325,6 @@ const webStyles = RNStyleSheet.create({
221
325
  borderRadius: 8,
222
326
  zIndex: 0,
223
327
  },
224
- itemsContainer: {
225
- zIndex: 1,
226
- },
227
- item: {
228
- height: ITEM_HEIGHT,
229
- justifyContent: 'center',
230
- alignItems: 'center',
231
- },
232
328
  itemText: {
233
329
  fontSize: 21,
234
330
  fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif',
@@ -0,0 +1,38 @@
1
+ import { createContext, useContext } from 'react';
2
+ import type { DatePickerThemeOverrides } from '../types/datepicker';
3
+ import type { TimePickerThemeOverrides } from '../types/timepicker';
4
+
5
+ /**
6
+ * Combined theme overrides context value
7
+ */
8
+ export interface ThemeOverrideContextValue {
9
+ datePicker?: DatePickerThemeOverrides;
10
+ timePicker?: TimePickerThemeOverrides;
11
+ }
12
+
13
+ const ThemeOverrideContext = createContext<ThemeOverrideContextValue>({});
14
+
15
+ export const ThemeOverrideProvider = ThemeOverrideContext.Provider;
16
+
17
+ /**
18
+ * Hook to access DatePicker theme overrides
19
+ */
20
+ export function useDatePickerOverrides(): DatePickerThemeOverrides | undefined {
21
+ const context = useContext(ThemeOverrideContext);
22
+ return context.datePicker;
23
+ }
24
+
25
+ /**
26
+ * Hook to access TimePicker theme overrides
27
+ */
28
+ export function useTimePickerOverrides(): TimePickerThemeOverrides | undefined {
29
+ const context = useContext(ThemeOverrideContext);
30
+ return context.timePicker;
31
+ }
32
+
33
+ /**
34
+ * Hook to access all theme overrides
35
+ */
36
+ export function useThemeOverrides(): ThemeOverrideContextValue {
37
+ return useContext(ThemeOverrideContext);
38
+ }
package/src/index.ts CHANGED
@@ -65,6 +65,7 @@ export type {
65
65
  DatePickerTheme,
66
66
  DatePickerVariant,
67
67
  DatePickerProps,
68
+ DatePickerThemeOverrides,
68
69
  } from './types';
69
70
 
70
71
  // Time picker types
@@ -74,4 +75,14 @@ export type {
74
75
  Time12Hour,
75
76
  MinuteInterval,
76
77
  TimePickerTheme,
78
+ TimePickerThemeOverrides,
77
79
  } from './types';
80
+
81
+ // Theme override context (for advanced usage)
82
+ export {
83
+ ThemeOverrideProvider,
84
+ useDatePickerOverrides,
85
+ useTimePickerOverrides,
86
+ useThemeOverrides,
87
+ } from './context/ThemeOverrideContext';
88
+ export type { ThemeOverrideContextValue } from './context/ThemeOverrideContext';
@@ -4,6 +4,39 @@ export type DatePickerTheme = 'native' | 'ios' | 'android' | 'custom';
4
4
 
5
5
  export type DatePickerVariant = 'wheel' | 'calendar' | 'compact';
6
6
 
7
+ /**
8
+ * Theme overrides for DatePicker components.
9
+ * These allow customizing colors without matching Kaal's internal theme structure.
10
+ */
11
+ export interface DatePickerThemeOverrides {
12
+ // Calendar cell colors
13
+ /** Background color of selected date cell */
14
+ cellSelectedColor?: string;
15
+ /** Background color of today's date cell */
16
+ cellTodayColor?: string;
17
+ /** Default text color for dates */
18
+ textColor?: string;
19
+ /** Text color for selected date */
20
+ textSelectedColor?: string;
21
+ /** Text color for disabled dates */
22
+ textDisabledColor?: string;
23
+ /** Text color for weekend dates */
24
+ textWeekendColor?: string;
25
+ /** Primary accent color (navigation arrows, today border) */
26
+ primaryColor?: string;
27
+ /** Header background color */
28
+ headerBackground?: string;
29
+ /** Container background color */
30
+ backgroundColor?: string;
31
+ // Layout
32
+ /** Border radius for calendar container */
33
+ borderRadius?: number;
34
+ /** Border radius for date cells */
35
+ cellBorderRadius?: number;
36
+ /** Padding for calendar container */
37
+ padding?: number;
38
+ }
39
+
7
40
  export interface DatePickerProps {
8
41
  value: Date;
9
42
  onChange: (date: Date) => void;
@@ -14,4 +47,15 @@ export interface DatePickerProps {
14
47
  maxDate?: Date;
15
48
  disabledDates?: Date[];
16
49
  locale?: string;
50
+ /**
51
+ * First day of the week: 0 = Sunday, 1 = Monday
52
+ * @default 0 (Sunday)
53
+ *
54
+ * TODO: This is a temporary solution. In the future, we need to add full
55
+ * locale support to handle different calendar formats, layouts, and
56
+ * localized day/month names across different regions.
57
+ */
58
+ weekStartsOn?: 0 | 1;
59
+ /** Custom theme overrides for styling without matching Kaal's theme structure */
60
+ themeOverrides?: DatePickerThemeOverrides;
17
61
  }
@@ -18,6 +18,78 @@ export type TimePickerTheme = 'native' | 'ios' | 'android';
18
18
  */
19
19
  export type MinuteInterval = 1 | 5 | 10 | 15 | 30;
20
20
 
21
+ /**
22
+ * Theme overrides for TimePicker components.
23
+ * These allow customizing colors without matching Kaal's internal theme structure.
24
+ */
25
+ export interface TimePickerThemeOverrides {
26
+ // Clock face (Material style)
27
+ /** Background color of the clock face */
28
+ clockBackground?: string;
29
+ /** Color of the clock hand */
30
+ clockHandColor?: string;
31
+ /** Color of clock numbers */
32
+ clockTextColor?: string;
33
+ /** Color of selected clock number */
34
+ clockTextSelectedColor?: string;
35
+ /** Color of the center dot */
36
+ clockCenterColor?: string;
37
+ /** Color of the selection dot on clock edge */
38
+ clockSelectionColor?: string;
39
+
40
+ // Period toggle (AM/PM)
41
+ /** Background color of period buttons */
42
+ periodBackground?: string;
43
+ /** Background color of active period button */
44
+ periodActiveBackground?: string;
45
+ /** Border color of period buttons */
46
+ periodBorderColor?: string;
47
+ /** Text color of period buttons */
48
+ periodTextColor?: string;
49
+ /** Text color of active period button */
50
+ periodTextActiveColor?: string;
51
+
52
+ // Time field (header display)
53
+ /** Background color of time fields */
54
+ timeFieldBackground?: string;
55
+ /** Background color of active time field */
56
+ timeFieldActiveBackground?: string;
57
+ /** Text color of time fields */
58
+ textColor?: string;
59
+ /** Text color of active time field */
60
+ textActiveColor?: string;
61
+ /** Color of the colon separator */
62
+ separatorColor?: string;
63
+
64
+ // Wheel picker (iOS style)
65
+ /** Background color of wheel picker */
66
+ wheelContainerBackground?: string;
67
+ /** Color of wheel selection highlight */
68
+ wheelSelectionHighlight?: string;
69
+ /** Color of wheel separator */
70
+ wheelSeparatorColor?: string;
71
+ /** Text color in wheel */
72
+ wheelTextColor?: string;
73
+ /** Text color of selected wheel item */
74
+ wheelTextSelectedColor?: string;
75
+
76
+ // Material container
77
+ /** Container background color */
78
+ containerBackground?: string;
79
+ /** Header text color */
80
+ headerColor?: string;
81
+ /** Action button text color */
82
+ actionButtonColor?: string;
83
+
84
+ // General
85
+ /** Primary accent color */
86
+ primaryColor?: string;
87
+ /** Border radius for container */
88
+ borderRadius?: number;
89
+ /** Generic background color (fallback for all container backgrounds) */
90
+ backgroundColor?: string;
91
+ }
92
+
21
93
  /**
22
94
  * Props for the TimePicker component
23
95
  */
@@ -36,6 +108,8 @@ export interface TimePickerProps {
36
108
  minTime?: TimeValue;
37
109
  /** Maximum selectable time */
38
110
  maxTime?: TimeValue;
111
+ /** Custom theme overrides for styling without matching Kaal's theme structure */
112
+ themeOverrides?: TimePickerThemeOverrides;
39
113
  }
40
114
 
41
115
  /**
@@ -1,76 +1,132 @@
1
- import * as v from 'valibot';
2
-
3
1
  /**
4
- * Helper to parse ISO date string parts
5
- * Assumes input is already validated by regex pattern
2
+ * Simple validation utilities for date/time pickers
3
+ * No external dependencies - just inline checks
6
4
  */
7
- const parseISOParts = (val: string): [number, number, number] => {
8
- const parts = val.split('-').map(Number) as [number, number, number];
9
- return parts;
10
- };
5
+
6
+ // ISO date regex pattern
7
+ const ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
8
+ const ISO_DATETIME_PATTERN =
9
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?(Z|[+-]\d{2}:\d{2})?$/;
11
10
 
12
11
  /**
13
12
  * Validates an ISO 8601 date string (YYYY-MM-DD)
14
13
  */
15
- export const isoDateSchema = v.pipe(
16
- v.string(),
17
- v.regex(/^\d{4}-\d{2}-\d{2}$/, 'Must be in YYYY-MM-DD format'),
18
- v.check((val) => {
19
- const [year, month, day] = parseISOParts(val);
20
- const date = new Date(Date.UTC(year, month - 1, day));
21
- return (
22
- date.getUTCFullYear() === year &&
23
- date.getUTCMonth() === month - 1 &&
24
- date.getUTCDate() === day
25
- );
26
- }, 'Invalid ISO 8601 date format'),
27
- );
14
+ export function isValidISODate(value: string): boolean {
15
+ if (!ISO_DATE_PATTERN.test(value)) return false;
16
+
17
+ const [year, month, day] = value.split('-').map(Number) as [
18
+ number,
19
+ number,
20
+ number,
21
+ ];
22
+ const date = new Date(Date.UTC(year, month - 1, day));
23
+
24
+ return (
25
+ date.getUTCFullYear() === year &&
26
+ date.getUTCMonth() === month - 1 &&
27
+ date.getUTCDate() === day
28
+ );
29
+ }
28
30
 
29
31
  /**
30
- * Validates an ISO 8601 datetime string with timezone offset
32
+ * Validates an ISO 8601 datetime string
31
33
  */
32
- export const isoDateTimeSchema = v.pipe(v.string(), v.isoTimestamp());
34
+ export function isValidISODateTime(value: string): boolean {
35
+ if (!ISO_DATETIME_PATTERN.test(value)) return false;
36
+ const date = new Date(value);
37
+ return !Number.isNaN(date.getTime());
38
+ }
33
39
 
34
40
  /**
35
- * Validates a date range where start <= end
41
+ * Validates a time value (24-hour format)
36
42
  */
37
- export const dateRangeSchema = v.pipe(
38
- v.object({
39
- start: isoDateSchema,
40
- end: isoDateSchema,
41
- }),
42
- v.check((data) => {
43
- return data.start <= data.end;
44
- }, 'Start date must be before or equal to end date'),
45
- );
43
+ export function isValidTime(hours: number, minutes: number): boolean {
44
+ return (
45
+ Number.isInteger(hours) &&
46
+ Number.isInteger(minutes) &&
47
+ hours >= 0 &&
48
+ hours <= 23 &&
49
+ minutes >= 0 &&
50
+ minutes <= 59
51
+ );
52
+ }
46
53
 
47
54
  /**
48
- * Validates date picker configuration
55
+ * Validates a date range where start <= end
49
56
  */
50
- export const datePickerValueSchema = v.object({
51
- selectedDate: isoDateSchema,
52
- minDate: v.optional(isoDateSchema),
53
- maxDate: v.optional(isoDateSchema),
54
- disabledDates: v.optional(v.array(isoDateSchema)),
55
- });
57
+ export function isValidDateRange(start: string, end: string): boolean {
58
+ if (!isValidISODate(start) || !isValidISODate(end)) return false;
59
+ return start <= end;
60
+ }
56
61
 
57
62
  /**
58
63
  * Parses an ISO date string to a Date object
59
- * @deprecated Use parseISODate from date utils instead. Kept for backward compatibility.
64
+ * Returns null if invalid
60
65
  */
61
- export const dateSchema = v.pipe(
62
- v.string(),
63
- v.regex(/^\d{4}-\d{2}-\d{2}$/, 'Must be in YYYY-MM-DD format'),
64
- v.transform((val) => {
65
- const [year, month, day] = parseISOParts(val);
66
- return new Date(Date.UTC(year, month - 1, day));
67
- }),
68
- );
66
+ export function parseISODateSafe(value: string): Date | null {
67
+ if (!isValidISODate(value)) return null;
68
+ const [year, month, day] = value.split('-').map(Number) as [
69
+ number,
70
+ number,
71
+ number,
72
+ ];
73
+ return new Date(Date.UTC(year, month - 1, day));
74
+ }
69
75
 
70
- /**
71
- * @deprecated Alias for dateSchema for backward compatibility
72
- */
73
- export const temporalDateSchema = dateSchema;
76
+ // Types for date picker values
77
+ export interface DatePickerValue {
78
+ selectedDate: string;
79
+ minDate?: string;
80
+ maxDate?: string;
81
+ disabledDates?: string[];
82
+ }
83
+
84
+ export interface DateRange {
85
+ start: string;
86
+ end: string;
87
+ }
88
+
89
+ // Legacy schema exports for backward compatibility
90
+ // These are now simple validation functions, not valibot schemas
74
91
 
75
- export type DatePickerValue = v.InferOutput<typeof datePickerValueSchema>;
76
- export type DateRange = v.InferOutput<typeof dateRangeSchema>;
92
+ /** @deprecated Use isValidISODate() instead */
93
+ export const isoDateSchema = {
94
+ parse: (value: string) => {
95
+ if (!isValidISODate(value)) throw new Error('Invalid ISO date');
96
+ return value;
97
+ },
98
+ };
99
+
100
+ /** @deprecated Use isValidISODateTime() instead */
101
+ export const isoDateTimeSchema = {
102
+ parse: (value: string) => {
103
+ if (!isValidISODateTime(value)) throw new Error('Invalid ISO datetime');
104
+ return value;
105
+ },
106
+ };
107
+
108
+ /** @deprecated Use isValidDateRange() instead */
109
+ export const dateRangeSchema = {
110
+ parse: (value: DateRange) => {
111
+ if (!isValidDateRange(value.start, value.end))
112
+ throw new Error('Invalid date range');
113
+ return value;
114
+ },
115
+ };
116
+
117
+ /** @deprecated Use DatePickerValue type instead */
118
+ export const datePickerValueSchema = {
119
+ parse: (value: DatePickerValue) => value,
120
+ };
121
+
122
+ /** @deprecated Use parseISODateSafe() instead */
123
+ export const dateSchema = {
124
+ parse: (value: string) => {
125
+ const date = parseISODateSafe(value);
126
+ if (!date) throw new Error('Invalid date');
127
+ return date;
128
+ },
129
+ };
130
+
131
+ /** @deprecated Alias for dateSchema */
132
+ export const temporalDateSchema = dateSchema;
@@ -1,9 +0,0 @@
1
- "use strict";
2
-
3
- // Re-export theme configuration from @dreamstack-us/kaal-themes
4
- // This ensures the module augmentation is applied
5
- export { configureKaalThemes } from '@dreamstack-us/kaal-themes';
6
-
7
- // Import to ensure type augmentation is applied during build
8
- import '@dreamstack-us/kaal-themes';
9
- //# sourceMappingURL=unistyles.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["configureKaalThemes"],"sourceRoot":"../../src","sources":["unistyles.ts"],"mappings":";;AAAA;AACA;AACA,SAASA,mBAAmB,QAAQ,4BAA4B;;AAEhE;AACA,OAAO,4BAA4B","ignoreList":[]}
@@ -1,3 +0,0 @@
1
- export { configureKaalThemes } from '@dreamstack-us/kaal-themes';
2
- import '@dreamstack-us/kaal-themes';
3
- //# sourceMappingURL=unistyles.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"unistyles.d.ts","sourceRoot":"","sources":["../../src/unistyles.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGjE,OAAO,4BAA4B,CAAC"}