From 77b46649204ce6df2ab8418eb8d5c4d1803da910 Mon Sep 17 00:00:00 2001 From: Aries Date: Sun, 29 Sep 2024 17:57:50 +0800 Subject: [PATCH] Feature/filter by file type (#6852) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update basic filters, add file type filter * feat: optimize code * feat: optimize code * feat: optimize code * feat: optimize code --------- Co-authored-by: 杨国璇 --- .../data-process-setter/filter-setter.js | 7 +- ...-filter.js => gallery-file-type-filter.js} | 6 +- .../filter-popover/basic-filters/index.css | 24 +++++- .../filter-popover/basic-filters/index.js | 15 ++-- .../basic-filters/table-file-type-filter.js | 77 +++++++++++++++++++ .../popover/filter-popover/index.js | 5 +- .../metadata/components/view-toolbar/index.js | 9 ++- frontend/src/metadata/constants/view.js | 6 +- frontend/src/metadata/model/metadata/view.js | 6 +- frontend/src/metadata/utils/column/index.js | 10 ++- 10 files changed, 141 insertions(+), 24 deletions(-) rename frontend/src/metadata/components/popover/filter-popover/basic-filters/{file-type-filter.js => gallery-file-type-filter.js} (91%) create mode 100644 frontend/src/metadata/components/popover/filter-popover/basic-filters/table-file-type-filter.js diff --git a/frontend/src/metadata/components/data-process-setter/filter-setter.js b/frontend/src/metadata/components/data-process-setter/filter-setter.js index 4465dea34d..83f8db7a35 100644 --- a/frontend/src/metadata/components/data-process-setter/filter-setter.js +++ b/frontend/src/metadata/components/data-process-setter/filter-setter.js @@ -7,6 +7,7 @@ import { FilterPopover } from '../popover'; import { getValidFilters } from '../../utils/filter'; import { gettext } from '../../../utils/constants'; import { isEnter, isSpace } from '../../utils/hotkey'; +import { VIEW_TYPE } from '../../constants'; const FilterSetter = ({ readOnly, @@ -20,7 +21,8 @@ const FilterSetter = ({ target, filterConjunction, basicFilters, - modifyFilters + modifyFilters, + viewType, }) => { const [isShowSetter, setShowSetter] = useState(false); @@ -84,6 +86,7 @@ const FilterSetter = ({ hidePopover={onSetterToggle} update={onChange} isPre={isPre} + viewType={viewType} /> } @@ -104,12 +107,14 @@ FilterSetter.propTypes = { collaborators: PropTypes.array, isPre: PropTypes.bool, basicFilters: PropTypes.array, + viewType: PropTypes.string, }; FilterSetter.defaultProps = { target: 'sf-metadata-filter-popover', isNeedSubmit: false, basicFilters: [], + viewType: VIEW_TYPE.TABLE, }; export default FilterSetter; diff --git a/frontend/src/metadata/components/popover/filter-popover/basic-filters/file-type-filter.js b/frontend/src/metadata/components/popover/filter-popover/basic-filters/gallery-file-type-filter.js similarity index 91% rename from frontend/src/metadata/components/popover/filter-popover/basic-filters/file-type-filter.js rename to frontend/src/metadata/components/popover/filter-popover/basic-filters/gallery-file-type-filter.js index fd3063475d..115ed9774b 100644 --- a/frontend/src/metadata/components/popover/filter-popover/basic-filters/file-type-filter.js +++ b/frontend/src/metadata/components/popover/filter-popover/basic-filters/gallery-file-type-filter.js @@ -9,7 +9,7 @@ const OPTIONS = [ { value: 'all', name: gettext('Pictures and videos') }, ]; -const FileTypeFilter = ({ readOnly, value = 'picture', onChange: onChangeAPI }) => { +const GalleryFileTypeFilter = ({ readOnly, value = 'picture', onChange: onChangeAPI }) => { const options = useMemo(() => { return OPTIONS.map(o => { @@ -60,10 +60,10 @@ const FileTypeFilter = ({ readOnly, value = 'picture', onChange: onChangeAPI }) ); }; -FileTypeFilter.propTypes = { +GalleryFileTypeFilter.propTypes = { readOnly: PropTypes.bool, value: PropTypes.string, onChange: PropTypes.func, }; -export default FileTypeFilter; +export default GalleryFileTypeFilter; diff --git a/frontend/src/metadata/components/popover/filter-popover/basic-filters/index.css b/frontend/src/metadata/components/popover/filter-popover/basic-filters/index.css index 57fd3f4ef7..a6ac0b8397 100644 --- a/frontend/src/metadata/components/popover/filter-popover/basic-filters/index.css +++ b/frontend/src/metadata/components/popover/filter-popover/basic-filters/index.css @@ -25,6 +25,10 @@ margin-right: 8px; } +.sf-metadata-basic-filters-select .selected-option .custom-select-dropdown-icon { + margin-left: 0; +} + .select-basic-filter-option { width: 100%; display: flex; @@ -52,10 +56,22 @@ fill: #fff; } -.sf-metadata-basic-filters-select .sf-metadata-option-group { - margin-left: 6px; -} - .filter-group-basic .sf-metadata-filters-list { min-height: unset; + display: flex; +} + +.sf-metadata-table-view-basic-filter-file-type-select .sf-metadata-option { + padding: 0.25rem 1rem; +} + +.sf-metadata-table-view-basic-filter-file-type-select .select-basic-filter-option .select-basic-filter-option-checkbox { + display: inline-flex; + align-items: center; + flex-shrink: 0; +} + +.sf-metadata-table-view-basic-filter-file-type-select .select-basic-filter-option .select-basic-filter-option-checkbox, +.sf-metadata-table-view-basic-filter-file-type-select .select-basic-filter-option .select-basic-filter-option-checkbox input { + cursor: inherit; } diff --git a/frontend/src/metadata/components/popover/filter-popover/basic-filters/index.js b/frontend/src/metadata/components/popover/filter-popover/basic-filters/index.js index a92148e239..83b596a9d7 100644 --- a/frontend/src/metadata/components/popover/filter-popover/basic-filters/index.js +++ b/frontend/src/metadata/components/popover/filter-popover/basic-filters/index.js @@ -2,13 +2,14 @@ import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; import { FormGroup, Label } from 'reactstrap'; import FileOrFolderFilter from './file-folder-filter'; -import FileTypeFilter from './file-type-filter'; +import TableFileTypeFilter from './table-file-type-filter'; +import GalleryFileTypeFilter from './gallery-file-type-filter'; import { gettext } from '../../../../../utils/constants'; -import { PRIVATE_COLUMN_KEY } from '../../../../constants'; +import { PRIVATE_COLUMN_KEY, VIEW_TYPE } from '../../../../constants'; import './index.css'; -const BasicFilters = ({ readOnly, filters = [], onChange }) => { +const BasicFilters = ({ readOnly, filters = [], onChange, viewType }) => { const onChangeFileOrFolderFilter = useCallback((newValue) => { const filterIndex = filters.findIndex(filter => filter.column_key === PRIVATE_COLUMN_KEY.IS_DIR); @@ -31,7 +32,7 @@ const BasicFilters = ({ readOnly, filters = [], onChange }) => {
- {filters.map((filter, index) => { + {filters.map((filter) => { const { column_key, filter_term } = filter; if (column_key === PRIVATE_COLUMN_KEY.IS_DIR) { return ( @@ -39,9 +40,8 @@ const BasicFilters = ({ readOnly, filters = [], onChange }) => { ); } if (column_key === PRIVATE_COLUMN_KEY.FILE_TYPE) { - return ( - - ); + const FileTypeFilter = viewType === VIEW_TYPE.GALLERY ? GalleryFileTypeFilter : TableFileTypeFilter; + return (); } return null; })} @@ -56,6 +56,7 @@ BasicFilters.propTypes = { filters: PropTypes.array, columns: PropTypes.array, onChange: PropTypes.func, + viewType: PropTypes.oneOf(Object.values(VIEW_TYPE)), }; export default BasicFilters; diff --git a/frontend/src/metadata/components/popover/filter-popover/basic-filters/table-file-type-filter.js b/frontend/src/metadata/components/popover/filter-popover/basic-filters/table-file-type-filter.js new file mode 100644 index 0000000000..667433e7e6 --- /dev/null +++ b/frontend/src/metadata/components/popover/filter-popover/basic-filters/table-file-type-filter.js @@ -0,0 +1,77 @@ +import React, { useCallback, useMemo } from 'react'; +import PropTypes from 'prop-types'; +import { CustomizeSelect } from '@seafile/sf-metadata-ui-component'; +import { gettext } from '../../../../../utils/constants'; +import { getFileTypeColumnOptions } from '../../../../utils/column'; + +const TableFileTypeFilter = ({ readOnly, value, onChange: onChangeAPI }) => { + + const OPTIONS = useMemo(() => { + const optionsMap = getFileTypeColumnOptions(); + let options = []; + for (const [key, option] of Object.entries(optionsMap)) { + options.push({ value: key, name: option.name }); + } + return options; + }, []); + + const options = useMemo(() => { + return OPTIONS.map(o => { + const { name } = o; + return { + value: o.value, + label: ( +
+
+ +
+
{name}
+
+ ) + }; + }); + }, [OPTIONS, value]); + + const displayValue = useMemo(() => { + const selectedOptions = OPTIONS.filter(o => value.includes(o.value)); + return { + label: ( +
+ {selectedOptions.length > 0 ? selectedOptions.map(o => o.name).join(', ') : gettext('File type')} +
+ ) + }; + }, [OPTIONS, value]); + + const onChange = useCallback((newValue) => { + if (value.includes(newValue)) { + onChangeAPI(value.filter(v => v !== newValue)); + } else { + onChangeAPI([...value, newValue]); + } + }, [value, onChangeAPI]); + + return ( + + ) + }} + /> + ); +}; + +TableFileTypeFilter.propTypes = { + readOnly: PropTypes.bool, + value: PropTypes.array, + onChange: PropTypes.func, +}; + +export default TableFileTypeFilter; diff --git a/frontend/src/metadata/components/popover/filter-popover/index.js b/frontend/src/metadata/components/popover/filter-popover/index.js index aea0f82977..b35acd5ba0 100644 --- a/frontend/src/metadata/components/popover/filter-popover/index.js +++ b/frontend/src/metadata/components/popover/filter-popover/index.js @@ -153,7 +153,7 @@ class FilterPopover extends Component { }; render() { - const { readOnly, target, columns, placement } = this.props; + const { readOnly, target, columns, placement, viewType } = this.props; const { filters, filterConjunction, basicFilters } = this.state; const canAddFilter = columns.length > 0; return ( @@ -168,7 +168,7 @@ class FilterPopover extends Component { > {({ scheduleUpdate }) => (
this.dtablePopoverRef = ref} onClick={this.onPopoverInsideClick} className={this.props.filtersClassName}> - +
@@ -222,6 +222,7 @@ FilterPopover.propTypes = { basicFilters: PropTypes.array, hidePopover: PropTypes.func, update: PropTypes.func, + viewType: PropTypes.string, }; export default FilterPopover; diff --git a/frontend/src/metadata/components/view-toolbar/index.js b/frontend/src/metadata/components/view-toolbar/index.js index 7ec417a3f3..b4ab8b9074 100644 --- a/frontend/src/metadata/components/view-toolbar/index.js +++ b/frontend/src/metadata/components/view-toolbar/index.js @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; import { GalleryGroupBySetter, GallerySliderSetter, FilterSetter, GroupbySetter, SortSetter, HideColumnSetter } from '../data-process-setter'; -import { EVENT_BUS_TYPE, VIEW_TYPE } from '../../constants'; +import { EVENT_BUS_TYPE, PRIVATE_COLUMN_KEY, VIEW_TYPE } from '../../constants'; import './index.css'; @@ -14,6 +14,10 @@ const ViewToolBar = ({ viewId }) => { return view.columns; }, [view]); + const filterColumns = useMemo(() => { + return viewColumns.filter(c => c.key !== PRIVATE_COLUMN_KEY.FILE_TYPE); + }, [viewColumns]); + const onHeaderClick = useCallback(() => { window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SELECT_NONE); }, []); @@ -86,9 +90,10 @@ const ViewToolBar = ({ viewId }) => { filterConjunction={view.filter_conjunction} basicFilters={view.basic_filters} filters={view.filters} - columns={viewColumns} + columns={filterColumns} modifyFilters={modifyFilters} collaborators={collaborators} + viewType={viewType} /> 0 ? object.basic_filters : VIEW_TYPE_DEFAULT_BASIC_FILTER[this.type]; + const defaultBasicFilters = VIEW_TYPE_DEFAULT_BASIC_FILTER[this.type]; + this.basic_filters = object.basic_filters && object.basic_filters.length > 0 ? object.basic_filters : defaultBasicFilters; + if (this.basic_filters.length !== defaultBasicFilters.length) { + this.basic_filters = [...this.basic_filters, ...defaultBasicFilters.slice(this.basic_filters.length)]; + } // sort this.sorts = object.sorts && object.sorts.length > 0 ? object.sorts : VIEW_TYPE_DEFAULT_SORTS[this.type]; diff --git a/frontend/src/metadata/utils/column/index.js b/frontend/src/metadata/utils/column/index.js index 41e64fb10a..377b1f6122 100644 --- a/frontend/src/metadata/utils/column/index.js +++ b/frontend/src/metadata/utils/column/index.js @@ -262,9 +262,8 @@ export const getNormalizedColumnType = (key, type) => { } }; -const getFileTypeColumnData = (column) => { - const { data } = column; - const _OPTIONS = { +export const getFileTypeColumnOptions = () => { + return { [PREDEFINED_FILE_TYPE_OPTION_KEY.PICTURE]: { name: gettext('Picture'), color: '#FFFCB5', textColor: '#202428' }, [PREDEFINED_FILE_TYPE_OPTION_KEY.DOCUMENT]: { name: gettext('Document'), color: '#B7CEF9', textColor: '#202428' }, [PREDEFINED_FILE_TYPE_OPTION_KEY.VIDEO]: { name: gettext('Video'), color: '#9860E5', textColor: '#FFFFFF', borderColor: '#844BD2' }, @@ -272,6 +271,11 @@ const getFileTypeColumnData = (column) => { [PREDEFINED_FILE_TYPE_OPTION_KEY.CODE]: { name: gettext('Code'), color: '#4ad8fb', textColor: '#FFFFFF', borderColor: '#4283e5' }, [PREDEFINED_FILE_TYPE_OPTION_KEY.COMPRESSED]: { name: gettext('Compressed'), color: '#4a9afb', textColor: '#FFFFFF', borderColor: '#da42e5' }, }; +}; + +const getFileTypeColumnData = (column) => { + const { data } = column; + const _OPTIONS = getFileTypeColumnOptions(); let newData = { ...data }; newData.options = Array.isArray(data.options) ? data.options.map(o => { return { ...o, ..._OPTIONS[o.id] };