@scripso-homepad/ui 0.4.31 → 0.4.34
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/dist/index.cjs +101 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +101 -67
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Calendar.tsx +2 -5
- package/src/components/DatePicker.tsx +55 -11
- package/src/theme/calendar.ts +9 -1
package/package.json
CHANGED
|
@@ -81,12 +81,9 @@ function resolveDayCellStyle(day: CalendarDayCell): StyleProp<ViewStyle> {
|
|
|
81
81
|
return [calendarDropdownMetrics.dayCell, calendarDropdownMetrics.dayCellSelected];
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
const useMutedBackground =
|
|
85
|
-
day.inCurrentMonth && !day.isToday && (day.isPast || day.isDisabled);
|
|
86
|
-
|
|
87
84
|
return [
|
|
88
85
|
calendarDropdownMetrics.dayCell,
|
|
89
|
-
|
|
86
|
+
day.isDisabled ? calendarDropdownMetrics.dayCellDisabled : null,
|
|
90
87
|
];
|
|
91
88
|
}
|
|
92
89
|
|
|
@@ -103,7 +100,7 @@ function resolveDayLabelStyle(day: CalendarDayCell): StyleProp<TextStyle> {
|
|
|
103
100
|
return [calendarDropdownMetrics.dayLabel, calendarDropdownMetrics.dayLabelToday];
|
|
104
101
|
}
|
|
105
102
|
|
|
106
|
-
if (day.
|
|
103
|
+
if (day.isDisabled) {
|
|
107
104
|
return [calendarDropdownMetrics.dayLabel, calendarDropdownMetrics.dayLabelMuted];
|
|
108
105
|
}
|
|
109
106
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEffect, useRef, useState, type ComponentRef } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Modal,
|
|
4
|
+
Platform,
|
|
4
5
|
Pressable,
|
|
5
6
|
StyleSheet,
|
|
6
7
|
Text,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
calendarFieldTypography,
|
|
20
21
|
calendarLabelTypography,
|
|
21
22
|
resolveCalendarDropdownLeft,
|
|
23
|
+
resolveCalendarDropdownLeftCentered,
|
|
22
24
|
resolveCalendarDropdownTop,
|
|
23
25
|
} from "../theme/calendar";
|
|
24
26
|
import { getSelectFieldStyles, resolveInputVisualState, SELECT_LABEL_GAP } from "../theme/select";
|
|
@@ -59,6 +61,10 @@ type DatePickerFieldProps = {
|
|
|
59
61
|
hintClassName?: string;
|
|
60
62
|
/** Opens the calendar popup on mount (useful for Storybook previews). */
|
|
61
63
|
defaultOpen?: boolean;
|
|
64
|
+
/** Controlled open state for the calendar popup. */
|
|
65
|
+
open?: boolean;
|
|
66
|
+
/** Called when the calendar popup opens or closes. */
|
|
67
|
+
onOpenChange?: (open: boolean) => void;
|
|
62
68
|
/** Closes the popup after a complete range is selected. Defaults to true in range mode. */
|
|
63
69
|
closeOnRangeComplete?: boolean;
|
|
64
70
|
};
|
|
@@ -78,6 +84,8 @@ export type DatePickerRangeProps = Omit<CalendarRangeProps, "style" | "className
|
|
|
78
84
|
|
|
79
85
|
export type DatePickerProps = DatePickerSingleProps | DatePickerRangeProps;
|
|
80
86
|
|
|
87
|
+
const isDesktopCalendar = Platform.OS === "web";
|
|
88
|
+
|
|
81
89
|
export function DatePicker(props: DatePickerProps) {
|
|
82
90
|
const {
|
|
83
91
|
label,
|
|
@@ -104,6 +112,8 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
104
112
|
errorClassName,
|
|
105
113
|
hintClassName,
|
|
106
114
|
defaultOpen = false,
|
|
115
|
+
open: controlledOpen,
|
|
116
|
+
onOpenChange,
|
|
107
117
|
} = props;
|
|
108
118
|
const mode = props.mode ?? "single";
|
|
109
119
|
const closeOnRangeComplete =
|
|
@@ -115,7 +125,9 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
115
125
|
const wrapperRef = useRef<ComponentRef<typeof View>>(null);
|
|
116
126
|
const triggerRef = useRef<ComponentRef<typeof Pressable>>(null);
|
|
117
127
|
const helperRef = useRef<ComponentRef<typeof Text>>(null);
|
|
118
|
-
const
|
|
128
|
+
const isControlled = controlledOpen !== undefined;
|
|
129
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
|
130
|
+
const open = isControlled ? controlledOpen : uncontrolledOpen;
|
|
119
131
|
const [anchor, setAnchor] = useState<{
|
|
120
132
|
x: number;
|
|
121
133
|
y: number;
|
|
@@ -127,6 +139,12 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
127
139
|
useApplyWebClassName(triggerRef, fieldClassName);
|
|
128
140
|
useApplyWebClassName(helperRef, error ? errorClassName : hintClassName);
|
|
129
141
|
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!open) {
|
|
144
|
+
setAnchor(null);
|
|
145
|
+
}
|
|
146
|
+
}, [open]);
|
|
147
|
+
|
|
130
148
|
useEffect(() => {
|
|
131
149
|
if (!defaultOpen || disabled) {
|
|
132
150
|
return;
|
|
@@ -135,7 +153,7 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
135
153
|
const frame = requestAnimationFrame(() => {
|
|
136
154
|
triggerRef.current?.measureInWindow((x, y, width, height) => {
|
|
137
155
|
setAnchor({ x, y, width, height });
|
|
138
|
-
|
|
156
|
+
setOpenState(true);
|
|
139
157
|
});
|
|
140
158
|
});
|
|
141
159
|
|
|
@@ -184,15 +202,23 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
184
202
|
};
|
|
185
203
|
const helperMessage = error ?? hint;
|
|
186
204
|
const panelWidth = CALENDAR_PANEL_WIDTH;
|
|
187
|
-
const panelLeft = anchor
|
|
205
|
+
const panelLeft = anchor
|
|
206
|
+
? isDesktopCalendar
|
|
207
|
+
? resolveCalendarDropdownLeft(anchor, panelWidth)
|
|
208
|
+
: resolveCalendarDropdownLeftCentered(panelWidth)
|
|
209
|
+
: 0;
|
|
188
210
|
const dropdownTop = anchor ? resolveCalendarDropdownTop(anchor) : 0;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
(
|
|
192
|
-
|
|
211
|
+
|
|
212
|
+
function setOpenState(next: boolean) {
|
|
213
|
+
if (!isControlled) {
|
|
214
|
+
setUncontrolledOpen(next);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
onOpenChange?.(next);
|
|
218
|
+
}
|
|
193
219
|
|
|
194
220
|
function closePicker() {
|
|
195
|
-
|
|
221
|
+
setOpenState(false);
|
|
196
222
|
setAnchor(null);
|
|
197
223
|
}
|
|
198
224
|
|
|
@@ -203,10 +229,15 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
203
229
|
|
|
204
230
|
triggerRef.current?.measureInWindow((x, y, width, height) => {
|
|
205
231
|
setAnchor({ x, y, width, height });
|
|
206
|
-
|
|
232
|
+
setOpenState(true);
|
|
207
233
|
});
|
|
208
234
|
}
|
|
209
235
|
|
|
236
|
+
const resolvedDefaultViewDate =
|
|
237
|
+
defaultViewDate ??
|
|
238
|
+
(props.mode === "range" ? props.value?.start : props.value) ??
|
|
239
|
+
today;
|
|
240
|
+
|
|
210
241
|
function handleSingleSelect(date: Date | undefined) {
|
|
211
242
|
if (props.mode === "range") {
|
|
212
243
|
return;
|
|
@@ -283,7 +314,14 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
283
314
|
</Text>
|
|
284
315
|
) : null}
|
|
285
316
|
|
|
286
|
-
<Modal
|
|
317
|
+
<Modal
|
|
318
|
+
visible={open}
|
|
319
|
+
transparent
|
|
320
|
+
animationType="fade"
|
|
321
|
+
onRequestClose={closePicker}
|
|
322
|
+
statusBarTranslucent
|
|
323
|
+
{...(Platform.OS === "ios" ? { presentationStyle: "overFullScreen" as const } : null)}
|
|
324
|
+
>
|
|
287
325
|
<View style={styles.modalRoot}>
|
|
288
326
|
<Pressable
|
|
289
327
|
style={styles.backdrop}
|
|
@@ -293,6 +331,7 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
293
331
|
/>
|
|
294
332
|
{anchor ? (
|
|
295
333
|
<View
|
|
334
|
+
pointerEvents="box-none"
|
|
296
335
|
style={[
|
|
297
336
|
styles.panelPosition,
|
|
298
337
|
{
|
|
@@ -303,6 +342,7 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
303
342
|
},
|
|
304
343
|
]}
|
|
305
344
|
>
|
|
345
|
+
<View pointerEvents="auto">
|
|
306
346
|
{props.mode === "range" ? (
|
|
307
347
|
<Calendar
|
|
308
348
|
mode="range"
|
|
@@ -334,6 +374,7 @@ export function DatePicker(props: DatePickerProps) {
|
|
|
334
374
|
className={panelClassName}
|
|
335
375
|
/>
|
|
336
376
|
)}
|
|
377
|
+
</View>
|
|
337
378
|
</View>
|
|
338
379
|
) : null}
|
|
339
380
|
</View>
|
|
@@ -378,10 +419,13 @@ const styles = StyleSheet.create({
|
|
|
378
419
|
},
|
|
379
420
|
modalRoot: {
|
|
380
421
|
flex: 1,
|
|
422
|
+
width: "100%",
|
|
423
|
+
height: "100%",
|
|
381
424
|
},
|
|
382
425
|
backdrop: {
|
|
383
426
|
...StyleSheet.absoluteFillObject,
|
|
384
|
-
backgroundColor:
|
|
427
|
+
backgroundColor: "rgba(0, 0, 0, 0.35)",
|
|
428
|
+
zIndex: 0,
|
|
385
429
|
},
|
|
386
430
|
panelPosition: {
|
|
387
431
|
position: "absolute",
|
package/src/theme/calendar.ts
CHANGED
|
@@ -182,7 +182,7 @@ export const calendarDropdownMetrics = StyleSheet.create({
|
|
|
182
182
|
} as ViewStyle)
|
|
183
183
|
: null),
|
|
184
184
|
},
|
|
185
|
-
|
|
185
|
+
dayCellDisabled: {
|
|
186
186
|
backgroundColor: colors.stormGray50,
|
|
187
187
|
},
|
|
188
188
|
dayCellSelected: {
|
|
@@ -321,6 +321,14 @@ export function resolveCalendarDropdownLeft(
|
|
|
321
321
|
return Math.max(0, Math.min(anchor.x, maxLeft));
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
+
export function resolveCalendarDropdownLeftCentered(
|
|
325
|
+
panelWidth: number = CALENDAR_PANEL_WIDTH,
|
|
326
|
+
): number {
|
|
327
|
+
const windowWidth = Dimensions.get("window").width;
|
|
328
|
+
|
|
329
|
+
return Math.max(0, (windowWidth - panelWidth) / 2);
|
|
330
|
+
}
|
|
331
|
+
|
|
324
332
|
export const calendarFieldMetrics = StyleSheet.create({
|
|
325
333
|
container: {
|
|
326
334
|
flexDirection: "row",
|