@lodev09/react-native-true-sheet 3.10.0 → 3.11.0-beta.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.
Files changed (111) hide show
  1. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +11 -4
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +10 -5
  3. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +2 -5
  4. package/lib/module/TrueSheet.js +3 -0
  5. package/lib/module/TrueSheet.js.map +1 -1
  6. package/lib/module/TrueSheet.web.js +604 -376
  7. package/lib/module/TrueSheet.web.js.map +1 -1
  8. package/lib/module/TrueSheetProvider.js.map +1 -1
  9. package/lib/module/TrueSheetProvider.web.js +141 -100
  10. package/lib/module/TrueSheetProvider.web.js.map +1 -1
  11. package/lib/module/mocks/index.js.map +1 -1
  12. package/lib/module/reanimated/ReanimatedTrueSheet.web.js.map +1 -1
  13. package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js +2 -3
  14. package/lib/module/reanimated/useReanimatedPositionChangeHandler.web.js.map +1 -1
  15. package/lib/module/web/constants.js +16 -0
  16. package/lib/module/web/constants.js.map +1 -0
  17. package/lib/module/web/vaul/README.md +9 -0
  18. package/lib/module/web/vaul/browser.js +30 -0
  19. package/lib/module/web/vaul/browser.js.map +1 -0
  20. package/lib/module/web/vaul/constants.js +14 -0
  21. package/lib/module/web/vaul/constants.js.map +1 -0
  22. package/lib/module/web/vaul/context.js +57 -0
  23. package/lib/module/web/vaul/context.js.map +1 -0
  24. package/lib/module/web/vaul/helpers.js +93 -0
  25. package/lib/module/web/vaul/helpers.js.map +1 -0
  26. package/lib/module/web/vaul/index.js +1206 -0
  27. package/lib/module/web/vaul/index.js.map +1 -0
  28. package/lib/module/web/vaul/style.css +264 -0
  29. package/lib/module/web/vaul/types.js +2 -0
  30. package/lib/module/web/vaul/types.js.map +1 -0
  31. package/lib/module/web/vaul/use-composed-refs.js +34 -0
  32. package/lib/module/web/vaul/use-composed-refs.js.map +1 -0
  33. package/lib/module/web/vaul/use-controllable-state.js +54 -0
  34. package/lib/module/web/vaul/use-controllable-state.js.map +1 -0
  35. package/lib/module/web/vaul/use-position-fixed.js +123 -0
  36. package/lib/module/web/vaul/use-position-fixed.js.map +1 -0
  37. package/lib/module/web/vaul/use-prevent-scroll.js +258 -0
  38. package/lib/module/web/vaul/use-prevent-scroll.js.map +1 -0
  39. package/lib/module/web/vaul/use-scale-background.js +57 -0
  40. package/lib/module/web/vaul/use-scale-background.js.map +1 -0
  41. package/lib/module/web/vaul/use-snap-points.js +294 -0
  42. package/lib/module/web/vaul/use-snap-points.js.map +1 -0
  43. package/lib/typescript/src/TrueSheet.d.ts +2 -2
  44. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  45. package/lib/typescript/src/TrueSheet.types.d.ts +79 -36
  46. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  47. package/lib/typescript/src/TrueSheet.web.d.ts +3 -11
  48. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  49. package/lib/typescript/src/TrueSheetProvider.d.ts +2 -2
  50. package/lib/typescript/src/TrueSheetProvider.d.ts.map +1 -1
  51. package/lib/typescript/src/TrueSheetProvider.web.d.ts +17 -14
  52. package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
  53. package/lib/typescript/src/mocks/index.d.ts +1 -2
  54. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  55. package/lib/typescript/src/navigation/types.d.ts +1 -1
  56. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  57. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts +0 -1
  58. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts.map +1 -1
  59. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts +2 -3
  60. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.web.d.ts.map +1 -1
  61. package/lib/typescript/src/web/constants.d.ts +14 -0
  62. package/lib/typescript/src/web/constants.d.ts.map +1 -0
  63. package/lib/typescript/src/web/vaul/browser.d.ts +8 -0
  64. package/lib/typescript/src/web/vaul/browser.d.ts.map +1 -0
  65. package/lib/typescript/src/web/vaul/constants.d.ts +12 -0
  66. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -0
  67. package/lib/typescript/src/web/vaul/context.d.ts +45 -0
  68. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -0
  69. package/lib/typescript/src/web/vaul/helpers.d.ts +17 -0
  70. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -0
  71. package/lib/typescript/src/web/vaul/index.d.ts +210 -0
  72. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -0
  73. package/lib/typescript/src/web/vaul/types.d.ts +7 -0
  74. package/lib/typescript/src/web/vaul/types.d.ts.map +1 -0
  75. package/lib/typescript/src/web/vaul/use-composed-refs.d.ts +14 -0
  76. package/lib/typescript/src/web/vaul/use-composed-refs.d.ts.map +1 -0
  77. package/lib/typescript/src/web/vaul/use-controllable-state.d.ts +9 -0
  78. package/lib/typescript/src/web/vaul/use-controllable-state.d.ts.map +1 -0
  79. package/lib/typescript/src/web/vaul/use-position-fixed.d.ts +19 -0
  80. package/lib/typescript/src/web/vaul/use-position-fixed.d.ts.map +1 -0
  81. package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts +18 -0
  82. package/lib/typescript/src/web/vaul/use-prevent-scroll.d.ts.map +1 -0
  83. package/lib/typescript/src/web/vaul/use-scale-background.d.ts +2 -0
  84. package/lib/typescript/src/web/vaul/use-scale-background.d.ts.map +1 -0
  85. package/lib/typescript/src/web/vaul/use-snap-points.d.ts +37 -0
  86. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -0
  87. package/package.json +4 -4
  88. package/src/TrueSheet.tsx +9 -1
  89. package/src/TrueSheet.types.ts +88 -38
  90. package/src/TrueSheet.web.tsx +675 -450
  91. package/src/TrueSheetProvider.tsx +2 -4
  92. package/src/TrueSheetProvider.web.tsx +193 -125
  93. package/src/mocks/index.ts +1 -2
  94. package/src/navigation/types.ts +0 -1
  95. package/src/reanimated/ReanimatedTrueSheet.web.tsx +0 -1
  96. package/src/reanimated/useReanimatedPositionChangeHandler.web.ts +2 -3
  97. package/src/web/constants.ts +13 -0
  98. package/src/web/vaul/README.md +9 -0
  99. package/src/web/vaul/browser.ts +38 -0
  100. package/src/web/vaul/constants.ts +18 -0
  101. package/src/web/vaul/context.ts +87 -0
  102. package/src/web/vaul/helpers.ts +124 -0
  103. package/src/web/vaul/index.tsx +1590 -0
  104. package/src/web/vaul/style.css +264 -0
  105. package/src/web/vaul/types.ts +7 -0
  106. package/src/web/vaul/use-composed-refs.ts +35 -0
  107. package/src/web/vaul/use-controllable-state.ts +66 -0
  108. package/src/web/vaul/use-position-fixed.ts +147 -0
  109. package/src/web/vaul/use-prevent-scroll.ts +309 -0
  110. package/src/web/vaul/use-scale-background.ts +64 -0
  111. package/src/web/vaul/use-snap-points.ts +433 -0
