@scripso-homepad/ui 0.4.27 → 0.4.29
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/web/index.cjs +37 -13
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +6 -2
- package/dist/web/index.d.ts +6 -2
- package/dist/web/index.js +37 -13
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
- package/src/web/components/ConfirmModal.tsx +6 -3
- package/src/web/components/Drawer.tsx +25 -3
- package/src/web/components/Modal.tsx +11 -5
- package/src/web/layout/AppHeader.tsx +5 -0
package/package.json
CHANGED
|
@@ -57,7 +57,10 @@ export function ConfirmModal({
|
|
|
57
57
|
return null;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
const sharedButtonClassName = cn(
|
|
60
|
+
const sharedButtonClassName = cn(
|
|
61
|
+
'btn h-10! w-full! min-w-0! justify-center!',
|
|
62
|
+
buttonClassName,
|
|
63
|
+
);
|
|
61
64
|
|
|
62
65
|
return (
|
|
63
66
|
<div
|
|
@@ -71,7 +74,7 @@ export function ConfirmModal({
|
|
|
71
74
|
aria-labelledby="homepad-confirm-modal-title"
|
|
72
75
|
aria-describedby="homepad-confirm-modal-description"
|
|
73
76
|
className={cn(
|
|
74
|
-
'flex w-full max-w-md flex-col gap-8 rounded-
|
|
77
|
+
'flex w-full max-w-md flex-col gap-8 rounded-3xl bg-white p-6 opacity-100 md:p-8',
|
|
75
78
|
containerClassName,
|
|
76
79
|
)}
|
|
77
80
|
onClick={(event) => {
|
|
@@ -98,7 +101,7 @@ export function ConfirmModal({
|
|
|
98
101
|
</div>
|
|
99
102
|
</header>
|
|
100
103
|
|
|
101
|
-
<footer className="
|
|
104
|
+
<footer className="grid w-full grid-cols-2 gap-4">
|
|
102
105
|
<Button
|
|
103
106
|
title={cancelText}
|
|
104
107
|
onPress={onCancel}
|
|
@@ -71,10 +71,12 @@ export function Drawer({
|
|
|
71
71
|
}: DrawerProps) {
|
|
72
72
|
const [isPresent, setIsPresent] = useState(open);
|
|
73
73
|
const [isAnimatedIn, setIsAnimatedIn] = useState(false);
|
|
74
|
+
const [isTransformSettled, setIsTransformSettled] = useState(open);
|
|
74
75
|
|
|
75
76
|
useEffect(() => {
|
|
76
77
|
if (open) {
|
|
77
78
|
setIsPresent(true);
|
|
79
|
+
setIsTransformSettled(false);
|
|
78
80
|
|
|
79
81
|
const frame = window.requestAnimationFrame(() => {
|
|
80
82
|
window.requestAnimationFrame(() => {
|
|
@@ -82,18 +84,32 @@ export function Drawer({
|
|
|
82
84
|
});
|
|
83
85
|
});
|
|
84
86
|
|
|
87
|
+
const settleTimeout = window.setTimeout(() => {
|
|
88
|
+
setIsTransformSettled(true);
|
|
89
|
+
}, DRAWER_TRANSITION_MS);
|
|
90
|
+
|
|
85
91
|
return () => {
|
|
86
92
|
window.cancelAnimationFrame(frame);
|
|
93
|
+
window.clearTimeout(settleTimeout);
|
|
87
94
|
};
|
|
88
95
|
}
|
|
89
96
|
|
|
90
|
-
|
|
97
|
+
// Re-enable transform at the open position, then slide out.
|
|
98
|
+
setIsTransformSettled(false);
|
|
99
|
+
setIsAnimatedIn(true);
|
|
100
|
+
|
|
101
|
+
const frame = window.requestAnimationFrame(() => {
|
|
102
|
+
window.requestAnimationFrame(() => {
|
|
103
|
+
setIsAnimatedIn(false);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
91
106
|
|
|
92
107
|
const timeout = window.setTimeout(() => {
|
|
93
108
|
setIsPresent(false);
|
|
94
109
|
}, DRAWER_TRANSITION_MS);
|
|
95
110
|
|
|
96
111
|
return () => {
|
|
112
|
+
window.cancelAnimationFrame(frame);
|
|
97
113
|
window.clearTimeout(timeout);
|
|
98
114
|
};
|
|
99
115
|
}, [open]);
|
|
@@ -144,8 +160,14 @@ export function Drawer({
|
|
|
144
160
|
aria-labelledby="homepad-drawer-title"
|
|
145
161
|
{...(subtitle ? { 'aria-describedby': 'homepad-drawer-subtitle' } : {})}
|
|
146
162
|
className={cn(
|
|
147
|
-
'relative z-10 flex h-[calc(100dvh-16px)] min-h-0 flex-col overflow-hidden rounded-[16px] bg-white shadow-2xl transition-transform duration-300 ease-in-out
|
|
148
|
-
|
|
163
|
+
'relative z-10 flex h-[calc(100dvh-16px)] min-h-0 flex-col overflow-hidden rounded-[16px] bg-white shadow-2xl transition-transform duration-300 ease-in-out max-md:h-dvh max-md:rounded-none',
|
|
164
|
+
// Keep GPU transform only while animating — settled translate-x-0 blurs text.
|
|
165
|
+
!isTransformSettled && 'will-change-transform',
|
|
166
|
+
isAnimatedIn
|
|
167
|
+
? isTransformSettled
|
|
168
|
+
? 'transform-none'
|
|
169
|
+
: 'translate-x-0'
|
|
170
|
+
: 'translate-x-full',
|
|
149
171
|
widthClassName,
|
|
150
172
|
className,
|
|
151
173
|
)}
|
|
@@ -14,6 +14,8 @@ export type ModalProps = {
|
|
|
14
14
|
subtitle?: string;
|
|
15
15
|
icon?: ReactNode;
|
|
16
16
|
iconContainerClassName?: string;
|
|
17
|
+
titleClassName?: string;
|
|
18
|
+
subtitleClassName?: string;
|
|
17
19
|
children: ReactNode;
|
|
18
20
|
cancelText: string;
|
|
19
21
|
confirmText: string;
|
|
@@ -42,6 +44,8 @@ export function Modal({
|
|
|
42
44
|
subtitle,
|
|
43
45
|
icon,
|
|
44
46
|
iconContainerClassName,
|
|
47
|
+
titleClassName,
|
|
48
|
+
subtitleClassName,
|
|
45
49
|
children,
|
|
46
50
|
cancelText,
|
|
47
51
|
confirmText,
|
|
@@ -84,9 +88,8 @@ export function Modal({
|
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
const sharedButtonClassName = cn(
|
|
87
|
-
'btn h-13 w-full justify-center!',
|
|
88
|
-
|
|
89
|
-
footerLayout !== 'split' && buttonClassName,
|
|
91
|
+
'btn h-13 w-full! min-w-0! justify-center!',
|
|
92
|
+
buttonClassName,
|
|
90
93
|
);
|
|
91
94
|
|
|
92
95
|
const modal = (
|
|
@@ -132,14 +135,17 @@ export function Modal({
|
|
|
132
135
|
<div className="flex min-w-0 flex-col gap-2">
|
|
133
136
|
<h2
|
|
134
137
|
id="homepad-modal-title"
|
|
135
|
-
className=
|
|
138
|
+
className={cn('typography-title-sm tracking-normal text-black', titleClassName)}
|
|
136
139
|
>
|
|
137
140
|
{title}
|
|
138
141
|
</h2>
|
|
139
142
|
{subtitle ? (
|
|
140
143
|
<p
|
|
141
144
|
id="homepad-modal-subtitle"
|
|
142
|
-
className=
|
|
145
|
+
className={cn(
|
|
146
|
+
'typography-caption tracking-normal text-storm-gray-400',
|
|
147
|
+
subtitleClassName,
|
|
148
|
+
)}
|
|
143
149
|
>
|
|
144
150
|
{subtitle}
|
|
145
151
|
</p>
|
|
@@ -25,6 +25,8 @@ export type AppHeaderProps = {
|
|
|
25
25
|
notificationsIcon?: ReactNode;
|
|
26
26
|
searchIcon?: ReactNode;
|
|
27
27
|
buildingChevronIcon?: ReactNode;
|
|
28
|
+
/** Rendered before the building select (e.g. month period control). */
|
|
29
|
+
actions?: ReactNode;
|
|
28
30
|
buildingSelectDisabled?: boolean;
|
|
29
31
|
searchDisabled?: boolean;
|
|
30
32
|
notificationsDisabled?: boolean;
|
|
@@ -60,6 +62,7 @@ export function AppHeader({
|
|
|
60
62
|
notificationsIcon,
|
|
61
63
|
searchIcon,
|
|
62
64
|
buildingChevronIcon,
|
|
65
|
+
actions,
|
|
63
66
|
buildingSelectDisabled = false,
|
|
64
67
|
searchDisabled = false,
|
|
65
68
|
notificationsDisabled = false,
|
|
@@ -77,6 +80,8 @@ export function AppHeader({
|
|
|
77
80
|
<h1 className="truncate typography-title text-slate-blue">{title}</h1>
|
|
78
81
|
|
|
79
82
|
<div className="flex shrink-0 items-center gap-3">
|
|
83
|
+
{actions}
|
|
84
|
+
|
|
80
85
|
{hasBuildingSelect ? (
|
|
81
86
|
<BuildingSelect
|
|
82
87
|
options={buildingOptions}
|