solid-router
Version:
A declarative router for solid-js
165 lines (141 loc) • 3.86 kB
JavaScript
import { createComponent, ssr, getHydrationKey, ssrSpread, escape } from 'solid-js/dom';
import { createMemoryHistory } from 'history';
import { createSignal, onCleanup, createContext, useContext, createMemo, suspend, sample, createEffect } from 'solid-js';
import { match } from 'path-to-regexp';
const _ck$ = ["children"];
const HistoryContext = createContext();
function ContextProvider(props) {
const history = props.history;
const [locationSignal, setLocationSignal] = createSignal(history.location);
const locationHandler = history.listen(location => {
setLocationSignal(location);
});
onCleanup(() => {
locationHandler();
});
return createComponent(HistoryContext.Provider, {
value: {
history,
locationSignal
},
children: () => props.children
}, _ck$);
}
const _ck$$1 = ["children", "history"];
function ServerContextProvider(props) {
return createComponent(ContextProvider, {
history: () => createMemoryHistory(props.options),
children: () => props.children
}, _ck$$1);
}
function getArrayOf(array) {
if (Array.isArray(array)) {
return array;
}
if (!!array) {
return [array];
}
return [];
}
const RouteContext = createContext();
function useLocationSignal() {
const {
locationSignal
} = useContext(HistoryContext);
return locationSignal;
}
function useRouteMatchSignal(routeOrPath) {
const locationSignal = useLocationSignal();
let route;
if (typeof routeOrPath === 'string') {
route = {
path: routeOrPath,
options: {}
};
} else {
route = routeOrPath;
}
if (!route.path) {
route.path = '*';
}
const matcher = match(route.path, route.options);
return () => {
const pathname = locationSignal().pathname;
return matcher(pathname);
};
}
const _ck$$2 = ["children"];
function Router(props) {
const routes = getArrayOf(props.children);
const routeMatchSignals = routes.map(useRouteMatchSignal);
const useFallback = ('fallback' in props);
const evalConditions = createMemo(() => {
return routeMatchSignals.reduce((result, matcher, index) => {
if (result.index === -1) {
const match = matcher();
if (match) {
return {
index,
params: match.params
};
}
}
return result;
}, {
index: -1,
params: {}
});
});
return suspend(createMemo(() => {
const {
index,
params
} = evalConditions();
return sample(() => createComponent(RouteContext.Provider, {
value: params,
children: () => index < 0 ? useFallback && props.fallback : routes[index].children
}, _ck$$2));
}));
}
function Route(props) {
return props;
}
function useHistory() {
const {
history
} = useContext(HistoryContext);
return history;
}
function Link(props) {
const history = useHistory();
return ssr`<a _hk="${getHydrationKey()}" ${ssrSpread(props, false, false)}></a>`;
}
function Redirect(props) {
const history = useHistory();
createEffect(() => {
history.push(props.href);
});
return props.children;
}
function NavLink({
href,
activeClass = '',
className = '',
children,
options,
...props
}) {
const history = useHistory();
const routeMatchSignal = useRouteMatchSignal({
path: href,
options
});
return createMemo(() => {
const isMatched = !!routeMatchSignal();
return sample(() => ssr`<a _hk="${getHydrationKey()}" class="${isMatched ? `${className} ${activeClass}` : className}" href="${href}" ${ssrSpread(props, false, true)}>${escape(children)}</a>`);
});
}
function useParams() {
return useContext(RouteContext);
}
export { ServerContextProvider as ContextProvider, Link, NavLink, Redirect, Route, Router, useHistory, useLocationSignal, useParams, useRouteMatchSignal as useRouteMatchSignalCreator };