@sigmela/router 0.2.7 → 0.3.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 +41 -0
- package/lib/module/Drawer/Drawer.js +250 -0
- package/lib/module/Drawer/DrawerContext.js +4 -0
- package/lib/module/Drawer/DrawerIcon.web.js +47 -0
- package/lib/module/Drawer/RenderDrawer.native.js +241 -0
- package/lib/module/Drawer/RenderDrawer.web.js +197 -0
- package/lib/module/Drawer/useDrawer.js +11 -0
- package/lib/module/Navigation.js +4 -2
- package/lib/module/NavigationStack.js +22 -5
- package/lib/module/Router.js +214 -60
- package/lib/module/RouterContext.js +1 -1
- package/lib/module/ScreenStack/ScreenStack.web.js +78 -12
- package/lib/module/ScreenStackItem/ScreenStackItem.js +7 -7
- package/lib/module/ScreenStackItem/ScreenStackItem.web.js +65 -6
- package/lib/module/ScreenStackSheetItem/ScreenStackSheetItem.native.js +4 -5
- package/lib/module/SplitView/RenderSplitView.web.js +5 -7
- package/lib/module/SplitView/SplitView.js +10 -2
- package/lib/module/StackRenderer.js +10 -4
- package/lib/module/TabBar/RenderTabBar.native.js +10 -9
- package/lib/module/TabBar/RenderTabBar.web.js +25 -2
- package/lib/module/TabBar/TabBar.js +8 -1
- package/lib/module/TabBar/TabIcon.web.js +12 -7
- package/lib/module/index.js +2 -0
- package/lib/module/styles.css +247 -106
- package/lib/typescript/src/Drawer/Drawer.d.ts +100 -0
- package/lib/typescript/src/Drawer/DrawerContext.d.ts +3 -0
- package/lib/typescript/src/Drawer/DrawerIcon.web.d.ts +7 -0
- package/lib/typescript/src/Drawer/RenderDrawer.native.d.ts +8 -0
- package/lib/typescript/src/Drawer/RenderDrawer.web.d.ts +8 -0
- package/lib/typescript/src/Drawer/useDrawer.d.ts +2 -0
- package/lib/typescript/src/NavigationStack.d.ts +6 -4
- package/lib/typescript/src/Router.d.ts +13 -0
- package/lib/typescript/src/ScreenStack/ScreenStackContext.d.ts +1 -1
- package/lib/typescript/src/ScreenStack/animationHelpers.d.ts +1 -1
- package/lib/typescript/src/SplitView/SplitView.d.ts +2 -0
- package/lib/typescript/src/TabBar/TabBar.d.ts +1 -0
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/types.d.ts +10 -1
- package/package.json +15 -4
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { StackRenderer } from "../StackRenderer.js";
|
|
4
|
+
import { DrawerContext } from "./DrawerContext.js";
|
|
5
|
+
import { useRouter } from "../RouterContext.js";
|
|
6
|
+
import { DrawerIcon } from './DrawerIcon';
|
|
7
|
+
import { useCallback, useSyncExternalStore, memo, useMemo } from 'react';
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
const isImageSource = value => {
|
|
10
|
+
if (value == null) return false;
|
|
11
|
+
const valueType = typeof value;
|
|
12
|
+
if (valueType === 'number') return true;
|
|
13
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
14
|
+
if (valueType === 'object') {
|
|
15
|
+
const v = value;
|
|
16
|
+
if ('uri' in v || 'width' in v || 'height' in v) return true;
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
};
|
|
20
|
+
const toColorString = c => typeof c === 'string' ? c : undefined;
|
|
21
|
+
const DrawerStackRenderer = /*#__PURE__*/memo(({
|
|
22
|
+
stack,
|
|
23
|
+
appearance
|
|
24
|
+
}) => {
|
|
25
|
+
const router = useRouter();
|
|
26
|
+
const stackId = stack.getId();
|
|
27
|
+
const subscribe = useCallback(cb => router.subscribeStack(stackId, cb), [router, stackId]);
|
|
28
|
+
const get = useCallback(() => router.getStackHistory(stackId), [router, stackId]);
|
|
29
|
+
const history = useSyncExternalStore(subscribe, get, get);
|
|
30
|
+
return /*#__PURE__*/_jsx(StackRenderer, {
|
|
31
|
+
appearance: appearance,
|
|
32
|
+
stackId: stackId,
|
|
33
|
+
history: history
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
DrawerStackRenderer.displayName = 'DrawerStackRenderer';
|
|
37
|
+
const DrawerNodeRenderer = /*#__PURE__*/memo(({
|
|
38
|
+
node,
|
|
39
|
+
appearance
|
|
40
|
+
}) => {
|
|
41
|
+
const Renderer = useMemo(() => node.getRenderer(), [node]);
|
|
42
|
+
return /*#__PURE__*/_jsx(Renderer, {
|
|
43
|
+
appearance: appearance
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
DrawerNodeRenderer.displayName = 'DrawerNodeRenderer';
|
|
47
|
+
const joinPrefixAndPath = (prefix, path) => {
|
|
48
|
+
const base = (prefix ?? '').trim();
|
|
49
|
+
const child = (path || '/').trim();
|
|
50
|
+
if (!base) return child || '/';
|
|
51
|
+
const baseNorm = base === '/' ? '' : base.endsWith('/') ? base.slice(0, -1) : base;
|
|
52
|
+
if (!child || child === '/') {
|
|
53
|
+
return baseNorm || '/';
|
|
54
|
+
}
|
|
55
|
+
if (child.startsWith('/')) {
|
|
56
|
+
return `${baseNorm}${child}`;
|
|
57
|
+
}
|
|
58
|
+
return `${baseNorm}/${child}`;
|
|
59
|
+
};
|
|
60
|
+
export const RenderDrawer = /*#__PURE__*/memo(({
|
|
61
|
+
drawer,
|
|
62
|
+
appearance
|
|
63
|
+
}) => {
|
|
64
|
+
const router = useRouter();
|
|
65
|
+
const subscribe = useCallback(cb => drawer.subscribe(cb), [drawer]);
|
|
66
|
+
const snapshot = useSyncExternalStore(subscribe, drawer.getState, drawer.getState);
|
|
67
|
+
const {
|
|
68
|
+
tabs,
|
|
69
|
+
index,
|
|
70
|
+
config,
|
|
71
|
+
isOpen
|
|
72
|
+
} = snapshot;
|
|
73
|
+
const focusedTab = tabs[index];
|
|
74
|
+
const stack = focusedTab ? drawer.stacks[focusedTab.tabKey] : undefined;
|
|
75
|
+
const node = focusedTab ? drawer.nodes[focusedTab.tabKey] : undefined;
|
|
76
|
+
const Screen = focusedTab ? drawer.screens[focusedTab.tabKey] : undefined;
|
|
77
|
+
const onItemClick = useCallback(nextIndex => {
|
|
78
|
+
const targetTab = tabs[nextIndex];
|
|
79
|
+
if (!targetTab) return;
|
|
80
|
+
const targetStack = drawer.stacks[targetTab.tabKey];
|
|
81
|
+
const targetNode = drawer.nodes[targetTab.tabKey];
|
|
82
|
+
if (targetStack) {
|
|
83
|
+
if (nextIndex !== index) {
|
|
84
|
+
drawer.onIndexChange(nextIndex);
|
|
85
|
+
}
|
|
86
|
+
const firstRoutePath = targetStack.getFirstRoute()?.path;
|
|
87
|
+
if (firstRoutePath) {
|
|
88
|
+
router.reset(firstRoutePath);
|
|
89
|
+
}
|
|
90
|
+
} else if (targetNode) {
|
|
91
|
+
if (nextIndex !== index) {
|
|
92
|
+
drawer.onIndexChange(nextIndex);
|
|
93
|
+
}
|
|
94
|
+
const seedPath = targetNode.seed?.()?.path;
|
|
95
|
+
const fallbackFirstPath = targetNode.getNodeRoutes()?.[0]?.path;
|
|
96
|
+
const path = seedPath ?? fallbackFirstPath ?? '/';
|
|
97
|
+
const fullPath = joinPrefixAndPath(targetTab.tabPrefix, path);
|
|
98
|
+
router.reset(fullPath);
|
|
99
|
+
} else {
|
|
100
|
+
if (nextIndex !== index) {
|
|
101
|
+
drawer.onIndexChange(nextIndex);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Close drawer on mobile after navigation
|
|
106
|
+
drawer.close();
|
|
107
|
+
}, [router, drawer, tabs, index]);
|
|
108
|
+
const handleItemClick = useCallback(e => {
|
|
109
|
+
const idx = Number(e.currentTarget.dataset.index);
|
|
110
|
+
onItemClick(idx);
|
|
111
|
+
}, [onItemClick]);
|
|
112
|
+
const handleOverlayClick = useCallback(() => {
|
|
113
|
+
drawer.close();
|
|
114
|
+
}, [drawer]);
|
|
115
|
+
const drawerStyle = useMemo(() => {
|
|
116
|
+
const bg = toColorString(appearance?.tabBar?.backgroundColor);
|
|
117
|
+
const style = {};
|
|
118
|
+
if (bg) {
|
|
119
|
+
style['--drawer-bg'] = bg;
|
|
120
|
+
}
|
|
121
|
+
if (drawer.width !== 280) {
|
|
122
|
+
style['--drawer-width'] = `${drawer.width}px`;
|
|
123
|
+
}
|
|
124
|
+
return Object.keys(style).length ? style : undefined;
|
|
125
|
+
}, [appearance?.tabBar?.backgroundColor, drawer.width]);
|
|
126
|
+
const titleBaseStyle = useMemo(() => ({
|
|
127
|
+
fontFamily: appearance?.tabBar?.title?.fontFamily,
|
|
128
|
+
fontSize: appearance?.tabBar?.title?.fontSize,
|
|
129
|
+
fontWeight: appearance?.tabBar?.title?.fontWeight,
|
|
130
|
+
fontStyle: appearance?.tabBar?.title?.fontStyle
|
|
131
|
+
}), [appearance?.tabBar?.title?.fontFamily, appearance?.tabBar?.title?.fontSize, appearance?.tabBar?.title?.fontWeight, appearance?.tabBar?.title?.fontStyle]);
|
|
132
|
+
const CustomDrawer = config.component;
|
|
133
|
+
return /*#__PURE__*/_jsx(DrawerContext.Provider, {
|
|
134
|
+
value: drawer,
|
|
135
|
+
children: /*#__PURE__*/_jsxs("div", {
|
|
136
|
+
className: "drawer-container",
|
|
137
|
+
"data-drawer-open": isOpen ? 'true' : 'false',
|
|
138
|
+
style: drawerStyle,
|
|
139
|
+
children: [CustomDrawer ? /*#__PURE__*/_jsx(CustomDrawer, {
|
|
140
|
+
onItemPress: onItemClick,
|
|
141
|
+
activeIndex: index,
|
|
142
|
+
items: tabs,
|
|
143
|
+
isOpen: isOpen,
|
|
144
|
+
onClose: handleOverlayClick
|
|
145
|
+
}) : /*#__PURE__*/_jsx("div", {
|
|
146
|
+
className: "drawer-sidebar",
|
|
147
|
+
children: /*#__PURE__*/_jsx("div", {
|
|
148
|
+
className: "drawer-sidebar-content",
|
|
149
|
+
children: tabs.map((tab, i) => {
|
|
150
|
+
const isActive = i === index;
|
|
151
|
+
const iconTint = toColorString(isActive ? appearance?.tabBar?.iconColorActive : appearance?.tabBar?.iconColor);
|
|
152
|
+
const title = appearance?.tabBar?.title;
|
|
153
|
+
const labelColor = isActive ? toColorString(title?.activeColor) ?? toColorString(title?.color) : toColorString(title?.color);
|
|
154
|
+
const labelStyle = {
|
|
155
|
+
...titleBaseStyle,
|
|
156
|
+
color: labelColor
|
|
157
|
+
};
|
|
158
|
+
return /*#__PURE__*/_jsxs("button", {
|
|
159
|
+
type: "button",
|
|
160
|
+
"data-index": i,
|
|
161
|
+
"data-active": isActive ? 'true' : 'false',
|
|
162
|
+
"aria-current": isActive ? 'page' : undefined,
|
|
163
|
+
className: `drawer-item${isActive ? ' active' : ''}`,
|
|
164
|
+
onClick: handleItemClick,
|
|
165
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
166
|
+
className: "drawer-item-icon",
|
|
167
|
+
children: isImageSource(tab.icon) ? /*#__PURE__*/_jsx(DrawerIcon, {
|
|
168
|
+
source: tab.icon,
|
|
169
|
+
tintColor: iconTint
|
|
170
|
+
}) : null
|
|
171
|
+
}), /*#__PURE__*/_jsx("div", {
|
|
172
|
+
className: "drawer-item-label",
|
|
173
|
+
style: labelStyle,
|
|
174
|
+
children: tab.title
|
|
175
|
+
}), tab.badgeValue ? /*#__PURE__*/_jsx("span", {
|
|
176
|
+
className: "drawer-item-badge",
|
|
177
|
+
children: tab.badgeValue
|
|
178
|
+
}) : null]
|
|
179
|
+
}, tab.tabKey);
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
}), /*#__PURE__*/_jsx("div", {
|
|
183
|
+
className: "drawer-overlay",
|
|
184
|
+
onClick: handleOverlayClick
|
|
185
|
+
}), /*#__PURE__*/_jsx("div", {
|
|
186
|
+
className: "drawer-main",
|
|
187
|
+
children: stack ? /*#__PURE__*/_jsx(DrawerStackRenderer, {
|
|
188
|
+
appearance: appearance,
|
|
189
|
+
stack: stack
|
|
190
|
+
}) : node ? /*#__PURE__*/_jsx(DrawerNodeRenderer, {
|
|
191
|
+
appearance: appearance,
|
|
192
|
+
node: node
|
|
193
|
+
}) : Screen ? /*#__PURE__*/_jsx(Screen, {}) : null
|
|
194
|
+
})]
|
|
195
|
+
})
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useContext } from 'react';
|
|
4
|
+
import { DrawerContext } from "./DrawerContext.js";
|
|
5
|
+
export const useDrawer = () => {
|
|
6
|
+
const drawer = useContext(DrawerContext);
|
|
7
|
+
if (!drawer) {
|
|
8
|
+
throw new Error('useDrawer must be used within a DrawerProvider');
|
|
9
|
+
}
|
|
10
|
+
return drawer;
|
|
11
|
+
};
|
package/lib/module/Navigation.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ScreenStackItem } from "./ScreenStackItem/index.js";
|
|
|
4
4
|
import { RouterContext } from "./RouterContext.js";
|
|
5
5
|
import { ScreenStack } from "./ScreenStack/index.js";
|
|
6
6
|
import { StyleSheet } from 'react-native';
|
|
7
|
-
import { useSyncExternalStore, memo, useCallback, useEffect, useState } from 'react';
|
|
7
|
+
import { useSyncExternalStore, memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
8
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
9
|
const EMPTY_HISTORY = [];
|
|
10
10
|
function useStackHistory(router, stackId) {
|
|
@@ -38,7 +38,9 @@ export const Navigation = /*#__PURE__*/memo(({
|
|
|
38
38
|
const {
|
|
39
39
|
rootId
|
|
40
40
|
} = root;
|
|
41
|
-
const
|
|
41
|
+
const rootTransitionRef = useRef(undefined);
|
|
42
|
+
rootTransitionRef.current = router.getRootTransition();
|
|
43
|
+
const rootTransition = rootTransitionRef.current;
|
|
42
44
|
const rootItems = useStackHistory(router, rootId);
|
|
43
45
|
return /*#__PURE__*/_jsx(RouterContext.Provider, {
|
|
44
46
|
value: router,
|
|
@@ -9,15 +9,20 @@ export class NavigationStack {
|
|
|
9
9
|
routes = [];
|
|
10
10
|
children = [];
|
|
11
11
|
debugEnabled = false;
|
|
12
|
+
_cachedRenderer = null;
|
|
12
13
|
constructor(idOrOptions, maybeOptions, maybeDebug) {
|
|
13
14
|
if (typeof idOrOptions === 'string') {
|
|
14
|
-
this.stackId = idOrOptions
|
|
15
|
+
this.stackId = idOrOptions;
|
|
15
16
|
this.defaultOptions = maybeOptions;
|
|
16
17
|
this.debugEnabled = maybeDebug ?? false;
|
|
17
|
-
|
|
18
|
+
this.provider = maybeOptions?.provider;
|
|
19
|
+
} else if (typeof idOrOptions === 'object') {
|
|
18
20
|
this.stackId = `stack-${nanoid()}`;
|
|
19
21
|
this.defaultOptions = idOrOptions;
|
|
20
|
-
this.debugEnabled = false;
|
|
22
|
+
this.debugEnabled = idOrOptions.debug ?? false;
|
|
23
|
+
this.provider = idOrOptions?.provider;
|
|
24
|
+
} else {
|
|
25
|
+
this.stackId = `stack-${nanoid()}`;
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
log(message, data) {
|
|
@@ -84,6 +89,7 @@ export class NavigationStack {
|
|
|
84
89
|
}
|
|
85
90
|
addModal(path, mixedComponent, options) {
|
|
86
91
|
return this.addScreen(path, mixedComponent, {
|
|
92
|
+
syncWithUrl: false,
|
|
87
93
|
...options,
|
|
88
94
|
stackPresentation: 'modal'
|
|
89
95
|
});
|
|
@@ -132,15 +138,26 @@ export class NavigationStack {
|
|
|
132
138
|
return this.children.slice();
|
|
133
139
|
}
|
|
134
140
|
getRenderer() {
|
|
141
|
+
if (this._cachedRenderer) {
|
|
142
|
+
return this._cachedRenderer;
|
|
143
|
+
}
|
|
144
|
+
|
|
135
145
|
// eslint-disable-next-line consistent-this
|
|
136
146
|
const stackInstance = this;
|
|
137
147
|
const stackId = stackInstance.getId();
|
|
138
|
-
|
|
139
|
-
|
|
148
|
+
const provider = this.provider;
|
|
149
|
+
const renderer = function NavigationStackRenderer(props) {
|
|
150
|
+
const stackElement = /*#__PURE__*/React.createElement(StackRenderer, {
|
|
140
151
|
stackId: stackId,
|
|
141
152
|
appearance: props.appearance
|
|
142
153
|
});
|
|
154
|
+
if (provider) {
|
|
155
|
+
return /*#__PURE__*/React.createElement(provider, null, stackElement);
|
|
156
|
+
}
|
|
157
|
+
return stackElement;
|
|
143
158
|
};
|
|
159
|
+
this._cachedRenderer = renderer;
|
|
160
|
+
return renderer;
|
|
144
161
|
}
|
|
145
162
|
seed() {
|
|
146
163
|
const first = this.getFirstRoute();
|