From 46597a96441b04b48a98823b7b4d8ae9ff6bd379 Mon Sep 17 00:00:00 2001 From: Guy Ben-Aharon Date: Mon, 12 Aug 2024 15:17:28 +0300 Subject: [PATCH] field halfway --- package-lock.json | 61 ++++++++++ package.json | 2 + src/App.tsx | 7 +- src/components/combobox/combobox.tsx | 2 +- src/components/toggle/toggle-variants.tsx | 25 ++++ src/components/toggle/toggle.tsx | 22 ++++ src/components/tooltip/tooltip.tsx | 28 +++++ .../chartdb-context/chartdb-context.tsx | 28 ++++- .../chartdb-context/chartdb-provider.tsx | 29 ++++- src/lib/data/data-types.ts | 14 ++- src/lib/domain/database-type.ts | 5 + .../table-list-item-content.tsx | 107 +++++++++++------- 12 files changed, 286 insertions(+), 44 deletions(-) create mode 100644 src/components/toggle/toggle-variants.tsx create mode 100644 src/components/toggle/toggle.tsx create mode 100644 src/components/tooltip/tooltip.tsx create mode 100644 src/lib/domain/database-type.ts diff --git a/package-lock.json b/package-lock.json index a30af27d..e60bf97f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,8 @@ "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-toggle": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.2", "@xyflow/react": "^12.0.4", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", @@ -1855,6 +1857,65 @@ } } }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.0.tgz", + "integrity": "sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.2.tgz", + "integrity": "sha512-9XRsLwe6Yb9B/tlnYCPVUd/TFS4J7HuOZW345DCeC6vKIxQGMZdx21RK4VoZauPD5frgkXTYVS5y90L+3YBn4w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", diff --git a/package.json b/package.json index 4889f45c..3136f0af 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-toggle": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.2", "@xyflow/react": "^12.0.4", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", diff --git a/src/App.tsx b/src/App.tsx index 2dd9e66d..2764ce0a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,12 @@ import React from 'react'; import { RouterProvider } from 'react-router-dom'; import { router } from './router'; +import { TooltipProvider } from './components/tooltip/tooltip'; export const App = () => { - return ; + return ( + + + + ); }; diff --git a/src/components/combobox/combobox.tsx b/src/components/combobox/combobox.tsx index bf7b1781..157fe637 100644 --- a/src/components/combobox/combobox.tsx +++ b/src/components/combobox/combobox.tsx @@ -90,7 +90,7 @@ export function Combobox({ - + { if (value.includes(search)) return 1; diff --git a/src/components/toggle/toggle-variants.tsx b/src/components/toggle/toggle-variants.tsx new file mode 100644 index 00000000..b4d43bc4 --- /dev/null +++ b/src/components/toggle/toggle-variants.tsx @@ -0,0 +1,25 @@ +import { cva } from 'class-variance-authority'; + +const toggleVariants = cva( + 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground', + { + variants: { + variant: { + default: 'bg-transparent', + outline: + 'border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground', + }, + size: { + default: 'h-9 px-3', + sm: 'h-8 px-2', + lg: 'h-10 px-3', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + } +); + +export { toggleVariants }; diff --git a/src/components/toggle/toggle.tsx b/src/components/toggle/toggle.tsx new file mode 100644 index 00000000..8503723e --- /dev/null +++ b/src/components/toggle/toggle.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import * as TogglePrimitive from '@radix-ui/react-toggle'; +import { type VariantProps } from 'class-variance-authority'; + +import { cn } from '@/lib/utils'; +import { toggleVariants } from './toggle-variants'; + +const Toggle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, size, ...props }, ref) => ( + +)); + +Toggle.displayName = TogglePrimitive.Root.displayName; + +export { Toggle }; diff --git a/src/components/tooltip/tooltip.tsx b/src/components/tooltip/tooltip.tsx new file mode 100644 index 00000000..c57df4e4 --- /dev/null +++ b/src/components/tooltip/tooltip.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import * as TooltipPrimitive from '@radix-ui/react-tooltip'; + +import { cn } from '@/lib/utils'; + +const TooltipProvider = TooltipPrimitive.Provider; + +const Tooltip = TooltipPrimitive.Root; + +const TooltipTrigger = TooltipPrimitive.Trigger; + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/src/context/chartdb-context/chartdb-context.tsx b/src/context/chartdb-context/chartdb-context.tsx index 8715ae36..c6c0be85 100644 --- a/src/context/chartdb-context/chartdb-context.tsx +++ b/src/context/chartdb-context/chartdb-context.tsx @@ -1,19 +1,43 @@ import { createContext } from 'react'; import { DBTable } from '@/lib/domain/db-table'; import { emptyFn } from '@/lib/utils'; +import { DatabaseType } from '@/lib/domain/database-type'; +import { DBField } from '@/lib/domain/db-field'; export interface ChartDBContext { + databaseType: DatabaseType; + tables: DBTable[]; + + // Database type operations + setDatabaseType: (databaseType: DatabaseType) => void; + + // Table operations createTable: () => void; addTable: (table: DBTable) => void; removeTable: (id: string) => void; updateTable: (id: string, table: Partial) => void; - tables: DBTable[]; + + // Field operations + updateField: ( + tableId: string, + fieldId: string, + field: Partial + ) => void; } export const chartDBContext = createContext({ + databaseType: DatabaseType.GENERIC, + tables: [], + + // Database type operations + setDatabaseType: emptyFn, + + // Table operations createTable: emptyFn, addTable: emptyFn, removeTable: emptyFn, updateTable: emptyFn, - tables: [], + + // Field operations + updateField: emptyFn, }); diff --git a/src/context/chartdb-context/chartdb-provider.tsx b/src/context/chartdb-context/chartdb-provider.tsx index 25dae812..0c847489 100644 --- a/src/context/chartdb-context/chartdb-provider.tsx +++ b/src/context/chartdb-context/chartdb-provider.tsx @@ -3,10 +3,15 @@ import { DBTable } from '@/lib/domain/db-table'; import { randomHSLA } from '@/lib/utils'; import { nanoid } from 'nanoid'; import { chartDBContext } from './chartdb-context'; +import { DatabaseType } from '@/lib/domain/database-type'; +import { DBField } from '@/lib/domain/db-field'; export const ChartDBProvider: React.FC = ({ children, }) => { + const [databaseType, setDatabaseType] = React.useState( + DatabaseType.GENERIC + ); const [tables, setTables] = React.useState([]); const addTable = (table: DBTable) => { @@ -46,14 +51,36 @@ export const ChartDBProvider: React.FC = ({ ); }; + const updateField = ( + tableId: string, + fieldId: string, + field: Partial + ) => { + setTables((tables) => + tables.map((table) => + table.id === tableId + ? { + ...table, + fields: table.fields.map((f) => + f.id === fieldId ? { ...f, ...field } : f + ), + } + : table + ) + ); + }; + return ( {children} diff --git a/src/lib/data/data-types.ts b/src/lib/data/data-types.ts index 2eb1c2fb..d6eeb7aa 100644 --- a/src/lib/data/data-types.ts +++ b/src/lib/data/data-types.ts @@ -1,6 +1,18 @@ +import { DatabaseType } from '../domain/database-type'; + +export const genericDataTypes = ['bigint', 'bfile', 'clob', 'cursor'] as const; export const postgresDataTypes = ['bigint', 'bigserial'] as const; export const mysqlDataTypes = ['bigint', 'bigserial'] as const; export const dataTypes = [ - ...new Set([...postgresDataTypes, ...mysqlDataTypes]), + ...new Set([...postgresDataTypes, ...mysqlDataTypes, ...genericDataTypes]), ] as const; + +export const dataTypeMap: Record< + DatabaseType, + readonly (typeof dataTypes)[number][] +> = { + [DatabaseType.GENERIC]: genericDataTypes, + [DatabaseType.POSTGRES]: postgresDataTypes, + [DatabaseType.MYSQL]: mysqlDataTypes, +} as const; diff --git a/src/lib/domain/database-type.ts b/src/lib/domain/database-type.ts new file mode 100644 index 00000000..e3deaae0 --- /dev/null +++ b/src/lib/domain/database-type.ts @@ -0,0 +1,5 @@ +export enum DatabaseType { + GENERIC = 'generic', + POSTGRES = 'postgres', + MYSQL = 'mysql', +} diff --git a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-list-item-content.tsx b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-list-item-content.tsx index 8ffcf369..15721f79 100644 --- a/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-list-item-content.tsx +++ b/src/pages/editor-page/side-panel/tables-section/table-list/table-list-item/table-list-item-content/table-list-item-content.tsx @@ -12,6 +12,15 @@ import { } from '@/components/accordion/accordion'; import { Separator } from '@/components/separator/separator'; import { DBTable } from '@/lib/domain/db-table'; +import { DBField, FieldType } from '@/lib/domain/db-field'; +import { useChartDB } from '@/hooks/use-chartdb'; +import { dataTypeMap } from '@/lib/data/data-types'; +import { Toggle } from '@/components/toggle/toggle'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/tooltip/tooltip'; export interface TableListItemContentProps { table: DBTable; @@ -20,56 +29,78 @@ export interface TableListItemContentProps { export const TableListItemContent: React.FC = ({ table, }) => { + const { databaseType, updateField } = useChartDB(); const { color } = table; - const renderField = () => { + + const dataFieldOptions = dataTypeMap[databaseType].map((type) => ({ + label: type, + value: type, + })); + + const RenderField = ({ field }: { field: DBField }) => { return (
+ updateField(table.id, field.id, { + name: e.target.value, + }) + } /> console.log(value)} + selected={field.type} + onChange={(value) => + updateField(table.id, field.id, { + type: value as FieldType, + }) + } emptyText="No types found." />
-
- - +
+ + + + updateField(table.id, field.id, { + nullable: value, + }) + } + > + N + + + Nullable? + + + + + updateField(table.id, field.id, { + primaryKey: value, + }) + } + > + + + + Primary Key +
- {renderField()} - {renderField()} - {renderField()} + {table.fields.map((field) => ( + + ))}