dew-router
Version:
449 lines (379 loc) • 11 kB
JavaScript
;
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