@johly/vaul-svelte 1.0.0-next.8

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.
Files changed (53) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +58 -0
  3. package/dist/components/drawer/drawer-content.svelte +60 -0
  4. package/dist/components/drawer/drawer-content.svelte.d.ts +5 -0
  5. package/dist/components/drawer/drawer-handle.svelte +31 -0
  6. package/dist/components/drawer/drawer-handle.svelte.d.ts +4 -0
  7. package/dist/components/drawer/drawer-nested.svelte +37 -0
  8. package/dist/components/drawer/drawer-nested.svelte.d.ts +3 -0
  9. package/dist/components/drawer/drawer-overlay.svelte +32 -0
  10. package/dist/components/drawer/drawer-overlay.svelte.d.ts +5 -0
  11. package/dist/components/drawer/drawer-portal.svelte +10 -0
  12. package/dist/components/drawer/drawer-portal.svelte.d.ts +3 -0
  13. package/dist/components/drawer/drawer.svelte +383 -0
  14. package/dist/components/drawer/drawer.svelte.d.ts +3 -0
  15. package/dist/components/drawer/index.d.ts +12 -0
  16. package/dist/components/drawer/index.js +11 -0
  17. package/dist/components/drawer/types.d.ts +126 -0
  18. package/dist/components/drawer/types.js +1 -0
  19. package/dist/components/index.d.ts +1 -0
  20. package/dist/components/index.js +1 -0
  21. package/dist/components/utils/mounted.svelte +12 -0
  22. package/dist/components/utils/mounted.svelte.d.ts +6 -0
  23. package/dist/context.d.ts +42 -0
  24. package/dist/context.js +2 -0
  25. package/dist/helpers.d.ts +16 -0
  26. package/dist/helpers.js +95 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +2 -0
  29. package/dist/internal/browser.d.ts +8 -0
  30. package/dist/internal/browser.js +30 -0
  31. package/dist/internal/constants.d.ts +11 -0
  32. package/dist/internal/constants.js +11 -0
  33. package/dist/internal/noop.d.ts +1 -0
  34. package/dist/internal/noop.js +3 -0
  35. package/dist/internal/use-id.d.ts +4 -0
  36. package/dist/internal/use-id.js +8 -0
  37. package/dist/types.d.ts +12 -0
  38. package/dist/types.js +1 -0
  39. package/dist/use-drawer-content.svelte.js +187 -0
  40. package/dist/use-drawer-handle.svelte.d.ts +18 -0
  41. package/dist/use-drawer-handle.svelte.js +83 -0
  42. package/dist/use-drawer-overlay.svelte.d.ts +15 -0
  43. package/dist/use-drawer-overlay.svelte.js +40 -0
  44. package/dist/use-drawer-root.svelte.js +575 -0
  45. package/dist/use-position-fixed.svelte.d.ts +20 -0
  46. package/dist/use-position-fixed.svelte.js +114 -0
  47. package/dist/use-prevent-scroll.svelte.d.ts +15 -0
  48. package/dist/use-prevent-scroll.svelte.js +235 -0
  49. package/dist/use-scale-background.svelte.d.ts +1 -0
  50. package/dist/use-scale-background.svelte.js +57 -0
  51. package/dist/use-snap-points.svelte.d.ts +34 -0
  52. package/dist/use-snap-points.svelte.js +260 -0
  53. package/package.json +64 -0
