Skip to content

Commit

Permalink
Merge pull request #7 from atomic-router/fix/create-routes-view
Browse files Browse the repository at this point in the history
fix: create route/routes view
  • Loading branch information
Drevoed committed Oct 1, 2022
2 parents cbc7e9c + 70da0b1 commit 0f0fe83
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 78 deletions.
16 changes: 11 additions & 5 deletions demo/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Route, RouterProvider } from 'atomic-router-solid';
import { createRoutesView, Route, RouterProvider } from 'atomic-router-solid';

import { HomePage } from '../pages/home';
import { NotFound } from '../pages/not-found';
Expand All @@ -7,13 +7,19 @@ import { PostsSingle } from '../pages/posts-single';

import { router } from './routing';

const RoutesView = createRoutesView({
routes: [
{ route: HomePage.route, view: HomePage.Page },
{ route: PostsList.route, view: PostsList.Page },
{ route: PostsSingle.route, view: PostsSingle.Page },
],
otherwise: NotFound.Page,
});

export const App = () => {
return (
<RouterProvider router={router}>
<Route route={HomePage.route} view={HomePage.Page} />
<Route route={PostsList.route} view={PostsList.Page} />
<Route route={PostsSingle.route} view={PostsSingle.Page} />
<Route route={NotFound.route} view={NotFound.Page} />
<RoutesView />
</RouterProvider>
);
};
6 changes: 0 additions & 6 deletions demo/app/routing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,3 @@ export const router = createHistoryRouter({
});

router.setHistory(history);

sample({
clock: router.routeNotFound,
fn: () => ({}),
target: NotFound.route.open,
});
16 changes: 16 additions & 0 deletions src/create-is-opened.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { RouteInstance } from 'atomic-router';
import { useUnit } from 'effector-solid';
import { createMemo } from 'solid-js';

export function createIsOpened(
route: RouteInstance<any> | RouteInstance<any>[]
) {
return createMemo(() => {
if (Array.isArray(route)) {
const allRoutes = useUnit(route.map((route) => route.$isOpened));
return allRoutes.some((r) => r());
}

return useUnit(route.$isOpened)();
});
}
56 changes: 37 additions & 19 deletions src/create-route-view.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
import type { RouteInstance } from 'atomic-router';
import { combine } from 'effector';
import { useUnit } from 'effector-solid';
import type { RouteInstance, RouteParams } from 'atomic-router';
import type { Component } from 'solid-js';
import { Show } from 'solid-js';
import { Match, mergeProps, Switch } from 'solid-js';
import { Dynamic } from 'solid-js/web';

