mirror of
https://github.com/docmost/docmost.git
synced 2026-08-29 10:05:03 +08:00
feat: bases - WIP
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import { memo, useCallback } from "react";
|
||||
import { Cell } from "@tanstack/react-table";
|
||||
import { useAtom } from "jotai";
|
||||
import { IBaseRow, IBaseProperty, EditingCell } from "@/features/base/types/base.types";
|
||||
import { editingCellAtom } from "@/features/base/atoms/base-atoms";
|
||||
import { isSystemPropertyType } from "@/features/base/hooks/use-base-table";
|
||||
import { CellText } from "@/features/base/components/cells/cell-text";
|
||||
import { CellNumber } from "@/features/base/components/cells/cell-number";
|
||||
import { CellSelect } from "@/features/base/components/cells/cell-select";
|
||||
import { CellStatus } from "@/features/base/components/cells/cell-status";
|
||||
import { CellMultiSelect } from "@/features/base/components/cells/cell-multi-select";
|
||||
import { CellDate } from "@/features/base/components/cells/cell-date";
|
||||
import { CellCheckbox } from "@/features/base/components/cells/cell-checkbox";
|
||||
import { CellUrl } from "@/features/base/components/cells/cell-url";
|
||||
import { CellEmail } from "@/features/base/components/cells/cell-email";
|
||||
import { CellPerson } from "@/features/base/components/cells/cell-person";
|
||||
import { CellFile } from "@/features/base/components/cells/cell-file";
|
||||
import { CellCreatedAt } from "@/features/base/components/cells/cell-created-at";
|
||||
import { CellLastEditedAt } from "@/features/base/components/cells/cell-last-edited-at";
|
||||
import { CellLastEditedBy } from "@/features/base/components/cells/cell-last-edited-by";
|
||||
import classes from "@/features/base/styles/grid.module.css";
|
||||
|
||||
type CellComponentProps = {
|
||||
value: unknown;
|
||||
property: IBaseProperty;
|
||||
rowId: string;
|
||||
isEditing: boolean;
|
||||
onCommit: (value: unknown) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const cellComponents: Record<
|
||||
string,
|
||||
React.ComponentType<CellComponentProps>
|
||||
> = {
|
||||
text: CellText,
|
||||
number: CellNumber,
|
||||
select: CellSelect,
|
||||
status: CellStatus,
|
||||
multiSelect: CellMultiSelect,
|
||||
date: CellDate,
|
||||
checkbox: CellCheckbox,
|
||||
url: CellUrl,
|
||||
email: CellEmail,
|
||||
person: CellPerson,
|
||||
file: CellFile,
|
||||
createdAt: CellCreatedAt,
|
||||
lastEditedAt: CellLastEditedAt,
|
||||
lastEditedBy: CellLastEditedBy,
|
||||
};
|
||||
|
||||
type RowDragProps = {
|
||||
draggable: boolean;
|
||||
onDragStart: (e: React.DragEvent) => void;
|
||||
};
|
||||
|
||||
type GridCellProps = {
|
||||
cell: Cell<IBaseRow, unknown>;
|
||||
rowIndex: number;
|
||||
onCellUpdate: (rowId: string, propertyId: string, value: unknown) => void;
|
||||
rowDragProps?: RowDragProps;
|
||||
};
|
||||
|
||||
export const GridCell = memo(function GridCell({
|
||||
cell,
|
||||
rowIndex,
|
||||
onCellUpdate,
|
||||
rowDragProps,
|
||||
}: GridCellProps) {
|
||||
const property = cell.column.columnDef.meta?.property;
|
||||
const isRowNumber = cell.column.id === "__row_number";
|
||||
const isPinned = cell.column.getIsPinned();
|
||||
const pinOffset = isPinned ? cell.column.getStart("left") : undefined;
|
||||
|
||||
const [editingCell, setEditingCell] = useAtom(editingCellAtom) as unknown as [EditingCell, (val: EditingCell) => void];
|
||||
|
||||
const rowId = cell.row.id;
|
||||
const isEditing =
|
||||
editingCell?.rowId === rowId &&
|
||||
editingCell?.propertyId === property?.id;
|
||||
|
||||
const handleDoubleClick = useCallback(() => {
|
||||
if (!property || isRowNumber) return;
|
||||
if (property.type === "checkbox") return;
|
||||
if (isSystemPropertyType(property.type)) return;
|
||||
setEditingCell({ rowId, propertyId: property.id });
|
||||
}, [property, isRowNumber, rowId, setEditingCell]);
|
||||
|
||||
const handleCommit = useCallback(
|
||||
(value: unknown) => {
|
||||
if (!property) return;
|
||||
const currentValue = cell.getValue();
|
||||
const hasChanged = value !== currentValue
|
||||
&& !(value === "" && (currentValue === null || currentValue === undefined))
|
||||
&& !(value === null && (currentValue === null || currentValue === undefined));
|
||||
if (hasChanged) {
|
||||
onCellUpdate(rowId, property.id, value);
|
||||
}
|
||||
setEditingCell(null);
|
||||
},
|
||||
[property, rowId, cell, onCellUpdate, setEditingCell],
|
||||
);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
setEditingCell(null);
|
||||
}, [setEditingCell]);
|
||||
|
||||
if (isRowNumber) {
|
||||
return (
|
||||
<div
|
||||
className={`${classes.cell} ${classes.rowNumberCell} ${isPinned ? classes.cellPinned : ""} ${rowDragProps ? classes.rowNumberDraggable : ""}`}
|
||||
style={{
|
||||
...(isPinned ? { left: pinOffset } : {}),
|
||||
}}
|
||||
draggable={rowDragProps?.draggable}
|
||||
onDragStart={rowDragProps?.onDragStart}
|
||||
>
|
||||
{rowIndex + 1}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!property) return null;
|
||||
|
||||
const CellComponent = cellComponents[property.type];
|
||||
if (!CellComponent) return null;
|
||||
|
||||
const value = cell.getValue();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${classes.cell} ${isPinned ? classes.cellPinned : ""} ${isEditing ? classes.cellEditing : ""} ${property.isPrimary ? classes.primaryCell : ""}`}
|
||||
style={{
|
||||
...(isPinned ? { left: pinOffset } : {}),
|
||||
}}
|
||||
onDoubleClick={handleDoubleClick}
|
||||
>
|
||||
<CellComponent
|
||||
value={value}
|
||||
property={property}
|
||||
rowId={rowId}
|
||||
isEditing={isEditing}
|
||||
onCommit={handleCommit}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user