@rn-tools/navigation 2.3.0 → 3.0.2

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.
@@ -1,487 +0,0 @@
1
- import * as React from "react";
2
- import { ScreenProps as RNScreenProps } from "react-native-screens";
3
-
4
- import type {
5
- NavigationState,
6
- PushScreenOptions,
7
- RenderCharts,
8
- StackItem,
9
- ScreenItem,
10
- TabItem,
11
- } from "./types";
12
- import {
13
- generateScreenId,
14
- generateStackId,
15
- generateTabId,
16
- serializeTabIndexKey,
17
- } from "./utils";
18
-
19
- export let DEFAULT_SLOT_NAME = "DEFAULT_SLOT";
20
-
21
- function getInitialState(): NavigationState {
22
- return {
23
- stacks: {
24
- ids: [],
25
- lookup: {},
26
- },
27
- screens: {
28
- ids: [],
29
- lookup: {},
30
- },
31
- tabs: {
32
- ids: [],
33
- lookup: {},
34
- },
35
- debugModeEnabled: false,
36
- };
37
- }
38
-
39
- export let initialState = getInitialState();
40
-
41
- export let initialRenderCharts: RenderCharts = {
42
- stacksByDepth: {},
43
- tabsByDepth: {},
44
- tabParentsById: {},
45
- stackParentsById: {},
46
- stacksByTabIndex: {},
47
- };
48
-
49
- type CreateStackAction = {
50
- type: "CREATE_STACK_INSTANCE";
51
- stackId?: string;
52
- defaultSlotName?: string;
53
- };
54
-
55
- type RegisterStackAction = {
56
- type: "REGISTER_STACK";
57
- depth: number;
58
- isActive: boolean;
59
- stackId: string;
60
- parentStackId: string;
61
- parentTabId: string;
62
- tabIndex: number;
63
- };
64
-
65
- type UnregisterStackAction = {
66
- type: "UNREGISTER_STACK";
67
- stackId: string;
68
- };
69
-
70
- type PushScreenStackAction = PushScreenOptions & {
71
- type: "PUSH_SCREEN";
72
- element: React.ReactElement<RNScreenProps>;
73
- };
74
-
75
- type PopScreenByCountAction = {
76
- type: "POP_SCREEN_BY_COUNT";
77
- count: number;
78
- stackId: string;
79
- };
80
-
81
- type PopScreenByKeyAction = {
82
- type: "POP_SCREEN_BY_KEY";
83
- key: string;
84
- };
85
-
86
- type StackActions =
87
- | CreateStackAction
88
- | RegisterStackAction
89
- | UnregisterStackAction
90
- | PushScreenStackAction
91
- | PopScreenByCountAction
92
- | PopScreenByKeyAction;
93
-
94
- type CreateTabAction = {
95
- type: "CREATE_TAB_INSTANCE";
96
- tabId?: string;
97
- initialActiveIndex?: number;
98
- };
99
-
100
- type SetTabIndexAction = {
101
- type: "SET_TAB_INDEX";
102
- index: number;
103
- tabId: string;
104
- };
105
-
106
- type PopActiveTabAction = {
107
- type: "POP_ACTIVE_TAB";
108
- tabId: string;
109
- index: number;
110
- };
111
-
112
- type RegisterTabAction = {
113
- type: "REGISTER_TAB";
114
- depth: number;
115
- tabId: string;
116
- isActive: boolean;
117
- parentTabId?: string;
118
- };
119
-
120
- type UnregisterTabAction = {
121
- type: "UNREGISTER_TAB";
122
- tabId: string;
123
- };
124
-
125
- type TabBackAction = {
126
- type: "TAB_BACK";
127
- tabId: string;
128
- };
129
-
130
- type TabActions =
131
- | CreateTabAction
132
- | SetTabIndexAction
133
- | RegisterTabAction
134
- | UnregisterTabAction
135
- | TabBackAction
136
- | PopActiveTabAction;
137
-
138
- type SetDebugModeAction = {
139
- type: "SET_DEBUG_MODE";
140
- enabled: boolean;
141
- };
142
-
143
- type DebugActions = SetDebugModeAction;
144
-
145
- type ResetNavigationAction = {
146
- type: "RESET_NAVIGATION";
147
- };
148
-
149
- export type NavigationAction =
150
- | StackActions
151
- | TabActions
152
- | DebugActions
153
- | ResetNavigationAction;
154
-
155
- export function reducer(
156
- state: NavigationState,
157
- action: NavigationAction,
158
- context: { renderCharts: RenderCharts }
159
- ): NavigationState {
160
- switch (action.type) {
161
- case "CREATE_STACK_INSTANCE": {
162
- action.stackId = action.stackId || generateStackId();
163
-
164
- let initialStack: StackItem = {
165
- id: action.stackId,
166
- defaultSlotName: action.defaultSlotName || DEFAULT_SLOT_NAME,
167
- screens: [],
168
- };
169
-
170
- let nextState = Object.assign({}, state);
171
- nextState.stacks.ids = nextState.stacks.ids
172
- .filter((id) => id !== initialStack.id)
173
- .concat(initialStack.id);
174
-
175
- nextState.stacks.lookup[action.stackId] = initialStack;
176
- return nextState;
177
- }
178
-
179
- case "REGISTER_STACK": {
180
- let { depth, isActive, stackId, parentStackId, parentTabId, tabIndex } =
181
- action;
182
- let { renderCharts } = context;
183
-
184
- renderCharts.stacksByDepth[depth] =
185
- renderCharts.stacksByDepth[depth] || [];
186
-
187
- Object.keys(renderCharts.stacksByDepth).forEach((depth) => {
188
- renderCharts.stacksByDepth[depth] = renderCharts.stacksByDepth[
189
- depth
190
- ].filter((id) => id !== stackId);
191
- });
192
-
193
- if (isActive && !renderCharts.stacksByDepth[depth].includes(stackId)) {
194
- renderCharts.stacksByDepth[depth].push(stackId);
195
- }
196
-
197
- if (parentStackId) {
198
- renderCharts.stackParentsById[stackId] = parentStackId;
199
- }
200
-
201
- if (parentTabId) {
202
- let tabIndexKey = serializeTabIndexKey(parentTabId, tabIndex);
203
- renderCharts.stacksByTabIndex[tabIndexKey] =
204
- renderCharts.stacksByTabIndex[tabIndexKey] || [];
205
-
206
- if (!renderCharts.stacksByTabIndex[tabIndexKey].includes(stackId)) {
207
- renderCharts.stacksByTabIndex[tabIndexKey].push(stackId);
208
- }
209
- }
210
-
211
- return state;
212
- }
213
-
214
- case "UNREGISTER_STACK": {
215
- let { stackId } = action;
216
- let { renderCharts } = context;
217
-
218
- let nextState = Object.assign({}, state);
219
-
220
- for (let depth in renderCharts.stacksByDepth) {
221
- renderCharts.stacksByDepth[depth] = renderCharts.stacksByDepth[
222
- depth
223
- ].filter((id) => id !== stackId);
224
- }
225
-
226
- let stack = state.stacks.lookup[stackId];
227
-
228
- if (stack && renderCharts.stackParentsById[stackId] != null) {
229
- stack.screens.forEach((screenId) => {
230
- delete nextState.screens.lookup[screenId];
231
- nextState.screens.ids = nextState.screens.ids.filter(
232
- (id) => id !== screenId
233
- );
234
- });
235
-
236
- nextState.stacks.ids = nextState.stacks.ids.filter(
237
- (id) => id !== stackId
238
- );
239
- delete nextState.stacks.lookup[stackId];
240
- }
241
-
242
- return nextState;
243
- }
244
-
245
- case "PUSH_SCREEN": {
246
- let { element, stackId, screenId, slotName } = action;
247
- let stack = state.stacks.lookup[stackId];
248
-
249
- if (!stack) {
250
- if (state.debugModeEnabled) {
251
- console.warn("Stack not found: ", stackId);
252
- }
253
- return state;
254
- }
255
-
256
- if (screenId && state.screens.lookup[screenId] != null) {
257
- return state;
258
- }
259
-
260
- let nextState = Object.assign({}, state);
261
-
262
- let screenItem: ScreenItem = {
263
- element,
264
- slotName: slotName || stack.defaultSlotName,
265
- id: screenId || generateScreenId(),
266
- stackId: stack.id,
267
- };
268
-
269
- nextState.screens.ids = nextState.screens.ids.concat(screenItem.id);
270
- nextState.screens.lookup[screenItem.id] = screenItem;
271
-
272
- nextState.stacks.lookup[stackId] = Object.assign(stack, {
273
- screens: stack.screens
274
- .filter((id) => id !== screenItem.id)
275
- .concat(screenItem.id),
276
- });
277
-
278
- return nextState;
279
- }
280
-
281
- case "POP_SCREEN_BY_COUNT": {
282
- let { count, stackId } = action;
283
- let stack = state.stacks.lookup[stackId];
284
-
285
- if (!stack) {
286
- if (state.debugModeEnabled) {
287
- console.warn("Stack not found: ", stackId);
288
- }
289
- return state;
290
- }
291
-
292
- if (count === -1) {
293
- count = stack.screens.length;
294
- }
295
-
296
- let nextState = Object.assign({}, state);
297
- let poppedScreenIds = nextState.stacks.lookup[stackId].screens.splice(
298
- -count,
299
- count
300
- );
301
-
302
- poppedScreenIds.forEach((screenId) => {
303
- delete nextState.screens.lookup[screenId];
304
- nextState.screens.ids = nextState.screens.ids.filter(
305
- (id) => id !== screenId
306
- );
307
- });
308
-
309
- return nextState;
310
- }
311
-
312
- case "POP_SCREEN_BY_KEY": {
313
- let { key } = action;
314
-
315
- let stackId = state.screens.lookup[key]?.stackId;
316
- let stack = state.stacks.lookup[stackId];
317
-
318
- if (!stack) {
319
- if (state.debugModeEnabled) {
320
- console.warn("Stack not found: ", stackId);
321
- }
322
-
323
- return state;
324
- }
325
-
326
- let nextState = Object.assign({}, state);
327
-
328
- nextState.stacks.lookup[stackId] = Object.assign(stack, {
329
- screens: stack.screens.filter((screenId) => screenId !== key),
330
- });
331
-
332
- delete nextState.screens.lookup[key];
333
-
334
- nextState.screens = {
335
- ids: nextState.screens.ids.filter((id) => id !== key),
336
- lookup: nextState.screens.lookup,
337
- };
338
-
339
- return nextState;
340
- }
341
-
342
- case "CREATE_TAB_INSTANCE": {
343
- let { tabId, initialActiveIndex = 0 } = action;
344
-
345
- let initialTabs: TabItem = {
346
- id: tabId || generateTabId(),
347
- activeIndex: initialActiveIndex,
348
- history: [],
349
- };
350
-
351
- let nextState = Object.assign({}, state);
352
-
353
- nextState.tabs.lookup[initialTabs.id] = initialTabs;
354
- nextState.tabs.ids = nextState.tabs.ids
355
- .filter((id) => id !== initialTabs.id)
356
- .concat(initialTabs.id);
357
-
358
- return nextState;
359
- }
360
-
361
- case "SET_TAB_INDEX": {
362
- let { tabId, index } = action;
363
- let { renderCharts } = context;
364
-
365
- let tab = state.tabs.lookup[tabId];
366
- if (!tab) {
367
- if (state.debugModeEnabled) {
368
- console.warn("Tab not found: ", tabId);
369
- }
370
-
371
- return state;
372
- }
373
-
374
- let nextState: NavigationState = Object.assign({}, state);
375
- let currentIndex = tab.activeIndex;
376
- nextState.tabs.lookup[tabId] = Object.assign(
377
- {},
378
- {
379
- ...nextState.tabs.lookup[tabId],
380
- activeIndex: index,
381
- history: tab.history.filter((i) => i !== index).concat(currentIndex),
382
- }
383
- );
384
-
385
- return nextState;
386
- }
387
-
388
- case "POP_ACTIVE_TAB": {
389
- let { tabId, index } = action;
390
- let { renderCharts } = context;
391
-
392
- let tabKey = serializeTabIndexKey(tabId, index);
393
- let stackIds = renderCharts.stacksByTabIndex[tabKey];
394
-
395
- let nextState: NavigationState = Object.assign({}, state);
396
-
397
- if (stackIds?.length > 0) {
398
- stackIds.forEach((stackId) => {
399
- let stack = nextState.stacks.lookup[stackId];
400
- let screenIdsToRemove = stack.screens;
401
-
402
- let nextScreensLookup = Object.assign({}, nextState.screens.lookup);
403
-
404
- screenIdsToRemove.forEach((id) => {
405
- delete nextScreensLookup[id];
406
- });
407
-
408
- nextState.stacks.lookup[stackId].screens = [];
409
- nextState.screens.ids = nextState.screens.ids.filter(
410
- (id) => !screenIdsToRemove.includes(id)
411
- );
412
- nextState.screens.lookup = nextScreensLookup;
413
- });
414
- }
415
-
416
- return nextState;
417
- }
418
-
419
- case "REGISTER_TAB": {
420
- let { depth, tabId, parentTabId, isActive } = action;
421
- let { renderCharts } = context;
422
- renderCharts.tabsByDepth[depth] = renderCharts.tabsByDepth[depth] || [];
423
-
424
- Object.keys(renderCharts.tabsByDepth).forEach((depth) => {
425
- renderCharts.tabsByDepth[depth] = renderCharts.tabsByDepth[
426
- depth
427
- ].filter((id) => id !== tabId);
428
- });
429
-
430
- renderCharts.tabParentsById[tabId] = parentTabId ?? "";
431
-
432
- if (isActive) {
433
- renderCharts.tabsByDepth[depth]?.push(tabId);
434
- }
435
- return state;
436
- }
437
-
438
- case "UNREGISTER_TAB": {
439
- let { tabId } = action;
440
- let { renderCharts } = context;
441
- for (let depth in renderCharts.tabsByDepth) {
442
- renderCharts.tabsByDepth[depth] = renderCharts.tabsByDepth[
443
- depth
444
- ].filter((id) => id !== tabId);
445
- }
446
-
447
- let nextState: NavigationState = Object.assign({}, state);
448
-
449
- nextState.tabs.ids = state.tabs.ids.filter((id) => id !== tabId);
450
- delete nextState.tabs.lookup[tabId];
451
-
452
- return nextState;
453
- }
454
-
455
- case "TAB_BACK": {
456
- let { tabId } = action;
457
- let nextState: NavigationState = Object.assign({}, state);
458
-
459
- let tab = nextState.tabs.lookup[tabId];
460
-
461
- let lastActiveIndex = tab.history[tab.history.length - 1];
462
-
463
- nextState.tabs.lookup = Object.assign({}, nextState.tabs.lookup, {
464
- [tabId]: Object.assign({}, nextState.tabs.lookup[tabId], {
465
- activeIndex: lastActiveIndex,
466
- history: tab.history.filter((i) => i !== lastActiveIndex),
467
- }),
468
- });
469
-
470
- return nextState;
471
- }
472
-
473
- case "SET_DEBUG_MODE": {
474
- let { enabled } = action;
475
- return Object.assign(state, { debugModeEnabled: enabled });
476
- }
477
-
478
- case "RESET_NAVIGATION": {
479
- context.renderCharts = initialRenderCharts;
480
- return getInitialState();
481
- }
482
-
483
- default: {
484
- return state;
485
- }
486
- }
487
- }
@@ -1,58 +0,0 @@
1
- import * as React from "react";
2
- import { createStore, useStore as useStoreContext } from "zustand";
3
- import { devtools, redux } from "zustand/middleware";
4
-
5
- import {
6
- initialRenderCharts,
7
- initialState,
8
- reducer as navigationReducer,
9
- type NavigationAction,
10
- } from "./navigation-reducer";
11
- import type { NavigationState } from "./types";
12
-
13
- export type NavigationStore = ReturnType<typeof createNavigationStore>;
14
-
15
- export function createNavigationStore() {
16
- let renderCharts = Object.assign({}, initialRenderCharts);
17
-
18
- let reducer = (state: NavigationState, action: NavigationAction) => {
19
- let nextState = navigationReducer(state, action, { renderCharts });
20
- if (nextState.debugModeEnabled) {
21
- console.debug(
22
- `[@rntoolkit/navigation] action: ${action.type}`,
23
- state,
24
- nextState
25
- );
26
- }
27
- return { ...nextState };
28
- };
29
-
30
- let store = createStore(devtools(redux(reducer, initialState)));
31
-
32
- return {
33
- store: store,
34
- dispatch: store.dispatch,
35
- renderCharts,
36
- };
37
- }
38
-
39
- export let rootStore = createNavigationStore();
40
- export let NavigationStateContext = React.createContext(rootStore.store);
41
- export let NavigationDispatchContext = React.createContext(rootStore.dispatch);
42
-
43
- export function useNavigationState<T>(
44
- selector?: (state: NavigationState) => T
45
- ) {
46
- let context = React.useContext(NavigationStateContext);
47
- return useStoreContext(context, selector);
48
- }
49
-
50
- export function useNavigationDispatch() {
51
- let dispatch = React.useContext(NavigationDispatchContext);
52
- return dispatch;
53
- }
54
-
55
- export function useGetNavigationStore() {
56
- let context = React.useContext(NavigationStateContext);
57
- return context.getState
58
- }
package/src/types.ts DELETED
@@ -1,48 +0,0 @@
1
- export type PushScreenOptions = {
2
- stackId?: string;
3
- slotName?: string;
4
- screenId?: string;
5
- };
6
-
7
- export type StackItem = {
8
- id: string;
9
- defaultSlotName?: string;
10
- screens: string[];
11
- };
12
-
13
- export type ScreenItem = {
14
- id: string;
15
- stackId: string;
16
- element: React.ReactElement<unknown>;
17
- slotName?: string;
18
- };
19
-
20
- export type TabItem = {
21
- id: string;
22
- activeIndex: number;
23
- history: number[];
24
- };
25
-
26
- export type NavigationState = {
27
- stacks: {
28
- lookup: Record<string, StackItem>;
29
- ids: string[];
30
- };
31
- tabs: {
32
- lookup: Record<string, TabItem>;
33
- ids: string[];
34
- };
35
- screens: {
36
- lookup: Record<string, ScreenItem>;
37
- ids: string[];
38
- };
39
- debugModeEnabled: boolean;
40
- };
41
-
42
- export type RenderCharts = {
43
- stacksByDepth: Record<string, string[]>;
44
- tabsByDepth: Record<string, string[]>;
45
- tabParentsById: Record<string, string>;
46
- stackParentsById: Record<string, string>;
47
- stacksByTabIndex: Record<string, string[]>;
48
- };
package/src/utils.ts DELETED
@@ -1,40 +0,0 @@
1
- import { Platform } from 'react-native';
2
-
3
- export let generateStackId = createIdGenerator("stack");
4
- export let generateScreenId = createIdGenerator("screen");
5
- export let generateTabId = createIdGenerator("tab");
6
-
7
- function createIdGenerator(name: string) {
8
- let counter = 0;
9
-
10
- return function generateId() {
11
- return name + "-" + counter++;
12
- };
13
- }
14
-
15
- export let serializeTabIndexKey = (tabId: string, index: number) =>
16
- `${tabId}-${index}`;
17
-
18
-
19
-
20
-
21
- let baseInsets = {
22
- top: Platform.OS === "ios" ? 59 : 49,
23
- bottom: Platform.OS === "ios" ? 34 : 0,
24
- right: 0,
25
- left: 0,
26
- };
27
-
28
- export function useSafeAreaInsetsSafe() {
29
- let insets = baseInsets;
30
-
31
- try {
32
- // Linter thinks this is conditional but it seems fine
33
- // eslint-disable-next-line
34
- insets = baseInsets;
35
- } catch (error) {
36
- console.warn("`react-native-safe-area-context` missing - Please install and wrap your app in a SafeAreaProvider");
37
- }
38
-
39
- return insets;
40
- }