Skip to content

Commit d7ab54e

Browse files
committed
inversion of control for where to insert tab at
1 parent 6ace8d6 commit d7ab54e

File tree

12 files changed

+36
-74
lines changed

12 files changed

+36
-74
lines changed

src/examples/basic/components/AdminLayout/AdminLayout.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ export function AdminLayout() {
5050
router,
5151
tabs,
5252
onTabsChange: setTabs,
53-
startPinnedTabs,
54-
endPinnedTabs: useMemo(() => [], []),
5553
config: config,
5654
fallbackPath: homeRoute,
5755
});

src/examples/basic/routes.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ const productRoutes = [
7070
tabs: [
7171
{
7272
key: TabStoreKey.Products,
73-
title: ({ params }: { params: { id: string } }) =>
74-
`product ${params.id}`,
73+
title: (match) => `product ${match.params.id}`,
74+
insertAt: () => 1,
7575
},
7676
],
7777
} as Handle,
@@ -115,8 +115,8 @@ const categoryRoutes = [
115115
tabs: [
116116
{
117117
key: TabStoreKey.Categories,
118-
title: ({ params }: { params: { id: string } }) =>
119-
`category ${params.id}`,
118+
title: (match) => `category ${match.params.id}`,
119+
insertAt: () => 1,
120120
},
121121
],
122122
} as Handle,

src/examples/basic/routes/CategoriesRoute.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ export function CategoriesRoute() {
6666
router,
6767
config: config,
6868
fallbackPath: homeRoute,
69-
endPinnedTabs: useMemo(() => [], []),
70-
startPinnedTabs,
7169
tabs,
7270
onTabsChange: setTabs,
7371
});

src/examples/basic/routes/ProductsRoute.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ export function ProductsRoute() {
6262
router,
6363
config: config,
6464
fallbackPath: homeRoute,
65-
startPinnedTabs,
66-
endPinnedTabs: useMemo(() => [], []),
6765
tabs,
6866
onTabsChange: setTabs,
6967
});

src/examples/basic/types.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { Params } from "react-router-dom";
1+
import { DataRouteMatch } from "react-router-dom";
2+
import { RouterTabModel } from "src/lib/tabs/useRouterTabs.tsx";
23

34
export type TabHandle = {
45
key: any;
5-
title: (props: { params: Params }) => string;
6+
title: (match: DataRouteMatch) => string;
7+
insertAt?: (models: RouterTabModel[]) => number;
68
};
79

810
export type Handle = {

src/examples/basic/utils/convertRouteTreeToRouterTabsConfig.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { RouteObject } from "react-router-dom";
22
import { flattenRoutes } from "src/lib/tabs/flattenRoutes.ts";
33
import { Handle } from "src/examples/basic/types.ts";
4-
import { InsertMethod, TabConfig } from "src/lib/tabs/useRouterTabs.tsx";
4+
import { TabConfig } from "src/lib/tabs/useRouterTabs.tsx";
5+
import { theBeginning } from "src/lib/tabs/theBeginning.ts";
56

67
export const convertRouteTreeToRouterTabsConfig = (
78
tree: RouteObject[],
@@ -20,7 +21,7 @@ export const convertRouteTreeToRouterTabsConfig = (
2021
return {
2122
title: tabMeta!.title,
2223
shouldOpen: (match) => match.route.id === route.id!,
23-
insertMethod: InsertMethod.Prepend,
24+
insertAt: tabMeta!.insertAt || theBeginning,
2425
};
2526
});
2627
return config;

src/examples/clip-one/components/AdminLayout/AdminLayout.tsx

+7-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Sidebar } from "../Sidebar/Sidebar.tsx";
22
import { Tabs } from "../Tabs/Tabs.tsx";
3-
import { useEffect, useMemo, useState } from "react";
3+
import { useEffect, useState } from "react";
44
import { localStorageDriver } from "src/lib/storage/local-storage.ts";
55

66
import { validateTabs, usePersistTabs } from "src/lib/tabs";
@@ -13,14 +13,11 @@ import {
1313
productsRoute,
1414
suppliersRoute,
1515
} from "../../constants/routes.constants.ts";
16-
import {
17-
InsertMethod,
18-
TabConfig,
19-
useRouterTabs,
20-
} from "src/lib/tabs/useRouterTabs.tsx";
16+
import { TabConfig, useRouterTabs } from "src/lib/tabs/useRouterTabs.tsx";
2117
import { css } from "@emotion/react";
2218
import { Outlet } from "react-router-dom";
2319
import { TabModel } from "src/lib/tabs-ui/tabs-ui.types.ts";
20+
import { theBeginning } from "src/lib/tabs/theBeginning.ts";
2421

