mirror of
https://github.com/colanode/colanode.git
synced 2025-12-16 19:57:46 +01:00
148 lines
3.8 KiB
TypeScript
148 lines
3.8 KiB
TypeScript
import { createMemoryHistory, createRouter } from '@tanstack/react-router';
|
|
import { useCallback, useRef } from 'react';
|
|
|
|
import { Tab } from '@colanode/client/types';
|
|
import {
|
|
compareString,
|
|
generateFractionalIndex,
|
|
generateId,
|
|
IdType,
|
|
} from '@colanode/core';
|
|
import { TabsContent } from '@colanode/ui/components/layouts/tabs/tabs-content';
|
|
import { TabsHeader } from '@colanode/ui/components/layouts/tabs/tabs-header';
|
|
import { TabManagerContext } from '@colanode/ui/contexts/tab-manager';
|
|
import { database } from '@colanode/ui/data';
|
|
import { router, routeTree } from '@colanode/ui/routes';
|
|
|
|
export const LayoutDesktop = () => {
|
|
const routersRef = useRef<Map<string, typeof router>>(new Map());
|
|
|
|
const handleTabAdd = useCallback((location: string) => {
|
|
const tabs = database.tabs.map((tab) => tab);
|
|
const orderedTabs = tabs.toSorted((a, b) =>
|
|
compareString(a.index, b.index)
|
|
);
|
|
|
|
const lastIndex = orderedTabs[orderedTabs.length - 1]?.index;
|
|
const tab: Tab = {
|
|
id: generateId(IdType.Tab),
|
|
location,
|
|
index: generateFractionalIndex(lastIndex, null),
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: null,
|
|
};
|
|
|
|
database.tabs.insert(tab);
|
|
}, []);
|
|
|
|
const handleTabDelete = useCallback((id: string) => {
|
|
const tabs = database.tabs.map((tab) => tab);
|
|
const tabMetadata = database.metadata.get('tab');
|
|
|
|
if (tabs.length === 1) {
|
|
return;
|
|
}
|
|
|
|
if (tabMetadata?.value === id) {
|
|
const nextTab = tabs
|
|
.filter((tab) => tab.id !== id)
|
|
.toSorted((a, b) => {
|
|
const aDate = new Date(a.updatedAt ?? a.createdAt);
|
|
const bDate = new Date(b.updatedAt ?? b.createdAt);
|
|
return aDate.getTime() - bDate.getTime();
|
|
})[0]?.id;
|
|
|
|
if (!nextTab) {
|
|
return;
|
|
}
|
|
|
|
const tabMetadata = database.metadata.get('tab');
|
|
if (tabMetadata) {
|
|
database.metadata.update('tab', (tab) => {
|
|
tab.value = nextTab;
|
|
tab.updatedAt = new Date().toISOString();
|
|
});
|
|
} else {
|
|
database.metadata.insert({
|
|
key: 'tab',
|
|
value: nextTab,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: null,
|
|
});
|
|
}
|
|
}
|
|
|
|
database.tabs.delete(id);
|
|
}, []);
|
|
|
|
const handleTabSwitch = useCallback((id: string) => {
|
|
const tabMetadata = database.metadata.get('tab');
|
|
if (!tabMetadata) {
|
|
database.metadata.insert({
|
|
key: 'tab',
|
|
value: id,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: null,
|
|
});
|
|
} else {
|
|
database.metadata.update('tab', (metadata) => {
|
|
metadata.value = id;
|
|
});
|
|
}
|
|
}, []);
|
|
|
|
const handleTabGetRouter = useCallback((id: string) => {
|
|
if (routersRef.current.has(id)) {
|
|
return routersRef.current.get(id)!;
|
|
}
|
|
|
|
const tab = database.tabs.get(id);
|
|
if (!tab) {
|
|
throw new Error(`Tab ${id} not found`);
|
|
}
|
|
|
|
const router = createRouter({
|
|
routeTree,
|
|
context: {},
|
|
history: createMemoryHistory({
|
|
initialEntries: [tab.location ?? '/'],
|
|
}),
|
|
defaultPreload: 'intent',
|
|
scrollRestoration: true,
|
|
defaultPreloadStaleTime: 0,
|
|
});
|
|
|
|
router.subscribe('onRendered', (event) => {
|
|
if (!event.hrefChanged) {
|
|
return;
|
|
}
|
|
|
|
const location = event.toLocation.href;
|
|
window.colanode.executeMutation({
|
|
type: 'tab.update',
|
|
id,
|
|
location,
|
|
});
|
|
});
|
|
|
|
routersRef.current.set(id, router);
|
|
return router;
|
|
}, []);
|
|
|
|
return (
|
|
<TabManagerContext.Provider
|
|
value={{
|
|
addTab: handleTabAdd,
|
|
deleteTab: handleTabDelete,
|
|
switchTab: handleTabSwitch,
|
|
getRouter: handleTabGetRouter,
|
|
}}
|
|
>
|
|
<div className="flex flex-col h-full">
|
|
<TabsHeader />
|
|
<TabsContent />
|
|
</div>
|
|
</TabManagerContext.Provider>
|
|
);
|
|
};
|