react-native-ruler-date-picker 0.1.0 → 0.2.0

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 (220) hide show
  1. package/README.md +186 -60
  2. package/lib/commonjs/LensTick.js +83 -0
  3. package/lib/commonjs/LensTick.js.map +1 -0
  4. package/lib/commonjs/Ruler.js +38 -0
  5. package/lib/commonjs/Ruler.js.map +1 -0
  6. package/lib/commonjs/RulerContext.js +17 -0
  7. package/lib/commonjs/RulerContext.js.map +1 -0
  8. package/lib/commonjs/RulerDatePicker.js +38 -351
  9. package/lib/commonjs/RulerDatePicker.js.map +1 -1
  10. package/lib/commonjs/RulerDateRangePicker.js +56 -0
  11. package/lib/commonjs/RulerDateRangePicker.js.map +1 -0
  12. package/lib/commonjs/RulerHeader.js +106 -0
  13. package/lib/commonjs/RulerHeader.js.map +1 -0
  14. package/lib/commonjs/RulerIndicator.js +67 -0
  15. package/lib/commonjs/RulerIndicator.js.map +1 -0
  16. package/lib/commonjs/RulerLensTicks.js +55 -0
  17. package/lib/commonjs/RulerLensTicks.js.map +1 -0
  18. package/lib/commonjs/RulerLensTrack.js +52 -0
  19. package/lib/commonjs/RulerLensTrack.js.map +1 -0
  20. package/lib/commonjs/RulerRange.js +39 -0
  21. package/lib/commonjs/RulerRange.js.map +1 -0
  22. package/lib/commonjs/RulerRangeContext.js +17 -0
  23. package/lib/commonjs/RulerRangeContext.js.map +1 -0
  24. package/lib/commonjs/RulerRangeHeader.js +154 -0
  25. package/lib/commonjs/RulerRangeHeader.js.map +1 -0
  26. package/lib/commonjs/RulerRangeHighlight.js +50 -0
  27. package/lib/commonjs/RulerRangeHighlight.js.map +1 -0
  28. package/lib/commonjs/RulerRangeIndicator.js +71 -0
  29. package/lib/commonjs/RulerRangeIndicator.js.map +1 -0
  30. package/lib/commonjs/RulerRangeRoot.js +80 -0
  31. package/lib/commonjs/RulerRangeRoot.js.map +1 -0
  32. package/lib/commonjs/RulerRangeTicks.js +57 -0
  33. package/lib/commonjs/RulerRangeTicks.js.map +1 -0
  34. package/lib/commonjs/RulerRangeTrack.js +46 -0
  35. package/lib/commonjs/RulerRangeTrack.js.map +1 -0
  36. package/lib/commonjs/RulerResetButton.js +46 -0
  37. package/lib/commonjs/RulerResetButton.js.map +1 -0
  38. package/lib/commonjs/RulerRoot.js +87 -0
  39. package/lib/commonjs/RulerRoot.js.map +1 -0
  40. package/lib/commonjs/RulerTicks.js +51 -0
  41. package/lib/commonjs/RulerTicks.js.map +1 -0
  42. package/lib/commonjs/RulerTrack.js +46 -0
  43. package/lib/commonjs/RulerTrack.js.map +1 -0
  44. package/lib/commonjs/Tick.js +41 -36
  45. package/lib/commonjs/Tick.js.map +1 -1
  46. package/lib/commonjs/defaults.js +20 -19
  47. package/lib/commonjs/defaults.js.map +1 -1
  48. package/lib/commonjs/index.js +287 -1
  49. package/lib/commonjs/index.js.map +1 -1
  50. package/lib/commonjs/presets.js +108 -0
  51. package/lib/commonjs/presets.js.map +1 -0
  52. package/lib/commonjs/tickResolver.js +101 -0
  53. package/lib/commonjs/tickResolver.js.map +1 -0
  54. package/lib/commonjs/useHaptics.js +1 -1
  55. package/lib/commonjs/useHaptics.web.js +43 -0
  56. package/lib/commonjs/useHaptics.web.js.map +1 -0
  57. package/lib/commonjs/useRulerDatePicker.js +264 -0
  58. package/lib/commonjs/useRulerDatePicker.js.map +1 -0
  59. package/lib/commonjs/useRulerDateRangePicker.js +313 -0
  60. package/lib/commonjs/useRulerDateRangePicker.js.map +1 -0
  61. package/lib/commonjs/utils.js +61 -47
  62. package/lib/commonjs/utils.js.map +1 -1
  63. package/lib/module/LensTick.js +77 -0
  64. package/lib/module/LensTick.js.map +1 -0
  65. package/lib/module/Ruler.js +35 -0
  66. package/lib/module/Ruler.js.map +1 -0
  67. package/lib/module/RulerContext.js +12 -0
  68. package/lib/module/RulerContext.js.map +1 -0
  69. package/lib/module/RulerDatePicker.js +38 -350
  70. package/lib/module/RulerDatePicker.js.map +1 -1
  71. package/lib/module/RulerDateRangePicker.js +52 -0
  72. package/lib/module/RulerDateRangePicker.js.map +1 -0
  73. package/lib/module/RulerHeader.js +101 -0
  74. package/lib/module/RulerHeader.js.map +1 -0
  75. package/lib/module/RulerIndicator.js +62 -0
  76. package/lib/module/RulerIndicator.js.map +1 -0
  77. package/lib/module/RulerLensTicks.js +50 -0
  78. package/lib/module/RulerLensTicks.js.map +1 -0
  79. package/lib/module/RulerLensTrack.js +47 -0
  80. package/lib/module/RulerLensTrack.js.map +1 -0
  81. package/lib/module/RulerRange.js +36 -0
  82. package/lib/module/RulerRange.js.map +1 -0
  83. package/lib/module/RulerRangeContext.js +12 -0
  84. package/lib/module/RulerRangeContext.js.map +1 -0
  85. package/lib/module/RulerRangeHeader.js +149 -0
  86. package/lib/module/RulerRangeHeader.js.map +1 -0
  87. package/lib/module/RulerRangeHighlight.js +45 -0
  88. package/lib/module/RulerRangeHighlight.js.map +1 -0
  89. package/lib/module/RulerRangeIndicator.js +66 -0
  90. package/lib/module/RulerRangeIndicator.js.map +1 -0
  91. package/lib/module/RulerRangeRoot.js +75 -0
  92. package/lib/module/RulerRangeRoot.js.map +1 -0
  93. package/lib/module/RulerRangeTicks.js +51 -0
  94. package/lib/module/RulerRangeTicks.js.map +1 -0
  95. package/lib/module/RulerRangeTrack.js +41 -0
  96. package/lib/module/RulerRangeTrack.js.map +1 -0
  97. package/lib/module/RulerResetButton.js +41 -0
  98. package/lib/module/RulerResetButton.js.map +1 -0
  99. package/lib/module/RulerRoot.js +82 -0
  100. package/lib/module/RulerRoot.js.map +1 -0
  101. package/lib/module/RulerTicks.js +45 -0
  102. package/lib/module/RulerTicks.js.map +1 -0
  103. package/lib/module/RulerTrack.js +41 -0
  104. package/lib/module/RulerTrack.js.map +1 -0
  105. package/lib/module/Tick.js +41 -36
  106. package/lib/module/Tick.js.map +1 -1
  107. package/lib/module/defaults.js +19 -18
  108. package/lib/module/defaults.js.map +1 -1
  109. package/lib/module/index.js +46 -1
  110. package/lib/module/index.js.map +1 -1
  111. package/lib/module/presets.js +103 -0
  112. package/lib/module/presets.js.map +1 -0
  113. package/lib/module/tickResolver.js +97 -0
  114. package/lib/module/tickResolver.js.map +1 -0
  115. package/lib/module/useHaptics.js +1 -1
  116. package/lib/module/useHaptics.js.map +1 -1
  117. package/lib/module/useHaptics.web.js +39 -0
  118. package/lib/module/useHaptics.web.js.map +1 -0
  119. package/lib/module/useRulerDatePicker.js +260 -0
  120. package/lib/module/useRulerDatePicker.js.map +1 -0
  121. package/lib/module/useRulerDateRangePicker.js +310 -0
  122. package/lib/module/useRulerDateRangePicker.js.map +1 -0
  123. package/lib/module/utils.js +59 -45
  124. package/lib/module/utils.js.map +1 -1
  125. package/lib/typescript/LensTick.d.ts +20 -0
  126. package/lib/typescript/LensTick.d.ts.map +1 -0
  127. package/lib/typescript/Ruler.d.ts +31 -0
  128. package/lib/typescript/Ruler.d.ts.map +1 -0
  129. package/lib/typescript/RulerContext.d.ts +11 -0
  130. package/lib/typescript/RulerContext.d.ts.map +1 -0
  131. package/lib/typescript/RulerDatePicker.d.ts +10 -7
  132. package/lib/typescript/RulerDatePicker.d.ts.map +1 -1
  133. package/lib/typescript/RulerDateRangePicker.d.ts +26 -0
  134. package/lib/typescript/RulerDateRangePicker.d.ts.map +1 -0
  135. package/lib/typescript/RulerHeader.d.ts +14 -0
  136. package/lib/typescript/RulerHeader.d.ts.map +1 -0
  137. package/lib/typescript/RulerIndicator.d.ts +17 -0
  138. package/lib/typescript/RulerIndicator.d.ts.map +1 -0
  139. package/lib/typescript/RulerLensTicks.d.ts +12 -0
  140. package/lib/typescript/RulerLensTicks.d.ts.map +1 -0
  141. package/lib/typescript/RulerLensTrack.d.ts +13 -0
  142. package/lib/typescript/RulerLensTrack.d.ts.map +1 -0
  143. package/lib/typescript/RulerRange.d.ts +32 -0
  144. package/lib/typescript/RulerRange.d.ts.map +1 -0
  145. package/lib/typescript/RulerRangeContext.d.ts +11 -0
  146. package/lib/typescript/RulerRangeContext.d.ts.map +1 -0
  147. package/lib/typescript/RulerRangeHeader.d.ts +12 -0
  148. package/lib/typescript/RulerRangeHeader.d.ts.map +1 -0
  149. package/lib/typescript/RulerRangeHighlight.d.ts +8 -0
  150. package/lib/typescript/RulerRangeHighlight.d.ts.map +1 -0
  151. package/lib/typescript/RulerRangeIndicator.d.ts +17 -0
  152. package/lib/typescript/RulerRangeIndicator.d.ts.map +1 -0
  153. package/lib/typescript/RulerRangeRoot.d.ts +7 -0
  154. package/lib/typescript/RulerRangeRoot.d.ts.map +1 -0
  155. package/lib/typescript/RulerRangeTicks.d.ts +14 -0
  156. package/lib/typescript/RulerRangeTicks.d.ts.map +1 -0
  157. package/lib/typescript/RulerRangeTrack.d.ts +8 -0
  158. package/lib/typescript/RulerRangeTrack.d.ts.map +1 -0
  159. package/lib/typescript/RulerResetButton.d.ts +10 -0
  160. package/lib/typescript/RulerResetButton.d.ts.map +1 -0
  161. package/lib/typescript/RulerRoot.d.ts +7 -0
  162. package/lib/typescript/RulerRoot.d.ts.map +1 -0
  163. package/lib/typescript/RulerTicks.d.ts +9 -0
  164. package/lib/typescript/RulerTicks.d.ts.map +1 -0
  165. package/lib/typescript/RulerTrack.d.ts +8 -0
  166. package/lib/typescript/RulerTrack.d.ts.map +1 -0
  167. package/lib/typescript/Tick.d.ts +3 -16
  168. package/lib/typescript/Tick.d.ts.map +1 -1
  169. package/lib/typescript/defaults.d.ts +6 -9
  170. package/lib/typescript/defaults.d.ts.map +1 -1
  171. package/lib/typescript/index.d.ts +37 -1
  172. package/lib/typescript/index.d.ts.map +1 -1
  173. package/lib/typescript/presets.d.ts +27 -0
  174. package/lib/typescript/presets.d.ts.map +1 -0
  175. package/lib/typescript/tickResolver.d.ts +10 -0
  176. package/lib/typescript/tickResolver.d.ts.map +1 -0
  177. package/lib/typescript/types.d.ts +302 -85
  178. package/lib/typescript/types.d.ts.map +1 -1
  179. package/lib/typescript/useHaptics.web.d.ts +17 -0
  180. package/lib/typescript/useHaptics.web.d.ts.map +1 -0
  181. package/lib/typescript/useRulerDatePicker.d.ts +26 -0
  182. package/lib/typescript/useRulerDatePicker.d.ts.map +1 -0
  183. package/lib/typescript/useRulerDateRangePicker.d.ts +40 -0
  184. package/lib/typescript/useRulerDateRangePicker.d.ts.map +1 -0
  185. package/lib/typescript/utils.d.ts +14 -12
  186. package/lib/typescript/utils.d.ts.map +1 -1
  187. package/package.json +25 -7
  188. package/src/LensTick.tsx +90 -0
  189. package/src/Ruler.ts +32 -0
  190. package/src/RulerContext.tsx +20 -0
  191. package/src/RulerDatePicker.tsx +41 -439
  192. package/src/RulerDateRangePicker.tsx +53 -0
  193. package/src/RulerHeader.tsx +125 -0
  194. package/src/RulerIndicator.tsx +84 -0
  195. package/src/RulerLensTicks.tsx +50 -0
  196. package/src/RulerLensTrack.tsx +45 -0
  197. package/src/RulerRange.ts +33 -0
  198. package/src/RulerRangeContext.tsx +20 -0
  199. package/src/RulerRangeHeader.tsx +180 -0
  200. package/src/RulerRangeHighlight.tsx +47 -0
  201. package/src/RulerRangeIndicator.tsx +85 -0
  202. package/src/RulerRangeRoot.tsx +87 -0
  203. package/src/RulerRangeTicks.tsx +68 -0
  204. package/src/RulerRangeTrack.tsx +39 -0
  205. package/src/RulerResetButton.tsx +59 -0
  206. package/src/RulerRoot.tsx +93 -0
  207. package/src/RulerTicks.tsx +57 -0
  208. package/src/RulerTrack.tsx +39 -0
  209. package/src/Tick.tsx +60 -49
  210. package/src/defaults.ts +21 -19
  211. package/src/index.ts +83 -1
  212. package/src/presets.ts +108 -0
  213. package/src/tickResolver.ts +123 -0
  214. package/src/types.ts +354 -102
  215. package/src/useHaptics.web.ts +55 -0
  216. package/src/useRulerDatePicker.ts +373 -0
  217. package/src/useRulerDateRangePicker.ts +468 -0
  218. package/src/utils.ts +67 -68
  219. package/lib/commonjs/package.json +0 -1
  220. package/lib/module/package.json +0 -1
