first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
import type { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import type { CompositionContextValue, CompositionRegistry } from './types';
/** @internal */
export declare const CompositionContext: import("react").Context<CompositionContextValue | null>;
type RegistryAction = {
type: 'set';
routeKey: string;
options: Partial<NativeStackNavigationOptions>;
} | {
type: 'unset';
routeKey: string;
options: Partial<NativeStackNavigationOptions>;
};
/** @internal */
export declare function registryReducer(state: CompositionRegistry, action: RegistryAction): CompositionRegistry;
/**
* Provides the composition registry to descendant composition components.
*
* Uses useReducer with immutable object updates for React Compiler compatibility.
* Each set/unset call produces a new object reference, which the compiler can
* track as a reactive dependency.
*/
export declare function useCompositionRegistry(): {
registry: CompositionRegistry;
contextValue: {
set: (routeKey: string, options: Partial<NativeStackNavigationOptions>) => void;
unset: (routeKey: string, options: Partial<NativeStackNavigationOptions>) => void;
};
};
/**
* Hook used by composition components to register their options in the composition registry.
*
* Registers options on mount/update via useSafeLayoutEffect, and unregisters on unmount.
* Callers should memoize the options object to avoid unnecessary re-registrations.
*/
export declare function useCompositionOption(options: Partial<NativeStackNavigationOptions>): void;
export {};
//# sourceMappingURL=CompositionOptionsContext.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CompositionOptionsContext.d.ts","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/CompositionOptionsContext.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAGnF,OAAO,KAAK,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAG5E,gBAAgB;AAChB,eAAO,MAAM,kBAAkB,yDAAsD,CAAC;AAEtF,KAAK,cAAc,GACf;IACE,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAChD,GACD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAA;CAAE,CAAC;AAExF,gBAAgB;AAChB,wBAAgB,eAAe,CAC7B,KAAK,EAAE,mBAAmB,EAC1B,MAAM,EAAE,cAAc,GACrB,mBAAmB,CAuBrB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB;;;wBAGD,MAAM,WAAW,OAAO,CAAC,4BAA4B,CAAC;0BAIpD,MAAM,WAAW,OAAO,CAAC,4BAA4B,CAAC;;EAS5F;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,QAiBlF"}

View File

@@ -0,0 +1,75 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompositionContext = void 0;
exports.registryReducer = registryReducer;
exports.useCompositionRegistry = useCompositionRegistry;
exports.useCompositionOption = useCompositionOption;
const native_1 = require("@react-navigation/native");
const react_1 = require("react");
const useSafeLayoutEffect_1 = require("../../../views/useSafeLayoutEffect");
/** @internal */
exports.CompositionContext = (0, react_1.createContext)(null);
/** @internal */
function registryReducer(state, action) {
if (action.type === 'set') {
const { routeKey, options } = action;
if (state[routeKey]?.includes(options)) {
return state;
}
return { ...state, [routeKey]: [...(state[routeKey] ?? []), options] };
}
if (action.type === 'unset') {
const { routeKey, options } = action;
const existing = state[routeKey];
const filtered = existing?.filter((o) => o !== options);
if (!existing || filtered?.length === existing.length) {
return state;
}
if (filtered.length === 0) {
const { [routeKey]: _, ...newState } = state;
return newState;
}
return { ...state, [routeKey]: filtered };
}
return state;
}
/**
* Provides the composition registry to descendant composition components.
*
* Uses useReducer with immutable object updates for React Compiler compatibility.
* Each set/unset call produces a new object reference, which the compiler can
* track as a reactive dependency.
*/
function useCompositionRegistry() {
const [registry, dispatch] = (0, react_1.useReducer)(registryReducer, {});
const set = (0, react_1.useCallback)((routeKey, options) => {
dispatch({ type: 'set', routeKey, options });
}, []);
const unset = (0, react_1.useCallback)((routeKey, options) => {
dispatch({ type: 'unset', routeKey, options });
}, []);
const contextValue = (0, react_1.useMemo)(() => ({ set, unset }), [set, unset]);
return { registry, contextValue };
}
/**
* Hook used by composition components to register their options in the composition registry.
*
* Registers options on mount/update via useSafeLayoutEffect, and unregisters on unmount.
* Callers should memoize the options object to avoid unnecessary re-registrations.
*/
function useCompositionOption(options) {
const context = (0, react_1.use)(exports.CompositionContext);
if (!context) {
throw new Error('useCompositionOption must be used within a RouterCompositionOptionsProvider. This is likely a bug in Expo Router.');
}
const route = (0, native_1.useRoute)();
const { set, unset } = context;
(0, useSafeLayoutEffect_1.useSafeLayoutEffect)(() => {
set(route.key, options);
return () => {
unset(route.key, options);
};
}, [route.key, set, unset, options]);
}
//# sourceMappingURL=CompositionOptionsContext.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
export { mergeOptions } from './mergeOptions';
export { CompositionContext, useCompositionRegistry, useCompositionOption, } from './CompositionOptionsContext';
export type { CompositionRegistry, CompositionContextValue } from './types';
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC"}

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useCompositionOption = exports.useCompositionRegistry = exports.CompositionContext = exports.mergeOptions = void 0;
var mergeOptions_1 = require("./mergeOptions");
Object.defineProperty(exports, "mergeOptions", { enumerable: true, get: function () { return mergeOptions_1.mergeOptions; } });
var CompositionOptionsContext_1 = require("./CompositionOptionsContext");
Object.defineProperty(exports, "CompositionContext", { enumerable: true, get: function () { return CompositionOptionsContext_1.CompositionContext; } });
Object.defineProperty(exports, "useCompositionRegistry", { enumerable: true, get: function () { return CompositionOptionsContext_1.useCompositionRegistry; } });
Object.defineProperty(exports, "useCompositionOption", { enumerable: true, get: function () { return CompositionOptionsContext_1.useCompositionOption; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/index.ts"],"names":[],"mappings":";;;AAAA,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,yEAIqC;AAHnC,+HAAA,kBAAkB,OAAA;AAClB,mIAAA,sBAAsB,OAAA;AACtB,iIAAA,oBAAoB,OAAA","sourcesContent":["export { mergeOptions } from './mergeOptions';\nexport {\n CompositionContext,\n useCompositionRegistry,\n useCompositionOption,\n} from './CompositionOptionsContext';\nexport type { CompositionRegistry, CompositionContextValue } from './types';\n"]}

View File

@@ -0,0 +1,13 @@
import type { ParamListBase, StackNavigationState } from '@react-navigation/native';
import type { NativeStackDescriptorMap } from '../descriptors-context';
import type { CompositionRegistry } from './types';
/**
* Merges composition component options into navigation descriptors.
*
* For each descriptor:
* 1. If no composition options registered → pass through unchanged
* 2. If route is preloaded AND not focused → skip composition (pass through)
* 3. Otherwise → merge descriptor.options with composition options (composition wins)
*/
export declare function mergeOptions(descriptors: NativeStackDescriptorMap, registry: CompositionRegistry, state: StackNavigationState<ParamListBase>): NativeStackDescriptorMap;
//# sourceMappingURL=mergeOptions.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"mergeOptions.d.ts","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/mergeOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEpF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,wBAAwB,EACrC,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,oBAAoB,CAAC,aAAa,CAAC,GACzC,wBAAwB,CAiC1B"}

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeOptions = mergeOptions;
/**
* Merges composition component options into navigation descriptors.
*
* For each descriptor:
* 1. If no composition options registered → pass through unchanged
* 2. If route is preloaded AND not focused → skip composition (pass through)
* 3. Otherwise → merge descriptor.options with composition options (composition wins)
*/
function mergeOptions(descriptors, registry, state) {
const result = {};
const focusedKey = state.routes[state.index]?.key;
for (const key in descriptors) {
const descriptor = descriptors[key];
const routeOptions = registry[key];
// No composition options or empty array → pass through
if (!routeOptions || routeOptions.length === 0) {
result[key] = descriptor;
continue;
}
// Check if route is preloaded and not focused → skip composition
const isPreloaded = state.preloadedRoutes?.some((r) => r.key === key) ?? false;
if (isPreloaded && key !== focusedKey) {
result[key] = descriptor;
continue;
}
// Merge: descriptor options as base, composition options override
const mergedOptions = Object.assign({}, descriptor.options, ...routeOptions);
const merged = {
...descriptor,
options: mergedOptions,
};
result[key] = merged;
}
return result;
}
//# sourceMappingURL=mergeOptions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"mergeOptions.js","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/mergeOptions.ts"],"names":[],"mappings":";;AAaA,oCAqCC;AA7CD;;;;;;;GAOG;AACH,SAAgB,YAAY,CAC1B,WAAqC,EACrC,QAA6B,EAC7B,KAA0C;IAE1C,MAAM,MAAM,GAA6B,EAAE,CAAC;IAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC;IAElD,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAEnC,uDAAuD;QACvD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;YACzB,SAAS;QACX,CAAC;QAED,iEAAiE;QACjE,MAAM,WAAW,GAAG,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC;QAC/E,IAAI,WAAW,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACtC,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;YACzB,SAAS;QACX,CAAC;QAED,kEAAkE;QAClE,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,YAAY,CAAC,CAAC;QAE7E,MAAM,MAAM,GAAG;YACb,GAAG,UAAU;YACb,OAAO,EAAE,aAAa;SACvB,CAAC;QAEF,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { ParamListBase, StackNavigationState } from '@react-navigation/native';\n\nimport type { NativeStackDescriptorMap } from '../descriptors-context';\nimport type { CompositionRegistry } from './types';\n\n/**\n * Merges composition component options into navigation descriptors.\n *\n * For each descriptor:\n * 1. If no composition options registered → pass through unchanged\n * 2. If route is preloaded AND not focused → skip composition (pass through)\n * 3. Otherwise → merge descriptor.options with composition options (composition wins)\n */\nexport function mergeOptions(\n descriptors: NativeStackDescriptorMap,\n registry: CompositionRegistry,\n state: StackNavigationState<ParamListBase>\n): NativeStackDescriptorMap {\n const result: NativeStackDescriptorMap = {};\n const focusedKey = state.routes[state.index]?.key;\n\n for (const key in descriptors) {\n const descriptor = descriptors[key];\n const routeOptions = registry[key];\n\n // No composition options or empty array → pass through\n if (!routeOptions || routeOptions.length === 0) {\n result[key] = descriptor;\n continue;\n }\n\n // Check if route is preloaded and not focused → skip composition\n const isPreloaded = state.preloadedRoutes?.some((r) => r.key === key) ?? false;\n if (isPreloaded && key !== focusedKey) {\n result[key] = descriptor;\n continue;\n }\n\n // Merge: descriptor options as base, composition options override\n const mergedOptions = Object.assign({}, descriptor.options, ...routeOptions);\n\n const merged = {\n ...descriptor,\n options: mergedOptions,\n };\n\n result[key] = merged;\n }\n\n return result;\n}\n"]}

View File

@@ -0,0 +1,25 @@
import type { NativeStackNavigationOptions } from '@react-navigation/native-stack';
/**
* Registry mapping route keys to composition component options.
*
* Structure: Record<routeKey, options[]>
*
* Each composition component (Title, BackButton, Header, Toolbar) registers
* its memoized options object. Array order reflects registration order,
* so later registrations override earlier ones during merge.
*
* @internal
*/
export type CompositionRegistry = Record<string, Partial<NativeStackNavigationOptions>[]>;
/** @internal */
export interface CompositionContextValue {
/**
* Register or update options for a composition component.
*/
set(routeKey: string, options: Partial<NativeStackNavigationOptions>): void;
/**
* Remove a composition component's options by reference (should be called on unmount).
*/
unset(routeKey: string, options: Partial<NativeStackNavigationOptions>): void;
}
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAEnF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,4BAA4B,CAAC,EAAE,CAAC,CAAC;AAE1F,gBAAgB;AAChB,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,GAAG,IAAI,CAAC;IAE5E;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,GAAG,IAAI,CAAC;CAC/E"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { NativeStackNavigationOptions } from '@react-navigation/native-stack';\n\n/**\n * Registry mapping route keys to composition component options.\n *\n * Structure: Record<routeKey, options[]>\n *\n * Each composition component (Title, BackButton, Header, Toolbar) registers\n * its memoized options object. Array order reflects registration order,\n * so later registrations override earlier ones during merge.\n *\n * @internal\n */\nexport type CompositionRegistry = Record<string, Partial<NativeStackNavigationOptions>[]>;\n\n/** @internal */\nexport interface CompositionContextValue {\n /**\n * Register or update options for a composition component.\n */\n set(routeKey: string, options: Partial<NativeStackNavigationOptions>): void;\n\n /**\n * Remove a composition component's options by reference (should be called on unmount).\n */\n unset(routeKey: string, options: Partial<NativeStackNavigationOptions>): void;\n}\n"]}