Skip to content

Commit

Permalink
clean up the project
Browse files Browse the repository at this point in the history
  • Loading branch information
layerok committed Nov 9, 2024
1 parent c430a7e commit 1e23738
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 59 deletions.
2 changes: 1 addition & 1 deletion src/examples/basic/components/AdminLayout/AdminLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Tabs } from "../../components/Tabs/Tabs.tsx";

import { TabStoreKey } from "../../constants/tabs.constants.ts";
import { validateTabPaths } from "src/lib/tabs/validateTabPaths.ts";
import { usePersistTabs } from "src/lib/tabs/persist.tsx";
import { usePersistTabs } from "src/lib/tabs/usePersistTabs.tsx";

import { useEffect, useMemo, useRef, useState } from "react";

Expand Down
2 changes: 1 addition & 1 deletion src/examples/basic/routes/CategoriesRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Tabs } from "src/examples/basic/components/Tabs/Tabs.tsx";
import { RouterTabPath, useRouterTabs } from "src/lib/tabs/useRouterTabs.tsx";

import { TabModel } from "src/lib/tabs-ui/tabs-ui.types.ts";
import { usePersistTabs } from "src/lib/tabs/persist.tsx";
import { usePersistTabs } from "src/lib/tabs/usePersistTabs.tsx";
import { localStorageDriver } from "src/lib/storage/local-storage.ts";
import { validateTabPaths } from "src/lib/tabs/validateTabPaths.ts";
import { useDataRouterContext } from "src/hooks/useDataRouterContext.tsx";
Expand Down
2 changes: 1 addition & 1 deletion src/examples/basic/routes/ProductsRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TabModel } from "src/lib/tabs-ui/tabs-ui.types.ts";
import { Link, useParams } from "react-router-dom";
import { useEffect, useMemo, useState } from "react";

import { usePersistTabs } from "src/lib/tabs/persist.tsx";
import { usePersistTabs } from "src/lib/tabs/usePersistTabs.tsx";
import { localStorageDriver } from "src/lib/storage/local-storage.ts";
import { validateTabPaths } from "src/lib/tabs/validateTabPaths.ts";
import { useDataRouterContext } from "src/hooks/useDataRouterContext.tsx";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Outlet, RouteObject } from "react-router-dom";
import { flattenRoutes } from "src/lib/tabs/flattenRoutes.ts";
import { Handle } from "src/examples/basic/types.ts";
import { TabConfig } from "src/lib/tabs/useRouterTabs.tsx";
import { TabDefinition } from "src/lib/tabs/useRouterTabs.tsx";
import { theBeginning } from "src/lib/tabs/theBeginning.ts";
import { TabModel } from "src/lib/tabs-ui/tabs-ui.types.ts";
import { whenRoutePathIs } from "src/lib/tabs/whenRoutePathIs.ts";
Expand All @@ -16,7 +16,7 @@ export const convertRouteTreeToRouterTabsConfig = (
return (route.handle as Handle)?.tabs.find((tab) => tab.key === key);
});

