@natsuneko-laboratory/react-native-desktop-navigation 0.1.0-alpha.12

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.
Files changed (103) hide show
  1. package/DesktopNavigation.podspec +18 -0
  2. package/LICENSE +21 -0
  3. package/NATIVE.md +300 -0
  4. package/README.md +127 -0
  5. package/dist/core/NavigationContainer.d.ts +23 -0
  6. package/dist/core/NavigationContainer.js +68 -0
  7. package/dist/core/NavigationItem.d.ts +27 -0
  8. package/dist/core/NavigationItem.js +71 -0
  9. package/dist/core/Scene.d.ts +19 -0
  10. package/dist/core/Scene.js +124 -0
  11. package/dist/core/SelectionNavigator.d.ts +38 -0
  12. package/dist/core/SelectionNavigator.js +194 -0
  13. package/dist/core/builder.d.ts +40 -0
  14. package/dist/core/builder.js +81 -0
  15. package/dist/core/context.d.ts +26 -0
  16. package/dist/core/context.js +36 -0
  17. package/dist/core/focus.d.ts +14 -0
  18. package/dist/core/focus.js +39 -0
  19. package/dist/core/hooks.d.ts +8 -0
  20. package/dist/core/hooks.js +48 -0
  21. package/dist/core/navigation.d.ts +6 -0
  22. package/dist/core/navigation.js +57 -0
  23. package/dist/core/store.d.ts +39 -0
  24. package/dist/core/store.js +391 -0
  25. package/dist/core/types.d.ts +155 -0
  26. package/dist/core/types.js +2 -0
  27. package/dist/index.d.ts +15 -0
  28. package/dist/index.js +46 -0
  29. package/dist/native/host.d.ts +100 -0
  30. package/dist/native/host.js +190 -0
  31. package/dist/native/icons.d.ts +56 -0
  32. package/dist/native/icons.js +55 -0
  33. package/dist/native/index.d.ts +13 -0
  34. package/dist/native/index.js +44 -0
  35. package/dist/native/sidebar.d.ts +39 -0
  36. package/dist/native/sidebar.js +126 -0
  37. package/dist/native/specs/DesktopNavigationHostNativeComponent.d.ts +10 -0
  38. package/dist/native/specs/DesktopNavigationHostNativeComponent.js +4 -0
  39. package/dist/native/split.d.ts +27 -0
  40. package/dist/native/split.js +105 -0
  41. package/dist/native/stack.d.ts +26 -0
  42. package/dist/native/stack.js +66 -0
  43. package/dist/platform/index.d.ts +52 -0
  44. package/dist/platform/index.js +28 -0
  45. package/dist/platform/macos.d.ts +2 -0
  46. package/dist/platform/macos.js +18 -0
  47. package/dist/platform/windows.d.ts +2 -0
  48. package/dist/platform/windows.js +18 -0
  49. package/dist/routers/index.d.ts +23 -0
  50. package/dist/routers/index.js +393 -0
  51. package/dist/routers/types.d.ts +116 -0
  52. package/dist/routers/types.js +2 -0
  53. package/dist/sidebar/index.d.ts +17 -0
  54. package/dist/sidebar/index.js +18 -0
  55. package/dist/split/index.d.ts +42 -0
  56. package/dist/split/index.js +199 -0
  57. package/dist/stack/index.d.ts +23 -0
  58. package/dist/stack/index.js +88 -0
  59. package/dist/tabs/index.d.ts +13 -0
  60. package/dist/tabs/index.js +15 -0
  61. package/docs/GUIDE.md +384 -0
  62. package/macos/DDNNavigationComponentView.h +4 -0
  63. package/macos/DDNNavigationComponentView.mm +40 -0
  64. package/macos/DDNNavigationView.swift +398 -0
  65. package/macos/DDNNavigationViewManager.mm +11 -0
  66. package/package.json +82 -0
  67. package/react-native.config.js +19 -0
  68. package/src/core/NavigationContainer.tsx +137 -0
  69. package/src/core/NavigationItem.tsx +149 -0
  70. package/src/core/Scene.tsx +194 -0
  71. package/src/core/SelectionNavigator.tsx +371 -0
  72. package/src/core/builder.tsx +147 -0
  73. package/src/core/context.tsx +43 -0
  74. package/src/core/focus.tsx +50 -0
  75. package/src/core/hooks.ts +42 -0
  76. package/src/core/navigation.ts +77 -0
  77. package/src/core/store.ts +471 -0
  78. package/src/core/types.ts +176 -0
  79. package/src/index.ts +36 -0
  80. package/src/native/host.tsx +377 -0
  81. package/src/native/icons.ts +110 -0
  82. package/src/native/index.ts +56 -0
  83. package/src/native/sidebar.tsx +230 -0
  84. package/src/native/specs/DesktopNavigationHostNativeComponent.ts +12 -0
  85. package/src/native/split.tsx +160 -0
  86. package/src/native/stack.tsx +157 -0
  87. package/src/platform/index.tsx +76 -0
  88. package/src/platform/macos.ts +15 -0
  89. package/src/platform/windows.ts +15 -0
  90. package/src/routers/index.ts +442 -0
  91. package/src/routers/types.ts +117 -0
  92. package/src/sidebar/index.tsx +42 -0
  93. package/src/split/index.tsx +350 -0
  94. package/src/stack/index.tsx +213 -0
  95. package/src/tabs/index.tsx +40 -0
  96. package/windows/DesktopNavigation/DesktopNavigation.def +3 -0
  97. package/windows/DesktopNavigation/DesktopNavigation.vcxproj +125 -0
  98. package/windows/DesktopNavigation/NavigationHost.cpp +432 -0
  99. package/windows/DesktopNavigation/ReactPackageProvider.cpp +9 -0
  100. package/windows/DesktopNavigation/ReactPackageProvider.h +10 -0
  101. package/windows/DesktopNavigation/ReactPackageProvider.idl +6 -0
  102. package/windows/DesktopNavigation/pch.cpp +1 -0
  103. package/windows/DesktopNavigation/pch.h +27 -0
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createSplitNavigator = exports.createSidebarNavigator = exports.createStackNavigator = exports.restoreNavigationState = exports.serializeNavigationState = exports.NavigationStore = exports.useNavigationTheme = exports.DefaultTheme = exports.NavigationFocusable = exports.useIsFocused = exports.useFocusEffect = exports.useNavigationState = exports.useRoute = exports.useNavigation = exports.createNavigationRef = exports.NavigationContainer = void 0;
18
+ // Same context, Store, Router and hooks as the JS-rendered navigators.
19
+ var NavigationContainer_1 = require("../core/NavigationContainer");
20
+ Object.defineProperty(exports, "NavigationContainer", { enumerable: true, get: function () { return NavigationContainer_1.NavigationContainer; } });
21
+ var navigation_1 = require("../core/navigation");
22
+ Object.defineProperty(exports, "createNavigationRef", { enumerable: true, get: function () { return navigation_1.createNavigationRef; } });
23
+ var hooks_1 = require("../core/hooks");
24
+ Object.defineProperty(exports, "useNavigation", { enumerable: true, get: function () { return hooks_1.useNavigation; } });
25
+ Object.defineProperty(exports, "useRoute", { enumerable: true, get: function () { return hooks_1.useRoute; } });
26
+ Object.defineProperty(exports, "useNavigationState", { enumerable: true, get: function () { return hooks_1.useNavigationState; } });
27
+ Object.defineProperty(exports, "useFocusEffect", { enumerable: true, get: function () { return hooks_1.useFocusEffect; } });
28
+ Object.defineProperty(exports, "useIsFocused", { enumerable: true, get: function () { return hooks_1.useIsFocused; } });
29
+ var focus_1 = require("../core/focus");
30
+ Object.defineProperty(exports, "NavigationFocusable", { enumerable: true, get: function () { return focus_1.NavigationFocusable; } });
31
+ var context_1 = require("../core/context");
32
+ Object.defineProperty(exports, "DefaultTheme", { enumerable: true, get: function () { return context_1.DefaultTheme; } });
33
+ Object.defineProperty(exports, "useNavigationTheme", { enumerable: true, get: function () { return context_1.useNavigationTheme; } });
34
+ var store_1 = require("../core/store");
35
+ Object.defineProperty(exports, "NavigationStore", { enumerable: true, get: function () { return store_1.NavigationStore; } });
36
+ Object.defineProperty(exports, "serializeNavigationState", { enumerable: true, get: function () { return store_1.serializeNavigationState; } });
37
+ Object.defineProperty(exports, "restoreNavigationState", { enumerable: true, get: function () { return store_1.restoreNavigationState; } });
38
+ __exportStar(require("../routers"), exports);
39
+ var stack_1 = require("./stack");
40
+ Object.defineProperty(exports, "createStackNavigator", { enumerable: true, get: function () { return stack_1.createStackNavigator; } });
41
+ var sidebar_1 = require("./sidebar");
42
+ Object.defineProperty(exports, "createSidebarNavigator", { enumerable: true, get: function () { return sidebar_1.createSidebarNavigator; } });
43
+ var split_1 = require("./split");
44
+ Object.defineProperty(exports, "createSplitNavigator", { enumerable: true, get: function () { return split_1.createSplitNavigator; } });
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ import type { ParamListBase, RouteName } from '../routers';
4
+ import { type ScreenConfig } from '../core/builder';
5
+ import type { CommonScreenOptions, SidebarFooterProps, SidebarNavigation } from '../core/types';
6
+ import { type NativeAppearance } from './host';
7
+ import { type NativeSidebarIconOption } from './icons';
8
+ export interface NativeSidebarScreenOptions extends CommonScreenOptions {
9
+ label?: string;
10
+ icon?: NativeSidebarIconOption;
11
+ /** Square slot size for React icons, in points / DIPs (default 24). */
12
+ iconSize?: number;
13
+ hidden?: boolean;
14
+ disabled?: boolean;
15
+ /** Native item badge. Numbers <= 0 and empty strings hide it. */
16
+ badge?: number | string;
17
+ }
18
+ export interface NativeSidebarNavigatorProps {
19
+ children: React.ReactNode;
20
+ id?: string;
21
+ initialRouteName?: string;
22
+ defaultCollapsed?: boolean;
23
+ width?: number;
24
+ style?: StyleProp<ViewStyle>;
25
+ appearance?: NativeAppearance;
26
+ screenOptions?: NativeSidebarScreenOptions;
27
+ /** React content below the native item list. `children` is always null. */
28
+ renderSidebarFooter?: (props: SidebarFooterProps) => React.ReactNode;
29
+ }
30
+ export declare function createSidebarNavigator<P extends ParamListBase = ParamListBase>(): {
31
+ Navigator: (props: Omit<NativeSidebarNavigatorProps, "initialRouteName"> & {
32
+ initialRouteName?: RouteName<P>;
33
+ }) => React.JSX.Element;
34
+ Screen: <N extends RouteName<P>>(_props: ScreenConfig<P, N, NativeSidebarScreenOptions, SidebarNavigation<P>>) => null;
35
+ Section: (_props: {
36
+ title?: string;
37
+ children: React.ReactNode;
38
+ }) => null;
39
+ };
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createSidebarNavigator = createSidebarNavigator;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = __importDefault(require("react"));
9
+ const builder_1 = require("../core/builder");
10
+ const Scene_1 = require("../core/Scene");
11
+ const navigation_1 = require("../core/navigation");
12
+ const host_1 = require("./host");
13
+ const icons_1 = require("./icons");
14
+ function resolveBadge(badge) {
15
+ if (typeof badge === 'number')
16
+ return Number.isFinite(badge) && badge > 0 ? String(badge) : undefined;
17
+ return typeof badge === 'string' && badge !== '' ? badge : undefined;
18
+ }
19
+ function createSidebarNavigator() {
20
+ function Screen(_props) {
21
+ return null;
22
+ }
23
+ function Section(_props) {
24
+ return null;
25
+ }
26
+ function Navigator(props) {
27
+ if (props.width !== undefined &&
28
+ (!Number.isFinite(props.width) || props.width < 100))
29
+ throw new Error('Native sidebar width must be a finite number of at least 100.');
30
+ const definitions = (0, builder_1.readScreens)(props.children, Screen, Section);
31
+ const { id, state, store, navigation } = (0, builder_1.useNavigator)('sidebar', {
32
+ routes: definitions.map((d) => ({
33
+ ...d,
34
+ ...props.screenOptions,
35
+ ...(typeof d.options === 'object' ? d.options : {}),
36
+ })),
37
+ initialRouteName: props.initialRouteName,
38
+ defaultCollapsed: props.defaultCollapsed,
39
+ }, props.id);
40
+ const [footerHeight, setFooterHeight] = react_1.default.useState(0);
41
+ const collapseSidebar = () => store.dispatch({ target: id, type: 'collapseSidebar' });
42
+ const expandSidebar = () => store.dispatch({ target: id, type: 'expandSidebar' });
43
+ const footer = props.renderSidebarFooter?.({
44
+ collapsed: state.collapsed,
45
+ collapseSidebar,
46
+ expandSidebar,
47
+ toggleSidebar: () => state.collapsed ? expandSidebar() : collapseSidebar(),
48
+ children: null,
49
+ });
50
+ const hasFooter = footer != null && footer !== false;
51
+ const items = state.routes.map((route) => {
52
+ const definition = definitions.find((d) => d.name === route.name);
53
+ if (!definition)
54
+ throw new Error(`Missing Screen: ${route.name}. Remount to change the screen list.`);
55
+ const options = (0, builder_1.resolveOptions)(definition, route, (0, navigation_1.createNavigation)(store, id, route.key), props.screenOptions);
56
+ const iconState = {
57
+ focused: route.key === state.activeRouteKey,
58
+ disabled: options.disabled ?? false,
59
+ };
60
+ const icon = typeof options.icon === 'function'
61
+ ? options.icon(iconState)
62
+ : options.icon;
63
+ return {
64
+ route,
65
+ definition,
66
+ options,
67
+ icon,
68
+ nativeIcon: (0, icons_1.resolveNativeIcon)(icon, iconState, options.iconSize),
69
+ };
70
+ });
71
+ react_1.default.useLayoutEffect(() => {
72
+ store.updateOptions(id, {
73
+ key: id,
74
+ routes: items.map((i) => ({ name: i.route.name, ...i.options })),
75
+ });
76
+ const active = items.find((i) => i.route.key === state.activeRouteKey);
77
+ if (active?.options.disabled || active?.options.hidden) {
78
+ const next = items.find((i) => !i.options.disabled && !i.options.hidden);
79
+ if (next)
80
+ navigation.select(next.route.name);
81
+ }
82
+ });
83
+ return ((0, jsx_runtime_1.jsx)(builder_1.NavigatorStateContext.Provider, { value: state, children: (0, jsx_runtime_1.jsx)(host_1.NativeSurface, { style: props.style, configuration: {
84
+ mode: 'sidebar',
85
+ items: items.map((i) => ({
86
+ key: i.route.key,
87
+ title: i.options.label ?? i.options.title ?? i.route.name,
88
+ hidden: i.options.hidden,
89
+ disabled: i.options.disabled,
90
+ section: i.definition.section?.title,
91
+ icon: i.nativeIcon,
92
+ badge: resolveBadge(i.options.badge),
93
+ })),
94
+ activeKey: state.activeRouteKey,
95
+ collapsed: state.collapsed,
96
+ paneWidth: props.width ?? 240,
97
+ footerHeight: hasFooter && footerHeight > 0 ? footerHeight : undefined,
98
+ appearance: props.appearance,
99
+ }, footer: hasFooter ? footer : undefined, onFooterHeight: (height) => setFooterHeight((previous) =>
100
+ // Sub-point changes would only churn native revisions.
101
+ Math.abs(previous - height) < 0.5 ? previous : Math.ceil(height)), icons: items.flatMap((i) => react_1.default.isValidElement(i.icon) && !i.options.hidden
102
+ ? [{ key: i.route.key, content: i.icon }]
103
+ : []), onRequest: (request) => {
104
+ if (request.type === 'collapse')
105
+ request.collapsed ? collapseSidebar() : expandSidebar();
106
+ if (request.type === 'select') {
107
+ const item = items.find((i) => i.route.key === request.key &&
108
+ !i.options.hidden &&
109
+ !i.options.disabled);
110
+ if (item) {
111
+ store.setFocusedNode(id);
112
+ navigation.select(item.route.name);
113
+ }
114
+ }
115
+ }, slots: items.map(({ route, definition, options }) => ({
116
+ key: route.key,
117
+ visible: route.key === state.activeRouteKey &&
118
+ !options.hidden &&
119
+ !options.disabled,
120
+ content: (ready) => ((0, jsx_runtime_1.jsx)(Scene_1.Scene, { focusReady: ready, nodeId: id, route: route, component: definition.component, visible: route.key === state.activeRouteKey &&
121
+ !options.hidden &&
122
+ !options.disabled, options: options })),
123
+ })) }) }));
124
+ }
125
+ return { Navigator, Screen, Section };
126
+ }
@@ -0,0 +1,10 @@
1
+ import type { HostComponent, ViewProps } from 'react-native';
2
+ import type { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
3
+ export interface NativeProps extends ViewProps {
4
+ configuration: string;
5
+ onNavigationEvent?: DirectEventHandler<Readonly<{
6
+ payload: string;
7
+ }>>;
8
+ }
9
+ declare const _default: HostComponent<NativeProps>;
10
+ export default _default;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_native_1 = require("react-native");
4
+ exports.default = (0, react_native_1.codegenNativeComponent)('DesktopNavigationHost');
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import { type StyleProp, type ViewStyle } from 'react-native';
3
+ import { type NativeAppearance } from './host';
4
+ export interface NativeSplitColumnProps {
5
+ id: string;
6
+ component: React.ComponentType;
7
+ defaultWidth?: number;
8
+ minWidth?: number;
9
+ maxWidth?: number;
10
+ style?: StyleProp<ViewStyle>;
11
+ contentStyle?: StyleProp<ViewStyle>;
12
+ }
13
+ export interface NativeSplitNavigatorProps {
14
+ children: React.ReactNode;
15
+ id?: string;
16
+ style?: StyleProp<ViewStyle>;
17
+ appearance?: NativeAppearance;
18
+ layout?: (size: {
19
+ width: number;
20
+ height: number;
21
+ }) => string[];
22
+ onColumnResize?: (id: string, width: number) => void;
23
+ }
24
+ export declare function createSplitNavigator(): {
25
+ Navigator: (props: NativeSplitNavigatorProps) => React.JSX.Element;
26
+ Column: (_props: NativeSplitColumnProps) => null;
27
+ };
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createSplitNavigator = createSplitNavigator;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = __importDefault(require("react"));
9
+ const react_native_1 = require("react-native");
10
+ const routers_1 = require("../routers");
11
+ const builder_1 = require("../core/builder");
12
+ const Scene_1 = require("../core/Scene");
13
+ const context_1 = require("../core/context");
14
+ const host_1 = require("./host");
15
+ function createSplitNavigator() {
16
+ function Column(_props) {
17
+ return null;
18
+ }
19
+ function Navigator(props) {
20
+ const columns = [];
21
+ react_1.default.Children.forEach(props.children, (child) => {
22
+ if (child == null || typeof child === 'boolean')
23
+ return;
24
+ if (!react_1.default.isValidElement(child) || child.type !== Column)
25
+ throw new Error('Split.Navigator children must be Split.Column elements.');
26
+ columns.push(child.props);
27
+ });
28
+ const { id, state, store } = (0, builder_1.useNavigator)('split', { routes: columns.map((c) => ({ ...c, name: c.id })) }, props.id);
29
+ const [size, setSize] = react_1.default.useState(null);
30
+ react_1.default.useLayoutEffect(() => {
31
+ if (!size)
32
+ return;
33
+ const ids = props.layout ? props.layout(size) : columns.map((c) => c.id);
34
+ store.dispatch({ type: 'layout', target: id, payload: { ids } });
35
+ });
36
+ return ((0, jsx_runtime_1.jsx)(builder_1.NavigatorStateContext.Provider, { value: state, children: (0, jsx_runtime_1.jsx)(host_1.NativeSurface, { style: props.style, onMeasurements: (frames) => {
37
+ const before = store.getNode(id)?.state;
38
+ if (!before)
39
+ return;
40
+ const widths = Object.fromEntries(before.visibleColumnIds
41
+ .filter((key) => frames[key]?.width > 0)
42
+ .map((key) => [key, frames[key].width]));
43
+ store.dispatch({
44
+ type: 'layout',
45
+ target: id,
46
+ payload: { ids: before.visibleColumnIds, widths },
47
+ });
48
+ const after = store.getNode(id).state;
49
+ for (const key of before.visibleColumnIds)
50
+ if (after.widths[key] !== before.widths[key])
51
+ props.onColumnResize?.(key, after.widths[key]);
52
+ }, onLayout: (event) => {
53
+ const { width, height } = event.nativeEvent.layout;
54
+ setSize((previous) => previous?.width === width && previous.height === height
55
+ ? previous
56
+ : { width, height });
57
+ }, configuration: {
58
+ mode: 'split',
59
+ items: columns.map((c) => ({ key: c.id, title: c.id })),
60
+ activeKey: state.activeRouteKey,
61
+ appearance: props.appearance,
62
+ columns: state.visibleColumnIds.map((key) => {
63
+ const c = columns.find((c) => c.id === key);
64
+ const constraints = (0, routers_1.splitColumnConfig)(state, {
65
+ ...c,
66
+ name: c.id,
67
+ });
68
+ return {
69
+ key,
70
+ width: state.widths[key],
71
+ minWidth: constraints.minWidth ?? 100,
72
+ maxWidth: constraints.maxWidth,
73
+ };
74
+ }),
75
+ }, onRequest: (request) => {
76
+ if (request.type !== 'resize' ||
77
+ !state.visibleColumnIds.includes(request.key))
78
+ return;
79
+ const before = store.getNode(id).state;
80
+ store.dispatch({
81
+ type: 'resize',
82
+ target: id,
83
+ payload: { id: request.key, width: request.width },
84
+ });
85
+ const after = store.getNode(id).state;
86
+ if (before.widths[request.key] !== after.widths[request.key])
87
+ props.onColumnResize?.(request.key, after.widths[request.key]);
88
+ }, slots: state.routes.map((route) => {
89
+ const column = columns.find((c) => c.id === route.name);
90
+ if (!column)
91
+ throw new Error(`Missing Column: ${route.name}. Remount to change the column list.`);
92
+ const visible = state.visibleColumnIds.includes(route.name);
93
+ return {
94
+ key: route.name,
95
+ visible,
96
+ content: (ready) => ((0, jsx_runtime_1.jsx)(context_1.SplitColumnContext.Provider, { value: null, children: (0, jsx_runtime_1.jsx)(context_1.ScopeContext.Provider, { value: { nodeId: id, routeKey: route.key }, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [{ flex: 1 }, column.style], children: (0, jsx_runtime_1.jsx)(Scene_1.Scene, { focusReady: ready, nodeId: id, route: route, component: column.component, visible: visible, options: {
97
+ focusBehavior: 'none',
98
+ inactiveBehavior: 'keep',
99
+ contentStyle: column.contentStyle,
100
+ } }) }) }) })),
101
+ };
102
+ }) }) }));
103
+ }
104
+ return { Navigator, Column };
105
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import type { HistoryBehavior, ParamListBase, RouteName } from '../routers';
3
+ import { type ScreenConfig } from '../core/builder';
4
+ import type { StackNavigation, StackScreenOptions } from '../core/types';
5
+ import { type NativeAppearance } from './host';
6
+ import type { StyleProp, ViewStyle } from 'react-native';
7
+ /** Native stacks currently support card presentation without RN entrance animations. */
8
+ export type NativeStackScreenOptions = Omit<StackScreenOptions, 'animation' | 'presentation' | 'overlayStyle' | 'dialogStyle'>;
9
+ export interface NativeStackNavigatorProps {
10
+ children: React.ReactNode;
11
+ id?: string;
12
+ initialRouteName?: string;
13
+ historyBehavior?: HistoryBehavior;
14
+ screenOptions?: NativeStackScreenOptions | ((context: {
15
+ navigation: StackNavigation;
16
+ route: import('../routers').NavigationRoute;
17
+ }) => NativeStackScreenOptions);
18
+ style?: StyleProp<ViewStyle>;
19
+ appearance?: NativeAppearance;
20
+ }
21
+ export declare function createStackNavigator<P extends ParamListBase = ParamListBase>(): {
22
+ Navigator: (props: Omit<NativeStackNavigatorProps, "initialRouteName"> & {
23
+ initialRouteName?: RouteName<P>;
24
+ }) => React.JSX.Element;
25
+ Screen: <N extends RouteName<P>>(_props: ScreenConfig<P, N, NativeStackScreenOptions, StackNavigation<P>>) => null;
26
+ };
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createStackNavigator = createStackNavigator;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = __importDefault(require("react"));
9
+ const builder_1 = require("../core/builder");
10
+ const Scene_1 = require("../core/Scene");
11
+ const navigation_1 = require("../core/navigation");
12
+ const stack_1 = require("../stack");
13
+ const host_1 = require("./host");
14
+ function createStackNavigator() {
15
+ function Screen(_props) {
16
+ return null;
17
+ }
18
+ function Navigator(props) {
19
+ const definitions = (0, builder_1.readScreens)(props.children, Screen);
20
+ const { id, state, store } = (0, builder_1.useNavigator)('stack', {
21
+ routes: definitions,
22
+ initialRouteName: props.initialRouteName,
23
+ historyBehavior: props.historyBehavior,
24
+ }, props.id);
25
+ const snapshot = react_1.default.useCallback(() => store.canDispatch('back', id), [store, id]);
26
+ const canGoBack = react_1.default.useSyncExternalStore(store.subscribe, snapshot, snapshot);
27
+ const scenes = state.routes.map((route) => {
28
+ const definition = definitions.find((d) => d.name === route.name);
29
+ if (!definition)
30
+ throw new Error(`Missing Screen: ${route.name}. Remount to change the screen list.`);
31
+ const navigation = (0, navigation_1.createNavigation)(store, id, route.key);
32
+ const options = (0, builder_1.resolveOptions)(definition, route, navigation, props.screenOptions);
33
+ // Any RN header customization selects the existing customizable header.
34
+ const customHeader = Object.keys(options).some((key) => key.startsWith('header') &&
35
+ !['headerShown', 'headerBackTitle'].includes(key));
36
+ return { route, definition, navigation, options, customHeader };
37
+ });
38
+ const active = scenes.find((scene) => scene.route.key === state.activeRouteKey);
39
+ return ((0, jsx_runtime_1.jsx)(builder_1.NavigatorStateContext.Provider, { value: state, children: (0, jsx_runtime_1.jsx)(host_1.NativeSurface, { style: props.style, configuration: {
40
+ mode: 'stack',
41
+ items: scenes.map((s) => ({
42
+ key: s.route.key,
43
+ title: s.options.title ?? s.route.name,
44
+ })),
45
+ activeKey: state.activeRouteKey,
46
+ appearance: props.appearance,
47
+ headerShown: active.options.headerShown !== false && !active.customHeader,
48
+ canGoBack,
49
+ backTitle: active.options.headerBackTitle ?? 'Back',
50
+ }, onRequest: (request) => {
51
+ if (request.type === 'back')
52
+ store.dispatch({ type: 'back', target: id });
53
+ if (request.type === 'pop' && request.count < state.routes.length)
54
+ store.dispatch({
55
+ type: 'pop',
56
+ target: id,
57
+ payload: { count: request.count },
58
+ });
59
+ }, slots: scenes.map(({ route, definition, navigation, options, customHeader }) => ({
60
+ key: route.key,
61
+ visible: route.key === state.activeRouteKey,
62
+ content: (ready) => ((0, jsx_runtime_1.jsx)(Scene_1.Scene, { focusReady: ready, nodeId: id, route: route, component: definition.component, visible: route.key === state.activeRouteKey, options: options, children: customHeader && ((0, jsx_runtime_1.jsx)(stack_1.StackHeader, { route: route, navigation: navigation, options: options, canGoBack: canGoBack })) })),
63
+ })) }) }));
64
+ }
65
+ return { Navigator, Screen };
66
+ }
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import { View, type ViewProps } from 'react-native';
3
+ export interface KeyboardShortcut {
4
+ key: string;
5
+ metaKey?: boolean;
6
+ ctrlKey?: boolean;
7
+ altKey?: boolean;
8
+ shiftKey?: boolean;
9
+ }
10
+ export interface DesktopKeyEvent {
11
+ nativeEvent: KeyboardShortcut & {
12
+ handled?: boolean;
13
+ isTextInput?: boolean;
14
+ target?: unknown;
15
+ };
16
+ defaultPrevented?: boolean;
17
+ isPropagationStopped?: () => boolean;
18
+ preventDefault?: () => void;
19
+ stopPropagation?: () => void;
20
+ }
21
+ export interface TransitionPreset {
22
+ duration: number;
23
+ offset: number;
24
+ }
25
+ export interface NavigationPlatformAdapter {
26
+ defaultTransitions: {
27
+ push: TransitionPreset;
28
+ pop: TransitionPreset;
29
+ };
30
+ keyboardShortcuts: {
31
+ back: KeyboardShortcut[];
32
+ forward: KeyboardShortcut[];
33
+ nextTab: KeyboardShortcut[];
34
+ previousTab: KeyboardShortcut[];
35
+ };
36
+ sidebar: {
37
+ defaultWidth: number;
38
+ };
39
+ isTextInputEvent(event: DesktopKeyEvent): boolean;
40
+ }
41
+ export declare const defaultPlatformAdapter: NavigationPlatformAdapter;
42
+ export declare function isTextInputEvent(event: DesktopKeyEvent): boolean;
43
+ export declare function matchesShortcut(event: DesktopKeyEvent, shortcut: KeyboardShortcut): boolean;
44
+ export declare const eventHandled: (event: DesktopKeyEvent) => boolean;
45
+ export declare function consumeKey(event: DesktopKeyEvent): void;
46
+ export interface DesktopViewProps extends ViewProps {
47
+ onKeyDown?: (event: DesktopKeyEvent) => void;
48
+ keyDownEvents?: KeyboardShortcut[];
49
+ onFocus?: () => void;
50
+ focusable?: boolean;
51
+ }
52
+ export declare const DesktopView: React.ComponentType<DesktopViewProps & React.RefAttributes<View>>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DesktopView = exports.eventHandled = exports.defaultPlatformAdapter = void 0;
4
+ exports.isTextInputEvent = isTextInputEvent;
5
+ exports.matchesShortcut = matchesShortcut;
6
+ exports.consumeKey = consumeKey;
7
+ const react_native_1 = require("react-native");
8
+ const macos_1 = require("./macos");
9
+ const windows_1 = require("./windows");
10
+ exports.defaultPlatformAdapter = react_native_1.Platform.OS === 'windows' ? windows_1.windowsAdapter : macos_1.macosAdapter;
11
+ function isTextInputEvent(event) {
12
+ return (event.nativeEvent.isTextInput === true ||
13
+ react_native_1.TextInput.State.currentlyFocusedInput() != null);
14
+ }
15
+ function matchesShortcut(event, shortcut) {
16
+ return (event.nativeEvent.key === shortcut.key &&
17
+ ['metaKey', 'ctrlKey', 'altKey', 'shiftKey'].every((key) => !!event.nativeEvent[key] === !!shortcut[key]));
18
+ }
19
+ const eventHandled = (event) => !!(event.defaultPrevented ||
20
+ event.nativeEvent.handled ||
21
+ event.isPropagationStopped?.());
22
+ exports.eventHandled = eventHandled;
23
+ function consumeKey(event) {
24
+ event.preventDefault?.();
25
+ event.stopPropagation?.();
26
+ }
27
+ // RN desktop adds these native View props. Keep the platform type extension in one place.
28
+ exports.DesktopView = react_native_1.View;
@@ -0,0 +1,2 @@
1
+ import { type NavigationPlatformAdapter } from './index';
2
+ export declare const macosAdapter: NavigationPlatformAdapter;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.macosAdapter = void 0;
4
+ const index_1 = require("./index");
5
+ exports.macosAdapter = {
6
+ defaultTransitions: {
7
+ push: { duration: 160, offset: 12 },
8
+ pop: { duration: 120, offset: -12 },
9
+ },
10
+ keyboardShortcuts: {
11
+ back: [{ key: '[', metaKey: true }],
12
+ forward: [{ key: ']', metaKey: true }],
13
+ nextTab: [{ key: 'Tab', ctrlKey: true }],
14
+ previousTab: [{ key: 'Tab', ctrlKey: true, shiftKey: true }],
15
+ },
16
+ sidebar: { defaultWidth: 220 },
17
+ isTextInputEvent: index_1.isTextInputEvent,
18
+ };
@@ -0,0 +1,2 @@
1
+ import { type NavigationPlatformAdapter } from './index';
2
+ export declare const windowsAdapter: NavigationPlatformAdapter;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.windowsAdapter = void 0;
4
+ const index_1 = require("./index");
5
+ exports.windowsAdapter = {
6
+ defaultTransitions: {
7
+ push: { duration: 160, offset: 16 },
8
+ pop: { duration: 120, offset: -16 },
9
+ },
10
+ keyboardShortcuts: {
11
+ back: [{ key: 'ArrowLeft', altKey: true }],
12
+ forward: [{ key: 'ArrowRight', altKey: true }],
13
+ nextTab: [{ key: 'Tab', ctrlKey: true }],
14
+ previousTab: [{ key: 'Tab', ctrlKey: true, shiftKey: true }],
15
+ },
16
+ sidebar: { defaultWidth: 240 },
17
+ isTextInputEvent: index_1.isTextInputEvent,
18
+ };
@@ -0,0 +1,23 @@
1
+ import type { NavigationState, Router, RouterOptions, StackNavigationState, SelectionNavigationState, SplitNavigationState, NavigationAction, RouteConfig } from './types';
2
+ export * from './types';
3
+ export declare const createRouteKey: (name: string) => string;
4
+ export declare function validateRoutes(options: RouterOptions): void;
5
+ export declare const StackRouter: Router<StackNavigationState>;
6
+ export declare const SidebarRouter: Router<SelectionNavigationState>;
7
+ export declare const TabRouter: Router<SelectionNavigationState>;
8
+ export declare function clampColumnWidth(width: number, config: {
9
+ minWidth?: number;
10
+ maxWidth?: number;
11
+ }): number;
12
+ /** Fit visible columns to the available content width (excluding divider widths). */
13
+ export declare function fitSplitWidths(columns: RouterOptions['routes'], widths: Record<string, number>, availableWidth: number): Record<string, number>;
14
+ /** Collapsed columns stay at their Sidebar width, even below the expanded minimum. */
15
+ export declare function splitColumnConfig(state: SplitNavigationState, config: RouteConfig): RouteConfig;
16
+ export declare const SplitRouter: Router<SplitNavigationState>;
17
+ export declare const routers: {
18
+ stack: Router<StackNavigationState>;
19
+ sidebar: Router<SelectionNavigationState>;
20
+ tabs: Router<SelectionNavigationState>;
21
+ split: Router<SplitNavigationState>;
22
+ };
23
+ export declare function reduceNavigation(state: NavigationState, action: NavigationAction, options: RouterOptions): NavigationState | null;