mirror of
https://github.com/umami-software/umami.git
synced 2026-02-04 04:37:11 +01:00
Add move row up/down functionality to board editor.
Rows can now be reordered using up/down buttons. Buttons are disabled at boundaries (up disabled on first row, down disabled on last row). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d9f698ca42
commit
ff6575ff54
1 changed files with 113 additions and 35 deletions
|
|
@ -4,7 +4,7 @@ import { Fragment, type ReactElement } from 'react';
|
||||||
import { Group, Panel, Separator } from 'react-resizable-panels';
|
import { Group, Panel, Separator } from 'react-resizable-panels';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { useBoard } from '@/components/hooks';
|
import { useBoard } from '@/components/hooks';
|
||||||
import { Minus, Plus } from '@/components/icons';
|
import { ChevronDown, Minus, Plus } from '@/components/icons';
|
||||||
import type { BoardColumn as BoardColumnType } from '@/lib/types';
|
import type { BoardColumn as BoardColumnType } from '@/lib/types';
|
||||||
|
|
||||||
const CATALOG = {
|
const CATALOG = {
|
||||||
|
|
@ -14,6 +14,12 @@ const CATALOG = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MIN_HEIGHT = 300;
|
||||||
|
const MAX_HEIGHT = 600;
|
||||||
|
const MIN_WIDTH = 300;
|
||||||
|
const MARGIN = 10;
|
||||||
|
const MAX_COLUMNS = 4;
|
||||||
|
|
||||||
export function BoardBody() {
|
export function BoardBody() {
|
||||||
const { board, updateBoard, saveBoard, isPending } = useBoard();
|
const { board, updateBoard, saveBoard, isPending } = useBoard();
|
||||||
|
|
||||||
|
|
@ -31,7 +37,6 @@ export function BoardBody() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveRow = (id: string) => {
|
const handleRemoveRow = (id: string) => {
|
||||||
console.log('Removing row', id);
|
|
||||||
updateBoard({
|
updateBoard({
|
||||||
parameters: produce(board.parameters, draft => {
|
parameters: produce(board.parameters, draft => {
|
||||||
if (!draft.rows) {
|
if (!draft.rows) {
|
||||||
|
|
@ -43,44 +48,90 @@ export function BoardBody() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMoveRowUp = (id: string) => {
|
||||||
|
updateBoard({
|
||||||
|
parameters: produce(board.parameters, draft => {
|
||||||
|
if (!draft.rows) return;
|
||||||
|
|
||||||
|
const index = draft.rows.findIndex(row => row.id === id);
|
||||||
|
if (index > 0) {
|
||||||
|
const temp = draft.rows[index - 1];
|
||||||
|
draft.rows[index - 1] = draft.rows[index];
|
||||||
|
draft.rows[index] = temp;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMoveRowDown = (id: string) => {
|
||||||
|
updateBoard({
|
||||||
|
parameters: produce(board.parameters, draft => {
|
||||||
|
if (!draft.rows) return;
|
||||||
|
|
||||||
|
const index = draft.rows.findIndex(row => row.id === id);
|
||||||
|
if (index < draft.rows.length - 1) {
|
||||||
|
const temp = draft.rows[index + 1];
|
||||||
|
draft.rows[index + 1] = draft.rows[index];
|
||||||
|
draft.rows[index] = temp;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const rows = board?.parameters?.rows ?? [];
|
const rows = board?.parameters?.rows ?? [];
|
||||||
const minHeight = 300 * (rows.length || 1);
|
const rowCount = (rows.length || 1) + 1;
|
||||||
|
const minHeight = (MAX_HEIGHT + MARGIN) * rowCount;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Group orientation="vertical" style={{ minHeight }}>
|
||||||
<Group orientation="vertical" style={{ minHeight }}>
|
{rows.map((row, index) => (
|
||||||
{rows.map((row, index) => (
|
<Fragment key={row.id}>
|
||||||
<Fragment key={row.id}>
|
<Panel minSize={MIN_HEIGHT}>
|
||||||
<Panel minSize={200}>
|
<BoardRow
|
||||||
<BoardRow {...row} rowId={row.id} onRemove={handleRemoveRow} />
|
{...row}
|
||||||
</Panel>
|
rowId={row.id}
|
||||||
{index < rows.length - 1 && <Separator />}
|
rowIndex={index}
|
||||||
</Fragment>
|
rowCount={rows.length}
|
||||||
))}
|
onRemove={handleRemoveRow}
|
||||||
</Group>
|
onMoveUp={handleMoveRowUp}
|
||||||
<Row>
|
onMoveDown={handleMoveRowDown}
|
||||||
<TooltipTrigger delay={0}>
|
/>
|
||||||
<Button variant="outline" onPress={handleAddRow}>
|
</Panel>
|
||||||
<Icon>
|
{index < rows.length - 1 && <Separator />}
|
||||||
<Plus />
|
</Fragment>
|
||||||
</Icon>
|
))}
|
||||||
</Button>
|
<Panel>
|
||||||
<Tooltip placement="bottom">Add row</Tooltip>
|
<Row padding="3">
|
||||||
</TooltipTrigger>
|
<TooltipTrigger delay={0}>
|
||||||
</Row>
|
<Button variant="outline" onPress={handleAddRow}>
|
||||||
</>
|
<Icon>
|
||||||
|
<Plus />
|
||||||
|
</Icon>
|
||||||
|
</Button>
|
||||||
|
<Tooltip placement="bottom">Add row</Tooltip>
|
||||||
|
</TooltipTrigger>
|
||||||
|
</Row>
|
||||||
|
</Panel>
|
||||||
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BoardRow({
|
function BoardRow({
|
||||||
rowId,
|
rowId,
|
||||||
|
rowIndex,
|
||||||
|
rowCount,
|
||||||
columns,
|
columns,
|
||||||
onRemove,
|
onRemove,
|
||||||
|
onMoveUp,
|
||||||
|
onMoveDown,
|
||||||
}: {
|
}: {
|
||||||
rowId: string;
|
rowId: string;
|
||||||
|
rowIndex: number;
|
||||||
|
rowCount: number;
|
||||||
columns: BoardColumnType[];
|
columns: BoardColumnType[];
|
||||||
onAddComponent?: () => void;
|
|
||||||
onRemove?: (id: string) => void;
|
onRemove?: (id: string) => void;
|
||||||
|
onMoveUp?: (id: string) => void;
|
||||||
|
onMoveDown?: (id: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const { board, updateBoard } = useBoard();
|
const { board, updateBoard } = useBoard();
|
||||||
|
|
||||||
|
|
@ -102,27 +153,54 @@ function BoardRow({
|
||||||
<Group style={{ height: '100%' }}>
|
<Group style={{ height: '100%' }}>
|
||||||
{columns?.map((column, index) => (
|
{columns?.map((column, index) => (
|
||||||
<Fragment key={column.id}>
|
<Fragment key={column.id}>
|
||||||
<Panel minSize={300}>
|
<Panel minSize={MIN_HEIGHT}>
|
||||||
<BoardColumn {...column} />
|
<BoardColumn {...column} />
|
||||||
</Panel>
|
</Panel>
|
||||||
{index < columns.length - 1 && <Separator />}
|
{index < columns.length - 1 && <Separator />}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
<Box alignSelf="center" padding="3">
|
<Column alignSelf="center" padding="3" gap="1">
|
||||||
<Button variant="outline" onPress={handleAddColumn}>
|
<TooltipTrigger delay={0}>
|
||||||
<Icon>
|
<Button variant="outline" onPress={() => onMoveUp?.(rowId)} isDisabled={rowIndex === 0}>
|
||||||
<Plus />
|
<Icon rotate={180}>
|
||||||
</Icon>
|
<ChevronDown />
|
||||||
</Button>
|
</Icon>
|
||||||
|
</Button>
|
||||||
|
<Tooltip placement="top">Move row up</Tooltip>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipTrigger delay={0}>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onPress={handleAddColumn}
|
||||||
|
isDisabled={columns.length >= MAX_COLUMNS}
|
||||||
|
>
|
||||||
|
<Icon>
|
||||||
|
<Plus />
|
||||||
|
</Icon>
|
||||||
|
</Button>
|
||||||
|
<Tooltip placement="left">Add column</Tooltip>
|
||||||
|
</TooltipTrigger>
|
||||||
<TooltipTrigger delay={0}>
|
<TooltipTrigger delay={0}>
|
||||||
<Button variant="outline" onPress={() => onRemove?.(rowId)}>
|
<Button variant="outline" onPress={() => onRemove?.(rowId)}>
|
||||||
<Icon>
|
<Icon>
|
||||||
<Minus />
|
<Minus />
|
||||||
</Icon>
|
</Icon>
|
||||||
</Button>
|
</Button>
|
||||||
<Tooltip placement="bottom">Remove row</Tooltip>
|
<Tooltip placement="left">Remove row</Tooltip>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
</Box>
|
<TooltipTrigger delay={0}>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onPress={() => onMoveDown?.(rowId)}
|
||||||
|
isDisabled={rowIndex === rowCount - 1}
|
||||||
|
>
|
||||||
|
<Icon>
|
||||||
|
<ChevronDown />
|
||||||
|
</Icon>
|
||||||
|
</Button>
|
||||||
|
<Tooltip placement="bottom">Move row down</Tooltip>
|
||||||
|
</TooltipTrigger>
|
||||||
|
</Column>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue