@dreamstack-us/kaal 0.0.2 → 0.0.3

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 (45) hide show
  1. package/README.md +165 -0
  2. package/lib/module/components/CalendarGrid/CalendarGrid.js +80 -18
  3. package/lib/module/components/CalendarGrid/CalendarGrid.js.map +1 -1
  4. package/lib/module/components/CalendarGrid/CalendarGrid.web.js +80 -18
  5. package/lib/module/components/CalendarGrid/CalendarGrid.web.js.map +1 -1
  6. package/lib/module/components/CalendarGrid/DayCell.js +17 -5
  7. package/lib/module/components/CalendarGrid/DayCell.js.map +1 -1
  8. package/lib/module/components/CalendarGrid/DayCell.web.js +17 -5
  9. package/lib/module/components/CalendarGrid/DayCell.web.js.map +1 -1
  10. package/lib/module/components/DatePicker/DatePicker.android.js +42 -19
  11. package/lib/module/components/DatePicker/DatePicker.android.js.map +1 -1
  12. package/lib/module/components/DatePicker/DatePicker.ios.js +44 -21
  13. package/lib/module/components/DatePicker/DatePicker.ios.js.map +1 -1
  14. package/lib/module/components/DatePicker/DatePicker.js.map +1 -1
  15. package/lib/module/components/DatePicker/DatePicker.web.js +40 -16
  16. package/lib/module/components/DatePicker/DatePicker.web.js.map +1 -1
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts +19 -7
  19. package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts.map +1 -1
  20. package/lib/typescript/components/CalendarGrid/CalendarGrid.web.d.ts +19 -7
  21. package/lib/typescript/components/CalendarGrid/CalendarGrid.web.d.ts.map +1 -1
  22. package/lib/typescript/components/CalendarGrid/DayCell.d.ts +3 -0
  23. package/lib/typescript/components/CalendarGrid/DayCell.d.ts.map +1 -1
  24. package/lib/typescript/components/CalendarGrid/DayCell.web.d.ts +3 -0
  25. package/lib/typescript/components/CalendarGrid/DayCell.web.d.ts.map +1 -1
  26. package/lib/typescript/components/DatePicker/DatePicker.android.d.ts.map +1 -1
  27. package/lib/typescript/components/DatePicker/DatePicker.d.ts +20 -8
  28. package/lib/typescript/components/DatePicker/DatePicker.d.ts.map +1 -1
  29. package/lib/typescript/components/DatePicker/DatePicker.ios.d.ts.map +1 -1
  30. package/lib/typescript/components/DatePicker/DatePicker.web.d.ts.map +1 -1
  31. package/lib/typescript/index.d.ts +1 -1
  32. package/lib/typescript/index.d.ts.map +1 -1
  33. package/lib/typescript/types/datepicker.d.ts +37 -3
  34. package/lib/typescript/types/datepicker.d.ts.map +1 -1
  35. package/package.json +1 -1
  36. package/src/components/CalendarGrid/CalendarGrid.tsx +216 -122
  37. package/src/components/CalendarGrid/CalendarGrid.web.tsx +227 -136
  38. package/src/components/CalendarGrid/DayCell.tsx +48 -6
  39. package/src/components/CalendarGrid/DayCell.web.tsx +48 -6
  40. package/src/components/DatePicker/DatePicker.android.tsx +39 -21
  41. package/src/components/DatePicker/DatePicker.ios.tsx +42 -24
  42. package/src/components/DatePicker/DatePicker.tsx +28 -8
  43. package/src/components/DatePicker/DatePicker.web.tsx +40 -16
  44. package/src/index.ts +2 -0
  45. package/src/types/datepicker.ts +43 -3
