add base layout
This commit is contained in:
@@ -20,6 +20,8 @@
|
||||
"@types/node": "^24.0.14",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"mobx": "^6.13.7",
|
||||
"mobx-react-lite": "^4.1.0",
|
||||
"radix-ui": "^1.4.2",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
|
||||
32
pnpm-lock.yaml
generated
32
pnpm-lock.yaml
generated
@@ -38,6 +38,12 @@ importers:
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
mobx:
|
||||
specifier: ^6.13.7
|
||||
version: 6.13.7
|
||||
mobx-react-lite:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0(mobx@6.13.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
radix-ui:
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
@@ -2037,6 +2043,22 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
mobx-react-lite@4.1.0:
|
||||
resolution: {integrity: sha512-QEP10dpHHBeQNv1pks3WnHRCem2Zp636lq54M2nKO2Sarr13pL4u6diQXf65yzXUn0mkk18SyIDCm9UOJYTi1w==}
|
||||
peerDependencies:
|
||||
mobx: ^6.9.0
|
||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||
react-dom: '*'
|
||||
react-native: '*'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
react-native:
|
||||
optional: true
|
||||
|
||||
mobx@6.13.7:
|
||||
resolution: {integrity: sha512-aChaVU/DO5aRPmk1GX8L+whocagUUpBQqoPtJk+cm7UOXUk87J4PeWCh6nNmTTIfEhiR9DI/+FnA8dln/hTK7g==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@@ -4439,6 +4461,16 @@ snapshots:
|
||||
|
||||
mkdirp@3.0.1: {}
|
||||
|
||||
mobx-react-lite@4.1.0(mobx@6.13.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
mobx: 6.13.7
|
||||
react: 19.1.0
|
||||
use-sync-external-store: 1.5.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
|
||||
mobx@6.13.7: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
17
src/components/common/main-menu.tsx
Normal file
17
src/components/common/main-menu.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Button } from "../ui/button";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useAccountStore } from "@/contexts/AccountContext";
|
||||
|
||||
export const MainMenu = observer(() => {
|
||||
const { isMainMenuOpen, setIsMainMenuOpen } = useAccountStore();
|
||||
return (
|
||||
<div
|
||||
data-open={isMainMenuOpen}
|
||||
className="w-[30px] data-[open=true]:w-[124px] transition-all border-r p-2"
|
||||
>
|
||||
<Button onClick={() => setIsMainMenuOpen(!isMainMenuOpen)}>
|
||||
{isMainMenuOpen ? "Close" : "Open"}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
24
src/components/interaction-panel/chat.tsx
Normal file
24
src/components/interaction-panel/chat.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { useAccountStore } from "@/contexts/AccountContext";
|
||||
import { Button } from "../ui/button";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
export const Chat = observer(() => {
|
||||
const { isSandboxOpen, setIsSandboxOpen, isMainMenuOpen, setIsMainMenuOpen } =
|
||||
useAccountStore();
|
||||
|
||||
return (
|
||||
<div className="flex-1">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsSandboxOpen(!isSandboxOpen);
|
||||
if (isMainMenuOpen && !isSandboxOpen) {
|
||||
setIsMainMenuOpen(false);
|
||||
}
|
||||
}}
|
||||
className="ml-10"
|
||||
>
|
||||
OPEN SANDBOX
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
15
src/components/interaction-panel/sandbox.tsx
Normal file
15
src/components/interaction-panel/sandbox.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useAccountStore } from "@/contexts/AccountContext";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
export const Sandbox = observer(() => {
|
||||
const { isSandboxOpen } = useAccountStore();
|
||||
|
||||
return (
|
||||
<div
|
||||
data-open={isSandboxOpen}
|
||||
className="w-0 data-[open=true]:w-[40svw] border-l-1 transition-all border-r p-0 bg-red-600"
|
||||
>
|
||||
SANDBOOOOX
|
||||
</div>
|
||||
);
|
||||
});
|
||||
47
src/contexts/AccountContext.tsx
Normal file
47
src/contexts/AccountContext.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { createContext, useContext, useState } from "react";
|
||||
import { AccountStore } from "@/store/account";
|
||||
|
||||
interface StoreProviderState {
|
||||
store?: AccountStore;
|
||||
setStore: (store: AccountStore) => void;
|
||||
}
|
||||
|
||||
interface StoreProviderProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const initialState: StoreProviderState = {
|
||||
store: undefined,
|
||||
setStore: () => null,
|
||||
};
|
||||
|
||||
const AccountStoreProviderContext = createContext(initialState);
|
||||
const _AccountStore = new AccountStore();
|
||||
|
||||
export function AccountStoreProvider({
|
||||
children,
|
||||
...props
|
||||
}: StoreProviderProps) {
|
||||
const [_store, _setStore] = useState(_AccountStore);
|
||||
|
||||
return (
|
||||
<AccountStoreProviderContext.Provider
|
||||
{...props}
|
||||
value={{
|
||||
store: _store,
|
||||
setStore: _setStore,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AccountStoreProviderContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAccountStore() {
|
||||
const context = useContext(AccountStoreProviderContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useStoreProvider must be used within a StoreProvider");
|
||||
}
|
||||
|
||||
return _AccountStore;
|
||||
}
|
||||
3
src/lib/constants.ts
Normal file
3
src/lib/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const LS_TOKENS = {
|
||||
isMainMenuOpen: "isMainMenuOpen",
|
||||
};
|
||||
11
src/main.tsx
11
src/main.tsx
@@ -9,6 +9,7 @@ import "./styles.css";
|
||||
import reportWebVitals from "./reportWebVitals.ts";
|
||||
import { Toaster } from "./components/ui/toaster.tsx";
|
||||
import { TooltipProvider } from "./components/ui/tooltip.tsx";
|
||||
import { AccountStoreProvider } from "./contexts/AccountContext.tsx";
|
||||
|
||||
// Create a new router instance
|
||||
const router = createRouter({
|
||||
@@ -33,10 +34,12 @@ if (rootElement && !rootElement.innerHTML) {
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<TooltipProvider delayDuration={50}>
|
||||
<RouterProvider router={router} />
|
||||
<Toaster />
|
||||
</TooltipProvider>
|
||||
<AccountStoreProvider>
|
||||
<TooltipProvider delayDuration={50}>
|
||||
<RouterProvider router={router} />
|
||||
<Toaster />
|
||||
</TooltipProvider>
|
||||
</AccountStoreProvider>
|
||||
</StrictMode>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,14 @@
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as InteractionPanelRouteImport } from './routes/interaction-panel'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
|
||||
const InteractionPanelRoute = InteractionPanelRouteImport.update({
|
||||
id: '/interaction-panel',
|
||||
path: '/interaction-panel',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const IndexRoute = IndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
@@ -19,28 +25,39 @@ const IndexRoute = IndexRouteImport.update({
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/interaction-panel': typeof InteractionPanelRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/interaction-panel': typeof InteractionPanelRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
'/interaction-panel': typeof InteractionPanelRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths: '/'
|
||||
fullPaths: '/' | '/interaction-panel'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to: '/'
|
||||
id: '__root__' | '/'
|
||||
to: '/' | '/interaction-panel'
|
||||
id: '__root__' | '/' | '/interaction-panel'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
InteractionPanelRoute: typeof InteractionPanelRoute
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/interaction-panel': {
|
||||
id: '/interaction-panel'
|
||||
path: '/interaction-panel'
|
||||
fullPath: '/interaction-panel'
|
||||
preLoaderRoute: typeof InteractionPanelRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
@@ -53,6 +70,7 @@ declare module '@tanstack/react-router' {
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
InteractionPanelRoute: InteractionPanelRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
import { Outlet, createRootRoute } from '@tanstack/react-router'
|
||||
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
|
||||
// src/routes/__root.tsx
|
||||
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
import { MainMenu } from "@/components/common/main-menu";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: () => (
|
||||
<>
|
||||
<Outlet />
|
||||
<TanStackRouterDevtools />
|
||||
</>
|
||||
),
|
||||
})
|
||||
component: RootLayout,
|
||||
});
|
||||
|
||||
function RootLayout() {
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
<MainMenu />
|
||||
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TanStackRouterDevtools />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { TextLink } from "@/components/ui/text-link";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
@@ -5,5 +6,9 @@ export const Route = createFileRoute("/")({
|
||||
});
|
||||
|
||||
function App() {
|
||||
return <div className="p-4">hi</div>;
|
||||
return (
|
||||
<div className="p-4">
|
||||
<TextLink>Lable</TextLink>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
17
src/routes/interaction-panel.tsx
Normal file
17
src/routes/interaction-panel.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
// src/routes/chat.tsx
|
||||
import { Chat } from "@/components/interaction-panel/chat";
|
||||
import { Sandbox } from "@/components/interaction-panel/sandbox";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/interaction-panel")({
|
||||
component: InteractionPanelRoute,
|
||||
});
|
||||
|
||||
function InteractionPanelRoute() {
|
||||
return (
|
||||
<div className="flex h-full">
|
||||
<Chat />
|
||||
<Sandbox />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
25
src/store/account.ts
Normal file
25
src/store/account.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { LS_TOKENS } from "@/lib/constants";
|
||||
import { makeAutoObservable } from "mobx";
|
||||
|
||||
export class AccountStore {
|
||||
isMainMenuOpen: boolean;
|
||||
isSandboxOpen: boolean;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this, {}, { autoBind: true });
|
||||
|
||||
const savedState = localStorage.getItem(LS_TOKENS.isMainMenuOpen);
|
||||
this.isMainMenuOpen = savedState ? savedState === "true" : false;
|
||||
|
||||
this.isSandboxOpen = false;
|
||||
}
|
||||
|
||||
setIsMainMenuOpen(value: boolean) {
|
||||
this.isMainMenuOpen = value;
|
||||
localStorage.setItem(LS_TOKENS.isMainMenuOpen, String(this.isMainMenuOpen));
|
||||
}
|
||||
|
||||
setIsSandboxOpen(value: boolean) {
|
||||
this.isSandboxOpen = value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user