react-modal-sheet 5.6.0 → 5.7.0

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/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.6.0",
2
+ "version": "5.7.0",
3
3
  "license": "MIT",
4
4
  "name": "react-modal-sheet",
5
5
  "description": "Flexible bottom sheet component for your React apps",
@@ -2,6 +2,7 @@ import { type MotionValue, transform } from 'motion';
2
2
  import { useEffect } from 'react';
3
3
 
4
4
  import type { SheetDetent, SheetSnapPoint } from '../types';
5
+ import { clamp } from '../utils';
5
6
  import { useSafeAreaInsets } from './use-safe-area-insets';
6
7
  import { useStableCallback } from './use-stable-callback';
7
8
 
@@ -79,13 +80,7 @@ export function useModalEffect({
79
80
  // Make sure progress is between 0 and 1
80
81
  progress = Math.max(0, Math.min(1, progress));
81
82
 
82
- const pageWidth = window.innerWidth;
83
-
84
- updateModalEffect(rootId, {
85
- scale: transform(progress, [0, 1], [1, (pageWidth - 16) / pageWidth]),
86
- translateY: transform(progress, [0, 1], [0, 24 + insetTop]),
87
- borderRadius: transform(progress, [0, 1], [0, 10]),
88
- });
83
+ updateModalEffect(rootId, { progress, insetTop });
89
84
  });
90
85
 
91
86
  const handleYComplete = useStableCallback(() => {
@@ -122,9 +117,18 @@ export function useModalEffect({
122
117
  }
123
118
 
124
119
  function setupModalEffect(rootId: string) {
125
- const root = document.querySelector(`#${rootId}`) as HTMLDivElement;
126
- if (!root) return;
120
+ const target = getModalEffectTarget(rootId);
121
+ if (!target) return;
122
+
123
+ if (target.type === 'sheet') {
124
+ const sheet = target.element;
127
125
 
126
+ // No transition as the sheet follows the `y` of the stacked sheet
127
+ sheet.style.transformOrigin = 'center top';
128
+ return;
129
+ }
130
+
131
+ const root = target.element;
128
132
  const body = document.querySelector('body') as HTMLBodyElement;
129
133
 
130
134
  body.style.backgroundColor = '#000000';
@@ -137,21 +141,55 @@ function setupModalEffect(rootId: string) {
137
141
 
138
142
  function updateModalEffect(
139
143
  rootId: string,
140
- opts: { scale: number; translateY: number; borderRadius: number }
144
+ opts: { progress: number; insetTop: number }
141
145
  ) {
142
- const root = document.querySelector(`#${rootId}`) as HTMLDivElement;
143
- if (!root) return;
146
+ const target = getModalEffectTarget(rootId);
147
+ if (!target) return;
148
+
149
+ const { progress, insetTop } = opts;
150
+
151
+ if (target.type === 'sheet') {
152
+ const sheet = target.element;
153
+
154
+ const sheetWidth = sheet.offsetWidth;
155
+ const sheetTop = sheet.offsetTop;
156
+ const scaleEnd = (sheetWidth - 16) / sheetWidth;
157
+ const scale = transform(progress, [0, 1], [1, scaleEnd]);
158
+ // Match the scaled app root's top edge, but cap the final lift for short sheets.
159
+ const translateYEnd = clamp(scaleEnd * (24 + insetTop) - sheetTop, -10, 0);
160
+ const translateY = transform(progress, [0, 1], [0, translateYEnd]);
161
+
162
+ sheet.style.scale = `${scale}`;
163
+ sheet.style.translate = `0 ${translateY}px`;
164
+ return;
165
+ }
166
+
167
+ const root = target.element;
168
+ const pageWidth = window.innerWidth;
169
+ const scaleEnd = (pageWidth - 16) / pageWidth;
170
+ const scale = transform(progress, [0, 1], [1, scaleEnd]);
171
+ const translateY = transform(progress, [0, 1], [0, 24 + insetTop]);
172
+ const borderRadius = transform(progress, [0, 1], [0, 10]);
144
173
 
145
- const { scale, translateY, borderRadius } = opts;
146
174
  root.style.transform = `scale(${scale}) translate3d(0, ${translateY}px, 0)`;
147
175
  root.style.borderTopRightRadius = `${borderRadius}px`;
148
176
  root.style.borderTopLeftRadius = `${borderRadius}px`;
149
177
  }
150
178
 
151
179
  function cleanupModalEffect(rootId: string) {
152
- const root = document.querySelector(`#${rootId}`) as HTMLDivElement;
153
- if (!root) return;
180
+ const target = getModalEffectTarget(rootId);
181
+ if (!target) return;
154
182
 
183
+ if (target.type === 'sheet') {
184
+ const sheet = target.element;
185
+
186
+ sheet.style.removeProperty('transform-origin');
187
+ sheet.style.removeProperty('scale');
188
+ sheet.style.removeProperty('translate');
189
+ return;
190
+ }
191
+
192
+ const root = target.element;
155
193
  const body = document.querySelector('body') as HTMLBodyElement;
156
194
 
157
195
  body.style.removeProperty('background-color');
@@ -164,3 +202,22 @@ function cleanupModalEffect(rootId: string) {
164
202
  root.style.removeProperty('border-top-right-radius');
165
203
  root.style.removeProperty('border-top-left-radius');
166
204
  }
205
+
206
+ function getModalEffectTarget(rootId: string) {
207
+ const root = document.getElementById(rootId) as HTMLDivElement;
208
+ if (!root) return null;
209
+
210
+ // When stacking sheets the effect is applied to the container of the sheet
211
+ const sheetRoot = root.closest('.react-modal-sheet-root');
212
+
213
+ if (sheetRoot) {
214
+ const container = sheetRoot.querySelector(
215
+ '.react-modal-sheet-container'
216
+ ) as HTMLDivElement;
217
+ if (!container) return null;
218
+
219
+ return { type: 'sheet', element: container };
220
+ }
221
+
222
+ return { type: 'root', element: root };
223
+ }
package/src/sheet.tsx CHANGED
@@ -75,7 +75,7 @@ export const Sheet = forwardRef<any, SheetProps>(
75
75
  },
76
76
  ref
77
77
  ) => {
78
- const [sheetBoundsRef, sheetBounds] = useMeasure();
78
+ const [sheetBoundsRef, sheetBounds] = useMeasure({ offsetSize: true });
79
79
  const sheetRef = useRef<HTMLDivElement>(null);
80
80
  const sheetHeight = Math.round(sheetBounds.height);
81
81
  const [currentSnap, setCurrentSnap] = useState(initialSnap);