@sigmela/router 0.2.8 → 0.3.1
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 +107 -1
- 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 +244 -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 +14 -4
- 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 +55 -24
- 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 +255 -91
- 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 +1 -0
- 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 +10 -1
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/types.d.ts +12 -3
- package/package.json +28 -12
|
@@ -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,17 +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;
|
|
18
|
-
} else {
|
|
19
|
+
} else if (typeof idOrOptions === 'object') {
|
|
19
20
|
this.stackId = `stack-${nanoid()}`;
|
|
20
21
|
this.defaultOptions = idOrOptions;
|
|
21
|
-
this.debugEnabled = false;
|
|
22
|
+
this.debugEnabled = idOrOptions.debug ?? false;
|
|
22
23
|
this.provider = idOrOptions?.provider;
|
|
24
|
+
} else {
|
|
25
|
+
this.stackId = `stack-${nanoid()}`;
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
log(message, data) {
|
|
@@ -86,6 +89,7 @@ export class NavigationStack {
|
|
|
86
89
|
}
|
|
87
90
|
addModal(path, mixedComponent, options) {
|
|
88
91
|
return this.addScreen(path, mixedComponent, {
|
|
92
|
+
syncWithUrl: false,
|
|
89
93
|
...options,
|
|
90
94
|
stackPresentation: 'modal'
|
|
91
95
|
});
|
|
@@ -134,11 +138,15 @@ export class NavigationStack {
|
|
|
134
138
|
return this.children.slice();
|
|
135
139
|
}
|
|
136
140
|
getRenderer() {
|
|
141
|
+
if (this._cachedRenderer) {
|
|
142
|
+
return this._cachedRenderer;
|
|
143
|
+
}
|
|
144
|
+
|
|
137
145
|
// eslint-disable-next-line consistent-this
|
|
138
146
|
const stackInstance = this;
|
|
139
147
|
const stackId = stackInstance.getId();
|
|
140
148
|
const provider = this.provider;
|
|
141
|
-
|
|
149
|
+
const renderer = function NavigationStackRenderer(props) {
|
|
142
150
|
const stackElement = /*#__PURE__*/React.createElement(StackRenderer, {
|
|
143
151
|
stackId: stackId,
|
|
144
152
|
appearance: props.appearance
|
|
@@ -148,6 +156,8 @@ export class NavigationStack {
|
|
|
148
156
|
}
|
|
149
157
|
return stackElement;
|
|
150
158
|
};
|
|
159
|
+
this._cachedRenderer = renderer;
|
|
160
|
+
return renderer;
|
|
151
161
|
}
|
|
152
162
|
seed() {
|
|
153
163
|
const first = this.getFirstRoute();
|