mirror of
https://github.com/umami-software/umami.git
synced 2026-02-16 02:25:35 +01:00
159 lines
4.7 KiB
TypeScript
159 lines
4.7 KiB
TypeScript
import { Button, Icon, Row, Tooltip, TooltipTrigger } from '@umami/react-zen';
|
|
import { produce } from 'immer';
|
|
import { Fragment, useEffect, useRef } from 'react';
|
|
import { Group, type GroupImperativeHandle, Panel, Separator } from 'react-resizable-panels';
|
|
import { v4 as uuid } from 'uuid';
|
|
import { useBoard } from '@/components/hooks';
|
|
import { GripHorizontal, Plus } from '@/components/icons';
|
|
import styles from './BoardEditLayout.module.css';
|
|
import { BoardEditRow } from './BoardEditRow';
|
|
import { BUTTON_ROW_HEIGHT, MAX_ROW_HEIGHT, MIN_ROW_HEIGHT } from './boardConstants';
|
|
|
|
export function BoardEditBody() {
|
|
const { board, updateBoard, registerLayoutGetter } = useBoard();
|
|
const rowGroupRef = useRef<GroupImperativeHandle>(null);
|
|
const columnGroupRefs = useRef<Map<string, GroupImperativeHandle>>(new Map());
|
|
|
|
useEffect(() => {
|
|
registerLayoutGetter(() => {
|
|
const rows = board?.parameters?.rows;
|
|
|
|
if (!rows?.length) return null;
|
|
|
|
const rowLayout = rowGroupRef.current?.getLayout();
|
|
|
|
const updatedRows = rows.map(row => {
|
|
const columnGroupRef = columnGroupRefs.current.get(row.id);
|
|
const columnLayout = columnGroupRef?.getLayout();
|
|
|
|
const updatedColumns = row.columns.map(col => ({
|
|
...col,
|
|
size: columnLayout?.[col.id],
|
|
}));
|
|
|
|
return {
|
|
...row,
|
|
size: rowLayout?.[row.id],
|
|
columns: updatedColumns,
|
|
};
|
|
});
|
|
|
|
return { rows: updatedRows };
|
|
});
|
|
}, [registerLayoutGetter, board?.parameters?.rows]);
|
|
|
|
const registerColumnGroupRef = (rowId: string, ref: GroupImperativeHandle | null) => {
|
|
if (ref) {
|
|
columnGroupRefs.current.set(rowId, ref);
|
|
} else {
|
|
columnGroupRefs.current.delete(rowId);
|
|
}
|
|
};
|
|
|
|
const handleAddRow = () => {
|
|
updateBoard({
|
|
parameters: produce(board.parameters, draft => {
|
|
if (!draft.rows) {
|
|
draft.rows = [];
|
|
}
|
|
draft.rows.push({ id: uuid(), columns: [{ id: uuid(), component: null }] });
|
|
}),
|
|
});
|
|
};
|
|
|
|
const handleRemoveRow = (id: string) => {
|
|
updateBoard({
|
|
parameters: produce(board.parameters, draft => {
|
|
if (!draft.rows) {
|
|
return;
|
|
}
|
|
|
|
draft.rows = draft.rows.filter(row => row?.id !== id);
|
|
}),
|
|
});
|
|
};
|
|
|
|
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 websiteId = board?.parameters?.websiteId;
|
|
const rows = board?.parameters?.rows ?? [];
|
|
const minHeight = (rows.length || 1) * MAX_ROW_HEIGHT + BUTTON_ROW_HEIGHT;
|
|
|
|
return (
|
|
<Group groupRef={rowGroupRef} orientation="vertical" style={{ minHeight }}>
|
|
{rows.map((row, index) => (
|
|
<Fragment key={row.id}>
|
|
<Panel
|
|
id={row.id}
|
|
minSize={MIN_ROW_HEIGHT}
|
|
maxSize={MAX_ROW_HEIGHT}
|
|
defaultSize={row.size}
|
|
>
|
|
<BoardEditRow
|
|
{...row}
|
|
rowId={row.id}
|
|
rowIndex={index}
|
|
rowCount={rows.length}
|
|
canEdit={!!websiteId}
|
|
onRemove={handleRemoveRow}
|
|
onMoveUp={handleMoveRowUp}
|
|
onMoveDown={handleMoveRowDown}
|
|
onRegisterRef={registerColumnGroupRef}
|
|
/>
|
|
</Panel>
|
|
{index < rows.length - 1 && (
|
|
<Separator className={styles.rowSeparator}>
|
|
<span className={styles.separatorHandle}>
|
|
<Icon size="sm">
|
|
<GripHorizontal />
|
|
</Icon>
|
|
</span>
|
|
</Separator>
|
|
)}
|
|
</Fragment>
|
|
))}
|
|
{!!websiteId && (
|
|
<Panel minSize={BUTTON_ROW_HEIGHT}>
|
|
<Row padding="3">
|
|
<TooltipTrigger delay={0}>
|
|
<Button variant="outline" onPress={handleAddRow}>
|
|
<Icon>
|
|
<Plus />
|
|
</Icon>
|
|
</Button>
|
|
<Tooltip placement="bottom">Add row</Tooltip>
|
|
</TooltipTrigger>
|
|
</Row>
|
|
</Panel>
|
|
)}
|
|
</Group>
|
|
);
|
|
}
|