@@ -0,0 +1,260 @@
1
+ import { onMount } from "svelte";
2
+ import { on } from "svelte/events";
3
+ import { isVertical, set } from "./helpers.js";
4
+ import { TRANSITIONS, VELOCITY_THRESHOLD } from "./internal/constants.js";
5
+ import { watch } from "runed";
6
+ export function useSnapPoints({ snapPoints, drawerNode: drawerNode, overlayNode: overlayNode, fadeFromIndex, setOpenTime, direction, container, snapToSequentialPoint, activeSnapPoint, open, isReleasing, }) {
7
+ let windowDimensions = $state(typeof window !== "undefined"
8
+ ? { innerWidth: window.innerWidth, innerHeight: window.innerHeight }
9
+ : undefined);
10
+ onMount(() => {
11
+ function onResize() {
12
+ windowDimensions = {
13
+ innerWidth: window.innerWidth,
14
+ innerHeight: window.innerHeight,
15
+ };
16
+ }
17
+ return on(window, "resize", onResize);
18
+ });
19
+ const isLastSnapPoint = $derived(activeSnapPoint.current === snapPoints.current?.[snapPoints.current.length - 1] || null);
20
+ const activeSnapPointIndex = $derived(snapPoints.current?.findIndex((snapPoint) => snapPoint === activeSnapPoint.current));
21
+ const shouldFade = $derived((snapPoints.current &&
22
+ snapPoints.current.length > 0 &&
23
+ (fadeFromIndex.current || fadeFromIndex.current === 0) &&
24
+ !Number.isNaN(fadeFromIndex.current) &&
25
+ snapPoints.current[fadeFromIndex.current] === activeSnapPoint.current) ||
26
+ !snapPoints.current);
27
+ const snapPointsOffset = $derived.by(() => {
28
+ open.current;
29
+ const containerSize = container.current
30
+ ? {
31
+ width: container.current.getBoundingClientRect().width,
32
+ height: container.current.getBoundingClientRect().height,
33
+ }
34
+ : typeof window !== "undefined"
35
+ ? { width: window.innerWidth, height: window.innerHeight }
36
+ : { width: 0, height: 0 };
37
+ return (snapPoints.current?.map((snapPoint) => {
38
+ const isPx = typeof snapPoint === "string";
39
+ let snapPointAsNumber = 0;
40
+ if (isPx) {
41
+ snapPointAsNumber = parseInt(snapPoint, 10);
42
+ }
43
+ if (isVertical(direction.current)) {
44
+ const height = isPx
45
+ ? snapPointAsNumber
46
+ : windowDimensions
47
+ ? snapPoint * containerSize.height
48
+ : 0;
49
+ if (windowDimensions) {
50
+ return direction.current === "bottom"
51
+ ? containerSize.height - height
52
+ : -containerSize.height + height;
53
+ }
54
+ return height;
55
+ }
56
+ const width = isPx
57
+ ? snapPointAsNumber
58
+ : windowDimensions
59
+ ? snapPoint * containerSize.width
60
+ : 0;
61
+ if (windowDimensions) {
62
+ return direction.current === "right"
63
+ ? containerSize.width - width
64
+ : -containerSize.width + width;
65
+ }
66
+ return width;
67
+ }) ?? []);
68
+ });
69
+ const activeSnapPointOffset = $derived.by(() => {
70
+ if (activeSnapPointIndex !== null) {
71
+ if (activeSnapPointIndex !== undefined) {
72
+ return snapPointsOffset[activeSnapPointIndex];
73
+ }
74
+ }
75
+ return null;
76
+ });
77
+ function onSnapPointChange(activeSnapPointIndex) {
78
+ if (snapPoints.current && activeSnapPointIndex === snapPointsOffset.length - 1) {
79
+ setOpenTime(new Date());
80
+ }
81
+ }
82
+ function snapToPoint(dimension) {
83
+ const newSnapPointIndex = snapPointsOffset?.findIndex((snapPointDim) => snapPointDim === dimension) ?? null;
84
+ onSnapPointChange(newSnapPointIndex);
85
+ set(drawerNode(), {
86
+ transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
87
+ transform: isVertical(direction.current)
88
+ ? `translate3d(0, ${dimension}px, 0)`
89
+ : `translate3d(${dimension}px, 0, 0)`,
90
+ });
91
+ if (snapPointsOffset &&
92
+ newSnapPointIndex !== snapPointsOffset.length - 1 &&
93
+ fadeFromIndex.current !== undefined &&
94
+ newSnapPointIndex !== fadeFromIndex.current &&
95
+ newSnapPointIndex < fadeFromIndex.current) {
96
+ set(overlayNode(), {
97
+ transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
98
+ opacity: "0",
99
+ });
100
+ }
101
+ else {
102
+ set(overlayNode(), {
103
+ transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
104
+ opacity: "1",
105
+ });
106
+ }
107
+ activeSnapPoint.current = snapPoints.current?.[Math.max(newSnapPointIndex, 0)];
108
+ }
109
+ watch([() => activeSnapPoint.current, () => open.current], () => {
110
+ // we only want to snap to the next point if we are closing via a
111
+ // means other than release, otherwise a race condition can occur
112
+ // where the drawer snaps to the previous point and then closes,
113
+ // rather than continuing to close from the current point
114
+ const releasing = isReleasing();
115
+ if (!activeSnapPoint.current || releasing)
116
+ return;
117
+ const newIndex = snapPoints.current?.findIndex((snapPoint) => snapPoint === activeSnapPoint.current) ??
118
+ -1;
119
+ if (snapPointsOffset && newIndex !== -1 && typeof snapPointsOffset[newIndex] === "number") {
120
+ if (snapPointsOffset[newIndex] === activeSnapPoint.current)
121
+ return;
122
+ snapToPoint(snapPointsOffset[newIndex]);
123
+ }
124
+ });
125
+ function onRelease({ draggedDistance, closeDrawer, velocity, dismissible, }) {
126
+ if (fadeFromIndex.current === undefined)
127
+ return;
128
+ const dir = direction.current;
129
+ const currentPosition = dir === "bottom" || dir === "right"
130
+ ? (activeSnapPointOffset ?? 0) - draggedDistance
131
+ : (activeSnapPointOffset ?? 0) + draggedDistance;
132
+ const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex.current - 1;
133
+ const isFirst = activeSnapPointIndex === 0;
134
+ const hasDraggedUp = draggedDistance > 0;
135
+ if (isOverlaySnapPoint) {
136
+ set(overlayNode(), {
137
+ transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
138
+ });
139
+ }
140
+ if (!snapToSequentialPoint.current && velocity > 2 && !hasDraggedUp) {
141
+ if (dismissible) {
142
+ closeDrawer();
143
+ }
144
+ else {
145
+ snapToPoint(snapPointsOffset[0]);
146
+ }
147
+ return;
148
+ }
149
+ if (!snapToSequentialPoint.current &&
150
+ velocity > 2 &&
151
+ hasDraggedUp &&
152
+ snapPointsOffset &&
153
+ snapPoints.current) {
154
+ snapToPoint(snapPointsOffset[snapPoints.current.length - 1]);
155
+ return;
156
+ }
157
+ // Find the closest snap point to the current position
158
+ const closestSnapPoint = snapPointsOffset?.reduce((prev, curr) => {
159
+ if (typeof prev !== "number" || typeof curr !== "number")
160
+ return prev;
161
+ return Math.abs(curr - currentPosition) < Math.abs(prev - currentPosition)
162
+ ? curr
163
+ : prev;
164
+ });
165
+ const dim = isVertical(dir) ? window.innerHeight : window.innerWidth;
166
+ if (velocity > VELOCITY_THRESHOLD && Math.abs(draggedDistance) < dim * 0.4) {
167
+ const dragDirection = hasDraggedUp ? 1 : -1; // 1 = up, -1 = down
168
+ // Don't do anything if we swipe upwards while being on the last snap point
169
+ if (dragDirection > 0 && isLastSnapPoint && snapPoints.current) {
170
+ snapToPoint(snapPointsOffset[snapPoints.current.length - 1]);
171
+ return;
172
+ }
173
+ if (isFirst && dragDirection < 0 && dismissible) {
174
+ closeDrawer();
175
+ }
176
+ if (activeSnapPointIndex === null)
177
+ return;
178
+ snapToPoint(snapPointsOffset[activeSnapPointIndex + dragDirection]);
179
+ return;
180
+ }
181
+ snapToPoint(closestSnapPoint);
182
+ }
183
+ function onDrag({ draggedDistance }) {
184
+ if (activeSnapPointOffset === null)
185
+ return;
186
+ const dir = direction.current;
187
+ const newValue = isBottomOrRight(dir)
188
+ ? activeSnapPointOffset - draggedDistance
189
+ : activeSnapPointOffset + draggedDistance;
190
+ const lastSnapPoint = snapPointsOffset[snapPointsOffset.length - 1];
191
+ // Don't do anything if we exceed the last(biggest) snap point
192
+ if (isBottomOrRight(dir) && newValue < lastSnapPoint)
193
+ return;
194
+ if (!isBottomOrRight(dir) && newValue > lastSnapPoint)
195
+ return;
196
+ set(drawerNode(), {
197
+ transform: isVertical(dir)
198
+ ? `translate3d(0, ${newValue}px, 0)`
199
+ : `translate3d(${newValue}px, 0, 0)`,
200
+ });
201
+ }
202
+ function getPercentageDragged(absDraggedDistance, isDraggingDown) {
203
+ if (!snapPoints.current ||
204
+ typeof activeSnapPointIndex !== "number" ||
205
+ !snapPointsOffset ||
206
+ fadeFromIndex.current === undefined) {
207
+ return null;
208
+ }
209
+ // If this is true we are dragging to a snap point that is supposed to have an overlay
210
+ const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex.current - 1;
211
+ const isOverlaySnapPointOrHigher = activeSnapPointIndex >= fadeFromIndex.current;
212
+ if (isOverlaySnapPointOrHigher && isDraggingDown) {
213
+ return 0;
214
+ }
215
+ // Don't animate, but still use this one if we are dragging away from the overlaySnapPoint
216
+ if (isOverlaySnapPoint && !isDraggingDown) {
217
+ return 1;
218
+ }
219
+ if (!shouldFade && !isOverlaySnapPoint) {
220
+ return null;
221
+ }
222
+ // Either fadeFrom index or the one before
223
+ const targetSnapPointIndex = isOverlaySnapPoint
224
+ ? activeSnapPointIndex + 1
225
+ : activeSnapPointIndex - 1;
226
+ // Get the distance from overlaySnapPoint to the one before or vice-versa to calculate the opacity percentage accordingly
227
+ const snapPointDistance = isOverlaySnapPoint
228
+ ? snapPointsOffset[targetSnapPointIndex] - snapPointsOffset[targetSnapPointIndex - 1]
229
+ : snapPointsOffset[targetSnapPointIndex + 1] - snapPointsOffset[targetSnapPointIndex];
230
+ const percentageDragged = absDraggedDistance / Math.abs(snapPointDistance);
231
+ if (isOverlaySnapPoint) {
232
+ return 1 - percentageDragged;
233
+ }
234
+ else {
235
+ return percentageDragged;
236
+ }
237
+ }
238
+ return {
239
+ get isLastSnapPoint() {
240
+ return isLastSnapPoint;
241
+ },
242
+ get shouldFade() {
243
+ return shouldFade;
244
+ },
245
+ get activeSnapPointIndex() {
246
+ return activeSnapPointIndex;
247
+ },
248
+ get snapPointsOffset() {
249
+ return $state.snapshot(snapPointsOffset);
250
+ },
251
+ getPercentageDragged,
252
+ onRelease,
253
+ onDrag,
254
+ };
255
+ }
256
+ export function isBottomOrRight(direction) {
257
+ if (direction === "bottom" || direction === "right")
258
+ return true;
259
+ return false;
260
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@johly/vaul-svelte",
3
+ "version": "1.0.0-next.8",
4
+ "license": "MIT",
5
+ "repository": "github:johanohly/vaul-svelte",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "svelte": "./dist/index.js"
10
+ }
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "!dist/**/*.test.*",
15
+ "!dist/**/*.spec.*"
16
+ ],
17
+ "peerDependencies": {
18
+ "svelte": "^5.0.0"
19
+ },
20
+ "devDependencies": {
21
+ "@sveltejs/kit": "^2.16.1",
22
+ "@sveltejs/package": "^2.3.9",
23
+ "@sveltejs/vite-plugin-svelte": "4.0.0",
24
+ "@testing-library/dom": "^10.4.0",
25
+ "@testing-library/jest-dom": "^6.4.8",
26
+ "@testing-library/svelte": "^5.2.1",
27
+ "@testing-library/user-event": "^14.5.2",
28
+ "@types/jest-axe": "^3.5.9",
29
+ "@types/node": "^20.14.10",
30
+ "@types/resize-observer-browser": "^0.1.11",
31
+ "@types/testing-library__jest-dom": "^5.14.9",
32
+ "autoprefixer": "^10.4.16",
33
+ "bits-ui": "^1.1.0",
34
+ "jsdom": "^24.1.0",
35
+ "publint": "^0.2.8",
36
+ "svelte": "^5.19.6",
37
+ "svelte-check": "^4.1.4",
38
+ "tslib": "^2.6.3",
39
+ "typescript": "^5.5.4",
40
+ "vite": "^5.4.8",
41
+ "vitest": "^2.1.1",
42
+ "vitest-dom": "^0.1.1"
43
+ },
44
+ "svelte": "./dist/index.js",
45
+ "types": "./dist/index.d.ts",
46
+ "type": "module",
47
+ "dependencies": {
48
+ "runed": "^0.23.2",
49
+ "svelte-toolbelt": "^0.7.1"
50
+ },
51
+ "engines": {
52
+ "pnpm": ">=8.7.0",
53
+ "node": ">=18"
54
+ },
55
+ "scripts": {
56
+ "build": "pnpm package",
57
+ "dev": "pnpm watch",
58
+ "dev:svelte": "vite dev",
59
+ "package": "svelte-kit sync && svelte-package && publint",
60
+ "check": "svelte-check --tsconfig ./tsconfig.json",
61
+ "test": "vitest",
62
+ "watch": "svelte-package --watch"
63
+ }
64
+ }