104 lines
3.7 KiB
JavaScript
104 lines
3.7 KiB
JavaScript
"use strict";
|
|
|
|
import { CommonActions, findFocusedRoute, getActionFromState, getPathFromState, getStateFromPath, NavigationHelpersContext, NavigationRouteContext, useStateForPath } from '@react-navigation/core';
|
|
import * as React from 'react';
|
|
import { LinkingContext } from "./LinkingContext.js";
|
|
/**
|
|
* Helper to build a href for a screen based on the linking options.
|
|
*/
|
|
export function useBuildHref() {
|
|
const navigation = React.useContext(NavigationHelpersContext);
|
|
const route = React.useContext(NavigationRouteContext);
|
|
const {
|
|
options
|
|
} = React.useContext(LinkingContext);
|
|
const focusedRouteState = useStateForPath();
|
|
const getPathFromStateHelper = options?.getPathFromState ?? getPathFromState;
|
|
const buildHref = React.useCallback((name, params) => {
|
|
if (options?.enabled === false) {
|
|
return undefined;
|
|
}
|
|
|
|
// Check that we're inside:
|
|
// - navigator's context
|
|
// - route context of the navigator (could be a screen, tab, etc.)
|
|
// - route matches the state for path (from the screen's context)
|
|
// This ensures that we're inside a screen
|
|
const isScreen = navigation && route?.key && focusedRouteState ? route.key === findFocusedRoute(focusedRouteState)?.key && navigation.getState().routes.some(r => r.key === route.key) : false;
|
|
const stateForRoute = {
|
|
routes: [{
|
|
name,
|
|
params
|
|
}]
|
|
};
|
|
const constructState = state => {
|
|
if (state) {
|
|
const route = state.routes[0];
|
|
|
|
// If we're inside a screen and at the innermost route
|
|
// We need to replace the state with the provided one
|
|
// This assumes that we're navigating to a sibling route
|
|
if (isScreen && !route.state) {
|
|
return stateForRoute;
|
|
}
|
|
|
|
// Otherwise, dive into the nested state of the route
|
|
return {
|
|
routes: [{
|
|
...route,
|
|
state: constructState(route.state)
|
|
}]
|
|
};
|
|
}
|
|
|
|
// Once there is no more nested state, we're at the innermost route
|
|
// We can add a state based on provided parameters
|
|
// This assumes that we're navigating to a child of this route
|
|
// In this case, the helper is used in a navigator for its routes
|
|
return stateForRoute;
|
|
};
|
|
const state = constructState(focusedRouteState);
|
|
const path = getPathFromStateHelper(state, options?.config);
|
|
return path;
|
|
}, [options?.enabled, options?.config, route?.key, navigation, focusedRouteState, getPathFromStateHelper]);
|
|
return buildHref;
|
|
}
|
|
|
|
/**
|
|
* Helper to build a navigation action from a href based on the linking options.
|
|
*/
|
|
export const useBuildAction = () => {
|
|
const {
|
|
options
|
|
} = React.useContext(LinkingContext);
|
|
const getStateFromPathHelper = options?.getStateFromPath ?? getStateFromPath;
|
|
const getActionFromStateHelper = options?.getActionFromState ?? getActionFromState;
|
|
const buildAction = React.useCallback(href => {
|
|
if (!href.startsWith('/')) {
|
|
throw new Error(`The href must start with '/' (${href}).`);
|
|
}
|
|
const state = getStateFromPathHelper(href, options?.config);
|
|
if (state) {
|
|
const action = getActionFromStateHelper(state, options?.config);
|
|
return action ?? CommonActions.reset(state);
|
|
} else {
|
|
throw new Error('Failed to parse the href to a navigation state.');
|
|
}
|
|
}, [options?.config, getStateFromPathHelper, getActionFromStateHelper]);
|
|
return buildAction;
|
|
};
|
|
|
|
/**
|
|
* Helpers to build href or action based on the linking options.
|
|
*
|
|
* @returns `buildHref` to build an `href` for screen and `buildAction` to build an action from an `href`.
|
|
*/
|
|
export function useLinkBuilder() {
|
|
const buildHref = useBuildHref();
|
|
const buildAction = useBuildAction();
|
|
return {
|
|
buildHref,
|
|
buildAction
|
|
};
|
|
}
|
|
//# sourceMappingURL=useLinkBuilder.js.map
|