Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: metadata gallery view
Browse files Browse the repository at this point in the history
杨国璇 committed Sep 7, 2024
1 parent 05e8573 commit 27cc63c
Showing 20 changed files with 411 additions and 159 deletions.
7 changes: 3 additions & 4 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion frontend/src/metadata/api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import axios from 'axios';
import cookie from 'react-cookies';
import { siteRoot } from '../utils/constants';
import { VIEW_TYPE_DEFAULT_BASIC_FILTER, VIEW_TYPE_DEFAULT_SORTS } from './metadata-view/_basic';

class MetadataManagerAPI {
init({ server, username, password, token }) {
@@ -114,7 +115,14 @@ class MetadataManagerAPI {

addView = (repoID, name, type = 'table') => {
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/views/';
const params = { name, type };
let params = {
name,
type,
data: {
basic_filters: VIEW_TYPE_DEFAULT_BASIC_FILTER[type],
sorts: VIEW_TYPE_DEFAULT_SORTS[type],
}
};
return this._sendPostRequest(url, params, { headers: { 'Content-type': 'application/json' } });
};

15 changes: 15 additions & 0 deletions frontend/src/metadata/metadata-view/_basic/constants/sort.js
Original file line number Diff line number Diff line change
@@ -19,12 +19,27 @@ const SORT_COLUMN_OPTIONS = [
CellType.RATE,
];

const GALLERY_SORT_COLUMN_OPTIONS = [
CellType.CTIME,
CellType.MTIME,
CellType.RATE,
CellType.NUMBER,
CellType.FILE_NAME,
];

const GALLERY_FIRST_SORT_COLUMN_OPTIONS = [
CellType.CTIME,
CellType.MTIME,
];

const TEXT_SORTER_COLUMN_TYPES = [CellType.TEXT];
const NUMBER_SORTER_COLUMN_TYPES = [CellType.NUMBER, CellType.RATE];

export {
SORT_TYPE,
SORT_COLUMN_OPTIONS,
GALLERY_SORT_COLUMN_OPTIONS,
GALLERY_FIRST_SORT_COLUMN_OPTIONS,
TEXT_SORTER_COLUMN_TYPES,
NUMBER_SORTER_COLUMN_TYPES,
};
24 changes: 24 additions & 0 deletions frontend/src/metadata/metadata-view/_basic/constants/view.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { PRIVATE_COLUMN_KEY } from './column';
import { FILTER_PREDICATE_TYPE } from './filter';
import { SORT_COLUMN_OPTIONS, GALLERY_SORT_COLUMN_OPTIONS, GALLERY_FIRST_SORT_COLUMN_OPTIONS, SORT_TYPE } from './sort';

export const VIEW_TYPE = {
TABLE: 'table',
GALLERY: 'gallery'
@@ -8,3 +12,23 @@ export const VIEW_TYPE_ICON = {
[VIEW_TYPE.GALLERY]: 'image',
'image': 'image'
};

export const VIEW_TYPE_DEFAULT_BASIC_FILTER = {
[VIEW_TYPE.TABLE]: [{ column_key: PRIVATE_COLUMN_KEY.IS_DIR, filter_predicate: FILTER_PREDICATE_TYPE.IS, filter_term: 'file' }],
[VIEW_TYPE.GALLERY]: [{ column_key: PRIVATE_COLUMN_KEY.FILE_TYPE, filter_predicate: FILTER_PREDICATE_TYPE.IS, filter_term: 'picture' }],
};

export const VIEW_TYPE_DEFAULT_SORTS = {
[VIEW_TYPE.TABLE]: [],
[VIEW_TYPE.GALLERY]: [{ column_key: PRIVATE_COLUMN_KEY.FILE_CTIME, sort_type: SORT_TYPE.DOWN }],
};

export const VIEW_SORT_COLUMN_OPTIONS = {
[VIEW_TYPE.TABLE]: SORT_COLUMN_OPTIONS,
[VIEW_TYPE.GALLERY]: GALLERY_SORT_COLUMN_OPTIONS,
};

export const VIEW_FIRST_SORT_COLUMN_OPTIONS = {
[VIEW_TYPE.TABLE]: SORT_COLUMN_OPTIONS,
[VIEW_TYPE.GALLERY]: GALLERY_FIRST_SORT_COLUMN_OPTIONS,
};
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
import React, { useState, useCallback, useEffect } from 'react';
import React, { useState, useCallback } from 'react';
import { Button, Input } from 'reactstrap';
import { EVENT_BUS_TYPE } from '../../constants';
import Icon from '../../../../components/icon';

import './slider-setter.css';

const SliderSetter = () => {
const [sliderValue, setSliderValue] = useState(() => {
const savedValue = localStorage.getItem('sliderValue');
return savedValue !== null ? parseInt(savedValue, 10) : 0;
const savedValue = window.sfMetadataContext.localStorage.getItem('zoom-gear', 0);
return savedValue || 0;
});

useEffect(() => {
localStorage.setItem('sliderValue', sliderValue);
}, [sliderValue]);

const handleGalleryColumnsChange = useCallback((e) => {
const adjust = parseInt(e.target.value, 10);
setSliderValue(adjust);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GALLERY_COLUMNS, adjust);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GALLERY_ZOOM_GEAR, adjust);
}, []);

const handleImageExpand = useCallback(() => {
const adjust = Math.min(sliderValue + 1, 2);
setSliderValue(adjust);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GALLERY_COLUMNS, adjust);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GALLERY_ZOOM_GEAR, adjust);
}, [sliderValue]);

const handleImageShrink = useCallback(() => {
const adjust = Math.max(sliderValue - 1, -2);
setSliderValue(adjust);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GALLERY_COLUMNS, adjust);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_GALLERY_ZOOM_GEAR, adjust);
}, [sliderValue]);