const config: TabConfig<TabModel>[] = matchedRoutes.map((route) => {
const config: TabDefinition<TabModel>[] = matchedRoutes.map((route) => {
const handle = route.handle as Handle;
const tabMeta = handle.tabs.find((tab) => (tab.key = key));

Expand Down
6 changes: 3 additions & 3 deletions src/examples/clip-one/components/AdminLayout/AdminLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import {
} from "../../constants/routes.constants.ts";
import {
RouterTabPath,
TabConfig,
TabDefinition,
useRouterTabs,
} from "src/lib/tabs/useRouterTabs.tsx";
import { css } from "@emotion/react";
import { Outlet } from "react-router-dom";
import { TabModel } from "src/lib/tabs-ui/tabs-ui.types.ts";
import { theBeginning } from "src/lib/tabs/theBeginning.ts";
import { validateTabPaths } from "src/lib/tabs/validateTabPaths.ts";
import { usePersistTabs } from "src/lib/tabs/persist.tsx";
import { usePersistTabs } from "src/lib/tabs/usePersistTabs.tsx";
import { localStorageDriver } from "src/lib/storage/local-storage.ts";
import { whenRoutePathIs } from "src/lib/tabs/whenRoutePathIs.ts";

Expand All @@ -47,7 +47,7 @@ export function AdminLayout() {
validateTabPaths(getTabsFromStorage() || [], router),
);

const [config] = useState<TabConfig<Properties>[]>(() => [
const [config] = useState<TabDefinition<Properties>[]>(() => [
{
mapToUiState: (_, path) => ({
id: path,
Expand Down
6 changes: 3 additions & 3 deletions src/examples/clip-one/routes/CategoriesRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { useEffect, useMemo, useState } from "react";
import { Tabs } from "../components/Tabs/Tabs.tsx";
import {
RouterTabPath,
TabConfig,
TabDefinition,
useRouterTabs,
} from "src/lib/tabs/useRouterTabs.tsx";
import { data as categories } from "../data/categories.json";

import { TabModel } from "src/lib/tabs-ui/tabs-ui.types.ts";
import { usePersistTabs } from "src/lib/tabs/persist.tsx";
import { usePersistTabs } from "src/lib/tabs/usePersistTabs.tsx";
import { localStorageDriver } from "src/lib/storage/local-storage.ts";
import { validateTabPaths } from "src/lib/tabs/validateTabPaths.ts";
import { useDataRouterContext } from "src/hooks/useDataRouterContext.tsx";
Expand Down Expand Up @@ -43,7 +43,7 @@ export function CategoriesRoute() {
validateTabPaths(getTabsFromStorage() || defaultTabs, router),
);

const config = useMemo<TabConfig<TabModel>[]>(
const config = useMemo<TabDefinition<TabModel>[]>(
() => [
{
mapToUiState: (_, path) => ({
Expand Down
6 changes: 3 additions & 3 deletions src/examples/clip-one/routes/ProductsRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Outlet, useNavigate, useParams } from "react-router-dom";
import { useEffect, useMemo, useState } from "react";
import { data as products } from "../data/products.json";

import { usePersistTabs } from "src/lib/tabs/persist.tsx";
import { usePersistTabs } from "src/lib/tabs/usePersistTabs.tsx";
import { localStorageDriver } from "src/lib/storage/local-storage.ts";
import { validateTabPaths } from "src/lib/tabs/validateTabPaths.ts";
import { useDataRouterContext } from "src/hooks/useDataRouterContext.tsx";
Expand All @@ -18,7 +18,7 @@ import {
} from "../constants/routes.constants.ts";
import {
RouterTabPath,
TabConfig,
TabDefinition,
useRouterTabs,
} from "src/lib/tabs/useRouterTabs.tsx";
import { css } from "@emotion/react";
Expand Down Expand Up @@ -185,7 +185,7 @@ export function ProductDetailRoute() {
productDetailSettingTabsRoute.replace(":id", params.id),
]);

const config = useMemo<TabConfig<TabModel>[]>(
const config = useMemo<TabDefinition<TabModel>[]>(
() => [
{
mapToUiState: (_, tab) => ({
Expand Down
8 changes: 0 additions & 8 deletions src/lib/tabs/getUrl.ts

This file was deleted.

File renamed without changes.
93 changes: 59 additions & 34 deletions src/lib/tabs/useRouterTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { DataRouteMatch, matchRoutes } from "react-router-dom";
import { DataRouteMatch, Location, matchRoutes } from "react-router-dom";
import { useCallback, useEffect } from "react";
import { RouterState } from "@remix-run/router";
import { last, replaceAt, insertAt } from "src/utils/array-utils.ts";
import { replaceAt, insertAt } from "src/utils/array-utils.ts";
import { Router } from "@remix-run/router";
import { getUrl } from "src/lib/tabs/getUrl.ts";
import { normalizePathname } from "src/lib/tabs/normalizePathname.ts";

type ValidUiState = Record<string, any>;

export type TabConfig<UiState extends ValidUiState = ValidUiState> = {
export type TabDefinition<UiState extends ValidUiState = ValidUiState> = {
shouldOpen: (match: DataRouteMatch) => boolean;
insertAt: (tabs: RouterTabPath[]) => number;
mapToUiState: (match: DataRouteMatch, path: RouterTabPath) => UiState;
Expand All @@ -20,10 +19,15 @@ type PathsChangeCallback = (
paths: RouterTabPath[] | { (prevPaths: RouterTabPath[]): RouterTabPath[] },
) => void;

type MatchRouterTabResult = {
definition: TabDefinition;
match: DataRouteMatch;
};

export const matchRouterTab = (
matches: DataRouteMatch[],
config: TabConfig[],
) => {
config: TabDefinition[],
): MatchRouterTabResult | undefined => {
for (let i = matches.length - 1; i > -1; i--) {
const match = matches[i];
const definition = config.find((def) => def.shouldOpen(match));
Expand All @@ -41,12 +45,29 @@ export const useRouterTabs = <
UiState extends ValidUiState = ValidUiState,
>(options: {
router: Router;
config: TabConfig<UiState>[];
config: TabDefinition<UiState>[];
onPathsChange?: PathsChangeCallback;
paths: RouterTabPath[];
}) => {
const { onPathsChange, paths, config, router } = options;

const isOpenFor = useCallback(
(match: DataRouteMatch) => (path: string) => {
const matches = matchRoutes(router.routes, path) || [];
const result = matchRouterTab(matches, config);

if (!result) {
return false;
}

return (
normalizePathname(result.match.pathname) ===
normalizePathname(match.pathname)
);
},
[router.routes, config],
);

const updateTabs = useCallback(
(state: RouterState) => {
const { matches, location, navigation } = state;
Expand All @@ -55,39 +76,33 @@ export const useRouterTabs = <
return;
}

const result = matchRouterTab(matches, config);
const matchResult = matchRouterTab(matches, config);

if (!result) {
if (!matchResult) {
return;
}
const { definition, match } = result;

const getNextPaths = (prevPaths: RouterTabPath[]) => {
const tab = prevPaths.find((path) => {
const matches = matchRoutes(router.routes, path) || [];
const result = matchRouterTab(matches, config);
return (
result &&
normalizePathname(result.match.pathname) ===
normalizePathname(match.pathname)
);
});
const tab = prevPaths.find(isOpenFor(matchResult.match));

const { pathname } = last(matches);
const { search } = location;
const path = normalizePathname(pathname) + search;
const path = getUrl(location);

if (tab) {
// update the tab path
const index = prevPaths.indexOf(tab);

return replaceAt(prevPaths, index, path);
} else {
return insertAt(prevPaths, definition.insertAt(prevPaths), path);
return insertAt(
prevPaths,
matchResult.definition.insertAt(prevPaths),
path,
);
}
};
onPathsChange?.(getNextPaths);
},
[config, onPathsChange, router.routes],
[config, onPathsChange, isOpenFor],
);

useEffect(() => {
Expand All @@ -96,23 +111,33 @@ export const useRouterTabs = <
return router.subscribe(updateTabs);
}, [router, updateTabs]);

const tabs = paths.map((path) => {
const toUiState = (path: string) => {
const matches = matchRoutes(router.routes, path) || [];
const result = matchRouterTab(matches, config)!;
const result = matchRouterTab(matches, config);

return result.definition.mapToUiState(result.match, path) as UiState;
});
if (!result) {
return undefined;
}
const { definition, match } = result;
return definition.mapToUiState(match, path);
};

const matches = matchRoutes(router.routes, router.state.location) || [];
const result = matchRouterTab(matches, config);
const tabs = paths
.map(toUiState)
.filter((tab): tab is UiState => Boolean(tab));

const activeTab = result?.definition.mapToUiState(
result.match,
getUrl(router.state.location),
) as UiState | undefined;
const activeTab = toUiState(getUrl(router.state.location)) as
| UiState
| undefined;

return {
tabs,
activeTab,
};
};

const getUrl = (location: Location) => {
const { pathname, search } = location;

return normalizePathname(pathname) + search;
};
10 changes: 7 additions & 3 deletions src/routes/HomeRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ export function HomeRoute() {
<div css={innerStyles}>
<h1 css={titleStyles}>Examples:</h1>
<ul>
<li>
<li css={listItemStyles}>
<Link to={basicHomeRoute}>Basic</Link>
</li>
<li>
<Link to={clipOneHomeRoute}>Clip one</Link>
<li css={listItemStyles}>
<Link to={clipOneHomeRoute}>Clip one admin</Link>
</li>
</ul>
</div>
Expand All @@ -31,6 +31,10 @@ const containerStyles = css`
flex-direction: column;
`;

const listItemStyles = css`
white-space: nowrap;
`;

const innerStyles = css`
width: 100px;
`;
Expand Down

0 comments on commit 1e23738

Please sign in to comment.