Skip to content

Commit

Permalink
feat: metadata multiple select
Browse files Browse the repository at this point in the history
  • Loading branch information
杨国璇 authored and 杨国璇 committed Aug 16, 2024
1 parent 0142fe4 commit 7a47c64
Show file tree
Hide file tree
Showing 46 changed files with 899 additions and 74 deletions.
8 changes: 4 additions & 4 deletions frontend/package-lock.json

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

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@seafile/sdoc-editor": "1.0.50",
"@seafile/seafile-calendar": "0.0.12",
"@seafile/seafile-editor": "1.0.109",
"@seafile/sf-metadata-ui-component": "0.0.21",
"@seafile/sf-metadata-ui-component": "0.0.22",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/react-codemirror": "^4.19.4",
"axios": "^1.7.3",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/assets/icons/multiple-select.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion frontend/src/components/dir-view-mode/dir-views.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const DirViews = ({ userPerm, repoID, currentPath, currentRepoInfo }) => {
return [
{ key: 'extended-properties', value: gettext('Extended properties') }
];
}, [enableMetadataManagement, userPerm]);
}, [enableMetadataManagement, currentRepoInfo]);

const moreOperationClick = useCallback((operationKey) => {
if (operationKey === 'extended-properties') {
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/metadata/metadata-details/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import toaster from '../../components/toast';
import { gettext } from '../../utils/constants';
import { DetailEditor, CellFormatter } from '../metadata-view';
import { getColumnOriginName } from '../metadata-view/utils/column-utils';
import { CellType, getColumnOptions, getOptionName, PREDEFINED_COLUMN_KEYS } from '../metadata-view/_basic';
import { CellType, getColumnOptions, getOptionName, PREDEFINED_COLUMN_KEYS, getColumnOptionNamesByIds } from '../metadata-view/_basic';

import './index.css';

Expand Down Expand Up @@ -47,6 +47,8 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType, emptyTip }) =
if (!PREDEFINED_COLUMN_KEYS.includes(field.key) && field.type === CellType.SINGLE_SELECT) {
const options = getColumnOptions(field);
update = { [fileName]: getOptionName(options, newValue) };
} else if (field.type === CellType.MULTIPLE_SELECT) {
update = { [fileName]: newValue ? getColumnOptionNamesByIds(field, newValue) : [] };
}
metadataAPI.modifyRecord(repoID, record._id, update, record._obj_id).then(res => {
const newMetadata = { ...metadata, record: { ...record, ...update } };
Expand All @@ -73,6 +75,9 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType, emptyTip }) =
update = { [fileName]: newOption.id };
if (!PREDEFINED_COLUMN_KEYS.includes(fieldKey) && newField.type === CellType.SINGLE_SELECT) {
update = { [fileName]: getOptionName(options, newOption.id) };
} else if (newField.type === CellType.MULTIPLE_SELECT) {
const oldValue = getCellValueByColumn(record, newField) || [];
update = { [fileName]: [...oldValue, newOption.name] };
}
return metadataAPI.modifyRecord(repoID, record._id, update, record._obj_id);
}).then(res => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const NOT_SUPPORT_EDIT_COLUMN_TYPE_MAP = {

const MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP = {
[CellType.COLLABORATOR]: true,
[CellType.MULTIPLE_SELECT]: true,
};
const SINGLE_CELL_VALUE_COLUMN_TYPE_MAP = {
[CellType.TEXT]: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const COLUMNS_ICON_CONFIG = {
[CellType.DATE]: 'date',
[CellType.LONG_TEXT]: 'long-text',
[CellType.SINGLE_SELECT]: 'single-select',
[CellType.MULTIPLE_SELECT]: 'multiple-select',
[CellType.NUMBER]: 'number',
[CellType.GEOLOCATION]: 'location',
};
Expand All @@ -30,6 +31,7 @@ const COLUMNS_ICON_NAME = {
[CellType.DATE]: 'Date',
[CellType.LONG_TEXT]: 'Long text',
[CellType.SINGLE_SELECT]: 'Single select',
[CellType.MULTIPLE_SELECT]: 'Multiple select',
[CellType.NUMBER]: 'Number',
[CellType.GEOLOCATION]: 'Geolocation',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const CellType = {
DATE: 'date',
LONG_TEXT: 'long-text',
SINGLE_SELECT: 'single-select',
MULTIPLE_SELECT: 'multiple-select',
NUMBER: 'number',
GEOLOCATION: 'geolocation',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ const FILTER_COLUMN_OPTIONS = {
FILTER_PREDICATE_TYPE.NOT_EMPTY,
],
},
[CellType.MULTIPLE_SELECT]: {
filterPredicateList: [
FILTER_PREDICATE_TYPE.HAS_ANY_OF,
FILTER_PREDICATE_TYPE.HAS_ALL_OF,
FILTER_PREDICATE_TYPE.HAS_NONE_OF,
FILTER_PREDICATE_TYPE.IS_EXACTLY,
FILTER_PREDICATE_TYPE.EMPTY,
FILTER_PREDICATE_TYPE.NOT_EMPTY,
],
},
[CellType.CTIME]: {
filterPredicateList: datePredicates,
filterTermModifierList: dateTermModifiers,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const SORT_COLUMN_OPTIONS = [
CellType.TEXT,
CellType.DATE,
CellType.SINGLE_SELECT,
CellType.MULTIPLE_SELECT,
CellType.COLLABORATOR,
CellType.CHECKBOX,
CellType.NUMBER,
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/metadata/metadata-view/_basic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,6 @@ export {
isNumber,
getCellValueDisplayString,
getCellValueStringResult,
getColumnOptionNamesByIds,
getColumnOptionIdsByNames,
} from './utils';
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export {
export {
getOption,
getColumnOptionNameById,
getColumnOptionNamesByIds,
getColumnOptionIdsByNames,
getOptionName,
getMultipleOptionName,
} from './option';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,46 @@ const getColumnOptionNameById = (column, optionId) => {
return getOptionName(options, optionId);
};

/**
* Get column option name by id
* @param {object} column e.g. { data: { options, ... }, ... }
* @param {array} optionIds
* @returns options name, array
*/
const getColumnOptionNamesByIds = (column, optionIds) => {
if (PRIVATE_COLUMN_KEYS.includes(column.key)) return optionIds;
if (!Array.isArray(optionIds) || optionIds.length === 0) return [];
const options = getColumnOptions(column);
if (!Array.isArray(options) || options.length === 0) return [];
return optionIds.map(optionId => getOptionName(options, optionId)).filter(name => name);
};

/**
* Get column option name by id
* @param {object} column e.g. { data: { options, ... }, ... }
* @param {array} option names
* @returns options id, array
*/
const getColumnOptionIdsByNames = (column, names) => {
if (PRIVATE_COLUMN_KEYS.includes(column.key)) return names;
if (!Array.isArray(names) || names.length === 0) return [];
const options = getColumnOptions(column);
if (!Array.isArray(options) || options.length === 0) return [];
return names.map(name => {
const option = getOption(options, name);
if (option) return option.id;
return null;
}).filter(name => name);
};

/**
* Get concatenated options names of given ids.
* @param {array} options e.g. [ { id, color, name, ... }, ... ]
* @param {array} targetOptionsIds e.g. [ option.id, ... ]
* @returns concatenated options names, string. e.g. 'name1, name2'
*/
const getMultipleOptionName = (options, targetOptionsIds) => {
const getMultipleOptionName = (column, targetOptionsIds) => {
const options = getColumnOptions(column);
if (!Array.isArray(targetOptionsIds) || !Array.isArray(options)) return '';
const selectedOptions = options.filter((option) => targetOptionsIds.includes(option.id));
if (selectedOptions.length === 0) return '';
Expand All @@ -53,5 +86,7 @@ export {
getOption,
getOptionName,
getColumnOptionNameById,
getColumnOptionNamesByIds,
getColumnOptionIdsByNames,
getMultipleOptionName,
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ export {
getGeolocationByGranularity,
getFloatNumber,
isNumber,
getColumnOptionNamesByIds,
getColumnOptionIdsByNames,
} from './column';
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { checkboxFilter } from './checkbox';
export { singleSelectFilter } from './single-select';
export { collaboratorFilter } from './collaborator';
export { numberFilter } from './number';
export { multipleSelectFilter } from './multiple-select';
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { FILTER_PREDICATE_TYPE } from '../../../constants/filter/filter-predicate';

/**
* Filter multiple-select
* @param {array} optionIds e.g. [ option.id, ... ]
* @param {string} filter_predicate
* @param {array} filter_term option ids
* @returns bool
*/
const multipleSelectFilter = (optionIds, { filter_predicate, filter_term }) => {
switch (filter_predicate) {
case FILTER_PREDICATE_TYPE.HAS_ANY_OF: {
return (
filter_term.length === 0
|| (Array.isArray(optionIds) && optionIds.some((optionId) => filter_term.includes(optionId)))
);
}
case FILTER_PREDICATE_TYPE.HAS_ALL_OF: {
return (
filter_term.length === 0
|| (Array.isArray(optionIds) && filter_term.every((optionId) => optionIds.includes(optionId)))
);
}
case FILTER_PREDICATE_TYPE.HAS_NONE_OF: {
if (filter_term.length === 0 || !Array.isArray(optionIds) || optionIds.length === 0) {
return true;
}
return filter_term.every((optionId) => optionIds.indexOf(optionId) < 0);
}
case FILTER_PREDICATE_TYPE.IS_EXACTLY: {
if (filter_term.length === 0) {
return true;
}
if (!Array.isArray(optionIds)) {
return false;
}
const uniqueArr = (arr) => [...new Set(arr)].sort();
return uniqueArr(optionIds).toString() === uniqueArr(filter_term).toString();
}
case FILTER_PREDICATE_TYPE.EMPTY: {
return !Array.isArray(optionIds) || optionIds.length === 0;
}
case FILTER_PREDICATE_TYPE.NOT_EMPTY: {
return Array.isArray(optionIds) && optionIds.length > 0;
}
default: {
return false;
}
}
};

export {
multipleSelectFilter,
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
singleSelectFilter,
collaboratorFilter,
numberFilter,
multipleSelectFilter,
} from './filter-column';
import {
FILTER_CONJUNCTION_TYPE,
Expand Down Expand Up @@ -42,6 +43,9 @@ const getFilterResult = (row, filter, { username, userId }) => {
case CellType.SINGLE_SELECT: {
return singleSelectFilter(cellValue, filter);
}
case CellType.MULTIPLE_SELECT: {
return multipleSelectFilter(cellValue, filter);
}
case CellType.NUMBER: {
return numberFilter(cellValue, filter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
sortNumber,
sortCheckbox,
sortCollaborator,
sortSingleSelect,
sortMultipleSelect,
} from '../sort/sort-column';
import { MAX_GROUP_LEVEL } from '../../constants/group';
import {
Expand Down Expand Up @@ -45,6 +47,9 @@ const _getFormattedCellValue = (cellValue, groupby) => {
case CellType.SINGLE_SELECT: {
return cellValue || null;
}
case CellType.MULTIPLE_SELECT: {
return Array.isArray(cellValue) ? cellValue : [];
}
case CellType.COLLABORATOR: {
return Array.isArray(cellValue) ? cellValue : [];
}
Expand Down Expand Up @@ -98,8 +103,18 @@ const _findGroupIndex = (sCellValue, cellValue2GroupIndexMap, groupsLength) => {
const getSortedGroups = (groups, groupbys, level, collaborators = []) => {
const sortFlag = 0;
const { column, sort_type } = groupbys[level];
const { type: columnType } = column;
const { type: columnType, data: columnData } = column;
const normalizedSortType = sort_type || SORT_TYPE.UP;
let option_id_index_map = {};
if (columnType === CellType.SINGLE_SELECT || columnType === CellType.MULTIPLE_SELECT) {
const { options } = columnData || {};
if (Array.isArray(options)) {
options.forEach((option, index) => {
option_id_index_map[option.id] = index;
});
}
}

groups.sort((currGroupRow, nextGroupRow) => {
let { cell_value: currCellVal } = currGroupRow;
let { cell_value: nextCellVal } = nextGroupRow;
Expand All @@ -121,6 +136,10 @@ const getSortedGroups = (groups, groupbys, level, collaborators = []) => {
nextCollaborators = getCollaboratorsNames(nextCollaborators, collaborators);
}
sortResult = sortCollaborator(currCollaborators, nextCollaborators, normalizedSortType);
} else if (columnType === CellType.SINGLE_SELECT) {
sortResult = sortSingleSelect(currCellVal, nextCellVal, { sort_type: normalizedSortType, option_id_index_map });
} else if (columnType === CellType.MULTIPLE_SELECT) {
sortResult = sortMultipleSelect(currCellVal, nextCellVal, { sort_type: normalizedSortType, option_id_index_map });
}
return sortFlag || sortResult;
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/metadata/metadata-view/_basic/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export {
isNumber,
getCellValueDisplayString,
getCellValueStringResult,
getColumnOptionNamesByIds,
getColumnOptionIdsByNames,
} from './cell';
export {
getColumnType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ const deleteInvalidSort = (sorts, columns) => {
let newSort = { ...sort, column: sortColumn };
const { type: columnType } = sortColumn;
switch (columnType) {
case CellType.SINGLE_SELECT: {
case CellType.SINGLE_SELECT:
case CellType.MULTIPLE_SELECT: {
const options = getColumnOptions(sortColumn);
let option_id_index_map = {};
options.forEach((option, index) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export {
sortCollaborator,
sortNumber,
sortSingleSelect,
sortMultipleSelect,
} from './sort-column';

export {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export { sortCheckbox } from './checkbox';
export { sortCollaborator } from './collaborator';
export { sortNumber } from './number';
export { sortSingleSelect } from './single-select';
export { sortMultipleSelect } from './multiple-select';
Loading

0 comments on commit 7a47c64

Please sign in to comment.