@johly/vaul-svelte 1.0.0-next.11 → 1.0.0-next.13

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.
@@ -6,11 +6,14 @@
6
6
  import { CLOSE_THRESHOLD, SCROLL_LOCK_TIMEOUT } from "../../internal/constants.js";
7
7
  import { useDrawerRoot } from "../../use-drawer-root.svelte.js";
8
8
  import type { ParentDrawerState } from "../../types.js";
9
+ import {
10
+ registerDrawer,
11
+ unregisterDrawer,
12
+ getCurrentParentDrawer,
13
+ } from "../../drawer-registry.js";
9
14
 
10
15
  let {
11
16
  open = $bindable(false),
12
- drawerState = $bindable(),
13
- parentDrawer,
14
17
  onOpenChange = noop,
15
18
  onDrag: onDragProp = noop,
16
19
  onRelease: onReleaseProp = noop,
@@ -38,35 +41,47 @@
38
41
  autoFocus = false,
39
42
  disablePreventScroll = true,
40
43
  ...restProps
41
- }: RootProps & {
42
- drawerState?: ParentDrawerState;
43
- parentDrawer?: ParentDrawerState;
44
- } = $props();
44
+ }: RootProps = $props();
45
+
46
+ // Track the auto-detected parent drawer
47
+ // If drawer starts open, try to auto-detect immediately
48
+ const initialParent = open && !nested ? getCurrentParentDrawer() : undefined;
49
+ let resolvedParentDrawer = $state<ParentDrawerState | undefined>(initialParent);
50
+
51
+ // Auto-detect parent when opening (for drawers that open after mount)
52
+ let wasOpen = open;
53
+ $effect.pre(() => {
54
+ // Detect parent on open transition (false -> true)
55
+ if (open && !wasOpen && !nested) {
56
+ resolvedParentDrawer = getCurrentParentDrawer();
57
+ }
58
+ wasOpen = open;
59
+ });
45
60
 
46
- // When parentDrawer is provided, this drawer behaves as nested
47
- const isNestedViaParent = $derived(!!parentDrawer);
61
+ // When parentDrawer is provided or auto-detected, this drawer behaves as nested
62
+ const isNestedViaParent = $derived(!!resolvedParentDrawer);
48
63
  const effectiveNested = $derived(nested || isNestedViaParent);
49
64
 
50
65
  // Wrap onDrag to also notify parent drawer