package/src/types.ts CHANGED
@@ -1,129 +1,381 @@
1
1
  import type React from 'react';
2
+ import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
2
3
 
3
- export interface RulerDatePickerProps {
4
- // ── Selection & Bounds ─────────────────────────────────────
5
- /** The currently selected date. Accepts a Date object or a YYYY-MM-DD string. */
6
- selectedDate: Date | string;
7
- /**
8
- * Called when the user scrolls to a new date.
9
- * Provides both the Date object and the formatted YYYY-MM-DD string.
10
- */
11
- onSelectDate: (date: Date, dateString: string) => void;
12
- /** Minimum selectable date. Defaults to 365 days before today. */
13
- minDate?: Date;
14
- /** Maximum selectable date. Defaults to 365 days after today. */
15
- maxDate?: Date;
4
+ // ── Tick System Types ────────────────────────────────────────────────────────
5
+
6
+ /**
7
+ * Context provided to each tick rule during evaluation.
8
+ */
9
+ export interface TickContext {
10
+ /** The Date represented by this tick */
11
+ date: Date;
12
+ /** Day offset relative to reference time (0 = today) */
13
+ offset: number;
14
+ /** True if this date is today */
15
+ isToday: boolean;
16
+ /** True if this date matches the currently selected date */
17
+ isSelected: boolean;
18
+ /** True if this date falls within the selected range (range picker mode) */
19
+ isInRange?: boolean;
20
+ }
21
+
22
+ /**
23
+ * Predicate condition determining when a tick rule matches.
24
+ */
25
+ export type TickCondition =
26
+ | 'daily' // Matches every day (base / fallback)
27
+ | 'weekly' // Every 7th day relative to reference
28
+ | 'monthly' // 1st day of each month
29
+ | 'yearly' // Jan 1st of each year
30
+ | { interval: number } // Every N days (offset % interval === 0)
31
+ | { dayOfWeek: number[] } // Days of week: 0 = Sun, 1 = Mon, ..., 6 = Sat
32
+ | ((ctx: TickContext) => boolean); // Custom predicate callback
33
+
34
+ /**
35
+ * Configuration for tick date labels.
36
+ */
37
+ export interface TickLabelConfig {
38
+ /** Custom label text formatter */
39
+ format?: (date: Date, ctx: TickContext) => string;
40
+ /** Text color override */
41
+ color?: string;
42
+ /** Font size in pixels (default: 10) */
43
+ fontSize?: number;
44
+ /** Font weight */
45
+ fontWeight?: TextStyle['fontWeight'];
46
+ }
16
47
 
17
- // ── Label & Highlight Rules ────────────────────────────────
48
+ /**
49
+ * Configuration for the dot indicator (e.g. today dot or milestone badge).
50
+ */
51
+ export interface TickDotConfig {
52
+ /** Dot diameter in pixels (default: 6) */
53
+ size?: number;
54
+ /** Dot fill color */
55
+ color?: string;
56
+ }
57
+
58
+ /**
59
+ * Complete declarative definition for a tick variant.
60
+ * Evaluated top-to-bottom; first matching rule is applied.
61
+ */
62
+ export interface TickRule {
18
63
  /**
19
- * Controls which ticks show a date label.
20
- * - `'daily'` — every tick gets a label
21
- * - `'weekly'` (default) — every 7th day
22
- * - `'monthly'` — 1st of each month
23
- * - `(date: Date) => boolean` — custom callback
64
+ * When this rule applies.
65
+ * If omitted, this rule matches unconditionally (serves as the fallback).
24
66
  */
25
- labelInterval?: 'daily' | 'weekly' | 'monthly' | ((date: Date) => boolean);
26
- /** Days of the week to visually highlight. 0 = Sunday, 6 = Saturday. e.g. [0, 6] for weekends. */
27
- highlightDays?: number[];
67
+ condition?: TickCondition;
28
68
 
29
- // ── Tick Dimensions ────────────────────────────────────────
30
- /** Gap between ticks in pixels. Default: 24 */
31
- tickSpacing?: number;
32
- /** Height of small (unlabeled) ticks in pixels. Default: 12 */
33
- tickHeight?: number;
34
- /** Height of tall (labeled) ticks in pixels. Default: 24 */
35
- tickTallHeight?: number;
36
- /** Width of each tick bar in pixels. Default: 4 */
37
- tickThickness?: number;
38
-
39
- // ── Tick Colors (small & large independently) ──────────────
40
- /** Color of small (unlabeled) ticks. */
41
- tickColor?: string;
42
- /** Color of tall (labeled) ticks. Falls back to tickColor if not specified. */
43
- tickTallColor?: string;
44
-
45
- // ── Label & Accent Colors ─────────────────────────────────
46
- /** Color of date label text beneath labeled ticks. */
47
- labelColor?: string;
48
- /** Color of the today dot indicator and reset button text. */
49
- accentColor?: string;
50
- /** Color of the center selection indicator line. */
69
+ /** Height of the tick bar in pixels */
70
+ height: number;
71
+
72
+ /** Width/thickness of the tick bar in pixels (defaults to tickWidth or 3) */
73
+ width?: number;
74
+
75
+ /** Color of the tick bar */
76
+ color?: string;
77
+
78
+ /** Label configuration or boolean (true uses day number) */
79
+ label?: boolean | TickLabelConfig;
80
+
81
+ /** Dot badge indicator (e.g., today marker) */
82
+ dot?: boolean | TickDotConfig;
83
+
84
+ /** Custom React component render override for this tick */
85
+ render?: (ctx: TickContext) => React.ReactNode;
86
+ }
87
+
88
+ /**
89
+ * Resolved visual attributes for rendering a single tick.
90
+ */
91
+ export interface ResolvedTick {
92
+ offset: number;
93
+ date: Date;
94
+ dateStr: string;
95
+ height: number;
96
+ width: number;
97
+ color: string;
98
+ labelText?: string;
99
+ labelColor: string;
100
+ labelFontSize: number;
101
+ labelFontWeight: TextStyle['fontWeight'];
102
+ hasDot: boolean;
103
+ dotSize: number;
104
+ dotColor: string;
105
+ customRender?: (ctx: TickContext) => React.ReactNode;
106
+ isToday: boolean;
107
+ isSelected: boolean;
108
+ isInRange: boolean;
109
+ }
110
+
111
+ // ── Theming & Styling ────────────────────────────────────────────────────────
112
+
113
+ export interface RulerTheme {
114
+ /** Background color of the picker component */
115
+ background?: string;
116
+ /** Color of the center selection needle */
51
117
  indicatorColor?: string;
52
- /** Background color of the entire component. Default: transparent */
53
- backgroundColor?: string;
118
+ /** Primary accent color (used for today dot, active labels, etc.) */
119
+ accentColor?: string;
120
+ /** Default label text color */
121
+ labelColor?: string;
122
+ /** Default tick bar color when not overridden by a rule */
123
+ defaultTickColor?: string;
124
+ /** Color for range selection connecting bar in range mode */
125
+ rangeHighlightColor?: string;
126
+ }
127
+
128
+ export type ResolvedRulerTheme = Required<RulerTheme>;
129
+
130
+ // ── Single Picker Props ──────────────────────────────────────────────────────
54
131
 
55
- // ── Layout Dimensions ─────────────────────────────────────
56
- /** Total component height in pixels. Default: 120 */
132
+ /**
133
+ * Configuration for the center alignment needle indicator.
134
+ */
135
+ export interface IndicatorConfig {
136
+ /** Width of the center indicator needle in pixels (default: 2) */
137
+ width?: number;
138
+ /** Height of the center indicator needle in pixels (default: 40) */
57
139
  height?: number;
58
- /** Height of the ruler track area in pixels. Default: 60 */
59
- rulerHeight?: number;
60
- /** Width of the center selection indicator bar in pixels. Default: 2 */
61
- indicatorWidth?: number;
62
- /** Height of the center selection indicator bar in pixels. Default: 40 */
63
- indicatorHeight?: number;
64
- /** Diameter of the today dot indicator in pixels. Default: 6 */
65
- todayDotSize?: number;
66
- /** Font size of tick date labels in pixels. Default: 10 */
67
- labelFontSize?: number;
68
-
69
- // ── Performance Tuning ────────────────────────────────────
70
- /** Number of off-screen ticks to render as buffer on each side. Default: 10 */
71
- bufferTicks?: number;
72
- /** Debounce interval in ms before calling onSelectDate. Default: 100 */
73
- updateDebounceMs?: number;
74
-
75
- // ── Haptic Tuning ─────────────────────────────────────────
76
- /** Enable or disable haptic feedback. Default: true */
77
- enableHaptics?: boolean;
140
+ /** Color of the center indicator needle */
141
+ color?: string;
78
142
  /**
79
- * Custom haptic trigger callback. When provided, overrides the default
80
- * expo-haptics implementation. Receives the haptic mode:
81
- * - `'tick'` — single discrete haptic (slow scrolling)
82
- * - `'continuous'` — continuous buzz (fast scrolling)
143
+ * Offset [x, y] in pixels of the center needle pointer.
144
+ * Default: [0, 0] (anchored to bottom).
83
145
  */
84
- onHapticTrigger?: (mode: 'tick' | 'continuous') => void;
85
- /** Number of ticks crossed per frame to trigger fast/continuous haptic mode. Default: 1 */
146
+ offset?: [number, number];
147
+ /** Custom style for the center indicator needle */
148
+ style?: StyleProp<ViewStyle>;
149
+ /** Custom renderer for the center indicator */
150
+ render?: () => React.ReactNode;
151
+ }
152
+
153
+ /**
154
+ * Configuration for the date picker header.
155
+ */
156
+ export interface HeaderConfig {
157
+ /** Show "Today" reset button when not on today (default: false) */
158
+ showResetToToday?: boolean;
159
+ /** Text label for the reset button (default: 'Today') */
160
+ resetLabel?: string;
161
+ /** Header container style */
162
+ style?: StyleProp<ViewStyle>;
163
+ /** Header date label style */
164
+ dateStyle?: StyleProp<TextStyle>;
165
+ /** Header year label style */
166
+ yearStyle?: StyleProp<TextStyle>;
167
+ /** Custom date format function for the header text */
168
+ format?: (date: Date) => string;
169
+ /** Custom header renderer replacing the default header */
170
+ render?: (
171
+ selectedDate: Date,
172
+ isToday: boolean,
173
+ resetToToday: () => void
174
+ ) => React.ReactNode;
175
+ }
176
+
177
+ /**
178
+ * Configuration for tactile haptic feedback.
179
+ */
180
+ export interface HapticConfig {
181
+ /** Whether haptics are enabled (default: true) */
182
+ enabled?: boolean;
183
+ /** Custom haptic callback (e.g. for Expo Haptics) */
184
+ onTrigger?: (mode: 'tick' | 'continuous') => void;
185
+ /** Speed threshold (ticks/frame) to trigger continuous buzz mode (default: 1) */
86
186
  fastSwipeTickThreshold?: number;
87
- /** Interval in ms between haptic pulses in continuous mode. Default: 20 */
187
+ /** Pulse interval in ms during continuous haptic mode (default: 20) */
88
188
  continuousHapticIntervalMs?: number;
189
+ }
89
190
 
90
- // ── Reset Control ─────────────────────────────────────────
91
- /** Show a "Today" reset button when not on today's date. Default: false */
92
- showResetToToday?: boolean;
191
+ export interface RulerDatePickerProps {
192
+ // ── Value & Selection ──
193
+ /** Currently selected date (controlled). Accepts Date or YYYY-MM-DD string. */
194
+ value?: Date | string;
195
+ /** Initial selected date (uncontrolled). Accepts Date or YYYY-MM-DD string. */
196
+ defaultValue?: Date | string;
197
+ /** Called when user scrolls to a date */
198
+ onChange?: (date: Date, dateString: string) => void;
199
+ /** Minimum selectable date */
200
+ minDate?: Date | string;
201
+ /** Maximum selectable date */
202
+ maxDate?: Date | string;
203
+ /** BCP 47 locale string for date formatting (default: system 'default') */
204
+ locale?: string;
205
+
206
+ // ── Tick Configuration ──
207
+ /** Array of tick rules or a preset name ('calendar' | 'metric' | 'minimal') */
208
+ ticks?: TickRule[] | 'calendar' | 'metric' | 'minimal';
209
+ /** Distance in pixels between tick centers (default: 24) */
210
+ tickSpacing?: number;
211
+ /** Base width of tick bars in pixels (default: 3) */
212
+ tickWidth?: number;
213
+
214
+ // ── Dimensions & Theming ──
215
+ /** Width of the ruler viewport track in pixels (default: window width) */
216
+ width?: number;
217
+ /** Height of the ruler track area in pixels (default: 60) */
218
+ rulerHeight?: number;
219
+ /** Color theme overrides (background, indicatorColor, accentColor, labelColor, etc.) */
220
+ theme?: RulerTheme;
221
+ /** Container style */
222
+ style?: StyleProp<ViewStyle>;
93
223
 
94
- // ── Custom Renderers ──────────────────────────────────────
224
+ // ── Indicator, Header, & Haptics (Consolidated APIs) ──
225
+ /** Center indicator needle configuration or boolean toggle (default: true) */
226
+ indicator?: boolean | IndicatorConfig;
227
+ /** Header configuration or boolean toggle (default: true) */
228
+ header?: boolean | HeaderConfig;
229
+ /** Haptics configuration or boolean toggle (default: true) */
230
+ haptics?: boolean | HapticConfig;
231
+
232
+ // ── Fisheye Lens Effect ──
95
233
  /**
96
- * Custom header renderer. When provided, replaces the default date header.
97
- * Receives the selected date, whether it's today, and a function to reset to today.
234
+ * Enable non-linear fisheye lens distortion where tick spacing is wider near the center
235
+ * needle and gradually flattens out to standard spacing away from the center.
236
+ * Can be true (for default configuration) or a custom LensConfig object.
98
237
  */
99
- renderHeader?: (
100
- selectedDate: Date,
101
- isToday: boolean,
102
- resetToToday: () => void
238
+ lensEffect?: boolean | LensConfig;
239
+
240
+ // ── Performance ──
241
+ /** Debounce ms for onChange during scrolling (default: 100) */
242
+ debounceMs?: number;
243
+
244
+ /** Test ID for automated tests */
245
+ testID?: string;
246
+ /** Accessibility label for assistive technologies / screen readers */
247
+ accessibilityLabel?: string;
248
+ }
249
+
250
+ // ── Fisheye Lens Types ───────────────────────────────────────────────────────
251
+
252
+ export interface LensConfig {
253
+ /**
254
+ * Spacing magnification factor at the center needle.
255
+ * (e.g. 1.6 means 60% wider spacing at the needle).
256
+ * Default: 1.6
257
+ */
258
+ magnification?: number;
259
+ /**
260
+ * Radius in pixels around the center needle over which the magnification
261
+ * smoothly transitions back to standard linear spacing.
262
+ * Default: 60
263
+ */
264
+ radius?: number;
265
+ /**
266
+ * Whether to subtly scale tick bar height and label prominence near the center.
267
+ * Default: true
268
+ */
269
+ scaleTicks?: boolean;
270
+ }
271
+
272
+ // ── Range Picker Types [EXPERIMENTAL] ────────────────────────────────────────
273
+
274
+ /**
275
+ * Date range boundaries representation.
276
+ *
277
+ * @experimental
278
+ */
279
+ export interface DateRange {
280
+ start: Date | string;
281
+ end: Date | string;
282
+ }
283
+
284
+ /**
285
+ * Payload delivered to onChange when range selection changes.
286
+ *
287
+ * @experimental
288
+ */
289
+ export interface DateRangeChangePayload {
290
+ start: Date;
291
+ end: Date;
292
+ startDate: Date;
293
+ endDate: Date;
294
+ startString: string;
295
+ endString: string;
296
+ spanDays: number;
297
+ }
298
+
299
+ /**
300
+ * Context provided to custom range header render callbacks.
301
+ *
302
+ * @experimental
303
+ */
304
+ export interface RangeHeaderContext {
305
+ start: Date;
306
+ end: Date;
307
+ spanDays: number;
308
+ activeHandle: 'start' | 'end';
309
+ scrollToHandle: (handle: 'start' | 'end', animated?: boolean) => void;
310
+ }
311
+
312
+ /**
313
+ * Configuration options for the range header.
314
+ *
315
+ * @experimental
316
+ */
317
+ export interface RangeHeaderConfig {
318
+ /** Header container style */
319
+ style?: StyleProp<ViewStyle>;
320
+ /** Header date label style */
321
+ dateStyle?: StyleProp<TextStyle>;
322
+ /** Custom date format function for start/end labels */
323
+ format?: (date: Date) => string;
324
+ /** Custom header renderer replacing default range header */
325
+ render?: (context: RangeHeaderContext) => React.ReactNode;
326
+ }
327
+
328
+ /**
329
+ * Configuration for the date range highlight span connecting the handles.
330
+ *
331
+ * @experimental
332
+ */
333
+ export interface RangeHighlightConfig {
334
+ /** Color of the range connecting highlight strip (default: theme.rangeHighlightColor) */
335
+ color?: string;
336
+ /** Custom style for the range highlight span */
337
+ style?: StyleProp<ViewStyle>;
338
+ /** Custom renderer for the range highlight span */
339
+ render?: (
340
+ startOffset: number,
341
+ endOffset: number,
342
+ tickSpacing: number
103
343
  ) => React.ReactNode;
104
344
  }
105
345
 
106
346
  /**
107
- * Internal type for resolved color values.
108
- * @internal
347
+ * Configuration for the center indicator needle in range mode.
348
+ *
349
+ * @experimental
109
350
  */
110
- export interface ResolvedColors {
111
- tickColor: string;
112
- tickTallColor: string;
113
- labelColor: string;
114
- accentColor: string;
115
- indicatorColor: string;
116
- backgroundColor: string;
351
+ export interface RangeIndicatorConfig extends Omit<IndicatorConfig, 'render'> {
352
+ /** Custom renderer for range needle indicator receiving active handle and date */
353
+ render?: (handle: 'start' | 'end', date: Date) => React.ReactNode;
117
354
  }
118
355
 
119
356
  /**
120
- * Internal tick data used for rendering.
121
- * @internal
357
+ * Props for `RulerDateRangePicker`.
358
+ *
359
+ * @experimental This component and its props are experimental. The API may
360
+ * undergo breaking changes in future minor versions.
122
361
  */
123
- export interface TickData {
124
- offset: number;
125
- isLabeled: boolean;
126
- isToday: boolean;
127
- isHighlighted: boolean;
128
- dateStr: string;
362
+ export interface RulerDateRangePickerProps
363
+ extends Omit<RulerDatePickerProps, 'value' | 'defaultValue' | 'onChange' | 'header' | 'indicator'> {
364
+ /** Currently selected date range (controlled) */
365
+ value?: DateRange;
366
+ /** Initial selected date range (uncontrolled) */
367
+ defaultValue?: DateRange;
368
+ /** Called when the selected range changes */
369
+ onChange?: (range: DateRangeChangePayload) => void;
370
+ /** Header configuration or boolean toggle (default: true) */
371
+ header?: boolean | RangeHeaderConfig;
372
+ /** Center indicator needle configuration or boolean toggle (default: true) */
373
+ indicator?: boolean | RangeIndicatorConfig;
374
+ /** Range connecting highlight strip configuration or boolean toggle (default: true) */
375
+ highlight?: boolean | RangeHighlightConfig;
376
+
377
+ /** Minimum selectable span in days (default: 1) */
378
+ minSpanDays?: number;
379
+ /** Maximum selectable span in days */
380
+ maxSpanDays?: number;
129
381
  }
@@ -0,0 +1,55 @@
1
+ import { useCallback, useRef } from 'react';
2
+ import { DEFAULTS } from './defaults';
3
+
4
+ interface UseHapticsOptions {
5
+ enableHaptics: boolean;
6
+ onHapticTrigger?: (mode: 'tick' | 'continuous') => void;
7
+ continuousHapticIntervalMs?: number;
8
+ }
9
+
10
+ interface HapticsAPI {
11
+ fireDiscreteHaptic: () => void;
12
+ startContinuousHaptic: () => void;
13
+ stopContinuousHaptic: () => void;
14
+ }
15
+
16
+ /**
17
+ * Haptics hook implementation for Web environments.
18
+ * Delegates to onHapticTrigger if provided, otherwise gracefully no-ops without requiring expo-haptics.
19
+ */
20
+ export function useHaptics({
21
+ enableHaptics,
22
+ onHapticTrigger,
23
+ continuousHapticIntervalMs = DEFAULTS.continuousHapticIntervalMs,
24
+ }: UseHapticsOptions): HapticsAPI {
25
+ const hapticIntervalRef = useRef<ReturnType<typeof setInterval> | null>(
26
+ null
27
+ );
28
+
29
+ const fireDiscreteHaptic = useCallback(() => {
30
+ if (!enableHaptics || !onHapticTrigger) return;
31
+ onHapticTrigger('tick');
32
+ }, [enableHaptics, onHapticTrigger]);
33
+
34
+ const startContinuousHaptic = useCallback(() => {
35
+ if (!enableHaptics || !onHapticTrigger) return;
36
+ if (hapticIntervalRef.current) return;
37
+ onHapticTrigger('continuous');
38
+ hapticIntervalRef.current = setInterval(() => {
39
+ onHapticTrigger('continuous');
40
+ }, continuousHapticIntervalMs);
41
+ }, [enableHaptics, onHapticTrigger, continuousHapticIntervalMs]);
42
+
43
+ const stopContinuousHaptic = useCallback(() => {
44
+ if (hapticIntervalRef.current) {
45
+ clearInterval(hapticIntervalRef.current);
46
+ hapticIntervalRef.current = null;
47
+ }
48
+ }, []);
49
+
50
+ return {
51
+ fireDiscreteHaptic,
52
+ startContinuousHaptic,
53
+ stopContinuousHaptic,
54
+ };
55
+ }