@@ -1 +1 @@
1
- {"version":3,"file":"DatePicker.ios.d.ts","sourceRoot":"","sources":["../../../../src/components/DatePicker/DatePicker.ios.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAKrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAgCxD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA8CpD,CAAC"}
1
+ {"version":3,"file":"DatePicker.ios.d.ts","sourceRoot":"","sources":["../../../../src/components/DatePicker/DatePicker.ios.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAKxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAgCxD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAgEpD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"DatePicker.web.d.ts","sourceRoot":"","sources":["../../../../src/components/DatePicker/DatePicker.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAqCpD,CAAC"}
1
+ {"version":3,"file":"DatePicker.web.d.ts","sourceRoot":"","sources":["../../../../src/components/DatePicker/DatePicker.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA6DpD,CAAC"}
@@ -7,7 +7,7 @@ export { useTimePicker, to12Hour, to24Hour, formatTime, snapToInterval, } from '
7
7
  export { toISODateString, toISODateTimeString, parseISODate, parseISODateTime, fromISODateString, fromISODateTimeString, getDateRange, isDateInRange, getUserTimezone, addDays, addMonths, compareDates, isSameDay, isSameMonth, getMonthDays, getFirstDayOfMonth, getLastDayOfMonth, getDayOfWeek, formatMonth, formatWeekday, formatYearMonth, today, } from './utils';
8
8
  export { isoDateSchema, isoDateTimeSchema, dateRangeSchema, datePickerValueSchema, dateSchema, temporalDateSchema, } from './utils';
9
9
  export type { DatePickerValue, DateRange } from './utils';
10
- export type { DatePickerMode, DatePickerTheme, DatePickerVariant, DatePickerProps, DatePickerThemeOverrides, } from './types';
10
+ export type { DatePickerMode, DatePickerTheme, DatePickerVariant, DatePickerSelectionMode, DatePickerProps, DatePickerThemeOverrides, DateRange as DatePickerDateRange, } from './types';
11
11
  export type { TimePeriod, ClockMode, Time12Hour, MinuteInterval, TimePickerTheme, TimePickerThemeOverrides, } from './types';
12
12
  export { ThemeOverrideProvider, useDatePickerOverrides, useTimePickerOverrides, useThemeOverrides, } from './context/ThemeOverrideContext';
13
13
  export type { ThemeOverrideContextValue } from './context/ThemeOverrideContext';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EACL,UAAU,EACV,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,SAAS,GACf,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,OAAO,EACP,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,aAAa,EACb,eAAe,EACf,KAAK,GACN,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC1D,YAAY,EACV,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,wBAAwB,GACzB,MAAM,SAAS,CAAC;AAGjB,YAAY,EACV,UAAU,EACV,SAAS,EACT,UAAU,EACV,cAAc,EACd,eAAe,EACf,wBAAwB,GACzB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EACL,UAAU,EACV,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,SAAS,GACf,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EACL,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,OAAO,EACP,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,aAAa,EACb,eAAe,EACf,KAAK,GACN,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC1D,YAAY,EACV,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,wBAAwB,EACxB,SAAS,IAAI,mBAAmB,GACjC,MAAM,SAAS,CAAC;AAGjB,YAAY,EACV,UAAU,EACV,SAAS,EACT,UAAU,EACV,cAAc,EACd,eAAe,EACf,wBAAwB,GACzB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC"}
@@ -1,6 +1,11 @@
1
1
  export type DatePickerMode = 'date' | 'time' | 'datetime';
2
2
  export type DatePickerTheme = 'native' | 'ios' | 'android' | 'custom';
3
3
  export type DatePickerVariant = 'wheel' | 'calendar' | 'compact';
4
+ export type DatePickerSelectionMode = 'single' | 'range';
5
+ export interface DateRange {
6
+ startDate: Date;
7
+ endDate: Date | null;
8
+ }
4
9
  /**
5
10
  * Theme overrides for DatePicker components.
6
11
  * These allow customizing colors without matching Kaal's internal theme structure.
@@ -30,10 +35,15 @@ export interface DatePickerThemeOverrides {
30
35
  cellBorderRadius?: number;
31
36
  /** Padding for calendar container */
32
37
  padding?: number;
38
+ /** Background color for dates in range (between start and end) */
39
+ cellInRangeColor?: string;
40
+ /** Text color for dates in range */
41
+ textInRangeColor?: string;
33
42
  }
34
- export interface DatePickerProps {
35
- value: Date;
36
- onChange: (date: Date) => void;
43
+ /**
44
+ * Base props shared between single and range selection modes
45
+ */
46
+ interface DatePickerBaseProps {
37
47
  mode?: DatePickerMode;
38
48
  theme?: DatePickerTheme;
39
49
  variant?: DatePickerVariant;
@@ -53,4 +63,28 @@ export interface DatePickerProps {
53
63
  /** Custom theme overrides for styling without matching Kaal's theme structure */
54
64
  themeOverrides?: DatePickerThemeOverrides;
55
65
  }
66
+ /**
67
+ * Props for single date selection mode (default)
68
+ */
69
+ interface DatePickerSingleProps extends DatePickerBaseProps {
70
+ selectionMode?: 'single';
71
+ value: Date;
72
+ onChange: (date: Date) => void;
73
+ startDate?: never;
74
+ endDate?: never;
75
+ onRangeChange?: never;
76
+ }
77
+ /**
78
+ * Props for range selection mode
79
+ */
80
+ interface DatePickerRangeProps extends DatePickerBaseProps {
81
+ selectionMode: 'range';
82
+ startDate: Date | null;
83
+ endDate: Date | null;
84
+ onRangeChange: (range: DateRange) => void;
85
+ value?: never;
86
+ onChange?: never;
87
+ }
88
+ export type DatePickerProps = DatePickerSingleProps | DatePickerRangeProps;
89
+ export {};
56
90
  //# sourceMappingURL=datepicker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"datepicker.d.ts","sourceRoot":"","sources":["../../../src/types/datepicker.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtE,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IAEvC,6CAA6C;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8BAA8B;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iCAAiC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC/B,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACrB,iFAAiF;IACjF,cAAc,CAAC,EAAE,wBAAwB,CAAC;CAC3C"}
1
+ {"version":3,"file":"datepicker.d.ts","sourceRoot":"","sources":["../../../src/types/datepicker.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtE,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;AAEjE,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IAEvC,6CAA6C;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8BAA8B;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iCAAiC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oCAAoC;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,UAAU,mBAAmB;IAC3B,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACrB,iFAAiF;IACjF,cAAc,CAAC,EAAE,wBAAwB,CAAC;CAC3C;AAED;;GAEG;AACH,UAAU,qBAAsB,SAAQ,mBAAmB;IACzD,aAAa,CAAC,EAAE,QAAQ,CAAC;IACzB,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAE/B,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB;AAED;;GAEG;AACH,UAAU,oBAAqB,SAAQ,mBAAmB;IACxD,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,aAAa,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAE1C,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AAED,MAAM,MAAM,eAAe,GAAG,qBAAqB,GAAG,oBAAoB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamstack-us/kaal",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "High-performance React Native DatePicker with Unistyles v3",
5
5
  "type": "module",
6
6
  "main": "./lib/module/index.js",
@@ -14,9 +14,9 @@ import {
14
14
  import { styles } from './CalendarGrid.styles';
15
15
  import { DayCell } from './DayCell';
16
16
 
17
- interface CalendarGridProps {
18
- value: Date;
19
- onChange: (date: Date) => void;
17
+ import type { DateRange } from '../../types/datepicker';
18
+
19
+ interface CalendarGridBaseProps {
20
20
  minDate?: Date;
21
21
  maxDate?: Date;
22
22
  disabledDates?: Date[];
@@ -24,14 +24,30 @@ interface CalendarGridProps {
24
24
  /**
25
25
  * First day of the week: 0 = Sunday, 1 = Monday
26
26
  * @default 0 (Sunday)
27
- *
28
- * TODO: This is a temporary solution. In the future, we need to add full
29
- * locale support to handle different calendar formats, layouts, and
30
- * localized day/month names across different regions.
31
27
  */
32
28
  weekStartsOn?: 0 | 1;
33
29
  }
34
30
 
31
+ interface CalendarGridSingleProps extends CalendarGridBaseProps {
32
+ selectionMode?: 'single';
33
+ value: Date;
34
+ onChange: (date: Date) => void;
35
+ startDate?: never;
36
+ endDate?: never;
37
+ onRangeChange?: never;
38
+ }
39
+
40
+ interface CalendarGridRangeProps extends CalendarGridBaseProps {
41
+ selectionMode: 'range';
42
+ startDate: Date | null;
43
+ endDate: Date | null;
44
+ onRangeChange: (range: DateRange) => void;
45
+ value?: never;
46
+ onChange?: never;
47
+ }
48
+
49
+ type CalendarGridProps = CalendarGridSingleProps | CalendarGridRangeProps;
50
+
35
51
  const CELL_SIZE = 44;
36
52
 
37
53
  // Week day labels starting from Sunday
@@ -100,138 +116,216 @@ const generateMonthDays = (
100
116
  return days;
101
117
  };
102
118
 
103
- export const CalendarGrid: React.FC<CalendarGridProps> = memo(
104
- ({
105
- value,
106
- onChange,
119
+ export const CalendarGrid: React.FC<CalendarGridProps> = memo((props) => {
120
+ const {
107
121
  minDate,
108
122
  maxDate,
109
123
  disabledDates,
110
124
  themeMode,
111
125
  weekStartsOn = 0,
112
- }) => {
113
- const overrides = useDatePickerOverrides();
114
- const [currentMonth, setCurrentMonth] = React.useState(() =>
115
- getFirstDayOfMonth(value),
116
- );
117
-
118
- const days = useMemo(
119
- () => generateMonthDays(currentMonth, weekStartsOn),
120
- [currentMonth, weekStartsOn],
121
- );
122
-
123
- const weekDays =
124
- weekStartsOn === 0 ? WEEK_DAYS_SUNDAY_START : WEEK_DAYS_MONDAY_START;
125
-
126
- const todayDate = useMemo(() => today(), []);
127
-
128
- const isDisabled = useCallback(
129
- (date: Date | null): boolean => {
130
- if (!date) return true;
131
- if (minDate && compareDates(date, minDate) < 0) return true;
132
- if (maxDate && compareDates(date, maxDate) > 0) return true;
133
- if (disabledDates?.some((d) => isSameDay(date, d))) return true;
126
+ selectionMode = 'single',
127
+ } = props;
128
+
129
+ // Extract mode-specific props
130
+ const singleValue = selectionMode === 'single' ? props.value : null;
131
+ const singleOnChange = selectionMode === 'single' ? props.onChange : null;
132
+ const rangeStart = selectionMode === 'range' ? props.startDate : null;
133
+ const rangeEnd = selectionMode === 'range' ? props.endDate : null;
134
+ const rangeOnChange = selectionMode === 'range' ? props.onRangeChange : null;
135
+
136
+ const overrides = useDatePickerOverrides();
137
+ const [currentMonth, setCurrentMonth] = React.useState(() =>
138
+ getFirstDayOfMonth(singleValue ?? rangeStart ?? new Date()),
139
+ );
140
+
141
+ const days = useMemo(
142
+ () => generateMonthDays(currentMonth, weekStartsOn),
143
+ [currentMonth, weekStartsOn],
144
+ );
145
+
146
+ const weekDays =
147
+ weekStartsOn === 0 ? WEEK_DAYS_SUNDAY_START : WEEK_DAYS_MONDAY_START;
148
+
149
+ const todayDate = useMemo(() => today(), []);
150
+
151
+ const isDisabled = useCallback(
152
+ (date: Date | null): boolean => {
153
+ if (!date) return true;
154
+ if (minDate && compareDates(date, minDate) < 0) return true;
155
+ if (maxDate && compareDates(date, maxDate) > 0) return true;
156
+ if (disabledDates?.some((d) => isSameDay(date, d))) return true;
157
+ return false;
158
+ },
159
+ [minDate, maxDate, disabledDates],
160
+ );
161
+
162
+ const navigateMonth = useCallback((direction: 1 | -1) => {
163
+ setCurrentMonth((prev) => addMonths(prev, direction));
164
+ }, []);
165
+
166
+ // Handle date press for both single and range modes
167
+ const handleDatePress = useCallback(
168
+ (date: Date) => {
169
+ if (selectionMode === 'single' && singleOnChange) {
170
+ singleOnChange(date);
171
+ } else if (selectionMode === 'range' && rangeOnChange) {
172
+ // Range selection logic:
173
+ // 1. If no start date, set start date
174
+ // 2. If start date but no end date, set end date (if after start)
175
+ // 3. If both dates exist, reset to new start date
176
+ if (!rangeStart || (rangeStart && rangeEnd)) {
177
+ rangeOnChange({ startDate: date, endDate: null });
178
+ } else {
179
+ // Have start but no end
180
+ if (compareDates(date, rangeStart) < 0) {
181
+ // Clicked before start - make this the new start
182
+ rangeOnChange({ startDate: date, endDate: null });
183
+ } else if (isSameDay(date, rangeStart)) {
184
+ // Clicked same day - clear selection
185
+ rangeOnChange({ startDate: date, endDate: null });
186
+ } else {
187
+ // Clicked after start - set as end date
188
+ rangeOnChange({ startDate: rangeStart, endDate: date });
189
+ }
190
+ }
191
+ }
192
+ },
193
+ [selectionMode, singleOnChange, rangeOnChange, rangeStart, rangeEnd],
194
+ );
195
+
196
+ // Check if date is in range (between start and end)
197
+ const isDateInRange = useCallback(
198
+ (date: Date | null): boolean => {
199
+ if (!date || selectionMode !== 'range' || !rangeStart || !rangeEnd) {
134
200
  return false;
135
- },
136
- [minDate, maxDate, disabledDates],
137
- );
201
+ }
202
+ return (
203
+ compareDates(date, rangeStart) > 0 && compareDates(date, rangeEnd) < 0
204
+ );
205
+ },
206
+ [selectionMode, rangeStart, rangeEnd],
207
+ );
138
208
 
139
- const navigateMonth = useCallback((direction: 1 | -1) => {
140
- setCurrentMonth((prev) => addMonths(prev, direction));
141
- }, []);
209
+ const renderDay = useCallback(
210
+ ({ item }: { item: Date | null }) => {
211
+ const isRangeStart =
212
+ selectionMode === 'range' && item && rangeStart
213
+ ? isSameDay(item, rangeStart)
214
+ : false;
215
+ const isRangeEnd =
216
+ selectionMode === 'range' && item && rangeEnd
217
+ ? isSameDay(item, rangeEnd)
218
+ : false;
219
+ const isSelected =
220
+ selectionMode === 'single' && item && singleValue
221
+ ? isSameDay(item, singleValue)
222
+ : false;
142
223
 
143
- const renderDay = useCallback(
144
- ({ item }: { item: Date | null }) => (
224
+ return (
145
225
  <DayCell
146
226
  date={item}
147
- isSelected={item ? isSameDay(item, value) : false}
227
+ isSelected={isSelected}
148
228
  isToday={item ? isSameDay(item, todayDate) : false}
149
229
  isDisabled={isDisabled(item)}
150
230
  isWeekend={
151
231
  item ? getDayOfWeek(item) === 0 || getDayOfWeek(item) === 6 : false
152
232
  }
153
- onPress={item && !isDisabled(item) ? () => onChange(item) : undefined}
233
+ isRangeStart={isRangeStart}
234
+ isRangeEnd={isRangeEnd}
235
+ isInRange={isDateInRange(item)}
236
+ onPress={
237
+ item && !isDisabled(item) ? () => handleDatePress(item) : undefined
238
+ }
154
239
  />
155
- ),
156
- [value, todayDate, isDisabled, onChange],
157
- );
158
-
159
- const keyExtractor = useCallback(
160
- (item: Date | null, index: number) =>
161
- item?.toISOString() ?? `empty-${index}`,
162
- [],
163
- );
164
-
165
- const getItemLayout = useCallback(
166
- (_data: ArrayLike<Date | null> | null | undefined, index: number) => ({
167
- length: CELL_SIZE,
168
- offset: CELL_SIZE * Math.floor(index / 7),
169
- index,
170
- }),
171
- [],
172
- );
173
-
174
- // Build override styles from themeOverrides
175
- const containerStyle = useMemo(
176
- () => ({
177
- backgroundColor: overrides?.backgroundColor ?? '#1E1E1E',
178
- borderRadius: overrides?.borderRadius ?? 16,
179
- padding: overrides?.padding ?? 16,
180
- }),
181
- [overrides],
182
- );
183
-
184
- const navTextStyle = useMemo(
185
- () => ({
186
- color: overrides?.primaryColor ?? '#4DA6FF',
187
- }),
188
- [overrides],
189
- );
190
-
191
- const monthTitleStyle = useMemo(
192
- () => ({
193
- color: overrides?.textColor ?? '#FFFFFF',
194
- }),
195
- [overrides],
196
- );
197
-
198
- return (
199
- <View style={[styles.container, containerStyle]}>
200
- <View style={styles.header}>
201
- <Pressable onPress={() => navigateMonth(-1)} style={styles.navButton}>
202
- <Text style={[styles.navText, navTextStyle]}>‹</Text>
203
- </Pressable>
204
- <Text style={[styles.monthTitle, monthTitleStyle]}>
205
- {formatYearMonth(currentMonth)}
240
+ );
241
+ },
242
+ [
243
+ selectionMode,
244
+ singleValue,
245
+ rangeStart,
246
+ rangeEnd,
247
+ todayDate,
248
+ isDisabled,
249
+ isDateInRange,
250
+ handleDatePress,
251
+ ],
252
+ );
253
+
254
+ const keyExtractor = useCallback(
255
+ (item: Date | null, index: number) =>
256
+ item?.toISOString() ?? `empty-${index}`,
257
+ [],
258
+ );
259
+
260
+ const getItemLayout = useCallback(
261
+ (_data: ArrayLike<Date | null> | null | undefined, index: number) => ({
262
+ length: CELL_SIZE,
263
+ offset: CELL_SIZE * Math.floor(index / 7),
264
+ index,
265
+ }),
266
+ [],
267
+ );
268
+
269
+ // Build override styles from themeOverrides
270
+ const containerStyle = useMemo(
271
+ () => ({
272
+ backgroundColor: overrides?.backgroundColor ?? '#1E1E1E',
273
+ borderRadius: overrides?.borderRadius ?? 16,
274
+ padding: overrides?.padding ?? 16,
275
+ }),
276
+ [overrides],
277
+ );
278
+
279
+ const navTextStyle = useMemo(
280
+ () => ({
281
+ color: overrides?.primaryColor ?? '#4DA6FF',
282
+ }),
283
+ [overrides],
284
+ );
285
+
286
+ const monthTitleStyle = useMemo(
287
+ () => ({
288
+ color: overrides?.textColor ?? '#FFFFFF',
289
+ }),
290
+ [overrides],
291
+ );
292
+
293
+ return (
294
+ <View style={[styles.container, containerStyle]}>
295
+ <View style={styles.header}>
296
+ <Pressable onPress={() => navigateMonth(-1)} style={styles.navButton}>
297
+ <Text style={[styles.navText, navTextStyle]}>‹</Text>
298
+ </Pressable>
299
+ <Text style={[styles.monthTitle, monthTitleStyle]}>
300
+ {formatYearMonth(currentMonth)}
301
+ </Text>
302
+ <Pressable onPress={() => navigateMonth(1)} style={styles.navButton}>
303
+ <Text style={[styles.navText, navTextStyle]}>›</Text>
304
+ </Pressable>
305
+ </View>
306
+
307
+ <View style={styles.weekDays}>
308
+ {weekDays.map((day) => (
309
+ <Text key={day} style={styles.weekDayText}>
310
+ {day}
206
311
  </Text>
207
- <Pressable onPress={() => navigateMonth(1)} style={styles.navButton}>
208
- <Text style={[styles.navText, navTextStyle]}>›</Text>
209
- </Pressable>
210
- </View>
211
-
212
- <View style={styles.weekDays}>
213
- {weekDays.map((day) => (
214
- <Text key={day} style={styles.weekDayText}>
215
- {day}
216
- </Text>
217
- ))}
218
- </View>
219
-
220
- <FlatList
221
- data={days}
222
- renderItem={renderDay}
223
- keyExtractor={keyExtractor}
224
- getItemLayout={getItemLayout}
225
- numColumns={7}
226
- scrollEnabled={false}
227
- removeClippedSubviews={true}
228
- maxToRenderPerBatch={14}
229
- windowSize={3}
230
- initialNumToRender={42}
231
- />
312
+ ))}
232
313
  </View>
233
- );
234
- },
235
- );
314
+
315
+ <FlatList
316
+ data={days}
317
+ renderItem={renderDay}
318
+ keyExtractor={keyExtractor}
319
+ getItemLayout={getItemLayout}
320
+ numColumns={7}
321
+ scrollEnabled={false}
322
+ removeClippedSubviews={true}
323
+ maxToRenderPerBatch={14}
324
+ windowSize={3}
325
+ initialNumToRender={42}
326
+ />
327
+ </View>
328
+ );
329
+ });
236
330
 
237
331
  CalendarGrid.displayName = 'CalendarGrid';