51
66
  function onDrag(event: PointerEvent, percentageDragged: number) {
52
67
  onDragProp(event, percentageDragged);
53
- if (parentDrawer) {
54
- parentDrawer.onNestedDrag(event, percentageDragged);
68
+ if (resolvedParentDrawer) {
69
+ resolvedParentDrawer.onNestedDrag(event, percentageDragged);
55
70
  }
56
71
  }
57
72
 
58
73
  // Wrap onRelease to also notify parent drawer
59
74
  function onRelease(event: PointerEvent, isOpen: boolean) {
60
75
  onReleaseProp(event, isOpen);
61
- if (parentDrawer) {
62
- parentDrawer.onNestedRelease(event, isOpen);
76
+ if (resolvedParentDrawer) {
77
+ resolvedParentDrawer.onNestedRelease(event, isOpen);
63
78
  }
64
79
  }
65
80
 
66
81
  // Watch open state to notify parent drawer
67
82
  $effect(() => {
68
- if (parentDrawer) {
69
- parentDrawer.onNestedOpenChange(open);
83
+ if (resolvedParentDrawer) {
84
+ resolvedParentDrawer.onNestedOpenChange(open);
70
85
  }
71
86
  });
72
87
 
@@ -111,9 +126,25 @@
111
126
  onAnimationEnd: box.with(() => onAnimationEnd),
112
127
  });
113
128
 
114
- // Expose drawer state for parent-child relationships with global/portal drawers
129
+ // Get stable reference to drawer state for registry
130
+ const stableDrawerState = rootState.parentDrawerState;
131
+
132
+ // Register/unregister with global drawer registry
133
+ $effect(() => {
134
+ if (open) {
135
+ registerDrawer(stableDrawerState);
136
+ return () => unregisterDrawer(stableDrawerState);
137
+ }
138
+ });
139
+
140
+ // Clear auto-detected parent after drawer closes (separate effect to avoid loops)
115
141
  $effect(() => {
116
- drawerState = rootState.parentDrawerState;
142
+ if (!open && resolvedParentDrawer) {
143
+ // Use queueMicrotask to avoid triggering other effects synchronously
144
+ queueMicrotask(() => {
145
+ resolvedParentDrawer = undefined;
146
+ });
147
+ }
117
148
  });
118
149
  </script>
119
150
 
@@ -1,9 +1,3 @@
1
- import type { RootProps } from "./index.js";
2
- import type { ParentDrawerState } from "../../types.js";
3
- type $$ComponentProps = RootProps & {
4
- drawerState?: ParentDrawerState;
5
- parentDrawer?: ParentDrawerState;
6
- };
7
- declare const Drawer: import("svelte").Component<$$ComponentProps, {}, "open" | "activeSnapPoint" | "drawerState">;
1
+ declare const Drawer: import("svelte").Component<import("../../types.js").DrawerRootPropsWithoutHTML, {}, "open" | "activeSnapPoint">;
8
2
  type Drawer = ReturnType<typeof Drawer>;
9
3
  export default Drawer;
@@ -0,0 +1,15 @@
1
+ import type { ParentDrawerState } from "./types.js";
2
+ /**
3
+ * Register a drawer as open.
4
+ */
5
+ export declare function registerDrawer(state: ParentDrawerState): void;
6
+ /**
7
+ * Unregister a drawer when it closes.
8
+ */
9
+ export declare function unregisterDrawer(state: ParentDrawerState): void;
10
+ /**
11
+ * Get the current topmost drawer state (if any).
12
+ * Used to auto-detect parent drawer when a new drawer opens.
13
+ * Returns the most recently opened drawer, which becomes the parent of the new drawer.
14
+ */
15
+ export declare function getCurrentParentDrawer(): ParentDrawerState | undefined;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Global registry for tracking open drawers.
3
+ * Enables automatic parent-child relationships without manual prop passing.
4
+ *
5
+ * NOTE: This is intentionally NOT using $state to avoid creating reactive
6
+ * dependencies that would cause infinite effect loops.
7
+ */
8
+ // Stack of open drawers (most recent last) - intentionally non-reactive
9
+ let openDrawers = [];
10
+ /**
11
+ * Register a drawer as open.
12
+ */
13
+ export function registerDrawer(state) {
14
+ openDrawers.push(state);
15
+ }
16
+ /**
17
+ * Unregister a drawer when it closes.
18
+ */
19
+ export function unregisterDrawer(state) {
20
+ const index = openDrawers.indexOf(state);
21
+ if (index !== -1) {
22
+ openDrawers.splice(index, 1);
23
+ }
24
+ }
25
+ /**
26
+ * Get the current topmost drawer state (if any).
27
+ * Used to auto-detect parent drawer when a new drawer opens.
28
+ * Returns the most recently opened drawer, which becomes the parent of the new drawer.
29
+ */
30
+ export function getCurrentParentDrawer() {
31
+ return openDrawers.at(-1);
32
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./components/index.js";
2
2
  export * from "./types.js";
3
+ export { getCurrentParentDrawer } from "./drawer-registry.js";
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./components/index.js";
2
2
  export * from "./types.js";
3
+ export { getCurrentParentDrawer } from "./drawer-registry.js";
@@ -109,6 +109,10 @@ export function useDrawerContent(opts) {
109
109
  const delta = { x: xPosition, y: yPosition };
110
110
  const isAllowedToSwipe = isDeltaInDirection(delta, ctx.direction.current, swipeStartThreshold);
111
111
  if (isAllowedToSwipe) {
112
+ // Prevent scrollable content from scrolling while dragging the drawer
113
+ if (ctx.isDragging) {
114
+ e.preventDefault();
115
+ }
112
116
  ctx.onDrag(e);
113
117
  // Prevent parent drawer from receiving the drag event
114
118
  if (ctx.nested.current) {
@@ -163,6 +167,10 @@ export function useDrawerContent(opts) {
163
167
  const delta = { x: xPosition, y: yPosition };
164
168
  const isAllowedToSwipe = isDeltaInDirection(delta, ctx.direction.current, swipeStartThreshold);
165
169
  if (isAllowedToSwipe) {
170
+ // Prevent scrollable content from scrolling while dragging the drawer
171
+ if (ctx.isDragging) {
172
+ e.preventDefault();
173
+ }
166
174
  ctx.onDrag(syntheticEvent);
167
175
  // Prevent parent drawer from receiving the touch drag event
168
176
  if (ctx.nested.current) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johly/vaul-svelte",
3
- "version": "1.0.0-next.11",
3
+ "version": "1.0.0-next.13",
4
4
  "license": "MIT",
5
5
  "repository": "github:johanohly/vaul-svelte",
6
6
  "exports": {