feat: column manager

This commit is contained in:
2024-03-18 10:50:08 +07:00
parent c8e014f33e
commit 05b52564e0
17 changed files with 269 additions and 87 deletions

View File

@@ -1,87 +0,0 @@
import { useCallback, useEffect, useMemo, useRef } from "react";
import {
LogicalPosition,
LogicalSize,
getCurrent,
} from "@tauri-apps/api/window";
import { Webview } from "@tauri-apps/api/webview";
import { LumeColumn } from "@lume/types";
import { useDebouncedCallback } from "use-debounce";
import { type UnlistenFn } from "@tauri-apps/api/event";
export function Column({
column,
isScroll,
}: {
column: LumeColumn;
isScroll: boolean;
}) {
const mainWindow = useMemo(() => getCurrent(), []);
const childWindow = useRef<Webview>(null);
const divRef = useRef<HTMLDivElement>(null);
const initialRect = useRef<DOMRect>(null);
const unlisten = useRef<UnlistenFn>(null);
const handleResize = useDebouncedCallback(() => {
const newRect = divRef.current.getBoundingClientRect();
if (initialRect.current.height !== newRect.height) {
childWindow.current.setSize(
new LogicalSize(newRect.width, newRect.height),
);
}
}, 500);
const trackResize = useCallback(async () => {
unlisten.current = await mainWindow.onResized(() => {
handleResize();
});
}, []);
useEffect(() => {
if (isScroll) {
const newRect = divRef.current.getBoundingClientRect();
childWindow.current.setPosition(
new LogicalPosition(newRect.x, newRect.y),
);
}
}, [isScroll]);
useEffect(() => {
if (!mainWindow) return;
if (!divRef.current) return;
if (childWindow.current) return;
const rect = divRef.current.getBoundingClientRect();
const name = column.name.toLowerCase().replace(/\W/g, "");
// create new webview
initialRect.current = rect;
childWindow.current = new Webview(mainWindow, name, {
url: column.content,
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
transparent: true,
userAgent: "Lume/4.0",
});
// track window resize event
trackResize();
return () => {
if (unlisten.current) unlisten.current();
};
}, []);
return (
<div className="shadow-primary relative flex h-full w-[420px] shrink-0 flex-col rounded-xl bg-white dark:bg-black">
<div className="flex h-11 w-full shrink-0 items-center justify-center gap-2 border-b border-neutral-100 dark:border-neutral-900">
<div className="inline-flex items-center gap-1.5">
<div className="text-[13px] font-medium">{column.name}</div>
</div>
</div>
<div ref={divRef} className="flex-1" />
<div className="h-6 w-full shrink-0 border-t border-neutral-100 dark:border-neutral-900" />
</div>
);
}

View File

@@ -0,0 +1,16 @@
import { cn } from "@lume/utils";
import { ReactNode } from "react";
export function ColumnContent({
children,
className,
}: {
children: ReactNode;
className?: string;
}) {
return (
<div className={cn("flex-1 overflow-y-auto overflow-x-hidden", className)}>
{children}
</div>
);
}

View File

@@ -0,0 +1,22 @@
import { cn } from "@lume/utils";
export function ColumnHeader({
name,
className,
}: {
name: string;
className?: string;
}) {
return (
<div
className={cn(
"flex h-11 w-full shrink-0 items-center justify-center gap-2 border-b border-neutral-100 dark:border-neutral-900",
className,
)}
>
<div className="inline-flex items-center gap-1.5">
<div className="text-[13px] font-medium">{name}</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,9 @@
import { ColumnContent } from "./content";
import { ColumnHeader } from "./header";
import { ColumnRoot } from "./root";
export const Column = {
Root: ColumnRoot,
Header: ColumnHeader,
Content: ColumnContent,
};

View File

@@ -0,0 +1,23 @@
import { cn } from "@lume/utils";
import { ReactNode } from "react";
export function ColumnRoot({
children,
className,
}: {
children: ReactNode;
className?: string;
}) {
return (
<div className="h-full w-full p-2">
<div
className={cn(
"shadow-primary relative flex h-full w-full flex-col rounded-xl bg-white dark:bg-black",
className,
)}
>
{children}
</div>
</div>
);
}

View File

@@ -1,7 +1,7 @@
export * from "./user";
export * from "./note";
export * from "./column";
// UI
export * from "./column";
export * from "./container";
export * from "./box";