diff --git a/frontend/src/assets/icons/eye-slash.svg b/frontend/src/assets/icons/eye-slash.svg new file mode 100644 index 0000000000..33373126c9 --- /dev/null +++ b/frontend/src/assets/icons/eye-slash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/icons/filter.svg b/frontend/src/assets/icons/filter.svg index d95a21617c..5ed48ca7b7 100644 --- a/frontend/src/assets/icons/filter.svg +++ b/frontend/src/assets/icons/filter.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/frontend/src/assets/icons/group.svg b/frontend/src/assets/icons/group.svg index fac73d4a02..f99da1c8ce 100644 --- a/frontend/src/assets/icons/group.svg +++ b/frontend/src/assets/icons/group.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/frontend/src/assets/icons/sort.svg b/frontend/src/assets/icons/sort.svg index 7f4a8c117f..4f58f1bfd6 100644 --- a/frontend/src/assets/icons/sort.svg +++ b/frontend/src/assets/icons/sort.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/frontend/src/components/cur-dir-path/dir-tool.js b/frontend/src/components/cur-dir-path/dir-tool.js index e22be58bfd..c95677895c 100644 --- a/frontend/src/components/cur-dir-path/dir-tool.js +++ b/frontend/src/components/cur-dir-path/dir-tool.js @@ -8,6 +8,7 @@ import SeahubPopover from '../common/seahub-popover'; import ListTagPopover from '../popover/list-tag-popover'; import ViewModes from '../../components/view-modes'; import { PRIVATE_FILE_TYPE } from '../../constants'; +import MetadataViewToolBar from '../../metadata/metadata-view/components/view-toolbar'; const propTypes = { repoID: PropTypes.string.isRequired, @@ -83,11 +84,20 @@ class DirTool extends React.Component { const { repoID, currentMode, currentPath } = this.props; const propertiesText = TextTranslation.PROPERTIES.value; const isFileExtended = currentPath === '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES; + + if (isFileExtended) { + return ( +
+ +
+ ); + } + return (
- {(!this.props.isCustomPermission && !isFileExtended) && + {(!this.props.isCustomPermission) && this.props.switchViewMode('detail')}> diff --git a/frontend/src/components/dir-view-mode/dir-column-file.css b/frontend/src/components/dir-view-mode/dir-column-file.css deleted file mode 100644 index 138636ed7b..0000000000 --- a/frontend/src/components/dir-view-mode/dir-column-file.css +++ /dev/null @@ -1,15 +0,0 @@ -.dir-column-file { - padding-right: 10px; - padding-left: 10px; - flex-direction: column; - align-items: center; -} - -.dir-column-file .dir-column-file-top { - width: 100%; - height: 10px; - z-index: 7; - transform: translateZ(1000px); - position: relative; - background: #ffffff; -} diff --git a/frontend/src/components/dir-view-mode/dir-column-file.js b/frontend/src/components/dir-view-mode/dir-column-file.js index fecd36b01a..7ea6adaf6b 100644 --- a/frontend/src/components/dir-view-mode/dir-column-file.js +++ b/frontend/src/components/dir-view-mode/dir-column-file.js @@ -5,8 +5,6 @@ import { Utils } from '../../utils/utils'; import { gettext, siteRoot, lang, mediaUrl } from '../../utils/constants'; import SeafileMarkdownViewer from '../seafile-markdown-viewer'; -import './dir-column-file.css'; - const propTypes = { path: PropTypes.string.isRequired, repoID: PropTypes.string.isRequired, @@ -60,12 +58,7 @@ class DirColumnFile extends React.Component { mediaUrl, }; - return ( -
-
- -
- ); + return (); } return ( diff --git a/frontend/src/metadata/metadata-tree-view/index.js b/frontend/src/metadata/metadata-tree-view/index.js index 2258ebf5d1..d86bf1639d 100644 --- a/frontend/src/metadata/metadata-tree-view/index.js +++ b/frontend/src/metadata/metadata-tree-view/index.js @@ -43,7 +43,7 @@ const MetadataTreeView = ({ repoID, currentPath, onNodeClick }) => { return (
-
+
-
+
{ const modifyFilters = useCallback((filters, filterConjunction) => { store.modifyFilters(filterConjunction, filters); - store.saveView(); }, [store]); const modifySorts = useCallback((sorts) => { store.modifySorts(sorts); - store.saveView(); }, [store]); const modifyGroupbys = useCallback(() => { @@ -154,9 +151,17 @@ const Container = () => { useEffect(() => { document.addEventListener('keydown', onKeyDown); const unsubscribeSelectCell = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.SELECT_CELL, onSelectCell); + const unsubscribeModifyFilters = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_FILTERS, modifyFilters); + const unsubscribeModifySorts = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_SORTS, modifySorts); + const unsubscribeModifyGroupbys = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_GROUPBYS, modifyGroupbys); + const unsubscribeModifyHiddenColumns = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_HIDDEN_COLUMNS, modifyHiddenColumns); return () => { document.removeEventListener('keydown', onKeyDown); unsubscribeSelectCell(); + unsubscribeModifyFilters(); + unsubscribeModifySorts(); + unsubscribeModifyGroupbys(); + unsubscribeModifyHiddenColumns(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -164,7 +169,6 @@ const Container = () => { return ( <>
-
{errorMsg && (
{gettext(errorMsg)}
)} {!errorMsg && ( @@ -188,9 +192,7 @@ const Container = () => {
- ); - }; export default Container; diff --git a/frontend/src/metadata/metadata-view/components/table/index.css b/frontend/src/metadata/metadata-view/components/table/index.css index d2c99ea564..87d423e981 100644 --- a/frontend/src/metadata/metadata-view/components/table/index.css +++ b/frontend/src/metadata/metadata-view/components/table/index.css @@ -16,15 +16,6 @@ align-items: center; } -.sf-metadata-wrapper .seatable-app-header.searcher-active .table-right-operations { - width: 100%; - margin-right: 10px; -} - -.sf-metadata-wrapper .seatable-app-header .table-left-operations .setting-item { - margin-right: 0 !important; -} - .sf-metadata-wrapper .table-left-operations .custom-filter-label { padding: 0 0.5rem; } @@ -186,7 +177,6 @@ } .sf-metadata-result-container { - flex: 1; overflow-x: scroll; overflow-y: hidden; } @@ -299,6 +289,11 @@ background-color: #fff; } +.sf-metadata-result-table-content .sf-metadata-result-table-row.sf-metadata-last-table-row { + height: 32px !important; + border-bottom: none; +} + .sf-metadata-result-table-cell.index { width: 90px; height: 100%; @@ -361,7 +356,7 @@ .sf-metadata-result-column-content .header-name-text.double { white-space: normal; display: -webkit-box; - -webkit-line-clamp: 2; + line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis; @@ -394,7 +389,6 @@ flex: 1 1; position: relative; overflow-y: scroll; - padding-bottom: 150px; } .sf-metadata-result-table-content .sf-metadata-result-loading { @@ -409,18 +403,6 @@ align-items: flex-start; } -.sf-metadata-result-footer { - position: relative; - height: 30px; - width: 100%; - overflow: hidden; - line-height: 30px; - background-color: #f9f9f9; - border-top: 1px solid #ddd; - display: flex; - flex-shrink: 0; -} - .sf-metadata-result-table-cell .cell-file-add { height: 31px; line-height: 31px; @@ -975,12 +957,6 @@ max-width: 250px; } -.sf-metadata-wrapper .sf-metadata-main, -.sf-metadata-wrapper .seatable-app-header { - border-left: 1px solid #ddd; - border-right: 1px solid #ddd; -} - .sf-metadata-wrapper .sf-metadata-main { flex: 1; overflow: hidden; diff --git a/frontend/src/metadata/metadata-view/components/table/table-main/records/record-footer/index.css b/frontend/src/metadata/metadata-view/components/table/table-main/records/record-footer/index.css index fba0063219..8e882803f0 100644 --- a/frontend/src/metadata/metadata-view/components/table/table-main/records/record-footer/index.css +++ b/frontend/src/metadata/metadata-view/components/table/table-main/records/record-footer/index.css @@ -1,3 +1,20 @@ +.sf-metadata-result-footer { + position: relative; + height: 32px; + width: 100%; + overflow: hidden; + line-height: 32px; + background-color: #f9f9f9; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + display: flex; + flex-shrink: 0; +} + +.sf-metadata-result-footer.at-border { + border-bottom: none; +} + .sf-metadata-result-footer .rows-record { width: 80px; padding-left: 8px; diff --git a/frontend/src/metadata/metadata-view/components/table/table-main/records/record-footer/index.js b/frontend/src/metadata/metadata-view/components/table/table-main/records/record-footer/index.js index f06741cdcb..434c4a64ad 100644 --- a/frontend/src/metadata/metadata-view/components/table/table-main/records/record-footer/index.js +++ b/frontend/src/metadata/metadata-view/components/table/table-main/records/record-footer/index.js @@ -8,11 +8,42 @@ import RecordMetrics from '../../../../../utils/record-metrics'; import { SEQUENCE_COLUMN_WIDTH, CANVAS_RIGHT_INTERVAL } from '../../../../../constants'; import { getRecordsFromSelectedRange } from '../../../../../utils/selected-cell-utils'; import { gettext } from '../../../../../../../utils/constants'; +import { addClassName, removeClassName } from '../../../../../utils'; import './index.css'; class RecordsFooter extends React.Component { + ref = null; + + componentDidMount() { + window.addEventListener('resize', this.calculateAtBorder); + } + + componentWillUnmount() { + window.removeEventListener('resize', this.calculateAtBorder); + } + + componentDidUpdate() { + this.calculateAtBorder(); + } + + calculateAtBorder = () => { + const { bottom } = this.ref.getBoundingClientRect(); + + // update classnames after records count change + const originClassName = this.ref ? this.ref.className : ''; + let newClassName; + if (bottom >= window.innerHeight) { + newClassName = addClassName(originClassName, 'at-border'); + } else { + newClassName = removeClassName(originClassName, 'at-border'); + } + if (newClassName !== originClassName && this.ref) { + this.ref.className = newClassName; + } + }; + onClick = () => { if (this.props.isLoadingMore) { return; @@ -105,7 +136,7 @@ class RecordsFooter extends React.Component { const recordWidth = (isLoadingMore || hasMore ? SEQUENCE_COLUMN_WIDTH + columns[0].width : SEQUENCE_COLUMN_WIDTH) + groupOffsetLeft; return ( -
+
this.ref = ref}>
{this.getRecord()} {!isLoadingMore && hasMore && diff --git a/frontend/src/metadata/metadata-view/components/table/table-main/records/records-body.js b/frontend/src/metadata/metadata-view/components/table/table-main/records/records-body.js index abd786fdc3..ad2bbbe5ce 100644 --- a/frontend/src/metadata/metadata-view/components/table/table-main/records/records-body.js +++ b/frontend/src/metadata/metadata-view/components/table/table-main/records/records-body.js @@ -100,7 +100,8 @@ class RecordsBody extends Component { const { startRenderIndex, endRenderIndex } = this.state; const contentScrollTop = this.resultContentRef.scrollTop; const start = Math.max(0, Math.floor(contentScrollTop / ROW_HEIGHT) - RENDER_MORE_NUMBER); - const end = Math.min(Math.ceil((contentScrollTop + this.resultContentRef.offsetHeight) / ROW_HEIGHT) + RENDER_MORE_NUMBER, recordIds.length); + const { height } = this.props.getTableContentRect(); + const end = Math.min(Math.ceil((contentScrollTop + height) / ROW_HEIGHT) + RENDER_MORE_NUMBER, recordIds.length); if (start !== startRenderIndex) { this.setState({ startRenderIndex: start }); } diff --git a/frontend/src/metadata/metadata-view/components/table/table-tool/index.js b/frontend/src/metadata/metadata-view/components/table/table-tool/index.js deleted file mode 100644 index 77606aa761..0000000000 --- a/frontend/src/metadata/metadata-view/components/table/table-tool/index.js +++ /dev/null @@ -1,75 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { FilterSetter, GroupbySetter, SortSetter, HideColumnSetter } from '../../data-process-setter'; -import { Z_INDEX } from '../../../_basic'; -import { EVENT_BUS_TYPE } from '../../../constants'; -import { useCollaborators } from '../../../hooks'; - -import './index.css'; - -const TableTool = ({ searcherActive, view, modifyFilters, modifySorts, modifyGroupbys, modifyHiddenColumns }) => { - - const columns = useMemo(() => { - return view.available_columns; - }, [view]); - - const { collaborators } = useCollaborators(); - - const onHeaderClick = useCallback(() => { - window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SELECT_NONE); - }, []); - - return ( -
-
- - - - -
-
-
- ); -}; - -TableTool.propTypes = { - searcherActive: PropTypes.bool, - view: PropTypes.object, - modifyFilters: PropTypes.func, - modifySorts: PropTypes.func, - modifyGroupbys: PropTypes.func, - modifyHiddenColumns: PropTypes.func, -}; - -export default TableTool; diff --git a/frontend/src/metadata/metadata-view/components/table/table-tool/index.css b/frontend/src/metadata/metadata-view/components/view-toolbar/index.css similarity index 86% rename from frontend/src/metadata/metadata-view/components/table/table-tool/index.css rename to frontend/src/metadata/metadata-view/components/view-toolbar/index.css index 223987f280..a05d85ae3c 100644 --- a/frontend/src/metadata/metadata-view/components/table/table-tool/index.css +++ b/frontend/src/metadata/metadata-view/components/view-toolbar/index.css @@ -1,18 +1,10 @@ .sf-metadata-tool { background-color: #fff; - border-bottom: 1px solid #e4e4e4; - border-top: 1px solid #ddd; - border-top-left-radius: 5px; - border-top-right-radius: 5px; display: flex; flex-shrink: 0; flex-wrap: nowrap; height: 48px; justify-content: space-between; - padding: 0 20px; - position: relative; - border-left: 1px solid #ddd; - border-right: 1px solid #ddd; } .sf-metadata-tool .sf-metadata-tool-left-operations, diff --git a/frontend/src/metadata/metadata-view/components/view-toolbar/index.js b/frontend/src/metadata/metadata-view/components/view-toolbar/index.js new file mode 100644 index 0000000000..5e2c0056fa --- /dev/null +++ b/frontend/src/metadata/metadata-view/components/view-toolbar/index.js @@ -0,0 +1,120 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import { FilterSetter, GroupbySetter, SortSetter, HideColumnSetter } from '../data-process-setter'; +import { EVENT_BUS_TYPE } from '../../constants'; + +import './index.css'; + +const ViewToolBar = () => { + const [isLoading, setLoading] = useState(true); + const [view, setView] = useState(null); + const [collaborators, setCollaborators] = useState([]); + + const columns = useMemo(() => { + if (!view) return []; + return view.available_columns; + }, [view]); + + const onHeaderClick = useCallback(() => { + window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SELECT_NONE); + }, []); + + const modifyFilters = useCallback((filters, filterConjunction) => { + window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_FILTERS, filters, filterConjunction); + }, []); + + const modifySorts = useCallback((sorts) => { + window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_SORTS, sorts); + }, []); + + const modifyGroupbys = useCallback(() => { + window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GROUPBYS); + }, []); + + const modifyHiddenColumns = useCallback(() => { + window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GROUPBYS); + }, []); + + const viewChange = useCallback((view) => { + setView(view); + }, []); + + useEffect(() => { + let timer = setInterval(() => { + if (window.sfMetadataContext) { + timer && clearInterval(timer); + timer = null; + setLoading(false); + setView(window.sfMetadataStore.data.view); + setCollaborators(window.sfMetadataStore?.collaborators || []); + } + }, 300); + return () => { + timer && clearInterval(timer); + }; + }, []); + + useEffect(() => { + if (isLoading) return; + const unsubscribeViewChange = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.VIEW_CHANGED, viewChange); + return () => { + unsubscribeViewChange(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isLoading]); + + if (!view) return null; + + return ( +
+
+ + + + +
+
+
+ ); +}; + +ViewToolBar.propTypes = { + view: PropTypes.object, + modifyFilters: PropTypes.func, + modifySorts: PropTypes.func, + modifyGroupbys: PropTypes.func, + modifyHiddenColumns: PropTypes.func, +}; + +export default ViewToolBar; diff --git a/frontend/src/metadata/metadata-view/constants/event-bus-type.js b/frontend/src/metadata/metadata-view/constants/event-bus-type.js index ede764d7bf..9a6faf0518 100644 --- a/frontend/src/metadata/metadata-view/constants/event-bus-type.js +++ b/frontend/src/metadata/metadata-view/constants/event-bus-type.js @@ -25,4 +25,13 @@ export const EVENT_BUS_TYPE = { DRAG_ENTER: 'drag_enter', COLLAPSE_ALL_GROUPS: 'collapse_all_groups', EXPAND_ALL_GROUPS: 'expand_all_groups', + + // modify view + MODIFY_FILTERS: 'modify_filters', + MODIFY_SORTS:'modify_sorts', + MODIFY_GROUPBYS:'modify_groupbys', + MODIFY_HIDDEN_COLUMNS:'modify_hidden_columns', + + // change + VIEW_CHANGED: 'view_changed', }; diff --git a/frontend/src/metadata/metadata-view/hooks/metadata.js b/frontend/src/metadata/metadata-view/hooks/metadata.js index 654aa2927f..655ce25766 100644 --- a/frontend/src/metadata/metadata-view/hooks/metadata.js +++ b/frontend/src/metadata/metadata-view/hooks/metadata.js @@ -37,6 +37,7 @@ export const MetadataProvider = ({ window.sfMetadataContext.init({ otherSettings: params }); const repoId = window.sfMetadataContext.getSetting('repoID'); storeRef.current = new Store({ context: window.sfMetadataContext, repoId }); + window.sfMetadataStore = storeRef.current; storeRef.current.initStartIndex(); storeRef.current.loadData(PER_LOAD_NUMBER).then(() => { setMetadata(storeRef.current.data); diff --git a/frontend/src/metadata/metadata-view/store/index.js b/frontend/src/metadata/metadata-view/store/index.js index 008d32e4ce..9893cc640c 100644 --- a/frontend/src/metadata/metadata-view/store/index.js +++ b/frontend/src/metadata/metadata-view/store/index.js @@ -34,6 +34,7 @@ class Store { saveView = () => { const { filters, sorts, gropbys, filter_conjunction } = this.data.view; const view = { filters, sorts, gropbys, filter_conjunction }; + window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.VIEW_CHANGED, this.data.view); this.context.localStorage.setItem('view', view); }; @@ -330,6 +331,7 @@ class Store { type, filter_conjunction: filterConjunction, filters, }); this.applyOperation(operation); + this.saveView(); } modifySorts(sorts) { @@ -338,6 +340,7 @@ class Store { type, sorts, }); this.applyOperation(operation); + this.saveView(); } modifyGroupbys(groupbys) { @@ -346,6 +349,7 @@ class Store { type, groupbys, }); this.applyOperation(operation); + this.saveView(); } modifyHiddenColumns(shown_column_keys) { @@ -354,6 +358,7 @@ class Store { type, shown_column_keys }); this.applyOperation(operation); + this.saveView(); } }