mirror of
https://github.com/colanode/colanode.git
synced 2025-12-29 00:25:03 +01:00
Implement table view context
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Icon } from '@/components/ui/icon';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { getDefaultFieldWidth, getFieldIcon } from '@/lib/databases';
|
||||
import { getFieldIcon } from '@/lib/databases';
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
import { Resizable } from 're-resizable';
|
||||
import { Field } from '@/types/databases';
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { FieldDeleteDialog } from '@/components/databases/fields/field-delete-dialog';
|
||||
import { FieldRenameInput } from '@/components/databases/fields/field-rename-input';
|
||||
import { useTableView } from '@/contexts/table-view';
|
||||
|
||||
interface TableViewFieldHeaderProps {
|
||||
field: Field;
|
||||
@@ -23,6 +24,8 @@ export const TableViewFieldHeader = ({
|
||||
field,
|
||||
index,
|
||||
}: TableViewFieldHeaderProps) => {
|
||||
const tableView = useTableView();
|
||||
|
||||
const canEditDatabase = true;
|
||||
const canEditView = true;
|
||||
|
||||
@@ -32,7 +35,7 @@ export const TableViewFieldHeader = ({
|
||||
type: 'table-field-header',
|
||||
item: field,
|
||||
canDrag: () => canEditView,
|
||||
end: (item, monitor) => {
|
||||
end: (_item, monitor) => {
|
||||
const dropResult = monitor.getDropResult<{ index: number }>();
|
||||
if (!dropResult?.index) return;
|
||||
|
||||
@@ -60,18 +63,19 @@ export const TableViewFieldHeader = ({
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
const dragDropRef = dragRef(dropRef(divRef));
|
||||
|
||||
const defaultWidth = getDefaultFieldWidth(field.type);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Resizable
|
||||
defaultSize={{
|
||||
width: defaultWidth,
|
||||
width: `${tableView.getFieldWidth(field.id, field.type)}px`,
|
||||
height: '2rem',
|
||||
}}
|
||||
minWidth={100}
|
||||
maxWidth={500}
|
||||
size={{ width: defaultWidth, height: '2rem' }}
|
||||
size={{
|
||||
width: `${tableView.getFieldWidth(field.id, field.type)}px`,
|
||||
height: '2rem',
|
||||
}}
|
||||
enable={{
|
||||
bottom: false,
|
||||
bottomLeft: false,
|
||||
@@ -91,12 +95,9 @@ export const TableViewFieldHeader = ({
|
||||
right: '-3px',
|
||||
},
|
||||
}}
|
||||
onResizeStop={(e, direction, ref) => {
|
||||
// const newWidth = ref.offsetWidth;
|
||||
// view.updateFieldAttrs({
|
||||
// ...viewField.attrs,
|
||||
// width: newWidth,
|
||||
// });
|
||||
onResizeStop={(_e, _direction, ref) => {
|
||||
const newWidth = ref.offsetWidth;
|
||||
tableView.resizeField(field.id, newWidth);
|
||||
}}
|
||||
>
|
||||
<Popover modal={true}>
|
||||
@@ -160,10 +161,7 @@ export const TableViewFieldHeader = ({
|
||||
<div
|
||||
className="flex cursor-pointer flex-row items-center gap-2 p-1 hover:bg-gray-100"
|
||||
onClick={() => {
|
||||
// view.updateFieldAttrs({
|
||||
// ...viewField.attrs,
|
||||
// visible: false,
|
||||
// });
|
||||
tableView.hideField(field.id);
|
||||
}}
|
||||
>
|
||||
<Icon name="eye-off-line" />
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import React from 'react';
|
||||
import { useDatabase } from '@/contexts/database';
|
||||
import { useTableView } from '@/contexts/table-view';
|
||||
import { TableViewNameHeader } from '@/components/databases/tables/table-view-name-header';
|
||||
import { TableViewFieldHeader } from '@/components/databases/tables/table-view-field-header';
|
||||
import { FieldCreatePopover } from '@/components/databases/fields/field-create-popover';
|
||||
|
||||
export const TableViewHeader = () => {
|
||||
const database = useDatabase();
|
||||
const tableView = useTableView();
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center gap-0.5">
|
||||
<div style={{ width: '30px', minWidth: '30px' }} />
|
||||
<TableViewNameHeader />
|
||||
{database.fields.map((field, index) => {
|
||||
// const isVisible =
|
||||
// viewField.attrs.visible ?? getDefaultFieldVisibility(view.layout);
|
||||
// if (!isVisible) return null;
|
||||
|
||||
{tableView.fields.map((field, index) => {
|
||||
return (
|
||||
<TableViewFieldHeader field={field} key={field.id} index={index} />
|
||||
);
|
||||
|
||||
@@ -8,21 +8,22 @@ import {
|
||||
} from '@/components/ui/popover';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useTableView } from '@/contexts/table-view';
|
||||
|
||||
export const TableViewNameHeader = () => {
|
||||
const defaultWidth = '300px';
|
||||
const tableView = useTableView();
|
||||
|
||||
const canEditView = true;
|
||||
|
||||
return (
|
||||
<Resizable
|
||||
defaultSize={{
|
||||
width: defaultWidth,
|
||||
width: `${tableView.getNameWidth()}px`,
|
||||
height: '2rem',
|
||||
}}
|
||||
minWidth={100}
|
||||
maxWidth={500}
|
||||
size={{ width: defaultWidth, height: '2rem' }}
|
||||
size={{ width: `${tableView.getNameWidth()}px`, height: '2rem' }}
|
||||
enable={{
|
||||
bottom: false,
|
||||
bottomLeft: false,
|
||||
@@ -43,11 +44,8 @@ export const TableViewNameHeader = () => {
|
||||
},
|
||||
}}
|
||||
onResizeStop={(e, direction, ref) => {
|
||||
// const newWidth = ref.offsetWidth;
|
||||
// view.updateFieldAttrs({
|
||||
// ...viewField.attrs,
|
||||
// width: newWidth,
|
||||
// });
|
||||
const newWidth = ref.offsetWidth;
|
||||
tableView.resizeName(newWidth);
|
||||
}}
|
||||
>
|
||||
<Popover modal={true}>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { TableViewNameCell } from '@/components/databases/tables/table-view-name-cell';
|
||||
import { getDefaultFieldWidth } from '@/lib/databases';
|
||||
import { useDatabase } from '@/contexts/database';
|
||||
import { TableViewFieldCell } from './table-view-field-cell';
|
||||
import { RecordNode } from '@/types/databases';
|
||||
import { useTableView } from '@/contexts/table-view';
|
||||
|
||||
interface TableViewRowProps {
|
||||
index: number;
|
||||
@@ -11,8 +10,8 @@ interface TableViewRowProps {
|
||||
}
|
||||
|
||||
export const TableViewRow = ({ index, record }: TableViewRowProps) => {
|
||||
const database = useDatabase();
|
||||
const nameCellWidth = '300px';
|
||||
const tableView = useTableView();
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in flex flex-row items-center gap-0.5 border-b">
|
||||
<span
|
||||
@@ -23,12 +22,12 @@ export const TableViewRow = ({ index, record }: TableViewRowProps) => {
|
||||
</span>
|
||||
<div
|
||||
className="h-8 border-r"
|
||||
style={{ width: nameCellWidth, minWidth: nameCellWidth }}
|
||||
style={{ width: `${tableView.getNameWidth()}px`, minWidth: '300px' }}
|
||||
>
|
||||
<TableViewNameCell record={record} />
|
||||
</div>
|
||||
{database.fields.map((field) => {
|
||||
const width = getDefaultFieldWidth(field.type) + 'px';
|
||||
{tableView.fields.map((field) => {
|
||||
const width = tableView.getFieldWidth(field.id, field.type);
|
||||
return (
|
||||
<div
|
||||
key={`row-${record.id}-${field.id}`}
|
||||
|
||||
@@ -3,17 +3,90 @@ import { LocalNode } from '@/types/nodes';
|
||||
import { TableViewHeader } from '@/components/databases/tables/table-view-header';
|
||||
import { TableViewBody } from '@/components/databases/tables/table-view-body';
|
||||
import { TableViewRecordCreateRow } from '@/components/databases/tables/table-view-record-create-row';
|
||||
import { TableViewContext } from '@/contexts/table-view';
|
||||
import { useDatabase } from '@/contexts/database';
|
||||
import { compareString } from '@/lib/utils';
|
||||
import { FieldType } from '@/types/databases';
|
||||
import { getDefaultFieldWidth, getDefaultNameWidth } from '@/lib/databases';
|
||||
|
||||
interface TableViewProps {
|
||||
node: LocalNode;
|
||||
}
|
||||
|
||||
export const TableView = ({ node }: TableViewProps) => {
|
||||
const database = useDatabase();
|
||||
|
||||
const attrs = node.attrs ?? {};
|
||||
const [hiddenFields, setHiddenFields] = React.useState<string[]>(
|
||||
attrs.hiddenFields ?? [],
|
||||
);
|
||||
const [fieldIndexes, setFieldIndexes] = React.useState<
|
||||
Record<string, string>
|
||||
>(attrs.fieldIndexes ?? {});
|
||||
const [fieldWidths, setFieldWidths] = React.useState<Record<string, number>>(
|
||||
attrs.fieldWidths ?? {},
|
||||
);
|
||||
const [nameWidth, setNameWidth] = React.useState<number>(
|
||||
attrs.nameWidth ?? getDefaultNameWidth(),
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const attrs = node.attrs ?? {};
|
||||
setHiddenFields(attrs.hiddenFields ?? []);
|
||||
setFieldIndexes(attrs.fieldIndexes ?? {});
|
||||
setFieldWidths(attrs.fieldWidths ?? {});
|
||||
setNameWidth(attrs.nameWidth ?? getDefaultNameWidth());
|
||||
}, [node.versionId]);
|
||||
|
||||
return (
|
||||
<div className="mt-2 w-full min-w-full max-w-full overflow-auto pr-5">
|
||||
<TableViewHeader />
|
||||
<TableViewBody />
|
||||
<TableViewRecordCreateRow />
|
||||
</div>
|
||||
<TableViewContext.Provider
|
||||
value={{
|
||||
id: node.id,
|
||||
fields: database.fields
|
||||
.filter((field) => !hiddenFields.includes(field.id))
|
||||
.sort((a, b) =>
|
||||
compareString(
|
||||
fieldIndexes[a.id] ?? a.index,
|
||||
fieldIndexes[b.id] ?? b.index,
|
||||
),
|
||||
),
|
||||
hideField: (id: string) => {
|
||||
if (hiddenFields.includes(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setHiddenFields([...hiddenFields, id]);
|
||||
},
|
||||
showField: (id: string) => {
|
||||
if (!hiddenFields.includes(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setHiddenFields(hiddenFields.filter((fieldId) => fieldId !== id));
|
||||
},
|
||||
getNameWidth: () => nameWidth,
|
||||
resizeName: (width: number) => {
|
||||
setNameWidth(width);
|
||||
},
|
||||
getFieldWidth: (id: string, type: FieldType) => {
|
||||
return fieldWidths[id] ?? getDefaultFieldWidth(type);
|
||||
},
|
||||
resizeField: (id, width) => {
|
||||
setFieldWidths({ ...fieldWidths, [id]: width });
|
||||
},
|
||||
moveField: (id, after) => {
|
||||
setFieldIndexes({
|
||||
...fieldIndexes,
|
||||
[id]: fieldIndexes[after],
|
||||
});
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div className="mt-2 w-full min-w-full max-w-full overflow-auto pr-5">
|
||||
<TableViewHeader />
|
||||
<TableViewBody />
|
||||
<TableViewRecordCreateRow />
|
||||
</div>
|
||||
</TableViewContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
20
desktop/src/contexts/table-view.ts
Normal file
20
desktop/src/contexts/table-view.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Field, FieldType } from '@/types/databases';
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
interface TableViewContext {
|
||||
id: string;
|
||||
fields: Field[];
|
||||
hideField: (id: string) => void;
|
||||
showField: (id: string) => void;
|
||||
getNameWidth: () => number;
|
||||
resizeName: (width: number) => void;
|
||||
getFieldWidth: (id: string, type: FieldType) => number;
|
||||
resizeField: (id: string, width: number) => void;
|
||||
moveField: (id: string, after: string) => void;
|
||||
}
|
||||
|
||||
export const TableViewContext = createContext<TableViewContext>(
|
||||
{} as TableViewContext,
|
||||
);
|
||||
|
||||
export const useTableView = () => useContext(TableViewContext);
|
||||
@@ -90,6 +90,10 @@ export const getDefaultFieldWidth = (type: FieldType): number => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getDefaultNameWidth = (): number => {
|
||||
return 300;
|
||||
};
|
||||
|
||||
interface SelectOptionColor {
|
||||
label: string;
|
||||
value: string;
|
||||
|
||||
Reference in New Issue
Block a user