mirror of
https://github.com/stackrender/stackrender.git
synced 2026-09-10 03:05:42 +00:00
Improved UI/UX + Lazy Loading
This commit is contained in:
Generated
+857
-483
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "StackRender",
|
||||
"private": true,
|
||||
"version": "1.0.2",
|
||||
"version": "1.3.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -86,7 +86,7 @@
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.11.0",
|
||||
"@typescript-eslint/parser": "8.11.0",
|
||||
"@vitejs/plugin-react": "^6.0.2",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "10.4.19",
|
||||
"esbuild": "^0.28.0",
|
||||
"eslint": "^8.57.0",
|
||||
@@ -102,8 +102,8 @@
|
||||
"rollup": "^4.61.1",
|
||||
"tw-animate-css": "^1.3.6",
|
||||
"typescript": "5.6.3",
|
||||
"vite": "^8.0.3",
|
||||
"vite": "^5.2.0",
|
||||
"vite-plugin-top-level-await": "^1.5.0",
|
||||
"vite-tsconfig-paths": "^4.3.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,14 +41,13 @@ const Clipboard: React.FC<ClipboardProps> = ({ text }) => {
|
||||
}, [text])
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
size="icon"
|
||||
|
||||
variant="outline"
|
||||
className="size-8"
|
||||
className="w-7 h-7 text-muted-foreground bg-card/50 backdrop-blur-xs dark:backdrop-blur-md shadow-lg"
|
||||
onClick={copyToClipboard}
|
||||
>
|
||||
{
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTheme } from "@/providers/theme-provider/theme-provider";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Spinner } from "./ui/shadcn-io/spinner";
|
||||
|
||||
interface CodeEditorProps {
|
||||
defaultValue?: string;
|
||||
value?: string;
|
||||
className?: string | undefined,
|
||||
readOnly?: boolean,
|
||||
onChange?: (sql: string) => void
|
||||
}
|
||||
|
||||
const CodeEditor: React.FC<CodeEditorProps> = ({
|
||||
defaultValue,
|
||||
value,
|
||||
className,
|
||||
readOnly = false,
|
||||
onChange
|
||||
}) => {
|
||||
|
||||
|
||||
const [editor, setEditor] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
let mounted = true;
|
||||
|
||||
Promise.all([
|
||||
import("@uiw/react-codemirror"),
|
||||
import("@codemirror/lang-sql"),
|
||||
import("@codemirror/view"),
|
||||
]).then(([cm, sqlLang, view]) => {
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
const overrideDarkTheme = view.EditorView.theme({
|
||||
|
||||
'.cm-content': {
|
||||
backgroundColor: "#1c2025",
|
||||
},
|
||||
".cm-gutter": {
|
||||
backgroundColor: "#1c2025",
|
||||
},
|
||||
|
||||
".cm-gutterElement": {
|
||||
color: "#4b515a"
|
||||
},
|
||||
|
||||
".ͼp": {
|
||||
color: "#A994FF"
|
||||
},
|
||||
|
||||
".cm-line .ͼq": {
|
||||
color: "#ff6363"
|
||||
},
|
||||
".ͼu": {
|
||||
color: "#B6E672"
|
||||
},
|
||||
".ͼv": {
|
||||
color: "#6cdcc4"
|
||||
}
|
||||
}, { dark: true });
|
||||
|
||||
const overrideLightTheme = view.EditorView.theme({
|
||||
|
||||
".ͼb": {
|
||||
color: "#2A1D66"
|
||||
},
|
||||
".cm-gutterElement": {
|
||||
color: "#62748e"
|
||||
},
|
||||
".cm-gutter": {
|
||||
backgroundColor: "white",
|
||||
|
||||
},
|
||||
".cm-gutters": {
|
||||
borderColor: "#f2f4f6"
|
||||
},
|
||||
".cm-line": {
|
||||
color: "#0f172b"
|
||||
},
|
||||
|
||||
})
|
||||
setEditor({
|
||||
CodeMirror: cm.default,
|
||||
oneDark: cm.oneDark,
|
||||
sql: sqlLang.sql,
|
||||
overrideDarkTheme,
|
||||
overrideLightTheme,
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
const { theme } = useTheme();
|
||||
|
||||
if (!editor) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-1 w-full min-h-9 rounded-sm border border-border bg-card items-center justify-center",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Spinner className="text-primary" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { CodeMirror, oneDark, sql, overrideDarkTheme, overrideLightTheme } = editor;
|
||||
return (
|
||||
<CodeMirror
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
className={cn("flex flex-1 w-full min-h-9 rounded-sm h-full bg-card border-1 border-border !min-w-0 overflow-hidden", className)}
|
||||
extensions={[sql()]}
|
||||
readOnly={readOnly}
|
||||
theme={theme != "dark" ? overrideLightTheme : [oneDark, overrideDarkTheme]}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default React.memo(CodeEditor);
|
||||
@@ -16,11 +16,11 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
const sidebarData = useSidebarData();
|
||||
const { openController } = useDiagramOps();
|
||||
return (
|
||||
<Sidebar collapsible='icon' variant='floating' {...props} className='bg-card border-r' >
|
||||
<Sidebar collapsible='icon' {...props} >
|
||||
<SidebarHeader>
|
||||
<div className='flex items-center'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<img
|
||||
className='w-8 h-8 p-[6px] rounded-md '
|
||||
className='w-8 p-[4px] rounded-md '
|
||||
src='/stackrender.png'
|
||||
/>
|
||||
<h3 className='data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground truncate font-semibold text-sm '>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { SidebarProvider } from "@/components/ui/sidebar";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
|
||||
import { AppSidebar } from "@/components/layout/app-sidebar/app-sidebar";
|
||||
import { Header } from "@/components/header";
|
||||
|
||||
@@ -14,24 +13,20 @@ interface Props {
|
||||
const Dashboard: React.FC<Props> = ({ children }) => {
|
||||
return (
|
||||
<SidebarProvider defaultOpen={false}>
|
||||
<AppSidebar />
|
||||
<div
|
||||
id='content'
|
||||
className={cn(
|
||||
'ml-auto w-full max-w-full',
|
||||
'peer-data-[state=collapsed]:w-[calc(100%-var(--sidebar-width-icon)-1rem)]',
|
||||
'peer-data-[state=expanded]:w-[calc(100%-var(--sidebar-width))]',
|
||||
'sm:transition-[width] sm:duration-200 sm:ease-linear',
|
||||
'flex h-svh flex-col',
|
||||
'group-data-[scroll-locked=1]/body:h-full',
|
||||
'has-[main.fixed-main]:group-data-[scroll-locked=1]/body:h-svh'
|
||||
)}
|
||||
>
|
||||
<Header className="bg-card border-b pl-3"/>
|
||||
{
|
||||
children ? children : <Outlet />
|
||||
}
|
||||
</div>
|
||||
<AppSidebar variant="inset" />
|
||||
{/* dark:p-0 dark:border-l-1 */}
|
||||
<SidebarInset className=" overflow-hidden max-h-screen flex flex-col flex-1 !m-0 !rounded-none bg-sidebar p-1 ">
|
||||
{/*dark:border-none dark:rounded-none */}
|
||||
<div className="border-4 border-sidebar-border ring-1 ring-border/30 dark:!border-background dark:ring-sidebar-border flex flex-col min-h-0 !overflow-hidden rounded-2xl h-screen">
|
||||
<div className="overflow-hidden h-full flex flex-col flex-1 bg-white dark:bg-background dark:!border-1 dark:border-sidebar-border dark:rounded-[14px] !overflow-hidden" >
|
||||
<Header className="bg-card border-b pl-3" />
|
||||
{
|
||||
children ? children : <Outlet />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</SidebarInset>
|
||||
|
||||
</SidebarProvider>
|
||||
)
|
||||
|
||||
@@ -48,8 +48,8 @@ const CardinalityMarker: React.FC<CardinalityMarkerProps> = ({ selected = false,
|
||||
>
|
||||
<svg
|
||||
fill="transparent"
|
||||
className={selected ? "stroke-ring dark:!stroke-primary-foreground" : "stroke-ring/60 dark:!stroke-muted-foreground/60"}
|
||||
strokeWidth="4"
|
||||
className={selected ? ' !stroke-[oklch(0.60_0.028_263.3984)] dark:!stroke-primary-foreground' : 'stroke-[oklch(0.72_0.022_263.3984)] dark:!stroke-muted-foreground'}
|
||||
strokeWidth="6"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 100 100">
|
||||
@@ -74,24 +74,24 @@ const CardinalityMarker: React.FC<CardinalityMarkerProps> = ({ selected = false,
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="8"
|
||||
r="6"
|
||||
strokeWidth="1"
|
||||
className={
|
||||
cn(" fill-background",
|
||||
selected ? " stroke-ring fill-background dark:!stroke-primary-foreground" :
|
||||
" stroke-ring/60 dark:!stroke-muted-foreground "
|
||||
cn(" fill-background ",
|
||||
selected ? " stroke-[oklch(0.60_0.028_263.3984)] fill-background dark:!stroke-primary-foreground" :
|
||||
" stroke-[oklch(0.72_0.022_263.3984)] dark:!stroke-muted-foreground "
|
||||
)
|
||||
}
|
||||
/>
|
||||
<text
|
||||
x="12"
|
||||
y="13"
|
||||
y="12.5"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
fontSize="8"
|
||||
fontSize="6"
|
||||
className={
|
||||
cn("fill-ring/60 font-semibold dark:!fill-muted-foreground" ,
|
||||
selected ? "fill-ring dark:!fill-primary-foreground" : "" ,
|
||||
cn("fill-[oklch(0.72_0.022_263.3984)] font-semibold dark:!fill-muted-foreground" ,
|
||||
selected ? "fill-[oklch(0.60_0.028_263.3984)] dark:!fill-primary-foreground" : "" ,
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useDatabaseHistory } from "@/providers/database-history/database-histor
|
||||
import { useOnViewportChange, useReactFlow } from "@xyflow/react";
|
||||
|
||||
import { LayoutGrid, Redo, Scan, Undo, ZoomIn, ZoomOut } from "lucide-react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface DbControlButtons {
|
||||
@@ -58,8 +58,8 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex !rounded-md p-2 border-1 !overflow-hidden !bg-background/20 dark:!bg-card/20 text-muted-foreground shadow-md backdrop-blur-sm"
|
||||
>
|
||||
<div className="flex !rounded-md p-2 border-1 !overflow-hidden !bg-input/50 dark:!bg-card/50 text-muted-foreground shadow-md backdrop-blur-xs dark:backdrop-blur-md">
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
|
||||
@@ -52,6 +52,7 @@ import { Modals } from "@/providers/modal-provider/modal-contxet";
|
||||
import { useModal } from "@/providers/modal-provider/modal-provider";
|
||||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import { cn } from "@/lib/utils";
|
||||
import useToast from "@/hooks/use-toast";
|
||||
|
||||
|
||||
|
||||
@@ -72,7 +73,7 @@ const DatabaseDiagram: React.FC = () => {
|
||||
|
||||
// Diagram-related state (e.g. connection in progress)
|
||||
const { setIsConnectionInProgress, cardinalityStyle, showController, openController } = useDiagramOps();
|
||||
|
||||
const raise = useToast() ;
|
||||
// Destructure tables and relationships from database
|
||||
const { tables, relationships } = database || { tables: [], relationships: [] };
|
||||
|
||||
@@ -145,13 +146,11 @@ const DatabaseDiagram: React.FC = () => {
|
||||
} else {
|
||||
|
||||
|
||||
toast(t("db_controller.invalid_relationship.title"), {
|
||||
description: t("db_controller.invalid_relationship.description"),
|
||||
classNames: {
|
||||
description: "!text-destructive",
|
||||
title: "!text-destructive"
|
||||
},
|
||||
})
|
||||
raise(
|
||||
t("db_controller.invalid_relationship.title"),
|
||||
t("db_controller.invalid_relationship.description"),
|
||||
"ERROR"
|
||||
)
|
||||
}
|
||||
|
||||
setIsConnectionInProgress(false);
|
||||
@@ -266,17 +265,18 @@ const DatabaseDiagram: React.FC = () => {
|
||||
</Controls >
|
||||
<MiniMap
|
||||
nodeStrokeWidth={4}
|
||||
className="!bg-background border-1 rounded-lg overflow-hidden "
|
||||
maskStrokeColor={resolvedTheme == "dark" ? "#FFFFFF1A" : "#e2e8f0"}
|
||||
maskColor={resolvedTheme == "dark" ? "#21262d77" : "#62748e05"}
|
||||
className="!bg-input/50 ring-1 ring-border shadow-sm rounded-md overflow-hidden shadow-md backdrop-blur-xs dark:!bg-background/50 "
|
||||
maskStrokeColor={resolvedTheme == "dark" ? "#FFFFFF1A" : "#cad5e2"}
|
||||
maskColor={resolvedTheme == "dark" ? "#21262d88" : "#f1f5f9cc"}
|
||||
maskStrokeWidth={1}
|
||||
nodeClassName={"!fill-muted-foreground/20 "}
|
||||
|
||||
nodeClassName={"!fill-foreground/20 "}
|
||||
style={{
|
||||
width: 164,
|
||||
width: 148,
|
||||
height: 128
|
||||
}}
|
||||
/>
|
||||
<Background color="#62748e" className="dark:!bg-background " />
|
||||
<Background color="#62748e" className="!bg-background dark:!bg-background" />
|
||||
|
||||
</ReactFlow>
|
||||
<div
|
||||
|
||||
@@ -52,7 +52,7 @@ const CircularDependencyAlert: React.FC<CircularDependencyAlertProps> = ({ error
|
||||
deleteRelationship(id)
|
||||
}, [])
|
||||
return (
|
||||
<div className="flex flex-col gap-2 w-full h-full items-center pt-12 ">
|
||||
<div className="flex flex-col gap-2 w-full h-full items-center pt-12 ">
|
||||
<AlertTriangle
|
||||
className="size-12 text-destructive"
|
||||
/>
|
||||
@@ -63,14 +63,14 @@ const CircularDependencyAlert: React.FC<CircularDependencyAlertProps> = ({ error
|
||||
{t("db_controller.circular_dependency.description")} , <span className="font-medium text-foreground"> {t("db_controller.circular_dependency.suggestion")} </span>
|
||||
</p>
|
||||
|
||||
<ul aria-label="Relationships" className="w-[80%] max-w-[360px]"
|
||||
<ul aria-label="Relationships" className="w-[80%] max-w-[360px] mt-4"
|
||||
>
|
||||
{
|
||||
circularRelationships.map((relationship: RelationshipType) => (
|
||||
<li
|
||||
onClick={() => focus(relationship.id)}
|
||||
key={relationship.id}>
|
||||
<div className="hover:bg-secondary flex items-center justify-between h-10 mb-2 px-3 rounded-md cursor-pointer">
|
||||
<div className="hover:bg-secondary flex items-center justify-between h-10 mb-2 px-3 rounded-md cursor-pointer bg-card border-1">
|
||||
<span className="text-sm">
|
||||
{relationship.sourceTable.name} -> {relationship.targetTable.name}
|
||||
</span>
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
|
||||
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import { sql } from '@codemirror/lang-sql';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { overrideDarkTheme, overrideLightTheme } from "@/lib/colors";
|
||||
|
||||
import { DatabaseType } from "@/lib/schemas/database-schema";
|
||||
import CircularDependencyAlert from "./circular-dependecy-alert";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Clipboard from "@/components/clipboard";
|
||||
import { TableType } from "@/lib/schemas/table-schema";
|
||||
import { RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
import { useTheme } from "@/providers/theme-provider/theme-provider";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import BaseDatabaseRenderer from "@/utils/render/database/base-database-renderer";
|
||||
import { CircularDependencyError, getRenderer } from "@/utils/render/render-uttils";
|
||||
import { areArraysEqual } from "@/utils/utils";
|
||||
import CodeEditor from "@/components/code-editor";
|
||||
import useToast from "@/hooks/use-toast";
|
||||
|
||||
|
||||
interface SqlPreviewProps {
|
||||
tableFilterIds?: string[]
|
||||
tableFilterIds?: string[] ,
|
||||
className? : string ;
|
||||
}
|
||||
|
||||
const SqlPreview: React.FC<SqlPreviewProps> = ({ tableFilterIds }) => {
|
||||
const SqlPreview: React.FC<SqlPreviewProps> = ({ tableFilterIds , className}) => {
|
||||
|
||||
|
||||
const { database: currentDatabase } = useDatabase();
|
||||
const { data_types } = useDatabaseOperations();
|
||||
const [sqlCode, setSqlCode] = useState<string>("");
|
||||
|
||||
const [sqlCode, setSqlCode] = useState<string>("");
|
||||
const raise = useToast();
|
||||
const [circularDependency, setCircularDependency] = useState<CircularDependencyError | undefined>(undefined);
|
||||
|
||||
const database = useMemo(() => {
|
||||
@@ -42,19 +41,18 @@ const SqlPreview: React.FC<SqlPreviewProps> = ({ tableFilterIds }) => {
|
||||
),
|
||||
} as DatabaseType;
|
||||
}, [currentDatabase, tableFilterIds])
|
||||
|
||||
const { theme } = useTheme();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
|
||||
if (database?.dialect && data_types.length > 0) {
|
||||
|
||||
|
||||
try {
|
||||
const renderer: BaseDatabaseRenderer = getRenderer(database.dialect, data_types);
|
||||
const sql: string = await renderer.renderDDL(database)
|
||||
|
||||
|
||||
setSqlCode(sql);
|
||||
setCircularDependency(undefined);
|
||||
|
||||
@@ -76,35 +74,34 @@ const SqlPreview: React.FC<SqlPreviewProps> = ({ tableFilterIds }) => {
|
||||
}, [database, data_types]);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (circularDependency)
|
||||
toast(t("db_controller.circular_dependency.title"), {
|
||||
description: t("db_controller.circular_dependency.description"),
|
||||
classNames: {
|
||||
description: "!text-destructive",
|
||||
title: "!text-destructive"
|
||||
},
|
||||
});
|
||||
raise(
|
||||
t("db_controller.circular_dependency.title"),
|
||||
t("db_controller.circular_dependency.description"),
|
||||
"ERROR"
|
||||
);
|
||||
|
||||
}, [circularDependency]);
|
||||
|
||||
|
||||
if (circularDependency)
|
||||
return <CircularDependencyAlert error={circularDependency} />
|
||||
|
||||
else
|
||||
return (
|
||||
<div className="flex w-full h-full relative ">
|
||||
<div className="absolute right-[12px] top-[4px] z-[1] ">
|
||||
<div className="flex w-full h-full relative min-w-0 !min-h-0 ">
|
||||
<div className="absolute right-2 top-2 z-1">
|
||||
<Clipboard
|
||||
text={sqlCode}
|
||||
/>
|
||||
</div>
|
||||
<CodeMirror
|
||||
<CodeEditor
|
||||
defaultValue={sqlCode}
|
||||
value={sqlCode}
|
||||
className="flex flex-1 w-full "
|
||||
extensions={[sql()]}
|
||||
readOnly
|
||||
theme={theme != "dark" ? overrideLightTheme : [oneDark, overrideDarkTheme]}
|
||||
className={ className}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -206,9 +206,10 @@ const TablesController: React.FC = ({ }) => {
|
||||
}
|
||||
{
|
||||
allTables.length > 0 && showSqlPreview &&
|
||||
<div className=" flex-1 overflow-auto -ml-3">
|
||||
<div className=" flex-1 overflow-auto ">
|
||||
<SqlPreview
|
||||
tableFilterIds={tableFilterIds}
|
||||
className="rounded-none border-0 border-t-1"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ interface Props {
|
||||
field: FieldType,
|
||||
showHandles?: boolean,
|
||||
highlight?: boolean,
|
||||
color?: string
|
||||
color?: string,
|
||||
className?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +29,7 @@ export const TARGET_PREFIX = "target_";
|
||||
|
||||
const Field: React.FC<Props> = (props) => {
|
||||
|
||||
const { field, showHandles, highlight, color } = props;
|
||||
const { field, showHandles, highlight, color, className } = props;
|
||||
const [editMode, setEditMode] = useState<boolean>(false);
|
||||
const { deleteField, editField } = useDatabaseOperations();
|
||||
const [fieldName, setFieldName] = useState<string>(field.name);
|
||||
@@ -39,14 +40,17 @@ const Field: React.FC<Props> = (props) => {
|
||||
}, [field.name]);
|
||||
|
||||
const removeField = useCallback(() => {
|
||||
|
||||
deleteField(field.id)
|
||||
}, [])
|
||||
}, [field.id])
|
||||
|
||||
const saveFieldName = useCallback(() => {
|
||||
|
||||
editField({
|
||||
id: field.id,
|
||||
name: fieldName
|
||||
} as FieldType);
|
||||
|
||||
setEditMode(false);
|
||||
}, [fieldName]);
|
||||
|
||||
@@ -54,16 +58,17 @@ const Field: React.FC<Props> = (props) => {
|
||||
return (
|
||||
<div className={cn(
|
||||
"group relative flex h-8 items-center justify-between gap-1 px-1.5 text-sm hover:bg-secondary transition-all duration-200 ease-in-out ",
|
||||
highlight ? "bg-secondary" : ""
|
||||
highlight ? "bg-secondary" : "",
|
||||
className
|
||||
)}>
|
||||
<div className="text-muted-foreground flex items-center truncate gap-1.5">
|
||||
<div className="text-muted-foreground flex items-center truncate w-full gap-1.5 min-w-0 ">
|
||||
{
|
||||
field.isPrimary &&
|
||||
<IconKey className="size-3" />
|
||||
}
|
||||
{
|
||||
field.nullable && !field.isPrimary &&
|
||||
<IconKeyframe className="size-3" />
|
||||
<IconKeyframe className="size-3 stroke-3" />
|
||||
}
|
||||
{
|
||||
!field.nullable && !field.isPrimary &&
|
||||
@@ -72,7 +77,7 @@ const Field: React.FC<Props> = (props) => {
|
||||
{
|
||||
!editMode ?
|
||||
<label
|
||||
className={"truncate flex gap-1 text-xs text-foreground "}
|
||||
className={"truncate flex gap-1 text-xs text-foreground font-medium "}
|
||||
onDoubleClick={() => setEditMode(true)}
|
||||
>
|
||||
{fieldName}
|
||||
@@ -98,7 +103,7 @@ const Field: React.FC<Props> = (props) => {
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onChange={(e) => setFieldName(e.target.value)}
|
||||
className="h-6 font-bold pb-1.5 rounded-sm px-1"
|
||||
onKeyDown={(e: any) => {
|
||||
onKeyDown={(e: any) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
saveFieldName();
|
||||
@@ -106,15 +111,15 @@ const Field: React.FC<Props> = (props) => {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
}
|
||||
</div>
|
||||
{
|
||||
!editMode ?
|
||||
<div className="text-xs text-muted-foreground flex shrink-0 ">
|
||||
<div className="group-hover:opacity-0">
|
||||
<div className={cn(" !text-muted-foreground font-medium group-hover:opacity-0")}>
|
||||
{field.type?.name?.split(' ')[0]}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-1 opacity-0 shrink-0 flex-row group-hover:opacity-100 transition-opacity duration-200 absolute right-1 top-1 ">
|
||||
<Button variant="outline" size="icon" className="size-6 shrink-0 shadow-sm rounded-sm" onClick={() => setEditMode(true)}>
|
||||
<IconPencil className="size-3 text-muted-foreground " />
|
||||
@@ -123,6 +128,7 @@ const Field: React.FC<Props> = (props) => {
|
||||
<IconTrash className="size-3 text-destructive " />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
:
|
||||
<div className="text-xs text-muted-foreground flex ml-1">
|
||||
|
||||
@@ -80,7 +80,7 @@ const Relationship: React.FC<EdgeProps<RelationshipProps>> = (props) => {
|
||||
sourceY,
|
||||
targetX: targetSide === 'left' ? targetLeftX : targetRightX,
|
||||
targetY,
|
||||
borderRadius: 6,
|
||||
borderRadius: 8,
|
||||
sourcePosition:
|
||||
sourceSide === 'left' ? Position.Left : Position.Right,
|
||||
targetPosition:
|
||||
@@ -125,7 +125,7 @@ const Relationship: React.FC<EdgeProps<RelationshipProps>> = (props) => {
|
||||
markerEnd={`url(#${endMarker})`}
|
||||
fill="none"
|
||||
className={cn([
|
||||
`!stroke-1 ${selected ? '!stroke-ring dark:!stroke-primary-foreground' : 'stroke-ring/60 dark:!stroke-muted-foreground'}`,
|
||||
`!stroke-[1.5px] ${selected ? ' !stroke-[oklch(0.60_0.028_263.3984)] dark:!stroke-primary-foreground' : 'stroke-[oklch(0.72_0.022_263.3984)] dark:!stroke-muted-foreground'}`,
|
||||
])}
|
||||
onClick={(e) => {
|
||||
if (e.detail === 2) {
|
||||
@@ -143,7 +143,6 @@ const Relationship: React.FC<EdgeProps<RelationshipProps>> = (props) => {
|
||||
fill="none"
|
||||
strokeOpacity={0}
|
||||
strokeWidth={16}
|
||||
|
||||
className="react-flow__edge-interaction"
|
||||
onClick={(e) => {
|
||||
if (e.detail === 2) {
|
||||
|
||||
@@ -56,7 +56,7 @@ const Table: React.FC<NodeProps<TableProps>> = (props) => {
|
||||
|
||||
|
||||
const fields: React.ReactNode[] = useMemo(() => {
|
||||
return table.fields.map((field: FieldType) => {
|
||||
return table.fields.map((field: FieldType , index : number) => {
|
||||
|
||||
const highlight: boolean = highlightedEdges.find((edge: any) =>
|
||||
(edge.data?.relationship as RelationshipType).sourceFieldId == field.id ||
|
||||
@@ -68,6 +68,7 @@ const Table: React.FC<NodeProps<TableProps>> = (props) => {
|
||||
showHandles={selected}
|
||||
highlight={highlight}
|
||||
color={table.color as string}
|
||||
className={ index == table.fields.length - 1 ? "!rounded-b-md" : undefined }
|
||||
/>)
|
||||
})
|
||||
}, [table.fields, selected, highlightedEdges]);
|
||||
@@ -78,11 +79,11 @@ const Table: React.FC<NodeProps<TableProps>> = (props) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Card
|
||||
<Card
|
||||
className={cn(
|
||||
"rounded-[12px] p-0.5 gap-0 transition-all duration-200",
|
||||
"rounded-lg p-0.5 gap-0 transition-all duration-200 border-none ring-1 ring-slate-300 shadow-xs dark:ring-border ",
|
||||
overlapping
|
||||
? 'ring-1 ring-destructive scale-105 shadow-danger '
|
||||
? 'ring-1 !ring-destructive scale-105 shadow-danger '
|
||||
: '',
|
||||
!pulsing && overlapping
|
||||
? 'scale-105'
|
||||
@@ -91,23 +92,23 @@ const Table: React.FC<NodeProps<TableProps>> = (props) => {
|
||||
? 'scale-110'
|
||||
: '',
|
||||
|
||||
selected && !overlapping ? "ring-1 ring-primary" : ""
|
||||
selected && !overlapping ? "ring-[1.5px] !ring-primary" : ""
|
||||
)}
|
||||
style={
|
||||
(selected && table.color && !overlapping) ?
|
||||
{
|
||||
boxShadow: "0 0 0 1px " + table.color,
|
||||
boxShadow: "0 0 0 1.5px " + table.color,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onDoubleClick={focus}
|
||||
|
||||
|
||||
>
|
||||
<CardHeader className="group rounded-t rounded-t-md p-1.5 flex items-center mb-0 bg-primary/10 "
|
||||
<CardHeader className="group rounded-t rounded-t-md p-1.5 border-1 border-primary/20 flex items-center mb-0 bg-primary/10 "
|
||||
style={
|
||||
table.color ? {
|
||||
backgroundColor: table.color + "20" as string
|
||||
backgroundColor: table.color + "20" as string,
|
||||
border: " 1px solid " + table.color + "60"
|
||||
} : undefined
|
||||
}
|
||||
>
|
||||
@@ -115,15 +116,17 @@ const Table: React.FC<NodeProps<TableProps>> = (props) => {
|
||||
{!editMode ? <>
|
||||
<label
|
||||
className=" w-full text-editable truncate py-0.5 text-sm font-bold text-primary"
|
||||
onDoubleClick={() => setEditMode(true)}
|
||||
onDoubleClick={ () => setEditMode(true) }
|
||||
style={{ color: table.color as string }}
|
||||
>
|
||||
{tableName}
|
||||
</label>
|
||||
<div className="flex gap-1 hidden shrink-0 flex-row group-hover:flex ">
|
||||
<Button variant="outline" size="icon" className="size-6 shrink-0 shadow-sm rounded-sm" onClick={() => setEditMode(true)}>
|
||||
<IconPencil className="size-3 text-muted-foreground " />
|
||||
</Button>
|
||||
|
||||
<Button variant="outline" size="icon" className="size-6 shrink-0 shadow-sm rounded-sm" onClick={() => setEditMode(true)}>
|
||||
<IconPencil className="size-3 text-muted-foreground " />
|
||||
</Button>
|
||||
|
||||
<Button variant="outline" size="icon" className="size-6 shrink-0 shadow-sm rounded-sm" onClick={focus}>
|
||||
<IconFocus2 className="size-3 text-muted-foreground " />
|
||||
</Button>
|
||||
@@ -159,14 +162,14 @@ const Table: React.FC<NodeProps<TableProps>> = (props) => {
|
||||
|
||||
}
|
||||
</CardHeader>
|
||||
<CardContent className="p-0 mt-0">
|
||||
<CardContent className="p-0 mt-0 rounded-b-md ">
|
||||
{
|
||||
!showMore ? fields.slice(0, MAX_FIELDS) : fields
|
||||
}
|
||||
|
||||
{fields.length > MAX_FIELDS && (
|
||||
<div
|
||||
className="flex h-8 cursor-pointer items-center gap-1 justify-center text-xs transition-colors duration-200 "
|
||||
className="flex h-8 cursor-pointer items-center gap-1 justify-center text-xs transition-colors duration-200 "
|
||||
onClick={toggleShowMore}
|
||||
>
|
||||
{showMore ? (
|
||||
|
||||
@@ -5,25 +5,28 @@ import Modal, { ModalProps } from "@/components/modal";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SqlPreview from "../components/db-controller/sql-preview";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
const ExportSqlModal: React.FC<ModalProps> = ({ isOpen, onOpenChange }) => {
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
title={t("modals.export_sql")}
|
||||
className="w-full lg:min-w-[960px]"
|
||||
className="w-full lg:min-w-[960px] "
|
||||
description={t("modals.export_sql_header")}
|
||||
>
|
||||
<div className="h-full max-h-[72vh] min-w-0">
|
||||
<Separator />
|
||||
<div className="h-full min-h-[65vh] max-h-[65vh] min-w-0 !bg-background p-1 border-1 rounded-md shadow-xs" >
|
||||
<SqlPreview />
|
||||
</div>
|
||||
<Separator />
|
||||
|
||||
</Modal>
|
||||
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import Modal, { ModalProps } from "@/components/modal"
|
||||
import ReactCodeMirror, { oneDark } from "@uiw/react-codemirror";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { overrideDarkTheme, overrideLightTheme } from "@/lib/colors";
|
||||
import { sql } from '@codemirror/lang-sql';
|
||||
import { DatabaseDialect, ImportDatabaseMethod, ImportDatabaseOption, ImportMethodType, MARIADB_DUMP_EXAMPLE, MARIADB_DUMP_INSTRUCTIONS, MYSQL_DUMP_EXAMPLE, MYSQL_DUMP_INSTRUCTIONS, PG_DUMP_EXAMPLE, PG_DUMP_INSTRUCTIONS, SQLITE_DUMP_EXAMPLE, SQLITE_DUMP_INSRUCTION } from "@/lib/database";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { AlertCircleIcon, Code, HelpCircle } from "lucide-react";
|
||||
@@ -20,6 +17,7 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { BaseSqlImporter } from "@/utils/import/base-sql-importer";
|
||||
import { getImporter } from "@/utils/import/import-utils";
|
||||
import CodeEditor from "@/components/code-editor";
|
||||
|
||||
|
||||
const options: ImportDatabaseOption[] = [{
|
||||
@@ -180,8 +178,7 @@ const ImportDatabaseModal: React.FC<ModalProps> = ({ isOpen, onOpenChange }) =>
|
||||
setParsedDatabase(parsedDatabase);
|
||||
setError(false);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
setError(true);
|
||||
setParsedDatabase(undefined)
|
||||
}
|
||||
@@ -235,7 +232,7 @@ const ImportDatabaseModal: React.FC<ModalProps> = ({ isOpen, onOpenChange }) =>
|
||||
return;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
title={t("modals.import_database.title")}
|
||||
@@ -245,7 +242,7 @@ const ImportDatabaseModal: React.FC<ModalProps> = ({ isOpen, onOpenChange }) =>
|
||||
isDisabled={!parsedDatabase}
|
||||
>
|
||||
<div className="flex flex-col gap-4 !min-w-0 ">
|
||||
<div className="w-full space-y-2 !min-w-0 ">
|
||||
<div className="w-full space-y-2 min-w-0 ">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("modals.import_database.import_options")}
|
||||
</p>
|
||||
@@ -298,6 +295,7 @@ const ImportDatabaseModal: React.FC<ModalProps> = ({ isOpen, onOpenChange }) =>
|
||||
</RadioGroup>
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
{
|
||||
selectedImportMethod?.type == ImportMethodType.DUMP &&
|
||||
@@ -316,7 +314,7 @@ const ImportDatabaseModal: React.FC<ModalProps> = ({ isOpen, onOpenChange }) =>
|
||||
<div className=" font-normal w-full">
|
||||
<span className="border-1 border-border p-1 px-2 rounded-md w-full flex items-center justify-between">
|
||||
{selectedImportMethod.instruction}
|
||||
<Clipboard text={selectedImportMethod.example} />
|
||||
<Clipboard text={selectedImportMethod.instruction} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -362,18 +360,17 @@ const ImportDatabaseModal: React.FC<ModalProps> = ({ isOpen, onOpenChange }) =>
|
||||
}
|
||||
|
||||
</div>
|
||||
<div className="flex flex-col ">
|
||||
<ReactCodeMirror
|
||||
<div className="flex flex-1 flex-col ">
|
||||
<CodeEditor
|
||||
className={
|
||||
cn("flex w-full border-1 rounded-md border-divider overflow-hidden ",
|
||||
cn("flex w-full border-1 rounded-md border-divider overflow-hidden ",
|
||||
(error || parsedDatabase?.errors?.length > 0) ? "min-h-[360px] max-h-[360px]" : "min-h-[424px] max-h-[424px]"
|
||||
)
|
||||
}
|
||||
extensions={[sql()]}
|
||||
value={sqlCode}
|
||||
onChange={validateSql}
|
||||
theme={resolvedTheme == "light" ? overrideLightTheme : [oneDark, overrideDarkTheme]}
|
||||
/>
|
||||
|
||||
<div className="mt-4">
|
||||
{
|
||||
error &&
|
||||
@@ -394,13 +391,13 @@ const ImportDatabaseModal: React.FC<ModalProps> = ({ isOpen, onOpenChange }) =>
|
||||
(parsedDatabase?.errors && parsedDatabase?.errors?.length > 0) &&
|
||||
<Alert
|
||||
variant={"default"}
|
||||
className="text-chart-5 dark:text-chart-3"
|
||||
className="text-chart-4"
|
||||
>
|
||||
|
||||
<AlertTitle>
|
||||
{t("modals.import_database.import_warning")}
|
||||
</AlertTitle>
|
||||
<AlertDescription className="text-chart-5 dark:text-chart-3">
|
||||
<AlertDescription className="text-chart-4 ">
|
||||
{t("modals.import_database.import_warning_description")}
|
||||
</AlertDescription>
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
import { IconCircleCheck } from "@tabler/icons-react";
|
||||
import { CircleAlert, OctagonX } from "lucide-react";
|
||||
import { useCallback } from "react";
|
||||
import { toast } from "sonner";
|
||||
export type ToastVariant = "INFO" | "ERROR" | "SUCCESS";
|
||||
|
||||
export interface ToastAction {
|
||||
title: string,
|
||||
actionHandler: () => void;
|
||||
}
|
||||
|
||||
const useToast = () => {
|
||||
|
||||
const raise = useCallback((title: string, description: React.ReactNode, variant: ToastVariant = "INFO", action?: ToastAction) => {
|
||||
|
||||
toast(title, {
|
||||
description,
|
||||
...toastStyles[variant],
|
||||
action: action ? {
|
||||
label: action.title,
|
||||
onClick: action.actionHandler,
|
||||
} : undefined,
|
||||
|
||||
});
|
||||
}, []);
|
||||
|
||||
return raise;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const toastStyles = {
|
||||
SUCCESS: {
|
||||
classNames: {
|
||||
description: "!text-chart-2"
|
||||
},
|
||||
|
||||
style: {
|
||||
'--normal-bg':
|
||||
'color-mix(in oklab, light-dark(var(--color-chart-2), var(--color-chart-2)) 10%, var(--background))',
|
||||
'--normal-text': 'light-dark(var(--color-chart-2), var(--color-chart-2))',
|
||||
'--normal-border': 'light-dark(var(--color-chart-2), var(--color-chart-2))'
|
||||
} as React.CSSProperties,
|
||||
icon: <IconCircleCheck className="size-5" />
|
||||
},
|
||||
INFO: {
|
||||
classNames: {
|
||||
description: "!text-primary"
|
||||
},
|
||||
|
||||
style: {
|
||||
'--normal-bg':
|
||||
'color-mix(in oklab, light-dark(var(--color-primary), var(--primary)) 10%, var(--background))',
|
||||
'--normal-text': 'light-dark(var(--color-primary), var(--primary))',
|
||||
'--normal-border': 'light-dark(var(--color-primary), var(--primary))'
|
||||
} as React.CSSProperties,
|
||||
icon: <CircleAlert className="size-4" />
|
||||
|
||||
},
|
||||
ERROR: {
|
||||
classNames: {
|
||||
description: "!text-destructive"
|
||||
},
|
||||
style: {
|
||||
'--normal-bg': 'color-mix(in oklab, var(--destructive) 10%, var(--background))',
|
||||
'--normal-text': 'var(--destructive)',
|
||||
'--normal-border': 'var(--destructive)'
|
||||
} as React.CSSProperties ,
|
||||
icon : <OctagonX className="size-4" />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default useToast;
|
||||
+118
-165
@@ -1,72 +1,87 @@
|
||||
@import 'tailwindcss';
|
||||
@import "tailwindcss";
|
||||
@import 'tw-animate-css';
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.129 0.042 264.695);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.129 0.042 264.695);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.129 0.042 264.695);
|
||||
--primary: oklch(0.606 0.25 292.717);
|
||||
--primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--secondary: oklch(0.968 0.007 247.896);
|
||||
--secondary-foreground: oklch(0.208 0.042 265.755);
|
||||
--muted: oklch(0.968 0.007 247.896);
|
||||
--muted-foreground: oklch(0.554 0.046 257.417);
|
||||
--accent: oklch(0.968 0.007 247.896);
|
||||
--accent-foreground: oklch(0.208 0.042 265.755);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.929 0.013 255.508);
|
||||
--input: oklch(0.929 0.013 255.508);
|
||||
--ring: oklch(0.704 0.04 256.788);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--background: oklch(0.9848 0 0);
|
||||
--foreground: oklch(0.2496 0.0417 263.3984);
|
||||
--card: oklch(1.0000 0 0);
|
||||
--card-foreground: oklch(0.2496 0.0417 263.3984);
|
||||
--popover: oklch(1.0000 0 0);
|
||||
--popover-foreground: oklch(0.2496 0.0417 263.3984);
|
||||
--primary: oklch(0.5424 0.2454 293.0160);
|
||||
--primary-foreground: oklch(0.9843 0.0017 247.8393);
|
||||
--secondary: oklch(0.9684 0.0068 247.8951);
|
||||
--secondary-foreground: oklch(0.2079 0.0399 265.7275);
|
||||
--muted: oklch(0.9684 0.0068 247.8951);
|
||||
--muted-foreground: oklch(0.5547 0.0407 257.4404);
|
||||
--accent: oklch(0.9544 0.0226 302.5680);
|
||||
--accent-foreground: oklch(0.5424 0.2454 293.0160);
|
||||
--destructive: oklch(0.6368 0.2078 25.3259);
|
||||
--destructive-foreground: oklch(0.9843 0.0017 247.8393);
|
||||
--border: oklch(0.9290 0.0126 255.5317);
|
||||
--input: oklch(0.9290 0.0126 255.5317);
|
||||
--ring: oklch(0.5424 0.2454 293.0160);
|
||||
--chart-1: oklch(0.5424 0.2454 293.0160);
|
||||
--chart-2: oklch(0.6309 0.1013 183.4907);
|
||||
--chart-3: oklch(0.3787 0.0440 225.5393);
|
||||
--chart-4: oklch(0.8336 0.1186 88.1463);
|
||||
--chart-5: oklch(0.7834 0.1261 58.7491);
|
||||
--sidebar: oklch(0.2064 0.0388 265.5472);
|
||||
--sidebar-foreground: oklch(0.9838 0.0035 247.8583);
|
||||
--sidebar-primary: oklch(0.5424 0.2454 293.0160);
|
||||
--sidebar-primary-foreground: oklch(1.0000 0 0);
|
||||
--sidebar-accent: oklch(0.2709 0.0592 265.1803);
|
||||
--sidebar-accent-foreground: oklch(0.9838 0.0035 247.8583);
|
||||
--sidebar-border: oklch(0.2887 0.0646 265.1126);
|
||||
--sidebar-ring: oklch(0.5424 0.2454 293.0160);
|
||||
--font-sans: Inter, -apple-system, sans-serif;
|
||||
--font-serif: Georgia, serif;
|
||||
--font-mono: JetBrains Mono, monospace;
|
||||
--radius: 0.75rem;
|
||||
--shadow-x: 0px;
|
||||
--shadow-y: 4px;
|
||||
--shadow-blur: 15px;
|
||||
--shadow-spread: 0px;
|
||||
--shadow-opacity: 0.05;
|
||||
--shadow-color: hsl(220, 40%, 15%);
|
||||
--shadow-2xs: 0px 4px 15px 0px hsl(220 40% 15% / 0.03);
|
||||
--shadow-xs: 0px 4px 15px 0px hsl(220 40% 15% / 0.03);
|
||||
--shadow-sm: 0px 4px 15px 0px hsl(220 40% 15% / 0.05), 0px 1px 2px -1px hsl(220 40% 15% / 0.05);
|
||||
--shadow: 0px 4px 15px 0px hsl(220 40% 15% / 0.05), 0px 1px 2px -1px hsl(220 40% 15% / 0.05);
|
||||
--shadow-md: 0px 4px 15px 0px hsl(220 40% 15% / 0.05), 0px 2px 4px -1px hsl(220 40% 15% / 0.05);
|
||||
--shadow-lg: 0px 4px 15px 0px hsl(220 40% 15% / 0.05), 0px 4px 6px -1px hsl(220 40% 15% / 0.05);
|
||||
--shadow-xl: 0px 4px 15px 0px hsl(220 40% 15% / 0.05), 0px 8px 10px -1px hsl(220 40% 15% / 0.05);
|
||||
--shadow-2xl: 0px 4px 15px 0px hsl(220 40% 15% / 0.13);
|
||||
--tracking-normal: -0.015em;
|
||||
--spacing: 0.25rem;
|
||||
|
||||
--sidebar: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
|
||||
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.240 0.011 256.8);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
|
||||
|
||||
--background: oklch(0.240 0.011 256.8);
|
||||
--foreground: oklch(0.9838 0.0035 247.8583);
|
||||
--card: oklch(0.266 0.015 257.5);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--card-foreground: oklch(0.9838 0.0035 247.8583);
|
||||
--popover: oklch(0.266 0.015 257.5);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.541 0.281 293.009);
|
||||
--popover-foreground: oklch(0.9838 0.0035 247.8583);
|
||||
--primary: oklch(0.4865 0.2423 291.8661);
|
||||
--primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--secondary: oklch(0.295 0.015 257.7);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--secondary-foreground: oklch(0.9838 0.0035 247.8583);
|
||||
--muted: oklch(0.295 0.015 257.7);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.295 0.015 257.7);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--accent-foreground: oklch(0.5424 0.2454 293.0160);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 10%);
|
||||
--ring: oklch(0.541 0.281 293.009);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--ring: oklch(0.4865 0.2423 291.8661);
|
||||
|
||||
--sidebar: oklch(0.266 0.015 257.5);
|
||||
--sidebar: oklch(0.2639 0.0161 264.25);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.541 0.281 293.009);
|
||||
--sidebar-primary-foreground: oklch(0.969 0.016 293.756);
|
||||
@@ -74,59 +89,21 @@
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.541 0.281 293.009);
|
||||
|
||||
--chart-1: oklch(0.5424 0.2454 293.0160);
|
||||
--chart-2: oklch(0.6309 0.1013 183.4907);
|
||||
--chart-3: oklch(0.3787 0.0440 225.5393);
|
||||
--chart-4: oklch(0.8336 0.1186 88.1463);
|
||||
--chart-5: oklch(0.7834 0.1261 58.7491);
|
||||
|
||||
|
||||
--font-sans: Inter, -apple-system, sans-serif;
|
||||
--font-serif: Georgia, serif;
|
||||
--font-mono: JetBrains Mono, monospace;
|
||||
--radius: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
.dark {
|
||||
--background: oklch(0.240 0.011 256.8);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.266 0.015 257.5);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.266 0.015 257.5);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.541 0.281 293.009);
|
||||
--primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.541 0.281 293.009);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
|
||||
--sidebar: oklch(0.295 0.015 256.8);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.541 0.281 293.009);
|
||||
--sidebar-primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.541 0.281 293.009);
|
||||
}
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@theme inline {
|
||||
--font-inter: 'Inter', 'sans-serif';
|
||||
--font-manrope: 'Manrope', 'sans-serif';
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
@@ -142,6 +119,7 @@
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
@@ -158,19 +136,41 @@
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
|
||||
--font-sans: var(--font-sans);
|
||||
--font-mono: var(--font-mono);
|
||||
--font-serif: var(--font-serif);
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
|
||||
--shadow-2xs: var(--shadow-2xs);
|
||||
--shadow-xs: var(--shadow-xs);
|
||||
--shadow-sm: var(--shadow-sm);
|
||||
--shadow: var(--shadow);
|
||||
--shadow-md: var(--shadow-md);
|
||||
--shadow-lg: var(--shadow-lg);
|
||||
--shadow-xl: var(--shadow-xl);
|
||||
--shadow-2xl: var(--shadow-2xl);
|
||||
|
||||
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
|
||||
--tracking-tight: calc(var(--tracking-normal) - 0.025em);
|
||||
--tracking-normal: var(--tracking-normal);
|
||||
--tracking-wide: calc(var(--tracking-normal) + 0.025em);
|
||||
--tracking-wider: calc(var(--tracking-normal) + 0.05em);
|
||||
--tracking-widest: calc(var(--tracking-normal) + 0.1em);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--border) transparent;
|
||||
}
|
||||
html {
|
||||
@apply overflow-x-hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground min-h-svh w-full;
|
||||
@apply bg-background text-foreground;
|
||||
letter-spacing: var(--tracking-normal);
|
||||
}
|
||||
|
||||
button:not(:disabled),
|
||||
@@ -180,74 +180,27 @@
|
||||
|
||||
/* Prevent focus zoom on mobile devices */
|
||||
@media screen and (max-width: 767px) {
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@utility container {
|
||||
margin-inline: auto;
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
|
||||
@utility no-scrollbar {
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
@utility faded-bottom {
|
||||
@apply after:pointer-events-none after:absolute after:bottom-0 after:left-0 after:hidden after:h-32 after:w-full after:bg-[linear-gradient(180deg,_transparent_10%,_var(--background)_70%)] md:after:block;
|
||||
}
|
||||
|
||||
/* styles.css */
|
||||
.CollapsibleContent {
|
||||
overflow: hidden;
|
||||
}
|
||||
.CollapsibleContent[data-state='open'] {
|
||||
animation: slideDown 300ms ease-out;
|
||||
}
|
||||
.CollapsibleContent[data-state='closed'] {
|
||||
animation: slideUp 300ms ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
height: 0;
|
||||
}
|
||||
to {
|
||||
height: var(--radix-collapsible-content-height);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
height: var(--radix-collapsible-content-height);
|
||||
}
|
||||
to {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.prose :where(h1, h2, h3, h4, h5, h6) {
|
||||
@apply font-bold text-foreground my-4;
|
||||
}
|
||||
|
||||
.prose :where(p) {
|
||||
@apply text-muted-foreground block mb-2;
|
||||
/* Target the scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
/* Width of the scrollbar */
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
position: absolute !important;
|
||||
|
||||
}
|
||||
.prose :where(strong) {
|
||||
@apply text-foreground font-semibold mb-2;
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--border);
|
||||
border-radius: 46px !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+2
-53
@@ -1,6 +1,5 @@
|
||||
|
||||
import { EditorView } from '@uiw/react-codemirror';
|
||||
|
||||
|
||||
|
||||
export const colorOptions = [
|
||||
|
||||
@@ -25,54 +24,4 @@ export const randomColor = () => {
|
||||
|
||||
|
||||
|
||||
export const overrideDarkTheme = EditorView.theme({
|
||||
|
||||
'.cm-content': {
|
||||
backgroundColor: "#20252c" ,
|
||||
},
|
||||
".cm-gutter": {
|
||||
backgroundColor: "#20252c",
|
||||
},
|
||||
|
||||
".cm-gutterElement": {
|
||||
color: "#4b515a"
|
||||
},
|
||||
|
||||
".ͼp": {
|
||||
color: "#A994FF"
|
||||
},
|
||||
|
||||
".cm-line .ͼq": {
|
||||
color: "#ff6363"
|
||||
},
|
||||
".ͼu": {
|
||||
color: "#B6E672"
|
||||
},
|
||||
".ͼv": {
|
||||
color: "#6cdcc4"
|
||||
}
|
||||
|
||||
}, { dark: true });
|
||||
|
||||
|
||||
|
||||
export const overrideLightTheme = EditorView.theme({
|
||||
|
||||
".ͼb": {
|
||||
color: "#2A1D66"
|
||||
},
|
||||
".cm-gutterElement": {
|
||||
color: "#a2a4a8"
|
||||
},
|
||||
".cm-gutter": {
|
||||
backgroundColor: "white",
|
||||
|
||||
},
|
||||
".cm-gutters": {
|
||||
borderColor: "#f2f4f6"
|
||||
},
|
||||
".cm-line": {
|
||||
color: "#333639"
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
import App from "./App.tsx";
|
||||
import { UIProviders } from "./provider.tsx";
|
||||
import "./styles/globals.css";
|
||||
import "./index.css";
|
||||
|
||||
import "./i18/index.ts";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ export function UIProviders({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
|
||||
<FontProvider>
|
||||
<ThemeProvider defaultTheme ={"dark"}>
|
||||
<ThemeProvider defaultTheme ={"light"}>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Toaster />
|
||||
<SearchProvider>
|
||||
|
||||
@@ -88,10 +88,7 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
database = database[0] as any;
|
||||
else
|
||||
database = undefined as any;
|
||||
|
||||
useEffect(() => {
|
||||
console.log(database);
|
||||
}, [database])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -145,8 +145,7 @@ export class BaseSqlImporter {
|
||||
} catch (error) {
|
||||
errors.push(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tables.length == 0)
|
||||
throw Error("Can't parse sql")
|
||||
@@ -307,7 +306,6 @@ export class BaseSqlImporter {
|
||||
const dataType: DataType | undefined = this.processDataType(typeName as string);
|
||||
|
||||
if (!dataType) {
|
||||
console.log (typeName , this.data_types)
|
||||
throw Error("Data type not found");
|
||||
}
|
||||
// get data type modifiers
|
||||
|
||||
@@ -18,5 +18,9 @@ export default defineConfig({
|
||||
worker: {
|
||||
format: 'es',
|
||||
plugins: () => [ topLevelAwait()]
|
||||
} ,
|
||||
|
||||
build: {
|
||||
target: "esnext"
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user