export const createRouteView = <Props,>(
route: RouteInstance<any> | RouteInstance<any>[],
View: Component<Props>
) => {
const $isOpened = Array.isArray(route)
? combine(combine(route.map((r) => r.$isOpened)), (isOpened) =>
isOpened.includes(true)
)
: route.$isOpened;
import { createIsOpened } from './create-is-opened';

return (props: Props) => {
const isOpened = useUnit($isOpened);
export interface RouteViewConfig<Props, Params extends RouteParams> {
route: RouteInstance<Params> | RouteInstance<Params>[];
view: Component<Props>;
otherwise?: Component<Props>;
}

export function createRouteView<
Props,
Params extends RouteParams,
Config extends {
[key in keyof RouteViewConfig<Props, Params>]?: RouteViewConfig<
Props,
Params
>[key];
}
>(config: Config) {
return (
props: Props & Omit<RouteViewConfig<Props, Params>, keyof Config>
) => {
const mergedConfig = mergeProps(config, props) as RouteViewConfig<
Props,
Params
>;
const isOpened = createIsOpened(mergedConfig.route);

return (
<Show when={isOpened()} keyed={false}>
<Dynamic component={View} {...props} />
</Show>
<Switch fallback={null}>
<Match when={isOpened()} keyed={false}>
<Dynamic component={mergedConfig.view} {...props} />
</Match>

<Match when={mergedConfig.otherwise} keyed={false}>
<Dynamic component={mergedConfig.otherwise} {...props} />
</Match>
</Switch>
);
};
};
}
77 changes: 42 additions & 35 deletions src/create-routes-view.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,50 @@
import type { RouteInstance } from 'atomic-router';
import { combine } from 'effector';
import { useUnit } from 'effector-solid';
import type { Component } from 'solid-js';
import { For, Show } from 'solid-js';
import { Dynamic } from 'solid-js/web';
import type { RouteInstance, RouteParams } from 'atomic-router';
import { Component, createMemo, Match, mergeProps, Switch } from 'solid-js';
import { Dynamic, For } from 'solid-js/web';

import { createRouteView } from './create-route-view';
import { createIsOpened } from './create-is-opened';

export const createRoutesView = (config: {
routes: {
route: RouteInstance<any> | RouteInstance<any>[];
view: Component<any>;
}[];
notFound?: Component<any>;
}) => {
const views = config.routes.map(({ route, view }) =>
createRouteView(route, view)
);
const $isSomeOpened = combine(
...config.routes
.map(({ route }) => route)
.flat()
.map((route) => route.$isOpened),
// @ts-expect-error
(...isOpened) => isOpened.some(Boolean)
);
interface RouteRecord<Props, Params extends RouteParams> {
route: RouteInstance<Params> | RouteInstance<Params>[];
view: Component<Props>;
}

const NotFound = config.notFound;
export interface RoutesViewConfig {
routes: RouteRecord<any, any>[];
otherwise?: Component<any>;
}

return () => {
const isSomeOpened = useUnit($isSomeOpened);
export function createRoutesView<Config extends RoutesViewConfig>(
config: Config
) {
return (props: Omit<Config, keyof Config>) => {
const mergedConfig = mergeProps(config, props) as Config;
const routes = createMemo(() =>
mergedConfig.routes.map((routeRecord) => {
const isOpened = createIsOpened(routeRecord.route);
return {
...routeRecord,
get isOpened() {
return isOpened();
},
};
})
);

return (
<Show
when={isSomeOpened()}
fallback={<Dynamic component={NotFound!} />}
keyed={false}>
<For each={views}>{(View) => <Dynamic component={View} />}</For>
</Show>
<Switch fallback={null}>
<For each={routes()}>
{(route) => (
<Match when={route.isOpened} keyed={false}>
<Dynamic component={route.view} />
</Match>
)}
</For>

<Match when={mergedConfig.otherwise} keyed={false}>
<Dynamic component={mergedConfig.otherwise} />
</Match>
</Switch>
);
};
};
}
2 changes: 1 addition & 1 deletion src/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Props<Params extends RouteParams> = {
} & Exclude<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>;

export function Link<Params extends RouteParams>(props: Props<Params>) {
props = mergeProps({activeClass: 'active'}, props);
props = mergeProps({ activeClass: 'active' }, props);

const toIsString = createMemo(() => typeof props.to === 'string');

Expand Down
16 changes: 4 additions & 12 deletions src/route.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import type { RouteInstance, RouteParams } from 'atomic-router';
import { useUnit } from 'effector-solid';
import type { Component } from 'solid-js';
import { createMemo, Show } from 'solid-js';
import { Show } from 'solid-js';
import { Dynamic } from 'solid-js/web';

import { createIsOpened } from './create-is-opened';

type RouteProps<Params extends RouteParams> = {
route: RouteInstance<Params> | RouteInstance<Params>[];
view: Component;
};

export function Route<Params extends RouteParams>(props: RouteProps<Params>) {
const isOpened = createMemo(() => {
const route = props.route;

if (Array.isArray(route)) {
const allRoutes = useUnit(route.map((route) => route.$isOpened));
return allRoutes.some((r) => r());
}

return useUnit(route.$isOpened)();
});
const isOpened = createIsOpened(props.route);

return (
<Show when={isOpened()} keyed={false}>
Expand Down

0 comments on commit 0f0fe83

Please sign in to comment.