diff --git a/frontend/src/components/sf-table/constants/tree.js b/frontend/src/components/sf-table/constants/tree.js new file mode 100644 index 0000000000..118ae6a25f --- /dev/null +++ b/frontend/src/components/sf-table/constants/tree.js @@ -0,0 +1,12 @@ +export const TREE_NODE_KEY = { + ID: '_id', + KEY: 'node_key', + DEPTH: 'node_depth', + HAS_SUB_NODES: 'has_sub_nodes', +}; + +export const LOCAL_KEY_TREE_NODE_FOLDED = 'table_key_tree_node_folded_map'; + +export const NODE_ICON_LEFT_INDENT = 18; + +export const NODE_CONTENT_LEFT_INDENT = 22; diff --git a/frontend/src/components/sf-table/editors/editor-container/popup-editor-container.js b/frontend/src/components/sf-table/editors/editor-container/popup-editor-container.js index 37a589840f..356feb1cab 100644 --- a/frontend/src/components/sf-table/editors/editor-container/popup-editor-container.js +++ b/frontend/src/components/sf-table/editors/editor-container/popup-editor-container.js @@ -71,6 +71,7 @@ class PopupEditorContainer extends React.Component { editorContainer: document.body, modifyColumnData, editorPosition, + editingRowId: this.editingRowId, record, height, columns, diff --git a/frontend/src/components/sf-table/editors/tags-editor/delete-tags/index.js b/frontend/src/components/sf-table/editors/tags-editor/delete-tags/index.js index bf868bbfbd..5c760f3872 100644 --- a/frontend/src/components/sf-table/editors/tags-editor/delete-tags/index.js +++ b/frontend/src/components/sf-table/editors/tags-editor/delete-tags/index.js @@ -1,7 +1,7 @@ import React from './index'; import PropTypes from 'prop-types'; import { IconBtn } from '@seafile/sf-metadata-ui-component'; -import { getTagColor, getTagName } from '../../../../../tag/utils/cell/core'; +import { getTagColor, getTagName } from '../../../../../tag/utils/cell'; import { getRowById } from '../../../utils/table'; import './index.css'; diff --git a/frontend/src/components/sf-table/editors/tags-editor/index.js b/frontend/src/components/sf-table/editors/tags-editor/index.js index 16f7fa8794..1862493f59 100644 --- a/frontend/src/components/sf-table/editors/tags-editor/index.js +++ b/frontend/src/components/sf-table/editors/tags-editor/index.js @@ -6,7 +6,7 @@ import DeleteTags from './delete-tags'; import { Utils } from '../../../../utils/utils'; import { KeyCodes } from '../../../../constants'; import { gettext } from '../../../../utils/constants'; -import { getTagColor, getTagId, getTagName, getTagsByNameOrColor } from '../../../../tag/utils/cell/core'; +import { getTagColor, getTagId, getTagName, getTagsByNameOrColor } from '../../../../tag/utils/cell'; import { getRecordIdFromRecord } from '../../../../metadata/utils/cell'; import { SELECT_OPTION_COLORS } from '../../../../metadata/constants'; import { getRowById } from '../../utils/table'; diff --git a/frontend/src/components/sf-table/index.css b/frontend/src/components/sf-table/index.css index bffd652ae7..2e442725e7 100644 --- a/frontend/src/components/sf-table/index.css +++ b/frontend/src/components/sf-table/index.css @@ -85,6 +85,7 @@ } .sf-table-column-content { + position: relative; height: 100%; width: 100%; text-align: left; diff --git a/frontend/src/components/sf-table/index.js b/frontend/src/components/sf-table/index.js index 8be9fb41d3..71f754568d 100644 --- a/frontend/src/components/sf-table/index.js +++ b/frontend/src/components/sf-table/index.js @@ -4,6 +4,7 @@ import classnames from 'classnames'; import TableMain from './table-main'; import './index.css'; +import './tree.css'; const SFTable = ({ table, @@ -14,6 +15,10 @@ const SFTable = ({ groups, showSequenceColumn, isGroupView, + showRecordAsTree, + recordsTree, + treeNodeKeyRecordIdMap, + keyTreeNodeFoldedMap, noRecordsTipsText, isLoadingMoreRecords, hasMoreRecords, @@ -46,10 +51,22 @@ const SFTable = ({ return recordId && recordGetterById(recordId); }, [recordGetterById]); + const getTreeNodeByIndex = useCallback((nodeIndex) => { + if (!window.sfTableBody || !window.sfTableBody.getTreeNodeByIndex) return null; + return window.sfTableBody.getTreeNodeByIndex(nodeIndex); + }, []); + + const treeRecordGetter = useCallback((nodeIndex) => { + const node = getTreeNodeByIndex(nodeIndex); + const recordId = node && node._id; + return recordId && recordGetterById(recordId); + }, [getTreeNodeByIndex, recordGetterById]); + const recordGetterByIndex = useCallback(({ isGroupView, groupRecordIndex, recordIndex }) => { + if (showRecordAsTree) return treeRecordGetter(recordIndex); if (isGroupView) return groupRecordGetter(groupRecordIndex); return recordGetter(recordIndex); - }, [groupRecordGetter, recordGetter]); + }, [showRecordAsTree, groupRecordGetter, treeRecordGetter, recordGetter]); const beforeUnloadHandler = useCallback(() => { if (window.sfTableBody) { @@ -71,17 +88,21 @@ const SFTable = ({ recordsIds={recordsIds} groupbys={groupbys} groups={groups} + recordsTree={recordsTree} + keyTreeNodeFoldedMap={keyTreeNodeFoldedMap} showSequenceColumn={showSequenceColumn} isGroupView={isGroupView} noRecordsTipsText={noRecordsTipsText} hasMoreRecords={hasMoreRecords} isLoadingMoreRecords={isLoadingMoreRecords} showGridFooter={showGridFooter} + showRecordAsTree={showRecordAsTree} loadMore={loadMore} loadAll={loadAll} getTableContentRect={getTableContentRect} onGridKeyDown={onGridKeyDown} onGridKeyUp={onGridKeyUp} + getTreeNodeByIndex={getTreeNodeByIndex} recordGetterById={recordGetterById} recordGetterByIndex={recordGetterByIndex} /> @@ -121,6 +142,16 @@ SFTable.propTypes = { supportCut: PropTypes.bool, supportPaste: PropTypes.bool, supportDragFill: PropTypes.bool, + showRecordAsTree: PropTypes.bool, + /** + * recordsTree: [ + * { _id, node_depth, node_index, node_key, ... } + * ... + * ] + * keyTreeNodeFoldedMap: { [node_key]: true, ... } + */ + recordsTree: PropTypes.array, + keyTreeNodeFoldedMap: PropTypes.object, checkCanModifyRecord: PropTypes.func, checkCellValueChanged: PropTypes.func, // for complex cell value compare onGridKeyDown: PropTypes.func, @@ -134,6 +165,7 @@ SFTable.defaultProps = { groupbys: [], groups: [], isGroupView: false, + showRecordAsTree: false, showSequenceColumn: true, hasMoreRecords: false, isLoadingMoreRecords: false, diff --git a/frontend/src/components/sf-table/masks/interaction-masks/index.js b/frontend/src/components/sf-table/masks/interaction-masks/index.js index 9a83d0302d..a21e167cbf 100644 --- a/frontend/src/components/sf-table/masks/interaction-masks/index.js +++ b/frontend/src/components/sf-table/masks/interaction-masks/index.js @@ -20,7 +20,8 @@ import { getSelectedRangeDimensions, getSelectedRow, getSelectedColumn, getRecordsFromSelectedRange, getSelectedCellValue, checkIsSelectedCellEditable, } from '../../utils/selected-cell-utils'; -import RecordMetrics from '../../utils/record-metrics'; +import { RecordMetrics } from '../../utils/record-metrics'; +import { TreeMetrics } from '../../utils/tree-metrics'; import setEventTransfer from '../../utils/set-event-transfer'; import getEventTransfer from '../../utils/get-event-transfer'; import { getGroupRecordByIndex } from '../../utils/group-metrics'; @@ -486,10 +487,12 @@ class InteractionMasks extends React.Component { onCopy = (e) => { e.preventDefault(); - const { recordMetrics } = this.props; + const { showRecordAsTree, recordMetrics, treeMetrics, treeNodeKeyRecordIdMap } = this.props; // select the records to copy - const selectedRecordIds = RecordMetrics.getSelectedIds(recordMetrics); + // TODO: need copy each nodes from tree? + const selectedRecordIds = showRecordAsTree ? TreeMetrics.getSelectedIds(treeMetrics, treeNodeKeyRecordIdMap) : RecordMetrics.getSelectedIds(recordMetrics); + if (selectedRecordIds.length > 0) { this.copyRows(e, selectedRecordIds); return; @@ -1074,6 +1077,9 @@ class InteractionMasks extends React.Component { {isValidElement(contextMenu) && cloneElement(contextMenu, { selectedPosition: isSelectedSingleCell ? selectedPosition : null, selectedRange: !isSelectedSingleCell ? selectedRange : null, + showRecordAsTree: this.props.showRecordAsTree, + treeNodeKeyRecordIdMap: this.props.treeNodeKeyRecordIdMap, + treeMetrics: this.props.treeMetrics, onClearSelected: this.handleSelectCellsDelete, onCopySelected: this.onCopySelected, getTableContentRect: this.props.getTableContentRect, @@ -1097,6 +1103,9 @@ InteractionMasks.propTypes = { rowHeight: PropTypes.number, groupOffsetLeft: PropTypes.number, frozenColumnsWidth: PropTypes.number, + showRecordAsTree: PropTypes.bool, + treeNodeKeyRecordIdMap: PropTypes.object, + treeMetrics: PropTypes.object, enableCellSelect: PropTypes.bool, canModifyRecords: PropTypes.bool, getRowTop: PropTypes.func, diff --git a/frontend/src/components/sf-table/table-main/index.js b/frontend/src/components/sf-table/table-main/index.js index f6cecb81da..327594dc8b 100644 --- a/frontend/src/components/sf-table/table-main/index.js +++ b/frontend/src/components/sf-table/table-main/index.js @@ -8,6 +8,7 @@ import { GROUP_VIEW_OFFSET } from '../constants/group'; import { SEQUENCE_COLUMN_WIDTH } from '../constants/grid'; import { getCellValueByColumn } from '../utils/cell'; import GridUtils from '../utils/grid-utils'; +import { generateKeyTreeNodeRowIdMap } from '../utils/tree'; const TableMain = ({ table, @@ -21,8 +22,11 @@ const TableMain = ({ hasMoreRecords, isLoadingMoreRecords, showGridFooter, + recordsTree, + showRecordAsTree, loadMore, loadAll, + getTreeNodeByIndex, recordGetterByIndex, recordGetterById, getClientCellValueDisplayString, @@ -44,6 +48,15 @@ const TableMain = ({ return recordsIds.length; }, [recordsIds]); + const treeNodesCount = useMemo(() => { + return recordsTree.length; + }, [recordsTree]); + + const treeNodeKeyRecordIdMap = useMemo(() => { + // treeNodeKeyRecordIdMap: { [node_key]: _id, ... } + return generateKeyTreeNodeRowIdMap(recordsTree); + }, [recordsTree]); + const hasNoRecords = useMemo(() => { return recordsCount === 0 && !hasMoreRecords; }, [recordsCount, hasMoreRecords]); @@ -76,7 +89,7 @@ const TableMain = ({ }, [getClientCellValueDisplayString]); return ( -