Init Commit

This commit is contained in:
KarimTamani
2025-05-04 19:17:04 +01:00
commit d9905351bd
61 changed files with 3015 additions and 0 deletions
@@ -0,0 +1,56 @@
import { colorOptions } from "@/lib/colors";
import { Button, Popover, PopoverContent, PopoverTrigger } from "@heroui/react";
import { useCallback, useState } from "react";
interface ColorPickerProps {
defaultColor?: string;
onChange?: (color: string) => void
}
const ColorPicker: React.FC<ColorPickerProps> = ({ defaultColor, onChange }) => {
const [color , setColor] = useState<string>( defaultColor as string ) ;
const [isOpen, setIsOpen] = useState(false);
const onSelect = useCallback((color: string) => {
setIsOpen(false);
onChange && onChange(color) ;
setColor ( color)
}, [onChange])
return (
<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"
isIconOnly
style={{
backgroundColor : color
}}
>
</Button>
</PopoverTrigger>
<PopoverContent>
<div className="grid grid-cols-4 gap-2">
{colorOptions.map(color => (
<div
key={color}
className="size-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md"
style={{
backgroundColor: color
}}
onClick={() => onSelect(color)}
>
</div>
))}
</div>
</PopoverContent>
</Popover>
)
}
export default ColorPicker;
+90
View File
@@ -0,0 +1,90 @@
import { Dropdown, DropdownMenu as HeroDropdownMenu, DropdownItem, DropdownTrigger, Button, useDisclosure, Popover, PopoverTrigger, PopoverContent, Divider, DropdownSection } from "@heroui/react";
import { useMemo, useState } from "react";
import SubmenuDropdown from "./submenu-dropdown";
export interface MenuDropdownProps {
title?: string;
children?: MenuDropdownProps[];
theme?: "default" | "danger",
isDisabled?: boolean,
divide?: boolean,
shortcut?: string,
isOpen?: boolean
}
const DropdownMenu: React.FC<MenuDropdownProps> = ({ title, children, isOpen }) => {
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 >
<Button size="sm" variant="light" >
<span className=" text-left flex justify-between text-small font-semibold">
{title}
</span>
</Button>
</DropdownTrigger>
}
<HeroDropdownMenu disabledKeys={disabledChilds} >
{
children ? children?.map((child: MenuDropdownProps) => (
<DropdownItem
key={child.title as string}
className={child.theme == "danger" ? "text-danger" : ""}
color={child.theme}
shortcut={child.shortcut}
>
{
!child.children ?
<div>
{child.title}
{
child.divide &&
<div className="w-full h-[0.5px] absolute bottom-[-0.5px] left-0 bg-default-200"></div>
}
</div>
:
<div >
<SubmenuDropdown {...child} />
{
child.divide &&
<div className="w-full h-[0.5px] absolute bottom-[-0.5px] left-0 bg-default-200"></div>
}
</div>
}
</DropdownItem>
)) : []
}
</HeroDropdownMenu>
</Dropdown>
}
export default DropdownMenu;
+117
View File
@@ -0,0 +1,117 @@
import DropdownMenu, { MenuDropdownProps } from "./menu-dropdown";
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> = ({ }) => {
return menu.map(menuItem => (
<DropdownMenu {...menuItem} />
))
}
export default Menu;
+35
View File
@@ -0,0 +1,35 @@
import { Button, Popover, PopoverContent, PopoverTrigger, useDisclosure } from "@heroui/react";
import { MenuDropdownProps } from "./menu-dropdown";
import { ChevronRight } from "lucide-react";
import DropdownMenu from "./menu-dropdown";
const SubmenuDropdown: React.FC<MenuDropdownProps> = ({ title, children }) => {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<div onMouseEnter={onOpen} onMouseLeave={onClose} >
<Popover placement="right" isOpen={isOpen} radius="sm" offset={90} >
<PopoverTrigger>
<Button className="w-full h-6 bg-transparent p-0" size="sm" value={"light"}>
<span className="w-full text-left flex justify-between text-small font-normal">
{title}
<ChevronRight className="size-4 text-icon" />
</span>
</Button>
</PopoverTrigger>
<PopoverContent>
<DropdownMenu
children={children}
/>
</PopoverContent>
</Popover>
</div>
)
};
export default SubmenuDropdown;
+31
View File
@@ -0,0 +1,31 @@
import { NavbarBrand, NavbarContent, Navbar as NavbarContainer, Image, Dropdown, DropdownTrigger, Button, DropdownMenu, DropdownItem } from "@heroui/react";
import Menu from "../menu/menu";
interface Props {
}
const Navbar: React.FC<Props> = ({ }) => {
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"
width={22}
alt="logo"
/>
<h3 className="font-semibold ml-2 text-slate-900 text-sm">StackRender</h3>
<div className="ml-4">
<Menu/>
</div>
</nav>
)
}
export default Navbar;
+44
View File
@@ -0,0 +1,44 @@
import { Link } from "react-router-dom";
import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip/tooltip";
import { Divider } from "@heroui/react";
import React from "react";
export interface SidebarItemProps {
type?: "item" | "divider"
title?: string;
icon?: React.ReactNode;
href?: string;
isActive? : boolean
}
const SidebarItem: React.FC<SidebarItemProps> = ({ title, icon, href , type = "item" , isActive}) => {
if (type == "item")
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}>
{icon}
</Link>
</TooltipTrigger>
<TooltipContent
side="left"
align="center"
>
{title}
</TooltipContent>
</Tooltip>
)
else {
return <Divider className="bg-default-200" />
}
}
export default SidebarItem
+116
View File
@@ -0,0 +1,116 @@
import { BookOpen, EarthLock, GitCommitHorizontal, Github, Settings, Sparkles, Table, TableProperties, Waypoints, Workflow, X, Zap } from "lucide-react";
import SidebarItem, { SidebarItemProps } from "./sidebar-item";
import { useMemo } from "react";
import { useLocation } from "react-router-dom";
import { useTranslation } from 'react-i18next';
interface SidebarProps {
children?: React.ReactNode
}
const sidebarItemClass : string = "text-gray-700 size-4 data-[active=true]:text-primary" ;
const Sidebar: React.FC<SidebarProps> = ({ }) => {
const { t , i18n } = useTranslation() ;
console.log ( i18n.language)
console.log ()
const location = useLocation() ;
const sidebarItems: SidebarItemProps[] = useMemo(() => [
{
title: t("sidebar.tables"),
icon: <TableProperties className={sidebarItemClass}></TableProperties>,
href: "/database/tables",
isActive : location.pathname.endsWith("/database/tables")
},
{
title: t("sidebar.relationships"),
icon: <Workflow className={sidebarItemClass}></Workflow>,
href: "/database/relationships",
isActive : location.pathname.endsWith("/database/relationships")
},
{
title: "AI",
icon: <Sparkles className={sidebarItemClass}></Sparkles>,
href: "/database/bot",
isActive : location.pathname.endsWith("/database/bot")
},
{
type: "divider"
},
{
title: "API",
icon: <Zap className={sidebarItemClass}></Zap>,
href: "/database/ai-bot",
},
{
title: "Authentication",
icon: <EarthLock className={sidebarItemClass}></EarthLock>,
href: "/database/ai-bot",
},
{
title: "Controllers",
icon: <Waypoints className={sidebarItemClass}></Waypoints>,
href: "/database/ai-bot",
},
{
type: "divider"
},
{
title: "Git",
icon: <GitCommitHorizontal className={sidebarItemClass}></GitCommitHorizontal>,
href: "/database/ai-bot",
},
{
type: "divider"
},
{
title: "Project Setting",
icon: <Settings className={sidebarItemClass}></Settings>,
href: "/database/ai-bot",
},
], [location])
const bottomSidebarItems: SidebarItemProps[] = useMemo(() => [
{
title: "Github",
icon: <Github className={sidebarItemClass}></Github>,
href: "/database/ai-bot",
},
{
title: "Docs",
icon: <BookOpen className={sidebarItemClass}></BookOpen>,
href: "/database/ai-bot",
},
], []) ;
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]">
<div className="flex flex-col items-center gap-2">
{
sidebarItems.map((item: SidebarItemProps) => (
<SidebarItem
{...item}
/>
))
}
</div>
<div className="flex flex-col items-center gap-2 ">
{
bottomSidebarItems.map((item: SidebarItemProps) => (
<SidebarItem
{...item}
/>
))
}
</div>
</aside>
)
}
export default Sidebar;
+30
View File
@@ -0,0 +1,30 @@
import React from 'react';
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
import { cn } from '@heroui/react';
const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = TooltipPrimitive.Trigger;
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<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',
className
)}
{...props}
/>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };