@ecohouse/ui 0.1.50 → 0.1.52
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 +50 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +50 -15
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
- package/src/components/AutocompleteInput/AutocompleteInput.tsx +8 -5
- package/src/components/Drawer/Drawer.tsx +52 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecohouse/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.52",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Cross-platform presentation-only UI components for EcoHouse (React Native + React Native Web)",
|
|
@@ -83,12 +83,17 @@
|
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@commitlint/cli": "^21.2.1",
|
|
85
85
|
"@commitlint/config-conventional": "^21.2.0",
|
|
86
|
+
"@csstools/css-parser-algorithms": "4.0.0",
|
|
87
|
+
"@csstools/css-tokenizer": "4.0.0",
|
|
88
|
+
"@emnapi/core": "1.11.2",
|
|
89
|
+
"@emnapi/runtime": "1.11.2",
|
|
86
90
|
"@eslint/js": "^9.17.0",
|
|
87
91
|
"@storybook/react-vite": "^10.5.6",
|
|
88
92
|
"@tailwindcss/postcss": "^4.1.13",
|
|
89
93
|
"@tailwindcss/vite": "^4.1.13",
|
|
90
94
|
"@types/react": "^18.3.18",
|
|
91
95
|
"@types/react-dom": "^18.3.7",
|
|
96
|
+
"css-tree": "3.2.1",
|
|
92
97
|
"eslint": "^9.17.0",
|
|
93
98
|
"eslint-config-prettier": "^10.1.8",
|
|
94
99
|
"eslint-plugin-react-hooks": "^5.1.0",
|
|
@@ -265,11 +265,14 @@ const styles = StyleSheet.create({
|
|
|
265
265
|
borderColor: colors.grey700,
|
|
266
266
|
borderRadius: 16,
|
|
267
267
|
backgroundColor: colors.grey700,
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
...Platform.select({
|
|
269
|
+
web: {
|
|
270
|
+
boxShadow: `0 8px 18px ${colors.black}66`,
|
|
271
|
+
},
|
|
272
|
+
default: {
|
|
273
|
+
elevation: 12,
|
|
274
|
+
},
|
|
275
|
+
}),
|
|
273
276
|
},
|
|
274
277
|
list: {
|
|
275
278
|
maxHeight: MENU_MAX_HEIGHT,
|
|
@@ -74,6 +74,7 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
74
74
|
forwardedRef,
|
|
75
75
|
) {
|
|
76
76
|
const panelRef = useRef<ComponentRef<typeof View>>(null);
|
|
77
|
+
const shellRef = useRef<HTMLDivElement | null>(null);
|
|
77
78
|
const setPanelRef = useMemo(() => mergeRefs(panelRef, forwardedRef), [forwardedRef]);
|
|
78
79
|
const resolvedClassName = className?.trim() || undefined;
|
|
79
80
|
const closeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
@@ -102,6 +103,27 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
102
103
|
};
|
|
103
104
|
}, [mounted, onClose]);
|
|
104
105
|
|
|
106
|
+
const targetWidth = Math.min(width, typeof window !== "undefined" ? window.innerWidth : width);
|
|
107
|
+
|
|
108
|
+
const playWidth = useCallback(
|
|
109
|
+
(target: number) => {
|
|
110
|
+
const shell = shellRef.current;
|
|
111
|
+
if (!shell) return;
|
|
112
|
+
const reduced =
|
|
113
|
+
typeof window !== "undefined" &&
|
|
114
|
+
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
115
|
+
const duration = reduced ? 0 : animationDuration;
|
|
116
|
+
const from = shell.getBoundingClientRect().width;
|
|
117
|
+
shell.style.transition = "none";
|
|
118
|
+
shell.style.width = `${from}px`;
|
|
119
|
+
// Flush layout so the transition starts from the current width, not the final one.
|
|
120
|
+
void shell.offsetWidth;
|
|
121
|
+
shell.style.transition = `width ${duration}ms cubic-bezier(0.32, 0.72, 0, 1)`;
|
|
122
|
+
shell.style.width = `${target}px`;
|
|
123
|
+
},
|
|
124
|
+
[animationDuration],
|
|
125
|
+
);
|
|
126
|
+
|
|
105
127
|
// Web: mount at width 0, then expand; on close collapse then unmount.
|
|
106
128
|
useEffect(() => {
|
|
107
129
|
if (Platform.OS !== "web") return undefined;
|
|
@@ -113,13 +135,11 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
113
135
|
|
|
114
136
|
if (open) {
|
|
115
137
|
setMounted(true);
|
|
116
|
-
|
|
117
|
-
requestAnimationFrame(() => setEntered(true));
|
|
118
|
-
});
|
|
119
|
-
return () => cancelAnimationFrame(frame);
|
|
138
|
+
return undefined;
|
|
120
139
|
}
|
|
121
140
|
|
|
122
141
|
setEntered(false);
|
|
142
|
+
playWidth(0);
|
|
123
143
|
// Fallback if transitionend is skipped (e.g. reduced motion / tab background).
|
|
124
144
|
closeTimeoutRef.current = setTimeout(() => {
|
|
125
145
|
setMounted(false);
|
|
@@ -132,7 +152,27 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
132
152
|
closeTimeoutRef.current = null;
|
|
133
153
|
}
|
|
134
154
|
};
|
|
135
|
-
}, [open, animationDuration]);
|
|
155
|
+
}, [open, animationDuration, playWidth]);
|
|
156
|
+
|
|
157
|
+
// Portal exists only after mounted flips. Animate from 0 on the next frame.
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
if (Platform.OS !== "web" || !mounted || !open) return undefined;
|
|
160
|
+
|
|
161
|
+
const shell = shellRef.current;
|
|
162
|
+
if (shell) {
|
|
163
|
+
shell.style.transition = "none";
|
|
164
|
+
shell.style.width = "0px";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const frame = requestAnimationFrame(() => {
|
|
168
|
+
requestAnimationFrame(() => {
|
|
169
|
+
setEntered(true);
|
|
170
|
+
playWidth(targetWidth);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return () => cancelAnimationFrame(frame);
|
|
175
|
+
}, [mounted, open, targetWidth, playWidth]);
|
|
136
176
|
|
|
137
177
|
// Native: animate width + backdrop opacity.
|
|
138
178
|
useEffect(() => {
|
|
@@ -267,28 +307,28 @@ export const Drawer = forwardRef<ComponentRef<typeof View>, DrawerProps>(functio
|
|
|
267
307
|
minWidth:0 is required — flex defaults would otherwise lock width to content.
|
|
268
308
|
*/}
|
|
269
309
|
<div
|
|
310
|
+
ref={shellRef}
|
|
270
311
|
onTransitionEnd={handleWebTransitionEnd}
|
|
271
312
|
style={{
|
|
272
313
|
position: "relative",
|
|
273
314
|
zIndex: 1,
|
|
274
315
|
height: "100%",
|
|
275
|
-
|
|
276
|
-
? Math.min(width, typeof window !== "undefined" ? window.innerWidth : width)
|
|
277
|
-
: 0,
|
|
316
|
+
flex: "0 0 auto",
|
|
278
317
|
minWidth: 0,
|
|
279
318
|
maxWidth: "100%",
|
|
280
319
|
overflow: "hidden",
|
|
281
|
-
transition: `width ${animationDuration}ms cubic-bezier(0.32, 0.72, 0, 1)`,
|
|
282
320
|
willChange: "width",
|
|
283
321
|
boxSizing: "border-box",
|
|
284
322
|
}}
|
|
285
323
|
>
|
|
286
324
|
<div
|
|
287
325
|
style={{
|
|
288
|
-
|
|
326
|
+
position: "absolute",
|
|
327
|
+
top: 0,
|
|
328
|
+
right: 0,
|
|
329
|
+
bottom: 0,
|
|
330
|
+
width: targetWidth,
|
|
289
331
|
maxWidth: "100%",
|
|
290
|
-
height: "100%",
|
|
291
|
-
marginLeft: "auto",
|
|
292
332
|
display: "flex",
|
|
293
333
|
flexDirection: "column",
|
|
294
334
|
}}
|