@surdeddd/bottom-sheet 0.2.0 → 0.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/README.md +51 -21
- package/dist/{BottomSheetEngine-CIT9Htti.d.cts → BottomSheetEngine-BJrH7qY3.d.cts} +105 -3
- package/dist/{BottomSheetEngine-Df9MwBbm.d.ts → BottomSheetEngine-L05jGZvU.d.ts} +105 -3
- package/dist/element.cjs +1170 -196
- package/dist/element.d.cts +29 -5
- package/dist/element.d.ts +29 -5
- package/dist/element.iife.global.js +2 -2
- package/dist/element.js +1170 -196
- package/dist/index.cjs +1051 -220
- package/dist/index.d.cts +18 -3
- package/dist/index.d.ts +18 -3
- package/dist/index.js +1049 -221
- package/dist/integrations/formik.d.cts +1 -1
- package/dist/integrations/formik.d.ts +1 -1
- package/dist/integrations/react-hook-form.d.cts +1 -1
- package/dist/integrations/react-hook-form.d.ts +1 -1
- package/dist/overlay.cjs +181 -37
- package/dist/overlay.d.cts +13 -0
- package/dist/overlay.d.ts +13 -0
- package/dist/overlay.js +181 -37
- package/dist/preact.d.cts +1 -1
- package/dist/preact.d.ts +1 -1
- package/dist/qwik.cjs +966 -195
- package/dist/qwik.d.cts +4 -2
- package/dist/qwik.d.ts +4 -2
- package/dist/qwik.js +967 -196
- package/dist/react.cjs +1242 -272
- package/dist/react.d.cts +47 -5
- package/dist/react.d.ts +47 -5
- package/dist/react.js +1242 -272
- package/dist/solid.cjs +992 -236
- package/dist/solid.d.cts +299 -4
- package/dist/solid.d.ts +299 -4
- package/dist/solid.js +993 -237
- package/dist/solid.source.jsx +3476 -0
- package/dist/styles.css +67 -66
- package/dist/svelte-core.cjs +3415 -0
- package/dist/svelte-core.js +3413 -0
- package/dist/svelte-src/BottomSheet.svelte +308 -0
- package/dist/svelte-src/index.js +2 -0
- package/dist/svelte.cjs +1034 -207
- package/dist/svelte.d.cts +121 -0
- package/dist/svelte.d.ts +28 -3
- package/dist/svelte.js +1035 -208
- package/dist/vue.cjs +1191 -205
- package/dist/vue.d.cts +127 -0
- package/dist/vue.d.ts +57 -4
- package/dist/vue.js +1192 -206
- package/package.json +105 -45
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
<script lang="ts" generics="TId extends string = string">
|
|
2
|
+
import { onMount, onDestroy, untrack, type Snippet } from "svelte";
|
|
3
|
+
import { BottomSheetEngine } from "@surdeddd/bottom-sheet";
|
|
4
|
+
import type {
|
|
5
|
+
EngineState,
|
|
6
|
+
SnapPointDef,
|
|
7
|
+
SheetMode,
|
|
8
|
+
ScrimUpdate,
|
|
9
|
+
ScrimOverlayOptions,
|
|
10
|
+
EngineOptions,
|
|
11
|
+
SheetEventMap,
|
|
12
|
+
} from "@surdeddd/bottom-sheet";
|
|
13
|
+
|
|
14
|
+
type Props = {
|
|
15
|
+
snapPoints: SnapPointDef<TId>[];
|
|
16
|
+
allowed?: TId[];
|
|
17
|
+
initial?: TId;
|
|
18
|
+
mode?: SheetMode;
|
|
19
|
+
animation?: "spring" | "tween";
|
|
20
|
+
spring?: { stiffness?: number; damping?: number; mass?: number };
|
|
21
|
+
focusTrap?: boolean;
|
|
22
|
+
closeOnEscape?: boolean;
|
|
23
|
+
closeOnBack?: boolean;
|
|
24
|
+
lockBodyScroll?: boolean;
|
|
25
|
+
rubberBand?: boolean;
|
|
26
|
+
backdropRange?: [number, number];
|
|
27
|
+
backdrop?: boolean;
|
|
28
|
+
closeOnBackdrop?: boolean;
|
|
29
|
+
persistent?: boolean;
|
|
30
|
+
disableClose?: boolean;
|
|
31
|
+
disableDrag?: boolean;
|
|
32
|
+
closeOnRouteChange?: boolean;
|
|
33
|
+
radius?: string | number;
|
|
34
|
+
maxHeight?: string | number;
|
|
35
|
+
backdropColor?: string;
|
|
36
|
+
backdropOpacity?: number;
|
|
37
|
+
open?: boolean;
|
|
38
|
+
snap?: TId;
|
|
39
|
+
returnFocusTo?: HTMLElement | string | (() => HTMLElement | null);
|
|
40
|
+
ariaLabel?: string;
|
|
41
|
+
header?: Snippet<[EngineState & { activeId: TId }]>;
|
|
42
|
+
leftButton?: Snippet;
|
|
43
|
+
rightButton?: Snippet;
|
|
44
|
+
screen?: Snippet;
|
|
45
|
+
children?: Snippet;
|
|
46
|
+
onsnap?: (id: TId) => void;
|
|
47
|
+
onopen?: (id: TId) => void;
|
|
48
|
+
onclose?: () => void;
|
|
49
|
+
onbeforeclose?: (payload: SheetEventMap["before-close"]) => void;
|
|
50
|
+
onopened?: (id: TId) => void;
|
|
51
|
+
onclosed?: () => void;
|
|
52
|
+
ondragstart?: () => void;
|
|
53
|
+
ondragend?: () => void;
|
|
54
|
+
onchange?: (state: EngineState & { activeId: TId }) => void;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
let {
|
|
58
|
+
snapPoints,
|
|
59
|
+
allowed,
|
|
60
|
+
initial,
|
|
61
|
+
mode = "bottom",
|
|
62
|
+
animation = "spring",
|
|
63
|
+
spring,
|
|
64
|
+
focusTrap = false,
|
|
65
|
+
closeOnEscape = true,
|
|
66
|
+
closeOnBack = false,
|
|
67
|
+
lockBodyScroll = true,
|
|
68
|
+
rubberBand = true,
|
|
69
|
+
backdropRange,
|
|
70
|
+
backdrop = true,
|
|
71
|
+
closeOnBackdrop = true,
|
|
72
|
+
persistent = false,
|
|
73
|
+
disableClose = false,
|
|
74
|
+
disableDrag = false,
|
|
75
|
+
closeOnRouteChange = false,
|
|
76
|
+
radius,
|
|
77
|
+
maxHeight,
|
|
78
|
+
backdropColor,
|
|
79
|
+
backdropOpacity,
|
|
80
|
+
open: openProp = $bindable(),
|
|
81
|
+
snap = $bindable(),
|
|
82
|
+
returnFocusTo,
|
|
83
|
+
ariaLabel = "Bottom sheet",
|
|
84
|
+
header,
|
|
85
|
+
leftButton,
|
|
86
|
+
rightButton,
|
|
87
|
+
screen,
|
|
88
|
+
children,
|
|
89
|
+
onsnap,
|
|
90
|
+
onopen,
|
|
91
|
+
onclose,
|
|
92
|
+
onbeforeclose,
|
|
93
|
+
onopened,
|
|
94
|
+
onclosed,
|
|
95
|
+
ondragstart,
|
|
96
|
+
ondragend,
|
|
97
|
+
onchange,
|
|
98
|
+
}: Props = $props();
|
|
99
|
+
|
|
100
|
+
let sheetEl: HTMLElement | undefined = $state();
|
|
101
|
+
let handleEl: HTMLElement | undefined = $state();
|
|
102
|
+
let contentEl: HTMLElement | undefined = $state();
|
|
103
|
+
let backdropEl: HTMLElement | undefined = $state();
|
|
104
|
+
let screenEl: HTMLElement | undefined = $state();
|
|
105
|
+
|
|
106
|
+
let viewState: EngineState & { activeId: TId } = $state({
|
|
107
|
+
size: 0,
|
|
108
|
+
activeId: untrack(
|
|
109
|
+
() => (initial ?? snapPoints[0]?.id ?? "default") as TId,
|
|
110
|
+
),
|
|
111
|
+
isDragging: false,
|
|
112
|
+
isAnimating: false,
|
|
113
|
+
progress: 0,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
let engine: BottomSheetEngine | null = null;
|
|
117
|
+
|
|
118
|
+
onMount(() => {
|
|
119
|
+
if (!sheetEl) return;
|
|
120
|
+
engine = new BottomSheetEngine({
|
|
121
|
+
element: sheetEl,
|
|
122
|
+
handle: handleEl,
|
|
123
|
+
scrollContainer: contentEl,
|
|
124
|
+
backdrop: backdropEl,
|
|
125
|
+
scrim: screenEl,
|
|
126
|
+
mode,
|
|
127
|
+
snapPoints,
|
|
128
|
+
allowed,
|
|
129
|
+
initial,
|
|
130
|
+
animation,
|
|
131
|
+
spring,
|
|
132
|
+
focusTrap,
|
|
133
|
+
closeOnEscape,
|
|
134
|
+
closeOnBack,
|
|
135
|
+
lockBodyScroll,
|
|
136
|
+
rubberBand,
|
|
137
|
+
backdropRange,
|
|
138
|
+
persistent,
|
|
139
|
+
disableClose,
|
|
140
|
+
disableDrag,
|
|
141
|
+
closeOnRouteChange,
|
|
142
|
+
radius,
|
|
143
|
+
maxHeight,
|
|
144
|
+
returnFocusTo,
|
|
145
|
+
});
|
|
146
|
+
const sync = () => {
|
|
147
|
+
if (!engine) return;
|
|
148
|
+
viewState = { ...engine.state } as EngineState & { activeId: TId };
|
|
149
|
+
onchange?.(viewState);
|
|
150
|
+
};
|
|
151
|
+
sync();
|
|
152
|
+
if (backdropColor !== undefined) engine.setScrimColor(backdropColor);
|
|
153
|
+
if (backdropOpacity !== undefined)
|
|
154
|
+
engine.setBackdropRange([0, backdropOpacity]);
|
|
155
|
+
engine.on("snap", payload => {
|
|
156
|
+
sync();
|
|
157
|
+
snap = payload.id as TId;
|
|
158
|
+
onsnap?.(payload.id as TId);
|
|
159
|
+
});
|
|
160
|
+
engine.on("open", payload => {
|
|
161
|
+
openProp = true;
|
|
162
|
+
onopen?.(payload.id as TId);
|
|
163
|
+
});
|
|
164
|
+
engine.on("close", () => {
|
|
165
|
+
openProp = false;
|
|
166
|
+
onclose?.();
|
|
167
|
+
});
|
|
168
|
+
engine.on("before-close", payload => onbeforeclose?.(payload));
|
|
169
|
+
engine.on("opened", payload => onopened?.(payload.id as TId));
|
|
170
|
+
engine.on("closed", () => onclosed?.());
|
|
171
|
+
engine.on("dragstart", () => {
|
|
172
|
+
sync();
|
|
173
|
+
ondragstart?.();
|
|
174
|
+
});
|
|
175
|
+
engine.on("dragend", () => {
|
|
176
|
+
sync();
|
|
177
|
+
ondragend?.();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
onDestroy(() => {
|
|
182
|
+
engine?.destroy();
|
|
183
|
+
engine = null;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
export const snapTo = (id: TId) =>
|
|
187
|
+
engine?.snapTo(id) ?? Promise.resolve();
|
|
188
|
+
export const open = (id?: TId) =>
|
|
189
|
+
engine?.open(id) ?? Promise.resolve();
|
|
190
|
+
export const close = () => engine?.close() ?? Promise.resolve();
|
|
191
|
+
export const expand = () => engine?.expand() ?? Promise.resolve();
|
|
192
|
+
export const collapse = () => engine?.collapse() ?? Promise.resolve();
|
|
193
|
+
export const isTop = (): boolean => engine?.isTop() ?? false;
|
|
194
|
+
export const depth = (): number => engine?.depth() ?? 0;
|
|
195
|
+
export const setAllowed = (ids: TId[], snap?: TId) =>
|
|
196
|
+
engine?.setAllowed(ids as unknown as string[], snap);
|
|
197
|
+
export const setSnapPoints = (
|
|
198
|
+
points: EngineOptions["snapPoints"],
|
|
199
|
+
nextAllowed?: string[],
|
|
200
|
+
) => engine?.setSnapPoints(points, nextAllowed);
|
|
201
|
+
export const setScrim = (opts: ScrimUpdate) => engine?.setScrim(opts);
|
|
202
|
+
export const setScrimOverlay = (opts: ScrimOverlayOptions): (() => void) =>
|
|
203
|
+
engine?.setScrimOverlay(opts) ?? (() => {});
|
|
204
|
+
export const setRadius = (r: string | number) => engine?.setRadius(r);
|
|
205
|
+
export const setMaxHeight = (h: string | number) => engine?.setMaxHeight(h);
|
|
206
|
+
export const canDismiss = (): boolean => engine?.canDismiss() ?? false;
|
|
207
|
+
export const getState = (): EngineState & { activeId: TId } =>
|
|
208
|
+
(engine?.state ?? viewState) as EngineState & { activeId: TId };
|
|
209
|
+
export const getEngine = (): BottomSheetEngine | null => engine;
|
|
210
|
+
|
|
211
|
+
$effect(() => {
|
|
212
|
+
if (radius !== undefined) engine?.setRadius(radius);
|
|
213
|
+
});
|
|
214
|
+
$effect(() => {
|
|
215
|
+
if (maxHeight !== undefined) engine?.setMaxHeight(maxHeight);
|
|
216
|
+
});
|
|
217
|
+
$effect(() => {
|
|
218
|
+
if (backdropColor !== undefined) engine?.setScrimColor(backdropColor);
|
|
219
|
+
});
|
|
220
|
+
$effect(() => {
|
|
221
|
+
if (backdropOpacity !== undefined)
|
|
222
|
+
engine?.setBackdropRange([0, backdropOpacity]);
|
|
223
|
+
});
|
|
224
|
+
$effect(() => {
|
|
225
|
+
if (snap === undefined) return;
|
|
226
|
+
const next = snap;
|
|
227
|
+
if (!engine || untrack(() => viewState.activeId) === next) return;
|
|
228
|
+
void engine.snapTo(next);
|
|
229
|
+
});
|
|
230
|
+
$effect(() => {
|
|
231
|
+
if (openProp === undefined) return;
|
|
232
|
+
const next = openProp;
|
|
233
|
+
if (!engine) return;
|
|
234
|
+
const isOpen = untrack(() => viewState.size) > 0;
|
|
235
|
+
if (next && !isOpen) void engine.open();
|
|
236
|
+
else if (!next && isOpen) void engine.close();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
let allowedIds = $derived(
|
|
240
|
+
allowed && allowed.length > 0
|
|
241
|
+
? allowed
|
|
242
|
+
: snapPoints.map(s => s.id),
|
|
243
|
+
);
|
|
244
|
+
let activeIdx = $derived(allowedIds.indexOf(viewState.activeId));
|
|
245
|
+
let isVerticalAxis = $derived(mode === "bottom" || mode === "top");
|
|
246
|
+
</script>
|
|
247
|
+
|
|
248
|
+
<div class="bs-root">
|
|
249
|
+
{#if backdrop}
|
|
250
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
251
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
252
|
+
<div
|
|
253
|
+
class="bs-backdrop"
|
|
254
|
+
bind:this={backdropEl}
|
|
255
|
+
aria-hidden="true"
|
|
256
|
+
onclick={closeOnBackdrop
|
|
257
|
+
? () => {
|
|
258
|
+
if (engine?.canDismiss()) void engine.close("backdrop");
|
|
259
|
+
}
|
|
260
|
+
: undefined}
|
|
261
|
+
></div>
|
|
262
|
+
{/if}
|
|
263
|
+
<div class="bs-screen" bind:this={screenEl}>
|
|
264
|
+
{#if screen}{@render screen()}{/if}
|
|
265
|
+
</div>
|
|
266
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
|
|
267
|
+
<section
|
|
268
|
+
class="bs-sheet"
|
|
269
|
+
bind:this={sheetEl}
|
|
270
|
+
data-mode={mode}
|
|
271
|
+
data-active={viewState.activeId}
|
|
272
|
+
role="dialog"
|
|
273
|
+
aria-modal={focusTrap ? "true" : undefined}
|
|
274
|
+
aria-label={ariaLabel}
|
|
275
|
+
>
|
|
276
|
+
{#if leftButton}
|
|
277
|
+
<div class="bs-button-slot" data-side="left">
|
|
278
|
+
{@render leftButton()}
|
|
279
|
+
</div>
|
|
280
|
+
{/if}
|
|
281
|
+
{#if rightButton}
|
|
282
|
+
<div class="bs-button-slot" data-side="right">
|
|
283
|
+
{@render rightButton()}
|
|
284
|
+
</div>
|
|
285
|
+
{/if}
|
|
286
|
+
<div
|
|
287
|
+
class="bs-handle"
|
|
288
|
+
bind:this={handleEl}
|
|
289
|
+
role="slider"
|
|
290
|
+
tabindex="0"
|
|
291
|
+
aria-label="Resize sheet"
|
|
292
|
+
aria-orientation={isVerticalAxis ? "vertical" : "horizontal"}
|
|
293
|
+
aria-valuemin={0}
|
|
294
|
+
aria-valuemax={Math.max(0, allowedIds.length - 1)}
|
|
295
|
+
aria-valuenow={Math.max(0, activeIdx)}
|
|
296
|
+
aria-valuetext={viewState.activeId}
|
|
297
|
+
>
|
|
298
|
+
{#if header}{@render header(viewState)}{/if}
|
|
299
|
+
</div>
|
|
300
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
301
|
+
<div class="bs-content" bind:this={contentEl} tabindex="0" role="region" aria-label="Sheet content">
|
|
302
|
+
{#if children}{@render children()}{/if}
|
|
303
|
+
</div>
|
|
304
|
+
<span class="bs-sr-only" role="status" aria-live="polite">
|
|
305
|
+
{viewState.activeId}
|
|
306
|
+
</span>
|
|
307
|
+
</section>
|
|
308
|
+
</div>
|