2522
const persistStoreKey = {
2623
name: "clip-one__main-tabs",
@@ -45,32 +42,30 @@ export function AdminLayout() {
4542
{
4643
title: () => "Dashboard",
4744
shouldOpen: (match) => match.route.path === dashboardRoute,
48-
insertMethod: InsertMethod.Prepend,
45+
insertAt: theBeginning,
4946
},
5047
{
5148
title: () => "Categories",
5249
shouldOpen: (match) => match.route.path === categoriesRoute,
53-
insertMethod: InsertMethod.Prepend,
50+
insertAt: theBeginning,
5451
},
5552
{
5653
title: () => "Products",
5754
shouldOpen: (match) => match.route.path === productsRoute,
58-
insertMethod: InsertMethod.Prepend,
55+
insertAt: theBeginning,
5956
},
6057
{
6158
title: () => "Suppliers",
6259
shouldOpen: (match) => match.route.path === suppliersRoute,
63-
insertMethod: InsertMethod.Prepend,
60+
insertAt: theBeginning,
6461
},
6562
]);
6663

6764
const { activeTabId, setActiveTabId, getTabTitleByTabPath } = useRouterTabs({
6865
router,
6966
config,
7067
tabs,
71-
endPinnedTabs: useMemo(() => [], []),
7268
onTabsChange: setTabs,
73-
startPinnedTabs,
7469
fallbackPath: homeRoute,
7570
});
7671

src/examples/clip-one/routes/CategoriesRoute.tsx

+3-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Outlet, useNavigate, useParams } from "react-router-dom";
33
import { useEffect, useMemo, useState } from "react";
44
import { Tabs } from "../components/Tabs/Tabs.tsx";
55
import {
6-
InsertMethod,
76
RouterTabModel,
87
TabConfig,
98
useRouterTabs,
@@ -22,6 +21,7 @@ import {
2221
} from "../constants/routes.constants.ts";
2322
import { css } from "@emotion/react";
2423
import { Table } from "src/examples/clip-one/components/Table/Table.tsx";
24+
import { theBeginning } from "src/lib/tabs/theBeginning.ts";
2525

2626
const persistStoreKey = {
2727
name: "clip-one__category-tabs",
@@ -58,14 +58,12 @@ export function CategoriesRoute() {
5858
categoriesRoute,
5959
]);
6060

61-
const [endPinnedTabs] = useState<string[]>([]);
62-
6361
const config = useMemo<TabConfig[]>(
6462
() => [
6563
{
6664
title: () => "All Categories",
6765
shouldOpen: (match) => match.route.path === categoriesRoute,
68-
insertMethod: InsertMethod.Prepend,
66+
insertAt: theBeginning,
6967
},
7068
{
7169
title: ({ params }) => {
@@ -75,7 +73,7 @@ export function CategoriesRoute() {
7573
return category!.title;
7674
},
7775
shouldOpen: (match) => match.route.path === categoryDetailRoute,
78-
insertMethod: InsertMethod.Prepend,
76+
insertAt: () => startPinnedTabs.length,
7977
},
8078
],
8179
[],
@@ -85,8 +83,6 @@ export function CategoriesRoute() {
8583
router,
8684
config: config,
8785
fallbackPath: homeRoute,
88-
endPinnedTabs,
89-
startPinnedTabs,
9086
tabs,
9187
onTabsChange: setTabs,
9288
});

src/examples/clip-one/routes/ProductsRoute.tsx

+7-10
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ import {
1818
productsRoute,
1919
} from "../constants/routes.constants.ts";
2020
import {
21-
InsertMethod,
2221
RouterTabModel,
2322
TabConfig,
2423
useRouterTabs,
2524
} from "src/lib/tabs/useRouterTabs.tsx";
2625
import { css } from "@emotion/react";
2726
import { Table } from "src/examples/clip-one/components/Table/Table.tsx";
2827
import { Button } from "src/examples/clip-one/components/Button/Button.tsx";
28+
import { theBeginning } from "src/lib/tabs/theBeginning.ts";
29+
import { theEnd } from "src/lib/tabs/theEnd.ts";
2930

3031
type DetailParams = { id: string };
3132

@@ -53,7 +54,7 @@ export function ProductsRoute() {
5354
{
5455
title: () => "All products",
5556
shouldOpen: (match) => match.route.path === productsRoute,
56-
insertMethod: InsertMethod.Prepend,
57+
insertAt: theBeginning,
5758
},
5859
{
5960
title: (match) => {
@@ -63,12 +64,12 @@ export function ProductsRoute() {
6364
return product!.title;
6465
},
6566
shouldOpen: (match) => match.route.path === productDetailRoute,
66-
insertMethod: InsertMethod.Prepend,
67+
insertAt: () => startPinnedTabs.length,
6768
},
6869
{
6970
title: () => "New product",
7071
shouldOpen: (match) => match.route.path === productsCreateRoute,
71-
insertMethod: InsertMethod.Append,
72+
insertAt: theEnd,
7273
},
7374
]);
7475

@@ -86,9 +87,7 @@ export function ProductsRoute() {
8687
router,
8788
config,
8889
fallbackPath: homeRoute,
89-
startPinnedTabs,
9090
tabs,
91-
endPinnedTabs,
9291
onTabsChange: setTabs,
9392
});
9493

@@ -223,13 +222,13 @@ export function ProductDetailRoute() {
223222
{
224223
title: () => "General",
225224
shouldOpen: (match) => match.route.path === productDetailRoute,
226-
insertMethod: InsertMethod.Prepend,
225+
insertAt: theBeginning,
227226
},
228227
{
229228
title: () => "Settings",
230229
shouldOpen: (match) =>
231230
match.route.path === productDetailSettingTabsRoute,
232-
insertMethod: InsertMethod.Prepend,
231+
insertAt: theBeginning,
233232
},
234233
],
235234
[],
@@ -240,8 +239,6 @@ export function ProductDetailRoute() {
240239
config,
241240
fallbackPath: homeRoute,
242241
tabs,
243-
startPinnedTabs: useMemo(() => [], []),
244-
endPinnedTabs: useMemo(() => [], []),
245242
onTabsChange: setTabs,
246243
});
247244

