"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Navigator = void 0;
var _element = require("@wordpress/element");
var _isShallowEqual = _interopRequireDefault(require("@wordpress/is-shallow-equal"));
var _warning = _interopRequireDefault(require("@wordpress/warning"));
var _context = require("../../context");
var _useCx = require("../../utils/hooks/use-cx");
var _router = require("../utils/router");
var _view = require("../../view");
var _context2 = require("../context");
var styles = _interopRequireWildcard(require("../styles"));
var _deprecated = _interopRequireDefault(require("@wordpress/deprecated"));
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
 * External dependencies
 */

/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

function addScreen({
  screens
}, screen) {
  if (screens.some(s => s.path === screen.path)) {
    globalThis.SCRIPT_DEBUG === true ? (0, _warning.default)(`Navigator: a screen with path ${screen.path} already exists.
The screen with id ${screen.id} will not be added.`) : void 0;
    return screens;
  }
  return [...screens, screen];
}
function removeScreen({
  screens
}, screen) {
  return screens.filter(s => s.id !== screen.id);
}
function goTo(state, path, options = {}) {
  var _focusSelectorsCopy2;
  const {
    focusSelectors
  } = state;
  const currentLocation = {
    ...state.currentLocation
  };
  const {
    // Default assignments
    isBack = false,
    skipFocus = false,
    // Extract to avoid forwarding
    replace,
    focusTargetSelector,
    // Rest
    ...restOptions
  } = options;
  if (currentLocation.path === path) {
    return {
      currentLocation,
      focusSelectors
    };
  }
  let focusSelectorsCopy;
  function getFocusSelectorsCopy() {
    var _focusSelectorsCopy;
    focusSelectorsCopy = (_focusSelectorsCopy = focusSelectorsCopy) !== null && _focusSelectorsCopy !== void 0 ? _focusSelectorsCopy : new Map(state.focusSelectors);
    return focusSelectorsCopy;
  }

  // Set a focus selector that will be used when navigating
  // back to the current location.
  if (focusTargetSelector && currentLocation.path) {
    getFocusSelectorsCopy().set(currentLocation.path, focusTargetSelector);
  }

  // Get the focus selector for the new location.
  let currentFocusSelector;
  if (focusSelectors.get(path)) {
    if (isBack) {
      // Use the found focus selector only when navigating back.
      currentFocusSelector = focusSelectors.get(path);
    }
    // Make a copy of the focusSelectors map to remove the focus selector
    // only if necessary (ie. a focus selector was found).
    getFocusSelectorsCopy().delete(path);
  }
  return {
    currentLocation: {
      ...restOptions,
      isInitial: false,
      path,
      isBack,
      hasRestoredFocus: false,
      focusTargetSelector: currentFocusSelector,
      skipFocus
    },
    focusSelectors: (_focusSelectorsCopy2 = focusSelectorsCopy) !== null && _focusSelectorsCopy2 !== void 0 ? _focusSelectorsCopy2 : focusSelectors
  };
}
function goToParent(state, options = {}) {
  const {
    screens,
    focusSelectors
  } = state;
  const currentLocation = {
    ...state.currentLocation
  };
  const currentPath = currentLocation.path;
  if (currentPath === undefined) {
    return {
      currentLocation,
      focusSelectors
    };
  }
  const parentPath = (0, _router.findParent)(currentPath, screens);
  if (parentPath === undefined) {
    return {
      currentLocation,
      focusSelectors
    };
  }
  return goTo(state, parentPath, {
    ...options,
    isBack: true
  });
}
function routerReducer(state, action) {
  let {
    screens,
    currentLocation,
    matchedPath,
    focusSelectors,
    ...restState
  } = state;
  switch (action.type) {
    case 'add':
      screens = addScreen(state, action.screen);
      break;
    case 'remove':
      screens = removeScreen(state, action.screen);
      break;
    case 'goto':
      ({
        currentLocation,
        focusSelectors
      } = goTo(state, action.path, action.options));
      break;
    case 'gotoparent':
      ({
        currentLocation,
        focusSelectors
      } = goToParent(state, action.options));
      break;
  }

  // Return early in case there is no change
  if (screens === state.screens && currentLocation === state.currentLocation) {
    return state;
  }

  // Compute the matchedPath
  const currentPath = currentLocation.path;
  matchedPath = currentPath !== undefined ? (0, _router.patternMatch)(currentPath, screens) : undefined;

  // If the new match is the same as the previous match,
  // return the previous one to keep immutability.
  if (matchedPath && state.matchedPath && matchedPath.id === state.matchedPath.id && (0, _isShallowEqual.default)(matchedPath.params, state.matchedPath.params)) {
    matchedPath = state.matchedPath;
  }
  return {
    ...restState,
    screens,
    currentLocation,
    matchedPath,
    focusSelectors
  };
}
function UnconnectedNavigator(props, forwardedRef) {
  const {
    initialPath: initialPathProp,
    children,
    className,
    ...otherProps
  } = (0, _context.useContextSystem)(props, 'Navigator');
  const [routerState, dispatch] = (0, _element.useReducer)(routerReducer, initialPathProp, path => ({
    screens: [],
    currentLocation: {
      path,
      isInitial: true
    },
    matchedPath: undefined,
    focusSelectors: new Map(),
    initialPath: initialPathProp
  }));

  // The methods are constant forever, create stable references to them.
  const methods = (0, _element.useMemo)(() => ({
    // Note: calling goBack calls `goToParent` internally, as it was established
    // that `goBack` should behave like `goToParent`, and `goToParent` should
    // be marked as deprecated.
    goBack: options => dispatch({
      type: 'gotoparent',
      options
    }),
    goTo: (path, options) => dispatch({
      type: 'goto',
      path,
      options
    }),
    goToParent: options => {
      (0, _deprecated.default)(`wp.components.useNavigator().goToParent`, {
        since: '6.7',
        alternative: 'wp.components.useNavigator().goBack'
      });
      dispatch({
        type: 'gotoparent',
        options
      });
    },
    addScreen: screen => dispatch({
      type: 'add',
      screen
    }),
    removeScreen: screen => dispatch({
      type: 'remove',
      screen
    })
  }), []);
  const {
    currentLocation,
    matchedPath
  } = routerState;
  const navigatorContextValue = (0, _element.useMemo)(() => {
    var _matchedPath$params;
    return {
      location: currentLocation,
      params: (_matchedPath$params = matchedPath?.params) !== null && _matchedPath$params !== void 0 ? _matchedPath$params : {},
      match: matchedPath?.id,
      ...methods
    };
  }, [currentLocation, matchedPath, methods]);
  const cx = (0, _useCx.useCx)();
  const classes = (0, _element.useMemo)(() => cx(styles.navigatorWrapper, className), [className, cx]);
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_view.View, {
    ref: forwardedRef,
    className: classes,
    ...otherProps,
    children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_context2.NavigatorContext.Provider, {
      value: navigatorContextValue,
      children: children
    })
  });
}
const Navigator = exports.Navigator = (0, _context.contextConnect)(UnconnectedNavigator, 'Navigator');
//# sourceMappingURL=component.js.map