1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-27 23:56:18 +00:00

Feature/filter by file type (#6852)

* update basic filters, add file type filter

* feat: optimize code

* feat: optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
Aries
2024-09-29 17:57:50 +08:00
committed by GitHub
parent 2a4949f518
commit 77b4664920
10 changed files with 141 additions and 24 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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 }) => {
<Label className="filter-group-name">{gettext('Basic')}</Label>
<div className="filter-group-container">
<div className="sf-metadata-filters-list">
{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 (
<FileTypeFilter key={column_key} readOnly={readOnly} value={filter_term} onChange={onChangeFileTypeFilter} />
);
const FileTypeFilter = viewType === VIEW_TYPE.GALLERY ? GalleryFileTypeFilter : TableFileTypeFilter;
return (<FileTypeFilter key={column_key} readOnly={readOnly} value={filter_term} onChange={onChangeFileTypeFilter} />);
}
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;

View File

@@ -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: (
<div className="select-basic-filter-option">
<div className="select-basic-filter-option-checkbox mr-2">
<input type="checkbox" checked={value.includes(o.value)} readOnly />
</div>
<div className="select-basic-filter-option-name" title={name} aria-label={name}>{name}</div>
</div>
)
};
});
}, [OPTIONS, value]);
const displayValue = useMemo(() => {
const selectedOptions = OPTIONS.filter(o => value.includes(o.value));
return {
label: (
<div className="select-basic-filter-display-name">
{selectedOptions.length > 0 ? selectedOptions.map(o => o.name).join(', ') : gettext('File type')}
</div>
)
};
}, [OPTIONS, value]);
const onChange = useCallback((newValue) => {
if (value.includes(newValue)) {
onChangeAPI(value.filter(v => v !== newValue));
} else {
onChangeAPI([...value, newValue]);
}
}, [value, onChangeAPI]);
return (
<CustomizeSelect
readOnly={readOnly}
className="sf-metadata-basic-filters-select sf-metadata-table-view-basic-filter-file-type-select ml-4"
value={displayValue}
options={options}
onSelectOption={onChange}
supportMultipleSelect={true}
component={{
DropDownIcon: (
<i className="sf3-font sf3-font-down"></i>
)
}}
/>
);
};
TableFileTypeFilter.propTypes = {
readOnly: PropTypes.bool,
value: PropTypes.array,
onChange: PropTypes.func,
};
export default TableFileTypeFilter;

View File

@@ -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 }) => (
<div ref={ref => this.dtablePopoverRef = ref} onClick={this.onPopoverInsideClick} className={this.props.filtersClassName}>
<BasicFilters filters={basicFilters} onChange={this.onBasicFilterChange} />
<BasicFilters filters={basicFilters} onChange={this.onBasicFilterChange} viewType={viewType}/>
<FormGroup className="filter-group-advanced filter-group mb-0">
<Label className="filter-group-name">{gettext('Advanced')}</Label>
<div className="filter-group-container">
@@ -222,6 +222,7 @@ FilterPopover.propTypes = {
basicFilters: PropTypes.array,
hidePopover: PropTypes.func,
update: PropTypes.func,
viewType: PropTypes.string,
};
export default FilterPopover;

View File

@@ -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}
/>
<SortSetter
isNeedSubmit={true}

View File

@@ -19,7 +19,11 @@ export const VIEW_TYPE_DEFAULT_BASIC_FILTER = {
column_key: PRIVATE_COLUMN_KEY.IS_DIR,
filter_predicate: FILTER_PREDICATE_TYPE.IS,
filter_term: 'file'
}
}, {
column_key: PRIVATE_COLUMN_KEY.FILE_TYPE,
filter_predicate: FILTER_PREDICATE_TYPE.IS_ANY_OF,
filter_term: []
},
],
[VIEW_TYPE.GALLERY]: [
{

View File

@@ -15,7 +15,11 @@ class View {
this.filters = object.filters || [];
this.filter_conjunction = object.filter_conjunction || 'Or';
this.basic_filters = object.basic_filters && object.basic_filters.length > 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];

View File

@@ -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] };