@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.
- package/README.md +165 -0
- package/lib/module/components/CalendarGrid/CalendarGrid.js +80 -18
- package/lib/module/components/CalendarGrid/CalendarGrid.js.map +1 -1
- package/lib/module/components/CalendarGrid/CalendarGrid.web.js +80 -18
- package/lib/module/components/CalendarGrid/CalendarGrid.web.js.map +1 -1
- package/lib/module/components/CalendarGrid/DayCell.js +17 -5
- package/lib/module/components/CalendarGrid/DayCell.js.map +1 -1
- package/lib/module/components/CalendarGrid/DayCell.web.js +17 -5
- package/lib/module/components/CalendarGrid/DayCell.web.js.map +1 -1
- package/lib/module/components/DatePicker/DatePicker.android.js +42 -19
- package/lib/module/components/DatePicker/DatePicker.android.js.map +1 -1
- package/lib/module/components/DatePicker/DatePicker.ios.js +44 -21
- package/lib/module/components/DatePicker/DatePicker.ios.js.map +1 -1
- package/lib/module/components/DatePicker/DatePicker.js.map +1 -1
- package/lib/module/components/DatePicker/DatePicker.web.js +40 -16
- package/lib/module/components/DatePicker/DatePicker.web.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts +19 -7
- package/lib/typescript/components/CalendarGrid/CalendarGrid.d.ts.map +1 -1
- package/lib/typescript/components/CalendarGrid/CalendarGrid.web.d.ts +19 -7
- package/lib/typescript/components/CalendarGrid/CalendarGrid.web.d.ts.map +1 -1
- package/lib/typescript/components/CalendarGrid/DayCell.d.ts +3 -0
- package/lib/typescript/components/CalendarGrid/DayCell.d.ts.map +1 -1
- package/lib/typescript/components/CalendarGrid/DayCell.web.d.ts +3 -0
- package/lib/typescript/components/CalendarGrid/DayCell.web.d.ts.map +1 -1
- package/lib/typescript/components/DatePicker/DatePicker.android.d.ts.map +1 -1
- package/lib/typescript/components/DatePicker/DatePicker.d.ts +20 -8
- package/lib/typescript/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/lib/typescript/components/DatePicker/DatePicker.ios.d.ts.map +1 -1
- package/lib/typescript/components/DatePicker/DatePicker.web.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types/datepicker.d.ts +37 -3
- package/lib/typescript/types/datepicker.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/CalendarGrid/CalendarGrid.tsx +216 -122
- package/src/components/CalendarGrid/CalendarGrid.web.tsx +227 -136
- package/src/components/CalendarGrid/DayCell.tsx +48 -6
- package/src/components/CalendarGrid/DayCell.web.tsx +48 -6
- package/src/components/DatePicker/DatePicker.android.tsx +39 -21
- package/src/components/DatePicker/DatePicker.ios.tsx +42 -24
- package/src/components/DatePicker/DatePicker.tsx +28 -8
- package/src/components/DatePicker/DatePicker.web.tsx +40 -16
- package/src/index.ts +2 -0
- package/src/types/datepicker.ts +43 -3
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
} from '../../utils/date';
|
|
15
15
|
import { DayCell } from './DayCell';
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
@@ -154,152 +170,227 @@ const webStyles = StyleSheet.create({
|
|
|
154
170
|
},
|
|
155
171
|
});
|
|
156
172
|
|
|
157
|
-
export const CalendarGrid: React.FC<CalendarGridProps> = memo(
|
|
158
|
-
|
|
159
|
-
value,
|
|
160
|
-
onChange,
|
|
173
|
+
export const CalendarGrid: React.FC<CalendarGridProps> = memo((props) => {
|
|
174
|
+
const {
|
|
161
175
|
minDate,
|
|
162
176
|
maxDate,
|
|
163
177
|
disabledDates,
|
|
164
178
|
themeMode,
|
|
165
179
|
weekStartsOn = 0,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
180
|
+
selectionMode = 'single',
|
|
181
|
+
} = props;
|
|
182
|
+
|
|
183
|
+
// Extract mode-specific props
|
|
184
|
+
const singleValue = selectionMode === 'single' ? props.value : null;
|
|
185
|
+
const singleOnChange = selectionMode === 'single' ? props.onChange : null;
|
|
186
|
+
const rangeStart = selectionMode === 'range' ? props.startDate : null;
|
|
187
|
+
const rangeEnd = selectionMode === 'range' ? props.endDate : null;
|
|
188
|
+
const rangeOnChange = selectionMode === 'range' ? props.onRangeChange : null;
|
|
189
|
+
|
|
190
|
+
const overrides = useDatePickerOverrides();
|
|
191
|
+
const [currentMonth, setCurrentMonth] = React.useState(() =>
|
|
192
|
+
getFirstDayOfMonth(singleValue ?? rangeStart ?? new Date()),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const days = useMemo(
|
|
196
|
+
() => generateMonthDays(currentMonth, weekStartsOn),
|
|
197
|
+
[currentMonth, weekStartsOn],
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const weekDays =
|
|
201
|
+
weekStartsOn === 0 ? WEEK_DAYS_SUNDAY_START : WEEK_DAYS_MONDAY_START;
|
|
202
|
+
|
|
203
|
+
const todayDate = useMemo(() => today(), []);
|
|
204
|
+
|
|
205
|
+
const isDisabled = useCallback(
|
|
206
|
+
(date: Date | null): boolean => {
|
|
207
|
+
if (!date) return true;
|
|
208
|
+
if (minDate && compareDates(date, minDate) < 0) return true;
|
|
209
|
+
if (maxDate && compareDates(date, maxDate) > 0) return true;
|
|
210
|
+
if (disabledDates?.some((d) => isSameDay(date, d))) return true;
|
|
211
|
+
return false;
|
|
212
|
+
},
|
|
213
|
+
[minDate, maxDate, disabledDates],
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const navigateMonth = useCallback((direction: 1 | -1) => {
|
|
217
|
+
setCurrentMonth((prev) => addMonths(prev, direction));
|
|
218
|
+
}, []);
|
|
219
|
+
|
|
220
|
+
// Handle date press for both single and range modes
|
|
221
|
+
const handleDatePress = useCallback(
|
|
222
|
+
(date: Date) => {
|
|
223
|
+
if (selectionMode === 'single' && singleOnChange) {
|
|
224
|
+
singleOnChange(date);
|
|
225
|
+
} else if (selectionMode === 'range' && rangeOnChange) {
|
|
226
|
+
// Range selection logic:
|
|
227
|
+
// 1. If no start date, set start date
|
|
228
|
+
// 2. If start date but no end date, set end date (if after start)
|
|
229
|
+
// 3. If both dates exist, reset to new start date
|
|
230
|
+
if (!rangeStart || (rangeStart && rangeEnd)) {
|
|
231
|
+
rangeOnChange({ startDate: date, endDate: null });
|
|
232
|
+
} else {
|
|
233
|
+
// Have start but no end
|
|
234
|
+
if (compareDates(date, rangeStart) < 0) {
|
|
235
|
+
// Clicked before start - make this the new start
|
|
236
|
+
rangeOnChange({ startDate: date, endDate: null });
|
|
237
|
+
} else if (isSameDay(date, rangeStart)) {
|
|
238
|
+
// Clicked same day - clear selection
|
|
239
|
+
rangeOnChange({ startDate: date, endDate: null });
|
|
240
|
+
} else {
|
|
241
|
+
// Clicked after start - set as end date
|
|
242
|
+
rangeOnChange({ startDate: rangeStart, endDate: date });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
[selectionMode, singleOnChange, rangeOnChange, rangeStart, rangeEnd],
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// Check if date is in range (between start and end)
|
|
251
|
+
const isDateInRange = useCallback(
|
|
252
|
+
(date: Date | null): boolean => {
|
|
253
|
+
if (!date || selectionMode !== 'range' || !rangeStart || !rangeEnd) {
|
|
188
254
|
return false;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
255
|
+
}
|
|
256
|
+
return (
|
|
257
|
+
compareDates(date, rangeStart) > 0 && compareDates(date, rangeEnd) < 0
|
|
258
|
+
);
|
|
259
|
+
},
|
|
260
|
+
[selectionMode, rangeStart, rangeEnd],
|
|
261
|
+
);
|
|
192
262
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
263
|
+
const renderDay = useCallback(
|
|
264
|
+
({ item }: { item: Date | null }) => {
|
|
265
|
+
const isRangeStart =
|
|
266
|
+
selectionMode === 'range' && item && rangeStart
|
|
267
|
+
? isSameDay(item, rangeStart)
|
|
268
|
+
: false;
|
|
269
|
+
const isRangeEnd =
|
|
270
|
+
selectionMode === 'range' && item && rangeEnd
|
|
271
|
+
? isSameDay(item, rangeEnd)
|
|
272
|
+
: false;
|
|
273
|
+
const isSelected =
|
|
274
|
+
selectionMode === 'single' && item && singleValue
|
|
275
|
+
? isSameDay(item, singleValue)
|
|
276
|
+
: false;
|
|
196
277
|
|
|
197
|
-
|
|
198
|
-
({ item }: { item: Date | null }) => (
|
|
278
|
+
return (
|
|
199
279
|
<DayCell
|
|
200
280
|
date={item}
|
|
201
|
-
isSelected={
|
|
281
|
+
isSelected={isSelected}
|
|
202
282
|
isToday={item ? isSameDay(item, todayDate) : false}
|
|
203
283
|
isDisabled={isDisabled(item)}
|
|
204
284
|
isWeekend={
|
|
205
285
|
item ? getDayOfWeek(item) === 0 || getDayOfWeek(item) === 6 : false
|
|
206
286
|
}
|
|
207
|
-
|
|
287
|
+
isRangeStart={isRangeStart}
|
|
288
|
+
isRangeEnd={isRangeEnd}
|
|
289
|
+
isInRange={isDateInRange(item)}
|
|
290
|
+
onPress={
|
|
291
|
+
item && !isDisabled(item) ? () => handleDatePress(item) : undefined
|
|
292
|
+
}
|
|
208
293
|
/>
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
294
|
+
);
|
|
295
|
+
},
|
|
296
|
+
[
|
|
297
|
+
selectionMode,
|
|
298
|
+
singleValue,
|
|
299
|
+
rangeStart,
|
|
300
|
+
rangeEnd,
|
|
301
|
+
todayDate,
|
|
302
|
+
isDisabled,
|
|
303
|
+
isDateInRange,
|
|
304
|
+
handleDatePress,
|
|
305
|
+
],
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const keyExtractor = useCallback(
|
|
309
|
+
(item: Date | null, index: number) =>
|
|
310
|
+
item?.toISOString() ?? `empty-${index}`,
|
|
311
|
+
[],
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const getItemLayout = useCallback(
|
|
315
|
+
(_data: ArrayLike<Date | null> | null | undefined, index: number) => ({
|
|
316
|
+
length: CELL_SIZE,
|
|
317
|
+
offset: CELL_SIZE * Math.floor(index / 7),
|
|
318
|
+
index,
|
|
319
|
+
}),
|
|
320
|
+
[],
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
// Build override styles
|
|
324
|
+
const containerStyle = useMemo(
|
|
325
|
+
() => ({
|
|
326
|
+
backgroundColor:
|
|
327
|
+
overrides?.backgroundColor ?? DEFAULT_COLORS.backgroundColor,
|
|
328
|
+
borderRadius: overrides?.borderRadius ?? DEFAULT_COLORS.borderRadius,
|
|
329
|
+
padding: overrides?.padding ?? DEFAULT_COLORS.padding,
|
|
330
|
+
}),
|
|
331
|
+
[overrides],
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
const navTextStyle = useMemo(
|
|
335
|
+
() => ({
|
|
336
|
+
color: overrides?.primaryColor ?? DEFAULT_COLORS.primaryColor,
|
|
337
|
+
}),
|
|
338
|
+
[overrides],
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
const monthTitleStyle = useMemo(
|
|
342
|
+
() => ({
|
|
343
|
+
color: overrides?.textColor ?? DEFAULT_COLORS.textColor,
|
|
344
|
+
}),
|
|
345
|
+
[overrides],
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
const weekDayTextStyle = useMemo(
|
|
349
|
+
() => ({
|
|
350
|
+
color: overrides?.textWeekendColor ?? DEFAULT_COLORS.weekdayColor,
|
|
351
|
+
}),
|
|
352
|
+
[overrides],
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
return (
|
|
356
|
+
<View style={[webStyles.container, containerStyle]}>
|
|
357
|
+
<View style={webStyles.header}>
|
|
358
|
+
<Pressable
|
|
359
|
+
onPress={() => navigateMonth(-1)}
|
|
360
|
+
style={webStyles.navButton}
|
|
361
|
+
>
|
|
362
|
+
<Text style={[webStyles.navText, navTextStyle]}>‹</Text>
|
|
363
|
+
</Pressable>
|
|
364
|
+
<Text style={[webStyles.monthTitle, monthTitleStyle]}>
|
|
365
|
+
{formatYearMonth(currentMonth)}
|
|
366
|
+
</Text>
|
|
367
|
+
<Pressable onPress={() => navigateMonth(1)} style={webStyles.navButton}>
|
|
368
|
+
<Text style={[webStyles.navText, navTextStyle]}>›</Text>
|
|
369
|
+
</Pressable>
|
|
370
|
+
</View>
|
|
371
|
+
|
|
372
|
+
<View style={webStyles.weekDays}>
|
|
373
|
+
{weekDays.map((day) => (
|
|
374
|
+
<Text key={day} style={[webStyles.weekDayText, weekDayTextStyle]}>
|
|
375
|
+
{day}
|
|
271
376
|
</Text>
|
|
272
|
-
|
|
273
|
-
onPress={() => navigateMonth(1)}
|
|
274
|
-
style={webStyles.navButton}
|
|
275
|
-
>
|
|
276
|
-
<Text style={[webStyles.navText, navTextStyle]}>›</Text>
|
|
277
|
-
</Pressable>
|
|
278
|
-
</View>
|
|
279
|
-
|
|
280
|
-
<View style={webStyles.weekDays}>
|
|
281
|
-
{weekDays.map((day) => (
|
|
282
|
-
<Text key={day} style={[webStyles.weekDayText, weekDayTextStyle]}>
|
|
283
|
-
{day}
|
|
284
|
-
</Text>
|
|
285
|
-
))}
|
|
286
|
-
</View>
|
|
287
|
-
|
|
288
|
-
<FlatList
|
|
289
|
-
data={days}
|
|
290
|
-
renderItem={renderDay}
|
|
291
|
-
keyExtractor={keyExtractor}
|
|
292
|
-
getItemLayout={getItemLayout}
|
|
293
|
-
numColumns={7}
|
|
294
|
-
scrollEnabled={false}
|
|
295
|
-
removeClippedSubviews={true}
|
|
296
|
-
maxToRenderPerBatch={14}
|
|
297
|
-
windowSize={3}
|
|
298
|
-
initialNumToRender={42}
|
|
299
|
-
/>
|
|
377
|
+
))}
|
|
300
378
|
</View>
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
379
|
+
|
|
380
|
+
<FlatList
|
|
381
|
+
data={days}
|
|
382
|
+
renderItem={renderDay}
|
|
383
|
+
keyExtractor={keyExtractor}
|
|
384
|
+
getItemLayout={getItemLayout}
|
|
385
|
+
numColumns={7}
|
|
386
|
+
scrollEnabled={false}
|
|
387
|
+
removeClippedSubviews={true}
|
|
388
|
+
maxToRenderPerBatch={14}
|
|
389
|
+
windowSize={3}
|
|
390
|
+
initialNumToRender={42}
|
|
391
|
+
/>
|
|
392
|
+
</View>
|
|
393
|
+
);
|
|
394
|
+
});
|
|
304
395
|
|
|
305
396
|
CalendarGrid.displayName = 'CalendarGrid';
|
|
@@ -9,6 +9,9 @@ interface DayCellProps {
|
|
|
9
9
|
isToday: boolean;
|
|
10
10
|
isDisabled: boolean;
|
|
11
11
|
isWeekend: boolean;
|
|
12
|
+
isRangeStart?: boolean;
|
|
13
|
+
isRangeEnd?: boolean;
|
|
14
|
+
isInRange?: boolean;
|
|
12
15
|
onPress?: () => void;
|
|
13
16
|
}
|
|
14
17
|
|
|
@@ -17,10 +20,12 @@ const DEFAULT_COLORS = {
|
|
|
17
20
|
cellBackground: 'transparent',
|
|
18
21
|
cellSelected: '#4DA6FF',
|
|
19
22
|
cellToday: '#1E3A5F',
|
|
23
|
+
cellInRange: 'rgba(77, 166, 255, 0.2)',
|
|
20
24
|
textDefault: '#FFFFFF',
|
|
21
25
|
textSelected: '#FFFFFF',
|
|
22
26
|
textDisabled: '#555555',
|
|
23
27
|
textWeekend: '#8E8E93',
|
|
28
|
+
textInRange: '#FFFFFF',
|
|
24
29
|
primary: '#4DA6FF',
|
|
25
30
|
cellBorderRadius: 22,
|
|
26
31
|
};
|
|
@@ -39,7 +44,17 @@ const styles = StyleSheet.create({
|
|
|
39
44
|
});
|
|
40
45
|
|
|
41
46
|
export const DayCell: React.FC<DayCellProps> = memo(
|
|
42
|
-
({
|
|
47
|
+
({
|
|
48
|
+
date,
|
|
49
|
+
isSelected,
|
|
50
|
+
isToday,
|
|
51
|
+
isDisabled,
|
|
52
|
+
isWeekend,
|
|
53
|
+
isRangeStart,
|
|
54
|
+
isRangeEnd,
|
|
55
|
+
isInRange,
|
|
56
|
+
onPress,
|
|
57
|
+
}) => {
|
|
43
58
|
const overrides = useDatePickerOverrides();
|
|
44
59
|
|
|
45
60
|
// Build cell style based on state and overrides
|
|
@@ -49,13 +64,18 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
49
64
|
backgroundColor: DEFAULT_COLORS.cellBackground,
|
|
50
65
|
};
|
|
51
66
|
|
|
52
|
-
|
|
67
|
+
// Range start/end get selected styling
|
|
68
|
+
if (isRangeStart || isRangeEnd || isSelected) {
|
|
53
69
|
style.backgroundColor =
|
|
54
70
|
overrides?.cellSelectedColor ??
|
|
55
71
|
overrides?.primaryColor ??
|
|
56
72
|
DEFAULT_COLORS.cellSelected;
|
|
57
73
|
style.borderRadius =
|
|
58
74
|
overrides?.cellBorderRadius ?? DEFAULT_COLORS.cellBorderRadius;
|
|
75
|
+
} else if (isInRange) {
|
|
76
|
+
// Dates in range get lighter background
|
|
77
|
+
style.backgroundColor =
|
|
78
|
+
overrides?.cellInRangeColor ?? DEFAULT_COLORS.cellInRange;
|
|
59
79
|
} else if (isToday) {
|
|
60
80
|
style.backgroundColor =
|
|
61
81
|
overrides?.cellTodayColor ?? DEFAULT_COLORS.cellToday;
|
|
@@ -70,7 +90,15 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
70
90
|
}
|
|
71
91
|
|
|
72
92
|
return style;
|
|
73
|
-
}, [
|
|
93
|
+
}, [
|
|
94
|
+
overrides,
|
|
95
|
+
isSelected,
|
|
96
|
+
isToday,
|
|
97
|
+
isDisabled,
|
|
98
|
+
isRangeStart,
|
|
99
|
+
isRangeEnd,
|
|
100
|
+
isInRange,
|
|
101
|
+
]);
|
|
74
102
|
|
|
75
103
|
// Build text style based on state and overrides
|
|
76
104
|
const textStyle = useMemo(() => {
|
|
@@ -79,10 +107,12 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
79
107
|
fontWeight: '400' as const,
|
|
80
108
|
};
|
|
81
109
|
|
|
82
|
-
if (isSelected) {
|
|
110
|
+
if (isRangeStart || isRangeEnd || isSelected) {
|
|
83
111
|
style.color =
|
|
84
112
|
overrides?.textSelectedColor ?? DEFAULT_COLORS.textSelected;
|
|
85
113
|
style.fontWeight = '600';
|
|
114
|
+
} else if (isInRange) {
|
|
115
|
+
style.color = overrides?.textInRangeColor ?? DEFAULT_COLORS.textInRange;
|
|
86
116
|
} else if (isToday) {
|
|
87
117
|
style.color = overrides?.primaryColor ?? DEFAULT_COLORS.primary;
|
|
88
118
|
style.fontWeight = '600';
|
|
@@ -94,7 +124,16 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
94
124
|
}
|
|
95
125
|
|
|
96
126
|
return style;
|
|
97
|
-
}, [
|
|
127
|
+
}, [
|
|
128
|
+
overrides,
|
|
129
|
+
isSelected,
|
|
130
|
+
isToday,
|
|
131
|
+
isDisabled,
|
|
132
|
+
isWeekend,
|
|
133
|
+
isRangeStart,
|
|
134
|
+
isRangeEnd,
|
|
135
|
+
isInRange,
|
|
136
|
+
]);
|
|
98
137
|
|
|
99
138
|
if (!date) {
|
|
100
139
|
return <Pressable style={styles.cell} disabled />;
|
|
@@ -121,7 +160,10 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
121
160
|
prev.date?.getTime() === next.date?.getTime() &&
|
|
122
161
|
prev.isSelected === next.isSelected &&
|
|
123
162
|
prev.isToday === next.isToday &&
|
|
124
|
-
prev.isDisabled === next.isDisabled
|
|
163
|
+
prev.isDisabled === next.isDisabled &&
|
|
164
|
+
prev.isRangeStart === next.isRangeStart &&
|
|
165
|
+
prev.isRangeEnd === next.isRangeEnd &&
|
|
166
|
+
prev.isInRange === next.isInRange,
|
|
125
167
|
);
|
|
126
168
|
|
|
127
169
|
DayCell.displayName = 'DayCell';
|
|
@@ -10,6 +10,9 @@ interface DayCellProps {
|
|
|
10
10
|
isToday: boolean;
|
|
11
11
|
isDisabled: boolean;
|
|
12
12
|
isWeekend: boolean;
|
|
13
|
+
isRangeStart?: boolean;
|
|
14
|
+
isRangeEnd?: boolean;
|
|
15
|
+
isInRange?: boolean;
|
|
13
16
|
onPress?: () => void;
|
|
14
17
|
}
|
|
15
18
|
|
|
@@ -18,10 +21,12 @@ const DEFAULT_COLORS = {
|
|
|
18
21
|
cellBackground: 'transparent',
|
|
19
22
|
cellSelected: '#007AFF',
|
|
20
23
|
cellToday: 'rgba(0, 122, 255, 0.1)',
|
|
24
|
+
cellInRange: 'rgba(0, 122, 255, 0.15)',
|
|
21
25
|
textDefault: '#1C1C1E',
|
|
22
26
|
textSelected: '#FFFFFF',
|
|
23
27
|
textDisabled: '#8E8E93',
|
|
24
28
|
textWeekend: '#8E8E93',
|
|
29
|
+
textInRange: '#1C1C1E',
|
|
25
30
|
primary: '#007AFF',
|
|
26
31
|
cellBorderRadius: 22,
|
|
27
32
|
};
|
|
@@ -41,7 +46,17 @@ const webStyles = StyleSheet.create({
|
|
|
41
46
|
});
|
|
42
47
|
|
|
43
48
|
export const DayCell: React.FC<DayCellProps> = memo(
|
|
44
|
-
({
|
|
49
|
+
({
|
|
50
|
+
date,
|
|
51
|
+
isSelected,
|
|
52
|
+
isToday,
|
|
53
|
+
isDisabled,
|
|
54
|
+
isWeekend,
|
|
55
|
+
isRangeStart,
|
|
56
|
+
isRangeEnd,
|
|
57
|
+
isInRange,
|
|
58
|
+
onPress,
|
|
59
|
+
}) => {
|
|
45
60
|
const overrides = useDatePickerOverrides();
|
|
46
61
|
|
|
47
62
|
// Build cell style based on state and overrides
|
|
@@ -51,13 +66,18 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
51
66
|
backgroundColor: DEFAULT_COLORS.cellBackground,
|
|
52
67
|
};
|
|
53
68
|
|
|
54
|
-
|
|
69
|
+
// Range start/end get selected styling
|
|
70
|
+
if (isRangeStart || isRangeEnd || isSelected) {
|
|
55
71
|
style.backgroundColor =
|
|
56
72
|
overrides?.cellSelectedColor ??
|
|
57
73
|
overrides?.primaryColor ??
|
|
58
74
|
DEFAULT_COLORS.cellSelected;
|
|
59
75
|
style.borderRadius =
|
|
60
76
|
overrides?.cellBorderRadius ?? DEFAULT_COLORS.cellBorderRadius;
|
|
77
|
+
} else if (isInRange) {
|
|
78
|
+
// Dates in range get lighter background
|
|
79
|
+
style.backgroundColor =
|
|
80
|
+
overrides?.cellInRangeColor ?? DEFAULT_COLORS.cellInRange;
|
|
61
81
|
} else if (isToday) {
|
|
62
82
|
style.backgroundColor =
|
|
63
83
|
overrides?.cellTodayColor ?? DEFAULT_COLORS.cellToday;
|
|
@@ -72,7 +92,15 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
72
92
|
}
|
|
73
93
|
|
|
74
94
|
return style;
|
|
75
|
-
}, [
|
|
95
|
+
}, [
|
|
96
|
+
overrides,
|
|
97
|
+
isSelected,
|
|
98
|
+
isToday,
|
|
99
|
+
isDisabled,
|
|
100
|
+
isRangeStart,
|
|
101
|
+
isRangeEnd,
|
|
102
|
+
isInRange,
|
|
103
|
+
]);
|
|
76
104
|
|
|
77
105
|
// Build text style based on state and overrides
|
|
78
106
|
const textStyle = useMemo(() => {
|
|
@@ -81,10 +109,12 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
81
109
|
fontWeight: '400' as const,
|
|
82
110
|
};
|
|
83
111
|
|
|
84
|
-
if (isSelected) {
|
|
112
|
+
if (isRangeStart || isRangeEnd || isSelected) {
|
|
85
113
|
style.color =
|
|
86
114
|
overrides?.textSelectedColor ?? DEFAULT_COLORS.textSelected;
|
|
87
115
|
style.fontWeight = '600';
|
|
116
|
+
} else if (isInRange) {
|
|
117
|
+
style.color = overrides?.textInRangeColor ?? DEFAULT_COLORS.textInRange;
|
|
88
118
|
} else if (isToday) {
|
|
89
119
|
style.color = overrides?.primaryColor ?? DEFAULT_COLORS.primary;
|
|
90
120
|
style.fontWeight = '600';
|
|
@@ -96,7 +126,16 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
96
126
|
}
|
|
97
127
|
|
|
98
128
|
return style;
|
|
99
|
-
}, [
|
|
129
|
+
}, [
|
|
130
|
+
overrides,
|
|
131
|
+
isSelected,
|
|
132
|
+
isToday,
|
|
133
|
+
isDisabled,
|
|
134
|
+
isWeekend,
|
|
135
|
+
isRangeStart,
|
|
136
|
+
isRangeEnd,
|
|
137
|
+
isInRange,
|
|
138
|
+
]);
|
|
100
139
|
|
|
101
140
|
if (!date) {
|
|
102
141
|
return <Pressable style={webStyles.cell} disabled />;
|
|
@@ -123,7 +162,10 @@ export const DayCell: React.FC<DayCellProps> = memo(
|
|
|
123
162
|
prev.date?.getTime() === next.date?.getTime() &&
|
|
124
163
|
prev.isSelected === next.isSelected &&
|
|
125
164
|
prev.isToday === next.isToday &&
|
|
126
|
-
prev.isDisabled === next.isDisabled
|
|
165
|
+
prev.isDisabled === next.isDisabled &&
|
|
166
|
+
prev.isRangeStart === next.isRangeStart &&
|
|
167
|
+
prev.isRangeEnd === next.isRangeEnd &&
|
|
168
|
+
prev.isInRange === next.isInRange,
|
|
127
169
|
);
|
|
128
170
|
|
|
129
171
|
DayCell.displayName = 'DayCell';
|