return (
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import { getValidSorts, CommonlyUsedHotkey } from '../../_basic';
import { gettext } from '../../utils';
import { SortPopover } from '../popover';

const SortSetter = ({ target, sorts: propsSorts, readOnly, columns, isNeedSubmit, wrapperClass, modifySorts }) => {
const SortSetter = ({ target, type, sorts: propsSorts, readOnly, columns, isNeedSubmit, wrapperClass, modifySorts }) => {
const [isShowSetter, setShowSetter] = useState(false);

const sorts = useMemo(() => {
@@ -54,6 +54,7 @@ const SortSetter = ({ target, sorts: propsSorts, readOnly, columns, isNeedSubmit
<SortPopover
isNeedSubmit={isNeedSubmit}
readOnly={readOnly}
type={type}
target={target}
columns={columns}
sorts={sorts}
@@ -68,10 +69,11 @@ const SortSetter = ({ target, sorts: propsSorts, readOnly, columns, isNeedSubmit


const propTypes = {
isNeedSubmit: PropTypes.bool,
readOnly: PropTypes.bool,
wrapperClass: PropTypes.string,
target: PropTypes.string,
isNeedSubmit: PropTypes.bool,
type: PropTypes.string,
sorts: PropTypes.array,
columns: PropTypes.array,
modifySorts: PropTypes.func,
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../../../utils';
import { CustomizeSelect, Icon } from '@seafile/sf-metadata-ui-component';

const OPTIONS = [
{ value: 'picture', name: gettext('Only pictures') },
{ value: 'video', name: gettext('Only videos') },
{ value: 'all', name: gettext('Pictures and videos') },
];

const FileTypeFilter = ({ readOnly, value = 'picture', onChange: onChangeAPI }) => {

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-name" title={name} aria-label={name}>{name}</div>
<div className="select-basic-filter-option-check-icon">
{value === o.value && (<Icon iconName="check-mark" />)}
</div>
</div>
)
};
});
}, [value]);

const displayValue = useMemo(() => {
const selectedOption = OPTIONS.find(o => o.value === value) || OPTIONS[2];
return {
label: (
<div>
{selectedOption.name}
</div>
)
};
}, [value]);

const onChange = useCallback((newValue) => {
if (newValue === value) return;
onChangeAPI(newValue);
}, [value, onChangeAPI]);

return (
<CustomizeSelect
readOnly={readOnly}
className="sf-metadata-basic-filters-select"
value={displayValue}
options={options}
onSelectOption={onChange}
component={{
DropDownIcon: (
<i className="sf3-font sf3-font-down"></i>
)
}}
/>
);
};

FileTypeFilter.propTypes = {
readOnly: PropTypes.bool,
value: PropTypes.string,
onChange: PropTypes.func,
};

export default FileTypeFilter;
Original file line number Diff line number Diff line change
@@ -55,3 +55,7 @@
.sf-metadata-basic-filters-select .sf-metadata-option-group {
margin-left: 6px;
}

.filter-group-basic .sf-metadata-filters-list {
min-height: unset;
}
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import { FormGroup, Label } from 'reactstrap';
import { gettext } from '../../../../utils';
import { PRIVATE_COLUMN_KEY } from '../../../../_basic';
import FileOrFolderFilter from './file-folder-filter';
import FileTypeFilter from './file-type-filter';

import './index.css';

@@ -17,27 +18,43 @@ const BasicFilters = ({ readOnly, filters = [], onChange }) => {
onChange(newFilters);
}, [filters, onChange]);

const onChangeFileTypeFilter = useCallback((newValue) => {
const filterIndex = filters.findIndex(filter => filter.column_key === PRIVATE_COLUMN_KEY.FILE_TYPE);
const filter = filters[filterIndex];
const newFilters = filters.slice(0);
newFilters[filterIndex] = { ...filter, filter_term: newValue };
onChange(newFilters);
}, [filters, onChange]);

return (
<FormGroup className="filter-group p-4">
<FormGroup className="filter-group-basic filter-group p-4">
<Label className="filter-group-name">{gettext('Basic')}</Label>
<div className="filter-group-container">
{filters.map(filter => {
const { column_key, filter_term } = filter;
if (column_key === PRIVATE_COLUMN_KEY.IS_DIR) {
return (
<FileOrFolderFilter key={column_key} readOnly={readOnly} value={filter_term} onChange={onChangeFileOrFolderFilter} />
);
}
return null;
})}
<div className="sf-metadata-filters-list">
{filters.map((filter, index) => {
const { column_key, filter_term } = filter;
if (column_key === PRIVATE_COLUMN_KEY.IS_DIR) {
return (
<FileOrFolderFilter key={column_key} readOnly={readOnly} value={filter_term} onChange={onChangeFileOrFolderFilter} />
);
}
if (column_key === PRIVATE_COLUMN_KEY.FILE_TYPE) {
return (
<FileTypeFilter key={column_key} readOnly={readOnly} value={filter_term} onChange={onChangeFileTypeFilter} />
);
}
return null;
})}
</div>
</div>
</FormGroup>
);
};

BasicFilters.propTypes = {
readOnly: PropTypes.bool,
value: PropTypes.string,
filters: PropTypes.array,
columns: PropTypes.array,
onChange: PropTypes.func,
};

Original file line number Diff line number Diff line change
@@ -5,8 +5,10 @@ import { Button, UncontrolledPopover } from 'reactstrap';
import { CustomizeAddTool, CustomizeSelect, Icon } from '@seafile/sf-metadata-ui-component';
import {
COLUMNS_ICON_CONFIG,
SORT_COLUMN_OPTIONS,
VIEW_SORT_COLUMN_OPTIONS,
VIEW_FIRST_SORT_COLUMN_OPTIONS,
SORT_TYPE,
VIEW_TYPE,
getColumnByKey,
} from '../../../_basic';
import { execSortsOperation, getDisplaySorts, isSortsEmpty, SORT_OPERATION } from './utils';
@@ -28,8 +30,9 @@ const SORT_TYPES = [

const propTypes = {
readOnly: PropTypes.bool,
target: PropTypes.string.isRequired,
isNeedSubmit: PropTypes.bool,
target: PropTypes.string.isRequired,
type: PropTypes.string,
sorts: PropTypes.array,
columns: PropTypes.array.isRequired,
onSortComponentToggle: PropTypes.func,
@@ -44,8 +47,10 @@ class SortPopover extends Component {

constructor(props) {
super(props);
const { sorts, columns } = this.props;
const { sorts, columns, type } = this.props;
this.sortTypeOptions = this.createSortTypeOptions();
this.supportFirstSortColumnOptions = VIEW_FIRST_SORT_COLUMN_OPTIONS[type || VIEW_TYPE.TABLE];
this.supportSortColumnOptions = VIEW_SORT_COLUMN_OPTIONS[type || VIEW_TYPE.TABLE];
this.columnsOptions = this.createColumnsOptions(columns);
this.state = {
sorts: getDisplaySorts(sorts, columns),
@@ -154,7 +159,7 @@ class SortPopover extends Component {
};

createColumnsOptions = (columns = []) => {
const sortableColumns = columns.filter(column => SORT_COLUMN_OPTIONS.includes(column.type));
const sortableColumns = columns.filter(column => this.supportSortColumnOptions.includes(column.type));
return sortableColumns.map((column) => {
const { type, name } = column;
return {
@@ -189,7 +194,7 @@ class SortPopover extends Component {

renderSortItem = (column, sort, index) => {
const { name, type } = column;
const { readOnly } = this.props;
const { readOnly, type: viewType } = this.props;
const selectedColumn = {
label: (
<Fragment>
@@ -205,11 +210,16 @@ class SortPopover extends Component {
label: <span className="select-option-name">{selectedTypeOption?.name || gettext('Up')}</span>
};

let columnsOptions = this.columnsOptions;
if (index === 0) {
columnsOptions = columnsOptions.filter(o => this.supportFirstSortColumnOptions.includes(o.value.column.type));
}

return (
<div key={'sort-item-' + index} className="sort-item">
{!readOnly &&
<div className="delete-sort" onClick={(event) => this.deleteSort(event, index)}>
<Icon iconName="fork-number"/>
<div className="delete-sort" onClick={!(viewType === VIEW_TYPE.GALLERY && index === 0) ? () => {} : (event) => this.deleteSort(event, index)}>
{!(viewType === VIEW_TYPE.GALLERY && index === 0) && <Icon iconName="fork-number"/>}
</div>
}
<div className="condition">
@@ -218,7 +228,7 @@ class SortPopover extends Component {
readOnly={readOnly}
value={selectedColumn}
onSelectOption={(value) => this.onSelectColumn(value, index)}
options={this.columnsOptions}
options={columnsOptions}
searchable={true}
searchPlaceholder={gettext('Search property')}
noOptionsPlaceholder={gettext('No results')}
Loading

0 comments on commit 27cc63c

Please sign in to comment.