@howells/stacksheet 1.0.1 → 1.1.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/dist/index.d.ts +37 -17
- package/dist/index.js +534 -251
- package/dist/index.js.map +1 -1
- package/package.json +16 -5
package/dist/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// src/springs.ts
|
|
2
2
|
var springs = {
|
|
3
|
-
soft: { stiffness: 120, damping: 18, mass: 1 },
|
|
4
3
|
subtle: { stiffness: 300, damping: 30, mass: 1 },
|
|
5
|
-
natural: { stiffness: 200, damping: 20, mass: 1 },
|
|
6
4
|
snappy: { stiffness: 400, damping: 28, mass: 0.8 },
|
|
7
5
|
stiff: { stiffness: 400, damping: 40, mass: 1 }
|
|
8
6
|
};
|
|
@@ -10,7 +8,7 @@ var springs = {
|
|
|
10
8
|
// src/config.ts
|
|
11
9
|
var DEFAULT_STACKING = {
|
|
12
10
|
scaleStep: 0.04,
|
|
13
|
-
offsetStep:
|
|
11
|
+
offsetStep: 16,
|
|
14
12
|
opacityStep: 0,
|
|
15
13
|
radius: 12,
|
|
16
14
|
renderThreshold: 3
|
|
@@ -19,9 +17,19 @@ var DEFAULT_SIDE = {
|
|
|
19
17
|
desktop: "right",
|
|
20
18
|
mobile: "bottom"
|
|
21
19
|
};
|
|
20
|
+
function resolveSide(side) {
|
|
21
|
+
if (typeof side === "string") {
|
|
22
|
+
return { desktop: side, mobile: side };
|
|
23
|
+
}
|
|
24
|
+
return { ...DEFAULT_SIDE, ...side };
|
|
25
|
+
}
|
|
26
|
+
function resolveSpring(spring) {
|
|
27
|
+
if (typeof spring === "string") {
|
|
28
|
+
return springs[spring];
|
|
29
|
+
}
|
|
30
|
+
return { ...springs.stiff, ...spring };
|
|
31
|
+
}
|
|
22
32
|
function resolveConfig(config = {}) {
|
|
23
|
-
const side = typeof config.side === "string" ? { desktop: config.side, mobile: config.side } : { ...DEFAULT_SIDE, ...config.side };
|
|
24
|
-
const spring = typeof config.spring === "string" ? springs[config.spring] : { ...springs.stiff, ...config.spring };
|
|
25
33
|
return {
|
|
26
34
|
maxDepth: config.maxDepth ?? Number.POSITIVE_INFINITY,
|
|
27
35
|
closeOnEscape: config.closeOnEscape ?? true,
|
|
@@ -31,13 +39,17 @@ function resolveConfig(config = {}) {
|
|
|
31
39
|
width: config.width ?? 420,
|
|
32
40
|
maxWidth: config.maxWidth ?? "90vw",
|
|
33
41
|
breakpoint: config.breakpoint ?? 768,
|
|
34
|
-
side,
|
|
42
|
+
side: resolveSide(config.side),
|
|
35
43
|
stacking: { ...DEFAULT_STACKING, ...config.stacking },
|
|
36
|
-
spring,
|
|
44
|
+
spring: resolveSpring(config.spring),
|
|
37
45
|
zIndex: config.zIndex ?? 100,
|
|
38
46
|
ariaLabel: config.ariaLabel ?? "Sheet dialog",
|
|
39
47
|
onOpenComplete: config.onOpenComplete,
|
|
40
48
|
onCloseComplete: config.onCloseComplete,
|
|
49
|
+
snapPoints: config.snapPoints ?? [],
|
|
50
|
+
snapPointIndex: config.snapPointIndex,
|
|
51
|
+
onSnapPointChange: config.onSnapPointChange,
|
|
52
|
+
snapToSequentialPoints: config.snapToSequentialPoints ?? false,
|
|
41
53
|
drag: config.drag ?? true,
|
|
42
54
|
closeThreshold: config.closeThreshold ?? 0.25,
|
|
43
55
|
velocityThreshold: config.velocityThreshold ?? 0.5,
|
|
@@ -137,6 +149,96 @@ function useSheetPanel() {
|
|
|
137
149
|
return ctx;
|
|
138
150
|
}
|
|
139
151
|
|
|
152
|
+
// src/snap-points.ts
|
|
153
|
+
var SNAP_POINT_RE = /^(\d+(?:\.\d+)?)(px|rem|em|vh|%)$/;
|
|
154
|
+
function resolveSnapPointPx(point, viewportHeight) {
|
|
155
|
+
if (typeof point === "number") {
|
|
156
|
+
return point <= 1 ? point * viewportHeight : point;
|
|
157
|
+
}
|
|
158
|
+
if (typeof point === "string") {
|
|
159
|
+
const match = point.match(SNAP_POINT_RE);
|
|
160
|
+
if (!match?.[1]) {
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
const value = Number.parseFloat(match[1]);
|
|
164
|
+
const unit = match[2];
|
|
165
|
+
switch (unit) {
|
|
166
|
+
case "px":
|
|
167
|
+
return value;
|
|
168
|
+
case "rem":
|
|
169
|
+
case "em":
|
|
170
|
+
return value * Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
171
|
+
case "vh":
|
|
172
|
+
case "%":
|
|
173
|
+
return value / 100 * viewportHeight;
|
|
174
|
+
default:
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
function resolveSnapPoints(points, viewportHeight) {
|
|
181
|
+
if (points.length === 0) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
const resolved = points.map((p) => resolveSnapPointPx(p, viewportHeight)).filter((px) => px > 0);
|
|
185
|
+
resolved.sort((a, b) => a - b);
|
|
186
|
+
const deduped = [];
|
|
187
|
+
for (const px of resolved) {
|
|
188
|
+
const last = deduped.at(-1);
|
|
189
|
+
if (last === void 0 || Math.abs(px - last) > 1) {
|
|
190
|
+
deduped.push(px);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return deduped;
|
|
194
|
+
}
|
|
195
|
+
var SNAP_VELOCITY_THRESHOLD = 0.4;
|
|
196
|
+
var MAX_SNAP_VELOCITY = 2;
|
|
197
|
+
var SNAP_VELOCITY_MULTIPLIER = 150;
|
|
198
|
+
function findSnapTarget(dragOffset, panelHeight, snapHeights, velocity, currentIndex, sequential) {
|
|
199
|
+
if (snapHeights.length === 0) {
|
|
200
|
+
return -1;
|
|
201
|
+
}
|
|
202
|
+
const snapOffsets = snapHeights.map((h) => panelHeight - h);
|
|
203
|
+
const currentPos = dragOffset;
|
|
204
|
+
if (sequential) {
|
|
205
|
+
const direction = velocity > 0 ? 1 : -1;
|
|
206
|
+
const nextIndex = currentIndex - direction;
|
|
207
|
+
if (nextIndex < 0) {
|
|
208
|
+
return -1;
|
|
209
|
+
}
|
|
210
|
+
if (nextIndex >= snapHeights.length) {
|
|
211
|
+
return snapHeights.length - 1;
|
|
212
|
+
}
|
|
213
|
+
return nextIndex;
|
|
214
|
+
}
|
|
215
|
+
const velocityOffset = Math.abs(velocity) >= SNAP_VELOCITY_THRESHOLD ? Math.min(Math.max(velocity, -MAX_SNAP_VELOCITY), MAX_SNAP_VELOCITY) * SNAP_VELOCITY_MULTIPLIER : 0;
|
|
216
|
+
const projectedPos = currentPos + velocityOffset;
|
|
217
|
+
const first = snapOffsets[0] ?? 0;
|
|
218
|
+
let bestIndex = 0;
|
|
219
|
+
let bestDist = Math.abs(projectedPos - first);
|
|
220
|
+
for (let i = 1; i < snapOffsets.length; i++) {
|
|
221
|
+
const offset = snapOffsets[i] ?? 0;
|
|
222
|
+
const dist = Math.abs(projectedPos - offset);
|
|
223
|
+
if (dist < bestDist) {
|
|
224
|
+
bestDist = dist;
|
|
225
|
+
bestIndex = i;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const dismissDist = Math.abs(projectedPos - panelHeight);
|
|
229
|
+
if (dismissDist <= bestDist) {
|
|
230
|
+
return -1;
|
|
231
|
+
}
|
|
232
|
+
return bestIndex;
|
|
233
|
+
}
|
|
234
|
+
function getSnapOffset(snapIndex, snapHeights, panelHeight) {
|
|
235
|
+
if (snapIndex < 0 || snapIndex >= snapHeights.length) {
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
const targetHeight = snapHeights[snapIndex] ?? 0;
|
|
239
|
+
return panelHeight - targetHeight;
|
|
240
|
+
}
|
|
241
|
+
|
|
140
242
|
// src/stacking.ts
|
|
141
243
|
function getStackTransform(depth, stacking) {
|
|
142
244
|
if (depth <= 0) {
|
|
@@ -181,6 +283,30 @@ function getSlideFrom(side) {
|
|
|
181
283
|
function getSlideTarget() {
|
|
182
284
|
return { x: 0, y: 0 };
|
|
183
285
|
}
|
|
286
|
+
function getStackOffset(side, offset) {
|
|
287
|
+
if (offset === 0) {
|
|
288
|
+
return {};
|
|
289
|
+
}
|
|
290
|
+
switch (side) {
|
|
291
|
+
case "right":
|
|
292
|
+
return { x: -offset };
|
|
293
|
+
case "left":
|
|
294
|
+
return { x: offset };
|
|
295
|
+
case "bottom":
|
|
296
|
+
return { y: -offset };
|
|
297
|
+
default:
|
|
298
|
+
return {};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function getTransformOrigin(side) {
|
|
302
|
+
if (side === "right") {
|
|
303
|
+
return "left center";
|
|
304
|
+
}
|
|
305
|
+
if (side === "left") {
|
|
306
|
+
return "right center";
|
|
307
|
+
}
|
|
308
|
+
return "center top";
|
|
309
|
+
}
|
|
184
310
|
function getPanelStyles(side, config, _depth, index) {
|
|
185
311
|
const { width, maxWidth, zIndex } = config;
|
|
186
312
|
const base = {
|
|
@@ -188,9 +314,8 @@ function getPanelStyles(side, config, _depth, index) {
|
|
|
188
314
|
zIndex: zIndex + 10 + index,
|
|
189
315
|
display: "flex",
|
|
190
316
|
flexDirection: "column",
|
|
191
|
-
overflow: "hidden",
|
|
192
317
|
willChange: "transform",
|
|
193
|
-
transformOrigin: side
|
|
318
|
+
transformOrigin: getTransformOrigin(side)
|
|
194
319
|
};
|
|
195
320
|
if (side === "bottom") {
|
|
196
321
|
return {
|
|
@@ -235,6 +360,29 @@ function isInteractiveElement(el) {
|
|
|
235
360
|
}
|
|
236
361
|
return false;
|
|
237
362
|
}
|
|
363
|
+
function findScrollableAncestor(el, axis) {
|
|
364
|
+
let current = el;
|
|
365
|
+
while (current) {
|
|
366
|
+
if (current instanceof HTMLElement) {
|
|
367
|
+
const style = getComputedStyle(current);
|
|
368
|
+
const overflow = axis === "y" ? style.overflowY : style.overflowX;
|
|
369
|
+
if (overflow === "auto" || overflow === "scroll") {
|
|
370
|
+
const scrollable = axis === "y" ? current.scrollHeight > current.clientHeight : current.scrollWidth > current.clientWidth;
|
|
371
|
+
if (scrollable) {
|
|
372
|
+
return current;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
current = current.parentElement;
|
|
377
|
+
}
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
function isAtScrollEdge(el, axis, sign) {
|
|
381
|
+
if (axis === "y") {
|
|
382
|
+
return sign === 1 ? el.scrollTop <= 0 : el.scrollTop + el.clientHeight >= el.scrollHeight - 1;
|
|
383
|
+
}
|
|
384
|
+
return sign === 1 ? el.scrollLeft <= 0 : el.scrollLeft + el.clientWidth >= el.scrollWidth - 1;
|
|
385
|
+
}
|
|
238
386
|
function getDismissAxis(side) {
|
|
239
387
|
switch (side) {
|
|
240
388
|
case "right":
|
|
@@ -249,6 +397,7 @@ function getDismissAxis(side) {
|
|
|
249
397
|
}
|
|
250
398
|
var DEAD_ZONE = 10;
|
|
251
399
|
var MAX_ANGLE_DEG = 35;
|
|
400
|
+
var RUBBER_BAND_FACTOR = 0.6;
|
|
252
401
|
function classifyGesture(dx, dy, axis, sign) {
|
|
253
402
|
const absDx = Math.abs(dx);
|
|
254
403
|
const absDy = Math.abs(dy);
|
|
@@ -277,6 +426,7 @@ function useDrag(panelRef, config, onDragUpdate) {
|
|
|
277
426
|
const startRef = useRef(null);
|
|
278
427
|
const committedRef = useRef(null);
|
|
279
428
|
const offsetRef = useRef(0);
|
|
429
|
+
const scrollTargetRef = useRef(null);
|
|
280
430
|
const { axis, sign } = getDismissAxis(config.side);
|
|
281
431
|
const handlePointerDown = useCallback(
|
|
282
432
|
(e) => {
|
|
@@ -294,12 +444,7 @@ function useDrag(panelRef, config, onDragUpdate) {
|
|
|
294
444
|
if (!isHandle && isInteractiveElement(target)) {
|
|
295
445
|
return;
|
|
296
446
|
}
|
|
297
|
-
|
|
298
|
-
const scrollable = target.closest("[data-radix-scroll-area-viewport]");
|
|
299
|
-
if (scrollable && scrollable.scrollTop > 0 && axis === "y") {
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
447
|
+
scrollTargetRef.current = isHandle ? null : findScrollableAncestor(target, axis);
|
|
303
448
|
startRef.current = { x: e.clientX, y: e.clientY, time: Date.now() };
|
|
304
449
|
committedRef.current = null;
|
|
305
450
|
offsetRef.current = 0;
|
|
@@ -319,66 +464,88 @@ function useDrag(panelRef, config, onDragUpdate) {
|
|
|
319
464
|
return;
|
|
320
465
|
}
|
|
321
466
|
if (committedRef.current === null) {
|
|
322
|
-
|
|
323
|
-
if (
|
|
467
|
+
const gesture = classifyGesture(dx, dy, axis, sign);
|
|
468
|
+
if (gesture === "none") {
|
|
469
|
+
committedRef.current = "none";
|
|
324
470
|
startRef.current = null;
|
|
325
471
|
return;
|
|
326
472
|
}
|
|
473
|
+
const scrollEl = scrollTargetRef.current;
|
|
474
|
+
if (scrollEl && !isAtScrollEdge(scrollEl, axis, sign)) {
|
|
475
|
+
committedRef.current = "none";
|
|
476
|
+
startRef.current = null;
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
committedRef.current = "drag";
|
|
327
480
|
}
|
|
328
481
|
if (committedRef.current !== "drag") {
|
|
329
482
|
return;
|
|
330
483
|
}
|
|
331
484
|
const rawOffset = axis === "x" ? dx : dy;
|
|
332
|
-
const
|
|
485
|
+
const directional = rawOffset * sign;
|
|
486
|
+
const clampedOffset = directional >= 0 ? directional : -Math.sqrt(Math.abs(directional)) * RUBBER_BAND_FACTOR;
|
|
333
487
|
offsetRef.current = clampedOffset;
|
|
334
488
|
onDragUpdate({ offset: clampedOffset, isDragging: true });
|
|
335
489
|
e.preventDefault();
|
|
336
490
|
},
|
|
337
491
|
[axis, sign, onDragUpdate]
|
|
338
492
|
);
|
|
493
|
+
const dismiss = useCallback(() => {
|
|
494
|
+
if (config.isNested) {
|
|
495
|
+
config.onPop();
|
|
496
|
+
} else {
|
|
497
|
+
config.onClose();
|
|
498
|
+
}
|
|
499
|
+
onDragUpdate({ offset: 0, isDragging: false });
|
|
500
|
+
}, [config, onDragUpdate]);
|
|
339
501
|
const handlePointerUp = useCallback(
|
|
340
502
|
(_e) => {
|
|
341
503
|
if (!startRef.current || committedRef.current !== "drag") {
|
|
342
504
|
startRef.current = null;
|
|
343
505
|
committedRef.current = null;
|
|
506
|
+
scrollTargetRef.current = null;
|
|
344
507
|
return;
|
|
345
508
|
}
|
|
346
|
-
const offset = offsetRef.current;
|
|
509
|
+
const offset = Math.max(0, offsetRef.current);
|
|
347
510
|
const elapsed = Date.now() - startRef.current.time;
|
|
348
511
|
const velocity = elapsed > 0 ? offset / elapsed : 0;
|
|
349
512
|
startRef.current = null;
|
|
350
513
|
committedRef.current = null;
|
|
351
514
|
offsetRef.current = 0;
|
|
515
|
+
scrollTargetRef.current = null;
|
|
352
516
|
const panelSize = getPanelDimension(panelRef.current, axis);
|
|
517
|
+
if (config.snapHeights.length > 0) {
|
|
518
|
+
const targetIndex = findSnapTarget(
|
|
519
|
+
offset,
|
|
520
|
+
panelSize,
|
|
521
|
+
config.snapHeights,
|
|
522
|
+
velocity,
|
|
523
|
+
config.activeSnapIndex,
|
|
524
|
+
config.sequential
|
|
525
|
+
);
|
|
526
|
+
if (targetIndex === -1) {
|
|
527
|
+
dismiss();
|
|
528
|
+
} else {
|
|
529
|
+
config.onSnap(targetIndex);
|
|
530
|
+
onDragUpdate({ offset: 0, isDragging: false });
|
|
531
|
+
}
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
353
534
|
const pastThreshold = offset / panelSize > config.closeThreshold;
|
|
354
535
|
const fastEnough = velocity > config.velocityThreshold;
|
|
355
536
|
if (pastThreshold || fastEnough) {
|
|
356
|
-
|
|
357
|
-
config.onPop();
|
|
358
|
-
} else {
|
|
359
|
-
config.onClose();
|
|
360
|
-
}
|
|
361
|
-
onDragUpdate({ offset: 0, isDragging: false });
|
|
537
|
+
dismiss();
|
|
362
538
|
} else {
|
|
363
539
|
onDragUpdate({ offset: 0, isDragging: false });
|
|
364
540
|
}
|
|
365
541
|
},
|
|
366
|
-
[
|
|
367
|
-
panelRef,
|
|
368
|
-
axis,
|
|
369
|
-
config.closeThreshold,
|
|
370
|
-
config.velocityThreshold,
|
|
371
|
-
config.isNested,
|
|
372
|
-
config.onClose,
|
|
373
|
-
config.onPop,
|
|
374
|
-
onDragUpdate,
|
|
375
|
-
config
|
|
376
|
-
]
|
|
542
|
+
[panelRef, axis, config, onDragUpdate, dismiss]
|
|
377
543
|
);
|
|
378
544
|
const handlePointerCancel = useCallback(() => {
|
|
379
545
|
startRef.current = null;
|
|
380
546
|
committedRef.current = null;
|
|
381
547
|
offsetRef.current = 0;
|
|
548
|
+
scrollTargetRef.current = null;
|
|
382
549
|
onDragUpdate({ offset: 0, isDragging: false });
|
|
383
550
|
}, [onDragUpdate]);
|
|
384
551
|
useEffect2(() => {
|
|
@@ -408,82 +575,40 @@ function useDrag(panelRef, config, onDragUpdate) {
|
|
|
408
575
|
|
|
409
576
|
// src/renderer.tsx
|
|
410
577
|
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
touchAction: "none"
|
|
446
|
-
},
|
|
447
|
-
children: /* @__PURE__ */ jsx2("div", { style: HANDLE_BAR_STYLE })
|
|
448
|
-
}
|
|
449
|
-
),
|
|
450
|
-
/* @__PURE__ */ jsxs(
|
|
451
|
-
"div",
|
|
452
|
-
{
|
|
453
|
-
style: {
|
|
454
|
-
display: "flex",
|
|
455
|
-
alignItems: "center",
|
|
456
|
-
height: 48,
|
|
457
|
-
flexShrink: 0,
|
|
458
|
-
padding: "0 12px",
|
|
459
|
-
borderBottom: "1px solid var(--border, transparent)"
|
|
460
|
-
},
|
|
461
|
-
children: [
|
|
462
|
-
isNested && /* @__PURE__ */ jsx2(
|
|
463
|
-
"button",
|
|
464
|
-
{
|
|
465
|
-
"aria-label": "Back",
|
|
466
|
-
onClick: onBack,
|
|
467
|
-
style: BUTTON_STYLE,
|
|
468
|
-
type: "button",
|
|
469
|
-
children: /* @__PURE__ */ jsx2(ArrowLeftIcon, {})
|
|
470
|
-
}
|
|
471
|
-
),
|
|
472
|
-
/* @__PURE__ */ jsx2("div", { style: { flex: 1 } }),
|
|
473
|
-
/* @__PURE__ */ jsx2(
|
|
474
|
-
"button",
|
|
475
|
-
{
|
|
476
|
-
"aria-label": "Close",
|
|
477
|
-
onClick: onClose,
|
|
478
|
-
style: BUTTON_STYLE,
|
|
479
|
-
type: "button",
|
|
480
|
-
children: /* @__PURE__ */ jsx2(XIcon, {})
|
|
481
|
-
}
|
|
482
|
-
)
|
|
483
|
-
]
|
|
484
|
-
}
|
|
485
|
-
)
|
|
486
|
-
] });
|
|
578
|
+
function DefaultHeader({
|
|
579
|
+
isNested,
|
|
580
|
+
onBack,
|
|
581
|
+
onClose,
|
|
582
|
+
className
|
|
583
|
+
}) {
|
|
584
|
+
return /* @__PURE__ */ jsxs(
|
|
585
|
+
"div",
|
|
586
|
+
{
|
|
587
|
+
className: `flex h-14 shrink-0 items-center justify-between border-b px-6 ${className ?? ""}`,
|
|
588
|
+
children: [
|
|
589
|
+
/* @__PURE__ */ jsx2("div", { className: "flex items-center gap-2", children: isNested && /* @__PURE__ */ jsx2(
|
|
590
|
+
"button",
|
|
591
|
+
{
|
|
592
|
+
"aria-label": "Back",
|
|
593
|
+
className: "flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-60 transition-opacity duration-150 hover:opacity-100",
|
|
594
|
+
onClick: onBack,
|
|
595
|
+
type: "button",
|
|
596
|
+
children: /* @__PURE__ */ jsx2(ArrowLeftIcon, {})
|
|
597
|
+
}
|
|
598
|
+
) }),
|
|
599
|
+
/* @__PURE__ */ jsx2(
|
|
600
|
+
"button",
|
|
601
|
+
{
|
|
602
|
+
"aria-label": "Close",
|
|
603
|
+
className: "flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-60 transition-opacity duration-150 hover:opacity-100",
|
|
604
|
+
onClick: onClose,
|
|
605
|
+
type: "button",
|
|
606
|
+
children: /* @__PURE__ */ jsx2(XIcon, {})
|
|
607
|
+
}
|
|
608
|
+
)
|
|
609
|
+
]
|
|
610
|
+
}
|
|
611
|
+
);
|
|
487
612
|
}
|
|
488
613
|
var EMPTY_CLASSNAMES = {
|
|
489
614
|
backdrop: "",
|
|
@@ -534,6 +659,102 @@ function getDragTransform(side, offset) {
|
|
|
534
659
|
return {};
|
|
535
660
|
}
|
|
536
661
|
}
|
|
662
|
+
function usePanelHeight(panelRef, hasSnapPoints) {
|
|
663
|
+
const [height, setHeight] = useState2(0);
|
|
664
|
+
useEffect3(() => {
|
|
665
|
+
const el = panelRef.current;
|
|
666
|
+
if (!(el && hasSnapPoints)) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
setHeight(el.offsetHeight);
|
|
670
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
671
|
+
if (entry) {
|
|
672
|
+
setHeight(entry.contentRect.height);
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
observer.observe(el);
|
|
676
|
+
return () => observer.disconnect();
|
|
677
|
+
}, [panelRef, hasSnapPoints]);
|
|
678
|
+
return height;
|
|
679
|
+
}
|
|
680
|
+
function buildPanelStyle(panelStyles, isTop, hasPanelClass, isDragging) {
|
|
681
|
+
return {
|
|
682
|
+
...panelStyles,
|
|
683
|
+
pointerEvents: isTop ? "auto" : "none",
|
|
684
|
+
...isTop ? {} : { contain: "layout style paint" },
|
|
685
|
+
...isDragging ? { transition: "none" } : {},
|
|
686
|
+
...hasPanelClass ? {} : {
|
|
687
|
+
background: "var(--background, #fff)",
|
|
688
|
+
borderColor: "var(--border, transparent)"
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function buildPanelTransition(isDragging, isTop, spring, stackSpring) {
|
|
693
|
+
const visualTween = {
|
|
694
|
+
type: "tween",
|
|
695
|
+
duration: 0.25,
|
|
696
|
+
ease: "easeOut"
|
|
697
|
+
};
|
|
698
|
+
if (isDragging) {
|
|
699
|
+
return { type: "tween", duration: 0 };
|
|
700
|
+
}
|
|
701
|
+
const base = selectSpring(isTop, spring, stackSpring);
|
|
702
|
+
return { ...base, borderRadius: visualTween, boxShadow: visualTween };
|
|
703
|
+
}
|
|
704
|
+
function computeSnapYOffset(side, snapHeights, activeSnapIndex, measuredHeight) {
|
|
705
|
+
if (side !== "bottom" || snapHeights.length === 0 || measuredHeight <= 0) {
|
|
706
|
+
return 0;
|
|
707
|
+
}
|
|
708
|
+
return getSnapOffset(activeSnapIndex, snapHeights, measuredHeight);
|
|
709
|
+
}
|
|
710
|
+
function PanelInnerContent({
|
|
711
|
+
isComposable,
|
|
712
|
+
shouldRender,
|
|
713
|
+
Content,
|
|
714
|
+
data,
|
|
715
|
+
renderHeader,
|
|
716
|
+
headerProps,
|
|
717
|
+
headerClassName
|
|
718
|
+
}) {
|
|
719
|
+
if (isComposable) {
|
|
720
|
+
return shouldRender && Content ? /* @__PURE__ */ jsx2(Content, { ...data }) : null;
|
|
721
|
+
}
|
|
722
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
723
|
+
renderHeader ? renderHeader(headerProps) : /* @__PURE__ */ jsx2(DefaultHeader, { ...headerProps, className: headerClassName }),
|
|
724
|
+
shouldRender && Content && /* @__PURE__ */ jsx2(
|
|
725
|
+
"div",
|
|
726
|
+
{
|
|
727
|
+
className: "min-h-0 flex-1 overflow-y-auto overscroll-contain",
|
|
728
|
+
"data-stacksheet-no-drag": "",
|
|
729
|
+
children: /* @__PURE__ */ jsx2(Content, { ...data })
|
|
730
|
+
}
|
|
731
|
+
)
|
|
732
|
+
] });
|
|
733
|
+
}
|
|
734
|
+
function BottomHandle() {
|
|
735
|
+
return /* @__PURE__ */ jsx2(
|
|
736
|
+
"div",
|
|
737
|
+
{
|
|
738
|
+
className: "flex shrink-0 cursor-grab touch-none items-center justify-center pt-4 pb-1",
|
|
739
|
+
"data-stacksheet-handle": "",
|
|
740
|
+
children: /* @__PURE__ */ jsx2("div", { className: "h-1 w-9 rounded-sm bg-current/25" })
|
|
741
|
+
}
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
function SideHandle({ side, isHovered }) {
|
|
745
|
+
const position = side === "right" ? { right: "100%" } : { left: "100%" };
|
|
746
|
+
return /* @__PURE__ */ jsx2(
|
|
747
|
+
m.div,
|
|
748
|
+
{
|
|
749
|
+
animate: { opacity: isHovered ? 1 : 0 },
|
|
750
|
+
className: "absolute top-0 bottom-0 flex w-6 cursor-grab touch-none items-center justify-center",
|
|
751
|
+
"data-stacksheet-handle": "",
|
|
752
|
+
style: position,
|
|
753
|
+
transition: { duration: isHovered ? 0.15 : 0.4, ease: "easeOut" },
|
|
754
|
+
children: /* @__PURE__ */ jsx2("div", { className: "h-10 w-[5px] rounded-sm bg-current/35 shadow-sm" })
|
|
755
|
+
}
|
|
756
|
+
);
|
|
757
|
+
}
|
|
537
758
|
function SheetPanel({
|
|
538
759
|
item,
|
|
539
760
|
index,
|
|
@@ -547,6 +768,11 @@ function SheetPanel({
|
|
|
547
768
|
shouldRender,
|
|
548
769
|
pop,
|
|
549
770
|
close,
|
|
771
|
+
swipeClose,
|
|
772
|
+
swipePop,
|
|
773
|
+
snapHeights,
|
|
774
|
+
activeSnapIndex,
|
|
775
|
+
onSnap,
|
|
550
776
|
renderHeader,
|
|
551
777
|
slideFrom,
|
|
552
778
|
slideTarget,
|
|
@@ -560,9 +786,10 @@ function SheetPanel({
|
|
|
560
786
|
offset: 0,
|
|
561
787
|
isDragging: false
|
|
562
788
|
});
|
|
789
|
+
const [isHovered, setIsHovered] = useState2(false);
|
|
790
|
+
const measuredHeight = usePanelHeight(panelRef, snapHeights.length > 0);
|
|
563
791
|
const transform = getStackTransform(depth, config.stacking);
|
|
564
792
|
const panelStyles = getPanelStyles(side, config, depth, index);
|
|
565
|
-
const stackOffset = getStackingOffset(side, transform.offset);
|
|
566
793
|
useEffect3(() => {
|
|
567
794
|
if (!isTop) {
|
|
568
795
|
hasEnteredRef.current = false;
|
|
@@ -581,9 +808,13 @@ function SheetPanel({
|
|
|
581
808
|
closeThreshold: config.closeThreshold,
|
|
582
809
|
velocityThreshold: config.velocityThreshold,
|
|
583
810
|
side,
|
|
584
|
-
onClose:
|
|
585
|
-
onPop:
|
|
586
|
-
isNested
|
|
811
|
+
onClose: swipeClose,
|
|
812
|
+
onPop: swipePop,
|
|
813
|
+
isNested,
|
|
814
|
+
snapHeights,
|
|
815
|
+
activeSnapIndex,
|
|
816
|
+
onSnap,
|
|
817
|
+
sequential: config.snapToSequentialPoints
|
|
587
818
|
},
|
|
588
819
|
setDragState
|
|
589
820
|
);
|
|
@@ -596,33 +827,39 @@ function SheetPanel({
|
|
|
596
827
|
const isComposable = renderHeader === false;
|
|
597
828
|
const hasPanelClass = classNames.panel !== "";
|
|
598
829
|
const dragOffset = getDragTransform(side, dragState.offset);
|
|
599
|
-
const panelStyle =
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
...hasPanelClass ? {} : {
|
|
606
|
-
background: "var(--background, #fff)",
|
|
607
|
-
borderColor: "var(--border, transparent)"
|
|
608
|
-
}
|
|
609
|
-
};
|
|
830
|
+
const panelStyle = buildPanelStyle(
|
|
831
|
+
panelStyles,
|
|
832
|
+
isTop,
|
|
833
|
+
hasPanelClass,
|
|
834
|
+
dragState.isDragging
|
|
835
|
+
);
|
|
610
836
|
const headerProps = {
|
|
611
837
|
isNested,
|
|
612
838
|
onBack: pop,
|
|
613
839
|
onClose: close,
|
|
614
840
|
side
|
|
615
841
|
};
|
|
616
|
-
const isModal = config.modal;
|
|
617
842
|
const ariaProps = buildAriaProps(
|
|
618
843
|
isTop,
|
|
619
|
-
|
|
844
|
+
config.modal,
|
|
620
845
|
isComposable,
|
|
621
846
|
ariaLabel,
|
|
622
847
|
panelId
|
|
623
848
|
);
|
|
624
|
-
const transition =
|
|
849
|
+
const transition = buildPanelTransition(
|
|
850
|
+
dragState.isDragging,
|
|
851
|
+
isTop,
|
|
852
|
+
spring,
|
|
853
|
+
stackSpring
|
|
854
|
+
);
|
|
625
855
|
const animatedRadius = getAnimatedBorderRadius(side, depth, config.stacking);
|
|
856
|
+
const snapYOffset = computeSnapYOffset(
|
|
857
|
+
side,
|
|
858
|
+
snapHeights,
|
|
859
|
+
activeSnapIndex,
|
|
860
|
+
measuredHeight
|
|
861
|
+
);
|
|
862
|
+
const stackOffset = getStackOffset(side, transform.offset);
|
|
626
863
|
const animateTarget = {
|
|
627
864
|
...slideTarget,
|
|
628
865
|
...stackOffset,
|
|
@@ -630,9 +867,19 @@ function SheetPanel({
|
|
|
630
867
|
scale: transform.scale,
|
|
631
868
|
opacity: transform.opacity,
|
|
632
869
|
...animatedRadius,
|
|
633
|
-
|
|
870
|
+
boxShadow: getShadow(side, !isTop),
|
|
871
|
+
transition,
|
|
872
|
+
...snapYOffset > 0 ? { y: (dragOffset.y ?? 0) + snapYOffset } : {}
|
|
634
873
|
};
|
|
635
|
-
const
|
|
874
|
+
const initialRadius = getInitialRadius(side);
|
|
875
|
+
const showSideHandle = isTop && side !== "bottom";
|
|
876
|
+
const showBottomHandle = isTop && side === "bottom";
|
|
877
|
+
const exitTween = {
|
|
878
|
+
type: "tween",
|
|
879
|
+
duration: 0.25,
|
|
880
|
+
ease: "easeOut"
|
|
881
|
+
};
|
|
882
|
+
const panelContent = /* @__PURE__ */ jsxs(
|
|
636
883
|
m.div,
|
|
637
884
|
{
|
|
638
885
|
animate: animateTarget,
|
|
@@ -640,41 +887,44 @@ function SheetPanel({
|
|
|
640
887
|
exit: {
|
|
641
888
|
...slideFrom,
|
|
642
889
|
opacity: 0.6,
|
|
643
|
-
|
|
890
|
+
boxShadow: getShadow(side, false),
|
|
891
|
+
transition: { ...exitSpring, boxShadow: exitTween }
|
|
644
892
|
},
|
|
645
893
|
initial: {
|
|
646
894
|
...slideFrom,
|
|
647
|
-
opacity: 0.8
|
|
895
|
+
opacity: 0.8,
|
|
896
|
+
...initialRadius,
|
|
897
|
+
boxShadow: getShadow(side, false)
|
|
648
898
|
},
|
|
649
899
|
onAnimationComplete: handleAnimationComplete,
|
|
900
|
+
onMouseEnter: showSideHandle ? () => setIsHovered(true) : void 0,
|
|
901
|
+
onMouseLeave: showSideHandle ? () => setIsHovered(false) : void 0,
|
|
650
902
|
ref: panelRef,
|
|
651
903
|
style: panelStyle,
|
|
652
904
|
tabIndex: isTop ? -1 : void 0,
|
|
653
|
-
transition: spring,
|
|
654
905
|
...ariaProps,
|
|
655
|
-
children:
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
] })
|
|
906
|
+
children: [
|
|
907
|
+
showSideHandle && /* @__PURE__ */ jsx2(SideHandle, { isHovered, side }),
|
|
908
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden rounded-[inherit]", children: [
|
|
909
|
+
showBottomHandle && /* @__PURE__ */ jsx2(BottomHandle, {}),
|
|
910
|
+
/* @__PURE__ */ jsx2(
|
|
911
|
+
PanelInnerContent,
|
|
912
|
+
{
|
|
913
|
+
Content,
|
|
914
|
+
data: item.data,
|
|
915
|
+
headerClassName: classNames.header || void 0,
|
|
916
|
+
headerProps,
|
|
917
|
+
isComposable,
|
|
918
|
+
renderHeader,
|
|
919
|
+
shouldRender
|
|
920
|
+
}
|
|
921
|
+
)
|
|
922
|
+
] })
|
|
923
|
+
]
|
|
674
924
|
},
|
|
675
925
|
item.id
|
|
676
926
|
);
|
|
677
|
-
if (!
|
|
927
|
+
if (!config.modal) {
|
|
678
928
|
return /* @__PURE__ */ jsx2(SheetPanelContext.Provider, { value: panelContext, children: panelContent });
|
|
679
929
|
}
|
|
680
930
|
return /* @__PURE__ */ jsx2(SheetPanelContext.Provider, { value: panelContext, children: /* @__PURE__ */ jsx2(
|
|
@@ -739,10 +989,51 @@ function SheetRenderer({
|
|
|
739
989
|
}) {
|
|
740
990
|
const isOpen = useStore(store, (s) => s.isOpen);
|
|
741
991
|
const stack = useStore(store, (s) => s.stack);
|
|
742
|
-
const
|
|
743
|
-
const
|
|
992
|
+
const rawClose = useStore(store, (s) => s.close);
|
|
993
|
+
const rawPop = useStore(store, (s) => s.pop);
|
|
744
994
|
const side = useResolvedSide(config);
|
|
745
995
|
const classNames = resolveClassNames(classNamesProp);
|
|
996
|
+
const snapHeights = useMemo(
|
|
997
|
+
() => side === "bottom" && config.snapPoints.length > 0 ? resolveSnapPoints(
|
|
998
|
+
config.snapPoints,
|
|
999
|
+
typeof window !== "undefined" ? window.innerHeight : 0
|
|
1000
|
+
) : [],
|
|
1001
|
+
[side, config.snapPoints]
|
|
1002
|
+
);
|
|
1003
|
+
const [internalSnapIndex, setInternalSnapIndex] = useState2(
|
|
1004
|
+
snapHeights.length > 0 ? snapHeights.length - 1 : 0
|
|
1005
|
+
);
|
|
1006
|
+
const activeSnapIndex = config.snapPointIndex ?? internalSnapIndex;
|
|
1007
|
+
const handleSnap = useCallback2(
|
|
1008
|
+
(index) => {
|
|
1009
|
+
setInternalSnapIndex(index);
|
|
1010
|
+
config.onSnapPointChange?.(index);
|
|
1011
|
+
},
|
|
1012
|
+
[config.onSnapPointChange, config]
|
|
1013
|
+
);
|
|
1014
|
+
useEffect3(() => {
|
|
1015
|
+
if (isOpen && snapHeights.length > 0) {
|
|
1016
|
+
const initial = config.snapPointIndex ?? snapHeights.length - 1;
|
|
1017
|
+
setInternalSnapIndex(initial);
|
|
1018
|
+
}
|
|
1019
|
+
}, [isOpen, snapHeights.length, config.snapPointIndex]);
|
|
1020
|
+
const closeReasonRef = useRef2("programmatic");
|
|
1021
|
+
const closeWith = useCallback2(
|
|
1022
|
+
(reason) => {
|
|
1023
|
+
closeReasonRef.current = reason;
|
|
1024
|
+
rawClose();
|
|
1025
|
+
},
|
|
1026
|
+
[rawClose]
|
|
1027
|
+
);
|
|
1028
|
+
const popWith = useCallback2(
|
|
1029
|
+
(reason) => {
|
|
1030
|
+
closeReasonRef.current = reason;
|
|
1031
|
+
rawPop();
|
|
1032
|
+
},
|
|
1033
|
+
[rawPop]
|
|
1034
|
+
);
|
|
1035
|
+
const close = useCallback2(() => closeWith("programmatic"), [closeWith]);
|
|
1036
|
+
const pop = useCallback2(() => popWith("programmatic"), [popWith]);
|
|
746
1037
|
useBodyScale(config, isOpen);
|
|
747
1038
|
const triggerRef = useRef2(null);
|
|
748
1039
|
const wasOpenRef = useRef2(false);
|
|
@@ -766,9 +1057,9 @@ function SheetRenderer({
|
|
|
766
1057
|
if (e.key === "Escape") {
|
|
767
1058
|
e.preventDefault();
|
|
768
1059
|
if (stack.length > 1) {
|
|
769
|
-
|
|
1060
|
+
popWith("escape");
|
|
770
1061
|
} else {
|
|
771
|
-
|
|
1062
|
+
closeWith("escape");
|
|
772
1063
|
}
|
|
773
1064
|
}
|
|
774
1065
|
}
|
|
@@ -779,9 +1070,23 @@ function SheetRenderer({
|
|
|
779
1070
|
config.closeOnEscape,
|
|
780
1071
|
config.dismissible,
|
|
781
1072
|
stack.length,
|
|
782
|
-
|
|
783
|
-
|
|
1073
|
+
popWith,
|
|
1074
|
+
closeWith
|
|
784
1075
|
]);
|
|
1076
|
+
useEffect3(() => {
|
|
1077
|
+
if (!(isOpen && config.dismissible) || typeof globalThis.CloseWatcher === "undefined") {
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
const watcher = new globalThis.CloseWatcher();
|
|
1081
|
+
watcher.onclose = () => {
|
|
1082
|
+
if (stack.length > 1) {
|
|
1083
|
+
popWith("escape");
|
|
1084
|
+
} else {
|
|
1085
|
+
closeWith("escape");
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
return () => watcher.destroy();
|
|
1089
|
+
}, [isOpen, config.dismissible, stack.length, popWith, closeWith]);
|
|
785
1090
|
const slideFrom = getSlideFrom(side);
|
|
786
1091
|
const slideTarget = getSlideTarget();
|
|
787
1092
|
const spring = {
|
|
@@ -796,27 +1101,27 @@ function SheetRenderer({
|
|
|
796
1101
|
const showOverlay = isModal && config.showOverlay;
|
|
797
1102
|
const hasBackdropClass = classNames.backdrop !== "";
|
|
798
1103
|
const backdropStyle = {
|
|
799
|
-
position: "fixed",
|
|
800
|
-
inset: 0,
|
|
801
1104
|
zIndex: config.zIndex,
|
|
802
1105
|
cursor: config.closeOnBackdrop && config.dismissible ? "pointer" : void 0,
|
|
803
1106
|
...hasBackdropClass ? {} : { background: "var(--overlay, rgba(0, 0, 0, 0.2))" }
|
|
804
1107
|
};
|
|
805
1108
|
const handleExitComplete = useCallback2(() => {
|
|
806
1109
|
if (stack.length === 0) {
|
|
807
|
-
config.onCloseComplete?.();
|
|
1110
|
+
config.onCloseComplete?.(closeReasonRef.current);
|
|
808
1111
|
}
|
|
809
1112
|
}, [stack.length, config]);
|
|
1113
|
+
const swipeClose = useCallback2(() => closeWith("swipe"), [closeWith]);
|
|
1114
|
+
const swipePop = useCallback2(() => popWith("swipe"), [popWith]);
|
|
810
1115
|
const shouldLockScroll = isOpen && isModal && config.lockScroll;
|
|
811
1116
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
812
1117
|
showOverlay && /* @__PURE__ */ jsx2(AnimatePresence, { children: isOpen && /* @__PURE__ */ jsx2(
|
|
813
1118
|
m.div,
|
|
814
1119
|
{
|
|
815
1120
|
animate: { opacity: 1 },
|
|
816
|
-
className: classNames.backdrop ||
|
|
1121
|
+
className: `fixed inset-0 ${classNames.backdrop || ""}`,
|
|
817
1122
|
exit: { opacity: 0 },
|
|
818
1123
|
initial: { opacity: 0 },
|
|
819
|
-
onClick: config.closeOnBackdrop && config.dismissible ?
|
|
1124
|
+
onClick: config.closeOnBackdrop && config.dismissible ? () => closeWith("backdrop") : void 0,
|
|
820
1125
|
style: backdropStyle,
|
|
821
1126
|
transition: spring
|
|
822
1127
|
},
|
|
@@ -825,22 +1130,18 @@ function SheetRenderer({
|
|
|
825
1130
|
/* @__PURE__ */ jsx2(RemoveScroll, { enabled: shouldLockScroll, forwardProps: true, children: /* @__PURE__ */ jsx2(
|
|
826
1131
|
"div",
|
|
827
1132
|
{
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
inset: 0,
|
|
831
|
-
zIndex: config.zIndex + 1,
|
|
832
|
-
overflow: "hidden",
|
|
833
|
-
pointerEvents: "none"
|
|
834
|
-
},
|
|
1133
|
+
className: "pointer-events-none fixed inset-0 overflow-hidden",
|
|
1134
|
+
style: { zIndex: config.zIndex + 1 },
|
|
835
1135
|
children: /* @__PURE__ */ jsx2(AnimatePresence, { onExitComplete: handleExitComplete, children: stack.map((item, index) => {
|
|
836
1136
|
const depth = stack.length - 1 - index;
|
|
837
1137
|
const isTop = depth === 0;
|
|
838
|
-
const isNested =
|
|
839
|
-
const shouldRender = depth
|
|
1138
|
+
const isNested = index > 0;
|
|
1139
|
+
const shouldRender = depth <= config.stacking.renderThreshold;
|
|
840
1140
|
const Content = componentMap.get(item.type) ?? sheets[item.type];
|
|
841
1141
|
return /* @__PURE__ */ jsx2(
|
|
842
1142
|
SheetPanel,
|
|
843
1143
|
{
|
|
1144
|
+
activeSnapIndex,
|
|
844
1145
|
Content,
|
|
845
1146
|
classNames,
|
|
846
1147
|
close,
|
|
@@ -851,14 +1152,18 @@ function SheetRenderer({
|
|
|
851
1152
|
isNested,
|
|
852
1153
|
isTop,
|
|
853
1154
|
item,
|
|
1155
|
+
onSnap: handleSnap,
|
|
854
1156
|
pop,
|
|
855
1157
|
renderHeader,
|
|
856
1158
|
shouldRender,
|
|
857
1159
|
side,
|
|
858
1160
|
slideFrom,
|
|
859
1161
|
slideTarget,
|
|
1162
|
+
snapHeights,
|
|
860
1163
|
spring,
|
|
861
|
-
stackSpring
|
|
1164
|
+
stackSpring,
|
|
1165
|
+
swipeClose,
|
|
1166
|
+
swipePop
|
|
862
1167
|
},
|
|
863
1168
|
item.id
|
|
864
1169
|
);
|
|
@@ -867,27 +1172,21 @@ function SheetRenderer({
|
|
|
867
1172
|
) })
|
|
868
1173
|
] });
|
|
869
1174
|
}
|
|
870
|
-
function
|
|
871
|
-
if (offset === 0) {
|
|
872
|
-
return {};
|
|
873
|
-
}
|
|
874
|
-
switch (side) {
|
|
875
|
-
case "right":
|
|
876
|
-
return { x: -offset };
|
|
877
|
-
case "left":
|
|
878
|
-
return { x: offset };
|
|
879
|
-
case "bottom":
|
|
880
|
-
return { y: -offset };
|
|
881
|
-
default:
|
|
882
|
-
return {};
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
function getShadow(side, isNested) {
|
|
1175
|
+
function getInitialRadius(side) {
|
|
886
1176
|
if (side === "bottom") {
|
|
887
|
-
return
|
|
1177
|
+
return {
|
|
1178
|
+
borderTopLeftRadius: 0,
|
|
1179
|
+
borderTopRightRadius: 0,
|
|
1180
|
+
borderBottomLeftRadius: 0,
|
|
1181
|
+
borderBottomRightRadius: 0
|
|
1182
|
+
};
|
|
888
1183
|
}
|
|
889
|
-
|
|
890
|
-
|
|
1184
|
+
return { borderRadius: 0 };
|
|
1185
|
+
}
|
|
1186
|
+
var SHADOW_SM = "0px 2px 5px 0px rgba(0,0,0,0.11), 0px 9px 9px 0px rgba(0,0,0,0.1), 0px 21px 13px 0px rgba(0,0,0,0.06)";
|
|
1187
|
+
var SHADOW_LG = "0px 23px 52px 0px rgba(0,0,0,0.08), 0px 94px 94px 0px rgba(0,0,0,0.07), 0px 211px 127px 0px rgba(0,0,0,0.04)";
|
|
1188
|
+
function getShadow(_side, isNested) {
|
|
1189
|
+
return isNested ? SHADOW_SM : SHADOW_LG;
|
|
891
1190
|
}
|
|
892
1191
|
|
|
893
1192
|
// src/store.ts
|
|
@@ -1172,21 +1471,6 @@ import {
|
|
|
1172
1471
|
} from "@radix-ui/react-scroll-area";
|
|
1173
1472
|
import { Slot } from "@radix-ui/react-slot";
|
|
1174
1473
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1175
|
-
var HANDLE_STYLE = {
|
|
1176
|
-
display: "flex",
|
|
1177
|
-
alignItems: "center",
|
|
1178
|
-
justifyContent: "center",
|
|
1179
|
-
padding: "12px 0 4px",
|
|
1180
|
-
flexShrink: 0,
|
|
1181
|
-
cursor: "grab",
|
|
1182
|
-
touchAction: "none"
|
|
1183
|
-
};
|
|
1184
|
-
var HANDLE_BAR_STYLE2 = {
|
|
1185
|
-
width: 36,
|
|
1186
|
-
height: 4,
|
|
1187
|
-
borderRadius: 2,
|
|
1188
|
-
background: "var(--muted-foreground, rgba(0, 0, 0, 0.25))"
|
|
1189
|
-
};
|
|
1190
1474
|
function SheetHandle({
|
|
1191
1475
|
asChild,
|
|
1192
1476
|
className,
|
|
@@ -1197,16 +1481,13 @@ function SheetHandle({
|
|
|
1197
1481
|
return /* @__PURE__ */ jsx4(
|
|
1198
1482
|
Comp,
|
|
1199
1483
|
{
|
|
1200
|
-
className
|
|
1484
|
+
className: `flex shrink-0 cursor-grab touch-none items-center justify-center pt-4 pb-1 ${className ?? ""}`,
|
|
1201
1485
|
"data-stacksheet-handle": "",
|
|
1202
|
-
style
|
|
1203
|
-
children: children ?? /* @__PURE__ */ jsx4("div", {
|
|
1486
|
+
style,
|
|
1487
|
+
children: children ?? /* @__PURE__ */ jsx4("div", { className: "h-1 w-9 rounded-sm bg-current/25" })
|
|
1204
1488
|
}
|
|
1205
1489
|
);
|
|
1206
1490
|
}
|
|
1207
|
-
var HEADER_STYLE = {
|
|
1208
|
-
flexShrink: 0
|
|
1209
|
-
};
|
|
1210
1491
|
function SheetHeader({
|
|
1211
1492
|
asChild,
|
|
1212
1493
|
className,
|
|
@@ -1214,12 +1495,27 @@ function SheetHeader({
|
|
|
1214
1495
|
children
|
|
1215
1496
|
}) {
|
|
1216
1497
|
const Comp = asChild ? Slot : "header";
|
|
1217
|
-
return /* @__PURE__ */ jsx4(
|
|
1498
|
+
return /* @__PURE__ */ jsx4(
|
|
1499
|
+
Comp,
|
|
1500
|
+
{
|
|
1501
|
+
className: `flex h-14 shrink-0 items-center justify-between border-b px-6 ${className ?? ""}`,
|
|
1502
|
+
style,
|
|
1503
|
+
children
|
|
1504
|
+
}
|
|
1505
|
+
);
|
|
1218
1506
|
}
|
|
1219
1507
|
function SheetTitle({ asChild, className, style, children }) {
|
|
1220
1508
|
const { panelId } = useSheetPanel();
|
|
1221
1509
|
const Comp = asChild ? Slot : "h2";
|
|
1222
|
-
return /* @__PURE__ */ jsx4(
|
|
1510
|
+
return /* @__PURE__ */ jsx4(
|
|
1511
|
+
Comp,
|
|
1512
|
+
{
|
|
1513
|
+
className: `font-semibold text-sm ${className ?? ""}`,
|
|
1514
|
+
id: `${panelId}-title`,
|
|
1515
|
+
style,
|
|
1516
|
+
children
|
|
1517
|
+
}
|
|
1518
|
+
);
|
|
1223
1519
|
}
|
|
1224
1520
|
function SheetDescription({
|
|
1225
1521
|
asChild,
|
|
@@ -1231,32 +1527,14 @@ function SheetDescription({
|
|
|
1231
1527
|
const Comp = asChild ? Slot : "p";
|
|
1232
1528
|
return /* @__PURE__ */ jsx4(Comp, { className, id: `${panelId}-desc`, style, children });
|
|
1233
1529
|
}
|
|
1234
|
-
var BODY_STYLE = {
|
|
1235
|
-
flex: 1,
|
|
1236
|
-
minHeight: 0,
|
|
1237
|
-
position: "relative"
|
|
1238
|
-
};
|
|
1239
|
-
var SCROLLBAR_STYLE = {
|
|
1240
|
-
display: "flex",
|
|
1241
|
-
userSelect: "none",
|
|
1242
|
-
touchAction: "none",
|
|
1243
|
-
padding: 2,
|
|
1244
|
-
width: 8
|
|
1245
|
-
};
|
|
1246
|
-
var THUMB_STYLE = {
|
|
1247
|
-
flex: 1,
|
|
1248
|
-
borderRadius: 4,
|
|
1249
|
-
background: "var(--border, rgba(0, 0, 0, 0.15))",
|
|
1250
|
-
position: "relative"
|
|
1251
|
-
};
|
|
1252
1530
|
function SheetBody({ asChild, className, style, children }) {
|
|
1253
1531
|
if (asChild) {
|
|
1254
1532
|
return /* @__PURE__ */ jsx4(
|
|
1255
1533
|
Slot,
|
|
1256
1534
|
{
|
|
1257
|
-
className
|
|
1535
|
+
className: `relative min-h-0 flex-1 ${className ?? ""}`,
|
|
1258
1536
|
"data-stacksheet-no-drag": "",
|
|
1259
|
-
style
|
|
1537
|
+
style,
|
|
1260
1538
|
children
|
|
1261
1539
|
}
|
|
1262
1540
|
);
|
|
@@ -1264,25 +1542,23 @@ function SheetBody({ asChild, className, style, children }) {
|
|
|
1264
1542
|
return /* @__PURE__ */ jsxs3(
|
|
1265
1543
|
ScrollAreaRoot,
|
|
1266
1544
|
{
|
|
1267
|
-
className
|
|
1545
|
+
className: `relative min-h-0 flex-1 overflow-hidden ${className ?? ""}`,
|
|
1268
1546
|
"data-stacksheet-no-drag": "",
|
|
1269
|
-
style
|
|
1547
|
+
style,
|
|
1270
1548
|
children: [
|
|
1549
|
+
/* @__PURE__ */ jsx4(ScrollAreaViewport, { className: "h-full w-full overscroll-contain", children }),
|
|
1271
1550
|
/* @__PURE__ */ jsx4(
|
|
1272
|
-
|
|
1551
|
+
ScrollAreaScrollbar,
|
|
1273
1552
|
{
|
|
1274
|
-
|
|
1275
|
-
|
|
1553
|
+
className: "flex w-2 touch-none select-none p-0.5",
|
|
1554
|
+
orientation: "vertical",
|
|
1555
|
+
children: /* @__PURE__ */ jsx4(ScrollAreaThumb, { className: "relative flex-1 rounded bg-current/15" })
|
|
1276
1556
|
}
|
|
1277
|
-
)
|
|
1278
|
-
/* @__PURE__ */ jsx4(ScrollAreaScrollbar, { orientation: "vertical", style: SCROLLBAR_STYLE, children: /* @__PURE__ */ jsx4(ScrollAreaThumb, { style: THUMB_STYLE }) })
|
|
1557
|
+
)
|
|
1279
1558
|
]
|
|
1280
1559
|
}
|
|
1281
1560
|
);
|
|
1282
1561
|
}
|
|
1283
|
-
var FOOTER_STYLE = {
|
|
1284
|
-
flexShrink: 0
|
|
1285
|
-
};
|
|
1286
1562
|
function SheetFooter({
|
|
1287
1563
|
asChild,
|
|
1288
1564
|
className,
|
|
@@ -1290,7 +1566,14 @@ function SheetFooter({
|
|
|
1290
1566
|
children
|
|
1291
1567
|
}) {
|
|
1292
1568
|
const Comp = asChild ? Slot : "footer";
|
|
1293
|
-
return /* @__PURE__ */ jsx4(
|
|
1569
|
+
return /* @__PURE__ */ jsx4(
|
|
1570
|
+
Comp,
|
|
1571
|
+
{
|
|
1572
|
+
className: `flex shrink-0 items-center gap-2 border-t px-6 py-3 ${className ?? ""}`,
|
|
1573
|
+
style,
|
|
1574
|
+
children
|
|
1575
|
+
}
|
|
1576
|
+
);
|
|
1294
1577
|
}
|
|
1295
1578
|
function SheetClose({ asChild, className, style, children }) {
|
|
1296
1579
|
const { close } = useSheetPanel();
|
|
@@ -1299,7 +1582,7 @@ function SheetClose({ asChild, className, style, children }) {
|
|
|
1299
1582
|
Comp,
|
|
1300
1583
|
{
|
|
1301
1584
|
"aria-label": children ? void 0 : "Close",
|
|
1302
|
-
className
|
|
1585
|
+
className: `flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-60 transition-opacity duration-150 hover:opacity-100 ${className ?? ""}`,
|
|
1303
1586
|
onClick: close,
|
|
1304
1587
|
style,
|
|
1305
1588
|
type: asChild ? void 0 : "button",
|
|
@@ -1317,7 +1600,7 @@ function SheetBack({ asChild, className, style, children }) {
|
|
|
1317
1600
|
Comp,
|
|
1318
1601
|
{
|
|
1319
1602
|
"aria-label": children ? void 0 : "Back",
|
|
1320
|
-
className
|
|
1603
|
+
className: `flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-60 transition-opacity duration-150 hover:opacity-100 ${className ?? ""}`,
|
|
1321
1604
|
onClick: back,
|
|
1322
1605
|
style,
|
|
1323
1606
|
type: asChild ? void 0 : "button",
|