react-router-lite
Version:
Similar to `react-router` but leaner.
137 lines (136 loc) • 5.35 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.Link = exports.Redirect = exports.Switch = exports.Route = exports.Match = exports.useMatch = exports.Router = exports.context = exports.go = void 0;
const tslib_1 = require("tslib");
const React = tslib_1.__importStar(require("react"));
const go_1 = require("./go");
Object.defineProperty(exports, "go", { enumerable: true, get: function () { return go_1.go; } });
const h = React.createElement;
const defaultGo = go_1.go;
exports.context = React.createContext(null);
const Router = (props) => {
const { route, fullRoute, parent, go, children } = props;
const value = {
fullRoute: fullRoute || route,
route,
go,
parent,
};
return h(exports.context.Provider, { value }, children);
};
exports.Router = Router;
// -------------------------------------------------------------------- <Match>
const createMatcher = (match, exact) => {
if (typeof match === 'function')
return match;
const regex = typeof match === 'string' ? new RegExp(`^(${match}${exact ? '$' : ''})`) : match;
return (route) => route.match(regex);
};
const useMatch = (match, exact) => {
if (process.env.NODE_ENV !== 'production') {
if (typeof match !== 'string') {
if (exact) {
// tslint:disable-next-line
console.warn('You are using useMatch(!string, true) with non-string match prop, ' +
'exact prop works only with string match prop.');
}
}
}
const { fullRoute, route, parent } = React.useContext(exports.context);
const mather = React.useMemo(() => createMatcher(match, exact), [match, exact]);
const matches = mather(route);
return {
fullRoute,
route,
parent,
matches,
};
};
exports.useMatch = useMatch;
const Match = (props) => {
const { match = '', exact = false, truncate, children, render = children } = props;
const data = (0, exports.useMatch)(match, exact);
const { route, matches } = data;
let element = (typeof render === 'function' ? render(data) : render) || null;
if (matches && truncate) {
element = h(exports.Router, {
fullRoute: route,
route: route.substring(matches[0].length),
parent: data,
children: element,
});
}
return element instanceof Array ? h(React.Fragment, null, element) : element;
};
exports.Match = Match;
// -------------------------------------------------------------------- <Route>
const Route = ({ children, render = children, ...rest }) => h(exports.Match, {
...rest,
render: (data) => (data.matches ? (typeof render === 'function' ? render(data) : render || null) : null),
});
exports.Route = Route;
const Switch = ({ children }) => {
const { route } = React.useContext(exports.context);
for (const element of children) {
if (process.env.NODE_ENV !== 'production') {
if (typeof element !== 'object' || element.type !== exports.Route) {
throw new TypeError('All <Switch> children must be <Route> elements.');
}
}
const { match, exact } = element.props;
if (createMatcher(match, exact)(route))
return element;
}
return null;
};
exports.Switch = Switch;
// ----------------------------------------------------------------- <Redirect>
const isBrowser = typeof window !== 'undefined';
const useIsomorphicLayoutEffect = isBrowser ? React.useLayoutEffect : React.useEffect;
const Redirect = ({ to, ...params }) => {
const go = React.useContext(exports.context).go || defaultGo;
useIsomorphicLayoutEffect(() => {
go(to, params);
}, []);
return null;
};
exports.Redirect = Redirect;
// --------------------------------------------------------------------- <Link>
const isModifiedEvent = (event) => !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
const EXTERNAL_REGEX = /(^https?:)?\/\/.+/;
const isExternal = (to) => EXTERNAL_REGEX.test(to);
exports.Link = React.forwardRef((props, ref) => {
const { replace, state, to = '', a, comp = a ? 'a' : 'button', onClick: originalClick, target, external, ...rest } = props;
const go = React.useContext(exports.context)?.go || defaultGo;
const onClick = React.useCallback((event) => {
if (!event.defaultPrevented && // onClick prevented default
event.button === 0 && // ignore everything but left clicks
!target && // let browser handle "target=*"
!isModifiedEvent(event) // ignore clicks with modifier keys
) {
if (!(external || isExternal(to))) {
event.preventDefault();
go(to, {
replace,
state: state ? state(props) : undefined,
});
}
if (originalClick)
originalClick(event);
}
}, [originalClick, replace, target, state]);
const attr = {
...rest,
ref,
onClick,
};
if (comp === 'a') {
attr.href = to;
attr.target = target;
if (external || isExternal(to)) {
attr.target = '_blank';
attr.rel = 'noopener noreferrer';
}
}
return h(comp, attr);
});
;