@patternmode/stacksheet 1.3.7 → 1.4.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/Drag/drag-velocity.d.ts +24 -0
- package/dist/Drag/drag-velocity.d.ts.map +1 -0
- package/dist/Drag/use-drag.d.ts.map +1 -1
- package/dist/SheetPanel/default-header.d.ts +1 -3
- package/dist/SheetPanel/default-header.d.ts.map +1 -1
- package/dist/SheetPanel/sheet-panel-content.d.ts +1 -2
- package/dist/SheetPanel/sheet-panel-content.d.ts.map +1 -1
- package/dist/SheetPanel/sheet-panel-focus.d.ts.map +1 -1
- package/dist/SheetPanel/sheet-panel-handles.d.ts +3 -2
- package/dist/SheetPanel/sheet-panel-handles.d.ts.map +1 -1
- package/dist/SheetPanel/sheet-panel-types.d.ts +2 -0
- package/dist/SheetPanel/sheet-panel-types.d.ts.map +1 -1
- package/dist/SheetPanel/sheet-panel.d.ts.map +1 -1
- package/dist/SheetParts/sheet-handle.d.ts +1 -1
- package/dist/SheetParts/sheet-handle.d.ts.map +1 -1
- package/dist/SheetParts/sheet-header.d.ts.map +1 -1
- package/dist/SheetParts/sheet-parts.d.ts +1 -1
- package/dist/SheetParts/sheet-title.d.ts.map +1 -1
- package/dist/Store/store-args.d.ts +6 -0
- package/dist/Store/store-args.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/index.mjs +300 -106
- package/dist/index.mjs.map +1 -1
- package/dist/panel-context.d.ts +4 -0
- package/dist/panel-context.d.ts.map +1 -1
- package/dist/renderer-effects.d.ts +12 -0
- package/dist/renderer-effects.d.ts.map +1 -1
- package/dist/renderer-helpers.d.ts +11 -3
- package/dist/renderer-helpers.d.ts.map +1 -1
- package/dist/renderer.d.ts.map +1 -1
- package/dist/stacking.d.ts +6 -0
- package/dist/stacking.d.ts.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types.d.ts +35 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Portal } from "@radix-ui/react-portal";
|
|
3
|
-
import { createContext, use, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
3
|
+
import { createContext, use, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { createStore, useStore } from "zustand";
|
|
5
5
|
import { useShallow } from "zustand/react/shallow";
|
|
6
6
|
import { springs as springs$1 } from "@howells/motion";
|
|
@@ -56,10 +56,12 @@ const DEFAULT_CONFIG = {
|
|
|
56
56
|
closeThreshold: .25,
|
|
57
57
|
dismissible: true,
|
|
58
58
|
drag: true,
|
|
59
|
+
handle: "inside",
|
|
59
60
|
lockScroll: true,
|
|
60
61
|
maxDepth: Number.POSITIVE_INFINITY,
|
|
61
62
|
maxWidth: "90vw",
|
|
62
63
|
modal: true,
|
|
64
|
+
repositionInputs: true,
|
|
63
65
|
scaleBackgroundAmount: .97,
|
|
64
66
|
shouldScaleBackground: false,
|
|
65
67
|
showOverlay: true,
|
|
@@ -169,38 +171,123 @@ const useViewportHeight = (active) => {
|
|
|
169
171
|
}, []);
|
|
170
172
|
return active ? height ?? 0 : 0;
|
|
171
173
|
};
|
|
172
|
-
|
|
174
|
+
/** Input types that don't summon the on-screen keyboard. */
|
|
175
|
+
const NON_TEXT_INPUT_TYPES = new Set([
|
|
176
|
+
"button",
|
|
177
|
+
"checkbox",
|
|
178
|
+
"color",
|
|
179
|
+
"file",
|
|
180
|
+
"hidden",
|
|
181
|
+
"image",
|
|
182
|
+
"radio",
|
|
183
|
+
"range",
|
|
184
|
+
"reset",
|
|
185
|
+
"submit"
|
|
186
|
+
]);
|
|
187
|
+
/** True for elements whose focus raises the on-screen keyboard. */
|
|
188
|
+
const isEditableElement = (el) => {
|
|
189
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
190
|
+
if (el.isContentEditable) return true;
|
|
191
|
+
if (el instanceof HTMLTextAreaElement) return true;
|
|
192
|
+
if (el instanceof HTMLInputElement) return !NON_TEXT_INPUT_TYPES.has(el.type);
|
|
193
|
+
return false;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Height (px) the on-screen keyboard occupies while a field inside `containerRef`
|
|
197
|
+
* is focused, else `0`. Derived from the gap between the layout viewport and the
|
|
198
|
+
* (keyboard-shrunk, possibly panned) visual viewport.
|
|
199
|
+
*
|
|
200
|
+
* Focus-gated to fields *inside the container* so unrelated `visualViewport`
|
|
201
|
+
* changes — Android URL-bar collapse, or typing into the page behind a
|
|
202
|
+
* non-modal sheet — don't move the sheet, and zero while pinch-zoomed since a
|
|
203
|
+
* shrunk visual viewport then isn't a keyboard. rAF-throttled because iOS
|
|
204
|
+
* fires many resize events over the keyboard's open animation.
|
|
205
|
+
*/
|
|
206
|
+
const useKeyboardInset = (active, containerRef) => {
|
|
207
|
+
const [inset, setInset] = useState(0);
|
|
173
208
|
useEffect(() => {
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
209
|
+
const canListen = active && typeof window !== "undefined";
|
|
210
|
+
const container = containerRef.current;
|
|
211
|
+
const viewport = canListen ? window.visualViewport : void 0;
|
|
212
|
+
let frame = 0;
|
|
213
|
+
let scheduled = false;
|
|
214
|
+
const measure = () => {
|
|
215
|
+
const el = document.activeElement;
|
|
216
|
+
const focused = isEditableElement(el) && container !== null && container.contains(el);
|
|
217
|
+
const zoomed = (viewport?.scale ?? 1) > 1;
|
|
218
|
+
const visibleHeight = viewport?.height ?? window.innerHeight;
|
|
219
|
+
const gap = window.innerHeight - (viewport?.offsetTop ?? 0) - visibleHeight;
|
|
220
|
+
setInset(focused && !zoomed ? Math.max(0, gap) : 0);
|
|
183
221
|
};
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
222
|
+
const schedule = () => {
|
|
223
|
+
if (scheduled) return;
|
|
224
|
+
scheduled = true;
|
|
225
|
+
frame = window.requestAnimationFrame(() => {
|
|
226
|
+
scheduled = false;
|
|
227
|
+
measure();
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
if (canListen) {
|
|
231
|
+
container?.addEventListener("focusin", schedule);
|
|
232
|
+
container?.addEventListener("focusout", schedule);
|
|
233
|
+
viewport?.addEventListener("resize", schedule, { passive: true });
|
|
234
|
+
viewport?.addEventListener("scroll", schedule, { passive: true });
|
|
235
|
+
schedule();
|
|
193
236
|
}
|
|
194
|
-
|
|
195
|
-
|
|
237
|
+
return () => {
|
|
238
|
+
if (frame !== 0) window.cancelAnimationFrame(frame);
|
|
239
|
+
container?.removeEventListener("focusin", schedule);
|
|
240
|
+
container?.removeEventListener("focusout", schedule);
|
|
241
|
+
viewport?.removeEventListener("resize", schedule);
|
|
242
|
+
viewport?.removeEventListener("scroll", schedule);
|
|
196
243
|
};
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
244
|
+
}, [active, containerRef]);
|
|
245
|
+
return active ? inset : 0;
|
|
246
|
+
};
|
|
247
|
+
const BODY_SCALE_TRANSITION = "transform 500ms cubic-bezier(0.32, 0.72, 0, 1), border-radius 500ms cubic-bezier(0.32, 0.72, 0, 1)";
|
|
248
|
+
/** Fallback delay (transition duration + margin) if `transitionend` never fires. */
|
|
249
|
+
const BODY_SCALE_RESET_FALLBACK_MS = 600;
|
|
250
|
+
/** Cancels a pending un-scale reset when the sheet reopens mid-animation. */
|
|
251
|
+
let cancelPendingBodyScaleReset;
|
|
252
|
+
const useBodyScale = (config, isOpen, prefersReducedMotion) => {
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
const wrapper = document.querySelector("[data-stacksheet-wrapper]");
|
|
255
|
+
const scalable = config.shouldScaleBackground && !prefersReducedMotion && wrapper instanceof HTMLElement && isOpen ? wrapper : null;
|
|
256
|
+
if (scalable !== null) {
|
|
257
|
+
cancelPendingBodyScaleReset?.();
|
|
258
|
+
scalable.style.transition = BODY_SCALE_TRANSITION;
|
|
259
|
+
scalable.style.transform = `scale(${config.scaleBackgroundAmount})`;
|
|
260
|
+
scalable.style.borderRadius = "8px";
|
|
261
|
+
scalable.style.overflow = "hidden";
|
|
262
|
+
scalable.style.transformOrigin = "center top";
|
|
201
263
|
}
|
|
202
264
|
return () => {
|
|
203
|
-
if (
|
|
265
|
+
if (scalable === null) return;
|
|
266
|
+
scalable.style.transition = BODY_SCALE_TRANSITION;
|
|
267
|
+
scalable.style.transform = "";
|
|
268
|
+
scalable.style.borderRadius = "";
|
|
269
|
+
const controller = new AbortController();
|
|
270
|
+
const { signal } = controller;
|
|
271
|
+
const finish = () => {
|
|
272
|
+
controller.abort();
|
|
273
|
+
cancelPendingBodyScaleReset = void 0;
|
|
274
|
+
scalable.style.transition = "";
|
|
275
|
+
scalable.style.overflow = "";
|
|
276
|
+
scalable.style.transformOrigin = "";
|
|
277
|
+
};
|
|
278
|
+
scalable.addEventListener("transitionend", (event) => {
|
|
279
|
+
if (event.target === scalable && event.propertyName === "transform") finish();
|
|
280
|
+
}, { signal });
|
|
281
|
+
const timeoutId = setTimeout(() => {
|
|
282
|
+
if (!signal.aborted) finish();
|
|
283
|
+
}, BODY_SCALE_RESET_FALLBACK_MS);
|
|
284
|
+
signal.addEventListener("abort", () => {
|
|
285
|
+
clearTimeout(timeoutId);
|
|
286
|
+
});
|
|
287
|
+
cancelPendingBodyScaleReset = () => {
|
|
288
|
+
controller.abort();
|
|
289
|
+
cancelPendingBodyScaleReset = void 0;
|
|
290
|
+
};
|
|
204
291
|
};
|
|
205
292
|
}, [
|
|
206
293
|
isOpen,
|
|
@@ -307,23 +394,22 @@ const getSnapOffset = (snapIndex, snapHeights, panelHeight) => {
|
|
|
307
394
|
//#region src/renderer-helpers.ts
|
|
308
395
|
const EMPTY_CLASSNAMES = {
|
|
309
396
|
backdrop: "",
|
|
310
|
-
header: "",
|
|
311
397
|
panel: ""
|
|
312
398
|
};
|
|
313
399
|
const resolveClassNames = (cn) => {
|
|
314
400
|
if (!cn) return EMPTY_CLASSNAMES;
|
|
315
401
|
return {
|
|
316
402
|
backdrop: cn.backdrop ?? "",
|
|
317
|
-
header: cn.header ?? "",
|
|
318
403
|
panel: cn.panel ?? ""
|
|
319
404
|
};
|
|
320
405
|
};
|
|
321
|
-
const buildAriaProps = (
|
|
406
|
+
const buildAriaProps = ({ ariaLabel, hasDescription, hasTitle, isComposable, isModal, isTop, panelId }) => {
|
|
322
407
|
if (!isTop) return {};
|
|
323
408
|
const props = { role: "dialog" };
|
|
324
409
|
if (isModal) props["aria-modal"] = "true";
|
|
325
410
|
if (isComposable) {
|
|
326
|
-
props["aria-labelledby"] = `${panelId}-title`;
|
|
411
|
+
if (hasTitle) props["aria-labelledby"] = `${panelId}-title`;
|
|
412
|
+
else props["aria-label"] = ariaLabel;
|
|
327
413
|
if (hasDescription) props["aria-describedby"] = `${panelId}-desc`;
|
|
328
414
|
} else props["aria-label"] = ariaLabel;
|
|
329
415
|
return props;
|
|
@@ -345,8 +431,21 @@ const VISUAL_TWEEN = {
|
|
|
345
431
|
const SHADOW_SM = "0px 1px 3px 0px rgba(0,0,0,0.06), 0px 6px 12px 0px rgba(0,0,0,0.06)";
|
|
346
432
|
const SHADOW_LG = "0px 8px 24px 0px rgba(0,0,0,0.06), 0px 24px 48px 0px rgba(0,0,0,0.04), 0px 48px 96px 0px rgba(0,0,0,0.03)";
|
|
347
433
|
const getShadow = (isNested) => isNested ? SHADOW_SM : SHADOW_LG;
|
|
348
|
-
|
|
434
|
+
/**
|
|
435
|
+
* Clear the on-screen keyboard. Plain sheets stay anchored at bottom: 0 and
|
|
436
|
+
* pad their content up instead — the panel surface extends under the keyboard
|
|
437
|
+
* (and iOS Safari's floating URL-pill chrome), so no backdrop gap ever shows
|
|
438
|
+
* between sheet and keyboard, and measurement error hides behind the keyboard.
|
|
439
|
+
* Snap sheets are transform-anchored, so they lift via `bottom` (not the
|
|
440
|
+
* Motion `y` transform) and keep their viewport-driven sizing untouched.
|
|
441
|
+
*/
|
|
442
|
+
const getKeyboardClearance = (keyboardInset, padForKeyboard) => {
|
|
443
|
+
if (keyboardInset <= 0) return {};
|
|
444
|
+
return padForKeyboard ? { paddingBottom: keyboardInset } : { bottom: keyboardInset };
|
|
445
|
+
};
|
|
446
|
+
const buildPanelStyle = (panelStyles, isTop, hasPanelClass, isDragging, keyboardInset, padForKeyboard) => ({
|
|
349
447
|
...panelStyles,
|
|
448
|
+
...getKeyboardClearance(keyboardInset, padForKeyboard),
|
|
350
449
|
pointerEvents: isTop ? "auto" : "none",
|
|
351
450
|
...isTop ? {} : { contain: "layout style paint" },
|
|
352
451
|
...isDragging ? { transition: "none" } : {},
|
|
@@ -504,12 +603,41 @@ const getPanelDimension = (panel, axis) => {
|
|
|
504
603
|
if (!panel) return 300;
|
|
505
604
|
return axis === "x" ? panel.offsetWidth : panel.offsetHeight;
|
|
506
605
|
};
|
|
606
|
+
/** Upper bound on retained samples — plenty for a 100ms window at 120Hz. */
|
|
607
|
+
const MAX_SAMPLES = 20;
|
|
608
|
+
/**
|
|
609
|
+
* Append a pointer sample, pruning entries that fall outside the sliding
|
|
610
|
+
* window (plus a hard cap as a memory guard). Mutates and returns `samples`.
|
|
611
|
+
*/
|
|
612
|
+
const appendVelocitySample = (samples, sample) => {
|
|
613
|
+
samples.push(sample);
|
|
614
|
+
const cutoff = sample.time - 100;
|
|
615
|
+
while (samples.length > MAX_SAMPLES || samples.length > 2 && (samples[0]?.time ?? 0) < cutoff) samples.shift();
|
|
616
|
+
return samples;
|
|
617
|
+
};
|
|
618
|
+
/**
|
|
619
|
+
* Compute release velocity (px/ms, positive = dismiss direction) from the
|
|
620
|
+
* samples recorded within the sliding window before `releaseTime`.
|
|
621
|
+
*
|
|
622
|
+
* Using only recent samples means a pause followed by a flick reports the
|
|
623
|
+
* flick's velocity — not the whole-gesture average, which would dilute it
|
|
624
|
+
* to near zero.
|
|
625
|
+
*/
|
|
626
|
+
const getReleaseVelocity = (samples, releaseTime) => {
|
|
627
|
+
const cutoff = releaseTime - 100;
|
|
628
|
+
const recent = samples.filter((sample) => sample.time >= cutoff);
|
|
629
|
+
const [first] = recent;
|
|
630
|
+
const last = recent.at(-1);
|
|
631
|
+
if (first === void 0 || last === void 0 || last.time <= first.time) return 0;
|
|
632
|
+
return (last.offset - first.offset) / (last.time - first.time);
|
|
633
|
+
};
|
|
507
634
|
//#endregion
|
|
508
635
|
//#region src/Drag/use-drag.ts
|
|
509
|
-
const resetDragRefs = ({ committedRef, offsetRef, scrollTargetRef, startRef }) => {
|
|
636
|
+
const resetDragRefs = ({ committedRef, offsetRef, samplesRef, scrollTargetRef, startRef }) => {
|
|
510
637
|
startRef.current = null;
|
|
511
638
|
committedRef.current = null;
|
|
512
639
|
offsetRef.current = 0;
|
|
640
|
+
samplesRef.current = [];
|
|
513
641
|
scrollTargetRef.current = null;
|
|
514
642
|
};
|
|
515
643
|
const createDragHandlers = ({ axis, config, onDragUpdate, panelRef, refs, sign }) => {
|
|
@@ -524,13 +652,15 @@ const createDragHandlers = ({ axis, config, onDragUpdate, panelRef, refs, sign }
|
|
|
524
652
|
if (!isHandle && isInteractiveElement(target)) return;
|
|
525
653
|
refs.scrollTargetRef.current = isHandle ? null : findScrollableAncestor(target, axis);
|
|
526
654
|
refs.startRef.current = {
|
|
527
|
-
time: Date.now(),
|
|
528
655
|
x: e.clientX,
|
|
529
656
|
y: e.clientY
|
|
530
657
|
};
|
|
531
658
|
refs.committedRef.current = null;
|
|
532
659
|
refs.offsetRef.current = 0;
|
|
533
|
-
|
|
660
|
+
refs.samplesRef.current = [{
|
|
661
|
+
offset: 0,
|
|
662
|
+
time: Date.now()
|
|
663
|
+
}];
|
|
534
664
|
};
|
|
535
665
|
const handlePointerMove = (e) => {
|
|
536
666
|
if (refs.startRef.current === null) return;
|
|
@@ -544,11 +674,16 @@ const createDragHandlers = ({ axis, config, onDragUpdate, panelRef, refs, sign }
|
|
|
544
674
|
refs.startRef.current = null;
|
|
545
675
|
return;
|
|
546
676
|
}
|
|
677
|
+
if (e.currentTarget instanceof HTMLElement) e.currentTarget.setPointerCapture(e.pointerId);
|
|
547
678
|
}
|
|
548
679
|
if (refs.committedRef.current !== "drag") return;
|
|
549
680
|
const directional = (axis === "x" ? dx : dy) * sign;
|
|
550
681
|
const clampedOffset = directional >= 0 ? directional : -Math.sqrt(Math.abs(directional)) * RUBBER_BAND_FACTOR;
|
|
551
682
|
refs.offsetRef.current = clampedOffset;
|
|
683
|
+
appendVelocitySample(refs.samplesRef.current, {
|
|
684
|
+
offset: clampedOffset,
|
|
685
|
+
time: Date.now()
|
|
686
|
+
});
|
|
552
687
|
onDragUpdate({
|
|
553
688
|
isDragging: true,
|
|
554
689
|
offset: clampedOffset
|
|
@@ -561,8 +696,7 @@ const createDragHandlers = ({ axis, config, onDragUpdate, panelRef, refs, sign }
|
|
|
561
696
|
return;
|
|
562
697
|
}
|
|
563
698
|
const offset = Math.max(0, refs.offsetRef.current);
|
|
564
|
-
const
|
|
565
|
-
const velocity = elapsed > 0 ? offset / elapsed : 0;
|
|
699
|
+
const velocity = getReleaseVelocity(refs.samplesRef.current, Date.now());
|
|
566
700
|
resetDragRefs(refs);
|
|
567
701
|
const panelSize = getPanelDimension(panelRef.current, axis);
|
|
568
702
|
if (config.snapHeights.length > 0) {
|
|
@@ -619,6 +753,7 @@ const useDrag = (panelRef, config, onDragUpdate) => {
|
|
|
619
753
|
const startRef = useRef(null);
|
|
620
754
|
const committedRef = useRef(null);
|
|
621
755
|
const offsetRef = useRef(0);
|
|
756
|
+
const samplesRef = useRef([]);
|
|
622
757
|
const scrollTargetRef = useRef(null);
|
|
623
758
|
const { axis, sign } = getDismissAxis(config.side);
|
|
624
759
|
const { handlePointerCancel, handlePointerDown, handlePointerMove, handlePointerUp } = createDragHandlers({
|
|
@@ -629,6 +764,7 @@ const useDrag = (panelRef, config, onDragUpdate) => {
|
|
|
629
764
|
refs: {
|
|
630
765
|
committedRef,
|
|
631
766
|
offsetRef,
|
|
767
|
+
samplesRef,
|
|
632
768
|
scrollTargetRef,
|
|
633
769
|
startRef
|
|
634
770
|
},
|
|
@@ -692,6 +828,12 @@ const useSheetPanel = () => {
|
|
|
692
828
|
//#endregion
|
|
693
829
|
//#region src/stacking.ts
|
|
694
830
|
/**
|
|
831
|
+
* Resting height of a bottom sheet. `dvh` tracks the dynamic viewport on iOS
|
|
832
|
+
* Safari (accounts for browser chrome). Shared so the keyboard-inset `calc()`
|
|
833
|
+
* in `buildPanelStyle` can't drift from the panel's base height.
|
|
834
|
+
*/
|
|
835
|
+
const BOTTOM_SHEET_HEIGHT = "85dvh";
|
|
836
|
+
/**
|
|
695
837
|
* Compute visual transforms for a panel at a given depth.
|
|
696
838
|
* depth=0 is the top (foreground) panel.
|
|
697
839
|
* Panels beyond renderThreshold are clamped to the edge position and faded out.
|
|
@@ -768,6 +910,7 @@ const getPanelStyles = (side, config, index) => {
|
|
|
768
910
|
const base = {
|
|
769
911
|
display: "flex",
|
|
770
912
|
flexDirection: "column",
|
|
913
|
+
outline: "none",
|
|
771
914
|
position: "fixed",
|
|
772
915
|
transformOrigin: getTransformOrigin(side),
|
|
773
916
|
willChange: "transform",
|
|
@@ -776,9 +919,9 @@ const getPanelStyles = (side, config, index) => {
|
|
|
776
919
|
if (side === "bottom") return {
|
|
777
920
|
...base,
|
|
778
921
|
bottom: 0,
|
|
779
|
-
height:
|
|
922
|
+
height: BOTTOM_SHEET_HEIGHT,
|
|
780
923
|
left: 0,
|
|
781
|
-
maxHeight:
|
|
924
|
+
maxHeight: BOTTOM_SHEET_HEIGHT,
|
|
782
925
|
right: 0
|
|
783
926
|
};
|
|
784
927
|
const sideStyles = side === "right" ? {
|
|
@@ -826,34 +969,25 @@ const XIcon = () => /* @__PURE__ */ jsx("svg", {
|
|
|
826
969
|
});
|
|
827
970
|
//#endregion
|
|
828
971
|
//#region src/SheetPanel/default-header.tsx
|
|
829
|
-
const DefaultHeader = ({ isNested, onBack, onClose
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
className: "flex min-h-11 min-w-11 shrink-0 cursor-pointer items-center justify-center rounded-full border-none bg-black/5 p-0 text-inherit opacity-70 transition-opacity duration-150 hover:opacity-100",
|
|
843
|
-
onClick: onClose,
|
|
844
|
-
type: "button",
|
|
845
|
-
children: /* @__PURE__ */ jsx(XIcon, {})
|
|
846
|
-
})]
|
|
847
|
-
});
|
|
972
|
+
const DefaultHeader = ({ isNested, onBack, onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [isNested && /* @__PURE__ */ jsx("button", {
|
|
973
|
+
"aria-label": "Back",
|
|
974
|
+
className: "absolute left-2 top-2 z-20 flex min-h-11 min-w-11 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-50 transition-opacity duration-150 hover:opacity-100",
|
|
975
|
+
onClick: onBack,
|
|
976
|
+
type: "button",
|
|
977
|
+
children: /* @__PURE__ */ jsx(ArrowLeftIcon, {})
|
|
978
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
979
|
+
"aria-label": "Close",
|
|
980
|
+
className: "absolute right-2 top-2 z-20 flex min-h-11 min-w-11 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-50 transition-opacity duration-150 hover:opacity-100",
|
|
981
|
+
onClick: onClose,
|
|
982
|
+
type: "button",
|
|
983
|
+
children: /* @__PURE__ */ jsx(XIcon, {})
|
|
984
|
+
})] });
|
|
848
985
|
//#endregion
|
|
849
986
|
//#region src/SheetPanel/sheet-panel-content.tsx
|
|
850
|
-
const PanelInnerContent = ({ isComposable, shouldRender, Content, data, renderHeader, headerProps
|
|
987
|
+
const PanelInnerContent = ({ isComposable, shouldRender, Content, data, renderHeader, headerProps }) => {
|
|
851
988
|
if (isComposable) return shouldRender && Content !== void 0 ? /* @__PURE__ */ jsx(Content, { ...data }) : null;
|
|
852
989
|
const customHeader = renderHeader !== void 0 && renderHeader !== false ? renderHeader : void 0;
|
|
853
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [customHeader === void 0 ? /* @__PURE__ */ jsx(DefaultHeader, {
|
|
854
|
-
...headerProps,
|
|
855
|
-
className: headerClassName
|
|
856
|
-
}) : customHeader(headerProps), shouldRender && Content !== void 0 && /* @__PURE__ */ jsx("div", {
|
|
990
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [customHeader === void 0 ? /* @__PURE__ */ jsx(DefaultHeader, { ...headerProps }) : customHeader(headerProps), shouldRender && Content !== void 0 && /* @__PURE__ */ jsx("div", {
|
|
857
991
|
className: "min-h-0 flex-1 overflow-y-auto overscroll-contain",
|
|
858
992
|
"data-stacksheet-no-drag": "",
|
|
859
993
|
children: /* @__PURE__ */ jsx(Content, { ...data })
|
|
@@ -896,7 +1030,7 @@ const ModalFocusTrap = ({ enabled, active, fallbackRef, children }) => {
|
|
|
896
1030
|
if (fallbackRef.current !== null) return fallbackRef.current;
|
|
897
1031
|
return document.body;
|
|
898
1032
|
},
|
|
899
|
-
initialFocus:
|
|
1033
|
+
initialFocus: () => fallbackRef.current ?? void 0,
|
|
900
1034
|
returnFocusOnDeactivate: true
|
|
901
1035
|
},
|
|
902
1036
|
paused,
|
|
@@ -905,9 +1039,9 @@ const ModalFocusTrap = ({ enabled, active, fallbackRef, children }) => {
|
|
|
905
1039
|
};
|
|
906
1040
|
//#endregion
|
|
907
1041
|
//#region src/SheetPanel/sheet-panel-handles.tsx
|
|
908
|
-
const BottomHandle = ({ onDismiss }) => /* @__PURE__ */ jsx("button", {
|
|
1042
|
+
const BottomHandle = ({ onDismiss, position = "inside" }) => /* @__PURE__ */ jsx("button", {
|
|
909
1043
|
"aria-label": "Dismiss",
|
|
910
|
-
className: "absolute inset-x-0
|
|
1044
|
+
className: joinClassNames("absolute inset-x-0 z-10 flex w-full cursor-grab touch-none items-center justify-center border-none bg-transparent text-inherit", position === "outside" ? "bottom-full pt-1 pb-2" : "top-0 pt-2.5 pb-2"),
|
|
911
1045
|
"data-stacksheet-handle": "",
|
|
912
1046
|
onClick: onDismiss,
|
|
913
1047
|
type: "button",
|
|
@@ -969,14 +1103,16 @@ const getHeaderProps = ({ close, isNested, pop, side }) => ({
|
|
|
969
1103
|
onClose: close,
|
|
970
1104
|
side
|
|
971
1105
|
});
|
|
972
|
-
const getPanelContext = ({ close, hasDescription, isNested, isTop, panelId, pop, registerDescription, side }) => ({
|
|
1106
|
+
const getPanelContext = ({ close, hasDescription, hasTitle, isNested, isTop, panelId, pop, registerDescription, registerTitle, side }) => ({
|
|
973
1107
|
back: pop,
|
|
974
1108
|
close,
|
|
975
1109
|
hasDescription,
|
|
1110
|
+
hasTitle,
|
|
976
1111
|
isNested,
|
|
977
1112
|
isTop,
|
|
978
1113
|
panelId,
|
|
979
1114
|
registerDescription,
|
|
1115
|
+
registerTitle,
|
|
980
1116
|
side
|
|
981
1117
|
});
|
|
982
1118
|
const getOptionalSideHandle = ({ isHovered, onDismiss, show, side }) => show ? /* @__PURE__ */ jsx(SideHandle, {
|
|
@@ -984,7 +1120,10 @@ const getOptionalSideHandle = ({ isHovered, onDismiss, show, side }) => show ? /
|
|
|
984
1120
|
onDismiss,
|
|
985
1121
|
side
|
|
986
1122
|
}) : null;
|
|
987
|
-
const getOptionalBottomHandle = (show, onDismiss) => show ? /* @__PURE__ */ jsx(BottomHandle, {
|
|
1123
|
+
const getOptionalBottomHandle = (show, onDismiss, position) => show ? /* @__PURE__ */ jsx(BottomHandle, {
|
|
1124
|
+
onDismiss,
|
|
1125
|
+
position
|
|
1126
|
+
}) : null;
|
|
988
1127
|
const completeOpeningAnimation = (hasEnteredRef, isTop, onOpenCompleteRef) => {
|
|
989
1128
|
if (isTop && !hasEnteredRef.current) {
|
|
990
1129
|
hasEnteredRef.current = true;
|
|
@@ -1024,28 +1163,49 @@ const useSheetPanelDrag = ({ activeSnapIndex, config, isNested, isTop, onSnap, p
|
|
|
1024
1163
|
};
|
|
1025
1164
|
const useSheetPanelContext = ({ close, isNested, isTop, pop, side }, panelId) => {
|
|
1026
1165
|
const [hasDescription, setHasDescription] = useState(false);
|
|
1027
|
-
const
|
|
1166
|
+
const [hasTitle, setHasTitle] = useState(false);
|
|
1167
|
+
const registerDescription = useCallback(() => {
|
|
1028
1168
|
setHasDescription(true);
|
|
1029
1169
|
return () => {
|
|
1030
1170
|
setHasDescription(false);
|
|
1031
1171
|
};
|
|
1032
|
-
};
|
|
1172
|
+
}, []);
|
|
1173
|
+
const registerTitle = useCallback(() => {
|
|
1174
|
+
setHasTitle(true);
|
|
1175
|
+
return () => {
|
|
1176
|
+
setHasTitle(false);
|
|
1177
|
+
};
|
|
1178
|
+
}, []);
|
|
1033
1179
|
return {
|
|
1034
1180
|
hasDescription,
|
|
1035
|
-
|
|
1181
|
+
hasTitle,
|
|
1182
|
+
panelContext: useMemo(() => getPanelContext({
|
|
1036
1183
|
close,
|
|
1037
1184
|
hasDescription,
|
|
1185
|
+
hasTitle,
|
|
1038
1186
|
isNested,
|
|
1039
1187
|
isTop,
|
|
1040
1188
|
panelId,
|
|
1041
1189
|
pop,
|
|
1042
1190
|
registerDescription,
|
|
1191
|
+
registerTitle,
|
|
1043
1192
|
side
|
|
1044
|
-
})
|
|
1193
|
+
}), [
|
|
1194
|
+
close,
|
|
1195
|
+
hasDescription,
|
|
1196
|
+
hasTitle,
|
|
1197
|
+
isNested,
|
|
1198
|
+
isTop,
|
|
1199
|
+
panelId,
|
|
1200
|
+
pop,
|
|
1201
|
+
registerDescription,
|
|
1202
|
+
registerTitle,
|
|
1203
|
+
side
|
|
1204
|
+
])
|
|
1045
1205
|
};
|
|
1046
1206
|
};
|
|
1047
1207
|
const useSheetPanelModel = (props) => {
|
|
1048
|
-
const { item, index, depth, isTop, isNested, side, config, classNames, pop, close, snapHeights, activeSnapIndex, layout, renderHeader, slideFrom, slideTarget, spring, stackSpring } = props;
|
|
1208
|
+
const { item, index, depth, isTop, isNested, side, config, classNames, pop, close, snapHeights, keyboardInset, activeSnapIndex, layout, renderHeader, slideFrom, slideTarget, spring, stackSpring } = props;
|
|
1049
1209
|
const panelRef = useRef(null);
|
|
1050
1210
|
const [isHovered, setIsHovered] = useState(false);
|
|
1051
1211
|
const measuredHeight = usePanelHeight(panelRef, snapHeights.length > 0);
|
|
@@ -1055,18 +1215,27 @@ const useSheetPanelModel = (props) => {
|
|
|
1055
1215
|
const dragState = useSheetPanelDrag(props, panelRef);
|
|
1056
1216
|
const ariaLabel = getPanelAriaLabel(item, config.ariaLabel);
|
|
1057
1217
|
const panelId = `stacksheet-${item.id}`;
|
|
1058
|
-
const { hasDescription, panelContext } = useSheetPanelContext(props, panelId);
|
|
1218
|
+
const { hasDescription, hasTitle, panelContext } = useSheetPanelContext(props, panelId);
|
|
1059
1219
|
const isComposable = resolvePanelLayout(layout, renderHeader) === "composable";
|
|
1060
1220
|
const hasPanelClass = classNames.panel !== "";
|
|
1061
1221
|
const dragOffset = getDragTransform(side, dragState.offset);
|
|
1062
|
-
const
|
|
1222
|
+
const activeKeyboardInset = isTop && side === "bottom" ? keyboardInset : 0;
|
|
1223
|
+
const panelStyle = buildPanelStyle(panelStyles, isTop, hasPanelClass, dragState.isDragging, activeKeyboardInset, snapHeights.length === 0);
|
|
1063
1224
|
const headerProps = getHeaderProps({
|
|
1064
1225
|
close,
|
|
1065
1226
|
isNested,
|
|
1066
1227
|
pop,
|
|
1067
1228
|
side
|
|
1068
1229
|
});
|
|
1069
|
-
const ariaProps = buildAriaProps(
|
|
1230
|
+
const ariaProps = buildAriaProps({
|
|
1231
|
+
ariaLabel,
|
|
1232
|
+
hasDescription,
|
|
1233
|
+
hasTitle,
|
|
1234
|
+
isComposable,
|
|
1235
|
+
isModal: config.modal,
|
|
1236
|
+
isTop,
|
|
1237
|
+
panelId
|
|
1238
|
+
});
|
|
1070
1239
|
const transition = buildPanelTransition(dragState.isDragging, isTop, spring, stackSpring);
|
|
1071
1240
|
const animatedRadius = getAnimatedBorderRadius(side, depth, config.stacking);
|
|
1072
1241
|
const snapYOffset = computeSnapYOffset(side, snapHeights, activeSnapIndex, measuredHeight);
|
|
@@ -1074,7 +1243,7 @@ const useSheetPanelModel = (props) => {
|
|
|
1074
1243
|
const animateTarget = buildAnimateTarget(slideTarget, getStackOffset(side, transform.offset), dragOffset, transform, animatedRadius, transition, snapYOffset, isTop);
|
|
1075
1244
|
const initialRadius = getInitialRadius(side);
|
|
1076
1245
|
const showSideHandle = isTop && side !== "bottom";
|
|
1077
|
-
const showBottomHandle = isTop && side === "bottom";
|
|
1246
|
+
const showBottomHandle = isTop && side === "bottom" && !isComposable;
|
|
1078
1247
|
const dismiss = isNested ? pop : close;
|
|
1079
1248
|
const sideHandle = getOptionalSideHandle({
|
|
1080
1249
|
isHovered,
|
|
@@ -1085,7 +1254,8 @@ const useSheetPanelModel = (props) => {
|
|
|
1085
1254
|
return {
|
|
1086
1255
|
animateTarget,
|
|
1087
1256
|
ariaProps,
|
|
1088
|
-
bottomHandle: getOptionalBottomHandle(showBottomHandle, dismiss),
|
|
1257
|
+
bottomHandle: getOptionalBottomHandle(showBottomHandle, dismiss, config.handle),
|
|
1258
|
+
bottomHandleOutside: showBottomHandle && config.handle === "outside",
|
|
1089
1259
|
handleAnimationComplete,
|
|
1090
1260
|
headerProps,
|
|
1091
1261
|
hoverProps: getPanelHoverProps(showSideHandle, setIsHovered),
|
|
@@ -1101,7 +1271,7 @@ const useSheetPanelModel = (props) => {
|
|
|
1101
1271
|
};
|
|
1102
1272
|
const SheetPanel = (props) => {
|
|
1103
1273
|
const { item, isTop, config, classNames, Content, shouldRender, renderHeader, prefersReducedMotion } = props;
|
|
1104
|
-
const { animateTarget, ariaProps, bottomHandle, handleAnimationComplete, headerProps, hoverProps, inactivePanelProps, initialRadius, isComposable, panelContext, panelRef, panelStyle, resolvedSlideFrom, sideHandle } = useSheetPanelModel(props);
|
|
1274
|
+
const { animateTarget, ariaProps, bottomHandle, bottomHandleOutside, handleAnimationComplete, headerProps, hoverProps, inactivePanelProps, initialRadius, isComposable, panelContext, panelRef, panelStyle, resolvedSlideFrom, sideHandle } = useSheetPanelModel(props);
|
|
1105
1275
|
const panelContent = /* @__PURE__ */ jsxs(m.div, {
|
|
1106
1276
|
animate: animateTarget,
|
|
1107
1277
|
className: classNames.panel || void 0,
|
|
@@ -1129,18 +1299,21 @@ const SheetPanel = (props) => {
|
|
|
1129
1299
|
...hoverProps,
|
|
1130
1300
|
...inactivePanelProps,
|
|
1131
1301
|
...ariaProps,
|
|
1132
|
-
children: [
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1302
|
+
children: [
|
|
1303
|
+
sideHandle,
|
|
1304
|
+
bottomHandleOutside ? bottomHandle : null,
|
|
1305
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1306
|
+
className: "relative flex min-h-0 flex-1 flex-col overflow-hidden rounded-[inherit]",
|
|
1307
|
+
children: [bottomHandleOutside ? null : bottomHandle, /* @__PURE__ */ jsx(PanelInnerContent, {
|
|
1308
|
+
Content,
|
|
1309
|
+
data: item.data,
|
|
1310
|
+
headerProps,
|
|
1311
|
+
isComposable,
|
|
1312
|
+
renderHeader,
|
|
1313
|
+
shouldRender
|
|
1314
|
+
})]
|
|
1315
|
+
})
|
|
1316
|
+
]
|
|
1144
1317
|
}, item.id);
|
|
1145
1318
|
return /* @__PURE__ */ jsx(SheetPanelContext.Provider, {
|
|
1146
1319
|
value: panelContext,
|
|
@@ -1227,6 +1400,13 @@ const useSnapState = (config, isOpen, side, stack) => {
|
|
|
1227
1400
|
snapHeights
|
|
1228
1401
|
};
|
|
1229
1402
|
};
|
|
1403
|
+
const usePanelKeyboardInset = (config, isOpen, side) => {
|
|
1404
|
+
const panelWrapperRef = useRef(null);
|
|
1405
|
+
return {
|
|
1406
|
+
keyboardInset: useKeyboardInset(isOpen && side === "bottom" && config.repositionInputs, panelWrapperRef),
|
|
1407
|
+
panelWrapperRef
|
|
1408
|
+
};
|
|
1409
|
+
};
|
|
1230
1410
|
const useCloseControls = (rawClose, rawPop) => {
|
|
1231
1411
|
const closeReasonRef = useRef("programmatic");
|
|
1232
1412
|
const closeWith = (reason) => {
|
|
@@ -1275,15 +1455,14 @@ const useDismissalEffects = ({ closeReasonRef, config, isOpen, rawClose, rawPop,
|
|
|
1275
1455
|
useEffect(() => {
|
|
1276
1456
|
const shouldListen = isOpen && config.closeOnEscape && config.dismissible;
|
|
1277
1457
|
const handleKeyDown = (e) => {
|
|
1278
|
-
if (e.key
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
}
|
|
1458
|
+
if (e.key !== "Escape" || e.defaultPrevented) return;
|
|
1459
|
+
e.preventDefault();
|
|
1460
|
+
dismissFromEscape({
|
|
1461
|
+
closeReasonRef,
|
|
1462
|
+
rawClose,
|
|
1463
|
+
rawPop,
|
|
1464
|
+
stackLengthRef
|
|
1465
|
+
});
|
|
1287
1466
|
};
|
|
1288
1467
|
if (shouldListen) document.addEventListener("keydown", handleKeyDown);
|
|
1289
1468
|
return () => {
|
|
@@ -1299,7 +1478,7 @@ const useDismissalEffects = ({ closeReasonRef, config, isOpen, rawClose, rawPop,
|
|
|
1299
1478
|
]);
|
|
1300
1479
|
useEffect(() => {
|
|
1301
1480
|
const CloseWatcherConstructor = globalThis.CloseWatcher;
|
|
1302
|
-
const shouldListen = isOpen && config.dismissible && CloseWatcherConstructor !== void 0;
|
|
1481
|
+
const shouldListen = isOpen && config.closeOnEscape && config.dismissible && CloseWatcherConstructor !== void 0;
|
|
1303
1482
|
let watcher;
|
|
1304
1483
|
const handleClose = () => {
|
|
1305
1484
|
dismissFromEscape({
|
|
@@ -1321,6 +1500,7 @@ const useDismissalEffects = ({ closeReasonRef, config, isOpen, rawClose, rawPop,
|
|
|
1321
1500
|
};
|
|
1322
1501
|
}, [
|
|
1323
1502
|
isOpen,
|
|
1503
|
+
config.closeOnEscape,
|
|
1324
1504
|
config.dismissible,
|
|
1325
1505
|
rawPop,
|
|
1326
1506
|
rawClose,
|
|
@@ -1341,6 +1521,7 @@ const SheetRenderer = ({ store, config, sheets, componentMap, classNames: classN
|
|
|
1341
1521
|
const classNames = resolveClassNames(classNamesProp);
|
|
1342
1522
|
const { activeSnapIndex, handleSnap, snapHeights } = useSnapState(config, isOpen, side, stack);
|
|
1343
1523
|
const { close, closeReasonRef, closeWith, pop, popWith } = useCloseControls(rawClose, rawPop);
|
|
1524
|
+
const { panelWrapperRef, keyboardInset } = usePanelKeyboardInset(config, isOpen, side);
|
|
1344
1525
|
useBodyScale(config, isOpen, prefersReducedMotion);
|
|
1345
1526
|
useFocusRestore(isOpen);
|
|
1346
1527
|
useDismissalEffects({
|
|
@@ -1386,6 +1567,7 @@ const SheetRenderer = ({ store, config, sheets, componentMap, classNames: classN
|
|
|
1386
1567
|
}), /* @__PURE__ */ jsx(RemoveScroll, {
|
|
1387
1568
|
enabled: shouldLockScroll,
|
|
1388
1569
|
forwardProps: true,
|
|
1570
|
+
ref: panelWrapperRef,
|
|
1389
1571
|
children: /* @__PURE__ */ jsx("div", {
|
|
1390
1572
|
className: "pointer-events-none fixed inset-0 overflow-hidden",
|
|
1391
1573
|
style: { zIndex: config.zIndex + 1 },
|
|
@@ -1407,6 +1589,7 @@ const SheetRenderer = ({ store, config, sheets, componentMap, classNames: classN
|
|
|
1407
1589
|
isNested,
|
|
1408
1590
|
isTop,
|
|
1409
1591
|
item,
|
|
1592
|
+
keyboardInset,
|
|
1410
1593
|
layout,
|
|
1411
1594
|
onSnap: handleSnap,
|
|
1412
1595
|
pop,
|
|
@@ -1438,6 +1621,15 @@ const getComponentName = (component) => {
|
|
|
1438
1621
|
return name === "" ? void 0 : name;
|
|
1439
1622
|
};
|
|
1440
1623
|
const getNodeEnv = () => globalThis.process?.env?.NODE_ENV;
|
|
1624
|
+
/**
|
|
1625
|
+
* Generate a unique sheet id. `crypto.randomUUID` is only available in
|
|
1626
|
+
* secure contexts, so fall back to a timestamp + random suffix on
|
|
1627
|
+
* non-secure origins (e.g. plain-HTTP LAN dev servers).
|
|
1628
|
+
*/
|
|
1629
|
+
const generateSheetId = () => {
|
|
1630
|
+
if (typeof globalThis.crypto?.randomUUID === "function") return globalThis.crypto.randomUUID();
|
|
1631
|
+
return `sheet-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
1632
|
+
};
|
|
1441
1633
|
const getStringArg = (value, name) => {
|
|
1442
1634
|
if (typeof value !== "string") throw new TypeError(`Expected ${name} to be a string.`);
|
|
1443
1635
|
return value;
|
|
@@ -1489,7 +1681,7 @@ const resolveArgs = (componentRegistry, componentMap, getNextKey, warnedNames, f
|
|
|
1489
1681
|
return {
|
|
1490
1682
|
ariaLabel: resolvePresentationOptions(third)?.ariaLabel,
|
|
1491
1683
|
data: toRecord(second),
|
|
1492
|
-
id:
|
|
1684
|
+
id: generateSheetId(),
|
|
1493
1685
|
type: typeKey
|
|
1494
1686
|
};
|
|
1495
1687
|
}
|
|
@@ -1828,7 +2020,7 @@ const SheetClose = ({ asChild, className, style, children }) => {
|
|
|
1828
2020
|
const isAsChild = asChild === true;
|
|
1829
2021
|
return /* @__PURE__ */ jsx(isAsChild ? Slot : "button", {
|
|
1830
2022
|
"aria-label": children === void 0 || children === null ? "Close" : void 0,
|
|
1831
|
-
className: joinClassNames(isAsChild ? void 0 : "flex min-h-11 min-w-11
|
|
2023
|
+
className: joinClassNames(isAsChild ? void 0 : "absolute right-2 top-2 z-20 flex min-h-11 min-w-11 cursor-pointer items-center justify-center rounded-md border-none bg-transparent p-0 text-inherit opacity-50 transition-opacity duration-150 hover:opacity-100", className),
|
|
1832
2024
|
onClick: close,
|
|
1833
2025
|
style,
|
|
1834
2026
|
type: isAsChild ? void 0 : "button",
|
|
@@ -1860,7 +2052,8 @@ const SheetFooter = ({ asChild, className, style, children }) => {
|
|
|
1860
2052
|
//#endregion
|
|
1861
2053
|
//#region src/SheetParts/sheet-handle.tsx
|
|
1862
2054
|
const SheetHandle = ({ asChild, className, style, children }) => {
|
|
1863
|
-
const { close, back, isNested } = useSheetPanel();
|
|
2055
|
+
const { close, back, isNested, side } = useSheetPanel();
|
|
2056
|
+
if (side !== "bottom") return null;
|
|
1864
2057
|
const dismiss = isNested ? back : close;
|
|
1865
2058
|
const isAsChild = asChild === true;
|
|
1866
2059
|
return /* @__PURE__ */ jsx(isAsChild ? Slot : "button", {
|
|
@@ -1881,7 +2074,7 @@ const SheetHandle = ({ asChild, className, style, children }) => {
|
|
|
1881
2074
|
const SheetHeader = ({ asChild, className, style, children }) => {
|
|
1882
2075
|
const isAsChild = asChild === true;
|
|
1883
2076
|
return /* @__PURE__ */ jsx(isAsChild ? Slot : "header", {
|
|
1884
|
-
className: joinClassNames(isAsChild ? "shrink-0" : "flex
|
|
2077
|
+
className: joinClassNames(isAsChild ? "shrink-0" : "flex shrink-0 items-center justify-between gap-3", className),
|
|
1885
2078
|
style,
|
|
1886
2079
|
children
|
|
1887
2080
|
});
|
|
@@ -1889,7 +2082,8 @@ const SheetHeader = ({ asChild, className, style, children }) => {
|
|
|
1889
2082
|
//#endregion
|
|
1890
2083
|
//#region src/SheetParts/sheet-title.tsx
|
|
1891
2084
|
const SheetTitle = ({ asChild, className, style, children }) => {
|
|
1892
|
-
const { panelId } = useSheetPanel();
|
|
2085
|
+
const { panelId, registerTitle } = useSheetPanel();
|
|
2086
|
+
useEffect(() => registerTitle(), [registerTitle]);
|
|
1893
2087
|
const isAsChild = asChild === true;
|
|
1894
2088
|
return /* @__PURE__ */ jsx(isAsChild ? Slot : "h2", {
|
|
1895
2089
|
className: joinClassNames(isAsChild ? void 0 : "font-semibold text-sm", className),
|