@ecohouse/ui 0.1.39 → 0.1.41
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 +3 -69
- package/dist/index.d.ts +3 -69
- package/dist/index.js +198 -104
- 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 +196 -44
- package/src/components/Select/Select.tsx +2 -2
- package/src/icons/index.ts +0 -2
- package/src/index.ts +0 -9
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,109 @@ 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
|
+
style={[styles.backdrop, { opacity: animatedBackdrop, pointerEvents: "none" }]}
|
|
317
|
+
/>
|
|
177
318
|
<Pressable
|
|
178
319
|
accessibilityRole="button"
|
|
179
320
|
accessibilityLabel={backdropAccessibilityLabel}
|
|
180
321
|
disabled={!closeOnBackdropPress}
|
|
181
322
|
onPress={closeOnBackdropPress ? onClose : undefined}
|
|
182
|
-
style={
|
|
323
|
+
style={StyleSheet.absoluteFill}
|
|
183
324
|
/>
|
|
184
|
-
{
|
|
325
|
+
<Animated.View style={[styles.panelShell, { width: animatedWidth }]}>
|
|
326
|
+
{panelContent}
|
|
327
|
+
</Animated.View>
|
|
185
328
|
</View>
|
|
186
329
|
</RNModal>
|
|
187
330
|
);
|
|
@@ -197,11 +340,19 @@ const styles = StyleSheet.create({
|
|
|
197
340
|
...StyleSheet.absoluteFillObject,
|
|
198
341
|
backgroundColor: BACKDROP_COLOR,
|
|
199
342
|
},
|
|
200
|
-
|
|
343
|
+
panelShell: {
|
|
201
344
|
zIndex: 1,
|
|
345
|
+
height: "100%",
|
|
346
|
+
maxWidth: "100%",
|
|
347
|
+
overflow: "hidden",
|
|
348
|
+
alignItems: "flex-end",
|
|
349
|
+
minWidth: 0,
|
|
350
|
+
},
|
|
351
|
+
panelInner: {
|
|
202
352
|
height: "100%",
|
|
203
353
|
maxWidth: "100%",
|
|
204
354
|
backgroundColor: colors.grey800,
|
|
355
|
+
flexShrink: 0,
|
|
205
356
|
},
|
|
206
357
|
header: {
|
|
207
358
|
height: 65,
|
|
@@ -220,10 +371,11 @@ const styles = StyleSheet.create({
|
|
|
220
371
|
fontFamily: fonts.sans,
|
|
221
372
|
fontSize: 16,
|
|
222
373
|
fontWeight: "600",
|
|
223
|
-
lineHeight:
|
|
374
|
+
lineHeight: 22,
|
|
224
375
|
letterSpacing: 0,
|
|
225
376
|
color: colors.white,
|
|
226
377
|
marginRight: 12,
|
|
378
|
+
overflow: "visible",
|
|
227
379
|
},
|
|
228
380
|
closeButton: {
|
|
229
381
|
width: CLOSE_ICON_SIZE,
|
|
@@ -198,7 +198,7 @@ function MultiValueChips({ options, disabled, onRemove }: MultiValueChipsProps)
|
|
|
198
198
|
return (
|
|
199
199
|
<View style={styles.multiValueRoot}>
|
|
200
200
|
{/* Hidden copy of every chip; RN has no synchronous text measurement. */}
|
|
201
|
-
<View
|
|
201
|
+
<View style={[styles.measureRow, { pointerEvents: "none" }]}>
|
|
202
202
|
{options.map((option) => (
|
|
203
203
|
<View
|
|
204
204
|
key={`measure-${option.value}`}
|
|
@@ -723,7 +723,7 @@ export const Select = forwardRef<ComponentRef<typeof View>, SelectProps>(
|
|
|
723
723
|
]}
|
|
724
724
|
>
|
|
725
725
|
{multiple ? (
|
|
726
|
-
<View
|
|
726
|
+
<View style={[styles.itemCheckbox, { pointerEvents: "none" }]}>
|
|
727
727
|
<Checkbox value={isSelected} variant="light" />
|
|
728
728
|
</View>
|
|
729
729
|
) : null}
|
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,
|
|
@@ -25,7 +23,6 @@ export {
|
|
|
25
23
|
Modal,
|
|
26
24
|
Drawer,
|
|
27
25
|
Input,
|
|
28
|
-
AutocompleteInput,
|
|
29
26
|
VerificationCodeInput,
|
|
30
27
|
Select,
|
|
31
28
|
Textarea,
|
|
@@ -49,10 +46,8 @@ export type {
|
|
|
49
46
|
InfoTileProps,
|
|
50
47
|
ContactInfoCardProps,
|
|
51
48
|
AccordionItemProps,
|
|
52
|
-
AccordionProps,
|
|
53
49
|
StepListProps,
|
|
54
50
|
CounterProps,
|
|
55
|
-
QuantityInputProps,
|
|
56
51
|
SegmentedToggleOption,
|
|
57
52
|
SegmentedToggleProps,
|
|
58
53
|
SidebarProps,
|
|
@@ -78,8 +73,6 @@ export type {
|
|
|
78
73
|
InputIcon,
|
|
79
74
|
InputProps,
|
|
80
75
|
InputSize,
|
|
81
|
-
AutocompleteInputProps,
|
|
82
|
-
AutocompleteOption,
|
|
83
76
|
VerificationCodeInputHandle,
|
|
84
77
|
VerificationCodeInputProps,
|
|
85
78
|
SelectIcon,
|
|
@@ -128,7 +121,6 @@ export {
|
|
|
128
121
|
iconGroups,
|
|
129
122
|
iconGroupNames,
|
|
130
123
|
getIconsByGroup,
|
|
131
|
-
ArrowLeftIcon,
|
|
132
124
|
ArrowRightIcon,
|
|
133
125
|
BagIcon,
|
|
134
126
|
BanIcon,
|
|
@@ -145,7 +137,6 @@ export {
|
|
|
145
137
|
ChevronLeftIcon,
|
|
146
138
|
ClockIcon,
|
|
147
139
|
CloseIcon,
|
|
148
|
-
CurrentLocationIcon,
|
|
149
140
|
EditIcon,
|
|
150
141
|
BathroomsIcon,
|
|
151
142
|
BedroomsIcon,
|