mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	chore(react/collections/table): fix some issues with col editing
This commit is contained in:
		@@ -2,31 +2,27 @@ import { useLegacyImperativeHandlers } from "../../react/hooks";
 | 
			
		||||
import { Attribute } from "../../../services/attribute_parser";
 | 
			
		||||
import { RefObject } from "preact";
 | 
			
		||||
import { Tabulator } from "tabulator-tables";
 | 
			
		||||
import { useEffect, useState } from "preact/hooks";
 | 
			
		||||
import { useRef, useState } from "preact/hooks";
 | 
			
		||||
import { CommandListenerData, EventData } from "../../../components/app_context";
 | 
			
		||||
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
 | 
			
		||||
import attributes from "../../../services/attributes";
 | 
			
		||||
import { renameColumn } from "../../view_widgets/table_view/bulk_actions";
 | 
			
		||||
import FNote from "../../../entities/fnote";
 | 
			
		||||
import { getAttributeFromField } from "./utils";
 | 
			
		||||
 | 
			
		||||
export default function useColTableEditing(api: RefObject<Tabulator>, attributeDetailWidget: AttributeDetailWidget, parentNote: FNote) {
 | 
			
		||||
 | 
			
		||||
    const [ existingAttributeToEdit, setExistingAttributeToEdit ] = useState<Attribute>();
 | 
			
		||||
    const [ newAttribute, setNewAttribute ] = useState<Attribute>();
 | 
			
		||||
    const [ newAttributePosition, setNewAttributePosition ] = useState<number>();
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
 | 
			
		||||
    }, []);
 | 
			
		||||
    const newAttribute = useRef<Attribute>();
 | 
			
		||||
    const newAttributePosition = useRef<number>();
 | 
			
		||||
 | 
			
		||||
    useLegacyImperativeHandlers({
 | 
			
		||||
        addNewTableColumnCommand({ referenceColumn, columnToEdit, direction, type }: EventData<"addNewTableColumn">) {
 | 
			
		||||
            console.log("Ding");
 | 
			
		||||
            let attr: Attribute | undefined;
 | 
			
		||||
 | 
			
		||||
            setExistingAttributeToEdit(undefined);
 | 
			
		||||
            if (columnToEdit) {
 | 
			
		||||
                attr = this.getAttributeFromField(columnToEdit.getField());
 | 
			
		||||
                attr = getAttributeFromField(parentNote, columnToEdit.getField());
 | 
			
		||||
                if (attr) {
 | 
			
		||||
                    setExistingAttributeToEdit({ ...attr });
 | 
			
		||||
                }
 | 
			
		||||
@@ -47,9 +43,9 @@ export default function useColTableEditing(api: RefObject<Tabulator>, attributeD
 | 
			
		||||
                    newPosition++;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                setNewAttributePosition(newPosition);
 | 
			
		||||
                newAttributePosition.current = newPosition;
 | 
			
		||||
            } else {
 | 
			
		||||
                setNewAttributePosition(undefined);
 | 
			
		||||
                newAttributePosition.current = undefined;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            attributeDetailWidget.showAttributeDetail({
 | 
			
		||||
@@ -63,26 +59,26 @@ export default function useColTableEditing(api: RefObject<Tabulator>, attributeD
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        async updateAttributeListCommand({ attributes }: CommandListenerData<"updateAttributeList">) {
 | 
			
		||||
            setNewAttribute(attributes[0]);
 | 
			
		||||
            newAttribute.current = attributes[0];
 | 
			
		||||
        },
 | 
			
		||||
        async saveAttributesCommand() {
 | 
			
		||||
            if (!newAttribute || !api.current) {
 | 
			
		||||
            if (!newAttribute.current || !api.current) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const { name, value, isInheritable } = newAttribute;
 | 
			
		||||
            const { name, value, isInheritable } = newAttribute.current;
 | 
			
		||||
 | 
			
		||||
            api.current.blockRedraw();
 | 
			
		||||
            const isRename = (this.existingAttributeToEdit && this.existingAttributeToEdit.name !== name);
 | 
			
		||||
            const isRename = (existingAttributeToEdit && existingAttributeToEdit.name !== name);
 | 
			
		||||
            try {
 | 
			
		||||
                if (isRename) {
 | 
			
		||||
                    const oldName = this.existingAttributeToEdit!.name.split(":")[1];
 | 
			
		||||
                    const oldName = existingAttributeToEdit!.name.split(":")[1];
 | 
			
		||||
                    const [ type, newName ] = name.split(":");
 | 
			
		||||
                    await renameColumn(parentNote.noteId, type as "label" | "relation", oldName, newName);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (existingAttributeToEdit && (isRename || existingAttributeToEdit.isInheritable !== isInheritable)) {
 | 
			
		||||
                    attributes.removeOwnedLabelByName(parentNote, this.existingAttributeToEdit.name);
 | 
			
		||||
                    attributes.removeOwnedLabelByName(parentNote, existingAttributeToEdit.name);
 | 
			
		||||
                }
 | 
			
		||||
                attributes.setLabel(parentNote.noteId, name, value, isInheritable);
 | 
			
		||||
            } finally {
 | 
			
		||||
@@ -91,5 +87,5 @@ export default function useColTableEditing(api: RefObject<Tabulator>, attributeD
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
    return { newAttributePosition };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ import useRowTableEditing, { canReorderRows } from "./row_editing";
 | 
			
		||||
import useColTableEditing from "./col_editing";
 | 
			
		||||
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
 | 
			
		||||
import attributes from "../../../services/attributes";
 | 
			
		||||
import { refreshTextDimensions } from "@excalidraw/excalidraw/element/newElement";
 | 
			
		||||
import { RefObject } from "preact";
 | 
			
		||||
 | 
			
		||||
interface TableConfig {
 | 
			
		||||
    tableData?: {
 | 
			
		||||
@@ -24,48 +24,15 @@ interface TableConfig {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function TableView({ note, noteIds, notePath, viewConfig, saveConfig }: ViewModeProps<TableConfig>) {
 | 
			
		||||
    const [ maxDepth ] = useNoteLabelInt(note, "maxNestingDepth") ?? -1;
 | 
			
		||||
    const [ columnDefs, setColumnDefs ] = useState<ColumnDefinition[]>();
 | 
			
		||||
    const [ rowData, setRowData ] = useState<TableData[]>();
 | 
			
		||||
    const [ movableRows, setMovableRows ] = useState<boolean>();
 | 
			
		||||
    const [ hasChildren, setHasChildren ] = useState<boolean>();
 | 
			
		||||
    const tabulatorRef = useRef<VanillaTabulator>(null);
 | 
			
		||||
    const parentComponent = useContext(ParentComponent);
 | 
			
		||||
 | 
			
		||||
    function refresh() {
 | 
			
		||||
        const info = getAttributeDefinitionInformation(note);
 | 
			
		||||
        buildRowDefinitions(note, info, maxDepth).then(({ definitions: rowData, hasSubtree: hasChildren, rowNumber }) => {
 | 
			
		||||
            const movableRows = canReorderRows(note) && !hasChildren;
 | 
			
		||||
            const columnDefs = buildColumnDefinitions({
 | 
			
		||||
                info,
 | 
			
		||||
                movableRows,
 | 
			
		||||
                existingColumnData: viewConfig?.tableData?.columns,
 | 
			
		||||
                rowNumberHint: rowNumber
 | 
			
		||||
            });
 | 
			
		||||
            setColumnDefs(columnDefs);
 | 
			
		||||
            setRowData(rowData);
 | 
			
		||||
            setMovableRows(movableRows);
 | 
			
		||||
            setHasChildren(hasChildren);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    useEffect(refresh, [ note, noteIds ]);
 | 
			
		||||
 | 
			
		||||
    // React to column changes.
 | 
			
		||||
    useTriliumEvent("entitiesReloaded", ({ loadResults}) => {
 | 
			
		||||
        if (loadResults.getAttributeRows().find(attr =>
 | 
			
		||||
            attr.type === "label" &&
 | 
			
		||||
            (attr.name?.startsWith("label:") || attr.name?.startsWith("relation:")) &&
 | 
			
		||||
            attributes.isAffecting(attr, note))) {
 | 
			
		||||
            refresh();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const [ attributeDetailWidgetEl, attributeDetailWidget ] = useLegacyWidget(() => new AttributeDetailWidget().contentSized());
 | 
			
		||||
    const contextMenuEvents = useContextMenu(note, parentComponent, tabulatorRef);
 | 
			
		||||
    const persistenceProps = usePersistence(viewConfig, saveConfig);
 | 
			
		||||
    const rowEditingEvents = useRowTableEditing(tabulatorRef, attributeDetailWidget, notePath);
 | 
			
		||||
    const colEditingEvents = useColTableEditing(tabulatorRef, attributeDetailWidget, note);
 | 
			
		||||
    const { newAttributePosition } = useColTableEditing(tabulatorRef, attributeDetailWidget, note);
 | 
			
		||||
    const { columnDefs, rowData, movableRows, hasChildren } = useData(note, noteIds, viewConfig, newAttributePosition);
 | 
			
		||||
    const dataTreeProps = useMemo<Options>(() => {
 | 
			
		||||
        if (!hasChildren) return {};
 | 
			
		||||
        return {
 | 
			
		||||
@@ -92,8 +59,7 @@ export default function TableView({ note, noteIds, notePath, viewConfig, saveCon
 | 
			
		||||
                        footerElement={<TableFooter note={note} />}
 | 
			
		||||
                        events={{
 | 
			
		||||
                            ...contextMenuEvents,
 | 
			
		||||
                            ...rowEditingEvents,
 | 
			
		||||
                            ...colEditingEvents
 | 
			
		||||
                            ...rowEditingEvents
 | 
			
		||||
                        }}
 | 
			
		||||
                        persistence {...persistenceProps}
 | 
			
		||||
                        layout="fitDataFill"
 | 
			
		||||
@@ -141,3 +107,44 @@ function usePersistence(initialConfig: TableConfig | null | undefined, saveConfi
 | 
			
		||||
    }, []);
 | 
			
		||||
    return { persistenceReaderFunc, persistenceWriterFunc };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function useData(note: FNote, noteIds: string[], viewConfig: TableConfig | undefined, newAttributePosition: RefObject<number | undefined>) {
 | 
			
		||||
    const [ maxDepth ] = useNoteLabelInt(note, "maxNestingDepth") ?? -1;
 | 
			
		||||
 | 
			
		||||
    const [ columnDefs, setColumnDefs ] = useState<ColumnDefinition[]>();
 | 
			
		||||
    const [ rowData, setRowData ] = useState<TableData[]>();
 | 
			
		||||
    const [ movableRows, setMovableRows ] = useState<boolean>();
 | 
			
		||||
    const [ hasChildren, setHasChildren ] = useState<boolean>();
 | 
			
		||||
 | 
			
		||||
    function refresh() {
 | 
			
		||||
        const info = getAttributeDefinitionInformation(note);
 | 
			
		||||
        buildRowDefinitions(note, info, maxDepth).then(({ definitions: rowData, hasSubtree: hasChildren, rowNumber }) => {
 | 
			
		||||
            const movableRows = canReorderRows(note) && !hasChildren;
 | 
			
		||||
            const columnDefs = buildColumnDefinitions({
 | 
			
		||||
                info,
 | 
			
		||||
                movableRows,
 | 
			
		||||
                existingColumnData: viewConfig?.tableData?.columns,
 | 
			
		||||
                rowNumberHint: rowNumber,
 | 
			
		||||
                position: newAttributePosition.current ?? undefined
 | 
			
		||||
            });
 | 
			
		||||
            setColumnDefs(columnDefs);
 | 
			
		||||
            setRowData(rowData);
 | 
			
		||||
            setMovableRows(movableRows);
 | 
			
		||||
            setHasChildren(hasChildren);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    useEffect(refresh, [ note, noteIds ]);
 | 
			
		||||
 | 
			
		||||
    // React to column changes.
 | 
			
		||||
    useTriliumEvent("entitiesReloaded", ({ loadResults}) => {
 | 
			
		||||
        if (loadResults.getAttributeRows().find(attr =>
 | 
			
		||||
            attr.type === "label" &&
 | 
			
		||||
            (attr.name?.startsWith("label:") || attr.name?.startsWith("relation:")) &&
 | 
			
		||||
            attributes.isAffecting(attr, note))) {
 | 
			
		||||
            refresh();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return { columnDefs, rowData, movableRows, hasChildren };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								apps/client/src/widgets/collections/table/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								apps/client/src/widgets/collections/table/utils.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
import FNote from "../../../entities/fnote";
 | 
			
		||||
import { Attribute } from "../../../services/attribute_parser";
 | 
			
		||||
 | 
			
		||||
export function getFAttributeFromField(parentNote: FNote, field: string) {
 | 
			
		||||
    const [ type, name ] = field.split(".", 2);
 | 
			
		||||
    const attrName = `${type.replace("s", "")}:${name}`;
 | 
			
		||||
    return parentNote.getLabel(attrName);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getAttributeFromField(parentNote: FNote, field: string): Attribute | undefined {
 | 
			
		||||
    const fAttribute = getFAttributeFromField(parentNote, field);
 | 
			
		||||
    if (fAttribute) {
 | 
			
		||||
        return {
 | 
			
		||||
            name: fAttribute.name,
 | 
			
		||||
            value: fAttribute.value,
 | 
			
		||||
            type: fAttribute.type,
 | 
			
		||||
            isInheritable: fAttribute.isInheritable
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    return undefined;
 | 
			
		||||
}
 | 
			
		||||
@@ -52,23 +52,6 @@ export default class TableColumnEditing extends Component {
 | 
			
		||||
        this.existingAttributeToEdit = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getFAttributeFromField(field: string) {
 | 
			
		||||
        const [ type, name ] = field.split(".", 2);
 | 
			
		||||
        const attrName = `${type.replace("s", "")}:${name}`;
 | 
			
		||||
        return this.parentNote.getLabel(attrName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getAttributeFromField(field: string): Attribute | undefined {
 | 
			
		||||
        const fAttribute = this.getFAttributeFromField(field);
 | 
			
		||||
        if (fAttribute) {
 | 
			
		||||
            return {
 | 
			
		||||
                name: fAttribute.name,
 | 
			
		||||
                value: fAttribute.value,
 | 
			
		||||
                type: fAttribute.type,
 | 
			
		||||
                isInheritable: fAttribute.isInheritable
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        return undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -123,11 +123,7 @@ export default class TableView extends ViewMode<StateInfo> {
 | 
			
		||||
        this.colEditing?.resetNewAttributePosition();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addNewRowCommand(e) { this.rowEditing?.addNewRowCommand(e); }
 | 
			
		||||
    addNewTableColumnCommand(e) { this.colEditing?.addNewTableColumnCommand(e); }
 | 
			
		||||
    deleteTableColumnCommand(e) { this.colEditing?.deleteTableColumnCommand(e); }
 | 
			
		||||
    updateAttributeListCommand(e) { this.colEditing?.updateAttributeListCommand(e); }
 | 
			
		||||
    saveAttributesCommand() { this.colEditing?.saveAttributesCommand(); }
 | 
			
		||||
 | 
			
		||||
    async #manageRowsUpdate() {
 | 
			
		||||
        if (!this.api) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user