feat: column manager
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
16
packages/ui/src/column/content.tsx
Normal file
16
packages/ui/src/column/content.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
22
packages/ui/src/column/header.tsx
Normal file
22
packages/ui/src/column/header.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
9
packages/ui/src/column/index.ts
Normal file
9
packages/ui/src/column/index.ts
Normal 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,
|
||||
};
|
||||
23
packages/ui/src/column/root.tsx
Normal file
23
packages/ui/src/column/root.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
export * from "./user";
|
||||
export * from "./note";
|
||||
export * from "./column";
|
||||
|
||||
// UI
|
||||
export * from "./column";
|
||||
export * from "./container";
|
||||
export * from "./box";
|
||||
|
||||
Reference in New Issue
Block a user