@mapples/navigation 1.1.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 +46 -0
- package/dist/MapplesNavigation.d.ts +10 -0
- package/dist/MapplesNavigation.d.ts.map +1 -0
- package/dist/MapplesNavigation.js +30 -0
- package/dist/MapplesNavigation.js.map +1 -0
- package/dist/NavigationActionHandler.d.ts +14 -0
- package/dist/NavigationActionHandler.d.ts.map +1 -0
- package/dist/NavigationActionHandler.js +50 -0
- package/dist/NavigationActionHandler.js.map +1 -0
- package/dist/actionCommands.d.ts +11 -0
- package/dist/actionCommands.d.ts.map +1 -0
- package/dist/actionCommands.js +45 -0
- package/dist/actionCommands.js.map +1 -0
- package/dist/buildNavigator.d.ts +14 -0
- package/dist/buildNavigator.d.ts.map +1 -0
- package/dist/buildNavigator.js +87 -0
- package/dist/buildNavigator.js.map +1 -0
- package/dist/constants.d.ts +19 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +18 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/mappers/drawerOptions.d.ts +5 -0
- package/dist/mappers/drawerOptions.d.ts.map +1 -0
- package/dist/mappers/drawerOptions.js +26 -0
- package/dist/mappers/drawerOptions.js.map +1 -0
- package/dist/mappers/headerOptions.d.ts +10 -0
- package/dist/mappers/headerOptions.d.ts.map +1 -0
- package/dist/mappers/headerOptions.js +42 -0
- package/dist/mappers/headerOptions.js.map +1 -0
- package/dist/mappers/index.d.ts +5 -0
- package/dist/mappers/index.d.ts.map +1 -0
- package/dist/mappers/index.js +5 -0
- package/dist/mappers/index.js.map +1 -0
- package/dist/mappers/screenOptions.d.ts +13 -0
- package/dist/mappers/screenOptions.d.ts.map +1 -0
- package/dist/mappers/screenOptions.js +37 -0
- package/dist/mappers/screenOptions.js.map +1 -0
- package/dist/mappers/tabBarOptions.d.ts +5 -0
- package/dist/mappers/tabBarOptions.d.ts.map +1 -0
- package/dist/mappers/tabBarOptions.js +37 -0
- package/dist/mappers/tabBarOptions.js.map +1 -0
- package/dist/model.d.ts +90 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +189 -0
- package/dist/model.js.map +1 -0
- package/dist/refs.d.ts +15 -0
- package/dist/refs.d.ts.map +1 -0
- package/dist/refs.js +21 -0
- package/dist/refs.js.map +1 -0
- package/dist/types.d.ts +126 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/useRefResolvers.d.ts +10 -0
- package/dist/useRefResolvers.d.ts.map +1 -0
- package/dist/useRefResolvers.js +64 -0
- package/dist/useRefResolvers.js.map +1 -0
- package/dist/vendor.d.ts +29 -0
- package/dist/vendor.d.ts.map +1 -0
- package/dist/vendor.js +29 -0
- package/dist/vendor.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { mapHeaderOptions } from './headerOptions';
|
|
2
|
+
/**
|
|
3
|
+
* Per-route native-stack options: the route's header overrides
|
|
4
|
+
* ({@link RouteNode.options} extends `Partial<HeaderOptions>`) plus its
|
|
5
|
+
* `presentation`.
|
|
6
|
+
*/
|
|
7
|
+
export const mapStackScreenOptions = (route, resolvers) => {
|
|
8
|
+
const patch = {
|
|
9
|
+
...mapHeaderOptions(route.options, resolvers),
|
|
10
|
+
};
|
|
11
|
+
if (route.presentation !== undefined)
|
|
12
|
+
patch.presentation = route.presentation;
|
|
13
|
+
return patch;
|
|
14
|
+
};
|
|
15
|
+
/** Per-route bottom-tabs item options (label + icon). */
|
|
16
|
+
export const mapTabItemOptions = (route, resolvers) => {
|
|
17
|
+
const patch = {};
|
|
18
|
+
if (route.options?.tabLabel !== undefined) {
|
|
19
|
+
patch.tabBarLabel = route.options.tabLabel;
|
|
20
|
+
}
|
|
21
|
+
const icon = resolvers.resolveIcon(route.options?.tabIcon);
|
|
22
|
+
if (icon)
|
|
23
|
+
patch.tabBarIcon = icon;
|
|
24
|
+
return patch;
|
|
25
|
+
};
|
|
26
|
+
/** Per-route drawer item options (label + icon). */
|
|
27
|
+
export const mapDrawerItemOptions = (route, resolvers) => {
|
|
28
|
+
const patch = {};
|
|
29
|
+
if (route.options?.drawerLabel !== undefined) {
|
|
30
|
+
patch.drawerLabel = route.options.drawerLabel;
|
|
31
|
+
}
|
|
32
|
+
const icon = resolvers.resolveIcon(route.options?.drawerIcon);
|
|
33
|
+
if (icon)
|
|
34
|
+
patch.drawerIcon = icon;
|
|
35
|
+
return patch;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=screenOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenOptions.js","sourceRoot":"","sources":["../../src/mappers/screenOptions.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,KAAgB,EAChB,SAAuB,EACE,EAAE;IAC3B,MAAM,KAAK,GAA4B;QACrC,GAAG,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC;KAC9C,CAAC;IACF,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;QAAE,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;IAC9E,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,yDAAyD;AACzD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,KAAgB,EAChB,SAAuB,EACF,EAAE;IACvB,MAAM,KAAK,GAAwB,EAAE,CAAC;IACtC,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC1C,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC7C,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3D,IAAI,IAAI;QAAE,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;IAClC,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,oDAAoD;AACpD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,KAAgB,EAChB,SAAuB,EACC,EAAE;IAC1B,MAAM,KAAK,GAA2B,EAAE,CAAC;IACzC,IAAI,KAAK,CAAC,OAAO,EAAE,WAAW,KAAK,SAAS,EAAE,CAAC;QAC7C,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;IAChD,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC9D,IAAI,IAAI;QAAE,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;IAClC,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TabBarOptions } from '@mapples/types';
|
|
2
|
+
import { RefResolvers, TabBarOptionsPatch } from '../types';
|
|
3
|
+
/** Map {@link TabBarOptions} to a bottom-tabs navigator options patch. */
|
|
4
|
+
export declare const mapTabBarOptions: (tabBar: TabBarOptions | undefined, resolvers: RefResolvers) => TabBarOptionsPatch;
|
|
5
|
+
//# sourceMappingURL=tabBarOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabBarOptions.d.ts","sourceRoot":"","sources":["../../src/mappers/tabBarOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE5D,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,aAAa,GAAG,SAAS,EACjC,WAAW,YAAY,KACtB,kBAkCF,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Map {@link TabBarOptions} to a bottom-tabs navigator options patch. */
|
|
2
|
+
export const mapTabBarOptions = (tabBar, resolvers) => {
|
|
3
|
+
const patch = {};
|
|
4
|
+
if (!tabBar)
|
|
5
|
+
return patch;
|
|
6
|
+
const style = {};
|
|
7
|
+
if (tabBar.visible !== undefined) {
|
|
8
|
+
style.display = tabBar.visible ? 'flex' : 'none';
|
|
9
|
+
}
|
|
10
|
+
const backgroundColor = resolvers.resolveColor(tabBar.backgroundColor);
|
|
11
|
+
if (backgroundColor)
|
|
12
|
+
style.backgroundColor = backgroundColor;
|
|
13
|
+
if (Object.keys(style).length)
|
|
14
|
+
patch.tabBarStyle = style;
|
|
15
|
+
const activeTintColor = resolvers.resolveColor(tabBar.activeTintColor);
|
|
16
|
+
if (activeTintColor)
|
|
17
|
+
patch.tabBarActiveTintColor = activeTintColor;
|
|
18
|
+
const inactiveTintColor = resolvers.resolveColor(tabBar.inactiveTintColor);
|
|
19
|
+
if (inactiveTintColor)
|
|
20
|
+
patch.tabBarInactiveTintColor = inactiveTintColor;
|
|
21
|
+
if (tabBar.labelVisible !== undefined) {
|
|
22
|
+
patch.tabBarShowLabel = tabBar.labelVisible;
|
|
23
|
+
}
|
|
24
|
+
if (tabBar.labelStyle) {
|
|
25
|
+
const labelStyle = {};
|
|
26
|
+
if (tabBar.labelStyle.fontFamily) {
|
|
27
|
+
labelStyle.fontFamily = tabBar.labelStyle.fontFamily;
|
|
28
|
+
}
|
|
29
|
+
if (tabBar.labelStyle.fontSize !== undefined) {
|
|
30
|
+
labelStyle.fontSize = tabBar.labelStyle.fontSize;
|
|
31
|
+
}
|
|
32
|
+
if (Object.keys(labelStyle).length)
|
|
33
|
+
patch.tabBarLabelStyle = labelStyle;
|
|
34
|
+
}
|
|
35
|
+
return patch;
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=tabBarOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabBarOptions.js","sourceRoot":"","sources":["../../src/mappers/tabBarOptions.ts"],"names":[],"mappings":"AAIA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,MAAiC,EACjC,SAAuB,EACH,EAAE;IACtB,MAAM,KAAK,GAAuB,EAAE,CAAC;IACrC,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAE1B,MAAM,KAAK,GAAmD,EAAE,CAAC;IACjE,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACnD,CAAC;IACD,MAAM,eAAe,GAAG,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACvE,IAAI,eAAe;QAAE,KAAK,CAAC,eAAe,GAAG,eAAe,CAAC;IAC7D,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM;QAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC;IAEzD,MAAM,eAAe,GAAG,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACvE,IAAI,eAAe;QAAE,KAAK,CAAC,qBAAqB,GAAG,eAAe,CAAC;IAEnE,MAAM,iBAAiB,GAAG,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC3E,IAAI,iBAAiB;QAAE,KAAK,CAAC,uBAAuB,GAAG,iBAAiB,CAAC;IAEzE,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACtC,KAAK,CAAC,eAAe,GAAG,MAAM,CAAC,YAAY,CAAC;IAC9C,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,UAAU,GAAwD,EAAE,CAAC;QAC3E,IAAI,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;YACjC,UAAU,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;QACvD,CAAC;QACD,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC7C,UAAU,CAAC,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;QACnD,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM;YAAE,KAAK,CAAC,gBAAgB,GAAG,UAAU,CAAC;IAC1E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { NavigationDocument, NavigatorNode, NavigatorType, RouteNode } from '@mapples/types';
|
|
2
|
+
/**
|
|
3
|
+
* Build the navigator-qualified screen name for a route.
|
|
4
|
+
*
|
|
5
|
+
* Route names are unique only *within* a navigator (the validator allows two
|
|
6
|
+
* stacks to each own an `index` route), so a bare name like `index` is
|
|
7
|
+
* ambiguous as a `navigate` target. Navigator names are unique per document, so
|
|
8
|
+
* `<navigatorName>:<routeName>` is a stable, unambiguous screen name. This is
|
|
9
|
+
* the name registered with React Navigation and stored as the action target.
|
|
10
|
+
*/
|
|
11
|
+
export declare const qualifyRouteName: (navigatorName: string, routeName: string) => string;
|
|
12
|
+
/** A screen within a {@link NavigatorModel}. */
|
|
13
|
+
export interface ScreenModel {
|
|
14
|
+
/** Navigator-qualified screen name (`<navigatorName>:<routeName>`). */
|
|
15
|
+
routeName: string;
|
|
16
|
+
routeId: string;
|
|
17
|
+
route: RouteNode;
|
|
18
|
+
/** Page leaf, or a nested navigator already expanded into a child model. */
|
|
19
|
+
target: {
|
|
20
|
+
kind: 'page';
|
|
21
|
+
pageUuid: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'navigator';
|
|
24
|
+
child: NavigatorModel;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** A fully-expanded navigator ready to be rendered. */
|
|
28
|
+
export interface NavigatorModel {
|
|
29
|
+
id: string;
|
|
30
|
+
type: NavigatorType;
|
|
31
|
+
name: string;
|
|
32
|
+
node: NavigatorNode;
|
|
33
|
+
/** Resolved initial route name (defaults to the first screen). */
|
|
34
|
+
initialRouteName?: string;
|
|
35
|
+
screens: ScreenModel[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Expand a {@link NavigationDocument} into a navigator tree by following
|
|
39
|
+
* `navigator` route targets from the root. Pure and side-effect free — it does
|
|
40
|
+
* not touch React Navigation, so it can be asserted on directly in tests.
|
|
41
|
+
*
|
|
42
|
+
* Assumes a validated document (see `validateNavigationDocument`); a missing or
|
|
43
|
+
* cyclic navigator reference simply stops that branch rather than throwing.
|
|
44
|
+
*/
|
|
45
|
+
export declare const buildNavigationModel: (document: NavigationDocument) => NavigatorModel | null;
|
|
46
|
+
/**
|
|
47
|
+
* The navigator-qualified screen name of the first route (anywhere in the
|
|
48
|
+
* document) that targets a given page. Used to resolve `pageUuid` navigation
|
|
49
|
+
* targets to a screen name.
|
|
50
|
+
*/
|
|
51
|
+
export declare const findRouteNameForPage: (document: NavigationDocument, pageUuid: string) => string | null;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve a navigation target to the qualified screen name the runtime
|
|
54
|
+
* navigates to.
|
|
55
|
+
*
|
|
56
|
+
* - An exact `<navigator>:<route>` match is returned as-is.
|
|
57
|
+
* - A bare legacy route name (no navigator prefix) resolves to the first route
|
|
58
|
+
* with that name, qualified, with a one-time warning — backward compatibility
|
|
59
|
+
* for documents authored before targets were qualified.
|
|
60
|
+
*
|
|
61
|
+
* Returns `null` when nothing matches.
|
|
62
|
+
*/
|
|
63
|
+
export declare const resolveScreenName: (document: NavigationDocument, target: string) => string | null;
|
|
64
|
+
/**
|
|
65
|
+
* The set of route ids nested beneath a tree node — every screen reachable
|
|
66
|
+
* under a navigator, or under a route that targets a navigator. The editor uses
|
|
67
|
+
* this to keep a parent node selected while the preview shows one of its
|
|
68
|
+
* descendant screens.
|
|
69
|
+
*/
|
|
70
|
+
export declare const collectDescendantRouteIds: (document: NavigationDocument, nodeId: string) => Set<string>;
|
|
71
|
+
/** A React Navigation `navigate` call: a screen name plus optional nested params. */
|
|
72
|
+
export interface ScreenNavigateTarget {
|
|
73
|
+
name: string;
|
|
74
|
+
params?: object;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build the (possibly nested) navigate target for a qualified screen name.
|
|
78
|
+
*
|
|
79
|
+
* React Navigation can't reach a screen inside a nested navigator by a flat
|
|
80
|
+
* name — it descends via `navigate(parent, { screen, params })`. We walk the
|
|
81
|
+
* document from the root to the target route and nest the qualified screen
|
|
82
|
+
* names so the action lands in the right navigator. Returns `null` when the
|
|
83
|
+
* name resolves to no route.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // screen "HomeStack:Feed" nested under the "Root:HomeTab" route →
|
|
87
|
+
* { name: 'Root:HomeTab', params: { screen: 'HomeStack:Feed', params: undefined } }
|
|
88
|
+
*/
|
|
89
|
+
export declare const buildScreenNavigateTarget: (document: NavigationDocument, screenName: string, params?: object) => ScreenNavigateTarget | null;
|
|
90
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,SAAS,EACV,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAC3B,eAAe,MAAM,EACrB,WAAW,MAAM,KAChB,MAAyC,CAAC;AAE7C,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,SAAS,CAAC;IACjB,4EAA4E;IAC5E,MAAM,EACF;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAClC;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,cAAc,CAAA;KAAE,CAAC;CAClD;AAED,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAC/B,UAAU,kBAAkB,KAC3B,cAAc,GAAG,IA+CnB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,UAAU,kBAAkB,EAC5B,UAAU,MAAM,KACf,MAAM,GAAG,IASX,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,kBAAkB,EAC5B,QAAQ,MAAM,KACb,MAAM,GAAG,IAuBX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACpC,UAAU,kBAAkB,EAC5B,QAAQ,MAAM,KACb,GAAG,CAAC,MAAM,CA8BZ,CAAC;AAEF,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAwBD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,GACpC,UAAU,kBAAkB,EAC5B,YAAY,MAAM,EAClB,SAAS,MAAM,KACd,oBAAoB,GAAG,IAYzB,CAAC"}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the navigator-qualified screen name for a route.
|
|
3
|
+
*
|
|
4
|
+
* Route names are unique only *within* a navigator (the validator allows two
|
|
5
|
+
* stacks to each own an `index` route), so a bare name like `index` is
|
|
6
|
+
* ambiguous as a `navigate` target. Navigator names are unique per document, so
|
|
7
|
+
* `<navigatorName>:<routeName>` is a stable, unambiguous screen name. This is
|
|
8
|
+
* the name registered with React Navigation and stored as the action target.
|
|
9
|
+
*/
|
|
10
|
+
export const qualifyRouteName = (navigatorName, routeName) => `${navigatorName}:${routeName}`;
|
|
11
|
+
/**
|
|
12
|
+
* Expand a {@link NavigationDocument} into a navigator tree by following
|
|
13
|
+
* `navigator` route targets from the root. Pure and side-effect free — it does
|
|
14
|
+
* not touch React Navigation, so it can be asserted on directly in tests.
|
|
15
|
+
*
|
|
16
|
+
* Assumes a validated document (see `validateNavigationDocument`); a missing or
|
|
17
|
+
* cyclic navigator reference simply stops that branch rather than throwing.
|
|
18
|
+
*/
|
|
19
|
+
export const buildNavigationModel = (document) => {
|
|
20
|
+
const build = (navigatorId, seen) => {
|
|
21
|
+
const node = document.navigators[navigatorId];
|
|
22
|
+
if (!node || seen.has(navigatorId))
|
|
23
|
+
return null;
|
|
24
|
+
const nextSeen = new Set(seen).add(navigatorId);
|
|
25
|
+
const screens = [];
|
|
26
|
+
for (const route of node.routes) {
|
|
27
|
+
const routeName = qualifyRouteName(node.name, route.name);
|
|
28
|
+
if (route.target.kind === 'page') {
|
|
29
|
+
screens.push({
|
|
30
|
+
routeName,
|
|
31
|
+
routeId: route.id,
|
|
32
|
+
route,
|
|
33
|
+
target: { kind: 'page', pageUuid: route.target.pageUuid },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const child = build(route.target.navigatorId, nextSeen);
|
|
38
|
+
if (!child)
|
|
39
|
+
continue;
|
|
40
|
+
screens.push({
|
|
41
|
+
routeName,
|
|
42
|
+
routeId: route.id,
|
|
43
|
+
route,
|
|
44
|
+
target: { kind: 'navigator', child },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const initialRouteName = screens.find((screen) => screen.routeId === node.initialRouteId)
|
|
49
|
+
?.routeName ?? screens[0]?.routeName;
|
|
50
|
+
return {
|
|
51
|
+
id: node.id,
|
|
52
|
+
type: node.type,
|
|
53
|
+
name: node.name,
|
|
54
|
+
node,
|
|
55
|
+
screens,
|
|
56
|
+
initialRouteName,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
return build(document.rootNavigatorId, new Set());
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* The navigator-qualified screen name of the first route (anywhere in the
|
|
63
|
+
* document) that targets a given page. Used to resolve `pageUuid` navigation
|
|
64
|
+
* targets to a screen name.
|
|
65
|
+
*/
|
|
66
|
+
export const findRouteNameForPage = (document, pageUuid) => {
|
|
67
|
+
for (const navigator of Object.values(document.navigators)) {
|
|
68
|
+
for (const route of navigator.routes) {
|
|
69
|
+
if (route.target.kind === 'page' && route.target.pageUuid === pageUuid) {
|
|
70
|
+
return qualifyRouteName(navigator.name, route.name);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Resolve a navigation target to the qualified screen name the runtime
|
|
78
|
+
* navigates to.
|
|
79
|
+
*
|
|
80
|
+
* - An exact `<navigator>:<route>` match is returned as-is.
|
|
81
|
+
* - A bare legacy route name (no navigator prefix) resolves to the first route
|
|
82
|
+
* with that name, qualified, with a one-time warning — backward compatibility
|
|
83
|
+
* for documents authored before targets were qualified.
|
|
84
|
+
*
|
|
85
|
+
* Returns `null` when nothing matches.
|
|
86
|
+
*/
|
|
87
|
+
export const resolveScreenName = (document, target) => {
|
|
88
|
+
let firstBareMatch = null;
|
|
89
|
+
for (const navigator of Object.values(document.navigators)) {
|
|
90
|
+
for (const route of navigator.routes) {
|
|
91
|
+
const qualified = qualifyRouteName(navigator.name, route.name);
|
|
92
|
+
if (qualified === target)
|
|
93
|
+
return qualified;
|
|
94
|
+
if (route.name === target && firstBareMatch === null) {
|
|
95
|
+
firstBareMatch = qualified;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (firstBareMatch) {
|
|
100
|
+
console.warn(`[mapples-navigation] navigation target "${target}" is a bare route name ` +
|
|
101
|
+
`and was resolved to "${firstBareMatch}". Re-save the navigation ` +
|
|
102
|
+
`document to store the qualified "navigator:route" target.`);
|
|
103
|
+
return firstBareMatch;
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* The set of route ids nested beneath a tree node — every screen reachable
|
|
109
|
+
* under a navigator, or under a route that targets a navigator. The editor uses
|
|
110
|
+
* this to keep a parent node selected while the preview shows one of its
|
|
111
|
+
* descendant screens.
|
|
112
|
+
*/
|
|
113
|
+
export const collectDescendantRouteIds = (document, nodeId) => {
|
|
114
|
+
const result = new Set();
|
|
115
|
+
const collect = (navigatorId, seen) => {
|
|
116
|
+
const navigator = document.navigators[navigatorId];
|
|
117
|
+
if (!navigator || seen.has(navigatorId))
|
|
118
|
+
return;
|
|
119
|
+
const next = new Set(seen).add(navigatorId);
|
|
120
|
+
for (const route of navigator.routes) {
|
|
121
|
+
result.add(route.id);
|
|
122
|
+
if (route.target.kind === 'navigator') {
|
|
123
|
+
collect(route.target.navigatorId, next);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
if (document.navigators[nodeId]) {
|
|
128
|
+
collect(nodeId, new Set());
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
for (const navigator of Object.values(document.navigators)) {
|
|
132
|
+
const route = navigator.routes.find((r) => r.id === nodeId);
|
|
133
|
+
if (route) {
|
|
134
|
+
if (route.target.kind === 'navigator') {
|
|
135
|
+
collect(route.target.navigatorId, new Set());
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
};
|
|
142
|
+
/** Qualified screen names from the root navigator down to `screenName`. */
|
|
143
|
+
const findScreenPath = (document, screenName) => {
|
|
144
|
+
const visit = (navigatorId, seen) => {
|
|
145
|
+
const navigator = document.navigators[navigatorId];
|
|
146
|
+
if (!navigator || seen.has(navigatorId))
|
|
147
|
+
return null;
|
|
148
|
+
const next = new Set(seen).add(navigatorId);
|
|
149
|
+
for (const route of navigator.routes) {
|
|
150
|
+
const qualified = qualifyRouteName(navigator.name, route.name);
|
|
151
|
+
if (qualified === screenName)
|
|
152
|
+
return [qualified];
|
|
153
|
+
if (route.target.kind === 'navigator') {
|
|
154
|
+
const sub = visit(route.target.navigatorId, next);
|
|
155
|
+
if (sub)
|
|
156
|
+
return [qualified, ...sub];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
};
|
|
161
|
+
return visit(document.rootNavigatorId, new Set());
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Build the (possibly nested) navigate target for a qualified screen name.
|
|
165
|
+
*
|
|
166
|
+
* React Navigation can't reach a screen inside a nested navigator by a flat
|
|
167
|
+
* name — it descends via `navigate(parent, { screen, params })`. We walk the
|
|
168
|
+
* document from the root to the target route and nest the qualified screen
|
|
169
|
+
* names so the action lands in the right navigator. Returns `null` when the
|
|
170
|
+
* name resolves to no route.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* // screen "HomeStack:Feed" nested under the "Root:HomeTab" route →
|
|
174
|
+
* { name: 'Root:HomeTab', params: { screen: 'HomeStack:Feed', params: undefined } }
|
|
175
|
+
*/
|
|
176
|
+
export const buildScreenNavigateTarget = (document, screenName, params) => {
|
|
177
|
+
const names = findScreenPath(document, screenName);
|
|
178
|
+
if (!names)
|
|
179
|
+
return null;
|
|
180
|
+
let target = { name: names[names.length - 1], params };
|
|
181
|
+
for (let i = names.length - 2; i >= 0; i--) {
|
|
182
|
+
target = {
|
|
183
|
+
name: names[i],
|
|
184
|
+
params: { screen: target.name, params: target.params },
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return target;
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
package/dist/refs.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing for the shared `ref(assetType:uuid:name)` grammar (same grammar as
|
|
3
|
+
* `@mapples/render`'s `RefPropsMap`). Used to resolve `ref(theme:…)` colours and
|
|
4
|
+
* `ref(icon:…)` icons at render time.
|
|
5
|
+
*/
|
|
6
|
+
export interface ParsedRef {
|
|
7
|
+
type: string;
|
|
8
|
+
uuid: string;
|
|
9
|
+
name: string;
|
|
10
|
+
}
|
|
11
|
+
/** Parse a `ref(type:uuid:name)` string, or `null` if it isn't one. */
|
|
12
|
+
export declare const parseRef: (value?: string) => ParsedRef | null;
|
|
13
|
+
/** Whether a value is a `ref(...)` string (of `type`, if given). */
|
|
14
|
+
export declare const isRef: (value?: string, type?: string) => boolean;
|
|
15
|
+
//# sourceMappingURL=refs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../src/refs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAID,uEAAuE;AACvE,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,KAAG,SAAS,GAAG,IAKrD,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,KAAK,GAAI,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,OAGrD,CAAC"}
|
package/dist/refs.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing for the shared `ref(assetType:uuid:name)` grammar (same grammar as
|
|
3
|
+
* `@mapples/render`'s `RefPropsMap`). Used to resolve `ref(theme:…)` colours and
|
|
4
|
+
* `ref(icon:…)` icons at render time.
|
|
5
|
+
*/
|
|
6
|
+
const REF_PATTERN = /^ref\(([^:]+):([^:]+):(.+)\)$/;
|
|
7
|
+
/** Parse a `ref(type:uuid:name)` string, or `null` if it isn't one. */
|
|
8
|
+
export const parseRef = (value) => {
|
|
9
|
+
if (!value)
|
|
10
|
+
return null;
|
|
11
|
+
const match = REF_PATTERN.exec(value);
|
|
12
|
+
if (!match)
|
|
13
|
+
return null;
|
|
14
|
+
return { type: match[1], uuid: match[2], name: match[3] };
|
|
15
|
+
};
|
|
16
|
+
/** Whether a value is a `ref(...)` string (of `type`, if given). */
|
|
17
|
+
export const isRef = (value, type) => {
|
|
18
|
+
const parsed = parseRef(value);
|
|
19
|
+
return !!parsed && (!type || parsed.type === type);
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=refs.js.map
|
package/dist/refs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refs.js","sourceRoot":"","sources":["../src/refs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,MAAM,WAAW,GAAG,+BAA+B,CAAC;AAEpD,uEAAuE;AACvE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoB,EAAE;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5D,CAAC,CAAC;AAEF,oEAAoE;AACpE,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAc,EAAE,IAAa,EAAW,EAAE;IAC9D,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACrD,CAAC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import { NavigationDocument } from '@mapples/types';
|
|
3
|
+
/** Renders an icon for a tab/drawer item, given the runtime colour/size. */
|
|
4
|
+
export type IconRenderer = (props: {
|
|
5
|
+
color?: string;
|
|
6
|
+
size?: number;
|
|
7
|
+
}) => ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* Resolves `ref(…)` option values to concrete runtime values. Supplied by the
|
|
10
|
+
* host (from `@mapples/style` + `@mapples/render` contexts); stubbed in tests.
|
|
11
|
+
*/
|
|
12
|
+
export interface RefResolvers {
|
|
13
|
+
/** Resolve a colour value or `ref(theme:…)` to a concrete colour. */
|
|
14
|
+
resolveColor: (value?: string) => string | undefined;
|
|
15
|
+
/** Resolve `ref(icon:…)` to an icon renderer. */
|
|
16
|
+
resolveIcon: (value?: string) => IconRenderer | undefined;
|
|
17
|
+
}
|
|
18
|
+
/** Subset of native-stack header options the runtime sets. */
|
|
19
|
+
export interface StackHeaderOptionsPatch {
|
|
20
|
+
headerShown?: boolean;
|
|
21
|
+
title?: string;
|
|
22
|
+
headerStyle?: {
|
|
23
|
+
backgroundColor?: string;
|
|
24
|
+
};
|
|
25
|
+
headerTintColor?: string;
|
|
26
|
+
headerTitleStyle?: {
|
|
27
|
+
fontFamily?: string;
|
|
28
|
+
fontSize?: number;
|
|
29
|
+
color?: string;
|
|
30
|
+
};
|
|
31
|
+
headerBackVisible?: boolean;
|
|
32
|
+
headerShadowVisible?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** Per-route native-stack options (header overrides + presentation). */
|
|
35
|
+
export interface StackScreenOptionsPatch extends StackHeaderOptionsPatch {
|
|
36
|
+
presentation?: 'card' | 'modal' | 'transparentModal';
|
|
37
|
+
}
|
|
38
|
+
/** Subset of bottom-tabs navigator options the runtime sets. */
|
|
39
|
+
export interface TabBarOptionsPatch {
|
|
40
|
+
tabBarStyle?: {
|
|
41
|
+
display?: 'flex' | 'none';
|
|
42
|
+
backgroundColor?: string;
|
|
43
|
+
};
|
|
44
|
+
tabBarActiveTintColor?: string;
|
|
45
|
+
tabBarInactiveTintColor?: string;
|
|
46
|
+
tabBarShowLabel?: boolean;
|
|
47
|
+
tabBarLabelStyle?: {
|
|
48
|
+
fontFamily?: string;
|
|
49
|
+
fontSize?: number;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Per-route bottom-tabs item options. */
|
|
53
|
+
export interface TabItemOptionsPatch {
|
|
54
|
+
tabBarLabel?: string;
|
|
55
|
+
tabBarIcon?: IconRenderer;
|
|
56
|
+
}
|
|
57
|
+
/** Subset of drawer navigator options the runtime sets. */
|
|
58
|
+
export interface DrawerOptionsPatch {
|
|
59
|
+
drawerPosition?: 'left' | 'right';
|
|
60
|
+
drawerType?: 'front' | 'back' | 'slide';
|
|
61
|
+
drawerStyle?: {
|
|
62
|
+
backgroundColor?: string;
|
|
63
|
+
width?: number;
|
|
64
|
+
};
|
|
65
|
+
drawerActiveTintColor?: string;
|
|
66
|
+
drawerInactiveTintColor?: string;
|
|
67
|
+
}
|
|
68
|
+
/** Per-route drawer item options. */
|
|
69
|
+
export interface DrawerItemOptionsPatch {
|
|
70
|
+
drawerLabel?: string;
|
|
71
|
+
drawerIcon?: IconRenderer;
|
|
72
|
+
}
|
|
73
|
+
/** A resolved navigation intent, applied to the navigation container ref. */
|
|
74
|
+
export type NavigationCommand = {
|
|
75
|
+
kind: 'navigate';
|
|
76
|
+
routeName: string;
|
|
77
|
+
params?: Record<string, unknown>;
|
|
78
|
+
} | {
|
|
79
|
+
kind: 'goBack';
|
|
80
|
+
} | {
|
|
81
|
+
kind: 'openDrawer';
|
|
82
|
+
} | {
|
|
83
|
+
kind: 'closeDrawer';
|
|
84
|
+
} | {
|
|
85
|
+
kind: 'switchTab';
|
|
86
|
+
routeName: string;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Structural subset of React Navigation's container ref the runtime uses.
|
|
90
|
+
* Typed here (rather than importing `@react-navigation/native`) so this module
|
|
91
|
+
* stays free of the React Navigation dependency. A real
|
|
92
|
+
* `NavigationContainerRef` satisfies it.
|
|
93
|
+
*/
|
|
94
|
+
export interface MapplesNavigationRef {
|
|
95
|
+
isReady: () => boolean;
|
|
96
|
+
canGoBack: () => boolean;
|
|
97
|
+
goBack: () => void;
|
|
98
|
+
navigate: (name: string, params?: object) => void;
|
|
99
|
+
dispatch: (action: unknown) => void;
|
|
100
|
+
getCurrentRoute: () => {
|
|
101
|
+
name: string;
|
|
102
|
+
} | undefined;
|
|
103
|
+
addListener: (type: string, callback: (...args: unknown[]) => void) => () => void;
|
|
104
|
+
}
|
|
105
|
+
/** Props for {@link MapplesNavigation}. */
|
|
106
|
+
export interface MapplesNavigationProps {
|
|
107
|
+
document: NavigationDocument;
|
|
108
|
+
/** pageUuid → component rendered for that page. */
|
|
109
|
+
pages: Record<string, ComponentType<Record<string, unknown>>>;
|
|
110
|
+
/** Rendered when a route references a missing page. */
|
|
111
|
+
fallback?: ComponentType<{
|
|
112
|
+
pageUuid: string;
|
|
113
|
+
}>;
|
|
114
|
+
/**
|
|
115
|
+
* Render inside `NavigationIndependentTree` instead of a `NavigationContainer`
|
|
116
|
+
* — required when the host app already owns a container (expo-router / Creator).
|
|
117
|
+
*/
|
|
118
|
+
independent?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Externally-created container ref. Supply one to observe/drive navigation
|
|
121
|
+
* from the host (e.g. the Creator preview's selection sync). When omitted, an
|
|
122
|
+
* internal ref is created.
|
|
123
|
+
*/
|
|
124
|
+
navigationRef?: MapplesNavigationRef;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RefResolvers } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Builds {@link RefResolvers} from the host's style + render contexts:
|
|
4
|
+
* `ref(theme:…)` resolves against `@mapples/style`'s active theme, `ref(icon:…)`
|
|
5
|
+
* against `@mapples/render`'s assets map. Unresolvable refs warn and fall back to
|
|
6
|
+
* a default rather than crashing.
|
|
7
|
+
*/
|
|
8
|
+
declare const useRefResolvers: () => RefResolvers;
|
|
9
|
+
export default useRefResolvers;
|
|
10
|
+
//# sourceMappingURL=useRefResolvers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRefResolvers.d.ts","sourceRoot":"","sources":["../src/useRefResolvers.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAgB,YAAY,EAAE,MAAM,SAAS,CAAC;AAYrD;;;;;GAKG;AACH,QAAA,MAAM,eAAe,QAAO,YA8C3B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import { useRender } from '@mapples/render';
|
|
3
|
+
import { useTheme } from '@mapples/style';
|
|
4
|
+
import { Image } from 'react-native';
|
|
5
|
+
import { parseRef } from './refs';
|
|
6
|
+
/** Resolve a dot-path (e.g. `primary.main`) against the active theme map. */
|
|
7
|
+
const lookupToken = (map, path) => {
|
|
8
|
+
let current = map;
|
|
9
|
+
for (const key of path.split('.')) {
|
|
10
|
+
if (current == null || typeof current === 'string')
|
|
11
|
+
return undefined;
|
|
12
|
+
current = current[key];
|
|
13
|
+
}
|
|
14
|
+
return typeof current === 'string' ? current : undefined;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Builds {@link RefResolvers} from the host's style + render contexts:
|
|
18
|
+
* `ref(theme:…)` resolves against `@mapples/style`'s active theme, `ref(icon:…)`
|
|
19
|
+
* against `@mapples/render`'s assets map. Unresolvable refs warn and fall back to
|
|
20
|
+
* a default rather than crashing.
|
|
21
|
+
*/
|
|
22
|
+
const useRefResolvers = () => {
|
|
23
|
+
const { theme, themeConfig } = useTheme();
|
|
24
|
+
const { assets } = useRender();
|
|
25
|
+
const resolveColor = (value) => {
|
|
26
|
+
if (!value)
|
|
27
|
+
return undefined;
|
|
28
|
+
const parsed = parseRef(value);
|
|
29
|
+
if (!parsed)
|
|
30
|
+
return value; // already a concrete colour value
|
|
31
|
+
if (parsed.type !== 'theme')
|
|
32
|
+
return undefined;
|
|
33
|
+
const resolved = lookupToken(theme, parsed.name);
|
|
34
|
+
if (resolved)
|
|
35
|
+
return resolved;
|
|
36
|
+
console.warn(`[@mapples/navigation] unresolved theme ref: ${value}`);
|
|
37
|
+
return themeConfig.defaultColor;
|
|
38
|
+
};
|
|
39
|
+
const resolveIcon = (value) => {
|
|
40
|
+
if (!value)
|
|
41
|
+
return undefined;
|
|
42
|
+
const parsed = parseRef(value);
|
|
43
|
+
if (!parsed || parsed.type !== 'icon')
|
|
44
|
+
return undefined;
|
|
45
|
+
const asset = assets[value];
|
|
46
|
+
if (asset == null) {
|
|
47
|
+
console.warn(`[@mapples/navigation] unresolved icon ref: ${value}`);
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const source = typeof asset === 'string'
|
|
51
|
+
? { uri: asset }
|
|
52
|
+
: asset;
|
|
53
|
+
// The returned value is a React Navigation icon option callback, not a
|
|
54
|
+
// component, so it needs no display name.
|
|
55
|
+
// eslint-disable-next-line react/display-name
|
|
56
|
+
return ({ size }) => createElement(Image, {
|
|
57
|
+
source,
|
|
58
|
+
style: { width: size ?? 24, height: size ?? 24 },
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
return { resolveColor, resolveIcon };
|
|
62
|
+
};
|
|
63
|
+
export default useRefResolvers;
|
|
64
|
+
//# sourceMappingURL=useRefResolvers.js.map
|