src/lib/tabs/theBeginning.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const theBeginning = () => 0;

src/lib/tabs/theEnd.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { RouterTabModel } from "src/lib/tabs/useRouterTabs.tsx";
2+
3+
export const theEnd = (tabs: RouterTabModel[]) => tabs.length - 1;

src/lib/tabs/useRouterTabs.tsx

+4-31
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,10 @@ import { Router } from "@remix-run/router";
66

77
export type TabConfig = {
88
shouldOpen: (match: DataRouteMatch) => boolean;
9-
insertMethod: InsertMethod;
9+
insertAt: (tabs: RouterTabModel[]) => number;
1010
title: (match: DataRouteMatch) => string;
1111
};
1212

13-
export enum InsertMethod {
14-
Append = "append",
15-
Prepend = "prepend",
16-
}
17-
1813
export type RouterTabModel = {
1914
id: string;
2015
route: {
@@ -53,19 +48,9 @@ export const useRouterTabs = (options: {
5348
config: TabConfig[];
5449
onTabsChange?: TabsChangeCallback;
5550
tabs: RouterTabModel[];
56-
startPinnedTabs: string[];
57-
endPinnedTabs: string[];
5851
fallbackPath: string;
5952
}) => {
60-
const {
61-
fallbackPath,
62-
onTabsChange,
63-
tabs = [],
64-
startPinnedTabs,
65-
endPinnedTabs,
66-
config,
67-
router,
68-
} = options;
53+
const { fallbackPath, onTabsChange, tabs = [], config, router } = options;
6954

7055
const updateTabs = useCallback(
7156
(state: RouterState) => {
@@ -99,31 +84,19 @@ export const useRouterTabs = (options: {
9984
path,
10085
});
10186
} else {
102-
const prepend = definition.insertMethod === InsertMethod.Prepend;
103-
10487
const newTab: RouterTabModel = {
10588
id: match.pathname,
10689
route: {
10790
id: match.route.id,
10891
},
10992
path,
11093
};
111-
112-
// prepend a new tab
113-
if (prepend) {
114-
return insertAt(prevTabs, startPinnedTabs.length, newTab);
115-
} else {
116-
return insertAt(
117-
prevTabs,
118-
prevTabs.length - endPinnedTabs.length,
119-
newTab,
120-
);
121-
}
94+
return insertAt(prevTabs, definition.insertAt(prevTabs), newTab);
12295
}
12396
};
12497
onTabsChange?.(handleTabsUpdate);
12598
},
126-
[startPinnedTabs, config, onTabsChange, endPinnedTabs],
99+
[config, onTabsChange],
127100
);
128101

129102
useEffect(() => {

0 commit comments

Comments
 (0)