@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,40 @@
1
+ import * as v from 'valibot';
2
+ /**
3
+ * Validates an ISO 8601 date string (YYYY-MM-DD)
4
+ */
5
+ export declare const isoDateSchema: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Must be in YYYY-MM-DD format">, v.CheckAction<string, "Invalid ISO 8601 date format">]>;
6
+ /**
7
+ * Validates an ISO 8601 datetime string with timezone offset
8
+ */
9
+ export declare const isoDateTimeSchema: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, undefined>]>;
10
+ /**
11
+ * Validates a date range where start <= end
12
+ */
13
+ export declare const dateRangeSchema: v.SchemaWithPipe<readonly [v.ObjectSchema<{
14
+ readonly start: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Must be in YYYY-MM-DD format">, v.CheckAction<string, "Invalid ISO 8601 date format">]>;
15
+ readonly end: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Must be in YYYY-MM-DD format">, v.CheckAction<string, "Invalid ISO 8601 date format">]>;
16
+ }, undefined>, v.CheckAction<{
17
+ start: string;
18
+ end: string;
19
+ }, "Start date must be before or equal to end date">]>;
20
+ /**
21
+ * Validates date picker configuration
22
+ */
23
+ export declare const datePickerValueSchema: v.ObjectSchema<{
24
+ readonly selectedDate: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Must be in YYYY-MM-DD format">, v.CheckAction<string, "Invalid ISO 8601 date format">]>;
25
+ readonly minDate: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Must be in YYYY-MM-DD format">, v.CheckAction<string, "Invalid ISO 8601 date format">]>, undefined>;
26
+ readonly maxDate: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Must be in YYYY-MM-DD format">, v.CheckAction<string, "Invalid ISO 8601 date format">]>, undefined>;
27
+ readonly disabledDates: v.OptionalSchema<v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Must be in YYYY-MM-DD format">, v.CheckAction<string, "Invalid ISO 8601 date format">]>, undefined>, undefined>;
28
+ }, undefined>;
29
+ /**
30
+ * Parses an ISO date string to a Date object
31
+ * @deprecated Use parseISODate from date utils instead. Kept for backward compatibility.
32
+ */
33
+ export declare const dateSchema: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Must be in YYYY-MM-DD format">, v.TransformAction<string, Date>]>;
34
+ /**
35
+ * @deprecated Alias for dateSchema for backward compatibility
36
+ */
37
+ export declare const temporalDateSchema: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Must be in YYYY-MM-DD format">, v.TransformAction<string, Date>]>;
38
+ export type DatePickerValue = v.InferOutput<typeof datePickerValueSchema>;
39
+ export type DateRange = v.InferOutput<typeof dateRangeSchema>;
40
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAW7B;;GAEG;AACH,eAAO,MAAM,aAAa,sKAYzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,iGAAuC,CAAC;AAEtE;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;sDAQ3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;aAKhC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,UAAU,gJAOtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,gJAAa,CAAC;AAE7C,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC1E,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,eAAe,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "@dreamstack-us/kaal",
3
+ "version": "0.0.1",
4
+ "description": "High-performance React Native DatePicker with Unistyles v3",
5
+ "type": "module",
6
+ "main": "./lib/module/index.js",
7
+ "types": "./lib/typescript/index.d.ts",
8
+ "react-native": "./src/index.ts",
9
+ "source": "./src/index.ts",
10
+ "exports": {
11
+ ".": {
12
+ "source": "./src/index.ts",
13
+ "react-native": {
14
+ "types": "./lib/typescript/index.d.ts",
15
+ "default": "./lib/module/index.js"
16
+ },
17
+ "types": "./lib/typescript/index.d.ts",
18
+ "default": "./lib/module/index.js"
19
+ },
20
+ "./package.json": "./package.json"
21
+ },
22
+ "files": ["lib", "src", "!**/__tests__", "!**/*.test.*"],
23
+ "scripts": {
24
+ "build": "bob build",
25
+ "dev": "bob build --watch",
26
+ "typecheck": "tsc --noEmit",
27
+ "lint": "biome check src/",
28
+ "test": "bun test",
29
+ "clean": "rm -rf lib",
30
+ "size": "size-limit"
31
+ },
32
+ "dependencies": {
33
+ "valibot": "^1.0.0"
34
+ },
35
+ "peerDependencies": {
36
+ "react": ">=18.2.0",
37
+ "react-native": ">=0.78.0",
38
+ "react-native-unistyles": "^3.0.0",
39
+ "react-native-nitro-modules": "0.31.4",
40
+ "react-native-reanimated": ">=3.17.0",
41
+ "react-native-gesture-handler": ">=2.20.0",
42
+ "react-native-svg": ">=13.0.0"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "@expo/ui": {
46
+ "optional": true
47
+ }
48
+ },
49
+ "devDependencies": {
50
+ "@dreamstack-us/kaal-themes": "workspace:*",
51
+ "@types/react": "^18.3.0",
52
+ "react": "18.3.1",
53
+ "react-native": "0.78.0",
54
+ "react-native-builder-bob": "^0.35.0",
55
+ "react-native-gesture-handler": "^2.21.0",
56
+ "react-native-nitro-modules": "0.31.4",
57
+ "react-native-reanimated": "^3.17.0",
58
+ "react-native-svg": "^15.15.1",
59
+ "react-native-unistyles": "^3.0.0",
60
+ "typescript": "^5.7.0",
61
+ "typescript-config": "workspace:*"
62
+ },
63
+ "react-native-builder-bob": {
64
+ "source": "src",
65
+ "output": "lib",
66
+ "targets": [
67
+ [
68
+ "module",
69
+ {
70
+ "esm": true
71
+ }
72
+ ],
73
+ "typescript"
74
+ ],
75
+ "exclude": "**/{__tests__,__fixtures__,__mocks__}/**"
76
+ },
77
+ "size-limit": [
78
+ {
79
+ "path": "lib/module/index.js",
80
+ "limit": "25 kB"
81
+ }
82
+ ],
83
+ "publishConfig": {
84
+ "access": "public",
85
+ "registry": "https://registry.npmjs.org/"
86
+ },
87
+ "repository": {
88
+ "type": "git",
89
+ "url": "https://github.com/DreamStack-us/kaal.git",
90
+ "directory": "packages/core"
91
+ },
92
+ "keywords": [
93
+ "react-native",
94
+ "datepicker",
95
+ "timepicker",
96
+ "calendar",
97
+ "unistyles",
98
+ "new-architecture"
99
+ ],
100
+ "license": "MIT"
101
+ }
@@ -0,0 +1,44 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+
3
+ export const styles = StyleSheet.create((theme) => ({
4
+ container: {
5
+ backgroundColor: theme.colors.background.default,
6
+ borderRadius: theme.radii.card,
7
+ padding: theme.spacing(4),
8
+ },
9
+ header: {
10
+ flexDirection: 'row',
11
+ justifyContent: 'space-between',
12
+ alignItems: 'center',
13
+ marginBottom: theme.spacing(4),
14
+ paddingHorizontal: theme.spacing(2),
15
+ },
16
+ navButton: {
17
+ width: 40,
18
+ height: 40,
19
+ justifyContent: 'center',
20
+ alignItems: 'center',
21
+ borderRadius: theme.radii.button,
22
+ },
23
+ navText: {
24
+ fontSize: 24,
25
+ color: theme.colors.primary.default,
26
+ fontWeight: '600',
27
+ },
28
+ monthTitle: {
29
+ fontSize: theme.typography.monthHeader.fontSize,
30
+ fontWeight: theme.typography.monthHeader.fontWeight,
31
+ color: theme.colors.foreground.default,
32
+ },
33
+ weekDays: {
34
+ flexDirection: 'row',
35
+ marginBottom: theme.spacing(2),
36
+ },
37
+ weekDayText: {
38
+ flex: 1,
39
+ textAlign: 'center',
40
+ fontSize: theme.typography.dayHeader.fontSize,
41
+ fontWeight: theme.typography.dayHeader.fontWeight,
42
+ color: theme.colors.foreground.muted,
43
+ },
44
+ }));
@@ -0,0 +1,151 @@
1
+ import React, { memo, useCallback, useMemo } from 'react';
2
+ import { FlatList, Pressable, Text, View } from 'react-native';
3
+ import {
4
+ addMonths,
5
+ compareDates,
6
+ formatYearMonth,
7
+ getDayOfWeek,
8
+ getFirstDayOfMonth,
9
+ getMonthDays,
10
+ isSameDay,
11
+ today,
12
+ } from '../../utils/date';
13
+ import { styles } from './CalendarGrid.styles';
14
+ import { DayCell } from './DayCell';
15
+
16
+ interface CalendarGridProps {
17
+ value: Date;
18
+ onChange: (date: Date) => void;
19
+ minDate?: Date;
20
+ maxDate?: Date;
21
+ disabledDates?: Date[];
22
+ themeMode: 'ios' | 'android' | 'custom';
23
+ }
24
+
25
+ const CELL_SIZE = 44;
26
+ const WEEK_DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
27
+
28
+ const generateMonthDays = (currentMonth: Date): (Date | null)[] => {
29
+ const firstDay = getFirstDayOfMonth(currentMonth);
30
+ // getDayOfWeek returns 0 for Sunday, we want Monday = 0
31
+ const startOfWeek = getDayOfWeek(firstDay);
32
+ const paddingDays = startOfWeek === 0 ? 6 : startOfWeek - 1;
33
+
34
+ const days: (Date | null)[] = [];
35
+
36
+ for (let i = 0; i < paddingDays; i++) {
37
+ days.push(null);
38
+ }
39
+
40
+ const monthDays = getMonthDays(
41
+ currentMonth.getUTCFullYear(),
42
+ currentMonth.getUTCMonth(),
43
+ );
44
+ for (const day of monthDays) {
45
+ days.push(day);
46
+ }
47
+
48
+ const remaining = 7 - (days.length % 7);
49
+ if (remaining < 7) {
50
+ for (let i = 0; i < remaining; i++) {
51
+ days.push(null);
52
+ }
53
+ }
54
+
55
+ return days;
56
+ };
57
+
58
+ export const CalendarGrid: React.FC<CalendarGridProps> = memo(
59
+ ({ value, onChange, minDate, maxDate, disabledDates, themeMode }) => {
60
+ const [currentMonth, setCurrentMonth] = React.useState(() =>
61
+ getFirstDayOfMonth(value),
62
+ );
63
+
64
+ const days = useMemo(() => generateMonthDays(currentMonth), [currentMonth]);
65
+
66
+ const todayDate = useMemo(() => today(), []);
67
+
68
+ const isDisabled = useCallback(
69
+ (date: Date | null): boolean => {
70
+ if (!date) return true;
71
+ if (minDate && compareDates(date, minDate) < 0) return true;
72
+ if (maxDate && compareDates(date, maxDate) > 0) return true;
73
+ if (disabledDates?.some((d) => isSameDay(date, d))) return true;
74
+ return false;
75
+ },
76
+ [minDate, maxDate, disabledDates],
77
+ );
78
+
79
+ const navigateMonth = useCallback((direction: 1 | -1) => {
80
+ setCurrentMonth((prev) => addMonths(prev, direction));
81
+ }, []);
82
+
83
+ const renderDay = useCallback(
84
+ ({ item }: { item: Date | null }) => (
85
+ <DayCell
86
+ date={item}
87
+ isSelected={item ? isSameDay(item, value) : false}
88
+ isToday={item ? isSameDay(item, todayDate) : false}
89
+ isDisabled={isDisabled(item)}
90
+ isWeekend={
91
+ item ? getDayOfWeek(item) === 0 || getDayOfWeek(item) === 6 : false
92
+ }
93
+ onPress={item && !isDisabled(item) ? () => onChange(item) : undefined}
94
+ />
95
+ ),
96
+ [value, todayDate, isDisabled, onChange],
97
+ );
98
+
99
+ const keyExtractor = useCallback(
100
+ (item: Date | null, index: number) =>
101
+ item?.toISOString() ?? `empty-${index}`,
102
+ [],
103
+ );
104
+
105
+ const getItemLayout = useCallback(
106
+ (_data: ArrayLike<Date | null> | null | undefined, index: number) => ({
107
+ length: CELL_SIZE,
108
+ offset: CELL_SIZE * Math.floor(index / 7),
109
+ index,
110
+ }),
111
+ [],
112
+ );
113
+
114
+ return (
115
+ <View style={styles.container}>
116
+ <View style={styles.header}>
117
+ <Pressable onPress={() => navigateMonth(-1)} style={styles.navButton}>
118
+ <Text style={styles.navText}>‹</Text>
119
+ </Pressable>
120
+ <Text style={styles.monthTitle}>{formatYearMonth(currentMonth)}</Text>
121
+ <Pressable onPress={() => navigateMonth(1)} style={styles.navButton}>
122
+ <Text style={styles.navText}>›</Text>
123
+ </Pressable>
124
+ </View>
125
+
126
+ <View style={styles.weekDays}>
127
+ {WEEK_DAYS.map((day) => (
128
+ <Text key={day} style={styles.weekDayText}>
129
+ {day}
130
+ </Text>
131
+ ))}
132
+ </View>
133
+
134
+ <FlatList
135
+ data={days}
136
+ renderItem={renderDay}
137
+ keyExtractor={keyExtractor}
138
+ getItemLayout={getItemLayout}
139
+ numColumns={7}
140
+ scrollEnabled={false}
141
+ removeClippedSubviews={true}
142
+ maxToRenderPerBatch={14}
143
+ windowSize={3}
144
+ initialNumToRender={42}
145
+ />
146
+ </View>
147
+ );
148
+ },
149
+ );
150
+
151
+ CalendarGrid.displayName = 'CalendarGrid';
@@ -0,0 +1,108 @@
1
+ import type React from 'react';
2
+ import { memo } from 'react';
3
+ import { Pressable, Text } from 'react-native';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+
6
+ interface DayCellProps {
7
+ date: Date | null;
8
+ isSelected: boolean;
9
+ isToday: boolean;
10
+ isDisabled: boolean;
11
+ isWeekend: boolean;
12
+ onPress?: () => void;
13
+ }
14
+
15
+ const styles = StyleSheet.create((theme) => ({
16
+ cell: {
17
+ width: 44,
18
+ height: 44,
19
+ justifyContent: 'center',
20
+ alignItems: 'center',
21
+ backgroundColor: theme.colors.datepicker.cellBackground,
22
+ variants: {
23
+ state: {
24
+ selected: {
25
+ backgroundColor: theme.colors.datepicker.cellSelected,
26
+ borderRadius: theme.radii.cell,
27
+ },
28
+ today: {
29
+ backgroundColor: theme.colors.datepicker.cellToday,
30
+ borderRadius: theme.radii.cell,
31
+ borderWidth: 1,
32
+ borderColor: theme.colors.primary.default,
33
+ },
34
+ disabled: {
35
+ opacity: 0.4,
36
+ },
37
+ weekend: {},
38
+ },
39
+ },
40
+ },
41
+ text: {
42
+ fontSize: theme.typography.dayCell.fontSize,
43
+ fontWeight: theme.typography.dayCell.fontWeight,
44
+ color: theme.colors.datepicker.textDefault,
45
+ variants: {
46
+ state: {
47
+ selected: {
48
+ color: theme.colors.datepicker.textSelected,
49
+ fontWeight: '600',
50
+ },
51
+ today: {
52
+ color: theme.colors.primary.default,
53
+ fontWeight: '600',
54
+ },
55
+ disabled: {
56
+ color: theme.colors.datepicker.textDisabled,
57
+ },
58
+ weekend: {
59
+ color: theme.colors.datepicker.textWeekend,
60
+ },
61
+ },
62
+ },
63
+ },
64
+ }));
65
+
66
+ export const DayCell: React.FC<DayCellProps> = memo(
67
+ ({ date, isSelected, isToday, isDisabled, isWeekend, onPress }) => {
68
+ if (!date) {
69
+ return <Pressable style={styles.cell} disabled />;
70
+ }
71
+
72
+ const state = isDisabled
73
+ ? 'disabled'
74
+ : isSelected
75
+ ? 'selected'
76
+ : isToday
77
+ ? 'today'
78
+ : isWeekend
79
+ ? 'weekend'
80
+ : undefined;
81
+
82
+ styles.useVariants({ state });
83
+
84
+ return (
85
+ <Pressable
86
+ style={styles.cell}
87
+ onPress={onPress}
88
+ disabled={isDisabled}
89
+ accessibilityRole="button"
90
+ accessibilityLabel={new Intl.DateTimeFormat('en-US', {
91
+ weekday: 'long',
92
+ month: 'long',
93
+ day: 'numeric',
94
+ }).format(date)}
95
+ accessibilityState={{ selected: isSelected, disabled: isDisabled }}
96
+ >
97
+ <Text style={styles.text}>{date.getUTCDate()}</Text>
98
+ </Pressable>
99
+ );
100
+ },
101
+ (prev, next) =>
102
+ prev.date?.getTime() === next.date?.getTime() &&
103
+ prev.isSelected === next.isSelected &&
104
+ prev.isToday === next.isToday &&
105
+ prev.isDisabled === next.isDisabled,
106
+ );
107
+
108
+ DayCell.displayName = 'DayCell';
@@ -0,0 +1 @@
1
+ export { CalendarGrid } from './CalendarGrid';
@@ -0,0 +1,69 @@
1
+ import React, { Suspense, useCallback } from 'react';
2
+ import { ActivityIndicator, View } from 'react-native';
3
+ import { toISODateString } from '../../utils/date';
4
+ import { CalendarGrid } from '../CalendarGrid';
5
+ import type { KaalDatePickerProps } from './DatePicker';
6
+ import { styles } from './DatePicker.styles';
7
+
8
+ interface ExpoDatePickerProps {
9
+ value: Date;
10
+ onChange: (date: Date) => void;
11
+ }
12
+
13
+ // @ts-expect-error - React.lazy fallback returns null when @expo/ui unavailable
14
+ const ExpoDatePicker = React.lazy(async () => {
15
+ try {
16
+ // @ts-expect-error - @expo/ui types not available
17
+ const { DateTimePicker } = await import('@expo/ui/jetpack-compose');
18
+ return {
19
+ default: ({ value, onChange }: ExpoDatePickerProps) => (
20
+ <DateTimePicker
21
+ onDateSelected={onChange}
22
+ displayedComponents="date"
23
+ initialDate={toISODateString(value)}
24
+ variant="picker"
25
+ />
26
+ ),
27
+ };
28
+ } catch {
29
+ // Fallback when @expo/ui is not available
30
+ return { default: (_props: ExpoDatePickerProps) => null };
31
+ }
32
+ });
33
+
34
+ export const DatePicker: React.FC<KaalDatePickerProps> = ({
35
+ value,
36
+ onChange,
37
+ theme = 'native',
38
+ minDate,
39
+ maxDate,
40
+ disabledDates,
41
+ }) => {
42
+ const handleDateChange = useCallback(
43
+ (date: Date) => {
44
+ onChange(date);
45
+ },
46
+ [onChange],
47
+ );
48
+
49
+ if (theme === 'native') {
50
+ return (
51
+ <View style={styles.container}>
52
+ <Suspense fallback={<ActivityIndicator />}>
53
+ <ExpoDatePicker value={value} onChange={handleDateChange} />
54
+ </Suspense>
55
+ </View>
56
+ );
57
+ }
58
+
59
+ return (
60
+ <CalendarGrid
61
+ value={value}
62
+ onChange={onChange}
63
+ minDate={minDate}
64
+ maxDate={maxDate}
65
+ disabledDates={disabledDates}
66
+ themeMode={theme}
67
+ />
68
+ );
69
+ };
@@ -0,0 +1,78 @@
1
+ import React, { Suspense, useCallback } from 'react';
2
+ import { ActivityIndicator, View } from 'react-native';
3
+ import { toISODateString } from '../../utils/date';
4
+ import { CalendarGrid } from '../CalendarGrid';
5
+ import type { KaalDatePickerProps } from './DatePicker';
6
+ import { styles } from './DatePicker.styles';
7
+
8
+ interface ExpoDatePickerProps {
9
+ value: Date;
10
+ onChange: (date: Date) => void;
11
+ variant?: string;
12
+ }
13
+
14
+ // @ts-expect-error - React.lazy fallback returns null when @expo/ui unavailable
15
+ const ExpoDatePicker = React.lazy(async () => {
16
+ try {
17
+ // @ts-expect-error - @expo/ui types not available
18
+ const { DateTimePicker, Host } = await import('@expo/ui/swift-ui');
19
+ return {
20
+ default: ({ value, onChange, variant }: ExpoDatePickerProps) => (
21
+ <Host matchContents>
22
+ <DateTimePicker
23
+ onDateSelected={onChange}
24
+ displayedComponents="date"
25
+ initialDate={toISODateString(value)}
26
+ variant={variant || 'wheel'}
27
+ />
28
+ </Host>
29
+ ),
30
+ };
31
+ } catch {
32
+ // Fallback when @expo/ui is not available
33
+ return { default: (_props: ExpoDatePickerProps) => null };
34
+ }
35
+ });
36
+
37
+ export const DatePicker: React.FC<KaalDatePickerProps> = ({
38
+ value,
39
+ onChange,
40
+ mode = 'date',
41
+ theme = 'native',
42
+ variant = 'wheel',
43
+ minDate,
44
+ maxDate,
45
+ disabledDates,
46
+ }) => {
47
+ const handleDateChange = useCallback(
48
+ (date: Date) => {
49
+ onChange(date);
50
+ },
51
+ [onChange],
52
+ );
53
+
54
+ if (theme === 'native') {
55
+ return (
56
+ <View style={styles.container}>
57
+ <Suspense fallback={<ActivityIndicator />}>
58
+ <ExpoDatePicker
59
+ value={value}
60
+ onChange={handleDateChange}
61
+ variant={variant}
62
+ />
63
+ </Suspense>
64
+ </View>
65
+ );
66
+ }
67
+
68
+ return (
69
+ <CalendarGrid
70
+ value={value}
71
+ onChange={onChange}
72
+ minDate={minDate}
73
+ maxDate={maxDate}
74
+ disabledDates={disabledDates}
75
+ themeMode={theme}
76
+ />
77
+ );
78
+ };
@@ -0,0 +1,35 @@
1
+ import { Platform } from 'react-native';
2
+ import { StyleSheet, type UnistylesVariants } from 'react-native-unistyles';
3
+
4
+ export const styles = StyleSheet.create((theme) => ({
5
+ container: {
6
+ backgroundColor: theme.colors.background.default,
7
+ borderRadius: theme.radii.card,
8
+ overflow: 'hidden',
9
+ variants: {
10
+ size: {
11
+ compact: {
12
+ padding: theme.spacing(2),
13
+ },
14
+ default: {
15
+ padding: theme.spacing(4),
16
+ },
17
+ large: {
18
+ padding: theme.spacing(6),
19
+ },
20
+ },
21
+ },
22
+ },
23
+ backdrop: Platform.select({
24
+ web: {
25
+ backdropFilter: 'blur(20px) saturate(180%)',
26
+ WebkitBackdropFilter: 'blur(20px) saturate(180%)',
27
+ backgroundColor: 'rgba(255, 255, 255, 0.7)',
28
+ },
29
+ default: {
30
+ backgroundColor: theme.colors.background.elevated,
31
+ },
32
+ }),
33
+ }));
34
+
35
+ export type DatePickerVariants = UnistylesVariants<typeof styles>;
@@ -0,0 +1,21 @@
1
+ import type React from 'react';
2
+ import type { DatePickerMode } from '../../types';
3
+
4
+ export interface KaalDatePickerProps {
5
+ value: Date;
6
+ onChange: (date: Date) => void;
7
+ mode?: DatePickerMode;
8
+ theme?: 'native' | 'ios' | 'android' | 'custom';
9
+ variant?: 'wheel' | 'calendar' | 'compact';
10
+ minDate?: Date;
11
+ maxDate?: Date;
12
+ disabledDates?: Date[];
13
+ locale?: string;
14
+ }
15
+
16
+ // Platform-specific implementations are handled by Metro's file resolution
17
+ // This file serves as the type definition and fallback
18
+ export const DatePicker: React.FC<KaalDatePickerProps> = (_props) => {
19
+ // This should never be reached - Metro resolves platform-specific files
20
+ return null;
21
+ };
@@ -0,0 +1,36 @@
1
+ import type React from 'react';
2
+ import { CalendarGrid } from '../CalendarGrid';
3
+ import { WheelPicker } from '../WheelPicker';
4
+ import type { KaalDatePickerProps } from './DatePicker';
5
+
6
+ export const DatePicker: React.FC<KaalDatePickerProps> = ({
7
+ value,
8
+ onChange,
9
+ theme = 'ios',
10
+ variant = 'calendar',
11
+ minDate,
12
+ maxDate,
13
+ disabledDates,
14
+ }) => {
15
+ if (theme === 'ios' && variant === 'wheel') {
16
+ return (
17
+ <WheelPicker
18
+ value={value}
19
+ onChange={onChange}
20
+ minDate={minDate}
21
+ maxDate={maxDate}
22
+ />
23
+ );
24
+ }
25
+
26
+ return (
27
+ <CalendarGrid
28
+ value={value}
29
+ onChange={onChange}
30
+ minDate={minDate}
31
+ maxDate={maxDate}
32
+ disabledDates={disabledDates}
33
+ themeMode={theme === 'native' ? 'ios' : theme}
34
+ />
35
+ );
36
+ };