mirror of
https://github.com/stackrender/stackrender.git
synced 2026-09-10 11:15:42 +00:00
split the database context into 2 data context and operatoins context to redeuce re-rendering
This commit is contained in:
+4
-2
@@ -9,6 +9,7 @@ import DatabaseProvider from "./providers/database-provider/database-provider";
|
||||
import DiagramProvider from "./providers/diagram-provider/diagram-provider";
|
||||
import { NextUIProvider } from "@nextui-org/react";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
import { HeroUIProvider, ToastProvider } from "@heroui/react";
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +17,8 @@ function App() {
|
||||
|
||||
const appRoutes = useAppRoutes();
|
||||
return (
|
||||
<NextUIProvider>
|
||||
<HeroUIProvider>
|
||||
<ToastProvider placement="bottom-right"/>
|
||||
<NextThemesProvider
|
||||
defaultTheme='system'
|
||||
attribute='class'
|
||||
@@ -35,7 +37,7 @@ function App() {
|
||||
</ReactFlowProvider>
|
||||
</SyncProvider>
|
||||
</NextThemesProvider>
|
||||
</NextUIProvider>
|
||||
</HeroUIProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { TableType } from "@/lib/schemas/table-schema";
|
||||
import { Node, useReactFlow } from "@xyflow/react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
const useGetOverlappingNodes = (): Set<string> => {
|
||||
const { getNodes } = useReactFlow();
|
||||
const nodes = getNodes();
|
||||
|
||||
const overlappingNodes: Set<string> = useMemo(() => {
|
||||
const overlaps: Set<string> = new Set();
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
for (let j = i + 1; j < nodes.length; j++) {
|
||||
|
||||
if (isNodesOverlapping(nodes[i], nodes[j])) {
|
||||
|
||||
overlaps.add(nodes[i].id);
|
||||
overlaps.add(nodes[j].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return overlaps
|
||||
}, [nodes]) ;
|
||||
return overlappingNodes;
|
||||
}
|
||||
|
||||
|
||||
function isNodesOverlapping(nodeA: Node, nodeB: Node) {
|
||||
|
||||
const nodeAWidth: number = nodeA.measured?.width || parseInt(nodeA.style?.width as string) || 224;
|
||||
const nodeAHeight: number = nodeA.measured?.height || (nodeA.data.table as TableType).fields.length * 32 + 36;
|
||||
|
||||
|
||||
const nodeBWidth: number = nodeB.measured?.width || parseInt(nodeB.style?.width as string) || 224;
|
||||
const nodeBHeight: number = nodeB.measured?.height || (nodeB.data.table as TableType).fields.length * 32 + 36;
|
||||
|
||||
|
||||
const a = {
|
||||
left: nodeA.position.x,
|
||||
right: nodeA.position.x + nodeAWidth,
|
||||
top: nodeA.position.y,
|
||||
bottom: nodeA.position.y + nodeAHeight,
|
||||
};
|
||||
|
||||
const b = {
|
||||
left: nodeB.position.x,
|
||||
right: nodeB.position.x + nodeBWidth,
|
||||
top: nodeB.position.y,
|
||||
bottom: nodeB.position.y + nodeBHeight,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
return !(a.right <= b.left || a.left >= b.right || a.bottom <= b.top || a.top >= b.bottom);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default useGetOverlappingNodes;
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Edge, useStore } from "@xyflow/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import hash from 'object-hash';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const useGetRelatedEdges = ( nodeId : string) => {
|
||||
const edges = useStore((store) => store.edges) as Edge[];
|
||||
const [relatedEdges, setRelatedEdges] = useState<Edge[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const newRelatedEdges: Edge[] = edges.filter((edge: Edge) => edge.source == nodeId || edge.target == nodeId);
|
||||
setRelatedEdges((previousEdges :Edge[]) => {
|
||||
return hash(newRelatedEdges) == hash(previousEdges) ? previousEdges : newRelatedEdges
|
||||
})
|
||||
}, [edges, nodeId])
|
||||
|
||||
return relatedEdges
|
||||
}
|
||||
|
||||
|
||||
export default useGetRelatedEdges;
|
||||
@@ -1,26 +1,40 @@
|
||||
|
||||
import { RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
import { LEFT_PREFIX, TARGET_PREFIX } from "@/pages/database/table/field";
|
||||
|
||||
import { Edge, useReactFlow } from "@xyflow/react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import hash from 'object-hash';
|
||||
export const useRelationshipToEdge = (relationships: RelationshipType[]): void => {
|
||||
const { setEdges } = useReactFlow();
|
||||
useEffect(() => {
|
||||
const edges = relationships.map((relationship: RelationshipType) => {
|
||||
|
||||
const relationshipEdges = relationships.map((relationship: RelationshipType) => {
|
||||
return {
|
||||
id: relationship.id,
|
||||
source: relationship.sourceTableId,
|
||||
sourceHandle: LEFT_PREFIX + relationship.sourceFieldId,
|
||||
target: relationship.targetTableId,
|
||||
targetHandle: TARGET_PREFIX + relationship.targetFieldId,
|
||||
data : {
|
||||
relationship
|
||||
selected: false ,
|
||||
animated : false ,
|
||||
data: {
|
||||
relationship
|
||||
}
|
||||
} as Edge
|
||||
})
|
||||
setEdges(edges)
|
||||
|
||||
setEdges((edges: any) => {
|
||||
return relationshipEdges.map((relationshipEdge: any) => {
|
||||
const edge = edges.find((edge: Edge) => edge.id == relationshipEdge.id);
|
||||
if (!edge)
|
||||
return relationshipEdge;
|
||||
|
||||
const relationshipEdgeHash: string = hash(relationshipEdge);
|
||||
const edgeHash: string = hash(edge);
|
||||
|
||||
return relationshipEdgeHash == edgeHash ? edge : relationshipEdge;
|
||||
})
|
||||
});
|
||||
}, [relationships])
|
||||
|
||||
}
|
||||
@@ -1,29 +1,47 @@
|
||||
|
||||
|
||||
import { TableType } from "@/lib/schemas/table-schema";
|
||||
import { Node, useReactFlow } from "@xyflow/react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import useGetOverlappingNodes from "./use-get-overlapping-nodes";
|
||||
import hash from 'object-hash';
|
||||
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",
|
||||
|
||||
position: {
|
||||
x: table.posX,
|
||||
y: table.posY
|
||||
},
|
||||
data: {
|
||||
table
|
||||
table,
|
||||
},
|
||||
style: {
|
||||
width: 224
|
||||
}
|
||||
} as Node
|
||||
})
|
||||
setNodes(nodes)
|
||||
|
||||
|
||||
setNodes((nodes) => {
|
||||
|
||||
return tableNodes.map((tableNode: Node) => {
|
||||
const node = nodes.find((node: Node) => node.id == tableNode.id);
|
||||
if (!node) {
|
||||
return tableNode;
|
||||
}
|
||||
|
||||
const hashNode: string = hash((node.data as any).table);
|
||||
const hashTableNode: string = hash((tableNode.data as any).table);
|
||||
return hashNode == hashTableNode ? node : tableNode;
|
||||
|
||||
})
|
||||
})
|
||||
}, [tables])
|
||||
|
||||
}
|
||||
@@ -52,7 +52,7 @@ const DatabaseControlButtons: React.FC<DbControlButtons> = ({ adjustPositions })
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Navbar className="flex rounded-md border-1 border-default-200 bg-transparent dark:border-default-800" isBlurred
|
||||
<Navbar className="flex rounded-md border-1 border-default-200 bg-transparent dark:border-default-800 " isBlurred
|
||||
classNames={{
|
||||
wrapper: "h-14 p-2 gap-1",
|
||||
}}
|
||||
|
||||
@@ -6,7 +6,7 @@ import '@xyflow/react/dist/style.css';
|
||||
import Relationship from "./table/relationship";
|
||||
import DBController from "./db-controller/db-controller";
|
||||
import { TableInsertType } from "@/lib/schemas/table-schema";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { useTableToNode } from "@/hooks/use-table-to-node";
|
||||
import { useRelationshipToEdge } from "@/hooks/use-relationship-to-edge";
|
||||
import { RelationshipInsertType, RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
@@ -16,42 +16,60 @@ import CardinalityMarker from "@/components/cardinality-marker/cardinality-marke
|
||||
import { areArraysEqual } from "@/utils/utils";
|
||||
import { useDiagram } from "@/providers/diagram-provider/diagram-provider";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/tooltip";
|
||||
import { Button } from "@heroui/react";
|
||||
import { LayoutGrid } from "lucide-react";
|
||||
import { addToast, Button, Image } from "@heroui/react";
|
||||
import { AlertTriangle, LayoutGrid } from "lucide-react";
|
||||
import { adjustTablesPositions } from "@/utils/tables";
|
||||
import { useTheme } from "next-themes";
|
||||
import DatabaseControlButtons from "./database-control-buttons";
|
||||
import useGetOverlappingNodes from "@/hooks/use-get-overlapping-nodes";
|
||||
import { FieldType } from "@/lib/schemas/field-schema";
|
||||
|
||||
|
||||
|
||||
const DatabasePage: React.FC<never> = () => {
|
||||
|
||||
const { database, updateTablePositions, deleteMultiTables, deleteMultiRelationships, createRelationship } = useDatabase();
|
||||
const { database } = useDatabase();
|
||||
const { updateTablePositions, deleteMultiTables, deleteMultiRelationships, createRelationship, getField } = useDatabaseOperations();
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||
|
||||
const { setIsConnectionInProgress } = useDiagram();
|
||||
const [selectedNodeIds, setSelectedNodeIds] = useState<string[]>([]);
|
||||
|
||||
const { tables, relationships } = database ;
|
||||
const [isTableOverlappingPulsing, setIsTableOverlappingPulsing] = useState<boolean>(false);
|
||||
const { tables, relationships } = database;
|
||||
const { fitView } = useReactFlow();
|
||||
|
||||
const nodeTypes = useMemo(() => ({ table: Table }), []);
|
||||
const edgeTypes = useMemo(() => ({ 'relationship-edge': Relationship }), []);
|
||||
|
||||
const onConnect = useCallback((connection: Connection) => {
|
||||
const sourceFieldId: string | undefined = (connection.sourceHandle as string).split("_").pop();
|
||||
const targetFieldId: string = (connection.targetHandle as string).replace(TARGET_PREFIX, "");
|
||||
|
||||
createRelationship({
|
||||
id: v4(),
|
||||
sourceTableId: connection.source,
|
||||
targetTableId: connection.target,
|
||||
sourceFieldId: (connection.sourceHandle as string).split("_").pop(),
|
||||
targetFieldId: (connection.targetHandle as string).replace(TARGET_PREFIX, "")
|
||||
} as RelationshipInsertType)
|
||||
const sourceField: FieldType | undefined = getField(connection.source, sourceFieldId as string);
|
||||
const targetField: FieldType | undefined = getField(connection.target, targetFieldId);
|
||||
console.log(sourceField, targetField)
|
||||
if (sourceField?.typeId == targetField?.typeId) {
|
||||
|
||||
setEdges((eds) => addEdge(connection, eds));
|
||||
createRelationship({
|
||||
id: v4(),
|
||||
sourceTableId: connection.source,
|
||||
targetTableId: connection.target,
|
||||
sourceFieldId,
|
||||
targetFieldId
|
||||
} as RelationshipInsertType);
|
||||
|
||||
setEdges((eds) => addEdge(connection, eds));
|
||||
} else {
|
||||
addToast({
|
||||
title: "Invalid Relationship",
|
||||
description: "Relationship should be between primary key and foreign key of the same time",
|
||||
color: "danger",
|
||||
})
|
||||
}
|
||||
setIsConnectionInProgress(false);
|
||||
|
||||
}, []);
|
||||
}, [database]);
|
||||
|
||||
const handleNodesChanges: OnNodesChange<never> = useCallback((changes: NodeChange<never>[]) => {
|
||||
|
||||
@@ -124,6 +142,7 @@ const DatabasePage: React.FC<never> = () => {
|
||||
useTableToNode(tables);
|
||||
useRelationshipToEdge(relationships);
|
||||
|
||||
|
||||
const adjustPositions = useCallback(async () => {
|
||||
updateTablePositions(await adjustTablesPositions(nodes, relationships));
|
||||
setTimeout(() => {
|
||||
@@ -133,46 +152,127 @@ const DatabasePage: React.FC<never> = () => {
|
||||
}, 500)
|
||||
}, [relationships, nodes]);
|
||||
|
||||
|
||||
const overlappingNodes = useGetOverlappingNodes();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const overlappingIds = Array.from(overlappingNodes);
|
||||
|
||||
const previousOverlappingNodes = nodes.filter((node: Node) => node.data.overlapping);
|
||||
|
||||
if (!areArraysEqual(previousOverlappingNodes, overlappingIds)) {
|
||||
setNodes((nodes: any) => {
|
||||
return nodes.map((node: any) => {
|
||||
const isOverlaped: boolean = overlappingIds.includes(node.id);
|
||||
|
||||
if (isOverlaped == node.data.overlapping)
|
||||
return node;
|
||||
else
|
||||
return {
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
overlapping: isOverlaped
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}, [overlappingNodes]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setNodes((nodes: any) => {
|
||||
return nodes.map((node: any) => {
|
||||
if (!node.data.overlapping)
|
||||
return node;
|
||||
else
|
||||
return {
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
pulsing: isTableOverlappingPulsing
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}, [isTableOverlappingPulsing])
|
||||
|
||||
const pulsOverlappingTables = useCallback(() => {
|
||||
setIsTableOverlappingPulsing(true);
|
||||
setTimeout(() => setIsTableOverlappingPulsing(false), 200);
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
||||
<div className="w-full h-screen flex relative">
|
||||
<DBController />
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
fitView
|
||||
className="w-full h-full cursor-default"
|
||||
onNodesChange={handleNodesChanges}
|
||||
onEdgesChange={handleEdgeChanges}
|
||||
onConnect={onConnect}
|
||||
defaultEdgeOptions={{
|
||||
type: 'relationship-edge',
|
||||
}}
|
||||
// onlyRenderVisibleElements
|
||||
panOnDrag={true}
|
||||
zoomOnScroll={true}
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
snapGrid={[20, 20]}
|
||||
onConnectStart={onConnectStart}
|
||||
onConnectEnd={onConnectEnd}
|
||||
>
|
||||
|
||||
<Controls
|
||||
position="bottom-center"
|
||||
showFitView={false}
|
||||
showZoom={false}
|
||||
showInteractive={false}
|
||||
className="shadow-none "
|
||||
<DBController />
|
||||
<div className="relative w-full h-full">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
fitView
|
||||
className="w-full h-full cursor-default"
|
||||
onNodesChange={handleNodesChanges}
|
||||
onEdgesChange={handleEdgeChanges}
|
||||
onConnect={onConnect}
|
||||
defaultEdgeOptions={{
|
||||
type: 'relationship-edge',
|
||||
}}
|
||||
//onlyRenderVisibleElements
|
||||
panOnDrag={true}
|
||||
zoomOnScroll={true}
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
snapGrid={[20, 20]}
|
||||
onConnectStart={onConnectStart}
|
||||
onConnectEnd={onConnectEnd}
|
||||
>
|
||||
|
||||
<DatabaseControlButtons
|
||||
adjustPositions={adjustPositions}
|
||||
/>
|
||||
<Controls
|
||||
position="bottom-center"
|
||||
showFitView={false}
|
||||
showZoom={false}
|
||||
showInteractive={false}
|
||||
className="shadow-none "
|
||||
>
|
||||
|
||||
</Controls >
|
||||
<Background />
|
||||
</ReactFlow>
|
||||
<DatabaseControlButtons
|
||||
adjustPositions={adjustPositions}
|
||||
/>
|
||||
</Controls >
|
||||
|
||||
<Background />
|
||||
</ReactFlow>
|
||||
<div
|
||||
className="absolute left-[12px] top-[64px] "
|
||||
>
|
||||
{
|
||||
overlappingNodes.size > 0 &&
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Button
|
||||
variant="shadow"
|
||||
size="sm"
|
||||
isIconOnly
|
||||
color="danger"
|
||||
className="size-8 p-1 "
|
||||
onPressEnd={pulsOverlappingTables}
|
||||
>
|
||||
<AlertTriangle className="size-4 text-white" />
|
||||
</Button>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
Overlapping Tables
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svg style={{ position: 'absolute', width: 0, height: 0 }}>
|
||||
<defs>
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/tooltip";
|
||||
import { Cardinality, RelationshipInsertType, RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { Button, Select, SelectItem, SharedSelection } from "@heroui/react";
|
||||
import { ChevronsLeftRightEllipsis, FileMinus2, FileOutput, SquareArrowLeft, SquareArrowRight, Trash2 } from "lucide-react";
|
||||
import { Key, useEffect, useState } from "react";
|
||||
@@ -19,7 +19,7 @@ interface RelationshipAccordionBodyProps {
|
||||
|
||||
const RelationshipAccordionBody: React.FC<RelationshipAccordionBodyProps> = ({ relationship }) => {
|
||||
const [cardinality, setCardinality] = useState(new Set([relationship.cardinality]));
|
||||
const { editRelationship, deleteRelationship } = useDatabase();
|
||||
const { editRelationship, deleteRelationship } = useDatabaseOperations();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
+8
-4
@@ -1,10 +1,10 @@
|
||||
import { useRelationshipName } from "@/hooks/use-relationship-name";
|
||||
import { RelationshipInsertType, RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { useDiagram } from "@/providers/diagram-provider/diagram-provider";
|
||||
import { Button, cn, Input, Listbox, ListboxItem, Popover, PopoverContent, PopoverTrigger } from "@heroui/react";
|
||||
import { Check, ChevronRight, EllipsisVertical, Focus, Pencil, Trash } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
|
||||
|
||||
const [editMode, setEditMode] = useState<boolean>(false);
|
||||
const { name: defaultName } = useRelationshipName(relationship);
|
||||
const { editRelationship, deleteRelationship } = useDatabase();
|
||||
const { editRelationship, deleteRelationship } = useDatabaseOperations();
|
||||
const { t } = useTranslation();
|
||||
const [popOverOpen, setPopOverOpen] = useState<boolean>(false);
|
||||
const [name, setName] = useState<string>(relationship.name ? relationship.name : defaultName);
|
||||
@@ -40,6 +40,11 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
|
||||
deleteRelationship(relationship.id);
|
||||
setPopOverOpen(false);
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setName(relationship.name ? relationship.name : defaultName) ;
|
||||
} , [relationship.name])
|
||||
return (
|
||||
<div className="group w-full flex h-12 gap-1 flex p-2 items-center" >
|
||||
<div className={cn(
|
||||
@@ -137,7 +142,6 @@ const RelationshipAccordionHeader: React.FC<RelationshipAccordionHeaderProps> =
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -8,7 +8,7 @@ import RelationshipAccordionBody from "./relationship-accordion-item/relationshi
|
||||
import Modal from "@/components/modal/modal";
|
||||
import CreateRelationshipForm from "./create-relationship-form/create-relationship-form";
|
||||
import { RelationshipInsertType, RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { v4 } from "uuid";
|
||||
import { useDiagram } from "@/providers/diagram-provider/diagram-provider";
|
||||
|
||||
@@ -24,8 +24,9 @@ const RelationshipController: React.FC<Props> = ({ }) => {
|
||||
const [relationship, setRelationship] = useState<RelationshipInsertType | undefined>(undefined);
|
||||
const [isValid, setIsValid] = useState<boolean>(false);
|
||||
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
||||
const { createRelationship, database } = useDatabase();
|
||||
const { relationships } = database ;
|
||||
const { database } = useDatabase();
|
||||
const { createRelationship, } = useDatabaseOperations();
|
||||
const { relationships } = database;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [selectedRelationship, setSelectedRelationship] = useState(new Set([]));
|
||||
@@ -37,7 +38,7 @@ const RelationshipController: React.FC<Props> = ({ }) => {
|
||||
|
||||
createRelationship({
|
||||
id: newRelationshipId,
|
||||
...relationship,
|
||||
...relationship,
|
||||
} as RelationshipInsertType);
|
||||
|
||||
setSelectedRelationship(new Set([newRelationshipId]) as any);
|
||||
|
||||
+4
-3
@@ -7,7 +7,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { FieldInsertType, FieldType } from "@/lib/schemas/field-schema";
|
||||
import { Key, useEffect, useState } from "react";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import Autocomplete from "@/components/auto-complete/auto-complete";
|
||||
import ToggleButton from "@/components/toggle/toggle";
|
||||
interface Props {
|
||||
@@ -21,8 +21,9 @@ const FieldItem: React.FC<Props> = ({ field }) => {
|
||||
const [fieldName, setFieldName] = useState<string>(field.name);
|
||||
|
||||
const [popOverOpen, setPopOverOpen] = useState<boolean>(false);
|
||||
const { deleteField, editField, data_types } = useDatabase();
|
||||
|
||||
const { data_types } = useDatabase();
|
||||
const {deleteField, editField} = useDatabaseOperations() ;
|
||||
|
||||
const [note, setNote] = useState<string | undefined>(field.note as string | undefined);
|
||||
const [selectedType, setSelectedType] = useState<string | undefined>(field.typeId as string | undefined);
|
||||
const { t } = useTranslation();
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import { arrayMove, SortableContext, verticalListSortingStrategy } from "@dnd-ki
|
||||
import { FieldType } from "@/lib/schemas/field-schema";
|
||||
import { TableType } from "@/lib/schemas/table-schema";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { v4 } from "uuid";
|
||||
import { getNextSequence } from "@/utils/field";
|
||||
|
||||
@@ -22,7 +22,7 @@ const FieldList: React.FC<Props> = ({ table }) => {
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [fields, setFields] = useState<FieldType[]>(table.fields);
|
||||
const { createField, orderTableFields } = useDatabase();
|
||||
const { createField, orderTableFields } = useDatabaseOperations();
|
||||
|
||||
useEffect(() => {
|
||||
setFields(table.fields)
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import ColorPicker from "@/components/color-picker/color-picker";
|
||||
import FieldList from "./field/field-list";
|
||||
import IndexesList from "./index/indexes-list";
|
||||
import { TableType } from "@/lib/schemas/table-schema";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { getNextSequence } from "@/utils/field";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
@@ -22,7 +22,7 @@ const TableAccordionBody: React.FC<TableAccordionBodyProps> = ({ table }) => {
|
||||
const [selectedKeys, setSelectedKeys] = useState(new Set(["fields"]));
|
||||
const [note, setNote] = useState<string>(table.note ? table.note : "");
|
||||
const { t } = useTranslation();
|
||||
const { editTable, createField } = useDatabase();
|
||||
const { editTable, createField } = useDatabaseOperations();
|
||||
|
||||
const onColorChange = useCallback((color: string | undefined) => {
|
||||
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TableInsertType, TableType } from "@/lib/schemas/table-schema";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { v4 } from "uuid";
|
||||
import { getNextSequence } from "@/utils/field";
|
||||
import { useDiagram } from "@/providers/diagram-provider/diagram-provider";
|
||||
@@ -18,7 +18,7 @@ export interface TableAccordionHeaderProps {
|
||||
|
||||
|
||||
const TableAccordionHeader: React.FC<TableAccordionHeaderProps> = ({ table, isOpen }) => {
|
||||
const { editTable, deleteTable, createField } = useDatabase();
|
||||
const { editTable, deleteTable, createField } = useDatabaseOperations();
|
||||
const [popOverOpen, setPopOverOpen] = useState<boolean>(false);
|
||||
const [tableName, setTableName] = useState<string>(table.name);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import TableAccordionHeader from "./table-accordion-item/table-accordion-header";
|
||||
import TableAccordionBody from "./table-accordion-item/table-accordion-body";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { TableInsertType, TableType } from "@/lib/schemas/table-schema";
|
||||
import { v4 } from "uuid";
|
||||
import { useDiagram } from "@/providers/diagram-provider/diagram-provider";
|
||||
@@ -17,8 +17,8 @@ interface Props { }
|
||||
const TablesController: React.FC<Props> = ({ }) => {
|
||||
|
||||
|
||||
const { database, createTable } = useDatabase();
|
||||
|
||||
const { database } = useDatabase();
|
||||
const {createTable} = useDatabaseOperations() ;
|
||||
const { tables } = database ;
|
||||
const { t } = useTranslation();
|
||||
const [selectedTable, setSelectedTable] = useState(new Set([]));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/tooltip";
|
||||
import { FieldType } from "@/lib/schemas/field-schema";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { useDiagram } from "@/providers/diagram-provider/diagram-provider";
|
||||
import { Button, cn, select } from "@heroui/react";
|
||||
import { Handle, Position, useConnection } from "@xyflow/react";
|
||||
@@ -12,8 +12,8 @@ import React, { useEffect, useState } from "react";
|
||||
interface Props {
|
||||
field: FieldType,
|
||||
showHandles?: boolean,
|
||||
highlight?: boolean ,
|
||||
showTargetHandle? : boolean
|
||||
highlight?: boolean,
|
||||
showTargetHandle?: boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -25,21 +25,18 @@ export const TARGET_PREFIX = "target_";
|
||||
const Field: React.FC<Props> = ({ field, showHandles, highlight }) => {
|
||||
|
||||
const [editMode, setEditMode] = useState<boolean>(false);
|
||||
const { deleteField, editField } = useDatabase();
|
||||
const { deleteField, editField } = useDatabaseOperations();
|
||||
const [fieldName, setFieldName] = useState<string>(field.name);
|
||||
const {isConnectionInProgress} = useDiagram() ;
|
||||
const { isConnectionInProgress } = useDiagram();
|
||||
|
||||
useEffect(() => {
|
||||
setFieldName(field.name);
|
||||
}, [field.name]);
|
||||
|
||||
|
||||
|
||||
const removeField = () => {
|
||||
deleteField(field.id)
|
||||
}
|
||||
|
||||
|
||||
const saveFieldName = () => {
|
||||
editField({
|
||||
id: field.id,
|
||||
@@ -48,11 +45,11 @@ const Field: React.FC<Props> = ({ field, showHandles, highlight }) => {
|
||||
setEditMode(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// console.log("render field ", field.name)
|
||||
|
||||
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-500 transition-all duration-200 ease-in-out" ,
|
||||
"group relative flex h-8 items-center justify-between gap-1 border-t border-default dark:border-default/5 px-3 text-sm last:rounded-b-[6px] hover:bg-slate-100 dark:hover:bg-primary-500 transition-all duration-200 ease-in-out",
|
||||
highlight ? "bg-primary/5" : ""
|
||||
)}>
|
||||
{
|
||||
@@ -125,12 +122,12 @@ const Field: React.FC<Props> = ({ field, showHandles, highlight }) => {
|
||||
type="source"
|
||||
position={Position.Left}
|
||||
id={LEFT_PREFIX + field.id}
|
||||
className="w-4 h-4 border-4 bg-primary dark:border-default-900"
|
||||
className="w-4 h-4 border-3 bg-primary dark:border-default-900"
|
||||
/>
|
||||
<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-3 bg-primary dark:border-default-900"
|
||||
id={RIGHT_PREFIX + field.id}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -113,6 +113,7 @@ const Relationship: React.FC<EdgeProps<RelationshipProps>> = (props) => {
|
||||
}
|
||||
}, [data?.relationship.cardinality, selected]);
|
||||
|
||||
// console.log ("render relationship " , props.data?.relationship.sourceTable.name , props.data?.relationship.targetTable.name)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -14,26 +14,28 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/tooltip/to
|
||||
import FieldComponent from "./field";
|
||||
import { FieldType } from "@/lib/schemas/field-schema";
|
||||
import { TableInsertType, TableType } from "@/lib/schemas/table-schema";
|
||||
import { useDatabase } from "@/providers/database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "@/providers/database-provider/database-provider";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RelationshipType } from "@/lib/schemas/relationship-schema";
|
||||
import { useDiagram } from "@/providers/diagram-provider/diagram-provider";
|
||||
|
||||
import useGetRelatedEdges from "@/hooks/use-get-related-edges";
|
||||
|
||||
export type TableProps = Node<{
|
||||
table: TableType,
|
||||
overlapping?: boolean,
|
||||
pulsing?: boolean,
|
||||
}>
|
||||
|
||||
const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table } }) => {
|
||||
|
||||
const Table: React.FC<NodeProps<TableProps>> = (props) => {
|
||||
const { selected, data: { table, overlapping = false, pulsing = false } } = props;
|
||||
const [editMode, setEditMode] = useState<boolean>(false);
|
||||
const [tableName, setTableName] = useState<string>(table.name);
|
||||
const { editTable } = useDatabase();
|
||||
|
||||
const { editTable } = useDatabaseOperations();
|
||||
const edges = useGetRelatedEdges(table.id as string);
|
||||
const { focusOnTable } = useDiagram();
|
||||
const { t } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
useEffect(() => {
|
||||
setTableName(table.name);
|
||||
}, [table.name])
|
||||
|
||||
@@ -46,8 +48,6 @@ const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table } }) =
|
||||
focusOnTable(table.id, false);
|
||||
}, [table])
|
||||
|
||||
const edges = useStore((store) => store.edges) as Edge[];
|
||||
|
||||
const highlightedEdges: Edge[] = useMemo(() => {
|
||||
return edges.filter((edge: Edge) => edge.animated || edge.selected);
|
||||
}, [edges]);
|
||||
@@ -68,86 +68,102 @@ const Table: React.FC<NodeProps<TableProps>> = ({ selected, data: { table } }) =
|
||||
}, [table.fields, selected, highlightedEdges]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
console.log("re-render ", table.name)
|
||||
|
||||
}, [useDatabase]) ;
|
||||
|
||||
return (
|
||||
|
||||
<Card className={cn(
|
||||
"w-full h-full bg-background rounded-lg entity-card noselect overflow-visible dark:bg-default-900",
|
||||
selected
|
||||
? 'ring-2 ring-primary'
|
||||
: '',
|
||||
)}
|
||||
shadow="sm"
|
||||
>
|
||||
<div className="px-[2px] ">
|
||||
<div
|
||||
className=" border-t-[4px] rounded-t-[6px] border-primary"
|
||||
style={{ borderColor: table.color as string }}
|
||||
></div>
|
||||
</div>
|
||||
<div className="group gap-2 flex h-9 items-center justify-between bg-default/50 px-2 dark:bg-black/20">
|
||||
<Table2 className="size-4 shrink-0 text-icon dark:text-white" />
|
||||
{
|
||||
editMode && <>
|
||||
<input
|
||||
placeholder={table.name}
|
||||
autoFocus
|
||||
onChange={(event: any) => setTableName(event.target.value)}
|
||||
value={tableName}
|
||||
onBlur={saveTableName}
|
||||
type="text"
|
||||
className="rounded-md outline-none px-2 py-0.5 w-full border-[0.5px] border-primary-700 font-bold bg-slate-100 focus-visible:ring-0 text-sm dark:bg-transparent dark:text-white"
|
||||
/>
|
||||
|
||||
<Card className={cn(
|
||||
"w-full h-full bg-background rounded-lg noselect overflow-visible dark:bg-default-900",
|
||||
selected
|
||||
? 'ring-2 ring-primary'
|
||||
: '',
|
||||
|
||||
overlapping
|
||||
? 'ring-2 dark:ring-offset-default-900 ring-danger ring-offset-1 scale-105 shadow-danger '
|
||||
: '',
|
||||
|
||||
!pulsing
|
||||
? 'scale-100'
|
||||
: '',
|
||||
pulsing
|
||||
? 'scale-105'
|
||||
: '',
|
||||
)}
|
||||
shadow="sm"
|
||||
|
||||
>
|
||||
<div className="px-[2px] ">
|
||||
<div
|
||||
className=" border-t-[4px] rounded-t-[6px] border-primary"
|
||||
style={{ borderColor: table.color as string }}
|
||||
></div>
|
||||
</div>
|
||||
<div className="group gap-2 flex h-9 items-center justify-between bg-default/50 px-2 dark:bg-black/20">
|
||||
<Table2 className="size-4 shrink-0 text-icon dark:text-white" />
|
||||
{
|
||||
editMode && <>
|
||||
<input
|
||||
placeholder={table.name}
|
||||
autoFocus
|
||||
onChange={(event: any) => setTableName(event.target.value)}
|
||||
value={tableName}
|
||||
onBlur={saveTableName}
|
||||
type="text"
|
||||
className="rounded-md outline-none px-2 py-0.5 w-full border-[0.5px] border-primary-700 font-bold bg-slate-100 focus-visible:ring-0 text-sm dark:bg-transparent dark:text-white"
|
||||
/>
|
||||
<Button
|
||||
variant="light"
|
||||
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-white"
|
||||
size="sm"
|
||||
onPressEnd={saveTableName}
|
||||
isIconOnly
|
||||
>
|
||||
<Check className="size-3" />
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
{
|
||||
!editMode &&
|
||||
<>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<label
|
||||
className=" w-full text-editable truncate px-2 py-0.5 text-sm font-bold dark:text-white dark:group-hover:bg-default-900"
|
||||
onDoubleClick={() => setEditMode(true)}
|
||||
>
|
||||
{tableName}
|
||||
</label>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="dark:bg-default-900">
|
||||
{t("table.double_click")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<div className="hidden shrink-0 flex-row group-hover:flex">
|
||||
|
||||
<Button
|
||||
variant="light"
|
||||
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:text-white"
|
||||
size="sm"
|
||||
onPressEnd={saveTableName}
|
||||
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:hover:bg-default-800 "
|
||||
isIconOnly
|
||||
onPressEnd={focus}
|
||||
>
|
||||
<Check className="size-3" />
|
||||
<Focus className="size-4 text-icon dark:text-white" />
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
{
|
||||
!editMode &&
|
||||
<>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<label
|
||||
className=" w-full text-editable truncate px-2 py-0.5 text-sm font-bold dark:text-white dark:group-hover:bg-default-900"
|
||||
onDoubleClick={() => setEditMode(true)}
|
||||
>
|
||||
{tableName}
|
||||
</label>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="dark:bg-default-900">
|
||||
{t("table.double_click")}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<div className="transition-[max-height] duration-200 ease-in-out">
|
||||
{fields}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="hidden shrink-0 flex-row group-hover:flex">
|
||||
|
||||
<Button
|
||||
variant="light"
|
||||
size="sm"
|
||||
className="size-6 p-0 text-slate-500 hover:bg-primary-foreground hover:text-slate-700 dark:hover:bg-default-800 "
|
||||
isIconOnly
|
||||
onPressEnd={focus}
|
||||
>
|
||||
<Focus className="size-4 text-icon dark:text-white" />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
<div className="transition-[max-height] duration-200 ease-in-out">
|
||||
{fields}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
|
||||
)
|
||||
};
|
||||
|
||||
export default React.memo(Table)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useContext, useEffect, useRef } from "react";
|
||||
import DatabaseHistoryContext from "./database-history-context";
|
||||
import useUndo from 'use-undo';
|
||||
import { useDatabase } from "../database-provider/database-provider";
|
||||
import { useDatabase, useDatabaseOperations } from "../database-provider/database-provider";
|
||||
import hash from 'object-hash';
|
||||
import { DBDiffOperation, mapDiffToDBDiffOperation, normalizeDatabase } from "@/utils/database";
|
||||
import { compare } from 'fast-json-patch';
|
||||
@@ -12,7 +12,9 @@ interface Props { children: React.ReactNode };
|
||||
const DatabaseHistoryProvider: React.FC<Props> = ({ children }) => {
|
||||
|
||||
const udpateDbFlag = useRef(false);
|
||||
const { database, executeDbDiffOps } = useDatabase();
|
||||
const { database } = useDatabase();
|
||||
const { executeDbDiffOps} = useDatabaseOperations() ;
|
||||
|
||||
const [datatbaseState, { set, undo: undoChanges, redo: redoChanges, canUndo, canRedo }] = useUndo<DatabaseType>(database);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -21,8 +23,6 @@ const DatabaseHistoryProvider: React.FC<Props> = ({ children }) => {
|
||||
const databaseHash: string = hash(database, { algorithm: 'sha1' });
|
||||
if (presentHash != databaseHash)
|
||||
set(database);
|
||||
|
||||
|
||||
}, [database]);
|
||||
|
||||
|
||||
@@ -47,15 +47,11 @@ const DatabaseHistoryProvider: React.FC<Props> = ({ children }) => {
|
||||
const differences = compare(normalizedDatabase, normalizedPresent);
|
||||
|
||||
if (differences && differences.length > 0) {
|
||||
|
||||
const operations: DBDiffOperation[] = mapDiffToDBDiffOperation(differences);
|
||||
executeDbDiffOps(operations)
|
||||
}
|
||||
|
||||
}, [datatbaseState.present]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<DatabaseHistoryContext.Provider
|
||||
value={{
|
||||
|
||||
@@ -11,34 +11,40 @@ import { createContext } from "react";
|
||||
|
||||
|
||||
|
||||
export interface DatabaseContextType {
|
||||
|
||||
interface DatabaseDataContextType {
|
||||
|
||||
data_types: DataType[],
|
||||
database : DatabaseType ,
|
||||
isLoading : boolean ,
|
||||
database: DatabaseType,
|
||||
isLoading: boolean,
|
||||
// table operations
|
||||
|
||||
}
|
||||
|
||||
|
||||
interface DatabaseOperationsContextType {
|
||||
|
||||
createTable: (table: TableInsertType) => Promise<void>,
|
||||
editTable: (table: TableInsertType) => Promise<QueryResult>,
|
||||
deleteTable: (id: string) => Promise<void>,
|
||||
updateTablePositions: (tables: TableInsertType[]) => Promise<QueryResult> ,
|
||||
updateTablePositions: (tables: TableInsertType[]) => Promise<QueryResult>,
|
||||
deleteMultiTables: (ids: string[]) => Promise<QueryResult>
|
||||
// field operations
|
||||
createField: (field: FieldInsertType) => Promise<QueryResult>,
|
||||
editField: (field: FieldInsertType) => Promise<QueryResult>,
|
||||
deleteField: (id: string) => Promise<void>,
|
||||
orderTableFields: (fields: FieldType[]) => Promise<QueryResult>,
|
||||
getField: (tableId: string, id: string) => FieldType | undefined,
|
||||
// relationship operations
|
||||
createRelationship: (relationship: RelationshipInsertType) => Promise<QueryResult>,
|
||||
editRelationship: (relationship: RelationshipInsertType) => Promise<QueryResult>,
|
||||
deleteRelationship: (id: string) => Promise<QueryResult>,
|
||||
deleteMultiRelationships: (ids: string[]) => Promise<QueryResult> ,
|
||||
|
||||
executeDbDiffOps : ( operations : DBDiffOperation[]) => void ,
|
||||
|
||||
deleteMultiRelationships: (ids: string[]) => Promise<QueryResult>,
|
||||
// execute the diff operation whenver user click in undo or redo
|
||||
executeDbDiffOps: (operations: DBDiffOperation[]) => void,
|
||||
}
|
||||
|
||||
export const DatabaseDataContext = createContext<DatabaseDataContextType>({} as DatabaseDataContextType);
|
||||
export const DatabaseOperationsContext = createContext<DatabaseOperationsContextType>({} as DatabaseOperationsContextType);
|
||||
|
||||
|
||||
|
||||
export default createContext<DatabaseContextType>({} as DatabaseContextType);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
import DatabaseContext from "./database-context";
|
||||
import { DatabaseDataContext, DatabaseOperationsContext } from "./database-context";
|
||||
import { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { db, powerSyncDb } from "../sync-provider/sync-provider";
|
||||
import { TableInsertType, tables } from "@/lib/schemas/table-schema";
|
||||
import { TableInsertType, tables, TableType } from "@/lib/schemas/table-schema";
|
||||
import { useQuery } from "@powersync/react";
|
||||
import { toCompilableQuery } from "@powersync/drizzle-driver";
|
||||
import { asc, desc, eq, inArray, or } from "drizzle-orm";
|
||||
@@ -113,6 +113,13 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
})
|
||||
}, [db]);
|
||||
|
||||
|
||||
const getField = useCallback((tableId: string, id: string) => {
|
||||
const table: TableType | undefined = (database as any).tables.find((table: TableType) => table.id == tableId);
|
||||
if (table)
|
||||
return table.fields.find((field: FieldType) => field.id == id);
|
||||
}, [database])
|
||||
|
||||
const orderTableFields = useCallback(async (fieldsList: FieldType[]): Promise<QueryResult> => {
|
||||
const caseStatements = fieldsList
|
||||
.map((field, index) => `WHEN '${field.id}' THEN ${index}`)
|
||||
@@ -188,7 +195,7 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
await tx.insert(fields).values(Object.values(operation.table.fields));
|
||||
}
|
||||
else if (operation.type === "UPDATE_TABLE") {
|
||||
|
||||
|
||||
await tx.update(tables).set(operation.changes).where(eq(tables.id, operation.tableId));
|
||||
|
||||
} else if (operation.type === "DELETE_TABLE") {
|
||||
@@ -216,43 +223,52 @@ const DatabaseProvider: React.FC<Props> = ({ children }) => {
|
||||
}
|
||||
})
|
||||
|
||||
}, [db, currentDatabaseId])
|
||||
}, [db, currentDatabaseId]);
|
||||
|
||||
return (
|
||||
|
||||
<DatabaseContext.Provider value={{
|
||||
createTable,
|
||||
editTable,
|
||||
deleteTable,
|
||||
updateTablePositions,
|
||||
deleteMultiTables,
|
||||
|
||||
createField,
|
||||
editField,
|
||||
deleteField,
|
||||
orderTableFields,
|
||||
|
||||
createRelationship,
|
||||
editRelationship,
|
||||
deleteRelationship,
|
||||
deleteMultiRelationships,
|
||||
<DatabaseDataContext.Provider value={{
|
||||
|
||||
|
||||
data_types,
|
||||
database: database as unknown as DatabaseType,
|
||||
isLoading,
|
||||
executeDbDiffOps
|
||||
}}>
|
||||
{
|
||||
!isLoading && database &&
|
||||
<DatabaseHistoryProvider>
|
||||
{children}
|
||||
</DatabaseHistoryProvider>
|
||||
}
|
||||
</DatabaseContext.Provider>
|
||||
<DatabaseOperationsContext.Provider value={{
|
||||
createTable,
|
||||
editTable,
|
||||
deleteTable,
|
||||
updateTablePositions,
|
||||
deleteMultiTables,
|
||||
|
||||
createField,
|
||||
editField,
|
||||
deleteField,
|
||||
orderTableFields,
|
||||
getField,
|
||||
|
||||
createRelationship,
|
||||
editRelationship,
|
||||
deleteRelationship,
|
||||
deleteMultiRelationships,
|
||||
|
||||
executeDbDiffOps,
|
||||
}}>
|
||||
|
||||
|
||||
{
|
||||
!isLoading && database &&
|
||||
<DatabaseHistoryProvider>
|
||||
{children}
|
||||
</DatabaseHistoryProvider>
|
||||
}
|
||||
</DatabaseOperationsContext.Provider>
|
||||
</DatabaseDataContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useDatabase = () => useContext(DatabaseContext);
|
||||
export const useDatabase = () => useContext(DatabaseDataContext);
|
||||
export const useDatabaseOperations = () => useContext(DatabaseOperationsContext);
|
||||
|
||||
export default DatabaseProvider;
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ export default {
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
"./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}",
|
||||
"./node_modules/@heroui/theme/dist/components/(button|code|dropdown|input|kbd|link|navbar|snippet|toggle|popover|ripple|spinner|menu|divider|form|modal).js",
|
||||
"./node_modules/@heroui/theme/dist/components/(button|code|dropdown|input|kbd|link|navbar|snippet|toggle|popover|ripple|spinner|menu|divider|form|modal|toast).js",
|
||||
|
||||
],
|
||||
darkMode: "class",
|
||||
|
||||
Reference in New Issue
Block a user