UNPKG

dew-router

Version:

449 lines (379 loc) 11 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); var dewQs = require('dew-qs'); var pathToRegexp = require('path-to-regexp'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } var LocationContext = /*#__PURE__*/React.createContext(undefined); function makePathFnMap(_routes) { var routes = [..._routes]; var result = {}; var _loop = function _loop() { var r = routes.shift(); if (!r) { return "break"; } var { children, name, lang, pattern } = r; result["".concat(name, ":").concat(lang)] = pathToRegexp.compile(pattern); if (Array.isArray(children)) { routes.push(...children.map(child => _objectSpread2(_objectSpread2({}, child), {}, { pattern: [pattern, child.pattern.replace(/^\//, '')].join('/') }))); } }; while (routes.length > 0) { var _ret = _loop(); if (_ret === "break") break; } return result; } function Router(_ref) { var { location, routes, matchedRoutes, navigator, children } = _ref; var pathFnMap = React.useMemo(() => { return makePathFnMap(routes); }, [routes]); return /*#__PURE__*/React__default['default'].createElement(LocationContext.Provider, { value: { location, navigator, routes, matchedRoutes, pathFnMap, query: dewQs.parseUrlQuery(location.search) } }, children); } function useRouter() { var context = React.useContext(LocationContext); if (!context) { throw new Error('useRouter must be use within StaticRouter or BrowserRouter'); } var { pathFnMap } = context; var parseLink = React.useCallback((_ref2) => { var { name, lang, params, query } = _ref2; var key = "".concat(name, ":").concat(lang); var fn = pathFnMap[key]; if (!fn) { console.warn("Cannot find route with key = ".concat(key)); return null; } return [fn(params), query && dewQs.stringifyUrlQuery(query)].filter(Boolean).join('?'); }, [pathFnMap]); return { routes: context.routes, matchedRoutes: context.matchedRoutes, location: context.location, query: context.query, navigator: context.navigator, parseLink, pathFnMap }; } function useUrlParams() { var ctx = React.useContext(LocationContext); if (!ctx) { throw new Error("useUrlParams must be use with Router (BrowserRouter or StaticRouter) component"); } var { matchedRoutes } = ctx; return matchedRoutes.reduce((acc, _ref) => { var { params } = _ref; return _objectSpread2(_objectSpread2({}, acc), params); }, {}); } function useUrlQuery() { var ctx = React.useContext(LocationContext); if (!ctx) { throw new Error("useUrlQuery must be use with Router (BrowserRouter or StaticRouter) component"); } var { query } = ctx; return query; } function useStringUrlParam(key) { var _ref2; var params = useUrlParams(); var v = params[key]; if (typeof v === 'string') return v; return (_ref2 = v && v[0]) !== null && _ref2 !== void 0 ? _ref2 : ''; } function useIntUrlParam(key) { var _parseInt; var v = useStringUrlParam(key); return (_parseInt = parseInt(v, 10)) !== null && _parseInt !== void 0 ? _parseInt : undefined; } function useFloatUrlParam(key) { var _parseFloat; var v = useStringUrlParam(key); return (_parseFloat = parseFloat(v)) !== null && _parseFloat !== void 0 ? _parseFloat : undefined; } function useStringUrlQuery(key) { var _ref3; var params = useUrlQuery(); var v = params[key]; if (typeof v === 'string') return v; return (_ref3 = v && v[0]) !== null && _ref3 !== void 0 ? _ref3 : ''; } function useIntUrlQuery(key) { var _parseInt2; var v = useStringUrlQuery(key); return (_parseInt2 = parseInt(v, 10)) !== null && _parseInt2 !== void 0 ? _parseInt2 : undefined; } function useFloatUrlQuery(key) { var _parseFloat2; var v = useStringUrlQuery(key); return (_parseFloat2 = parseFloat(v)) !== null && _parseFloat2 !== void 0 ? _parseFloat2 : undefined; } function matchRoutes(path, routes) { var result = []; var iter = [...routes]; var prefix = ''; while (iter.length > 0) { var _current$children; var [current, ...nextIter] = iter; var hasChildren = !!(current === null || current === void 0 ? void 0 : (_current$children = current.children) === null || _current$children === void 0 ? void 0 : _current$children.length); var pattern = prefix + current.pattern; if (hasChildren) { pattern = pattern + '/:others*'; } if (pattern !== '/' && pattern.endsWith('/')) { pattern = pattern.substring(0, pattern.length - 1); } var matcher = pathToRegexp.match(pattern, { decode: decodeURIComponent }); var matchResult = matcher(path); if (matchResult) { prefix = prefix + current.pattern; iter = hasChildren ? [...(current.children || [])] : []; result.push({ name: current.name, page: current.page, pattern: current.pattern, lang: current.lang, path, params: matchResult.params, matcher, pathFn: pathToRegexp.compile(pattern) }); } else { iter = nextIter; } } return result; } function BrowserRouter(_ref) { var { routes, children } = _ref; var [location, setLocation] = React.useState(() => { return { pathname: document.location.pathname, search: document.location.search }; }); // Add events listenner when mounted React.useEffect(() => { var cb = () => { setLocation({ pathname: document.location.pathname, search: document.location.search }); }; var toTop = () => { window.scrollTo({ top: 0 }); }; window.addEventListener('popstate', cb); window.addEventListener('historychange', cb); window.addEventListener('historychange', toTop); return () => { window.removeEventListener('popstate', cb); window.removeEventListener('historychange', cb); window.removeEventListener('historychange', toTop); }; }, []); var matchedRoutes = React.useMemo(() => { return matchRoutes(location.pathname, routes); }, [location, routes]); return /*#__PURE__*/React__default['default'].createElement(Router, { navigator: { pushState: function pushState() { window.history.pushState(...arguments); window.dispatchEvent(new Event('historychange', {})); }, navigate: to => { window.history.pushState({}, '', to); window.dispatchEvent(new Event('historychange', {})); } }, matchedRoutes: matchedRoutes, routes: routes, location: location }, children); } function Route(_ref) { var { name, lang, children } = _ref; var { matchedRoutes } = useRouter(); var route = matchedRoutes.find(x => x.name === name && x.lang === lang); if (!route) { return null; } var props = { params: route.params }; if (typeof children === 'function') { return children(props); } return /*#__PURE__*/React.cloneElement(children, props); } function RouterLink(_ref) { var { to, onClick: _onClick } = _ref, rest = _objectWithoutProperties(_ref, ["to", "onClick"]); var { navigator } = useRouter(); return /*#__PURE__*/React__default['default'].createElement("a", _extends({}, rest, { onClick: e => { e.preventDefault(); e.stopPropagation(); navigator.navigate(to); if (_onClick) { _onClick(e); } }, href: to })); } exports.BrowserRouter = BrowserRouter; exports.LocationContext = LocationContext; exports.Route = Route; exports.Router = Router; exports.RouterLink = RouterLink; exports.makePathFnMap = makePathFnMap; exports.matchRoutes = matchRoutes; exports.useFloatUrlParam = useFloatUrlParam; exports.useFloatUrlQuery = useFloatUrlQuery; exports.useIntUrlParam = useIntUrlParam; exports.useIntUrlQuery = useIntUrlQuery; exports.useRouter = useRouter; exports.useStringUrlParam = useStringUrlParam; exports.useStringUrlQuery = useStringUrlQuery; exports.useUrlParams = useUrlParams; exports.useUrlQuery = useUrlQuery; //# sourceMappingURL=index.js.map