@@ -1,11 +1,9 @@
1
1
  import type { ReactNode } from 'react';
2
2
 
3
3
  import { TrueSheet } from './TrueSheet';
4
+ import type { TrueSheetStaticMethods } from './TrueSheet.types';
4
5
 
5
- export type TrueSheetStaticMethods = Pick<
6
- typeof TrueSheet,
7
- 'present' | 'dismiss' | 'dismissStack' | 'resize' | 'dismissAll'
8
- >;
6
+ export type { TrueSheetStaticMethods };
9
7
 
10
8
  export interface TrueSheetProviderProps {
11
9
  children: ReactNode;
@@ -1,143 +1,211 @@
1
- import { createContext, useContext, useRef, type ReactNode, type RefObject } from 'react';
2
- import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
3
-
4
- import type { TrueSheetStaticMethods } from './TrueSheetProvider';
5
- import type { TrueSheet } from './TrueSheet';
6
-
7
- export type TrueSheetRefMethods = Pick<
8
- TrueSheet,
9
- 'present' | 'dismiss' | 'resize' | 'dismissStack'
10
- >;
11
-
12
- interface BottomSheetContextValue extends TrueSheetStaticMethods {
13
- register: (name: string, methods: RefObject<TrueSheetRefMethods>) => void;
14
- unregister: (name: string) => void;
15
- pushToStack: (name: string) => void;
16
- removeFromStack: (name: string) => void;
17
- getSheetsAbove: (name: string) => string[];
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useLayoutEffect,
7
+ useMemo,
8
+ useRef,
9
+ useSyncExternalStore,
10
+ type ReactNode,
11
+ type RefObject,
12
+ } from 'react';
13
+
14
+ import type { TrueSheetMethods, TrueSheetStaticMethods } from './TrueSheet.types';
15
+
16
+ type SheetRef = RefObject<TrueSheetMethods | null>;
17
+ type NodeRef = RefObject<HTMLDivElement | null>;
18
+
19
+ interface StackEntry {
20
+ ref: SheetRef;
21
+ nodeRef: NodeRef;
18
22
  }
19
23
 
20
- export const BottomSheetContext = createContext<BottomSheetContextValue | null>(null);
24
+ interface SheetContextValue {
25
+ registerByName: (name: string, ref: SheetRef) => () => void;
26
+ resolveByName: (name: string) => TrueSheetMethods;
27
+ pushOpen: (entry: StackEntry) => void;
28
+ popOpen: (entry: StackEntry) => void;
29
+ subscribeStack: (listener: () => void) => () => void;
30
+ getStackSnapshot: () => readonly StackEntry[];
31
+ portalContainer: HTMLElement | null;
32
+ }
33
+
34
+ const SheetContext = createContext<SheetContextValue | null>(null);
35
+
36
+ const NO_PROVIDER_ERROR =
37
+ 'TrueSheet: useTrueSheet() requires a <TrueSheetProvider> ancestor on web.';
38
+
39
+ const EMPTY_STACK: readonly StackEntry[] = Object.freeze([]);
21
40
 
22
41
  export interface TrueSheetProviderProps {
23
42
  children: ReactNode;
24
43
  }
25
44
 
26
- /**
27
- * Provider for TrueSheet on web.
28
- * Required to wrap your app for sheet management via useTrueSheet hook.
29
- */
30
45
  export function TrueSheetProvider({ children }: TrueSheetProviderProps) {
31
- const sheetsRef = useRef<Map<string, RefObject<TrueSheetRefMethods>>>(new Map());
32
- const presentedStackRef = useRef<string[]>([]);
46
+ const namedSheetsRef = useRef<Map<string, SheetRef>>(new Map());
47
+ const stackRef = useRef<readonly StackEntry[]>(EMPTY_STACK);
48
+ const listenersRef = useRef<Set<() => void>>(new Set());
49
+
50
+ // Stable portal container created once. Attached to an anchor div rendered
51
+ // in this provider's tree so the sheets unmount cleanly with the provider
52
+ // (e.g., when a screen-scoped provider unmounts on navigation).
53
+ const portalContainer = useMemo<HTMLDivElement | null>(
54
+ () => (typeof document !== 'undefined' ? document.createElement('div') : null),
55
+ []
56
+ );
57
+ const anchorRef = useRef<HTMLDivElement | null>(null);
58
+
59
+ useLayoutEffect(() => {
60
+ const anchor = anchorRef.current;
61
+ if (!anchor || !portalContainer) return;
62
+ anchor.appendChild(portalContainer);
63
+ return () => {
64
+ if (portalContainer.parentNode) {
65
+ portalContainer.parentNode.removeChild(portalContainer);
66
+ }
67
+ };
68
+ }, [portalContainer]);
69
+
70
+ const value = useMemo<SheetContextValue>(() => {
71
+ const notify = () => listenersRef.current.forEach((listener) => listener());
72
+
73
+ return {
74
+ registerByName: (name, ref) => {
75
+ namedSheetsRef.current.set(name, ref);
76
+ return () => {
77
+ if (namedSheetsRef.current.get(name) === ref) {
78
+ namedSheetsRef.current.delete(name);
79
+ }
80
+ };
81
+ },
82
+ resolveByName: (name) => {
83
+ const ref = namedSheetsRef.current.get(name);
84
+ const methods = ref?.current;
85
+ if (!methods) {
86
+ throw new Error(`TrueSheet: no sheet registered with name "${name}"`);
87
+ }
88
+ return methods;
89
+ },
90
+ pushOpen: (entry) => {
91
+ if (stackRef.current.some((e) => e.ref === entry.ref)) return;
92
+ stackRef.current = [...stackRef.current, entry];
93
+ notify();
94
+ },
95
+ popOpen: (entry) => {
96
+ const idx = stackRef.current.findIndex((e) => e.ref === entry.ref);
97
+ if (idx < 0) return;
98
+ stackRef.current = [...stackRef.current.slice(0, idx), ...stackRef.current.slice(idx + 1)];
99
+ notify();
100
+ },
101
+ subscribeStack: (listener) => {
102
+ listenersRef.current.add(listener);
103
+ return () => {
104
+ listenersRef.current.delete(listener);
105
+ };
106
+ },
107
+ getStackSnapshot: () => stackRef.current,
108
+ portalContainer,
109
+ };
110
+ }, [portalContainer]);
33
111
 
34
- const register = (name: string, methods: RefObject<TrueSheetRefMethods>) => {
35
- sheetsRef.current.set(name, methods);
36
- };
112
+ return (
113
+ <SheetContext.Provider value={value}>
114
+ {children}
115
+ <div ref={anchorRef} />
116
+ </SheetContext.Provider>
117
+ );
118
+ }
37
119
 
38
- const unregister = (name: string) => {
39
- sheetsRef.current.delete(name);
40
- };
120
+ export function usePortalContainer(): HTMLElement | null {
121
+ const ctx = useContext(SheetContext);
122
+ return ctx?.portalContainer ?? null;
123
+ }
41
124
 
42
- const pushToStack = (name: string) => {
43
- const index = presentedStackRef.current.indexOf(name);
44
- if (index >= 0) {
45
- presentedStackRef.current.splice(index, 1);
125
+ export function useTrueSheet(): TrueSheetStaticMethods {
126
+ const ctx = useContext(SheetContext);
127
+
128
+ return useMemo<TrueSheetStaticMethods>(() => {
129
+ if (!ctx) {
130
+ const reject = async (): Promise<never> => {
131
+ throw new Error(NO_PROVIDER_ERROR);
132
+ };
133
+ return {
134
+ present: reject,
135
+ resize: reject,
136
+ dismiss: reject,
137
+ dismissStack: reject,
138
+ dismissAll: reject,
139
+ };
46
140
  }
47
- presentedStackRef.current.push(name);
48
- };
49
141
 
50
- const removeFromStack = (name: string) => {
51
- const index = presentedStackRef.current.indexOf(name);
52
- if (index >= 0) {
53
- presentedStackRef.current.splice(index, 1);
54
- }
55
- };
56
-
57
- /**
58
- * Returns all sheets presented on top of the given sheet.
59
- * Returns them in reverse order (top-most first) for proper dismissal.
60
- */
61
- const getSheetsAbove = (name: string): string[] => {
62
- const index = presentedStackRef.current.indexOf(name);
63
- if (index < 0 || index >= presentedStackRef.current.length - 1) return [];
64
- return presentedStackRef.current.slice(index + 1).reverse();
65
- };
66
-
67
- const present = async (name: string, index: number = 0) => {
68
- const sheet = sheetsRef.current.get(name);
69
- if (!sheet?.current) {
70
- console.warn(`TrueSheet: Could not find sheet with name "${name}"`);
71
- return;
72
- }
73
- return sheet.current.present(index);
74
- };
75
-
76
- const dismiss = async (name: string) => {
77
- const sheet = sheetsRef.current.get(name);
78
- if (!sheet?.current) {
79
- console.warn(`TrueSheet: Could not find sheet with name "${name}"`);
80
- return;
81
- }
82
- return sheet.current.dismiss();
83
- };
84
-
85
- const dismissStack = async (name: string) => {
86
- const sheet = sheetsRef.current.get(name);
87
- if (!sheet?.current) {
88
- console.warn(`TrueSheet: Could not find sheet with name "${name}"`);
89
- return;
90
- }
91
- return sheet.current.dismissStack();
92
- };
93
-
94
- const resize = async (name: string, index: number) => {
95
- const sheet = sheetsRef.current.get(name);
96
- if (!sheet?.current) {
97
- console.warn(`TrueSheet: Could not find sheet with name "${name}"`);
98
- return;
99
- }
100
- return sheet.current.resize(index);
101
- };
142
+ return {
143
+ present: (name, index, animated) => ctx.resolveByName(name).present(index, animated),
144
+ resize: (name, index) => ctx.resolveByName(name).resize(index),
145
+ dismiss: (name, animated) => ctx.resolveByName(name).dismiss(animated),
146
+ dismissStack: (name, animated) => ctx.resolveByName(name).dismissStack(animated),
147
+ dismissAll: async (animated) => {
148
+ const stack = ctx.getStackSnapshot();
149
+ await Promise.all(
150
+ [...stack].reverse().map((entry) => entry.ref.current?.dismiss(animated))
151
+ );
152
+ },
153
+ };
154
+ }, [ctx]);
155
+ }
102
156
 
103
- const dismissAll = async () => {
104
- const rootSheet = presentedStackRef.current[0];
105
- if (!rootSheet) return;
106
- return dismiss(rootSheet);
107
- };
157
+ export function useRegisterSheet(name: string | undefined, ref: SheetRef): void {
158
+ const ctx = useContext(SheetContext);
159
+ useEffect(() => {
160
+ if (!ctx || !name) return;
161
+ return ctx.registerByName(name, ref);
162
+ }, [ctx, name, ref]);
163
+ }
108
164
 
109
- return (
110
- <BottomSheetContext.Provider
111
- value={{
112
- register,
113
- unregister,
114
- pushToStack,
115
- removeFromStack,
116
- getSheetsAbove,
117
- present,
118
- dismiss,
119
- dismissStack,
120
- resize,
121
- dismissAll,
122
- }}
123
- >
124
- <BottomSheetModalProvider>{children}</BottomSheetModalProvider>
125
- </BottomSheetContext.Provider>
165
+ /**
166
+ * Registers the sheet in the open stack while `isOpen` is true and returns
167
+ * live data used by each sheet to render stacked visuals and dismiss children.
168
+ */
169
+ export function useSheetStack(ref: SheetRef, nodeRef: NodeRef, isOpen: boolean) {
170
+ const ctx = useContext(SheetContext);
171
+
172
+ const entry = useMemo<StackEntry>(() => ({ ref, nodeRef }), [ref, nodeRef]);
173
+
174
+ useEffect(() => {
175
+ if (!ctx || !isOpen) return;
176
+ ctx.pushOpen(entry);
177
+ return () => ctx.popOpen(entry);
178
+ }, [ctx, entry, isOpen]);
179
+
180
+ const subscribe = useCallback(
181
+ (listener: () => void) => {
182
+ if (!ctx) return () => {};
183
+ return ctx.subscribeStack(listener);
184
+ },
185
+ [ctx]
126
186
  );
127
- }
128
187
 
129
- export function useTrueSheet(): TrueSheetStaticMethods {
130
- const context = useContext(BottomSheetContext);
131
-
132
- if (!context) {
133
- throw new Error('useTrueSheet must be used within a TrueSheetProvider');
134
- }
135
-
136
- return {
137
- present: context.present,
138
- dismiss: context.dismiss,
139
- dismissStack: context.dismissStack,
140
- resize: context.resize,
141
- dismissAll: context.dismissAll,
142
- };
188
+ const getSnapshot = useCallback(() => (ctx ? ctx.getStackSnapshot() : EMPTY_STACK), [ctx]);
189
+
190
+ const stack = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
191
+ const stackIndex = stack.findIndex((e) => e.ref === ref);
192
+ const isNested = stackIndex > 0;
193
+ const descendants = useMemo<readonly StackEntry[]>(
194
+ () => (stackIndex < 0 ? EMPTY_STACK : stack.slice(stackIndex + 1)),
195
+ [stack, stackIndex]
196
+ );
197
+
198
+ const dismissAbove = useCallback(
199
+ async (animated?: boolean) => {
200
+ if (!ctx) return;
201
+ const snapshot = ctx.getStackSnapshot();
202
+ const idx = snapshot.findIndex((e) => e.ref === ref);
203
+ if (idx < 0) return;
204
+ const above = snapshot.slice(idx + 1);
205
+ await Promise.all([...above].reverse().map((e) => e.ref.current?.dismiss(animated)));
206
+ },
207
+ [ctx, ref]
208
+ );
209
+
210
+ return { descendants, isNested, dismissAbove };
143
211
  }
@@ -1,8 +1,7 @@
1
1
  import React, { createElement, isValidElement, type ReactNode } from 'react';
2
2
  import { View } from 'react-native';
3
3
 
4
- import type { TrueSheetProps } from '../TrueSheet.types';
5
- import type { TrueSheetStaticMethods } from '../TrueSheetProvider';
4
+ import type { TrueSheetProps, TrueSheetStaticMethods } from '../TrueSheet.types';
6
5
 
7
6
  interface TrueSheetState {
8
7
  shouldRenderNativeView: boolean;
@@ -148,7 +148,6 @@ export type TrueSheetNavigationSheetProps = Pick<
148
148
  | 'elevation'
149
149
  | 'detached'
150
150
  | 'detachedOffset'
151
- | 'stackBehavior'
152
151
  >;
153
152
 
154
153
  /**
@@ -8,7 +8,6 @@ import { useReanimatedTrueSheet } from './ReanimatedTrueSheetProvider';
8
8
  interface ReanimatedTrueSheetProps extends TrueSheetProps {
9
9
  /**
10
10
  * Callback for position changes.
11
- * On web, this is called with the position data from @gorhom/bottom-sheet.
12
11
  *
13
12
  * @see {@link TrueSheetProps.onPositionChange}
14
13
  */
@@ -9,9 +9,8 @@ type PositionChangeHandler = (
9
9
  /**
10
10
  * Web implementation of useReanimatedPositionChangeHandler.
11
11
  *
12
- * On web, this returns a simple callback wrapper since @gorhom/bottom-sheet
13
- * already provides animated position values. The worklet directive is ignored
14
- * on web as there's no native UI thread.
12
+ * Returns a simple callback wrapper. The worklet directive is ignored on web
13
+ * since there's no native UI thread.
15
14
  *
16
15
  * @param handler - The position change handler function
17
16
  * @param _dependencies - Unused on web, kept for API compatibility
@@ -0,0 +1,13 @@
1
+ export const DEFAULT_CORNER_RADIUS = 16;
2
+ export const DEFAULT_MAX_WIDTH = 640;
3
+ export const DEFAULT_FORM_SHEET_WIDTH = 580;
4
+ export const DEFAULT_FORM_SHEET_HEIGHT_RATIO = 0.5;
5
+ export const COLOR_SURFACE_CONTAINER_LOW_LIGHT = '#F7F2FA';
6
+ export const COLOR_SURFACE_CONTAINER_LOW_DARK = '#1D1B20';
7
+ export const DEFAULT_ANCHOR_OFFSET = 16;
8
+ export const DEFAULT_DETACHED_OFFSET = 16;
9
+ export const DEFAULT_GRABBER_COLOR_LIGHT = 'rgba(0, 0, 0, 0.3)';
10
+ export const DEFAULT_GRABBER_COLOR_DARK = 'rgba(255, 255, 255, 0.3)';
11
+ export const DEFAULT_GRABBER_WIDTH = 32;
12
+ export const DEFAULT_GRABBER_HEIGHT = 4;
13
+ export const DEFAULT_GRABBER_TOP_MARGIN = 16;
@@ -0,0 +1,9 @@
1
+ # vaul
2
+
3
+ Originally forked from [vaul](https://github.com/emilkowalski/vaul) by Emil Kowalski at `v1.1.2`. Upstream is unmaintained; maintained here as part of truesheet's web renderer.
4
+
5
+ Only external runtime dep: `@radix-ui/react-dialog`.
6
+
7
+ ## License
8
+
9
+ vaul is MIT-licensed. See the [upstream LICENSE](https://github.com/emilkowalski/vaul/blob/main/LICENSE.md). Original copyright remains with Emil Kowalski.
@@ -0,0 +1,38 @@
1
+ export function isMobileFirefox(): boolean | undefined {
2
+ const userAgent = navigator.userAgent;
3
+ return (
4
+ typeof window !== 'undefined' &&
5
+ ((/Firefox/.test(userAgent) && /Mobile/.test(userAgent)) || // Android Firefox
6
+ /FxiOS/.test(userAgent)) // iOS Firefox
7
+ );
8
+ }
9
+
10
+ export function isMac(): boolean | undefined {
11
+ return testPlatform(/^Mac/);
12
+ }
13
+
14
+ export function isIPhone(): boolean | undefined {
15
+ return testPlatform(/^iPhone/);
16
+ }
17
+
18
+ export function isSafari(): boolean | undefined {
19
+ return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
20
+ }
21
+
22
+ export function isIPad(): boolean | undefined {
23
+ return (
24
+ testPlatform(/^iPad/) ||
25
+ // iPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
26
+ (isMac() && navigator.maxTouchPoints > 1)
27
+ );
28
+ }
29
+
30
+ export function isIOS(): boolean | undefined {
31
+ return isIPhone() || isIPad();
32
+ }
33
+
34
+ export function testPlatform(re: RegExp): boolean | undefined {
35
+ return typeof window !== 'undefined' && window.navigator != null
36
+ ? re.test(window.navigator.platform)
37
+ : undefined;
38
+ }
@@ -0,0 +1,18 @@
1
+ export const TRANSITIONS = {
2
+ DURATION: 0.5,
3
+ EASE: [0.32, 0.72, 0, 1],
4
+ };
5
+
6
+ export const VELOCITY_THRESHOLD = 0.4;
7
+
8
+ export const CLOSE_THRESHOLD = 0.25;
9
+
10
+ export const SCROLL_LOCK_TIMEOUT = 100;
11
+
12
+ export const BORDER_RADIUS = 8;
13
+
14
+ export const NESTED_DISPLACEMENT = 16;
15
+
16
+ export const WINDOW_TOP_OFFSET = 26;
17
+
18
+ export const DRAG_CLASS = 'vaul-dragging';
@@ -0,0 +1,87 @@
1
+ import React from 'react';
2
+ import type { DrawerDirection } from './types';
3
+
4
+ interface DrawerContextValue {
5
+ drawerRef: React.RefObject<HTMLDivElement | null>;
6
+ overlayRef: React.RefObject<HTMLDivElement | null>;
7
+ onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
8
+ onRelease: (event: React.PointerEvent<HTMLDivElement> | null) => void;
9
+ onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
10
+ onNestedDrag: (event: React.PointerEvent<HTMLDivElement>, percentageDragged: number) => void;
11
+ onNestedOpenChange: (o: boolean) => void;
12
+ onNestedRelease: (event: React.PointerEvent<HTMLDivElement>, open: boolean) => void;
13
+ dismissible: boolean;
14
+ isOpen: boolean;
15
+ isDragging: boolean;
16
+ keyboardIsOpen: React.MutableRefObject<boolean>;
17
+ snapPointsOffset: number[] | null;
18
+ snapPoints?: (number | string)[] | null;
19
+ activeSnapPointIndex?: number | null;
20
+ fadeFromIndex?: number;
21
+ modal: boolean;
22
+ shouldFade: boolean;
23
+ activeSnapPoint?: number | string | null;
24
+ setActiveSnapPoint: (o: number | string | null) => void;
25
+ closeDrawer: () => void;
26
+ openProp?: boolean;
27
+ onOpenChange?: (o: boolean) => void;
28
+ direction: DrawerDirection;
29
+ shouldScaleBackground: boolean;
30
+ setBackgroundColorOnScale: boolean;
31
+ noBodyStyles: boolean;
32
+ handleOnly?: boolean;
33
+ container?: HTMLElement | null;
34
+ autoFocus?: boolean;
35
+ shouldAnimate?: React.RefObject<boolean>;
36
+ onPositionChangeRef: React.RefObject<((position: number) => void) | undefined>;
37
+ setContentHeight: (height: number) => void;
38
+ detached: boolean;
39
+ detachedOffset: number;
40
+ detachedRadius: number;
41
+ detachedWrapperStyle?: React.CSSProperties;
42
+ }
43
+
44
+ export const DrawerContext = React.createContext<DrawerContextValue>({
45
+ drawerRef: { current: null },
46
+ overlayRef: { current: null },
47
+ onPress: () => {},
48
+ onRelease: () => {},
49
+ onDrag: () => {},
50
+ onNestedDrag: () => {},
51
+ onNestedOpenChange: () => {},
52
+ onNestedRelease: () => {},
53
+ openProp: undefined,
54
+ dismissible: false,
55
+ isOpen: false,
56
+ isDragging: false,
57
+ keyboardIsOpen: { current: false },
58
+ snapPointsOffset: null,
59
+ snapPoints: null,
60
+ handleOnly: false,
61
+ modal: false,
62
+ shouldFade: false,
63
+ activeSnapPoint: null,
64
+ onOpenChange: () => {},
65
+ setActiveSnapPoint: () => {},
66
+ closeDrawer: () => {},
67
+ direction: 'bottom',
68
+ shouldAnimate: { current: true },
69
+ shouldScaleBackground: false,
70
+ setBackgroundColorOnScale: true,
71
+ noBodyStyles: false,
72
+ container: null,
73
+ autoFocus: false,
74
+ onPositionChangeRef: { current: undefined },
75
+ setContentHeight: () => {},
76
+ detached: false,
77
+ detachedOffset: 0,
78
+ detachedRadius: 0,
79
+ });
80
+
81
+ export const useDrawerContext = () => {
82
+ const context = React.useContext(DrawerContext);
83
+ if (!context) {
84
+ throw new Error('useDrawerContext must be used within a Drawer.Root');
85
+ }
86
+ return context;
87
+ };