fix: improve UI for views (#1043)

* fix: improve UI for views

* fix

---------

Co-authored-by: Guy Ben-Aharon <baguy3@gmail.com>
This commit is contained in:
Jonathan Fishner
2026-01-04 16:37:51 +02:00
committed by GitHub
parent 84e55f5df6
commit 21bbd48c15
7 changed files with 118 additions and 86 deletions

View File

@@ -336,9 +336,13 @@ export const ChartDBProvider: React.FC<
const createTable: ChartDBContext['createTable'] = useCallback(
async (attributes) => {
const isView = attributes?.isView ?? false;
const count = isView
? tables.filter((t) => t.isView).length + 1
: tables.filter((t) => !t.isView).length + 1;
const table: DBTable = {
id: generateId(),
name: `table_${tables.length + 1}`,
name: isView ? `view_${count}` : `table_${count}`,
x: 0,
y: 0,
fields: [

View File

@@ -66,6 +66,7 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
name: table.name,
schema: table.schema,
parentAreaId: table.parentAreaId,
isView: table.isView,
})),
[tables, showDBViews, checkIfNewTable]
);

View File

@@ -24,4 +24,5 @@ export type RelevantTableData = {
name: string;
schema?: string | null;
parentAreaId?: string | null;
isView?: boolean;
};

View File

@@ -9,7 +9,7 @@ import type {
TableContext,
} from './types';
import type { TreeNode } from '@/components/tree-view/tree';
import { Box, Database, Layers, Table } from 'lucide-react';
import { Box, Database, Layers, Table, View } from 'lucide-react';
import { filterTable } from '@/lib/domain/diagram-filter/filter';
import { defaultSchemas } from '@/lib/data/default-schemas';
@@ -42,7 +42,7 @@ const createTableChildren = (
name: table.name,
type: 'table' as const,
isFolder: false,
icon: Table,
icon: table.isView ? View : Table,
context: {
tableSchema: table.schema,
visible: table.visible,

View File

@@ -24,6 +24,7 @@ import {
SquareDot,
SquarePlus,
SquareMinus,
View,
} from 'lucide-react';
import { Label } from '@/components/label/label';
import {
@@ -344,6 +345,7 @@ export const TableNode: React.FC<NodeProps<TableNodeType>> = React.memo(
() =>
cn(
'flex w-full flex-col border-2 bg-slate-50 dark:bg-slate-950 rounded-lg shadow-sm transition-transform duration-300',
table.isView ? 'border-dashed' : '',
selected || isTarget || isPartOfCreatingRelationship
? 'border-pink-600'
: 'border-slate-500 dark:border-slate-700',
@@ -390,6 +392,7 @@ export const TableNode: React.FC<NodeProps<TableNodeType>> = React.memo(
isTarget,
editTableMode,
isPartOfCreatingRelationship,
table.isView,
]
);
@@ -537,6 +540,8 @@ export const TableNode: React.FC<NodeProps<TableNodeType>> = React.memo(
Table Changed
</TooltipContent>
</Tooltip>
) : table.isView ? (
<View className="size-3.5 shrink-0 text-gray-600 dark:text-primary" />
) : (
<Table2 className="size-3.5 shrink-0 text-gray-600 dark:text-primary" />
)}

View File

@@ -207,71 +207,82 @@ export const TableListItemContent: React.FC<TableListItemContentProps> = ({
</AccordionContent>
</AccordionItem>
<AccordionItem value="indexes" className="mb-2 border-y-0">
<AccordionTrigger
iconPosition="right"
className="group flex flex-1 p-0 px-2 py-1 text-xs text-subtitle hover:bg-secondary"
asChild
>
<div className="flex flex-1 items-center justify-between">
<div className="flex flex-row items-center gap-1">
<FileKey2 className="size-4" />
{t('side_panel.tables_section.table.indexes')}
</div>
{!readonly ? (
<div className="flex flex-row-reverse">
<div className="hidden flex-row-reverse group-hover:flex">
<Button
variant="ghost"
className="size-4 p-0 text-xs hover:bg-primary-foreground"
onClick={createIndexHandler}
>
<Plus className="size-4 shrink-0 text-muted-foreground transition-transform duration-200" />
</Button>
{!table.isView ? (
<AccordionItem value="indexes" className="mb-2 border-y-0">
<AccordionTrigger
iconPosition="right"
className="group flex flex-1 p-0 px-2 py-1 text-xs text-subtitle hover:bg-secondary"
asChild
>
<div className="flex flex-1 items-center justify-between">
<div className="flex flex-row items-center gap-1">
<FileKey2 className="size-4" />
{t(
'side_panel.tables_section.table.indexes'
)}
</div>
{!readonly ? (
<div className="flex flex-row-reverse">
<div className="hidden flex-row-reverse group-hover:flex">
<Button
variant="ghost"
className="size-4 p-0 text-xs hover:bg-primary-foreground"
onClick={createIndexHandler}
>
<Plus className="size-4 shrink-0 text-muted-foreground transition-transform duration-200" />
</Button>
</div>
</div>
) : null}
</div>
</AccordionTrigger>
<AccordionContent className="pb-0 pt-1">
{[...table.indexes]
.sort((a, b) => {
// Sort PK indexes first
if (a.isPrimaryKey && !b.isPrimaryKey)
return -1;
if (!a.isPrimaryKey && b.isPrimaryKey)
return 1;
return 0;
})
.map((index) => (
<TableIndex
key={index.id}
index={index}
removeIndex={() =>
removeIndex(table.id, index.id)
}
updateIndex={(
attrs: Partial<DBIndex>
) =>
updateIndex(
table.id,
index.id,
attrs
)
}
fields={table.fields}
/>
))}
{!readonly ? (
<div className="flex justify-start p-1">
<Button
variant="ghost"
className="flex h-7 items-center gap-1 px-2 text-xs"
onClick={createIndexHandler}
>
<Plus className="size-4 text-muted-foreground" />
{t(
'side_panel.tables_section.table.add_index'
)}
</Button>
</div>
) : null}
</div>
</AccordionTrigger>
<AccordionContent className="pb-0 pt-1">
{[...table.indexes]
.sort((a, b) => {
// Sort PK indexes first
if (a.isPrimaryKey && !b.isPrimaryKey)
return -1;
if (!a.isPrimaryKey && b.isPrimaryKey) return 1;
return 0;
})
.map((index) => (
<TableIndex
key={index.id}
index={index}
removeIndex={() =>
removeIndex(table.id, index.id)
}
updateIndex={(attrs: Partial<DBIndex>) =>
updateIndex(table.id, index.id, attrs)
}
fields={table.fields}
/>
))}
{!readonly ? (
<div className="flex justify-start p-1">
<Button
variant="ghost"
className="flex h-7 items-center gap-1 px-2 text-xs"
onClick={createIndexHandler}
>
<Plus className="size-4 text-muted-foreground" />
{t(
'side_panel.tables_section.table.add_index'
)}
</Button>
</div>
) : null}
</AccordionContent>
</AccordionItem>
</AccordionContent>
</AccordionItem>
) : null}
<AccordionItem value="comments" className="border-y-0">
<AccordionTrigger
@@ -317,14 +328,16 @@ export const TableListItemContent: React.FC<TableListItemContentProps> = ({
{!readonly ? (
<div className="flex gap-1">
<Button
variant="outline"
className="h-8 p-2 text-xs"
onClick={createIndexHandler}
>
<FileKey2 className="h-4" />
{t('side_panel.tables_section.table.add_index')}
</Button>
{!table.isView ? (
<Button
variant="outline"
className="h-8 p-2 text-xs"
onClick={createIndexHandler}
>
<FileKey2 className="h-4" />
{t('side_panel.tables_section.table.add_index')}
</Button>
) : null}
<Button
variant="outline"
className="h-8 p-2 text-xs"

View File

@@ -184,18 +184,20 @@ export const TableListItemHeader: React.FC<TableListItemHeaderProps> = ({
)}
<FileType2 className="size-3.5" />
</DropdownMenuItem>
<DropdownMenuItem
className="flex justify-between gap-4"
onClick={(e) => {
e.stopPropagation();
createIndex(table.id);
}}
>
{t(
'side_panel.tables_section.table.table_actions.add_index'
)}
<FileKey2 className="size-3.5" />
</DropdownMenuItem>
{!table.isView ? (
<DropdownMenuItem
className="flex justify-between gap-4"
onClick={(e) => {
e.stopPropagation();
createIndex(table.id);
}}
>
{t(
'side_panel.tables_section.table.table_actions.add_index'
)}
<FileKey2 className="size-3.5" />
</DropdownMenuItem>
) : null}
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
@@ -226,6 +228,7 @@ export const TableListItemHeader: React.FC<TableListItemHeaderProps> = ({
),
[
table.id,
table.isView,
createField,
createIndex,
deleteTableHandler,
@@ -299,10 +302,15 @@ export const TableListItemHeader: React.FC<TableListItemHeaderProps> = ({
</div>
)}
</div>
<div className="flex flex-row-reverse">
<div className="flex flex-row-reverse items-center">
{!editMode ? (
<>
{!readonly ? <div>{renderDropDownMenu()}</div> : null}
{table.isView ? (
<span className="rounded bg-muted px-1.5 py-0 text-[10px] font-medium text-muted-foreground">
View
</span>
) : null}
<div className="flex flex-row-reverse md:hidden md:group-hover:flex">
{!readonly ? (
<ListItemHeaderButton onClick={enterEditMode}>