@ecohouse/ui 0.1.39 → 0.1.40
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 +197 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -92
- package/dist/index.d.ts +1 -92
- package/dist/index.js +198 -103
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DatePicker/DatePicker.tsx +2 -1
- package/src/components/Drawer/Drawer.tsx +197 -44
- package/src/icons/index.ts +0 -2
- package/src/index.ts +0 -11
package/package.json
CHANGED
|
@@ -716,9 +716,10 @@ const styles = StyleSheet.create({
|
|
|
716
716
|
},
|
|
717
717
|
rangeValueRow: {
|
|
718
718
|
minWidth: 0,
|
|
719
|
+
width: "100%",
|
|
719
720
|
flexDirection: "row",
|
|
720
721
|
alignItems: "center",
|
|
721
|
-
justifyContent: "
|
|
722
|
+
justifyContent: "space-between",
|
|
722
723
|
flexWrap: "nowrap",
|
|
723
724
|
gap: 10,
|
|
724
725
|
},
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
type ComponentRef,
|
|
9
|
+
type ReactNode,
|
|
10
|
+
type TransitionEvent,
|
|
11
|
+
} from "react";
|
|
2
12
|
import { createPortal } from "react-dom";
|
|
3
13
|
import {
|
|
14
|
+
Animated,
|
|
15
|
+
Easing,
|
|
4
16
|
Modal as RNModal,
|
|
5
17
|
Platform,
|
|
6
18
|
Pressable,
|
|
@@ -25,6 +37,8 @@ export interface DrawerProps {
|
|
|
25
37
|
title?: string;
|
|
26
38
|
/** Panel width in px. Defaults to `652`. */
|
|
27
39
|
width?: number;
|
|
40
|
+
/** Open/close width transition duration in ms. Defaults to `320`. */
|
|
41
|
+
animationDuration?: number;
|
|
28
42
|
/** Close when the dimmed backdrop is pressed. Defaults to `true`. */
|
|
29
43
|
closeOnBackdropPress?: boolean;
|
|
30
44
|
/** Accessible name for the backdrop control. Defaults to `"Close"`. */
|
|
@@ -39,36 +53,10 @@ export interface DrawerProps {
|
|
|
39
53
|
|
|
40
54
|
const BACKDROP_COLOR = "#09090933";
|
|
41
55
|
const DEFAULT_WIDTH = 652;
|
|
56
|
+
const DEFAULT_DURATION = 320;
|
|
42
57
|
const WEB_Z_INDEX = 500;
|
|
43
58
|
const CLOSE_ICON_SIZE = 24;
|
|
44
59
|
|
|
45
|
-
/** Plain DOM backdrop — RN StyleSheet often drops `backdrop-filter`. */
|
|
46
|
-
const webBackdropDomStyle = {
|
|
47
|
-
position: "absolute",
|
|
48
|
-
top: 0,
|
|
49
|
-
right: 0,
|
|
50
|
-
bottom: 0,
|
|
51
|
-
left: 0,
|
|
52
|
-
backgroundColor: BACKDROP_COLOR,
|
|
53
|
-
backdropFilter: "blur(18px)",
|
|
54
|
-
WebkitBackdropFilter: "blur(18px)",
|
|
55
|
-
border: "none",
|
|
56
|
-
padding: 0,
|
|
57
|
-
margin: 0,
|
|
58
|
-
cursor: "pointer",
|
|
59
|
-
} as const;
|
|
60
|
-
|
|
61
|
-
const webRootDomStyle = {
|
|
62
|
-
position: "fixed",
|
|
63
|
-
top: 0,
|
|
64
|
-
right: 0,
|
|
65
|
-
bottom: 0,
|
|
66
|
-
left: 0,
|
|
67
|
-
zIndex: WEB_Z_INDEX,
|
|
68
|
-
display: "flex",
|
|
69
|
-
justifyContent: "flex-end",
|
|
70
|
-
} as const;
|
|
71
|
-
|
|
72
60
|
export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(function Drawer(
|
|
73
61
|
{
|
|
74
62
|
open,
|
|
@@ -76,6 +64,7 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
76
64
|
children,
|
|
77
65
|
title,
|
|
78
66
|
width = DEFAULT_WIDTH,
|
|
67
|
+
animationDuration = DEFAULT_DURATION,
|
|
79
68
|
closeOnBackdropPress = true,
|
|
80
69
|
backdropAccessibilityLabel = "Close",
|
|
81
70
|
closeAccessibilityLabel = "Close",
|
|
@@ -87,11 +76,17 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
87
76
|
const panelRef = useRef<ComponentRef<typeof View>>(null);
|
|
88
77
|
const setPanelRef = useMemo(() => mergeRefs(panelRef, forwardedRef), [forwardedRef]);
|
|
89
78
|
const resolvedClassName = className?.trim() || undefined;
|
|
79
|
+
const closeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
90
80
|
|
|
91
|
-
|
|
81
|
+
const [mounted, setMounted] = useState(open);
|
|
82
|
+
const [entered, setEntered] = useState(false);
|
|
83
|
+
const animatedWidth = useRef(new Animated.Value(open ? width : 0)).current;
|
|
84
|
+
const animatedBackdrop = useRef(new Animated.Value(open ? 1 : 0)).current;
|
|
85
|
+
|
|
86
|
+
useApplyWebClassName(panelRef, resolvedClassName, mounted && Platform.OS === "web");
|
|
92
87
|
|
|
93
88
|
useEffect(() => {
|
|
94
|
-
if (!
|
|
89
|
+
if (!mounted || Platform.OS !== "web") return undefined;
|
|
95
90
|
|
|
96
91
|
const previousOverflow = document.body.style.overflow;
|
|
97
92
|
document.body.style.overflow = "hidden";
|
|
@@ -105,7 +100,96 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
105
100
|
document.body.style.overflow = previousOverflow;
|
|
106
101
|
document.removeEventListener("keydown", onKeyDown);
|
|
107
102
|
};
|
|
108
|
-
}, [
|
|
103
|
+
}, [mounted, onClose]);
|
|
104
|
+
|
|
105
|
+
// Web: mount at width 0, then expand; on close collapse then unmount.
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (Platform.OS !== "web") return undefined;
|
|
108
|
+
|
|
109
|
+
if (closeTimeoutRef.current) {
|
|
110
|
+
clearTimeout(closeTimeoutRef.current);
|
|
111
|
+
closeTimeoutRef.current = null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (open) {
|
|
115
|
+
setMounted(true);
|
|
116
|
+
const frame = requestAnimationFrame(() => {
|
|
117
|
+
requestAnimationFrame(() => setEntered(true));
|
|
118
|
+
});
|
|
119
|
+
return () => cancelAnimationFrame(frame);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
setEntered(false);
|
|
123
|
+
// Fallback if transitionend is skipped (e.g. reduced motion / tab background).
|
|
124
|
+
closeTimeoutRef.current = setTimeout(() => {
|
|
125
|
+
setMounted(false);
|
|
126
|
+
closeTimeoutRef.current = null;
|
|
127
|
+
}, animationDuration + 50);
|
|
128
|
+
|
|
129
|
+
return () => {
|
|
130
|
+
if (closeTimeoutRef.current) {
|
|
131
|
+
clearTimeout(closeTimeoutRef.current);
|
|
132
|
+
closeTimeoutRef.current = null;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}, [open, animationDuration]);
|
|
136
|
+
|
|
137
|
+
// Native: animate width + backdrop opacity.
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (Platform.OS === "web") return undefined;
|
|
140
|
+
|
|
141
|
+
if (open) {
|
|
142
|
+
setMounted(true);
|
|
143
|
+
Animated.parallel([
|
|
144
|
+
Animated.timing(animatedWidth, {
|
|
145
|
+
toValue: width,
|
|
146
|
+
duration: animationDuration,
|
|
147
|
+
easing: Easing.out(Easing.cubic),
|
|
148
|
+
useNativeDriver: false,
|
|
149
|
+
}),
|
|
150
|
+
Animated.timing(animatedBackdrop, {
|
|
151
|
+
toValue: 1,
|
|
152
|
+
duration: animationDuration,
|
|
153
|
+
easing: Easing.out(Easing.cubic),
|
|
154
|
+
useNativeDriver: true,
|
|
155
|
+
}),
|
|
156
|
+
]).start();
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
Animated.parallel([
|
|
161
|
+
Animated.timing(animatedWidth, {
|
|
162
|
+
toValue: 0,
|
|
163
|
+
duration: animationDuration,
|
|
164
|
+
easing: Easing.in(Easing.cubic),
|
|
165
|
+
useNativeDriver: false,
|
|
166
|
+
}),
|
|
167
|
+
Animated.timing(animatedBackdrop, {
|
|
168
|
+
toValue: 0,
|
|
169
|
+
duration: animationDuration,
|
|
170
|
+
easing: Easing.in(Easing.cubic),
|
|
171
|
+
useNativeDriver: true,
|
|
172
|
+
}),
|
|
173
|
+
]).start(({ finished }) => {
|
|
174
|
+
if (finished) setMounted(false);
|
|
175
|
+
});
|
|
176
|
+
return undefined;
|
|
177
|
+
}, [open, width, animationDuration, animatedWidth, animatedBackdrop]);
|
|
178
|
+
|
|
179
|
+
const handleWebTransitionEnd = useCallback(
|
|
180
|
+
(event: TransitionEvent<HTMLDivElement>) => {
|
|
181
|
+
if (event.target !== event.currentTarget) return;
|
|
182
|
+
if (event.propertyName !== "width") return;
|
|
183
|
+
if (!open) {
|
|
184
|
+
if (closeTimeoutRef.current) {
|
|
185
|
+
clearTimeout(closeTimeoutRef.current);
|
|
186
|
+
closeTimeoutRef.current = null;
|
|
187
|
+
}
|
|
188
|
+
setMounted(false);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
[open],
|
|
192
|
+
);
|
|
109
193
|
|
|
110
194
|
const header =
|
|
111
195
|
title != null && title !== "" ? (
|
|
@@ -125,11 +209,11 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
125
209
|
</View>
|
|
126
210
|
) : null;
|
|
127
211
|
|
|
128
|
-
const
|
|
212
|
+
const panelContent = (
|
|
129
213
|
<View
|
|
130
214
|
ref={setPanelRef}
|
|
131
215
|
accessibilityViewIsModal
|
|
132
|
-
style={[styles.
|
|
216
|
+
style={[styles.panelInner, { width }, style]}
|
|
133
217
|
onStartShouldSetResponder={() => true}
|
|
134
218
|
>
|
|
135
219
|
{header}
|
|
@@ -138,50 +222,110 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
138
222
|
);
|
|
139
223
|
|
|
140
224
|
if (Platform.OS === "web") {
|
|
141
|
-
if (!
|
|
225
|
+
if (!mounted || typeof document === "undefined") return null;
|
|
142
226
|
|
|
143
227
|
return createPortal(
|
|
144
|
-
<div
|
|
228
|
+
<div
|
|
229
|
+
style={{
|
|
230
|
+
position: "fixed",
|
|
231
|
+
top: 0,
|
|
232
|
+
right: 0,
|
|
233
|
+
bottom: 0,
|
|
234
|
+
left: 0,
|
|
235
|
+
zIndex: WEB_Z_INDEX,
|
|
236
|
+
display: "flex",
|
|
237
|
+
justifyContent: "flex-end",
|
|
238
|
+
pointerEvents: "auto",
|
|
239
|
+
}}
|
|
240
|
+
role="presentation"
|
|
241
|
+
>
|
|
145
242
|
<button
|
|
146
243
|
type="button"
|
|
147
244
|
aria-label={backdropAccessibilityLabel}
|
|
148
245
|
disabled={!closeOnBackdropPress}
|
|
149
246
|
onClick={closeOnBackdropPress ? onClose : undefined}
|
|
150
|
-
style={
|
|
247
|
+
style={{
|
|
248
|
+
position: "absolute",
|
|
249
|
+
top: 0,
|
|
250
|
+
right: 0,
|
|
251
|
+
bottom: 0,
|
|
252
|
+
left: 0,
|
|
253
|
+
backgroundColor: BACKDROP_COLOR,
|
|
254
|
+
backdropFilter: "blur(18px)",
|
|
255
|
+
WebkitBackdropFilter: "blur(18px)",
|
|
256
|
+
border: "none",
|
|
257
|
+
padding: 0,
|
|
258
|
+
margin: 0,
|
|
259
|
+
cursor: closeOnBackdropPress ? "pointer" : "default",
|
|
260
|
+
opacity: entered ? 1 : 0,
|
|
261
|
+
transition: `opacity ${animationDuration}ms ease`,
|
|
262
|
+
}}
|
|
151
263
|
/>
|
|
264
|
+
{/*
|
|
265
|
+
Shell width animates 0 ↔ panel width. Inner stays fixed-width and
|
|
266
|
+
right-aligned so content isn't crushed and appears to slide in.
|
|
267
|
+
minWidth:0 is required — flex defaults would otherwise lock width to content.
|
|
268
|
+
*/}
|
|
152
269
|
<div
|
|
270
|
+
onTransitionEnd={handleWebTransitionEnd}
|
|
153
271
|
style={{
|
|
154
272
|
position: "relative",
|
|
155
273
|
zIndex: 1,
|
|
156
274
|
height: "100%",
|
|
275
|
+
width: entered
|
|
276
|
+
? Math.min(width, typeof window !== "undefined" ? window.innerWidth : width)
|
|
277
|
+
: 0,
|
|
278
|
+
minWidth: 0,
|
|
157
279
|
maxWidth: "100%",
|
|
158
|
-
|
|
280
|
+
overflow: "hidden",
|
|
281
|
+
transition: `width ${animationDuration}ms cubic-bezier(0.32, 0.72, 0, 1)`,
|
|
282
|
+
willChange: "width",
|
|
283
|
+
boxSizing: "border-box",
|
|
159
284
|
}}
|
|
160
285
|
>
|
|
161
|
-
|
|
286
|
+
<div
|
|
287
|
+
style={{
|
|
288
|
+
width,
|
|
289
|
+
maxWidth: "100%",
|
|
290
|
+
height: "100%",
|
|
291
|
+
marginLeft: "auto",
|
|
292
|
+
display: "flex",
|
|
293
|
+
flexDirection: "column",
|
|
294
|
+
}}
|
|
295
|
+
>
|
|
296
|
+
{panelContent}
|
|
297
|
+
</div>
|
|
162
298
|
</div>
|
|
163
299
|
</div>,
|
|
164
300
|
document.body,
|
|
165
301
|
);
|
|
166
302
|
}
|
|
167
303
|
|
|
304
|
+
if (!mounted) return null;
|
|
305
|
+
|
|
168
306
|
return (
|
|
169
307
|
<RNModal
|
|
170
|
-
visible={
|
|
308
|
+
visible={mounted}
|
|
171
309
|
transparent
|
|
172
|
-
animationType="
|
|
310
|
+
animationType="none"
|
|
173
311
|
onRequestClose={onClose}
|
|
174
312
|
statusBarTranslucent
|
|
175
313
|
>
|
|
176
314
|
<View style={styles.root} accessibilityViewIsModal>
|
|
315
|
+
<Animated.View
|
|
316
|
+
pointerEvents="none"
|
|
317
|
+
style={[styles.backdrop, { opacity: animatedBackdrop }]}
|
|
318
|
+
/>
|
|
177
319
|
<Pressable
|
|
178
320
|
accessibilityRole="button"
|
|
179
321
|
accessibilityLabel={backdropAccessibilityLabel}
|
|
180
322
|
disabled={!closeOnBackdropPress}
|
|
181
323
|
onPress={closeOnBackdropPress ? onClose : undefined}
|
|
182
|
-
style={
|
|
324
|
+
style={StyleSheet.absoluteFill}
|
|
183
325
|
/>
|
|
184
|
-
{
|
|
326
|
+
<Animated.View style={[styles.panelShell, { width: animatedWidth }]}>
|
|
327
|
+
{panelContent}
|
|
328
|
+
</Animated.View>
|
|
185
329
|
</View>
|
|
186
330
|
</RNModal>
|
|
187
331
|
);
|
|
@@ -197,11 +341,19 @@ const styles = StyleSheet.create({
|
|
|
197
341
|
...StyleSheet.absoluteFillObject,
|
|
198
342
|
backgroundColor: BACKDROP_COLOR,
|
|
199
343
|
},
|
|
200
|
-
|
|
344
|
+
panelShell: {
|
|
201
345
|
zIndex: 1,
|
|
346
|
+
height: "100%",
|
|
347
|
+
maxWidth: "100%",
|
|
348
|
+
overflow: "hidden",
|
|
349
|
+
alignItems: "flex-end",
|
|
350
|
+
minWidth: 0,
|
|
351
|
+
},
|
|
352
|
+
panelInner: {
|
|
202
353
|
height: "100%",
|
|
203
354
|
maxWidth: "100%",
|
|
204
355
|
backgroundColor: colors.grey800,
|
|
356
|
+
flexShrink: 0,
|
|
205
357
|
},
|
|
206
358
|
header: {
|
|
207
359
|
height: 65,
|
|
@@ -220,10 +372,11 @@ const styles = StyleSheet.create({
|
|
|
220
372
|
fontFamily: fonts.sans,
|
|
221
373
|
fontSize: 16,
|
|
222
374
|
fontWeight: "600",
|
|
223
|
-
lineHeight:
|
|
375
|
+
lineHeight: 22,
|
|
224
376
|
letterSpacing: 0,
|
|
225
377
|
color: colors.white,
|
|
226
378
|
marginRight: 12,
|
|
379
|
+
overflow: "visible",
|
|
227
380
|
},
|
|
228
381
|
closeButton: {
|
|
229
382
|
width: CLOSE_ICON_SIZE,
|
package/src/icons/index.ts
CHANGED
|
@@ -9,7 +9,6 @@ export { AmenityIcon } from "./Amenity";
|
|
|
9
9
|
export type { AmenityIconProps, AmenityName } from "./Amenity";
|
|
10
10
|
export { AlertTriangleIcon } from "./AlertTriangle";
|
|
11
11
|
export { AppleIcon } from "./Store";
|
|
12
|
-
export { ArrowLeftIcon } from "./ArrowLeft";
|
|
13
12
|
export { ArrowRightIcon } from "./ArrowRight";
|
|
14
13
|
export { BagIcon } from "./Bag";
|
|
15
14
|
export { BanIcon } from "./Ban";
|
|
@@ -26,7 +25,6 @@ export { ChevronDownIcon } from "./ChevronDown";
|
|
|
26
25
|
export { ChevronLeftIcon } from "./ChevronLeft";
|
|
27
26
|
export { ClockIcon } from "./Clock";
|
|
28
27
|
export { CloseIcon } from "./Close";
|
|
29
|
-
export { CurrentLocationIcon } from "./CurrentLocation";
|
|
30
28
|
export { EditIcon } from "./Edit";
|
|
31
29
|
export {
|
|
32
30
|
BathroomsIcon,
|
package/src/index.ts
CHANGED
|
@@ -9,10 +9,8 @@ export {
|
|
|
9
9
|
InfoTile,
|
|
10
10
|
ContactInfoCard,
|
|
11
11
|
AccordionItem,
|
|
12
|
-
Accordion,
|
|
13
12
|
StepList,
|
|
14
13
|
Counter,
|
|
15
|
-
QuantityInput,
|
|
16
14
|
SegmentedToggle,
|
|
17
15
|
Sidebar,
|
|
18
16
|
LanguageSwitcher,
|
|
@@ -23,9 +21,7 @@ export {
|
|
|
23
21
|
DatePicker,
|
|
24
22
|
FloatingActionButton,
|
|
25
23
|
Modal,
|
|
26
|
-
Drawer,
|
|
27
24
|
Input,
|
|
28
|
-
AutocompleteInput,
|
|
29
25
|
VerificationCodeInput,
|
|
30
26
|
Select,
|
|
31
27
|
Textarea,
|
|
@@ -49,10 +45,8 @@ export type {
|
|
|
49
45
|
InfoTileProps,
|
|
50
46
|
ContactInfoCardProps,
|
|
51
47
|
AccordionItemProps,
|
|
52
|
-
AccordionProps,
|
|
53
48
|
StepListProps,
|
|
54
49
|
CounterProps,
|
|
55
|
-
QuantityInputProps,
|
|
56
50
|
SegmentedToggleOption,
|
|
57
51
|
SegmentedToggleProps,
|
|
58
52
|
SidebarProps,
|
|
@@ -74,12 +68,9 @@ export type {
|
|
|
74
68
|
DateRange,
|
|
75
69
|
FloatingActionButtonProps,
|
|
76
70
|
ModalProps,
|
|
77
|
-
DrawerProps,
|
|
78
71
|
InputIcon,
|
|
79
72
|
InputProps,
|
|
80
73
|
InputSize,
|
|
81
|
-
AutocompleteInputProps,
|
|
82
|
-
AutocompleteOption,
|
|
83
74
|
VerificationCodeInputHandle,
|
|
84
75
|
VerificationCodeInputProps,
|
|
85
76
|
SelectIcon,
|
|
@@ -128,7 +119,6 @@ export {
|
|
|
128
119
|
iconGroups,
|
|
129
120
|
iconGroupNames,
|
|
130
121
|
getIconsByGroup,
|
|
131
|
-
ArrowLeftIcon,
|
|
132
122
|
ArrowRightIcon,
|
|
133
123
|
BagIcon,
|
|
134
124
|
BanIcon,
|
|
@@ -145,7 +135,6 @@ export {
|
|
|
145
135
|
ChevronLeftIcon,
|
|
146
136
|
ClockIcon,
|
|
147
137
|
CloseIcon,
|
|
148
|
-
CurrentLocationIcon,
|
|
149
138
|
EditIcon,
|
|
150
139
|
BathroomsIcon,
|
|
151
140
|
BedroomsIcon,
|