dark theme implemented

This commit is contained in:
KarimTamani
2025-05-16 22:17:48 +01:00
parent 7f7779d579
commit d9c260bfce
26 changed files with 319 additions and 292 deletions
+2
View File
@@ -13,6 +13,7 @@
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@heroui/react": "^2.7.6",
"@nextui-org/react": "^2.6.11",
"@powersync/drizzle-driver": "^0.4.0",
"@powersync/react": "^1.5.3",
"@powersync/web": "^1.20.0",
@@ -25,6 +26,7 @@
"framer-motion": "11.15.0",
"i18next-browser-languagedetector": "^8.0.5",
"lucide-react": "^0.501.0",
"next-themes": "^0.4.6",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-i18next": "^15.5.1",
+24 -12
View File
@@ -1,4 +1,5 @@
import { Route, Routes } from "react-router-dom";
import "@/styles/globals.css"
import { TooltipProvider } from "./components/tooltip/tooltip";
import { ReactFlowProvider } from "@xyflow/react";
@@ -6,21 +7,32 @@ import useAppRoutes from "./routes/app-route";
import { SyncProvider } from "./providers/sync-provider/sync-provider";
import DatabaseProvider from "./providers/database-provider/database-provider";
import DiagramProvider from "./providers/diagram-provider/diagram-provider";
import { NextUIProvider } from "@nextui-org/react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
function App() {
const appRoutes = useAppRoutes();
return (
<SyncProvider>
<ReactFlowProvider>
<DatabaseProvider>
<DiagramProvider>
<TooltipProvider delayDuration={0}>
{appRoutes}
</TooltipProvider>
</DiagramProvider>
</DatabaseProvider>
</ReactFlowProvider>
</SyncProvider>
<NextUIProvider>
<NextThemesProvider
defaultTheme='system'
attribute='class'
>
<SyncProvider>
<ReactFlowProvider>
<DatabaseProvider>
<DiagramProvider>
<TooltipProvider delayDuration={0}>
{appRoutes}
</TooltipProvider>
</DiagramProvider>
</DatabaseProvider>
</ReactFlowProvider>
</SyncProvider>
</NextThemesProvider>
</NextUIProvider>
);
}
@@ -41,7 +41,7 @@ const CardinalityMarker: React.FC<CardinalityMarkerProps> = ({ selected = false,
>
<svg
fill="transparent"
className={selected ? "stroke-primary" : "stroke-slate-300"}
className={selected ? "stroke-primary" : "stroke-slate-300 dark:stroke-default-400"}
strokeWidth="8"
width="24"
height="24"
+3 -3
View File
@@ -27,7 +27,7 @@ const ColorPicker: React.FC<ColorPickerProps> = ({ defaultColor, onChange }) =>
<Popover placement="bottom" radius="sm" isOpen={isOpen} onOpenChange={(open) => setIsOpen(open)}>
<PopoverTrigger>
<Button size="sm"
className="size-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md"
className="size-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md dark:border-default-100"
isIconOnly
style={{
backgroundColor: color ? color : undefined
@@ -41,7 +41,7 @@ const ColorPicker: React.FC<ColorPickerProps> = ({ defaultColor, onChange }) =>
{colorOptions.map(color => (
<div
key={color}
className="size-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md"
className="size-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md dark:border-default-100"
style={{
backgroundColor: color
}}
@@ -54,7 +54,7 @@ const ColorPicker: React.FC<ColorPickerProps> = ({ defaultColor, onChange }) =>
<TooltipTrigger>
<div
key={undefined}
className="size-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md"
className="size-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md dark:border-default-100"
style={{
backgroundColor: "white"
}}
+12 -8
View File
@@ -1,5 +1,5 @@
import { Dropdown, DropdownMenu as HeroDropdownMenu, DropdownItem, DropdownTrigger, Button, useDisclosure, Popover, PopoverTrigger, PopoverContent, Divider, DropdownSection } from "@heroui/react";
import { useMemo, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import SubmenuDropdown from "./submenu-dropdown";
@@ -19,24 +19,26 @@ export interface MenuDropdownProps {
isDisabled?: boolean,
divide?: boolean,
shortcut?: string,
isOpen?: boolean
isOpen?: boolean ,
clickHandler? : () => void
}
const DropdownMenu: React.FC<MenuDropdownProps> = ({ title, children, isOpen }) => {
const DropdownMenu: React.FC<MenuDropdownProps> = ({ title, children, isOpen , clickHandler }) => {
const disabledChilds: string[] = useMemo(() => {
return children ? children?.filter((child: MenuDropdownProps) => child.isDisabled && child.title).map((child: MenuDropdownProps) => child.title as string) : []
}, [children]);
return <Dropdown radius="sm" >
{
title &&
<DropdownTrigger >
<DropdownTrigger onPressEnd={clickHandler}>
<Button size="sm" variant="light" >
<span className=" text-left flex justify-between text-small font-semibold">
{title}
@@ -54,8 +56,10 @@ const DropdownMenu: React.FC<MenuDropdownProps> = ({ title, children, isOpen })
className={child.theme == "danger" ? "text-danger" : ""}
color={child.theme}
shortcut={child.shortcut}
>
onPressEnd={ child.clickHandler }
>
{
!child.children ?
<div>
{child.title}
@@ -66,7 +70,7 @@ const DropdownMenu: React.FC<MenuDropdownProps> = ({ title, children, isOpen })
</div>
:
<div >
<SubmenuDropdown {...child} />
<SubmenuDropdown {...child} />
{
child.divide &&
<div className="w-full h-[0.5px] absolute bottom-[-0.5px] left-0 bg-default-200"></div>
+115 -96
View File
@@ -1,115 +1,134 @@
import { useTheme } from "next-themes";
import DropdownMenu, { MenuDropdownProps } from "./menu-dropdown";
import { useMemo } from "react";
import { menuItem } from "@heroui/react";
const menu: MenuDropdownProps[] = [
{
title: "File",
children: [{
title: "New"
},
{
title: "Open",
shortcut: "Ctnl + O"
}, {
title: "Save",
shortcut: "Ctnl + S",
divide: true
}, {
title: "Import",
divide: true,
children: [{
title: ".json",
}, {
title: ".dbml"
}, {
title: "MySql"
}, {
title: "Postgresql"
}]
}, {
title: "Export SQL",
children: [{
title: "Generic",
}, {
title: "MySql"
}, {
title: "Postgresql"
}]
}, {
title: "Export ORM Models",
divide: true
}, {
title: "Delete Project",
theme: "danger"
}]
}, {
title: "Edit",
children: [{
title: "Undo",
isDisabled: true,
},
{
title: "Redo",
isDisabled: true,
}, {
title: "Clear",
}]
} , {
title : "View" ,
children : [{
title : "Hide Controller" ,
shortcut : "Ctnl + B" ,
divide : true
} , {
title : "Zoom on scroll" ,
divide : true ,
children : [{
title : "On"
} , {
title : "Off"
}] ,
} , {
title : "Theme" ,
children : [{
title : "Light"
} , {
title : "Dark"
}]
}]
} , {
title : "Help" ,
children : [{
title : "Show Docs" ,
} , {
title : "Join Discord"
}]
}
]
interface MenuProps {
}
const Menu: React.FC<MenuProps> = ({ }) => {
const { resolvedTheme, setTheme } = useTheme()
const menu: MenuDropdownProps[] = useMemo(() => [
{
title: "File",
children: [{
title: "New"
},
{
title: "Open",
shortcut: "Ctnl + O"
}, {
title: "Save",
shortcut: "Ctnl + S",
divide: true
}, {
title: "Import",
divide: true,
children: [{
title: ".json",
}, {
title: ".dbml"
}, {
title: "MySql"
}, {
title: "Postgresql"
}]
}, {
title: "Export SQL",
children: [{
title: "Generic",
}, {
title: "MySql"
}, {
title: "Postgresql"
}]
}, {
title: "Export ORM Models",
divide: true
}, {
title: "Delete Project",
theme: "danger"
}]
}, {
title: "Edit",
clickHandler: () => {
console.log("hello wo")
},
children: [{
title: "Undo",
isDisabled: true,
},
{
title: "Redo",
isDisabled: true,
}, {
title: "Clear",
}]
}, {
title: "View",
children: [{
title: "Hide Controller",
shortcut: "Ctnl + B",
divide: true
}, {
title: "Zoom on scroll",
divide: true,
children: [{
title: "On"
}, {
title: "Off"
}],
}, {
title: "Theme",
clickHandler: () => {
console.log("hello wo")
},
children: [{
title: "Light",
clickHandler: () => {
setTheme("light") ;
}
}, {
title: "Dark",
clickHandler: () => {
setTheme("dark") ;
}
}]
}]
}, {
title: "Help",
children: [{
title: "Show Docs",
}, {
title: "Join Discord"
}]
}
], []);
return menu.map(menuItem => (
<DropdownMenu {...menuItem} />
<DropdownMenu {...menuItem} />
))
}
+5 -2
View File
@@ -1,5 +1,6 @@
import { NavbarBrand, NavbarContent, Navbar as NavbarContainer, Image, Dropdown, DropdownTrigger, Button, DropdownMenu, DropdownItem } from "@heroui/react";
import Menu from "../menu/menu";
import { useTheme } from "next-themes";
interface Props {
@@ -7,17 +8,19 @@ interface Props {
const Navbar: React.FC<Props> = ({ }) => {
const {resolvedTheme} = useTheme() ;
return (
<nav className="h-12 fixed z-50 bg-background w-full flex items-center p-4 border-b border-default-200">
<img
src="/stackrender.png"
src={`/stackrender.png`}
width={22}
alt="logo"
/>
<h3 className="font-semibold ml-2 text-slate-900 text-sm">StackRender</h3>
<h3 className="font-semibold ml-2 text-slate-900 text-sm dark:text-white">StackRender</h3>
<div className="ml-4">
<Menu/>
+2 -2
View File
@@ -22,7 +22,7 @@ const SidebarItem: React.FC<SidebarItemProps> = ({ title, icon, href , type = "i
return (
<Tooltip delayDuration={0} >
<TooltipTrigger asChild>
<Link to={href as string} className="p-2 rounded-md hover:bg-default-200 sidebar-item" data-active={isActive}>
<Link to={href as string} className="p-2 rounded-md hover:bg-default-200 sidebar-item " data-active={isActive}>
{icon}
</Link>
</TooltipTrigger>
@@ -36,7 +36,7 @@ const SidebarItem: React.FC<SidebarItemProps> = ({ title, icon, href , type = "i
)
else {
return <Divider className="bg-default-200" />
return <Divider className="bg-default-200 dark:bg-default-100" />
}
}
+4 -5
View File
@@ -8,13 +8,12 @@ interface SidebarProps {
children?: React.ReactNode
}
const sidebarItemClass : string = "text-gray-700 size-4 data-[active=true]:text-primary" ;
const sidebarItemClass : string = "text-gray-700 dark:text-default-500 size-4 data-[active=true]:text-primary" ;
const Sidebar: React.FC<SidebarProps> = ({ }) => {
const { t , i18n } = useTranslation() ;
const { t } = useTranslation() ;
const location = useLocation() ;
const sidebarItems: SidebarItemProps[] = useMemo(() => [
@@ -88,7 +87,7 @@ const Sidebar: React.FC<SidebarProps> = ({ }) => {
return (
<aside className="h-full z-[20] flex flex-col items-between py-2 justify-between sticky top-0 duration-500 w-12 bg-sidebar border-r pt-[56px]">
<aside className="h-full z-[20] flex flex-col items-between py-2 justify-between sticky top-0 duration-500 w-12 bg-sidebar dark:bg-background border-r pt-[56px] dark:border-default-100">
<div className="flex flex-col items-center gap-2">
{
sidebarItems.map((item: SidebarItemProps) => (
+2 -2
View File
@@ -20,10 +20,10 @@ const ToggleButton: React.FC<ToggleProps> = ({ active = false, children, onToggl
<Tooltip>
<TooltipTrigger asChild>
<button className={
cn("p-1 px-2 transition-all hover:bg-default rounded duration-200 ",
cn("p-1 px-2 transition-all hover:bg-default rounded duration-200 dark:text-default-400",
className ? className : "",
active ?
"text-slate-700 bg-default/80 " :
"text-slate-700 bg-default/80 dark:text-white dark:bg-default/50" :
"text-icon"
)
}
+2 -2
View File
@@ -17,9 +17,9 @@ const TooltipContent = React.forwardRef<
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 overflow-hidden rounded-md bg-foreground px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
'z-50 overflow-hidden rounded-md bg-foreground dark:bg-default-50 px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
+5 -2
View File
@@ -19,6 +19,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/to
import { Button } from "@heroui/react";
import { LayoutGrid } from "lucide-react";
import { adjustTablesPositions } from "@/utils/tables";
import { useTheme } from "next-themes";
@@ -29,6 +30,8 @@ const DatabasePage: React.FC<never> = () => {
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const { setIsConnectionInProgress } = useDiagram();
const [selectedNodeIds, setSelectedNodeIds] = useState<string[]>([]);
const { fitView } = useReactFlow();
const nodeTypes = useMemo(() => ({ table: Table }), []);
@@ -157,7 +160,7 @@ const DatabasePage: React.FC<never> = () => {
<div className="absolute top-[160px]">
</div>
<Controls>
<Controls >
<Tooltip>
<TooltipTrigger asChild>
<span>
@@ -179,7 +182,7 @@ const DatabasePage: React.FC<never> = () => {
</Tooltip>
</Controls>
<Background className="bg-default/30" />
<Background className="bg-default/30 dark:bg-black" />
</ReactFlow>
<svg style={{ position: 'absolute', width: 0, height: 0 }}>
@@ -9,7 +9,7 @@ interface Props {
const DBController : React.FC<Props> = ({ }) => {
return(
<div className="w-[640px] h-full bg-background border-r p-2 pt-[52px] ">
<div className="w-[640px] h-full bg-background border-r p-2 pt-[52px] border-default-100">
<Outlet/>
</div>
)
@@ -44,13 +44,13 @@ const RelationshipAccordionBody: React.FC<RelationshipAccordionBodyProps> = ({ r
<div className="w-full p-2 space-y-4">
<div className="flex">
<div className="w-full space-y-1">
<label className="font-medium flex text-slate-700 flex items-center gap-1 text-sm">
<FileOutput className="size-4" />
<label className="font-medium flex text-slate-700 flex items-center gap-1 text-sm dark:text-default-600">
<FileOutput className="size-4 " />
{t("db_controller.source_table")}
</label>
<Tooltip>
<TooltipTrigger>
<span className="truncate text-left text-sm">
<span className="truncate text-left text-sm dark:text-default-500">
{relationship.sourceTable?.name}({relationship.sourceField?.name})
</span>
</TooltipTrigger>
@@ -61,7 +61,7 @@ const RelationshipAccordionBody: React.FC<RelationshipAccordionBodyProps> = ({ r
</div>
<div className="w-full space-y-1">
<label className="font-medium flex text-slate-700 flex items-center gap-1 text-sm">
<label className="font-medium flex text-slate-700 flex items-center gap-1 text-sm dark:text-default-600">
<FileMinus2 className="size-4" />
{t("db_controller.target_table")}
@@ -69,7 +69,7 @@ const RelationshipAccordionBody: React.FC<RelationshipAccordionBodyProps> = ({ r
<Tooltip>
<TooltipTrigger>
<span className="truncate text-left text-sm ">
<span className="truncate text-left text-sm dark:text-default-500 ">
{relationship.targetTable?.name}({relationship.targetField?.name})
</span>
</TooltipTrigger>
@@ -80,7 +80,7 @@ const RelationshipAccordionBody: React.FC<RelationshipAccordionBodyProps> = ({ r
</div>
</div>
<div className="space-y-2">
<label className="flex text-sm font-medium flex text-slate-700 items-center gap-1">
<label className="flex text-sm font-medium flex text-slate-700 items-center gap-1 dark:text-default-600">
<ChevronsLeftRightEllipsis className="size-4 " />
{t('db_controller.cardinality.name')}
</label>
@@ -59,16 +59,16 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
onValueChange={setName}
onBlur={editRelationshipName}
type="text"
className="rounded-md px-2 py-0.5 w-full border-blue-400 focus-visible:ring-0 dark:bg-slate-900 text-sm "
className="rounded-md px-2 py-0.5 w-full border-blue-400 focus-visible:ring-0 text-sm "
/>
<Button
variant="light"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-200"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 "
size="sm"
onPress={editRelationshipName}
isIconOnly
>
<Check className="size-4 text-icon" />
<Check className="size-4 text-icon dark:text-white" />
</Button>
</>
}
@@ -77,7 +77,7 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
!editMode && <>
<label
className="w-full truncate px-2 py-1 text-sm font-semibold text-black"
className="w-full truncate px-2 py-1 text-sm font-semibold text-black dark:text-white"
>
{relationship.name ? relationship.name : defaultName}
</label>
@@ -89,7 +89,7 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
variant="light"
onPressEnd={() => focusOnRelationship(relationship.id, true)}
>
<Focus className="size-4 text-icon" />
<Focus className="size-4 text-icon dark:text-white" />
</Button>
<Button
size="sm"
@@ -97,7 +97,7 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
variant="light"
onPress={() => setEditMode(true)}
>
<Pencil className="size-4 text-icon" />
<Pencil className="size-4 text-icon dark:text-white" />
</Button>
</div>
@@ -110,7 +110,7 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
isIconOnly
variant="light"
>
<EllipsisVertical className="size-4 text-slate-500" />
<EllipsisVertical className="size-4 text-slate-500 dark:text-white" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[160px]" >
@@ -120,7 +120,7 @@ const RelationshipController: React.FC<Props> = ({ }) => {
aria-label={relationship.id}
classNames={{
trigger: "w-full hover:bg-default transition-all duration-200 h-12",
trigger: "w-full hover:bg-default transition-all duration-200 h-12 dark:bg-background dark:hover:bg-default-50",
base: "rounded-md p-0 overflow-hidden",
}}
subtitle={
@@ -85,7 +85,7 @@ const FieldItem: React.FC<Props> = ({ field }) => {
<div className="flex w-full gap-1 items-center " style={style} ref={setNodeRef} {...attributes}>
<div {...listeners}>
<GripVertical className="size-4 text-icon cursor-move" />
<GripVertical className="size-4 text-icon cursor-move dark:text-default-400 dark:hover:text-white" />
</div>
<Input
variant="bordered"
@@ -130,7 +130,7 @@ const FieldItem: React.FC<Props> = ({ field }) => {
isIconOnly
variant="light"
>
<EllipsisVertical className="size-4 text-slate-500" />
<EllipsisVertical className="size-4 text-slate-500 dark:text-default-600" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[210px]" >
@@ -87,7 +87,7 @@ const FieldList: React.FC<Props> = ({ table }) => {
radius="sm"
startContent={
<Plus className="size-4 text-icon" />
<Plus className="size-4 text-icon dark:text-white" />
}
className="h-8 p-2 text-xs bg-transparent hover:bg-default text-gray font-semibold"
onPressEnd={addField}
@@ -25,7 +25,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
const { editTable, createField } = useDatabase();
const onColorChange = useCallback((color: string | undefined) => {
editTable({ id: table.id, color: color ? color : null } as TableType);
}, [table]);
@@ -49,7 +49,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
}
return (
<div className="w-full">
<div className="w-full dark:bg-background">
<Accordion
isCompact
variant={"light"} selectionMode="multiple" selectedKeys={selectedKeys} onSelectionChange={setSelectedKeys as any}>
@@ -59,17 +59,15 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
'tarnsition-all duration-200 ',
isOpen ? "rotate-[-90deg]" : ""
)}>
<ChevronLeft className="size-4 text-icon" />
<ChevronLeft className="size-4 text-icon dark:text-default-600" />
</div>
)}
classNames={{
trigger: "hover:bg-default h-6 "
trigger: "hover:bg-default h-6 dark:hover:bg-background"
}}
subtitle={
<div className="group flex gap-2 items-center font-medium p-1 w-full hover:underline text-slate-500 hover:text-slate-600 transition-all duration-200">
<FileType className="size-4" />
<div className="group dark:text-default-600 flex gap-2 items-center font-medium p-1 w-full hover:underline text-slate-500 hover:text-slate-600 transition-all duration-200 dark:hover:text-white">
<FileType className="size-4 " />
<label className="text-sm w-full cursor-pointer">
{t("db_controller.fields")}
</label>
@@ -77,7 +75,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
className="size-4 p-0 text-xs opacity-0 group-hover:opacity-100 transition-all duration-200 hover:text-slate-700 text-icon"
onClick={addField}
>
<Plus className="size-4 " />
<Plus className="size-4 dark:text-default-600" />
</button>
</div>
}
@@ -90,14 +88,14 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
'tarnsition-all duration-200',
isOpen ? "rotate-[-90deg]" : ""
)}>
<ChevronLeft className="size-4 text-icon" />
<ChevronLeft className="size-4 text-icon dark:text-default-600" />
</div>
)}
classNames={{
trigger: "hover:bg-default h-6"
trigger: "hover:bg-default h-6 dark:hover:bg-background"
}}
subtitle={
<div className="flex gap-2 items-center font-medium p-1 w-full hover:underline text-slate-500 hover:text-slate-600 transition-all duration-200">
<div className="flex gap-2 dark:text-default-600 items-center font-medium p-1 w-full hover:underline text-slate-500 hover:text-slate-600 transition-all duration-200 dark:hover:text-white">
<FileKey className="size-4 " />
<label className="text-sm w-full cursor-pointer">
{t("db_controller.indexes")}
@@ -112,14 +110,14 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
'tarnsition-all duration-200',
isOpen ? "rotate-[-90deg]" : ""
)}>
<ChevronLeft className="size-4 text-icon" />
<ChevronLeft className="size-4 text-icon dark:text-default-600" />
</div>
)}
classNames={{
trigger: "hover:bg-default h-6"
trigger: "hover:bg-default h-6 dark:hover:bg-background"
}}
subtitle={
<div className="flex gap-2 items-center font-medium p-1 w-full hover:underline text-slate-500 hover:text-slate-600 transition-all duration-200">
<div className="flex gap-2 dark:text-default-600 items-center font-medium p-1 w-full hover:underline text-slate-500 hover:text-slate-600 transition-all duration-200 dark:hover:text-white">
<MessageSquareQuote className="size-4" />
<label className="text-sm w-full cursor-pointer">
{t("db_controller.note")}
@@ -128,7 +126,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
}>
<Textarea variant="bordered" className="w-full " label={t("db_controller.table_note")}
classNames={{
inputWrapper: "bg-default/80",
inputWrapper: "bg-default/80 ",
}}
value={note}
onValueChange={setNote}
@@ -137,7 +135,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
/>
</AccordionItem>
</Accordion>
<hr className="bg-default-200" />
<hr className="bg-default-200 dark:invisible" />
<div className="flex p-2 pb-0 items-start">
<div className="w-full">
<ColorPicker
@@ -150,7 +148,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
variant="ghost"
size="sm"
className="font-semibold p-4"
className="font-semibold p-4 border-default-50"
startContent={
<FileKey className="size-4 " />
}
@@ -161,7 +159,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
onPressEnd={addField}
variant="ghost"
size="sm"
className="font-semibold p-4"
className="font-semibold p-4 border-default-50"
startContent={
<FileType className="size-4 " />
}
@@ -40,8 +40,6 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
setPopOverOpen(false);
}
const addField = () => {
setPopOverOpen(false)
createField({
@@ -54,7 +52,7 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
}
return (
<div className="group w-full flex h-12 gap-1 border-l-4 flex p-2 items-center border-l-[6px] "
<div className="group w-full flex h-12 gap-1 border-l-4 flex p-2 items-center border-l-[6px] border-default"
style={{
borderColor: table.color as string | undefined
}}
@@ -63,10 +61,9 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
'tarnsition-all duration-200',
isOpen ? "rotate-[90deg]" : ""
)}>
<ChevronRight className="size-4 text-icon" />
<ChevronRight className="size-4 text-icon dark:text-white" />
</div>
<div className=" w-[1px] h-full bg-default-200">
<div className=" w-[1px] h-full bg-default-200 dark:bg-default-100">
</div>
{
!editMode &&
@@ -74,7 +71,7 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
<Tooltip>
<TooltipTrigger asChild>
<label
className="w-full text-editable truncate px-2 py-1 text-sm font-semibold text-black"
className="w-full text-editable truncate px-2 py-1 text-sm font-semibold text-black dark:text-white"
onDoubleClick={() => setEditMode(true)}
>
{table.name}
@@ -97,16 +94,16 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
variant="bordered"
onBlur={saveTableName}
type="text"
className="rounded-md px-2 py-0.5 w-full border-primary-700 focus-visible:ring-0 dark:bg-slate-900 text-sm "
className="rounded-md px-2 py-0.5 w-full border-primary-700 focus-visible:ring-0 text-sm "
/>
<Button
variant="light"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-200"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-white"
size="sm"
onPressEnd={saveTableName}
isIconOnly
>
<Check className="size-4 text-icon" />
<Check className="size-4 text-icon dark:text-white" />
</Button>
</>
}
@@ -122,7 +119,7 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
variant="light"
onPress={() => setEditMode(true)}
>
<Pencil className="size-4 text-icon" />
<Pencil className="size-4 text-icon dark:text-white" />
</Button>
<Button
size="sm"
@@ -130,7 +127,7 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
variant="light"
onPressEnd={() => focusOnTable(table.id , true)}
>
<Focus className="size-4 text-icon" />
<Focus className="size-4 text-icon dark:text-white" />
</Button>
</div>
@@ -141,7 +138,7 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
isIconOnly
variant="light"
>
<EllipsisVertical className="size-4 text-slate-500" />
<EllipsisVertical className="size-4 text-slate-500 dark:text-white" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[160px]" >
@@ -156,14 +153,14 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
key="add_field"
onPressEnd={addField}
endContent={
<FileType className="size-4 text-icon" />
<FileType className="size-4 text-icon dark:text-white" />
}>
Add Field
</ListboxItem>
<ListboxItem
key="add_index"
endContent={<FileKey className="size-4 text-icon " />}
endContent={<FileKey className="size-4 text-icon dark:text-white" />}
showDivider>
Add Index
</ListboxItem>
@@ -9,6 +9,7 @@ import { useDatabase } from "@/providers/database-provider/database-provider";
import { TableType } from "@/lib/schemas/table-schema";
import { v4 } from "uuid";
import { useDiagram } from "@/providers/diagram-provider/diagram-provider";
import { useTheme } from "next-themes";
interface Props { }
@@ -42,8 +43,9 @@ const TablesController: React.FC<Props> = ({ }) => {
}
} , [focusedTableId]) ;
const selectedTableId = selectedTable.values().next().value;
const selectedTableId = selectedTable.values().next().value;
return (
<div className="w-full h-full flex flex-col gap-2">
<div className="flex items-center justify-between gap-4 py-1">
@@ -103,7 +105,7 @@ const TablesController: React.FC<Props> = ({ }) => {
<Accordion
hideIndicator
variant="splitted"
variant={ "splitted"}
selectedKeys={selectedTable}
onSelectionChange={setSelectedTable as any}
isCompact
@@ -114,8 +116,8 @@ const TablesController: React.FC<Props> = ({ }) => {
key={table.id}
aria-label={table.name}
classNames={{
trigger: "w-full h-12 hover:bg-default transition-all duration-200",
base: "rounded-md shadow p-0 overflow-hidden",
trigger: "w-full h-12 hover:bg-default transition-all duration-200 dark:bg-background dark:hover:bg-default-50",
base: "rounded-md shadow p-0 overflow-hidden dark:border-1 dark:border-default-100",
}}
subtitle={
<TableAccordionHeader
+7 -7
View File
@@ -52,7 +52,7 @@ const Field: React.FC<Props> = ({ field, showHandles, highlight }) => {
return (
<div className={cn(
"group relative flex h-8 items-center justify-between gap-1 border-t border-default px-3 text-sm last:rounded-b-[6px] hover:bg-slate-100 dark:hover:bg-slate-800 transition-all duration-200 ease-in-out" ,
"group relative flex h-8 items-center justify-between gap-1 border-t border-default dark:border-default/5 px-3 text-sm last:rounded-b-[6px] hover:bg-slate-100 dark:hover:bg-primary-500 transition-all duration-200 ease-in-out" ,
highlight ? "bg-primary/5" : ""
)}>
{
@@ -60,14 +60,14 @@ const Field: React.FC<Props> = ({ field, showHandles, highlight }) => {
<>
<label
className={cn(
"truncate text-xs text-slate-900",
"truncate text-xs text-slate-900 dark:text-default-200",
field.isPrimary ? "font-semibold" : ""
)}
onDoubleClick={() => setEditMode(true)}
>
{field.name}
</label>
<span className={cn("content-center truncate flex items-center h-full gap-1 text-right text-xs text-default-600 group-hover:hidden font-semibold text-icon",
<span className={cn("content-center truncate flex items-center h-full gap-1 text-right text-xs text-default-600 group-hover:hidden font-semibold text-icon dark:text-default-200",
field.isPrimary ? "font-semibold text-slate-700" : ""
)}>
{field.isPrimary && <KeyRound className="size-3" />} {field.type?.name?.split(' ')[0]}{field.nullable ? "?" : ""}
@@ -101,12 +101,12 @@ const Field: React.FC<Props> = ({ field, showHandles, highlight }) => {
onClick={(e) => e.stopPropagation()}
onChange={(e) => setFieldName(e.target.value)}
className="rounded-md outline-none px-2 py-0.5 w-full border-[0.5px] border-primary-700 bg-slate-100 focus-visible:ring-0 dark:bg-slate-900 text-sm "
className="rounded-md outline-none px-2 py-0.5 w-full border-[0.5px] border-primary-700 bg-slate-100 focus-visible:ring-0 text-sm dark:bg-transparent dark:text-white"
/>
<Button
variant="light"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-200"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-white"
size="sm"
isIconOnly
onPress={saveFieldName}
@@ -125,12 +125,12 @@ const Field: React.FC<Props> = ({ field, showHandles, highlight }) => {
type="source"
position={Position.Left}
id={LEFT_PREFIX + field.id}
className="w-4 h-4 border-4 bg-primary"
className="w-4 h-4 border-4 bg-primary dark:border-default-900"
/>
<Handle
type="source"
position={Position.Right}
className="w-4 h-4 border-4 bg-primary"
className="w-4 h-4 border-4 bg-primary dark:border-default-900"
id={RIGHT_PREFIX + field.id}
/>
</div>
+1 -1
View File
@@ -125,7 +125,7 @@ const Relationship: React.FC<EdgeProps<RelationshipProps>> = (props) => {
fill="none"
className={cn([
`!stroke-2 ${selected ? '!stroke-primary' : 'stroke-slate-300'}`,
`!stroke-2 ${selected ? '!stroke-primary' : 'stroke-slate-300 dark:stroke-default-500'}`,
])}
onClick={(e) => {
+72 -76
View File
@@ -16,7 +16,7 @@ import { FieldType } from "@/lib/schemas/field-schema";
import { TableType } from "@/lib/schemas/table-schema";
import { useDatabase } from "@/providers/database-provider/database-provider";
import { useTranslation } from "react-i18next";
import { RelationshipType } from "@/lib/schemas/relationship-schema";
import { RelationshipType } from "@/lib/schemas/relationship-schema";
import { useDiagram } from "@/providers/diagram-provider/diagram-provider";
@@ -65,90 +65,86 @@ const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table } }) =
highlight={highlight}
/>)
})
}, [table.fields, selected, highlightedEdges]);
}, [table.fields, selected, highlightedEdges]);
// console.log ("re-render" , table.name)
return (
<Card className={cn(
"w-full h-full bg-background rounded-lg entity-card noselect overflow-visible ",
selected
? 'ring-1 ring-primary'
: '',
)}
shadow="sm"
>
<div className="px-[2px] ">
<div
className=" border-t-[4px] rounded-t-[6px] border-primary"
style={{ borderColor: table.color as string }}
></div>
</div>
<div className="group gap-2 flex h-9 items-center justify-between bg-default/50 px-2 dark:bg-default ">
<Table2 className="size-4 shrink-0 text-icon dark:text-primary" />
{
editMode && <>
<input
placeholder={table.name}
autoFocus
onChange={(event: any) => setTableName(event.target.value)}
value={tableName}
onBlur={saveTableName}
type="text"
className="rounded-md outline-none px-2 py-0.5 w-full border-[0.5px] border-primary-700 font-bold bg-slate-100 focus-visible:ring-0 dark:bg-slate-900 text-sm "
/>
<Button
variant="light"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-200"
size="sm"
onPressEnd={saveTableName}
isIconOnly
>
<Check className="size-3" />
</Button>
</>
}
{
!editMode &&
<>
<Tooltip>
<TooltipTrigger asChild>
<label
className=" w-full text-editable truncate px-2 py-0.5 text-sm font-bold"
onDoubleClick={() => setEditMode(true)}
>
{table.name}
</label>
</TooltipTrigger>
<TooltipContent>
{t("table.double_click")}
</TooltipContent>
</Tooltip>
<div className="hidden shrink-0 flex-row group-hover:flex">
<Card className={cn(
"w-full h-full bg-background rounded-lg entity-card noselect overflow-visible dark:bg-default-900",
selected
? 'ring-2 ring-primary'
: '',
)}
shadow="sm"
>
<div className="px-[2px] ">
<div
className=" border-t-[4px] rounded-t-[6px] border-primary"
style={{ borderColor: table.color as string }}
></div>
</div>
<div className="group gap-2 flex h-9 items-center justify-between bg-default/50 px-2 dark:bg-black/20">
<Table2 className="size-4 shrink-0 text-icon dark:text-white" />
{
editMode && <>
<input
placeholder={table.name}
autoFocus
onChange={(event: any) => setTableName(event.target.value)}
value={tableName}
onBlur={saveTableName}
type="text"
className="rounded-md outline-none px-2 py-0.5 w-full border-[0.5px] border-primary-700 font-bold bg-slate-100 focus-visible:ring-0 text-sm dark:bg-transparent dark:text-white"
/>
<Button
variant="light"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-white"
size="sm"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-200"
onPressEnd={saveTableName}
isIconOnly
onPressEnd={focus}
>
<Focus className="size-4 text-icon" />
<Check className="size-3" />
</Button>
</div>
</>
}
</div>
<div className="transition-[max-height] duration-200 ease-in-out">
{fields}
</div>
</Card>
</>
}
{
!editMode &&
<>
<Tooltip>
<TooltipTrigger asChild>
<label
className=" w-full text-editable truncate px-2 py-0.5 text-sm font-bold dark:text-white dark:group-hover:bg-default-900"
onDoubleClick={() => setEditMode(true)}
>
{table.name}
</label>
</TooltipTrigger>
<TooltipContent className="dark:bg-default-900">
{t("table.double_click")}
</TooltipContent>
</Tooltip>
<div className="hidden shrink-0 flex-row group-hover:flex">
<Button
variant="light"
size="sm"
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:hover:bg-default-800 "
isIconOnly
onPressEnd={focus}
>
<Focus className="size-4 text-icon dark:text-white" />
</Button>
</div>
</>
}
</div>
<div className="transition-[max-height] duration-200 ease-in-out">
{fields}
</div>
</Card>
)
};
+5 -9
View File
@@ -5,7 +5,7 @@
.text-editable {
@apply dark:group-hover:bg-slate-900 group-hover:bg-slate-100 group-hover:ring-[0.5px] group-hover:ring-primary-700 rounded-md cursor-pointer;
@apply dark:group-hover:bg-default-50 group-hover:bg-slate-100 group-hover:ring-[0.5px] group-hover:ring-primary-700 dark:group-hover:ring-white rounded-md cursor-pointer ;
}
@@ -72,23 +72,19 @@ div[data-slot="content"] hr[role="separator"] {
}
/* Track (background of the scrollbar) */
::-webkit-scrollbar-track {
background: hsl(210 40% 96.1%) ; /* Light gray background */
}
/* Handle (the scroll thumb) */
::-webkit-scrollbar-thumb {
background: hsl(var(--heroui-default-300)); /* Darker gray */
background: hsl(var(--heroui-default-100)); /* Darker gray */
border-radius: 6px; /* Rounded corners */
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: hsl(var(--nextui-default-400)); /* Darker on hover */
background: hsl(var(--heroui-default-300)); /* Darker on hover */
}
+4 -8
View File
@@ -14,10 +14,6 @@ export default {
themes: {
dark: {
colors: {
sidebar: {
DEFAULT: 'hsl(0 0% 98%)',
foreground: 'hsl(240 5.3% 26.1%)',
},
primary: {
DEFAULT: "#9822ff",
50: '#9822ff0D', // 5% opacity
@@ -31,7 +27,7 @@ export default {
800: '#961FFFCC', // 80% opacity
900: '#9822ffE6', // 90% opacity
},
},
}
},
light: {
colors: {
@@ -45,9 +41,9 @@ export default {
foreground: 'hsl(222.2 47.4% 11.2%)'
},
icon : {
DEFAULT : "hsl(215.4 16.3% 46.9%)"
} ,
icon: {
DEFAULT: "hsl(215.4 16.3% 46.9%)"
},
primary: {
DEFAULT: "#9822ff",