@niibase/bottom-sheet-manager 1.2.0 → 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/README.md +414 -69
- package/lib/commonjs/events.js +100 -15
- package/lib/commonjs/events.js.map +1 -1
- package/lib/commonjs/index.js +14 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/manager.js +153 -35
- package/lib/commonjs/manager.js.map +1 -1
- package/lib/commonjs/provider.js +92 -54
- package/lib/commonjs/provider.js.map +1 -1
- package/lib/commonjs/router/index.js +80 -21
- package/lib/commonjs/router/index.js.map +1 -1
- package/lib/commonjs/router/router.js +137 -12
- package/lib/commonjs/router/router.js.map +1 -1
- package/lib/commonjs/router/view.js +93 -126
- package/lib/commonjs/router/view.js.map +1 -1
- package/lib/commonjs/sheet.js +122 -98
- package/lib/commonjs/sheet.js.map +1 -1
- package/lib/module/events.js +100 -15
- package/lib/module/events.js.map +1 -1
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/manager.js +154 -35
- package/lib/module/manager.js.map +1 -1
- package/lib/module/provider.js +87 -50
- package/lib/module/provider.js.map +1 -1
- package/lib/module/router/index.js +66 -19
- package/lib/module/router/index.js.map +1 -1
- package/lib/module/router/router.js +135 -11
- package/lib/module/router/router.js.map +1 -1
- package/lib/module/router/view.js +92 -126
- package/lib/module/router/view.js.map +1 -1
- package/lib/module/sheet.js +124 -100
- package/lib/module/sheet.js.map +1 -1
- package/lib/typescript/events.d.ts +46 -12
- package/lib/typescript/events.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/manager.d.ts +73 -7
- package/lib/typescript/manager.d.ts.map +1 -1
- package/lib/typescript/provider.d.ts +22 -16
- package/lib/typescript/provider.d.ts.map +1 -1
- package/lib/typescript/router/index.d.ts +47 -17
- package/lib/typescript/router/index.d.ts.map +1 -1
- package/lib/typescript/router/router.d.ts +44 -5
- package/lib/typescript/router/router.d.ts.map +1 -1
- package/lib/typescript/router/types.d.ts +142 -32
- package/lib/typescript/router/types.d.ts.map +1 -1
- package/lib/typescript/router/view.d.ts +3 -3
- package/lib/typescript/router/view.d.ts.map +1 -1
- package/lib/typescript/sheet.d.ts +1 -1
- package/lib/typescript/sheet.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +52 -21
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +14 -15
- package/src/events.ts +118 -27
- package/src/index.ts +2 -1
- package/src/manager.ts +209 -42
- package/src/provider.tsx +144 -71
- package/src/router/index.tsx +77 -33
- package/src/router/router.ts +188 -15
- package/src/router/types.ts +172 -57
- package/src/router/view.tsx +111 -213
- package/src/sheet.tsx +192 -124
- package/src/types.ts +51 -24
package/src/manager.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { BottomSheetInstance, Sheets, StackBehavior } from "./types";
|
|
4
4
|
import { providerRegistryStack, sheetsRegistry } from "./provider";
|
|
5
|
-
import {
|
|
5
|
+
import { eventManager } from "./events";
|
|
6
6
|
|
|
7
7
|
// Array of all the ids of Sheets currently rendered in the app.
|
|
8
8
|
const ids: string[] = [];
|
|
@@ -12,23 +12,39 @@ const DEFAULT_Z_INDEX = 999;
|
|
|
12
12
|
|
|
13
13
|
const makeKey = (id: string, context: string) => `${id}:${context}`;
|
|
14
14
|
|
|
15
|
+
interface HistoryEntry {
|
|
16
|
+
id: string;
|
|
17
|
+
context: string;
|
|
18
|
+
behavior: StackBehavior;
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
export const PrivateManager = {
|
|
16
|
-
//
|
|
17
|
-
history: [] as
|
|
22
|
+
// Stack of sheet history for restoration when sheets are closed
|
|
23
|
+
history: [] as HistoryEntry[],
|
|
18
24
|
|
|
19
25
|
context(options?: { context?: string; id?: string }) {
|
|
20
26
|
if (!options) options = {};
|
|
21
27
|
if (!options?.context) {
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
// First try to find a context where this sheet is registered
|
|
29
|
+
if (options?.id) {
|
|
30
|
+
for (const context of providerRegistryStack.slice().reverse()) {
|
|
31
|
+
if (sheetsRegistry[context]?.[options.id]) {
|
|
32
|
+
options.context = context;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fall back to the top-most auto-generated nested context
|
|
39
|
+
if (!options.context) {
|
|
40
|
+
for (const context of providerRegistryStack.slice().reverse()) {
|
|
41
|
+
if (
|
|
42
|
+
context.startsWith("$$-auto") &&
|
|
43
|
+
!context.includes(options?.id as string)
|
|
44
|
+
) {
|
|
45
|
+
options.context = context;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
32
48
|
}
|
|
33
49
|
}
|
|
34
50
|
}
|
|
@@ -42,11 +58,12 @@ export const PrivateManager = {
|
|
|
42
58
|
) => {
|
|
43
59
|
const key = makeKey(id, context);
|
|
44
60
|
refs[key] = instance;
|
|
45
|
-
keys.
|
|
61
|
+
if (!keys.includes(key)) {
|
|
62
|
+
keys.push(key);
|
|
63
|
+
}
|
|
46
64
|
},
|
|
47
65
|
|
|
48
66
|
/**
|
|
49
|
-
*
|
|
50
67
|
* Get internal ref of a sheet by the given id.
|
|
51
68
|
*
|
|
52
69
|
* @param id Id of the sheet
|
|
@@ -57,8 +74,8 @@ export const PrivateManager = {
|
|
|
57
74
|
context?: string,
|
|
58
75
|
): RefObject<BottomSheetInstance<SheetId>> => {
|
|
59
76
|
if (!context) {
|
|
60
|
-
for (
|
|
61
|
-
for (
|
|
77
|
+
for (const ctx of providerRegistryStack.slice().reverse()) {
|
|
78
|
+
for (const _id in sheetsRegistry[ctx]) {
|
|
62
79
|
if (_id === id) {
|
|
63
80
|
context = ctx;
|
|
64
81
|
break;
|
|
@@ -70,14 +87,17 @@ export const PrivateManager = {
|
|
|
70
87
|
},
|
|
71
88
|
|
|
72
89
|
add: (id: string, context: string) => {
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
const key = makeKey(id, context);
|
|
91
|
+
if (!ids.includes(key)) {
|
|
92
|
+
ids.push(key);
|
|
75
93
|
}
|
|
76
94
|
},
|
|
77
95
|
|
|
78
96
|
remove: (id: string, context: string) => {
|
|
79
|
-
|
|
80
|
-
|
|
97
|
+
const key = makeKey(id, context);
|
|
98
|
+
const index = ids.indexOf(key);
|
|
99
|
+
if (index > -1) {
|
|
100
|
+
ids.splice(index, 1);
|
|
81
101
|
}
|
|
82
102
|
},
|
|
83
103
|
|
|
@@ -87,12 +107,49 @@ export const PrivateManager = {
|
|
|
87
107
|
},
|
|
88
108
|
|
|
89
109
|
stack: () =>
|
|
90
|
-
ids.map((id) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
110
|
+
ids.map((id) => ({
|
|
111
|
+
id: id.split(":")[0],
|
|
112
|
+
context: id.split(":")?.[1] || "global",
|
|
113
|
+
})),
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get the top-most sheet in the stack
|
|
117
|
+
*/
|
|
118
|
+
topSheet: () => {
|
|
119
|
+
if (ids.length === 0) return null;
|
|
120
|
+
const topId = ids[ids.length - 1];
|
|
121
|
+
return {
|
|
122
|
+
id: topId.split(":")[0],
|
|
123
|
+
context: topId.split(":")?.[1] || "global",
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check if a sheet is currently visible
|
|
129
|
+
*/
|
|
130
|
+
isSheetVisible: (id: string, context?: string): boolean => {
|
|
131
|
+
if (context) {
|
|
132
|
+
return ids.includes(makeKey(id, context));
|
|
133
|
+
}
|
|
134
|
+
return ids.some((key) => key.startsWith(`${id}:`));
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Clear all history entries
|
|
139
|
+
*/
|
|
140
|
+
clearHistory: () => {
|
|
141
|
+
PrivateManager.history = [];
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Reset all internal state. Useful for testing or HMR.
|
|
146
|
+
*/
|
|
147
|
+
reset: () => {
|
|
148
|
+
ids.length = 0;
|
|
149
|
+
keys.length = 0;
|
|
150
|
+
for (const key in refs) delete refs[key];
|
|
151
|
+
PrivateManager.history = [];
|
|
152
|
+
},
|
|
96
153
|
};
|
|
97
154
|
|
|
98
155
|
class _SheetManager {
|
|
@@ -119,36 +176,70 @@ class _SheetManager {
|
|
|
119
176
|
* Provide `context` of the `SheetProvider` where you want to show the action sheet.
|
|
120
177
|
*/
|
|
121
178
|
context?: string;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Stack behavior for this sheet.
|
|
182
|
+
* - `switch`: (default) Closes current sheet, shows new one
|
|
183
|
+
* - `replace`: Swaps content with crossfade animation
|
|
184
|
+
* - `push`: Stacks new sheet on top of current
|
|
185
|
+
*/
|
|
186
|
+
stackBehavior?: StackBehavior;
|
|
122
187
|
},
|
|
123
188
|
): Promise<Sheets[SheetId]["returnValue"]> {
|
|
124
189
|
return new Promise((resolve) => {
|
|
125
190
|
const currentContext = PrivateManager.context({ ...options, id: id });
|
|
126
|
-
const
|
|
191
|
+
const behavior = options?.stackBehavior ?? "switch";
|
|
192
|
+
|
|
193
|
+
const handler = (
|
|
194
|
+
data: unknown,
|
|
195
|
+
context = "global",
|
|
196
|
+
_reopened?: boolean,
|
|
197
|
+
_behavior?: StackBehavior,
|
|
198
|
+
) => {
|
|
127
199
|
if (context !== "global" && currentContext && currentContext !== context)
|
|
128
200
|
return;
|
|
129
|
-
options?.onClose?.(data);
|
|
201
|
+
options?.onClose?.(data as Sheets[SheetId]["returnValue"]);
|
|
130
202
|
sub?.unsubscribe();
|
|
131
|
-
resolve(data);
|
|
203
|
+
resolve(data as Sheets[SheetId]["returnValue"]);
|
|
132
204
|
};
|
|
133
205
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
206
|
+
const sub = eventManager.subscribe(`onclose_${id}`, handler);
|
|
207
|
+
|
|
208
|
+
// Handle existing sheets based on stack behavior.
|
|
209
|
+
// For "push" we do NOT hide existing sheets — they stay underneath.
|
|
210
|
+
if (behavior !== "push") {
|
|
211
|
+
const currentStack = PrivateManager.stack();
|
|
212
|
+
if (currentStack.length > 0) {
|
|
213
|
+
currentStack.forEach(({ id: sheetId, context }) => {
|
|
214
|
+
eventManager.publish(
|
|
215
|
+
`hide_${sheetId}`,
|
|
216
|
+
undefined,
|
|
217
|
+
context,
|
|
218
|
+
true,
|
|
219
|
+
behavior,
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
138
224
|
|
|
139
225
|
// Check if the sheet is registered with any `SheetProviders`.
|
|
140
226
|
let isRegisteredWithSheetProvider = false;
|
|
141
|
-
for (
|
|
142
|
-
for (
|
|
227
|
+
for (const ctx in sheetsRegistry) {
|
|
228
|
+
for (const _id in sheetsRegistry[ctx]) {
|
|
143
229
|
if (_id === id) {
|
|
144
230
|
isRegisteredWithSheetProvider = true;
|
|
231
|
+
break;
|
|
145
232
|
}
|
|
146
233
|
}
|
|
234
|
+
if (isRegisteredWithSheetProvider) break;
|
|
147
235
|
}
|
|
236
|
+
|
|
148
237
|
eventManager.publish(
|
|
149
238
|
isRegisteredWithSheetProvider ? `show_wrap_${id}` : `show_${id}`,
|
|
150
239
|
options?.payload,
|
|
151
240
|
currentContext || "global",
|
|
241
|
+
false,
|
|
242
|
+
behavior,
|
|
152
243
|
);
|
|
153
244
|
});
|
|
154
245
|
}
|
|
@@ -157,7 +248,7 @@ class _SheetManager {
|
|
|
157
248
|
* An async hide function. This is useful when you want to show one Sheet after closing another.
|
|
158
249
|
*
|
|
159
250
|
* @param id id of the Sheet to show
|
|
160
|
-
* @param
|
|
251
|
+
* @param options
|
|
161
252
|
*/
|
|
162
253
|
async hide<SheetId extends keyof Sheets>(
|
|
163
254
|
id: SheetId | (string & {}),
|
|
@@ -172,10 +263,11 @@ class _SheetManager {
|
|
|
172
263
|
context?: string;
|
|
173
264
|
},
|
|
174
265
|
): Promise<Sheets[SheetId]["returnValue"]> {
|
|
175
|
-
|
|
266
|
+
const currentContext = PrivateManager.context({
|
|
176
267
|
...options,
|
|
177
268
|
id: id,
|
|
178
269
|
});
|
|
270
|
+
|
|
179
271
|
return new Promise((resolve) => {
|
|
180
272
|
let isRegisteredWithSheetProvider = false;
|
|
181
273
|
// Check if the sheet is registered with any `SheetProviders`
|
|
@@ -188,14 +280,14 @@ class _SheetManager {
|
|
|
188
280
|
}
|
|
189
281
|
}
|
|
190
282
|
|
|
191
|
-
const hideHandler = (data:
|
|
283
|
+
const hideHandler = (data: unknown, context = "global") => {
|
|
192
284
|
if (context !== "global" && currentContext && currentContext !== context)
|
|
193
285
|
return;
|
|
194
286
|
sub?.unsubscribe();
|
|
195
|
-
resolve(data);
|
|
287
|
+
resolve(data as Sheets[SheetId]["returnValue"]);
|
|
196
288
|
};
|
|
197
289
|
|
|
198
|
-
|
|
290
|
+
const sub = eventManager.subscribe(`onclose_${id}`, hideHandler);
|
|
199
291
|
eventManager.publish(
|
|
200
292
|
isRegisteredWithSheetProvider ? `hide_wrap_${id}` : `hide_${id}`,
|
|
201
293
|
options?.payload,
|
|
@@ -210,11 +302,86 @@ class _SheetManager {
|
|
|
210
302
|
* @param id Hide all sheets for the specific id.
|
|
211
303
|
*/
|
|
212
304
|
hideAll<SheetId extends keyof Sheets>(id?: SheetId | (string & {})) {
|
|
305
|
+
// Clear history when hiding all sheets
|
|
306
|
+
PrivateManager.clearHistory();
|
|
307
|
+
|
|
213
308
|
PrivateManager.stack().forEach(({ id: _id, context }) => {
|
|
214
309
|
if (id && !_id.startsWith(id)) return;
|
|
215
310
|
eventManager.publish(`hide_${_id}`, undefined, context);
|
|
216
311
|
});
|
|
217
312
|
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Replace the current sheet with a new one using crossfade animation.
|
|
316
|
+
* This is a convenience method for show() with stackBehavior: 'replace'.
|
|
317
|
+
*/
|
|
318
|
+
async replace<SheetId extends keyof Sheets>(
|
|
319
|
+
id: SheetId | (string & {}),
|
|
320
|
+
options?: {
|
|
321
|
+
payload?: Sheets[SheetId]["payload"];
|
|
322
|
+
onClose?: (data: Sheets[SheetId]["returnValue"] | undefined) => void;
|
|
323
|
+
context?: string;
|
|
324
|
+
},
|
|
325
|
+
): Promise<Sheets[SheetId]["returnValue"]> {
|
|
326
|
+
return this.show(id, { ...options, stackBehavior: "replace" });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Push a new sheet on top of the current one, creating a stack.
|
|
331
|
+
* This is a convenience method for show() with stackBehavior: 'push'.
|
|
332
|
+
*
|
|
333
|
+
*/
|
|
334
|
+
async push<SheetId extends keyof Sheets>(
|
|
335
|
+
id: SheetId | (string & {}),
|
|
336
|
+
options?: {
|
|
337
|
+
payload?: Sheets[SheetId]["payload"];
|
|
338
|
+
onClose?: (data: Sheets[SheetId]["returnValue"] | undefined) => void;
|
|
339
|
+
context?: string;
|
|
340
|
+
},
|
|
341
|
+
): Promise<Sheets[SheetId]["returnValue"]> {
|
|
342
|
+
return this.show(id, { ...options, stackBehavior: "push" });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Pop the top sheet from the stack and restore the previous one.
|
|
347
|
+
* Only works when sheets were opened with stackBehavior: 'push'.
|
|
348
|
+
*/
|
|
349
|
+
pop(): void {
|
|
350
|
+
const topSheet = PrivateManager.topSheet();
|
|
351
|
+
if (topSheet) {
|
|
352
|
+
eventManager.publish(`hide_${topSheet.id}`, undefined, topSheet.context);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get the internal ref of a sheet instance.
|
|
358
|
+
*
|
|
359
|
+
* @param id Id of the sheet
|
|
360
|
+
* @param context Optional context of the SheetProvider
|
|
361
|
+
*/
|
|
362
|
+
get<SheetId extends keyof Sheets>(
|
|
363
|
+
id: SheetId | (string & {}),
|
|
364
|
+
context?: string,
|
|
365
|
+
): BottomSheetInstance<SheetId> | undefined {
|
|
366
|
+
return PrivateManager.get(id, context)?.current ?? undefined;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Check if a specific sheet is currently visible.
|
|
371
|
+
*/
|
|
372
|
+
isVisible<SheetId extends keyof Sheets>(
|
|
373
|
+
id: SheetId | (string & {}),
|
|
374
|
+
context?: string,
|
|
375
|
+
): boolean {
|
|
376
|
+
return PrivateManager.isSheetVisible(id, context);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Reset all internal state. Useful for testing environments.
|
|
381
|
+
*/
|
|
382
|
+
reset(): void {
|
|
383
|
+
PrivateManager.reset();
|
|
384
|
+
}
|
|
218
385
|
}
|
|
219
386
|
|
|
220
387
|
/**
|