mirror of
https://github.com/stackrender/stackrender.git
synced 2026-09-10 03:05:42 +00:00
Obsidian theme developed
This commit is contained in:
@@ -10,7 +10,7 @@ interface AutocompleteProps {
|
||||
onSelectionChange?: (item: any) => void,
|
||||
placeholder?: string
|
||||
isDisabled?: boolean
|
||||
selectedItem?: Key
|
||||
selectedItem?: Key
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,15 @@ const Autocomplete: React.FC<AutocompleteProps> = ({ items, label = "name", onSe
|
||||
variant="bordered"
|
||||
aria-label={placeholder}
|
||||
placeholder={placeholder}
|
||||
selectedKey={selectedItem as any }
|
||||
selectedKey={selectedItem as any}
|
||||
inputProps={{
|
||||
classNames: {
|
||||
|
||||
inputWrapper: "border-divider group-hover:border-primary",
|
||||
|
||||
},
|
||||
}}
|
||||
|
||||
>
|
||||
{(item: any) => <AutocompleteItem key={item.id}>{item[label]}</AutocompleteItem>}
|
||||
</HeroUiAutocomplete>
|
||||
|
||||
@@ -41,8 +41,8 @@ const CardinalityMarker: React.FC<CardinalityMarkerProps> = ({ selected = false,
|
||||
>
|
||||
<svg
|
||||
fill="transparent"
|
||||
className={selected ? "stroke-primary" : "stroke-slate-300 dark:stroke-default-400"}
|
||||
strokeWidth="8"
|
||||
className={selected ? "stroke-primary" : "stroke-default-600 dark:stroke-default-400"}
|
||||
strokeWidth="4"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 100 100">
|
||||
|
||||
@@ -28,10 +28,10 @@ const ColorPicker: React.FC<ColorPickerProps> = ({ defaultColor, onChange }) =>
|
||||
}, [onChange])
|
||||
|
||||
return (
|
||||
<Popover placement="bottom" radius="sm" isOpen={isOpen} onOpenChange={(open) => setIsOpen(open)}>
|
||||
<Popover placement="bottom" radius="sm" isOpen={isOpen} onOpenChange={(open) => setIsOpen(open)} shadow="sm">
|
||||
<PopoverTrigger>
|
||||
<Button size="sm"
|
||||
className="size-8 cursor-pointer rounded-md border-2 border-muted transition-shadow hover:shadow-md dark:border-default-100"
|
||||
className="size-8 cursor-pointer rounded-md border-2 transition-shadow hover:shadow-md border-divider"
|
||||
isIconOnly
|
||||
style={{
|
||||
backgroundColor: color ? color : undefined
|
||||
@@ -45,7 +45,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 dark:border-default-100"
|
||||
className="size-8 cursor-pointer rounded-md border-2 transition-shadow hover:shadow-md border-divider"
|
||||
style={{
|
||||
backgroundColor: color
|
||||
}}
|
||||
|
||||
@@ -27,12 +27,12 @@ const DropdownMenu: React.FC<MenuDropdownProps> = ({ title, children, clickHand
|
||||
return children ? children?.filter((child: MenuDropdownProps) => child.isDisabled && child.title).map((child: MenuDropdownProps) => child.title as string) : []
|
||||
}, [children]);
|
||||
|
||||
return <Dropdown radius="sm" showArrow >
|
||||
return <Dropdown radius="sm" shadow="sm" showArrow >
|
||||
{
|
||||
title &&
|
||||
<DropdownTrigger onPressEnd={clickHandler}>
|
||||
<DropdownTrigger onPressEnd={clickHandler}>
|
||||
<Button size="sm" variant="light" className="min-w-[42px]" >
|
||||
<span className=" text-left flex justify-between text-small font-semibold">
|
||||
<span className=" text-left flex justify-between text-small font-semibold text-font/90">
|
||||
{title}
|
||||
</span>
|
||||
</Button>
|
||||
@@ -48,16 +48,19 @@ const DropdownMenu: React.FC<MenuDropdownProps> = ({ title, children, clickHand
|
||||
className={child.theme == "danger" ? "text-danger" : ""}
|
||||
color={child.theme}
|
||||
shortcut={child.shortcut}
|
||||
classNames={{
|
||||
shortcut : "dark:border-font/10"
|
||||
}}
|
||||
onPressEnd={ child.clickHandler }
|
||||
>
|
||||
{
|
||||
|
||||
!child.children ?
|
||||
<div>
|
||||
<div className="text-font/90">
|
||||
{child.title}
|
||||
{
|
||||
child.divide &&
|
||||
<div className="w-full h-[0.5px] absolute bottom-[-0.5px] left-0 bg-default-200"></div>
|
||||
<div className="w-full h-[0.5px] absolute bottom-[-0.5px] left-0 bg-divider dark:bg-font/10"></div>
|
||||
}
|
||||
</div>
|
||||
:
|
||||
@@ -65,7 +68,7 @@ const DropdownMenu: React.FC<MenuDropdownProps> = ({ title, children, clickHand
|
||||
<SubmenuDropdown {...child} />
|
||||
{
|
||||
child.divide &&
|
||||
<div className="w-full h-[0.5px] absolute bottom-[-0.5px] left-0 bg-default-200"></div>
|
||||
<div className="w-full h-[0.5px] absolute bottom-[-0.5px] left-0 bg-divider dark:bg-font/10"></div>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ const Menu: React.FC<MenuProps> = ({ }) => {
|
||||
|
||||
const { setTheme } = useTheme()
|
||||
|
||||
|
||||
const { t } = useTranslation();
|
||||
const menu: MenuDropdownProps[] = useMemo(() => [
|
||||
{
|
||||
|
||||
@@ -11,9 +11,9 @@ const SubmenuDropdown: React.FC<MenuDropdownProps> = ({ title, children }) => {
|
||||
|
||||
return (
|
||||
<div onMouseEnter={onOpen} onMouseLeave={onClose} >
|
||||
<Popover placement="right" isOpen={isOpen} radius="sm" offset={90} >
|
||||
<Popover placement="right" isOpen={isOpen} radius="sm" offset={90} shadow="sm">
|
||||
<PopoverTrigger>
|
||||
<Button className="w-full h-6 bg-transparent p-0" size="sm" value={"light"}>
|
||||
<Button className="w-full h-6 bg-transparent p-0 text-font/90" size="sm" value={"light"}>
|
||||
<span className="w-full text-left flex justify-between text-small font-normal">
|
||||
{title}
|
||||
<ChevronRight className="size-4 text-icon" />
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
|
||||
interface ModalProps {
|
||||
export interface ModalProps {
|
||||
isOpen?: boolean,
|
||||
onOpenChange: (open: boolean) => void,
|
||||
className?: string,
|
||||
@@ -22,7 +22,6 @@ interface ModalProps {
|
||||
actionName?: string,
|
||||
actionHandler?: () => void ,
|
||||
isDisabled? : boolean
|
||||
|
||||
}
|
||||
|
||||
const Modal: React.FC<ModalProps> = ({ isOpen, onOpenChange, className, backdrop = "opaque", title, children, actionName = "Action" , actionHandler , isDisabled}) => {
|
||||
|
||||
@@ -83,8 +83,8 @@ const ConnectionStatus: React.FC = () => {
|
||||
<div className="flex items-center ">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="border-1 flex gap-2 p-2 px-3 rounded-lg text-xs relative overflow-hidden dark:border-default ">
|
||||
<span className={cn("flex gap-2 font-semibold", syncStatus.connected ? "text-success-400" : "text-danger-400")}>
|
||||
<div className="border-1 flex gap-2 p-2 px-3 rounded-lg text-xs relative overflow-hidden border-divider dark:border-font/10">
|
||||
<span className={cn("flex gap-2 font-semibold", syncStatus.connected ? "text-success" : "text-danger")}>
|
||||
{
|
||||
syncStatus.connected ?
|
||||
<Wifi className="size-4 " /> :
|
||||
@@ -102,7 +102,7 @@ const ConnectionStatus: React.FC = () => {
|
||||
{
|
||||
downloadProgress !== undefined ?
|
||||
<>
|
||||
<span className="text-slate-500 dark:text-white w-[96px] truncate">
|
||||
<span className="text-font/90 w-[96px] truncate">
|
||||
Saving ...
|
||||
</span>
|
||||
|
||||
@@ -121,7 +121,7 @@ const ConnectionStatus: React.FC = () => {
|
||||
|
||||
</>
|
||||
:
|
||||
<span className="text-slate-500 dark:text-white w-[96px] truncate font-semibold" >
|
||||
<span className="text-font/70 truncate font-semibold" >
|
||||
Saved {deltaUploadTime}
|
||||
</span>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ 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">
|
||||
<nav className="h-12 fixed z-50 bg-background w-full flex items-center p-4 border-b border-divider ">
|
||||
<img
|
||||
src={`/stackrender.png`}
|
||||
width={22}
|
||||
|
||||
@@ -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-100 dark:hover:bg-background-50 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 dark:bg-default-100" />
|
||||
return <Divider className="bg-default-200 dark:bg-divider" />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ interface SidebarProps {
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
const sidebarItemClass: string = "text-gray-700 dark:text-default-500 size-4 data-[active=true]:text-primary";
|
||||
const sidebarItemClass: string = "text-font size-4 data-[active=true]:text-primary-900";
|
||||
|
||||
const Sidebar: React.FC<SidebarProps> = ({ }) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -87,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 dark:bg-background border-r pt-[56px] dark:border-default-100">
|
||||
<aside className="h-full z-[20] flex flex-col items-between py-2 justify-between sticky top-0 duration-500 w-12 bg-sidebar pt-[56px] dark:bg-background">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
{
|
||||
sidebarItems.map((item: SidebarItemProps , index : number) => (
|
||||
|
||||
@@ -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 dark:text-default-400",
|
||||
cn("p-1 px-2 transition-all hover:bg-default hover:text-font/90 rounded duration-200 ",
|
||||
className ? className : "",
|
||||
active ?
|
||||
"text-slate-700 bg-default/80 dark:text-white dark:bg-default/50" :
|
||||
"text-font/90 bg-default dark:text-font/90 white dark:bg-default/40" :
|
||||
"text-icon"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ const TooltipContent = React.forwardRef<
|
||||
sideOffset={sideOffset}
|
||||
|
||||
className={cn(
|
||||
'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',
|
||||
'z-50 shadow-lg overflow-hidden rounded-md bg-foreground px-3 py-2 text-xs text-default 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}
|
||||
|
||||
@@ -70,7 +70,11 @@ const useHighlightedEdges = (nodes: Node[], relationships: RelationshipType[], e
|
||||
(edge.animated || edge.selected) &&
|
||||
(edge.source === node.id || edge.target === node.id)
|
||||
);
|
||||
const newHighlightedEdgeIds = newHighlightedEdges.map((edge : Edge) => edge.id) ;
|
||||
const previousHighlightedEdgeIds = node.data.highlightedEdges.map((edge : Edge) => edge.id) ;
|
||||
|
||||
if (areArraysEqual(previousHighlightedEdgeIds , newHighlightedEdgeIds))
|
||||
return node ;
|
||||
// Add the highlighted edges to the node's data
|
||||
return {
|
||||
...node,
|
||||
|
||||
@@ -3,13 +3,15 @@ import { TableType } from "@/lib/schemas/table-schema";
|
||||
import { Node, useReactFlow } from "@xyflow/react";
|
||||
import { useEffect } from "react";
|
||||
import { getDefaultTableOverlapping } from "@/utils/tables";
|
||||
import hash from "object-hash";
|
||||
import { compare } from "fast-json-patch";
|
||||
export const useTableToNode = (tables: TableType[]): void => {
|
||||
const { setNodes } = useReactFlow();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const nodes = tables.map((table: TableType) => {
|
||||
const tableNodes = tables.map((table: TableType) => {
|
||||
return {
|
||||
id: table.id,
|
||||
type: "table",
|
||||
@@ -29,7 +31,30 @@ export const useTableToNode = (tables: TableType[]): void => {
|
||||
} as Node
|
||||
})
|
||||
|
||||
setNodes(nodes);
|
||||
setNodes((nodes) => {
|
||||
|
||||
return tableNodes.map((tableNode) => {
|
||||
const node: Node | undefined = nodes.find((node: Node) => node.id == tableNode.id);
|
||||
if (!node)
|
||||
return tableNode;
|
||||
else {
|
||||
// const diff = compare(node.data.table as TableType, tableNode.data.table as TableType);
|
||||
|
||||
|
||||
//console.log (diff)
|
||||
|
||||
const hashNode: string = hash(node.data.table as TableType);
|
||||
const hashTableNode: string = hash(tableNode.data.table as TableType);
|
||||
|
||||
|
||||
|
||||
return hashNode == hashTableNode ? node : tableNode;
|
||||
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
}, [tables])
|
||||
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ i18n.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources ,
|
||||
lng: 'en',
|
||||
lng: 'ar',
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
|
||||
+120
@@ -1,6 +1,126 @@
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--color-primary-opacity: rgba(131, 93, 255, 0.05);
|
||||
--color-bg-gray: #f5f7f9;
|
||||
--color-font-light-hover: rgba(131, 93, 255, 0.7);
|
||||
--color-bg-light-hover: rgba(131, 93, 255, 0.1);
|
||||
--color-dashed: #e9ebf1;
|
||||
--color-bg-page: #F6F8FA;
|
||||
--color-bg-right: #ffffff;
|
||||
--color-bg-sidebar: #F1F3F6;
|
||||
--header-bg-color: #FFFFFF;
|
||||
--search-bg-color: #f2f4f7;
|
||||
--table-header-bg-color: #f9fafb;
|
||||
--search-big-bg-color: #f2f4f7;
|
||||
--search-big-select-color: #ffffff;
|
||||
--color-bg-folder-hover: #EEF1F4;
|
||||
--color-deep-gray: #e4e6ea;
|
||||
--select-hover-color: rgba(131, 93, 255, 0.05);
|
||||
--form-hover-color: #f9fafb;
|
||||
--highlight-change-color: #ffffff;
|
||||
--divider-color: #eef0f3;
|
||||
--color-border: #f2f4f6;
|
||||
--color-header-border: #f2f4f6;
|
||||
--color-border-light: rgba(238, 240, 243, 0.5);
|
||||
--icon-color: #a2a4a8;
|
||||
--font-title-color: #47494b;
|
||||
--font-content-color: rgba(51, 54, 57, 0.9);
|
||||
--font-light-color: rgba(51, 54, 57, 0.4);
|
||||
--font-pale-color: rgba(51, 54, 57, 0.6);
|
||||
--box-shadow-modal-color: rgba(16, 24, 40, 0.06);
|
||||
--line-height: 1.4;
|
||||
--border-radius: 4px;
|
||||
--color-primary: #835DFF;
|
||||
--icon-primary-color: #835DFF;
|
||||
--selected-border-color: rgba(131, 93, 255, 0.5);
|
||||
--white-color: #fff;
|
||||
--popover-select-bg-color: #ffffff;
|
||||
--popover-select-bg-hover-color: rgba(131, 93, 255, 0.05);
|
||||
--color-bg-tree-page: #f9fafb;
|
||||
--color-white: #fff;
|
||||
--check-box-border-color: #e6e7e8;
|
||||
--color-error: #F92814;
|
||||
--color-purple: #835CFF;
|
||||
--color-purple-opacity: rgba(131, 92, 255, 0.1);
|
||||
--color-purple-opacity-half: rgba(121, 88, 240, 0.7);
|
||||
--color-success: #26cea4;
|
||||
--color-warning: #ffb400;
|
||||
--color-info: #097bed;
|
||||
--color-error-bg: rgba(249, 40, 20, 0.1);
|
||||
--color-success-bg: rgba(12, 187, 82, 0.1);
|
||||
--color-warning-bg: rgba(255, 180, 0, 0.1);
|
||||
--color-info-bg: rgba(9, 123, 237, 0.1);
|
||||
|
||||
--scrollbar-thumb-bg-color: #eceff2;
|
||||
--scrollbar-thumb-hover-bg-color: #dcdfe2;
|
||||
--modal-content-bg: #ffffff;
|
||||
--modal-mask-bg: rgba(145, 152, 166, 0.7);
|
||||
--color-bg-tooltip: #ffffff;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--color-primary-opacity: rgba(131, 93, 255, 0.1);
|
||||
--color-bg-gray: #2b3138;
|
||||
--color-font-light-hover: rgba(131, 93, 255, 0.7);
|
||||
--color-bg-light-hover: rgba(131, 93, 255, 0.1);
|
||||
--color-dashed: #33373f;
|
||||
--color-bg-page: #20252c;
|
||||
--color-bg-right: #1c2026;
|
||||
--color-bg-folder: #282d34;
|
||||
--header-bg-color: #282d34;
|
||||
--search-bg-color: #191c21;
|
||||
--table-header-bg-color: #21262d;
|
||||
--search-big-bg-color: #171b1f;
|
||||
--search-big-select-color: #252a32;
|
||||
--color-bg-folder-hover: #282e37;
|
||||
--color-deep-gray: #373b45;
|
||||
--select-hover-color: rgba(131, 93, 255, 0.1);
|
||||
--form-hover-color: #21252d;
|
||||
--highlight-change-color: #373c44;
|
||||
--divider-color: #171b1f;
|
||||
--color-border: #272c35;
|
||||
--color-header-border: #272c35;
|
||||
--color-border-light: #2c2c2c;
|
||||
--icon-color: #4b515a;
|
||||
--font-title-color: #ededed;
|
||||
--font-content-color: #cecfd2;
|
||||
--font-light-color: rgba(198, 201, 210, 0.45);
|
||||
--font-pale-color: rgba(198, 201, 210, 0.7);
|
||||
--box-shadow-modal-color: rgba(0, 0, 0, 0.1);
|
||||
--line-height: 1.4;
|
||||
--border-radius: 4px;
|
||||
--color-primary: #835DFF;
|
||||
--icon-primary-color: #835DFF;
|
||||
--selected-border-color: rgba(131, 93, 255, 0.5);
|
||||
--white-color: #fff;
|
||||
--popover-select-bg-color: #2d3138;
|
||||
--popover-select-bg-hover-color: #353940;
|
||||
--color-bg-tree-page: transparent;
|
||||
--color-white: #fff;
|
||||
--check-box-border-color: #373d46;
|
||||
--color-error: #F92814;
|
||||
--color-purple: #7958F0;
|
||||
--color-purple-opacity: rgba(121, 88, 240, 0.1);
|
||||
--color-purple-opacity-half: rgba(121, 88, 240, 0.7);
|
||||
--color-success: #26cea4;
|
||||
--color-warning: #ffb400;
|
||||
--color-info: #097bed;
|
||||
--color-error-bg: rgba(249, 40, 20, 0.1);
|
||||
--color-success-bg: rgba(12, 187, 82, 0.1);
|
||||
--color-warning-bg: rgba(255, 180, 0, 0.1);
|
||||
--color-info-bg: rgba(9, 123, 237, 0.1);
|
||||
--scrollbar-thumb-bg-color: #242a31;
|
||||
--scrollbar-thumb-hover-bg-color: #2f3740;
|
||||
--modal-content-bg: #1c2026;
|
||||
--modal-mask-bg: rgba(10, 10, 10, 0.7);
|
||||
--color-bg-tooltip: #363a44;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*
|
||||
export const colorOptions = [
|
||||
"#fd7f6f", // Warm coral red
|
||||
"#7eb0d5", // Soft sky blue
|
||||
@@ -15,7 +16,37 @@ export const colorOptions = [
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const colorOptions = [
|
||||
|
||||
"#835dffAA",
|
||||
"#fa8c16AA",
|
||||
"#5fe9a0AA", // this one appears as #5f80e9 but you may have meant #5fcba0 or similar
|
||||
"#61cba0AA",
|
||||
"#e2578dAA",
|
||||
"#ec3e3eAA",
|
||||
"#5bbfd4AA",
|
||||
"#b49461AA",
|
||||
"#4971a6AA",
|
||||
"#9b6065AA"
|
||||
|
||||
]
|
||||
*/
|
||||
|
||||
export const colorOptions = [
|
||||
"#835CFFAA", // Light Hot Magenta
|
||||
"#26cea4AA", // Light Selective Yellow
|
||||
"#ffb400AA", // Light Neon Violet
|
||||
"#097bedAA", // Light Deep Sky Blue
|
||||
"#F92814AA", // Light Black (Gray)
|
||||
"#7958F0AA",
|
||||
"#FF47A5AA"
|
||||
];
|
||||
|
||||
|
||||
export const randomColor = () => {
|
||||
return colorOptions[Math.floor(Math.random() * colorOptions.length)];
|
||||
};
|
||||
@@ -4,7 +4,9 @@ import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
import App from "./App.tsx";
|
||||
import { Provider } from "./provider.tsx";
|
||||
import "./styles/globals.css" ;
|
||||
import "./index.css";
|
||||
|
||||
import "./i18/index.ts" ;
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
|
||||
@@ -52,7 +52,7 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Navbar className="flex rounded-md border-1 border-default-200 bg-background dark:bg-transparent dark:border-default-800 " isBlurred
|
||||
<Navbar className="flex rounded-md border-1 border-default-200 bg-background dark:bg-transparent bg-default dark:border-divider " isBlurred
|
||||
classNames={{
|
||||
wrapper: "h-14 p-2 gap-1",
|
||||
}}
|
||||
@@ -71,14 +71,14 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
>
|
||||
<Undo className={cn(
|
||||
"size-4 dark:text-white",
|
||||
!canUndo ? "text-default-400 dark:text-default-600" : ""
|
||||
!canUndo ? "text-font/30" : ""
|
||||
)} />
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="dark:bg-default-900">
|
||||
<TooltipContent>
|
||||
{t("control_buttons.undo")}
|
||||
<span className="ml-2 text-slate-400">
|
||||
<span className="ml-2 text-default-400">
|
||||
Cntl + Z
|
||||
</span>
|
||||
</TooltipContent>
|
||||
@@ -97,12 +97,12 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="dark:bg-default-900">
|
||||
<TooltipContent>
|
||||
{t("control_buttons.adjust_positions")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Divider orientation="vertical" className="bg-default-200 dark:bg-default-800" />
|
||||
<Divider orientation="vertical" className="bg-font/10" />
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@@ -117,7 +117,7 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="dark:bg-default-900">
|
||||
<TooltipContent>
|
||||
{t("control_buttons.zoom_out")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
@@ -145,11 +145,11 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="dark:bg-default-900">
|
||||
<TooltipContent>
|
||||
{t("control_buttons.zoom_in")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Divider orientation="vertical" className="bg-default-200 dark:bg-default-800" />
|
||||
<Divider orientation="vertical" className="bg-font/10" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
@@ -164,10 +164,10 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="dark:bg-default-900">
|
||||
<TooltipContent>
|
||||
{t("control_buttons.show_all")}
|
||||
|
||||
<span className="ml-2 text-slate-400">
|
||||
<span className="ml-2 text-default-400">
|
||||
Cntl + A
|
||||
</span>
|
||||
</TooltipContent>
|
||||
@@ -185,15 +185,15 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
>
|
||||
<Redo className={cn(
|
||||
"size-4 dark:text-white",
|
||||
!canRedo ? "text-default-400 dark:text-default-600" : ""
|
||||
!canRedo ? "text-font/30" : ""
|
||||
)} />
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="dark:bg-default-900">
|
||||
<TooltipContent>
|
||||
{t("control_buttons.redo")}
|
||||
|
||||
<span className="ml-2 text-slate-400">
|
||||
<span className="ml-2 text-default-400">
|
||||
Ctnl + Shift + Z
|
||||
</span>
|
||||
</TooltipContent>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Importing necessary types and hooks from React Flow (XYFlow)
|
||||
import {
|
||||
addEdge, Background, Connection, Controls, EdgeChange,
|
||||
addEdge, Background, ColorMode, Connection, Controls, EdgeChange,
|
||||
EdgeRemoveChange, NodeChange, NodePositionChange,
|
||||
NodeRemoveChange, OnEdgesChange, OnNodesChange,
|
||||
ReactFlow, useEdgesState, useNodesState, useReactFlow
|
||||
@@ -30,16 +30,18 @@ import { useDiagramOps } from "@/providers/diagram-provider/diagram-provider";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/tooltip";
|
||||
import { addToast, Button } from "@heroui/react";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { adjustTablesPositions } from "@/utils/tables";
|
||||
import { adjustTablesPositions, isTablesOverlapping } from "@/utils/tables";
|
||||
import DatabaseControlButtons from "./database-control-buttons";
|
||||
import { FieldType } from "@/lib/schemas/field-schema";
|
||||
import useHighlightedEdges from "@/hooks/use-highlighted-edges";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import useOverlappingTables from "@/hooks/use-overlapping-tables";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
|
||||
const DatabasePage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { resolvedTheme } = useTheme();
|
||||
// Extract database state and operations
|
||||
const { database, getField } = useDatabase();
|
||||
const { updateTablePositions, deleteMultiTables, deleteMultiRelationships, createRelationship } = useDatabaseOperations();
|
||||
@@ -100,7 +102,7 @@ const DatabasePage: React.FC = () => {
|
||||
) as NodePositionChange[];
|
||||
|
||||
const nodeRemoveChanges: NodeRemoveChange[] = changes.filter((change: NodeChange) => change.type == "remove");
|
||||
|
||||
|
||||
// Save new positions to the database
|
||||
if (nodePositionChanges.length > 0)
|
||||
await updateTablePositions(nodePositionChanges.map((change: NodePositionChange) => ({
|
||||
@@ -140,7 +142,9 @@ const DatabasePage: React.FC = () => {
|
||||
|
||||
// Automatically reposition tables to avoid overlap and fit the view
|
||||
const adjustPositions = useCallback(async () => {
|
||||
updateTablePositions(await adjustTablesPositions(nodes, relationships));
|
||||
const adjustedTables = await adjustTablesPositions(nodes, relationships);
|
||||
console.log (adjustedTables )
|
||||
updateTablePositions(adjustedTables)
|
||||
setTimeout(() => {
|
||||
fitView({
|
||||
duration: 500
|
||||
@@ -153,7 +157,9 @@ const DatabasePage: React.FC = () => {
|
||||
useRelationshipToEdge(relationships);
|
||||
useHighlightedEdges(nodes, relationships, edges);
|
||||
const { isOverlapping, puls } = useOverlappingTables(tables);
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div className="w-full h-screen flex relative overflow-hidden">
|
||||
@@ -162,6 +168,7 @@ const DatabasePage: React.FC = () => {
|
||||
</div>
|
||||
<div className="relative w-full h-full">
|
||||
<ReactFlow
|
||||
colorMode={resolvedTheme as ColorMode}
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
fitView
|
||||
@@ -172,7 +179,7 @@ const DatabasePage: React.FC = () => {
|
||||
defaultEdgeOptions={{
|
||||
type: 'relationship-edge',
|
||||
}}
|
||||
onlyRenderVisibleElements
|
||||
// onlyRenderVisibleElements
|
||||
panOnDrag={true}
|
||||
zoomOnScroll={true}
|
||||
nodeTypes={nodeTypes}
|
||||
@@ -195,7 +202,7 @@ const DatabasePage: React.FC = () => {
|
||||
/>
|
||||
</Controls >
|
||||
|
||||
<Background className="bg-background dark:bg-black" />
|
||||
<Background className="dark:bg-background-100" />
|
||||
</ReactFlow>
|
||||
<div
|
||||
className="absolute left-[24px] bottom-[24px] "
|
||||
|
||||
@@ -25,11 +25,11 @@ const DBController: React.FC<Props> = ({ }) => {
|
||||
minConstraints={[512]}
|
||||
axis="x"
|
||||
handle={
|
||||
<div className="w-[6px] h-full absolute right-0 top-0 cursor-ew-resize">
|
||||
<div className="w-[6px] border-r-2 border-transparent h-full absolute right-0 top-0 cursor-ew-resize hover:border-primary-300 active:border-primary transition-colors duration-200">
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="min-w-[512px] w-full h-full bg-background border-r p-2 pt-[52px] border-default-100">
|
||||
<div className="min-w-[512px] w-full h-full bg-background dark:bg-background-50 border-r p-2 pt-[52px] border-default-100 dark:border-divider">
|
||||
<Outlet />
|
||||
</div>
|
||||
|
||||
|
||||
+12
-8
@@ -1,10 +1,10 @@
|
||||
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/tooltip";
|
||||
import { Cardinality, RelationshipInsertType, RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
import { useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { Button, Select, SelectItem, SharedSelection } from "@heroui/react";
|
||||
import { ChevronsLeftRightEllipsis, FileMinus2, FileOutput, Trash2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ChevronsLeftRightEllipsis, FileMinus2, FileOutput, Trash2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
|
||||
@@ -51,13 +51,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 dark:text-default-600">
|
||||
<label className="font-medium flex text-font/90 flex items-center gap-1 text-sm ">
|
||||
<FileOutput className="size-4 " />
|
||||
{t("db_controller.source_table")}
|
||||
</label>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<span className="truncate text-left text-sm dark:text-default-500">
|
||||
<span className="truncate text-left text-sm text-font/70">
|
||||
{relationship.sourceTable?.name}({relationship.sourceField?.name})
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
@@ -68,7 +68,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 dark:text-default-600">
|
||||
<label className="font-medium flex text-font/90 flex items-center gap-1 text-sm ">
|
||||
<FileMinus2 className="size-4" />
|
||||
{t(" .target_table")}
|
||||
|
||||
@@ -76,7 +76,7 @@ const RelationshipAccordionBody: React.FC<RelationshipAccordionBodyProps> = ({ r
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<span className="truncate text-left text-sm dark:text-default-500 ">
|
||||
<span className="truncate text-left text-sm text-font/70 ">
|
||||
{relationship.targetTable?.name}({relationship.targetField?.name})
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
@@ -87,7 +87,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 dark:text-default-600">
|
||||
<label className="flex text-sm font-medium flex text-font/90 items-center gap-1 ">
|
||||
<ChevronsLeftRightEllipsis className="size-4 " />
|
||||
{t('db_controller.cardinality.name')}
|
||||
</label>
|
||||
@@ -98,6 +98,10 @@ const RelationshipAccordionBody: React.FC<RelationshipAccordionBodyProps> = ({ r
|
||||
aria-label="cardinality"
|
||||
selectedKeys={cardinality}
|
||||
onSelectionChange={changeCardinality}
|
||||
classNames={{
|
||||
trigger: "border-divider group-hover:border-primary",
|
||||
}}
|
||||
|
||||
|
||||
>
|
||||
<SelectItem key={Cardinality.one_to_one}>{t("db_controller.cardinality.one_to_one")}</SelectItem>
|
||||
|
||||
+9
-9
@@ -64,11 +64,11 @@ 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 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 "
|
||||
className="size-6 p-0 text-icon hover:bg-primary-foreground hover:text-font "
|
||||
size="sm"
|
||||
onPress={editRelationshipName}
|
||||
isIconOnly
|
||||
@@ -82,7 +82,7 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
|
||||
!editMode && <>
|
||||
|
||||
<label
|
||||
className="w-full truncate px-2 py-1 text-sm font-semibold text-black dark:text-white"
|
||||
className="w-full truncate px-2 py-1 text-sm font-semibold text-black dark:text-font"
|
||||
>
|
||||
{relationship.name ? relationship.name : defaultName}
|
||||
</label>
|
||||
@@ -94,7 +94,7 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
|
||||
variant="light"
|
||||
onPressEnd={() => focusOnRelationship(relationship.id, true)}
|
||||
>
|
||||
<Focus className="size-4 text-icon dark:text-white" />
|
||||
<Focus className="size-4 text-icon" />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -102,29 +102,29 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
|
||||
variant="light"
|
||||
onPress={() => setEditMode(true)}
|
||||
>
|
||||
<Pencil className="size-4 text-icon dark:text-white" />
|
||||
<Pencil className="size-4 text-icon" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<Popover placement="bottom" radius="sm" shadow="sm" showArrow isOpen={popOverOpen} onOpenChange={setPopOverOpen}>
|
||||
<Popover placement="bottom" radius="sm" shadow="sm" showArrow isOpen={popOverOpen} onOpenChange={setPopOverOpen} >
|
||||
<PopoverTrigger>
|
||||
<Button
|
||||
size="sm"
|
||||
isIconOnly
|
||||
variant="light"
|
||||
>
|
||||
<EllipsisVertical className="size-4 text-slate-500 dark:text-white" />
|
||||
<EllipsisVertical className="size-4 text-icon" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[160px]" >
|
||||
<div className="w-full flex flex-col gap-2 ">
|
||||
<h3 className="font-semibold text-sm text-gray p-2">
|
||||
<h3 className="font-semibold text-sm text-font/90 p-2">
|
||||
{t("db_controller.actions")}
|
||||
</h3>
|
||||
</div>
|
||||
<hr className="text-default-200" />
|
||||
<hr className="border-divider" />
|
||||
<Listbox aria-label="Actions" className="p-0 pb-1" >
|
||||
|
||||
<ListboxItem
|
||||
|
||||
+11
-8
@@ -1,6 +1,6 @@
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/tooltip"
|
||||
import { Accordion, AccordionItem, Button, Input, useDisclosure } from "@heroui/react"
|
||||
import { ListCollapse, Workflow } from "lucide-react"
|
||||
import { ListCollapse, Workflow } from "lucide-react"
|
||||
import { Ref, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RelationshipAccordionHeader from "./relationship-accordion-item/relationship-accordion-header";
|
||||
@@ -87,7 +87,7 @@ const RelationshipController: React.FC<Props> = ({ }) => {
|
||||
<span>
|
||||
<Button
|
||||
variant="light"
|
||||
className="size-8 p-0"
|
||||
className="size-8 p-0 text-icon hover:text-font/90"
|
||||
isIconOnly
|
||||
onPress={() =>
|
||||
//setShowDBML((value) => !value)
|
||||
@@ -111,10 +111,13 @@ const RelationshipController: React.FC<Props> = ({ }) => {
|
||||
|
||||
size="sm"
|
||||
radius="sm"
|
||||
variant="bordered"
|
||||
variant="faded"
|
||||
placeholder={t("db_controller.filter")}
|
||||
className="h-8 w-full focus-visible:ring-0"
|
||||
className="h-8 w-full focus-visible:ring-0 shadow-none "
|
||||
onKeyUp={searchRelationships}
|
||||
classNames={{
|
||||
inputWrapper: "dark:bg-default border-divider group-hover:border-primary ",
|
||||
}}
|
||||
|
||||
/>
|
||||
</div>
|
||||
@@ -135,9 +138,7 @@ const RelationshipController: React.FC<Props> = ({ }) => {
|
||||
|
||||
<Accordion
|
||||
hideIndicator
|
||||
dividerProps={{
|
||||
className: "bg-default-200"
|
||||
}}
|
||||
|
||||
isCompact
|
||||
selectedKeys={selectedRelationship}
|
||||
onSelectionChange={setSelectedRelationship as any}
|
||||
@@ -147,7 +148,9 @@ const RelationshipController: React.FC<Props> = ({ }) => {
|
||||
key={relationship.id}
|
||||
aria-label={relationship.id}
|
||||
classNames={{
|
||||
trigger: "w-full hover:bg-default transition-all duration-200 h-12 dark:bg-background dark:hover:bg-default-50",
|
||||
trigger: "w-full hover:bg-default transition-all duration-200 h-12 dark:hover:bg-background",
|
||||
|
||||
|
||||
base: "rounded-md p-0 overflow-hidden",
|
||||
}}
|
||||
subtitle={
|
||||
|
||||
+22
-16
@@ -93,7 +93,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 dark:text-default-400 dark:hover:text-white" />
|
||||
<GripVertical className="size-4 text-icon cursor-move dark:hover:text-font/90" />
|
||||
</div>
|
||||
<Input
|
||||
variant="bordered"
|
||||
@@ -102,7 +102,11 @@ const FieldItem: React.FC<Props> = ({ field }) => {
|
||||
placeholder={t("db_controller.name")}
|
||||
value={fieldName}
|
||||
onValueChange={setFieldName}
|
||||
|
||||
onBlur={saveFieldName}
|
||||
classNames={{
|
||||
inputWrapper: "border-divider group-hover:border-primary",
|
||||
}}
|
||||
/>
|
||||
<Autocomplete
|
||||
items={data_types}
|
||||
@@ -138,25 +142,26 @@ const FieldItem: React.FC<Props> = ({ field }) => {
|
||||
size="sm"
|
||||
isIconOnly
|
||||
variant="light"
|
||||
className="text-icon hover:text-font/90"
|
||||
>
|
||||
<EllipsisVertical className="size-4 text-slate-500 dark:text-default-600" />
|
||||
<EllipsisVertical className="size-4 " />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[210px]" >
|
||||
<div className="w-full flex flex-col gap-2 p-2">
|
||||
<h3 className="font-semibold text-sm text-gray">
|
||||
<PopoverContent className="w-[210px] bg-background" >
|
||||
<div className="w-full flex flex-col gap-2 p-2 ">
|
||||
<h3 className="font-semibold text-sm text-font/90">
|
||||
{t("db_controller.field_setting")}
|
||||
</h3>
|
||||
<hr className="text-default-200" />
|
||||
<div className="flex w-full 500 justify-between">
|
||||
<span className="text-sm text-slate-500 font-medium">
|
||||
<hr className="border-divider dark:border-font/10" />
|
||||
<div className="flex w-full justify-between">
|
||||
<span className="text-sm text-icon font-medium dark:text-font/90">
|
||||
{t("db_controller.unique")}
|
||||
</span>
|
||||
<Switch size="sm" defaultSelected={field.unique as boolean} onValueChange={toggleUnqiue}>
|
||||
</Switch>
|
||||
|
||||
</div>
|
||||
<label className="text-sm font-medium text-slate-500">
|
||||
</div>
|
||||
<label className="text-sm font-medium text-icon dark:text-font/90">
|
||||
{t("db_controller.note")}
|
||||
</label>
|
||||
<Textarea
|
||||
@@ -169,22 +174,23 @@ const FieldItem: React.FC<Props> = ({ field }) => {
|
||||
onValueChange={setNote}
|
||||
onBlur={updateFieldNote}
|
||||
classNames={{
|
||||
inputWrapper: "bg-default/80",
|
||||
inputWrapper: "bg-default border-divider dark:bg-background-100",
|
||||
base: "max-w-xs",
|
||||
input: "resize-y min-h-[60px] max-h-[180px]",
|
||||
}} />
|
||||
<hr className="text-default-200" />
|
||||
<hr className="border-divider dark:border-font/10" />
|
||||
|
||||
<Button
|
||||
className="bg-default"
|
||||
radius="sm" variant="faded"
|
||||
className="bg-default dark:bg-danger dark:border-none dark:text-white"
|
||||
radius="sm"
|
||||
variant="faded"
|
||||
color="danger"
|
||||
size="sm"
|
||||
onPressEnd={removeField}>
|
||||
<span className="font-medium text-sm">
|
||||
<span className="font-medium text-sm ">
|
||||
{t("db_controller.delete_field")}
|
||||
</span>
|
||||
<Trash2 className="mr-1 size-3.5 text-danger" />
|
||||
<Trash2 className="mr-1 size-3.5 text-danger dark:text-white" />
|
||||
</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
|
||||
+2
-2
@@ -87,9 +87,9 @@ const FieldList: React.FC<Props> = ({ tableFields , tableId}) => {
|
||||
radius="sm"
|
||||
|
||||
startContent={
|
||||
<Plus className="size-4 text-icon dark:text-white" />
|
||||
<Plus className="size-4 text-font/90 dark:text-white" />
|
||||
}
|
||||
className="h-8 p-2 text-xs bg-transparent hover:bg-default text-gray font-semibold"
|
||||
className="h-8 p-2 text-xs bg-transparent hover:bg-default text-font/90 font-semibold"
|
||||
onPressEnd={addField}
|
||||
>
|
||||
{t("db_controller.add_field")}
|
||||
|
||||
+11
-6
@@ -26,15 +26,15 @@ const IndexItem: React.FC<Props> = ({ index, fields }) => {
|
||||
const [popOverOpen, setPopOverOpen] = useState<boolean>(false);
|
||||
const [fieldIndices, setFieldIndices] = useState<Set<string>>(new Set());
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
setFieldIndices(
|
||||
new Set( index.fieldIndices.map((fieldIndex : FieldIndexType) => fieldIndex.fieldId))
|
||||
new Set(index.fieldIndices.map((fieldIndex: FieldIndexType) => fieldIndex.fieldId))
|
||||
)
|
||||
} , [index.fieldIndices])
|
||||
}, [index.fieldIndices])
|
||||
|
||||
const toggleUnique = (unique: boolean) => {
|
||||
|
||||
|
||||
editIndex({
|
||||
id: index.id,
|
||||
unique
|
||||
@@ -73,6 +73,10 @@ const IndexItem: React.FC<Props> = ({ index, fields }) => {
|
||||
size="sm"
|
||||
aria-label={t("db_controller.select_fields")}
|
||||
variant="bordered"
|
||||
classNames={{
|
||||
trigger: "border-divider group-hover:border-primary",
|
||||
}}
|
||||
|
||||
onSelectionChange={onInexFieldChange}
|
||||
onOpenChange={openChange}
|
||||
selectedKeys={fieldIndices}
|
||||
@@ -97,8 +101,9 @@ const IndexItem: React.FC<Props> = ({ index, fields }) => {
|
||||
size="sm"
|
||||
isIconOnly
|
||||
variant="light"
|
||||
className="text-icon hover:text-font/90"
|
||||
>
|
||||
<EllipsisVertical className="size-4 text-slate-500 dark:text-default-600" />
|
||||
<EllipsisVertical className="size-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[210px]" >
|
||||
|
||||
+2
-2
@@ -42,9 +42,9 @@ const IndexesList: React.FC<IndexesListProps> = ({ indices, fields, tableId }) =
|
||||
variant="flat"
|
||||
radius="sm"
|
||||
startContent={
|
||||
<Plus className="size-4 text-icon" />
|
||||
<Plus className="size-4 text-font/90" />
|
||||
}
|
||||
className="h-8 p-2 text-xs bg-transparent hover:bg-default text-gray font-semibold"
|
||||
className="h-8 p-2 text-xs bg-transparent hover:bg-default text-font/90 font-semibold"
|
||||
onPressEnd={addIndex}
|
||||
>
|
||||
{t("db_controller.add_index")}
|
||||
|
||||
+17
-17
@@ -66,7 +66,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full dark:bg-background">
|
||||
<div className="w-full dark:bg-background-50">
|
||||
<Accordion
|
||||
isCompact
|
||||
variant={"light"} selectionMode="multiple" selectedKeys={selectedKeys} onSelectionChange={setSelectedKeys as any}>
|
||||
@@ -74,25 +74,25 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
|
||||
indicator={({ isOpen }) => (
|
||||
<div className={cn(
|
||||
'tarnsition-all duration-200 ',
|
||||
isOpen ? "rotate-[-90deg]" : ""
|
||||
isOpen ? "rotate-[0deg]" : ""
|
||||
)}>
|
||||
<ChevronLeft className="size-4 text-icon dark:text-default-600" />
|
||||
<ChevronLeft className="size-4 text-icon " />
|
||||
</div>
|
||||
)}
|
||||
classNames={{
|
||||
trigger: "hover:bg-default h-6 dark:hover:bg-background"
|
||||
}}
|
||||
subtitle={
|
||||
<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">
|
||||
<div className="group text-font/90 hover:text-font flex gap-2 items-center font-medium p-1 w-full hover:underline 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>
|
||||
<button
|
||||
className="size-4 p-0 text-xs opacity-0 group-hover:opacity-100 transition-all duration-200 hover:text-slate-700 text-icon"
|
||||
className="size-4 p-0 text-xs opacity-0 group-hover:opacity-100 transition-all duration-200 hover:text-font text-icon"
|
||||
onClick={addField}
|
||||
>
|
||||
<Plus className="size-4 dark:text-default-600" />
|
||||
<Plus className="size-4 dark:text-font/90" />
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@@ -103,25 +103,25 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
|
||||
indicator={({ isOpen }) => (
|
||||
<div className={cn(
|
||||
'tarnsition-all duration-200',
|
||||
isOpen ? "rotate-[-90deg]" : ""
|
||||
isOpen ? "rotate-[0deg]" : ""
|
||||
)}>
|
||||
<ChevronLeft className="size-4 text-icon dark:text-default-600" />
|
||||
<ChevronLeft className="size-4 text-icon " />
|
||||
</div>
|
||||
)}
|
||||
classNames={{
|
||||
trigger: "hover:bg-default h-6 dark:hover:bg-background"
|
||||
}}
|
||||
subtitle={
|
||||
<div className="group 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">
|
||||
<div className="group flex gap-2 text-font/90 items-center font-medium p-1 w-full hover:underline hover:text-font transition-all duration-200 dark:hover:text-white">
|
||||
<FileKey className="size-4 " />
|
||||
<label className="text-sm w-full cursor-pointer">
|
||||
{t("db_controller.indexes")}
|
||||
</label>
|
||||
<button
|
||||
className="size-4 p-0 text-xs opacity-0 group-hover:opacity-100 transition-all duration-200 hover:text-slate-700 text-icon"
|
||||
className="size-4 p-0 text-xs opacity-0 group-hover:opacity-100 transition-all duration-200 hover:text-font text-icon"
|
||||
onClick={addIndex}
|
||||
>
|
||||
<Plus className="size-4 dark:text-default-600" />
|
||||
<Plus className="size-4 dark:text-font/90" />
|
||||
</button>
|
||||
</div>
|
||||
}>
|
||||
@@ -131,16 +131,16 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
|
||||
indicator={({ isOpen }) => (
|
||||
<div className={cn(
|
||||
'tarnsition-all duration-200',
|
||||
isOpen ? "rotate-[-90deg]" : ""
|
||||
isOpen ? "rotate-[0deg]" : ""
|
||||
)}>
|
||||
<ChevronLeft className="size-4 text-icon dark:text-default-600" />
|
||||
<ChevronLeft className="size-4 text-icon " />
|
||||
</div>
|
||||
)}
|
||||
classNames={{
|
||||
trigger: "hover:bg-default h-6 dark:hover:bg-background"
|
||||
}}
|
||||
subtitle={
|
||||
<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">
|
||||
<div className="flex gap-2 text-font/90 items-center font-medium p-1 w-full hover:underline hover:text-font transition-all duration-200 dark:hover:text-white">
|
||||
<MessageSquareQuote className="size-4" />
|
||||
<label className="text-sm w-full cursor-pointer">
|
||||
{t("db_controller.note")}
|
||||
@@ -149,7 +149,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 border-divider dark:bg-background-100 ",
|
||||
}}
|
||||
value={note}
|
||||
onValueChange={setNote}
|
||||
@@ -172,7 +172,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
|
||||
variant="ghost"
|
||||
onPressEnd={addIndex}
|
||||
size="sm"
|
||||
className="font-semibold p-4 border-default-50"
|
||||
className="font-semibold p-4 border-divider text-font/90"
|
||||
startContent={
|
||||
<FileKey className="size-4 " />
|
||||
}
|
||||
@@ -183,7 +183,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
|
||||
onPressEnd={addField}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="font-semibold p-4 border-default-50"
|
||||
className="font-semibold p-4 border-divider text-font/90"
|
||||
startContent={
|
||||
<FileType className="size-4 " />
|
||||
}
|
||||
|
||||
+20
-14
@@ -87,9 +87,9 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
|
||||
'tarnsition-all duration-200',
|
||||
isOpen ? "rotate-[90deg]" : ""
|
||||
)}>
|
||||
<ChevronRight className="size-4 text-icon dark:text-white" />
|
||||
<ChevronRight className="size-4 text-icon " />
|
||||
</div>
|
||||
<div className=" w-[1px] h-full bg-default-200 dark:bg-default-100">
|
||||
<div className=" w-[1px] h-full bg-divider ">
|
||||
</div>
|
||||
{
|
||||
!editMode &&
|
||||
@@ -97,7 +97,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 dark:text-white"
|
||||
className="w-full text-editable truncate px-2 py-1 text-sm font-semibold text-black dark:text-font"
|
||||
onDoubleClick={() => setEditMode(true)}
|
||||
>
|
||||
{tableName}
|
||||
@@ -120,16 +120,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 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-white"
|
||||
className="size-6 p-0 text-icon hover:bg-primary-foreground hover:text-font "
|
||||
size="sm"
|
||||
onPressEnd={saveTableName}
|
||||
isIconOnly
|
||||
>
|
||||
<Check className="size-4 text-icon dark:text-white" />
|
||||
<Check className="size-4 text-icon " />
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
@@ -145,7 +145,7 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
|
||||
variant="light"
|
||||
onPress={() => setEditMode(true)}
|
||||
>
|
||||
<Pencil className="size-4 text-icon dark:text-white" />
|
||||
<Pencil className="size-4 text-icon " />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -153,7 +153,7 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
|
||||
variant="light"
|
||||
onPressEnd={() => focusOnTable(table.id, true)}
|
||||
>
|
||||
<Focus className="size-4 text-icon dark:text-white" />
|
||||
<Focus className="size-4 text-icon " />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -164,29 +164,32 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
|
||||
isIconOnly
|
||||
variant="light"
|
||||
>
|
||||
<EllipsisVertical className="size-4 text-slate-500 dark:text-white" />
|
||||
<EllipsisVertical className="size-4 text-icon " />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[160px]" >
|
||||
<div className="w-full flex flex-col gap-2 ">
|
||||
<h3 className="font-semibold text-sm text-gray p-2">
|
||||
<h3 className="font-semibold text-sm text-font/90 p-2">
|
||||
{t("db_controller.table_actions")}
|
||||
</h3>
|
||||
</div>
|
||||
<hr className="text-default-200" />
|
||||
<hr className="border-divider" />
|
||||
<Listbox aria-label="Actions" className="p-0 pb-1" >
|
||||
<ListboxItem
|
||||
key="add_field"
|
||||
onPressEnd={addField}
|
||||
className="text-font/90"
|
||||
endContent={
|
||||
<FileType className="size-4 text-icon dark:text-white" />
|
||||
<FileType className="size-4 " />
|
||||
|
||||
}>
|
||||
{t("db_controller.add_field")}
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
key="add_index"
|
||||
endContent={<FileKey className="size-4 text-icon dark:text-white" />}
|
||||
|
||||
className="text-font/90"
|
||||
endContent={<FileKey className="size-4" />}
|
||||
showDivider
|
||||
onPressEnd={addIndex}
|
||||
>
|
||||
@@ -197,8 +200,10 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
|
||||
<ListboxItem
|
||||
key="duplicate"
|
||||
showDivider
|
||||
|
||||
className="text-font/90"
|
||||
onPressEnd={duplicate}
|
||||
endContent={<Copy className="size-4 text-icon" />}>
|
||||
endContent={<Copy className="size-4" />}>
|
||||
|
||||
{t("db_controller.duplicate")}
|
||||
</ListboxItem>
|
||||
@@ -207,6 +212,7 @@ const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOp
|
||||
className="text-danger"
|
||||
color="danger"
|
||||
onPressEnd={onDeleteTable}
|
||||
|
||||
endContent={<Trash className="size-4" />}
|
||||
>
|
||||
{t("db_controller.delete_table")}
|
||||
|
||||
@@ -80,7 +80,7 @@ const TablesController: React.FC<Props> = ({ }) => {
|
||||
<span>
|
||||
<Button
|
||||
variant="light"
|
||||
className="size-8 p-0"
|
||||
className="size-8 p-0 text-icon hover:text-font/90"
|
||||
isIconOnly
|
||||
onPress={() =>
|
||||
//setShowDBML((value) => !value)
|
||||
@@ -106,11 +106,15 @@ const TablesController: React.FC<Props> = ({ }) => {
|
||||
type="text"
|
||||
size="sm"
|
||||
autoFocus
|
||||
radius="sm"
|
||||
variant="bordered"
|
||||
radius="sm"
|
||||
variant="faded"
|
||||
placeholder={t("db_controller.filter")}
|
||||
className="h-8 w-full focus-visible:ring-0"
|
||||
className="h-8 w-full focus-visible:ring-0 shadow-none "
|
||||
classNames={{
|
||||
inputWrapper : "dark:bg-default border-divider group-hover:border-primary " ,
|
||||
}}
|
||||
onKeyUp={searchTables}
|
||||
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
@@ -130,7 +134,7 @@ const TablesController: React.FC<Props> = ({ }) => {
|
||||
|
||||
<Accordion
|
||||
hideIndicator
|
||||
variant={"splitted"}
|
||||
|
||||
selectedKeys={selectedTable}
|
||||
onSelectionChange={setSelectedTable as any}
|
||||
isCompact
|
||||
@@ -141,8 +145,9 @@ 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 dark:bg-background dark:hover:bg-default-50",
|
||||
base: "rounded-md shadow p-0 overflow-hidden dark:border-1 dark:border-default-100",
|
||||
trigger: "w-full h-12 hover:bg-default transition-all duration-200 dark:hover:bg-background",
|
||||
base: "rounded-md mb-1 mt-1 p-0 overflow-hidden dark:border-background-100",
|
||||
content : "bg-transparent"
|
||||
}}
|
||||
subtitle={
|
||||
<TableAccordionHeader
|
||||
@@ -162,6 +167,8 @@ const TablesController: React.FC<Props> = ({ }) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export default TablesController
|
||||
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ const Field: React.FC<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
"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/5 transition-all duration-200 ease-in-out",
|
||||
"group relative flex h-8 items-center justify-between gap-1 border-t border-divider px-3 text-sm last:rounded-b-[6px] hover:bg-slate-100 dark:hover:bg-primary/5 transition-all duration-200 ease-in-out",
|
||||
highlight ? "bg-primary/5" : ""
|
||||
)}>
|
||||
{
|
||||
@@ -58,7 +58,7 @@ const Field: React.FC<Props> = (props) => {
|
||||
<>
|
||||
<label
|
||||
className={cn(
|
||||
"truncate flex gap-1 text-xs text-slate-900 dark:text-default-200",
|
||||
"truncate flex gap-1 text-xs text-font/90 ",
|
||||
field.isPrimary ? "font-semibold" : ""
|
||||
)}
|
||||
onDoubleClick={() => setEditMode(true)}
|
||||
@@ -77,8 +77,8 @@ const Field: React.FC<Props> = (props) => {
|
||||
}
|
||||
</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 dark:text-default-200",
|
||||
field.isPrimary ? "font-semibold text-slate-700" : ""
|
||||
<span className={cn("content-center truncate flex items-center h-full gap-1 text-right text-xs group-hover:hidden font-semibold text-font/70 ",
|
||||
field.isPrimary ? "font-semibold text-font/90" : ""
|
||||
)}>
|
||||
{field.isPrimary && <KeyRound className="size-3" />} {field.type?.name?.split(' ')[0]}{field.nullable ? "?" : ""}
|
||||
</span>
|
||||
@@ -111,12 +111,12 @@ const Field: React.FC<Props> = (props) => {
|
||||
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 text-sm dark:bg-transparent dark:text-white"
|
||||
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 text-font/90"
|
||||
|
||||
/>
|
||||
<Button
|
||||
variant="light"
|
||||
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-white"
|
||||
className="size-6 p-0 text-icon hover:text-font/90"
|
||||
size="sm"
|
||||
isIconOnly
|
||||
onPress={saveFieldName}
|
||||
@@ -135,12 +135,12 @@ const Field: React.FC<Props> = (props) => {
|
||||
type="source"
|
||||
position={Position.Left}
|
||||
id={LEFT_PREFIX + field.id}
|
||||
className="w-4 h-4 border-4 bg-primary dark:border-default-900"
|
||||
className="w-4 h-4 border-4 bg-primary dark:border-background-50"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
className="w-4 h-4 border-4 bg-primary dark:border-default-900"
|
||||
className="w-4 h-4 border-4 bg-primary dark:border-background-50"
|
||||
id={RIGHT_PREFIX + field.id}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -125,7 +125,7 @@ const Relationship: React.FC<EdgeProps<RelationshipProps>> = (props) => {
|
||||
fill="none"
|
||||
className={cn([
|
||||
|
||||
`!stroke-2 ${selected ? '!stroke-primary' : 'stroke-slate-300 dark:stroke-default-500'}`,
|
||||
`!stroke-1 ${selected ? '!stroke-primary' : 'stroke-default-600 dark:stroke-default-300'}`,
|
||||
|
||||
|
||||
])}
|
||||
|
||||
@@ -32,8 +32,8 @@ export type TableProps = Node<{
|
||||
}>
|
||||
|
||||
const MAX_FIELDS = 10;
|
||||
const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table, overlapping = false, pulsing = false, highlightedEdges = [] } }) => {
|
||||
|
||||
const Table: React.FC<NodeProps<TableProps>> = (props) => {
|
||||
const { selected, data: { table, overlapping = false, pulsing = false, highlightedEdges = [] } } = props
|
||||
|
||||
const [editMode, setEditMode] = useState<boolean>(false);
|
||||
const [tableName, setTableName] = useState<string>(table.name);
|
||||
@@ -76,16 +76,17 @@ const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table, overl
|
||||
|
||||
const toggleShowMore = useCallback(() => {
|
||||
setShowMore((previousShowMore) => !previousShowMore);
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<Card className={cn(
|
||||
"w-full h-full bg-background rounded-lg noselect overflow-visible dark:bg-default-900",
|
||||
"w-full h-full bg-background rounded-md ring-1 ring-default-600 overflow-visible dark:bg-background-50 dark:ring-divider",
|
||||
selected
|
||||
? 'ring-2 ring-offset-1 ring-primary dark:ring-offset-default-900'
|
||||
? 'ring-2 ring-primary dark:ring-primary'
|
||||
: '',
|
||||
overlapping
|
||||
? 'ring-2 dark:ring-offset-default-900 ring-danger ring-offset-1 scale-105 shadow-danger '
|
||||
? 'ring-2 dark:ring-danger ring-danger scale-105 shadow-danger '
|
||||
: '',
|
||||
!pulsing && overlapping
|
||||
? 'scale-105'
|
||||
@@ -94,17 +95,16 @@ const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table, overl
|
||||
? 'scale-110'
|
||||
: '',
|
||||
)}
|
||||
shadow="sm"
|
||||
|
||||
shadow="none"
|
||||
>
|
||||
<div className="px-[2px] ">
|
||||
<div className="px-[1px] ">
|
||||
<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" />
|
||||
<Table2 className="size-4 shrink-0 text-icon " />
|
||||
{
|
||||
editMode && <>
|
||||
<input
|
||||
@@ -114,11 +114,11 @@ const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table, overl
|
||||
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"
|
||||
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"
|
||||
className="size-6 p-0 text-icon hover:bg-primary-foreground hover:text-font/90"
|
||||
size="sm"
|
||||
onPressEnd={saveTableName}
|
||||
isIconOnly
|
||||
@@ -133,13 +133,13 @@ const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table, overl
|
||||
<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"
|
||||
className=" w-full text-editable truncate px-2 py-0.5 text-sm font-bold dark:text-white "
|
||||
onDoubleClick={() => setEditMode(true)}
|
||||
>
|
||||
{tableName}
|
||||
</label>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="dark:bg-default-900">
|
||||
<TooltipContent >
|
||||
{t("table.double_click")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
@@ -149,11 +149,11 @@ const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table, overl
|
||||
<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 "
|
||||
className="size-6 p-0 text-icon hover:bg-primary-foreground "
|
||||
isIconOnly
|
||||
onPressEnd={focus}
|
||||
>
|
||||
<Focus className="size-4 text-icon dark:text-white" />
|
||||
<Focus className="size-4 " />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
@@ -188,8 +188,16 @@ const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table, overl
|
||||
)
|
||||
};
|
||||
|
||||
export default React.memo(Table, (previousState: any, newState: any) => {
|
||||
export default React.memo(Table , (previousState: any, newState: any) => {
|
||||
|
||||
|
||||
const previousStateHash: string = hash(previousState.data);
|
||||
const newStateHash: string = hash(newState.data);
|
||||
return previousStateHash == newStateHash && previousState.selected == newState.selected;
|
||||
})
|
||||
});
|
||||
|
||||
/*
|
||||
,
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
|
||||
export enum Modals {
|
||||
CREATE_RELATIONSHIP = "CREATE_RELATIONSHIP"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
interface ModalContextType {
|
||||
focusedTableId: string | undefined;
|
||||
focusedRelationshipId: string | undefined;
|
||||
}
|
||||
|
||||
export const ModalContext = createContext<ModalContextType>({} as ModalContextType);
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
/*
|
||||
import { useCallback, useContext, useState } from "react"
|
||||
import { ModalContext, Modals } from "./modal-contxet"
|
||||
import Modal, { ModalProps } from "@/components/modal/modal";
|
||||
import { useDisclosure } from "@heroui/react";
|
||||
|
||||
interface Props { children: React.ReactNode }
|
||||
interface CurrentModalProps {
|
||||
modal: Modals,
|
||||
modalProps: ModalProps,
|
||||
content?: any
|
||||
}
|
||||
|
||||
const ModalProvider: React.FC<Props> = ({ children }) => {
|
||||
const { isOpen, onOpen } = useDisclosure();
|
||||
const [currentModal, setCurrentModal] = useState<CurrentModalProps | undefined>(undefined);
|
||||
|
||||
const onOpenChange = useCallback(() => {
|
||||
onOpen();
|
||||
setCurrentModal(undefined);
|
||||
}, [onOpen]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<ModalContext.Provider>
|
||||
{
|
||||
currentModal &&
|
||||
<Modal isOpen={isOpen} {...currentModal.modalProps} onOpenChange={onOpenChange} >
|
||||
|
||||
</Modal>
|
||||
}
|
||||
{children}
|
||||
</ModalContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export const useModal = () => useContext(ModalContext);
|
||||
|
||||
*/
|
||||
+12
-5
@@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
.react-flow__attribution {
|
||||
display: none;
|
||||
}
|
||||
@@ -5,7 +7,7 @@
|
||||
|
||||
|
||||
.text-editable {
|
||||
@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;
|
||||
@apply dark:group-hover:bg-background group-hover:bg-slate-100 group-hover:ring-[0.5px] group-hover:ring-primary dark:group-hover:ring-divider rounded-sm cursor-pointer;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +47,7 @@ body {
|
||||
}
|
||||
|
||||
.sidebar-item[data-active="true"] svg {
|
||||
color: hsl(var(--heroui-primary-900));
|
||||
color: hsl(var(--heroui-primary));
|
||||
|
||||
}
|
||||
|
||||
@@ -78,7 +80,7 @@ div[data-slot="content"] hr[role="separator"] {
|
||||
|
||||
/* Target the scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
width: 6px;
|
||||
/* Width of the scrollbar */
|
||||
|
||||
}
|
||||
@@ -88,7 +90,7 @@ div[data-slot="content"] hr[role="separator"] {
|
||||
|
||||
/* Handle (the scroll thumb) */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: hsl(var(--heroui-default-100));
|
||||
background: hsl(var(--heroui-scrollbar));
|
||||
/* Darker gray */
|
||||
border-radius: 6px;
|
||||
/* Rounded corners */
|
||||
@@ -100,7 +102,7 @@ div[data-slot="content"] hr[role="separator"] {
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
|
||||
background: hsl(var(--heroui-default-300));
|
||||
background: hsl(var(--heroui-scrollbar-hover));
|
||||
/* Darker on hover */
|
||||
}
|
||||
|
||||
@@ -116,4 +118,9 @@ div[data-slot="content"] hr[role="separator"] {
|
||||
|
||||
.react-flow__pane {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
svg.text-icon:hover {
|
||||
|
||||
color : #333639
|
||||
}
|
||||
+8
-2
@@ -16,7 +16,10 @@ const adjustTablesPositions = async (
|
||||
): Promise<TableType[]> => {
|
||||
|
||||
// Extract tables (same as before)
|
||||
const tables: TableType[] = nodes.map((node: Node) => node.data.table) as TableType[];
|
||||
const tables: TableType[] = nodes.map((node: Node) => (
|
||||
{ ...node.data.table as TableType }
|
||||
)) as TableType[];
|
||||
|
||||
|
||||
// Build the ELK graph structure
|
||||
const graph = {
|
||||
@@ -37,7 +40,7 @@ const adjustTablesPositions = async (
|
||||
targets: [rel.targetTableId],
|
||||
})),
|
||||
};
|
||||
|
||||
console.log (graph.children ) ;
|
||||
// Run ELK layout (async)
|
||||
const layoutedGraph = await elk.layout(graph);
|
||||
|
||||
@@ -56,6 +59,9 @@ const adjustTablesPositions = async (
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const isTablesOverlapping = (tableA: TableType, tableB: TableType) => {
|
||||
const tableAWidth: number = 224;
|
||||
const tableAHeight: number = tableA.fields.length * 32 + 36;
|
||||
|
||||
+106
-29
@@ -10,53 +10,130 @@ export default {
|
||||
|
||||
],
|
||||
darkMode: "class",
|
||||
|
||||
plugins: [heroui({
|
||||
layout: {
|
||||
disabledOpacity: "0.3", // opacity-[0.3]
|
||||
radius: {
|
||||
small: "4px", // rounded-small
|
||||
medium: "8px", // rounded-medium
|
||||
large: "12px", // rounded-large
|
||||
},
|
||||
|
||||
},
|
||||
themes: {
|
||||
|
||||
dark: {
|
||||
|
||||
colors: {
|
||||
background: {
|
||||
DEFAULT: "#282a37",
|
||||
50: "#222430",
|
||||
100: "#1e1f28"
|
||||
},
|
||||
|
||||
danger: {
|
||||
DEFAULT: "#F92814"
|
||||
},
|
||||
font: {
|
||||
DEFAULT: "#cccdd7"
|
||||
},
|
||||
content1 : {
|
||||
DEFAULT : "#282a37"
|
||||
} ,
|
||||
default: {
|
||||
50: '#f2f3f5',
|
||||
100: '#d6d7db',
|
||||
200: '#b9bbc1',
|
||||
300: '#9d9fa7',
|
||||
400: '#80838d',
|
||||
500: '#191c21', // base
|
||||
600: '#15171b',
|
||||
700: '#111316',
|
||||
800: '#0d0f11',
|
||||
900: '#090b0c',
|
||||
DEFAULT: '#191c21',
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: "#9822ff",
|
||||
50: '#9822ff0D', // 5% opacity
|
||||
100: '#9822ff1A', // 10% opacity
|
||||
200: '#9822ff33', // 20% opacity
|
||||
300: '#9822ff4D', // 30% opacity
|
||||
400: '#9822ff66', // 40% opacity
|
||||
500: '#9822ff80', // 50% opacity
|
||||
600: '#9822ff99', // 60% opacity
|
||||
700: '#9822ffB3', // 70% opacity
|
||||
800: '#961FFFCC', // 80% opacity
|
||||
900: '#9822ffE6', // 90% opacity
|
||||
DEFAULT: "#835dff",
|
||||
50: '#F3F0FF',
|
||||
100: '#E3DCFF',
|
||||
200: '#C6B8FF',
|
||||
300: '#A994FF',
|
||||
400: '#8D70FF',
|
||||
500: '#835DFF', // Default
|
||||
600: '#6C4BD9',
|
||||
700: '#563BB3',
|
||||
800: '#402C8D',
|
||||
900: '#2A1D66'
|
||||
},
|
||||
divider: {
|
||||
DEFAULT: "#272c35"
|
||||
},
|
||||
icon: {
|
||||
DEFAULT: "#4b515a"
|
||||
},
|
||||
|
||||
success: {
|
||||
DEFAULT: "#26cea4"
|
||||
},
|
||||
scrollbar: {
|
||||
DEFAULT: "#242a31" ,
|
||||
hover : "#2f3740"
|
||||
},
|
||||
}
|
||||
},
|
||||
light: {
|
||||
|
||||
colors: {
|
||||
danger: {
|
||||
DEFAULT: "#F92814"
|
||||
},
|
||||
font: {
|
||||
DEFAULT: "#333639"
|
||||
},
|
||||
sidebar: {
|
||||
DEFAULT: 'hsl(0 0% 98%)',
|
||||
foreground: 'hsl(240 5.3% 26.1%)',
|
||||
DEFAULT: "#F1F3F6"
|
||||
},
|
||||
divider: {
|
||||
DEFAULT: "#f2f4f6"
|
||||
},
|
||||
success: {
|
||||
DEFAULT: "#26cea4"
|
||||
},
|
||||
scrollbar: {
|
||||
DEFAULT: "#eceff2" ,
|
||||
hover : "#dcdfe2"
|
||||
},
|
||||
|
||||
default: {
|
||||
DEFAULT: 'hsl(210 40% 96.1%)',
|
||||
foreground: 'hsl(222.2 47.4% 11.2%)'
|
||||
50: '#f9fafb',
|
||||
100: '#f4f6f8',
|
||||
200: '#e9edf1',
|
||||
300: '#dbe2e8',
|
||||
400: '#c6d0da',
|
||||
500: '#f2f4f7',
|
||||
600: '#a4b2c0',
|
||||
700: '#8493a4',
|
||||
800: '#69798a',
|
||||
900: '#55616f',
|
||||
DEFAULT: '#f2f4f7',
|
||||
},
|
||||
|
||||
icon: {
|
||||
DEFAULT: "hsl(215.4 16.3% 46.9%)"
|
||||
DEFAULT: "#a2a4a8"
|
||||
},
|
||||
|
||||
primary: {
|
||||
DEFAULT: "#9822ff",
|
||||
50: '#9822ff0D', // 5% opacity
|
||||
100: '#9822ff1A', // 10% opacity
|
||||
200: '#9822ff33', // 20% opacity
|
||||
300: '#9822ff4D', // 30% opacity
|
||||
400: '#9822ff66', // 40% opacity
|
||||
500: '#9822ff80', // 50% opacity
|
||||
600: '#9822ff99', // 60% opacity
|
||||
700: '#9822ffB3', // 70% opacity
|
||||
800: '#961FFFCC', // 80% opacity
|
||||
900: '#9822ffE6', // 90% opacity
|
||||
DEFAULT: "#835dff",
|
||||
50: '#F3F0FF',
|
||||
100: '#E3DCFF',
|
||||
200: '#C6B8FF',
|
||||
300: '#A994FF',
|
||||
400: '#8D70FF',
|
||||
500: '#835DFF', // Default
|
||||
600: '#6C4BD9',
|
||||
700: '#563BB3',
|
||||
800: '#402C8D',
|
||||
900: '#2A1D66'
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user