mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-13 05:39:59 +00:00
feat: metadata multiple select
This commit is contained in:
8
frontend/package-lock.json
generated
8
frontend/package-lock.json
generated
@@ -19,7 +19,7 @@
|
|||||||
"@seafile/sdoc-editor": "1.0.50",
|
"@seafile/sdoc-editor": "1.0.50",
|
||||||
"@seafile/seafile-calendar": "0.0.12",
|
"@seafile/seafile-calendar": "0.0.12",
|
||||||
"@seafile/seafile-editor": "1.0.109",
|
"@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/codemirror-extensions-langs": "^4.19.4",
|
||||||
"@uiw/react-codemirror": "^4.19.4",
|
"@uiw/react-codemirror": "^4.19.4",
|
||||||
"axios": "^1.7.3",
|
"axios": "^1.7.3",
|
||||||
@@ -5093,9 +5093,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@seafile/sf-metadata-ui-component": {
|
"node_modules/@seafile/sf-metadata-ui-component": {
|
||||||
"version": "0.0.21",
|
"version": "0.0.22",
|
||||||
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.21.tgz",
|
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.22.tgz",
|
||||||
"integrity": "sha512-bskuoVgMXDY5sD++MlMx9864+J3BdJ69pXZKifu40op4ebpC6qtJLAdZQV/j/ZqadyzGp1r0T340ClzhUfBQWw==",
|
"integrity": "sha512-sRFGl3JoD4m5+Hdmvxt9b6yer8HxT4mI5hHBPqF+AvKsyaWMQ2Dq108vKUUlADbZOLvFlx0YuVfYfOcxYJQeJQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@seafile/seafile-calendar": "0.0.24",
|
"@seafile/seafile-calendar": "0.0.24",
|
||||||
"@seafile/seafile-editor": "~1.0.102",
|
"@seafile/seafile-editor": "~1.0.102",
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
"@seafile/sdoc-editor": "1.0.50",
|
"@seafile/sdoc-editor": "1.0.50",
|
||||||
"@seafile/seafile-calendar": "0.0.12",
|
"@seafile/seafile-calendar": "0.0.12",
|
||||||
"@seafile/seafile-editor": "1.0.109",
|
"@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/codemirror-extensions-langs": "^4.19.4",
|
||||||
"@uiw/react-codemirror": "^4.19.4",
|
"@uiw/react-codemirror": "^4.19.4",
|
||||||
"axios": "^1.7.3",
|
"axios": "^1.7.3",
|
||||||
|
1
frontend/src/assets/icons/multiple-select.svg
Normal file
1
frontend/src/assets/icons/multiple-select.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723777705850" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15418" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M128 128C73.6 128 32 169.6 32 224s41.6 96 96 96 96-41.6 96-96-41.6-96-96-96zM128 416c-54.4 0-96 41.6-96 96s41.6 96 96 96 96-41.6 96-96-41.6-96-96-96zM128 704c-54.4 0-96 41.6-96 96s41.6 96 96 96 96-41.6 96-96-41.6-96-96-96zM963.2 736H380.8c-16 0-28.8 12.8-28.8 32v64c0 19.2 12.8 32 28.8 32h582.4c16 0 28.8-12.8 28.8-32v-64c0-19.2-12.8-32-28.8-32zM963.2 160H380.8c-16 0-28.8 12.8-28.8 32v64c0 19.2 12.8 32 28.8 32h582.4c16 0 28.8-12.8 28.8-32V192c0-19.2-12.8-32-28.8-32zM963.2 448H380.8c-16 0-28.8 12.8-28.8 32v64c0 19.2 12.8 32 28.8 32h582.4c16 0 28.8-12.8 28.8-32v-64c0-19.2-12.8-32-28.8-32z" p-id="15419"></path></svg>
|
After Width: | Height: | Size: 953 B |
@@ -17,7 +17,7 @@ const DirViews = ({ userPerm, repoID, currentPath, currentRepoInfo }) => {
|
|||||||
return [
|
return [
|
||||||
{ key: 'extended-properties', value: gettext('Extended properties') }
|
{ key: 'extended-properties', value: gettext('Extended properties') }
|
||||||
];
|
];
|
||||||
}, [enableMetadataManagement, userPerm]);
|
}, [enableMetadataManagement, currentRepoInfo]);
|
||||||
|
|
||||||
const moreOperationClick = useCallback((operationKey) => {
|
const moreOperationClick = useCallback((operationKey) => {
|
||||||
if (operationKey === 'extended-properties') {
|
if (operationKey === 'extended-properties') {
|
||||||
|
@@ -9,7 +9,7 @@ import toaster from '../../components/toast';
|
|||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import { DetailEditor, CellFormatter } from '../metadata-view';
|
import { DetailEditor, CellFormatter } from '../metadata-view';
|
||||||
import { getColumnOriginName } from '../metadata-view/utils/column-utils';
|
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';
|
import './index.css';
|
||||||
|
|
||||||
@@ -47,6 +47,8 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType, emptyTip }) =
|
|||||||
if (!PREDEFINED_COLUMN_KEYS.includes(field.key) && field.type === CellType.SINGLE_SELECT) {
|
if (!PREDEFINED_COLUMN_KEYS.includes(field.key) && field.type === CellType.SINGLE_SELECT) {
|
||||||
const options = getColumnOptions(field);
|
const options = getColumnOptions(field);
|
||||||
update = { [fileName]: getOptionName(options, newValue) };
|
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 => {
|
metadataAPI.modifyRecord(repoID, record._id, update, record._obj_id).then(res => {
|
||||||
const newMetadata = { ...metadata, record: { ...record, ...update } };
|
const newMetadata = { ...metadata, record: { ...record, ...update } };
|
||||||
@@ -73,6 +75,9 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType, emptyTip }) =
|
|||||||
update = { [fileName]: newOption.id };
|
update = { [fileName]: newOption.id };
|
||||||
if (!PREDEFINED_COLUMN_KEYS.includes(fieldKey) && newField.type === CellType.SINGLE_SELECT) {
|
if (!PREDEFINED_COLUMN_KEYS.includes(fieldKey) && newField.type === CellType.SINGLE_SELECT) {
|
||||||
update = { [fileName]: getOptionName(options, newOption.id) };
|
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);
|
return metadataAPI.modifyRecord(repoID, record._id, update, record._obj_id);
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
|
@@ -53,6 +53,7 @@ const NOT_SUPPORT_EDIT_COLUMN_TYPE_MAP = {
|
|||||||
|
|
||||||
const MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP = {
|
const MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP = {
|
||||||
[CellType.COLLABORATOR]: true,
|
[CellType.COLLABORATOR]: true,
|
||||||
|
[CellType.MULTIPLE_SELECT]: true,
|
||||||
};
|
};
|
||||||
const SINGLE_CELL_VALUE_COLUMN_TYPE_MAP = {
|
const SINGLE_CELL_VALUE_COLUMN_TYPE_MAP = {
|
||||||
[CellType.TEXT]: true,
|
[CellType.TEXT]: true,
|
||||||
|
@@ -13,6 +13,7 @@ const COLUMNS_ICON_CONFIG = {
|
|||||||
[CellType.DATE]: 'date',
|
[CellType.DATE]: 'date',
|
||||||
[CellType.LONG_TEXT]: 'long-text',
|
[CellType.LONG_TEXT]: 'long-text',
|
||||||
[CellType.SINGLE_SELECT]: 'single-select',
|
[CellType.SINGLE_SELECT]: 'single-select',
|
||||||
|
[CellType.MULTIPLE_SELECT]: 'multiple-select',
|
||||||
[CellType.NUMBER]: 'number',
|
[CellType.NUMBER]: 'number',
|
||||||
[CellType.GEOLOCATION]: 'location',
|
[CellType.GEOLOCATION]: 'location',
|
||||||
};
|
};
|
||||||
@@ -30,6 +31,7 @@ const COLUMNS_ICON_NAME = {
|
|||||||
[CellType.DATE]: 'Date',
|
[CellType.DATE]: 'Date',
|
||||||
[CellType.LONG_TEXT]: 'Long text',
|
[CellType.LONG_TEXT]: 'Long text',
|
||||||
[CellType.SINGLE_SELECT]: 'Single select',
|
[CellType.SINGLE_SELECT]: 'Single select',
|
||||||
|
[CellType.MULTIPLE_SELECT]: 'Multiple select',
|
||||||
[CellType.NUMBER]: 'Number',
|
[CellType.NUMBER]: 'Number',
|
||||||
[CellType.GEOLOCATION]: 'Geolocation',
|
[CellType.GEOLOCATION]: 'Geolocation',
|
||||||
};
|
};
|
||||||
|
@@ -11,6 +11,7 @@ const CellType = {
|
|||||||
DATE: 'date',
|
DATE: 'date',
|
||||||
LONG_TEXT: 'long-text',
|
LONG_TEXT: 'long-text',
|
||||||
SINGLE_SELECT: 'single-select',
|
SINGLE_SELECT: 'single-select',
|
||||||
|
MULTIPLE_SELECT: 'multiple-select',
|
||||||
NUMBER: 'number',
|
NUMBER: 'number',
|
||||||
GEOLOCATION: 'geolocation',
|
GEOLOCATION: 'geolocation',
|
||||||
};
|
};
|
||||||
|
@@ -72,6 +72,16 @@ const FILTER_COLUMN_OPTIONS = {
|
|||||||
FILTER_PREDICATE_TYPE.NOT_EMPTY,
|
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]: {
|
[CellType.CTIME]: {
|
||||||
filterPredicateList: datePredicates,
|
filterPredicateList: datePredicates,
|
||||||
filterTermModifierList: dateTermModifiers,
|
filterTermModifierList: dateTermModifiers,
|
||||||
|
@@ -12,6 +12,7 @@ const SORT_COLUMN_OPTIONS = [
|
|||||||
CellType.TEXT,
|
CellType.TEXT,
|
||||||
CellType.DATE,
|
CellType.DATE,
|
||||||
CellType.SINGLE_SELECT,
|
CellType.SINGLE_SELECT,
|
||||||
|
CellType.MULTIPLE_SELECT,
|
||||||
CellType.COLLABORATOR,
|
CellType.COLLABORATOR,
|
||||||
CellType.CHECKBOX,
|
CellType.CHECKBOX,
|
||||||
CellType.NUMBER,
|
CellType.NUMBER,
|
||||||
|
@@ -154,4 +154,6 @@ export {
|
|||||||
isNumber,
|
isNumber,
|
||||||
getCellValueDisplayString,
|
getCellValueDisplayString,
|
||||||
getCellValueStringResult,
|
getCellValueStringResult,
|
||||||
|
getColumnOptionNamesByIds,
|
||||||
|
getColumnOptionIdsByNames,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
@@ -11,6 +11,8 @@ export {
|
|||||||
export {
|
export {
|
||||||
getOption,
|
getOption,
|
||||||
getColumnOptionNameById,
|
getColumnOptionNameById,
|
||||||
|
getColumnOptionNamesByIds,
|
||||||
|
getColumnOptionIdsByNames,
|
||||||
getOptionName,
|
getOptionName,
|
||||||
getMultipleOptionName,
|
getMultipleOptionName,
|
||||||
} from './option';
|
} from './option';
|
||||||
|
@@ -36,13 +36,46 @@ const getColumnOptionNameById = (column, optionId) => {
|
|||||||
return getOptionName(options, 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.
|
* Get concatenated options names of given ids.
|
||||||
* @param {array} options e.g. [ { id, color, name, ... }, ... ]
|
* @param {array} options e.g. [ { id, color, name, ... }, ... ]
|
||||||
* @param {array} targetOptionsIds e.g. [ option.id, ... ]
|
* @param {array} targetOptionsIds e.g. [ option.id, ... ]
|
||||||
* @returns concatenated options names, string. e.g. 'name1, name2'
|
* @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 '';
|
if (!Array.isArray(targetOptionsIds) || !Array.isArray(options)) return '';
|
||||||
const selectedOptions = options.filter((option) => targetOptionsIds.includes(option.id));
|
const selectedOptions = options.filter((option) => targetOptionsIds.includes(option.id));
|
||||||
if (selectedOptions.length === 0) return '';
|
if (selectedOptions.length === 0) return '';
|
||||||
@@ -53,5 +86,7 @@ export {
|
|||||||
getOption,
|
getOption,
|
||||||
getOptionName,
|
getOptionName,
|
||||||
getColumnOptionNameById,
|
getColumnOptionNameById,
|
||||||
|
getColumnOptionNamesByIds,
|
||||||
|
getColumnOptionIdsByNames,
|
||||||
getMultipleOptionName,
|
getMultipleOptionName,
|
||||||
};
|
};
|
||||||
|
@@ -28,4 +28,6 @@ export {
|
|||||||
getGeolocationByGranularity,
|
getGeolocationByGranularity,
|
||||||
getFloatNumber,
|
getFloatNumber,
|
||||||
isNumber,
|
isNumber,
|
||||||
|
getColumnOptionNamesByIds,
|
||||||
|
getColumnOptionIdsByNames,
|
||||||
} from './column';
|
} from './column';
|
||||||
|
@@ -5,3 +5,4 @@ export { checkboxFilter } from './checkbox';
|
|||||||
export { singleSelectFilter } from './single-select';
|
export { singleSelectFilter } from './single-select';
|
||||||
export { collaboratorFilter } from './collaborator';
|
export { collaboratorFilter } from './collaborator';
|
||||||
export { numberFilter } from './number';
|
export { numberFilter } from './number';
|
||||||
|
export { multipleSelectFilter } from './multiple-select';
|
||||||
|
@@ -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,
|
||||||
|
};
|
@@ -10,6 +10,7 @@ import {
|
|||||||
singleSelectFilter,
|
singleSelectFilter,
|
||||||
collaboratorFilter,
|
collaboratorFilter,
|
||||||
numberFilter,
|
numberFilter,
|
||||||
|
multipleSelectFilter,
|
||||||
} from './filter-column';
|
} from './filter-column';
|
||||||
import {
|
import {
|
||||||
FILTER_CONJUNCTION_TYPE,
|
FILTER_CONJUNCTION_TYPE,
|
||||||
@@ -42,6 +43,9 @@ const getFilterResult = (row, filter, { username, userId }) => {
|
|||||||
case CellType.SINGLE_SELECT: {
|
case CellType.SINGLE_SELECT: {
|
||||||
return singleSelectFilter(cellValue, filter);
|
return singleSelectFilter(cellValue, filter);
|
||||||
}
|
}
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
|
return multipleSelectFilter(cellValue, filter);
|
||||||
|
}
|
||||||
case CellType.NUMBER: {
|
case CellType.NUMBER: {
|
||||||
return numberFilter(cellValue, filter);
|
return numberFilter(cellValue, filter);
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,8 @@ import {
|
|||||||
sortNumber,
|
sortNumber,
|
||||||
sortCheckbox,
|
sortCheckbox,
|
||||||
sortCollaborator,
|
sortCollaborator,
|
||||||
|
sortSingleSelect,
|
||||||
|
sortMultipleSelect,
|
||||||
} from '../sort/sort-column';
|
} from '../sort/sort-column';
|
||||||
import { MAX_GROUP_LEVEL } from '../../constants/group';
|
import { MAX_GROUP_LEVEL } from '../../constants/group';
|
||||||
import {
|
import {
|
||||||
@@ -45,6 +47,9 @@ const _getFormattedCellValue = (cellValue, groupby) => {
|
|||||||
case CellType.SINGLE_SELECT: {
|
case CellType.SINGLE_SELECT: {
|
||||||
return cellValue || null;
|
return cellValue || null;
|
||||||
}
|
}
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
|
return Array.isArray(cellValue) ? cellValue : [];
|
||||||
|
}
|
||||||
case CellType.COLLABORATOR: {
|
case CellType.COLLABORATOR: {
|
||||||
return Array.isArray(cellValue) ? cellValue : [];
|
return Array.isArray(cellValue) ? cellValue : [];
|
||||||
}
|
}
|
||||||
@@ -98,8 +103,18 @@ const _findGroupIndex = (sCellValue, cellValue2GroupIndexMap, groupsLength) => {
|
|||||||
const getSortedGroups = (groups, groupbys, level, collaborators = []) => {
|
const getSortedGroups = (groups, groupbys, level, collaborators = []) => {
|
||||||
const sortFlag = 0;
|
const sortFlag = 0;
|
||||||
const { column, sort_type } = groupbys[level];
|
const { column, sort_type } = groupbys[level];
|
||||||
const { type: columnType } = column;
|
const { type: columnType, data: columnData } = column;
|
||||||
const normalizedSortType = sort_type || SORT_TYPE.UP;
|
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) => {
|
groups.sort((currGroupRow, nextGroupRow) => {
|
||||||
let { cell_value: currCellVal } = currGroupRow;
|
let { cell_value: currCellVal } = currGroupRow;
|
||||||
let { cell_value: nextCellVal } = nextGroupRow;
|
let { cell_value: nextCellVal } = nextGroupRow;
|
||||||
@@ -121,6 +136,10 @@ const getSortedGroups = (groups, groupbys, level, collaborators = []) => {
|
|||||||
nextCollaborators = getCollaboratorsNames(nextCollaborators, collaborators);
|
nextCollaborators = getCollaboratorsNames(nextCollaborators, collaborators);
|
||||||
}
|
}
|
||||||
sortResult = sortCollaborator(currCollaborators, nextCollaborators, normalizedSortType);
|
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;
|
return sortFlag || sortResult;
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,8 @@ export {
|
|||||||
isNumber,
|
isNumber,
|
||||||
getCellValueDisplayString,
|
getCellValueDisplayString,
|
||||||
getCellValueStringResult,
|
getCellValueStringResult,
|
||||||
|
getColumnOptionNamesByIds,
|
||||||
|
getColumnOptionIdsByNames,
|
||||||
} from './cell';
|
} from './cell';
|
||||||
export {
|
export {
|
||||||
getColumnType,
|
getColumnType,
|
||||||
|
@@ -61,7 +61,8 @@ const deleteInvalidSort = (sorts, columns) => {
|
|||||||
let newSort = { ...sort, column: sortColumn };
|
let newSort = { ...sort, column: sortColumn };
|
||||||
const { type: columnType } = sortColumn;
|
const { type: columnType } = sortColumn;
|
||||||
switch (columnType) {
|
switch (columnType) {
|
||||||
case CellType.SINGLE_SELECT: {
|
case CellType.SINGLE_SELECT:
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
const options = getColumnOptions(sortColumn);
|
const options = getColumnOptions(sortColumn);
|
||||||
let option_id_index_map = {};
|
let option_id_index_map = {};
|
||||||
options.forEach((option, index) => {
|
options.forEach((option, index) => {
|
||||||
|
@@ -13,6 +13,7 @@ export {
|
|||||||
sortCollaborator,
|
sortCollaborator,
|
||||||
sortNumber,
|
sortNumber,
|
||||||
sortSingleSelect,
|
sortSingleSelect,
|
||||||
|
sortMultipleSelect,
|
||||||
} from './sort-column';
|
} from './sort-column';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@@ -8,3 +8,4 @@ export { sortCheckbox } from './checkbox';
|
|||||||
export { sortCollaborator } from './collaborator';
|
export { sortCollaborator } from './collaborator';
|
||||||
export { sortNumber } from './number';
|
export { sortNumber } from './number';
|
||||||
export { sortSingleSelect } from './single-select';
|
export { sortSingleSelect } from './single-select';
|
||||||
|
export { sortMultipleSelect } from './multiple-select';
|
||||||
|
@@ -0,0 +1,50 @@
|
|||||||
|
import { getMultipleIndexesOrderbyOptions } from '../core';
|
||||||
|
import { SORT_TYPE } from '../../../constants/sort';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort multiple-select
|
||||||
|
* @param {array} leftOptionIds the ids of options
|
||||||
|
* @param {array} rightOptionIds
|
||||||
|
* @param {string} sort_type e.g. 'up' | 'down'
|
||||||
|
* @param {object} option_id_index_map e.g. { [option.id]: 0, ... }
|
||||||
|
* @returns number
|
||||||
|
*/
|
||||||
|
const sortMultipleSelect = (leftOptionIds, rightOptionIds, { sort_type, option_id_index_map }) => {
|
||||||
|
const emptyLeftOptionIds = !leftOptionIds || leftOptionIds.length === 0;
|
||||||
|
const emptyRightOptionIds = !rightOptionIds || rightOptionIds.length === 0;
|
||||||
|
if (emptyLeftOptionIds && emptyRightOptionIds) return 0;
|
||||||
|
if (emptyLeftOptionIds) return 1;
|
||||||
|
if (emptyRightOptionIds) return -1;
|
||||||
|
|
||||||
|
const leftOptionIndexes = getMultipleIndexesOrderbyOptions(leftOptionIds, option_id_index_map);
|
||||||
|
const rightOptionIndexes = getMultipleIndexesOrderbyOptions(rightOptionIds, option_id_index_map);
|
||||||
|
const leftOptionsLen = leftOptionIndexes.length;
|
||||||
|
const rightOptionsLen = rightOptionIndexes.length;
|
||||||
|
|
||||||
|
// current multiple select equal to next multiple select.
|
||||||
|
if (
|
||||||
|
leftOptionsLen === rightOptionsLen
|
||||||
|
&& (leftOptionsLen === 0 || leftOptionIndexes.join('') === rightOptionIndexes.join(''))
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const len = Math.min(leftOptionsLen, rightOptionsLen);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
if (leftOptionIndexes[i] > rightOptionIndexes[i]) {
|
||||||
|
return sort_type === SORT_TYPE.UP ? 1 : -1;
|
||||||
|
}
|
||||||
|
if (leftOptionIndexes[i] < rightOptionIndexes[i]) {
|
||||||
|
return sort_type === SORT_TYPE.UP ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (leftOptionsLen > rightOptionsLen) {
|
||||||
|
return sort_type === SORT_TYPE.UP ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sort_type === SORT_TYPE.UP ? -1 : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
sortMultipleSelect,
|
||||||
|
};
|
@@ -6,6 +6,7 @@ import {
|
|||||||
sortNumber,
|
sortNumber,
|
||||||
sortCollaborator,
|
sortCollaborator,
|
||||||
sortCheckbox,
|
sortCheckbox,
|
||||||
|
sortMultipleSelect,
|
||||||
} from './sort-column';
|
} from './sort-column';
|
||||||
import { CellType, DATE_COLUMN_OPTIONS } from '../../constants/column';
|
import { CellType, DATE_COLUMN_OPTIONS } from '../../constants/column';
|
||||||
import { getCellValueByColumn, getCollaboratorsNames } from '../cell';
|
import { getCellValueByColumn, getCollaboratorsNames } from '../cell';
|
||||||
@@ -31,6 +32,8 @@ const sortRowsWithMultiSorts = (tableRows, sorts, { collaborators }) => {
|
|||||||
initValue = initValue || sortSingleSelect(currCellVal, nextCellVal, sort);
|
initValue = initValue || sortSingleSelect(currCellVal, nextCellVal, sort);
|
||||||
} else if (NUMBER_SORTER_COLUMN_TYPES.includes(columnType)) {
|
} else if (NUMBER_SORTER_COLUMN_TYPES.includes(columnType)) {
|
||||||
initValue = initValue || sortNumber(currCellVal, nextCellVal, sort_type);
|
initValue = initValue || sortNumber(currCellVal, nextCellVal, sort_type);
|
||||||
|
} else if (columnType === CellType.MULTIPLE_SELECT) {
|
||||||
|
initValue = initValue || sortMultipleSelect(currCellVal, nextCellVal, sort);
|
||||||
} else if (columnType === CellType.COLLABORATOR) {
|
} else if (columnType === CellType.COLLABORATOR) {
|
||||||
let currValidCollaborators = currCellVal;
|
let currValidCollaborators = currCellVal;
|
||||||
let nextValidCollaborators = nextCellVal;
|
let nextValidCollaborators = nextCellVal;
|
||||||
|
@@ -145,10 +145,9 @@ class ValidateFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter predicate should support: is_empty/is_not_empty(excludes checkbox and bool)
|
// Filter predicate should support: is_empty/is_not_empty(excludes checkbox and bool)
|
||||||
if (CHECK_EMPTY_PREDICATES.includes(predicate)) {
|
if (CHECK_EMPTY_PREDICATES.includes(predicate)) return true;
|
||||||
return true;
|
|
||||||
}
|
if (array_type === CellType.SINGLE_SELECT || array_type === CellType.DEPARTMENT_SINGLE_SELECT) {
|
||||||
if (array_type === CellType.SINGLE_SELECT) {
|
|
||||||
return this.validatePredicate(predicate, { type: CellType.MULTIPLE_SELECT });
|
return this.validatePredicate(predicate, { type: CellType.MULTIPLE_SELECT });
|
||||||
}
|
}
|
||||||
if (COLLABORATOR_COLUMN_TYPES.includes(array_type)) {
|
if (COLLABORATOR_COLUMN_TYPES.includes(array_type)) {
|
||||||
@@ -272,6 +271,15 @@ class ValidateFilter {
|
|||||||
// invalid filter_term if selected option is deleted
|
// invalid filter_term if selected option is deleted
|
||||||
return !!options.find((option) => term === option.id);
|
return !!options.find((option) => term === option.id);
|
||||||
}
|
}
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
|
if (!this.isValidTermType(term, TERM_TYPE_MAP.ARRAY)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains deleted option(s)
|
||||||
|
const options = getColumnOptions(filterColumn);
|
||||||
|
return this.isValidSelectedOptions(term, options);
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -299,9 +307,6 @@ class ValidateFilter {
|
|||||||
type: CellType.MULTIPLE_SELECT, data: array_data,
|
type: CellType.MULTIPLE_SELECT, data: array_data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (array_type === CellType.DEPARTMENT_SINGLE_SELECT) {
|
|
||||||
return this.isValidTermType(term, TERM_TYPE_MAP.ARRAY);
|
|
||||||
}
|
|
||||||
if (COLLABORATOR_COLUMN_TYPES.includes(array_type)) {
|
if (COLLABORATOR_COLUMN_TYPES.includes(array_type)) {
|
||||||
return this.isValidTerm(term, predicate, modifier, { type: CellType.COLLABORATOR });
|
return this.isValidTerm(term, predicate, modifier, { type: CellType.COLLABORATOR });
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,9 @@
|
|||||||
background-color: #f6f6f6;
|
background-color: #f6f6f6;
|
||||||
border-bottom: 1px solid #dde2ea;
|
border-bottom: 1px solid #dde2ea;
|
||||||
border-radius: 3px 3px 0 0;
|
border-radius: 3px 3px 0 0;
|
||||||
min-height: 34px;
|
min-height: 35px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collaborator {
|
.collaborator {
|
||||||
@@ -46,6 +47,11 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sf-metadata-delete-collaborator .collaborator {
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.sf-metadata-delete-collaborator .collaborator .collaborator-remove {
|
.sf-metadata-delete-collaborator .collaborator .collaborator-remove {
|
||||||
height: 14px;
|
height: 14px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
|
@@ -9,6 +9,7 @@ const POPUP_EDITOR_COLUMN_TYPES = [
|
|||||||
CellType.DATE,
|
CellType.DATE,
|
||||||
CellType.COLLABORATOR,
|
CellType.COLLABORATOR,
|
||||||
CellType.SINGLE_SELECT,
|
CellType.SINGLE_SELECT,
|
||||||
|
CellType.MULTIPLE_SELECT,
|
||||||
CellType.LONG_TEXT,
|
CellType.LONG_TEXT,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -2,15 +2,16 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { ClickOutside } from '@seafile/sf-metadata-ui-component';
|
import { ClickOutside } from '@seafile/sf-metadata-ui-component';
|
||||||
import { CellType, isFunction, Z_INDEX, getCellValueByColumn, getColumnOptionNameById, PRIVATE_COLUMN_KEYS } from '../../../_basic';
|
import { CellType, isFunction, Z_INDEX, getCellValueByColumn, getColumnOptionNameById, PRIVATE_COLUMN_KEYS,
|
||||||
|
getColumnOptionNamesByIds,
|
||||||
|
} from '../../../_basic';
|
||||||
import { isCellValueChanged } from '../../../utils/cell-comparer';
|
import { isCellValueChanged } from '../../../utils/cell-comparer';
|
||||||
import { EVENT_BUS_TYPE } from '../../../constants';
|
import { EVENT_BUS_TYPE } from '../../../constants';
|
||||||
import Editor from '../editor';
|
import Editor from '../editor';
|
||||||
import { canEditCell } from '../../../utils/column-utils';
|
import { canEditCell } from '../../../utils/column-utils';
|
||||||
|
|
||||||
const NOT_SUPPORT_EDITOR_COLUMN_TYPES = [
|
const NOT_SUPPORT_EDITOR_COLUMN_TYPES = [
|
||||||
CellType.CTIME, CellType.MTIME, CellType.CREATOR, CellType.LAST_MODIFIER,
|
CellType.CTIME, CellType.MTIME, CellType.CREATOR, CellType.LAST_MODIFIER, CellType.FILE_NAME
|
||||||
CellType.FILE_NAME, CellType.COLLABORATOR, CellType.LONG_TEXT, CellType.SINGLE_SELECT,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
class PopupEditorContainer extends React.Component {
|
class PopupEditorContainer extends React.Component {
|
||||||
@@ -146,6 +147,8 @@ class PopupEditorContainer extends React.Component {
|
|||||||
let updated = columnType === CellType.DATE ? { [columnKey]: newValue } : newValue;
|
let updated = columnType === CellType.DATE ? { [columnKey]: newValue } : newValue;
|
||||||
if (columnType === CellType.SINGLE_SELECT) {
|
if (columnType === CellType.SINGLE_SELECT) {
|
||||||
updated[columnKey] = newValue[columnKey] ? getColumnOptionNameById(column, newValue[columnKey]) : '';
|
updated[columnKey] = newValue[columnKey] ? getColumnOptionNameById(column, newValue[columnKey]) : '';
|
||||||
|
} else if (columnType === CellType.MULTIPLE_SELECT) {
|
||||||
|
updated[columnKey] = newValue[columnKey] ? getColumnOptionNamesByIds(column, newValue[columnKey]) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.commitData(updated, true);
|
this.commitData(updated, true);
|
||||||
|
@@ -6,6 +6,7 @@ import FileNameEditor from './file-name-editor';
|
|||||||
import TextEditor from './text-editor';
|
import TextEditor from './text-editor';
|
||||||
import NumberEditor from './number-editor';
|
import NumberEditor from './number-editor';
|
||||||
import SingleSelectEditor from './single-select-editor';
|
import SingleSelectEditor from './single-select-editor';
|
||||||
|
import MultipleSelectEditor from './multiple-select-editor';
|
||||||
import CollaboratorEditor from './collaborator-editor';
|
import CollaboratorEditor from './collaborator-editor';
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
@@ -28,6 +29,9 @@ const Editor = React.forwardRef((props, ref) => {
|
|||||||
case CellType.SINGLE_SELECT: {
|
case CellType.SINGLE_SELECT: {
|
||||||
return (<SingleSelectEditor ref={ref} {...props} />);
|
return (<SingleSelectEditor ref={ref} {...props} />);
|
||||||
}
|
}
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
|
return (<MultipleSelectEditor ref={ref} {...props} />);
|
||||||
|
}
|
||||||
case CellType.COLLABORATOR: {
|
case CellType.COLLABORATOR: {
|
||||||
return (<CollaboratorEditor ref={ref} {...props} />);
|
return (<CollaboratorEditor ref={ref} {...props} />);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,25 @@
|
|||||||
|
.sf-metadata-delete-select-options {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
border-bottom: 1px solid #dde2ea;
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
min-height: 35px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-metadata-delete-select-options .sf-metadata-delete-select-option {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-metadata-delete-select-options .sf-metadata-delete-select-option .sf-metadata-delete-select-remove {
|
||||||
|
height: 14px;
|
||||||
|
width: 14px;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-metadata-delete-select-options .sf-metadata-delete-select-option .sf-metadata-icon-x-01 {
|
||||||
|
fill: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
@@ -0,0 +1,59 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { IconBtn } from '@seafile/sf-metadata-ui-component';
|
||||||
|
import { gettext } from '../../../../utils';
|
||||||
|
import { DELETED_OPTION_TIPS, DELETED_OPTION_BACKGROUND_COLOR } from '../../../../constants';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const DeleteOption = ({ value, options, onDelete }) => {
|
||||||
|
|
||||||
|
const displayOptions = useMemo(() => {
|
||||||
|
if (!Array.isArray(value) || value.length === 0) return [];
|
||||||
|
const selectedOptions = options.filter((option) => value.includes(option.id) || value.includes(option.name));
|
||||||
|
const invalidOptionIds = value.filter(optionId => optionId && !options.find(o => o.id === optionId || o.name === optionId));
|
||||||
|
const invalidOptions = invalidOptionIds.map(optionId => ({
|
||||||
|
id: optionId,
|
||||||
|
name: gettext(DELETED_OPTION_TIPS),
|
||||||
|
color: DELETED_OPTION_BACKGROUND_COLOR,
|
||||||
|
}));
|
||||||
|
return [...selectedOptions, ...invalidOptions];
|
||||||
|
}, [options, value]);
|
||||||
|
|
||||||
|
if (displayOptions.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="sf-metadata-delete-select-options">
|
||||||
|
{displayOptions.map(option => {
|
||||||
|
if (!option) return null;
|
||||||
|
const { id, name } = option;
|
||||||
|
const style = {
|
||||||
|
display: 'inline-flex',
|
||||||
|
padding: '0px 10px',
|
||||||
|
height: '20px',
|
||||||
|
lineHeight: '20px',
|
||||||
|
textAlign: 'center',
|
||||||
|
borderRadius: '10px',
|
||||||
|
maxWidth: '250px',
|
||||||
|
fontSize: 13,
|
||||||
|
backgroundColor: option.color,
|
||||||
|
color: option.textColor || null,
|
||||||
|
fill: option.textColor || '#666',
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div key={id} className="sf-metadata-delete-select-option" style={style}>
|
||||||
|
<span className="sf-metadata-delete-select-option-name text-truncate" title={name} aria-label={name}>{name}</span>
|
||||||
|
<IconBtn className="sf-metadata-delete-select-remove" onClick={(event) => onDelete(id, event)} iconName="x-01" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DeleteOption.propTypes = {
|
||||||
|
value: PropTypes.array.isRequired,
|
||||||
|
onDelete: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeleteOption;
|
@@ -0,0 +1,278 @@
|
|||||||
|
import React, { forwardRef, useMemo, useImperativeHandle, useCallback, useState, useRef, useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { SearchInput, CustomizeAddTool, Icon } from '@seafile/sf-metadata-ui-component';
|
||||||
|
import { isFunction, getColumnOptions, getColumnOptionIdsByNames } from '../../../_basic';
|
||||||
|
import { generateNewOption } from '../../../utils/select-utils';
|
||||||
|
import { KeyCodes } from '../../../../../constants';
|
||||||
|
import { gettext } from '../../../../../utils/constants';
|
||||||
|
import DeleteOption from './delete-options';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const MultipleSelectEditor = forwardRef(({
|
||||||
|
saveImmediately,
|
||||||
|
column,
|
||||||
|
value: oldValue,
|
||||||
|
onCommit,
|
||||||
|
onPressTab,
|
||||||
|
modifyColumnData,
|
||||||
|
}, ref) => {
|
||||||
|
const [value, setValue] = useState(getColumnOptionIdsByNames(column, oldValue));
|
||||||
|
const [searchValue, setSearchValue] = useState('');
|
||||||
|
const [highlightIndex, setHighlightIndex] = useState(-1);
|
||||||
|
const [maxItemNum, setMaxItemNum] = useState(0);
|
||||||
|
const itemHeight = 30;
|
||||||
|
const editorContainerRef = useRef(null);
|
||||||
|
const editorRef = useRef(null);
|
||||||
|
const selectItemRef = useRef(null);
|
||||||
|
const canEditData = window.sfMetadataContext.canModifyColumnData(column);
|
||||||
|
|
||||||
|
const options = useMemo(() => {
|
||||||
|
return getColumnOptions(column);
|
||||||
|
}, [column]);
|
||||||
|
|
||||||
|
const displayOptions = useMemo(() => {
|
||||||
|
if (!searchValue) return options;
|
||||||
|
const value = searchValue.toLowerCase().trim();
|
||||||
|
if (!value) return options;
|
||||||
|
return options.filter((item) => item.name && item.name.toLowerCase().indexOf(value) > -1);
|
||||||
|
}, [searchValue, options]);
|
||||||
|
|
||||||
|
const isShowCreateBtn = useMemo(() => {
|
||||||
|
if (!canEditData || !searchValue) return false;
|
||||||
|
return displayOptions.findIndex(option => option.name === searchValue) === -1 ? true : false;
|
||||||
|
}, [canEditData, displayOptions, searchValue]);
|
||||||
|
|
||||||
|
const style = useMemo(() => {
|
||||||
|
return { width: column.width };
|
||||||
|
}, [column]);
|
||||||
|
|
||||||
|
const blur = useCallback(() => {
|
||||||
|
onCommit && onCommit(value);
|
||||||
|
}, [value, onCommit]);
|
||||||
|
|
||||||
|
const onChangeSearch = useCallback((newSearchValue) => {
|
||||||
|
if (searchValue === newSearchValue) return;
|
||||||
|
setSearchValue(newSearchValue);
|
||||||
|
}, [searchValue]);
|
||||||
|
|
||||||
|
const onSelectOption = useCallback((optionId) => {
|
||||||
|
const newValue = value.slice(0);
|
||||||
|
let optionIdx = value.indexOf(optionId);
|
||||||
|
if (optionIdx > -1) {
|
||||||
|
newValue.splice(optionIdx, 1);
|
||||||
|
} else {
|
||||||
|
newValue.push(optionId);
|
||||||
|
}
|
||||||
|
setValue(newValue);
|
||||||
|
if (saveImmediately) {
|
||||||
|
onCommit && onCommit(newValue);
|
||||||
|
}
|
||||||
|
}, [saveImmediately, value, onCommit]);
|
||||||
|
|
||||||
|
const onMenuMouseEnter = useCallback((highlightIndex) => {
|
||||||
|
setHighlightIndex(highlightIndex);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onMenuMouseLeave = useCallback((index) => {
|
||||||
|
setHighlightIndex(-1);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const createOption = useCallback((event) => {
|
||||||
|
event && event.stopPropagation();
|
||||||
|
event && event.nativeEvent.stopImmediatePropagation();
|
||||||
|
const newOption = generateNewOption(options, searchValue?.trim() || '');
|
||||||
|
let newOptions = options.slice(0);
|
||||||
|
newOptions.push(newOption);
|
||||||
|
modifyColumnData(column.key, { options: newOptions }, { options: column.data.options || [] });
|
||||||
|
onSelectOption(newOption.id);
|
||||||
|
}, [column, searchValue, options, onSelectOption, modifyColumnData]);
|
||||||
|
|
||||||
|
const onDeleteOption = useCallback((optionId) => {
|
||||||
|
const newValue = value.slice(0);
|
||||||
|
const index = newValue.indexOf(optionId);
|
||||||
|
if (index > -1) {
|
||||||
|
newValue.splice(index, 1);
|
||||||
|
}
|
||||||
|
setValue(newValue);
|
||||||
|
if (saveImmediately) {
|
||||||
|
onCommit && onCommit(newValue);
|
||||||
|
}
|
||||||
|
}, [saveImmediately, value, onCommit]);
|
||||||
|
|
||||||
|
const getMaxItemNum = useCallback(() => {
|
||||||
|
let selectContainerStyle = getComputedStyle(editorContainerRef.current, null);
|
||||||
|
let selectItemStyle = getComputedStyle(selectItemRef.current, null);
|
||||||
|
let maxSelectItemNum = Math.floor(parseInt(selectContainerStyle.maxHeight) / parseInt(selectItemStyle.height));
|
||||||
|
return maxSelectItemNum - 1;
|
||||||
|
}, [editorContainerRef, selectItemRef]);
|
||||||
|
|
||||||
|
const onEnter = useCallback((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
let option;
|
||||||
|
if (displayOptions.length === 1) {
|
||||||
|
option = displayOptions[0];
|
||||||
|
} else if (highlightIndex > -1) {
|
||||||
|
option = displayOptions[highlightIndex];
|
||||||
|
}
|
||||||
|
if (option) {
|
||||||
|
let newOptionId = option.id;
|
||||||
|
if (value === option.id) newOptionId = null;
|
||||||
|
onSelectOption(newOptionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let isShowCreateBtn = false;
|
||||||
|
if (searchValue) {
|
||||||
|
isShowCreateBtn = canEditData && displayOptions.findIndex(option => option.name === searchValue) === -1 ? true : false;
|
||||||
|
}
|
||||||
|
if (!isShowCreateBtn || displayOptions.length === 0) return;
|
||||||
|
createOption();
|
||||||
|
}, [canEditData, displayOptions, highlightIndex, value, searchValue, onSelectOption, createOption]);
|
||||||
|
|
||||||
|
const onUpArrow = useCallback((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (highlightIndex === 0) return;
|
||||||
|
setHighlightIndex(highlightIndex - 1);
|
||||||
|
if (highlightIndex > displayOptions.length - maxItemNum) {
|
||||||
|
editorContainerRef.current.scrollTop -= itemHeight;
|
||||||
|
}
|
||||||
|
}, [editorContainerRef, highlightIndex, maxItemNum, displayOptions, itemHeight]);
|
||||||
|
|
||||||
|
const onDownArrow = useCallback((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (highlightIndex === displayOptions.length - 1) return;
|
||||||
|
setHighlightIndex(highlightIndex + 1);
|
||||||
|
if (highlightIndex >= maxItemNum) {
|
||||||
|
editorContainerRef.current.scrollTop += itemHeight;
|
||||||
|
}
|
||||||
|
}, [editorContainerRef, highlightIndex, maxItemNum, displayOptions, itemHeight]);
|
||||||
|
|
||||||
|
const onHotKey = useCallback((event) => {
|
||||||
|
if (event.keyCode === KeyCodes.Enter) {
|
||||||
|
onEnter(event);
|
||||||
|
} else if (event.keyCode === KeyCodes.UpArrow) {
|
||||||
|
onUpArrow(event);
|
||||||
|
} else if (event.keyCode === KeyCodes.DownArrow) {
|
||||||
|
onDownArrow(event);
|
||||||
|
} else if (event.keyCode === KeyCodes.Tab) {
|
||||||
|
if (isFunction(onPressTab)) {
|
||||||
|
onPressTab(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [onEnter, onUpArrow, onDownArrow, onPressTab]);
|
||||||
|
|
||||||
|
const onKeyDown = useCallback((event) => {
|
||||||
|
if (
|
||||||
|
event.keyCode === KeyCodes.ChineseInputMethod ||
|
||||||
|
event.keyCode === KeyCodes.Enter ||
|
||||||
|
event.keyCode === KeyCodes.LeftArrow ||
|
||||||
|
event.keyCode === KeyCodes.RightArrow
|
||||||
|
) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editorRef.current) {
|
||||||
|
const { bottom } = editorRef.current.getBoundingClientRect();
|
||||||
|
if (bottom > window.innerHeight) {
|
||||||
|
editorRef.current.style.top = (parseInt(editorRef.current.style.top) - bottom + window.innerHeight) + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (editorContainerRef.current && selectItemRef.current) {
|
||||||
|
setMaxItemNum(getMaxItemNum());
|
||||||
|
}
|
||||||
|
document.addEventListener('keydown', onHotKey, true);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', onHotKey, true);
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [onHotKey]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const highlightIndex = displayOptions.length === 0 ? -1 : 0;
|
||||||
|
setHighlightIndex(highlightIndex);
|
||||||
|
}, [displayOptions]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
getValue: () => {
|
||||||
|
const { key } = column;
|
||||||
|
return { [key]: value };
|
||||||
|
},
|
||||||
|
onBlur: () => blur(),
|
||||||
|
|
||||||
|
}), [column, value, blur]);
|
||||||
|
|
||||||
|
const renderOptions = useCallback(() => {
|
||||||
|
if (displayOptions.length === 0) {
|
||||||
|
const noOptionsTip = searchValue ? gettext('No options available') : gettext('No option');
|
||||||
|
return (<span className="none-search-result">{noOptionsTip}</span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return displayOptions.map((option, i) => {
|
||||||
|
const isSelected = value.includes(option.id);
|
||||||
|
return (
|
||||||
|
<div key={option.id} className="sf-metadata-single-select-item" ref={selectItemRef}>
|
||||||
|
<div
|
||||||
|
className={classnames('single-select-container', { 'single-select-container-highlight': i === highlightIndex })}
|
||||||
|
onMouseDown={() => onSelectOption(option.id)}
|
||||||
|
onMouseEnter={() => onMenuMouseEnter(i)}
|
||||||
|
onMouseLeave={() => onMenuMouseLeave(i)}
|
||||||
|
>
|
||||||
|
<div className="single-select">
|
||||||
|
<span
|
||||||
|
className="single-select-name"
|
||||||
|
style={{ backgroundColor: option.color, color: option.textColor || null }}
|
||||||
|
title={option.name}
|
||||||
|
aria-label={option.name}
|
||||||
|
>
|
||||||
|
{option.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="single-select-check-icon">
|
||||||
|
{isSelected && (<Icon iconName="check-mark" />)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
}, [displayOptions, searchValue, value, highlightIndex, onMenuMouseEnter, onMenuMouseLeave, onSelectOption]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="sf-metadata-single-select-editor sf-metadata-multiple-select-editor" style={style} ref={editorRef}>
|
||||||
|
<DeleteOption value={value} options={options} onDelete={onDeleteOption} />
|
||||||
|
<div className="sf-metadata-search-single-select-options">
|
||||||
|
<SearchInput
|
||||||
|
placeholder={gettext('Search option')}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
onChange={onChangeSearch}
|
||||||
|
autoFocus={true}
|
||||||
|
className="sf-metadata-search-options"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="sf-metadata-single-select-editor-container" ref={editorContainerRef}>
|
||||||
|
{renderOptions()}
|
||||||
|
</div>
|
||||||
|
{isShowCreateBtn && (
|
||||||
|
<CustomizeAddTool
|
||||||
|
callBack={createOption}
|
||||||
|
footerName={`${gettext('Add option')} ${searchValue}`}
|
||||||
|
className="add-search-result"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
MultipleSelectEditor.propTypes = {
|
||||||
|
column: PropTypes.object,
|
||||||
|
value: PropTypes.array,
|
||||||
|
onCommit: PropTypes.func,
|
||||||
|
onPressTab: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MultipleSelectEditor;
|
@@ -5,6 +5,7 @@ import CheckboxEditor from './checkbox-editor';
|
|||||||
import TextEditor from './text-editor';
|
import TextEditor from './text-editor';
|
||||||
import NumberEditor from './number-editor';
|
import NumberEditor from './number-editor';
|
||||||
import SingleSelectEditor from './single-select-editor';
|
import SingleSelectEditor from './single-select-editor';
|
||||||
|
import MultipleSelectEditor from './multiple-select-editor';
|
||||||
import CollaboratorEditor from './collaborator-editor';
|
import CollaboratorEditor from './collaborator-editor';
|
||||||
import DateEditor from './date-editor';
|
import DateEditor from './date-editor';
|
||||||
import { lang } from '../../../../utils/constants';
|
import { lang } from '../../../../utils/constants';
|
||||||
@@ -16,7 +17,6 @@ const DetailEditor = ({ field, onChange: onChangeAPI, ...props }) => {
|
|||||||
onChangeAPI(field.key, newValue);
|
onChangeAPI(field.key, newValue);
|
||||||
}, [field, onChangeAPI]);
|
}, [field, onChangeAPI]);
|
||||||
|
|
||||||
|
|
||||||
switch (field.type) {
|
switch (field.type) {
|
||||||
case CellType.CHECKBOX: {
|
case CellType.CHECKBOX: {
|
||||||
return (<CheckboxEditor { ...props } field={field} onChange={onChange} />);
|
return (<CheckboxEditor { ...props } field={field} onChange={onChange} />);
|
||||||
@@ -33,6 +33,9 @@ const DetailEditor = ({ field, onChange: onChangeAPI, ...props }) => {
|
|||||||
case CellType.SINGLE_SELECT: {
|
case CellType.SINGLE_SELECT: {
|
||||||
return (<SingleSelectEditor { ...props } field={field} onChange={onChange} />);
|
return (<SingleSelectEditor { ...props } field={field} onChange={onChange} />);
|
||||||
}
|
}
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
|
return (<MultipleSelectEditor { ...props } field={field} onChange={onChange} />);
|
||||||
|
}
|
||||||
case CellType.COLLABORATOR: {
|
case CellType.COLLABORATOR: {
|
||||||
return (<CollaboratorEditor { ...props } field={field} onChange={onChange} />);
|
return (<CollaboratorEditor { ...props } field={field} onChange={onChange} />);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
.sf-metadata-multiple-select-property-detail-editor {
|
||||||
|
min-height: 34px;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-metadata-multiple-select-property-detail-editor .sf-metadata-delete-select-options {
|
||||||
|
min-height: 34px;
|
||||||
|
border-bottom: none;
|
||||||
|
background-color: inherit;
|
||||||
|
border-radius: unset;
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-metadata-multiple-select-property-detail-editor .sf-metadata-delete-select-options .sf-metadata-delete-select-option {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-metadata-multiple-select-property-editor-popover .sf-metadata-delete-select-options {
|
||||||
|
display: none;
|
||||||
|
}
|
@@ -0,0 +1,106 @@
|
|||||||
|
import React, { useMemo, useCallback, useState, useRef, useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Popover } from 'reactstrap';
|
||||||
|
import { getColumnOptionIdsByNames, getColumnOptions, KeyCodes } from '../../../_basic';
|
||||||
|
import { getEventClassName, gettext } from '../../../utils';
|
||||||
|
import Editor from '../../cell-editor/multiple-select-editor';
|
||||||
|
import DeleteOptions from '../../cell-editor/multiple-select-editor/delete-options';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const MultipleSelectEditor = ({ field, value, record, fields, onChange, modifyColumnData }) => {
|
||||||
|
const ref = useRef(null);
|
||||||
|
const [showEditor, setShowEditor] = useState(false);
|
||||||
|
const options = useMemo(() => getColumnOptions(field), [field]);
|
||||||
|
|
||||||
|
const onClick = useCallback((event) => {
|
||||||
|
if (!event.target) return;
|
||||||
|
const className = getEventClassName(event);
|
||||||
|
if (className.indexOf('sf-metadata-search-options') > -1) return;
|
||||||
|
const dom = document.querySelector('.sf-metadata-multiple-select-editor');
|
||||||
|
if (!dom) return;
|
||||||
|
if (dom.contains(event.target)) return;
|
||||||
|
if (ref.current && !ref.current.contains(event.target) && showEditor) {
|
||||||
|
setShowEditor(false);
|
||||||
|
}
|
||||||
|
}, [showEditor]);
|
||||||
|
|
||||||
|
const onHotKey = useCallback((event) => {
|
||||||
|
if (event.keyCode === KeyCodes.Esc) {
|
||||||
|
if (showEditor) {
|
||||||
|
setShowEditor(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [showEditor]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener('mousedown', onClick);
|
||||||
|
document.addEventListener('keydown', onHotKey, true);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', onClick);
|
||||||
|
document.removeEventListener('keydown', onHotKey, true);
|
||||||
|
};
|
||||||
|
}, [onClick, onHotKey]);
|
||||||
|
|
||||||
|
const openEditor = useCallback(() => {
|
||||||
|
setShowEditor(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const deleteOption = useCallback((id, event) => {
|
||||||
|
event && event.stopPropagation();
|
||||||
|
event && event.nativeEvent && event.nativeEvent.stopImmediatePropagation();
|
||||||
|
const oldValue = getColumnOptionIdsByNames(field, value);
|
||||||
|
const newValue = oldValue.filter(c => c !== id);
|
||||||
|
onChange(newValue);
|
||||||
|
}, [field, value, onChange]);
|
||||||
|
|
||||||
|
const onCommit = useCallback((newValue) => {
|
||||||
|
onChange(newValue);
|
||||||
|
}, [onChange]);
|
||||||
|
|
||||||
|
const renderEditor = useCallback(() => {
|
||||||
|
if (!showEditor) return null;
|
||||||
|
const { width } = ref.current.getBoundingClientRect();
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
target={ref}
|
||||||
|
isOpen={true}
|
||||||
|
placement="bottom-end"
|
||||||
|
hideArrow={true}
|
||||||
|
fade={false}
|
||||||
|
className="sf-metadata-property-editor-popover sf-metadata-single-select-property-editor-popover sf-metadata-multiple-select-property-editor-popover"
|
||||||
|
boundariesElement={document.body}
|
||||||
|
>
|
||||||
|
<Editor
|
||||||
|
saveImmediately={true}
|
||||||
|
value={value}
|
||||||
|
column={{ ...field, width: Math.max(width - 2, 200) }}
|
||||||
|
columns={fields}
|
||||||
|
modifyColumnData={modifyColumnData}
|
||||||
|
record={record}
|
||||||
|
onCommit={onCommit}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}, [showEditor, onCommit, record, value, modifyColumnData, fields, field]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="sf-metadata-property-detail-editor sf-metadata-single-select-property-detail-editor sf-metadata-multiple-select-property-detail-editor"
|
||||||
|
placeholder={gettext('Empty')}
|
||||||
|
ref={ref}
|
||||||
|
onClick={openEditor}
|
||||||
|
>
|
||||||
|
<DeleteOptions value={value} options={options} onDelete={deleteOption} />
|
||||||
|
{renderEditor()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
MultipleSelectEditor.propTypes = {
|
||||||
|
field: PropTypes.object.isRequired,
|
||||||
|
value: PropTypes.array,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MultipleSelectEditor;
|
@@ -69,7 +69,7 @@ const ColumnPopover = ({ target, onChange }) => {
|
|||||||
if (Object.keys(data).length === 0) {
|
if (Object.keys(data).length === 0) {
|
||||||
data = null;
|
data = null;
|
||||||
if (!column.unique) {
|
if (!column.unique) {
|
||||||
if (column.type === CellType.SINGLE_SELECT) {
|
if (column.type === CellType.SINGLE_SELECT || column.type === CellType.MULTIPLE_SELECT) {
|
||||||
data = { options: [] };
|
data = { options: [] };
|
||||||
} else if (column.type === CellType.DATE) {
|
} else if (column.type === CellType.DATE) {
|
||||||
data = { format: DEFAULT_DATE_FORMAT };
|
data = { format: DEFAULT_DATE_FORMAT };
|
||||||
|
@@ -18,12 +18,13 @@ const COLUMNS = [
|
|||||||
{ icon: COLUMNS_ICON_CONFIG[CellType.CHECKBOX], type: CellType.CHECKBOX, name: getColumnDisplayName(PRIVATE_COLUMN_KEY.FILE_EXPIRED), unique: true, key: PRIVATE_COLUMN_KEY.FILE_EXPIRED, canChangeName: false, groupby: 'predefined' },
|
{ icon: COLUMNS_ICON_CONFIG[CellType.CHECKBOX], type: CellType.CHECKBOX, name: getColumnDisplayName(PRIVATE_COLUMN_KEY.FILE_EXPIRED), unique: true, key: PRIVATE_COLUMN_KEY.FILE_EXPIRED, canChangeName: false, groupby: 'predefined' },
|
||||||
{ icon: COLUMNS_ICON_CONFIG[CellType.SINGLE_SELECT], type: CellType.SINGLE_SELECT, name: getColumnDisplayName(PRIVATE_COLUMN_KEY.FILE_STATUS), unique: true, key: PRIVATE_COLUMN_KEY.FILE_STATUS, canChangeName: false, groupby: 'predefined' },
|
{ icon: COLUMNS_ICON_CONFIG[CellType.SINGLE_SELECT], type: CellType.SINGLE_SELECT, name: getColumnDisplayName(PRIVATE_COLUMN_KEY.FILE_STATUS), unique: true, key: PRIVATE_COLUMN_KEY.FILE_STATUS, canChangeName: false, groupby: 'predefined' },
|
||||||
{ icon: COLUMNS_ICON_CONFIG[CellType.TEXT], type: CellType.TEXT, name: gettext(COLUMNS_ICON_NAME[CellType.TEXT]), canChangeName: true, key: CellType.TEXT, groupby: 'basics' },
|
{ icon: COLUMNS_ICON_CONFIG[CellType.TEXT], type: CellType.TEXT, name: gettext(COLUMNS_ICON_NAME[CellType.TEXT]), canChangeName: true, key: CellType.TEXT, groupby: 'basics' },
|
||||||
{ icon: COLUMNS_ICON_CONFIG[CellType.CHECKBOX], type: CellType.CHECKBOX, name: gettext(COLUMNS_ICON_NAME[CellType.CHECKBOX]), canChangeName: true, key: CellType.CHECKBOX, groupby: 'basics' },
|
|
||||||
{ icon: COLUMNS_ICON_CONFIG[CellType.COLLABORATOR], type: CellType.COLLABORATOR, name: gettext(COLUMNS_ICON_NAME[CellType.COLLABORATOR]), canChangeName: true, key: CellType.COLLABORATOR, groupby: 'basics' },
|
|
||||||
{ icon: COLUMNS_ICON_CONFIG[CellType.DATE], type: CellType.DATE, name: gettext(COLUMNS_ICON_NAME[CellType.DATE]), canChangeName: true, key: CellType.DATE, groupby: 'basics' },
|
|
||||||
{ icon: COLUMNS_ICON_CONFIG[CellType.LONG_TEXT], type: CellType.LONG_TEXT, name: gettext(COLUMNS_ICON_NAME[CellType.LONG_TEXT]), canChangeName: true, key: CellType.LONG_TEXT, groupby: 'basics' },
|
{ icon: COLUMNS_ICON_CONFIG[CellType.LONG_TEXT], type: CellType.LONG_TEXT, name: gettext(COLUMNS_ICON_NAME[CellType.LONG_TEXT]), canChangeName: true, key: CellType.LONG_TEXT, groupby: 'basics' },
|
||||||
{ icon: COLUMNS_ICON_CONFIG[CellType.SINGLE_SELECT], type: CellType.SINGLE_SELECT, name: gettext(COLUMNS_ICON_NAME[CellType.SINGLE_SELECT]), canChangeName: true, key: CellType.SINGLE_SELECT, groupby: 'basics' },
|
|
||||||
{ icon: COLUMNS_ICON_CONFIG[CellType.NUMBER], type: CellType.NUMBER, name: gettext(COLUMNS_ICON_NAME[CellType.NUMBER]), canChangeName: true, key: CellType.NUMBER, groupby: 'basics' },
|
{ icon: COLUMNS_ICON_CONFIG[CellType.NUMBER], type: CellType.NUMBER, name: gettext(COLUMNS_ICON_NAME[CellType.NUMBER]), canChangeName: true, key: CellType.NUMBER, groupby: 'basics' },
|
||||||
|
{ icon: COLUMNS_ICON_CONFIG[CellType.COLLABORATOR], type: CellType.COLLABORATOR, name: gettext(COLUMNS_ICON_NAME[CellType.COLLABORATOR]), canChangeName: true, key: CellType.COLLABORATOR, groupby: 'basics' },
|
||||||
|
{ icon: COLUMNS_ICON_CONFIG[CellType.CHECKBOX], type: CellType.CHECKBOX, name: gettext(COLUMNS_ICON_NAME[CellType.CHECKBOX]), canChangeName: true, key: CellType.CHECKBOX, groupby: 'basics' },
|
||||||
|
{ icon: COLUMNS_ICON_CONFIG[CellType.DATE], type: CellType.DATE, name: gettext(COLUMNS_ICON_NAME[CellType.DATE]), canChangeName: true, key: CellType.DATE, groupby: 'basics' },
|
||||||
|
{ icon: COLUMNS_ICON_CONFIG[CellType.SINGLE_SELECT], type: CellType.SINGLE_SELECT, name: gettext(COLUMNS_ICON_NAME[CellType.SINGLE_SELECT]), canChangeName: true, key: CellType.SINGLE_SELECT, groupby: 'basics' },
|
||||||
|
{ icon: COLUMNS_ICON_CONFIG[CellType.MULTIPLE_SELECT], type: CellType.MULTIPLE_SELECT, name: gettext(COLUMNS_ICON_NAME[CellType.MULTIPLE_SELECT]), canChangeName: true, key: CellType.MULTIPLE_SELECT, groupby: 'basics' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
|
@@ -481,6 +481,10 @@ class FilterItem extends React.Component {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
|
let { options = [] } = filterColumn.data || {};
|
||||||
|
return this.renderMultipleSelectOption(options, filter_term, readOnly);
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -523,9 +527,9 @@ class FilterItem extends React.Component {
|
|||||||
} else if (isCheckboxColumn(filterColumn)) {
|
} else if (isCheckboxColumn(filterColumn)) {
|
||||||
_isCheckboxColumn = true;
|
_isCheckboxColumn = true;
|
||||||
}
|
}
|
||||||
const isContainPredicate = [FILTER_PREDICATE_TYPE.CONTAINS, FILTER_PREDICATE_TYPE.NOT_CONTAIN].includes(filter_predicate);
|
// const isContainPredicate = [].includes(filterColumn.type) && [FILTER_PREDICATE_TYPE.CONTAINS, FILTER_PREDICATE_TYPE.NOT_CONTAIN].includes(filter_predicate);
|
||||||
const isRenderErrorTips = this.isRenderErrorTips();
|
// const isRenderErrorTips = this.isRenderErrorTips();
|
||||||
const showToolTip = isContainPredicate && !isRenderErrorTips;
|
// const showToolTip = isContainPredicate && !isRenderErrorTips;
|
||||||
|
|
||||||
// current predicate is not empty
|
// current predicate is not empty
|
||||||
const isNeedShowTermModifier = !EMPTY_PREDICATE.includes(filter_predicate);
|
const isNeedShowTermModifier = !EMPTY_PREDICATE.includes(filter_predicate);
|
||||||
@@ -574,12 +578,14 @@ class FilterItem extends React.Component {
|
|||||||
<div className="filter-term ml-2">
|
<div className="filter-term ml-2">
|
||||||
{this.renderFilterTerm(filterColumn)}
|
{this.renderFilterTerm(filterColumn)}
|
||||||
</div>
|
</div>
|
||||||
{showToolTip &&
|
{/* {showToolTip && (
|
||||||
<div className="ml-2">
|
<div className="ml-2" >
|
||||||
<span ref={this.filterToolTip} id="filter_tool_tip" aria-hidden="true" className="sf-metadata-font sf-metadata-icon-exclamation-triangle" style={{ color: '#FFC92C' }}></span>
|
<IconBtn id={`filter-tool-tip-${filterColumn.key}`} iconName="exclamation-triangle" style={{ color: '#FFC92C' }} />
|
||||||
{/* <UncontrolledTooltip placement="bottom" target={this.filterToolTip}></UncontrolledTooltip> */}
|
<UncontrolledTooltip placement="bottom" target={`filter-tool-tip-${filterColumn.key}`} >
|
||||||
|
{gettext('If there are multiple items in the cell, a random one will be chosen and be compared with the filter value.')}
|
||||||
|
</UncontrolledTooltip>
|
||||||
</div>
|
</div>
|
||||||
}
|
)} */}
|
||||||
{this.renderErrorMessage()}
|
{this.renderErrorMessage()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
/* basic css */
|
/* basic css */
|
||||||
.sf-metadata-single-select-option {
|
.sf-metadata-single-select-option,
|
||||||
|
.sf-metadata-multiple-select-option {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
@@ -12,5 +13,3 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: min-content;
|
width: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -7,11 +7,11 @@ import GridUtils from '../../../utils/grid-utils';
|
|||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
const TableMain = ({ metadata, modifyRecord, modifyRecords, loadMore, loadAll, searchResult, recordGetterByIndex, recordGetterById, ...params }) => {
|
const TableMain = ({ metadata, modifyRecord, modifyRecords, loadMore, loadAll, searchResult, recordGetterByIndex, recordGetterById, modifyColumnData, ...params }) => {
|
||||||
|
|
||||||
const gridUtils = useMemo(() => {
|
const gridUtils = useMemo(() => {
|
||||||
return new GridUtils(metadata, { modifyRecord, modifyRecords, recordGetterByIndex, recordGetterById });
|
return new GridUtils(metadata, { modifyRecord, modifyRecords, recordGetterByIndex, recordGetterById, modifyColumnData });
|
||||||
}, [metadata, modifyRecord, modifyRecords, recordGetterByIndex, recordGetterById]);
|
}, [metadata, modifyRecord, modifyRecords, recordGetterByIndex, recordGetterById, modifyColumnData]);
|
||||||
|
|
||||||
const groupbysCount = useMemo(() => {
|
const groupbysCount = useMemo(() => {
|
||||||
const groupbys = metadata?.view?.groupbys || [];
|
const groupbys = metadata?.view?.groupbys || [];
|
||||||
@@ -63,6 +63,7 @@ const TableMain = ({ metadata, modifyRecord, modifyRecords, loadMore, loadAll, s
|
|||||||
getCopiedRecordsAndColumnsFromRange={getCopiedRecordsAndColumnsFromRange}
|
getCopiedRecordsAndColumnsFromRange={getCopiedRecordsAndColumnsFromRange}
|
||||||
recordGetterById={recordGetterById}
|
recordGetterById={recordGetterById}
|
||||||
recordGetterByIndex={recordGetterByIndex}
|
recordGetterByIndex={recordGetterByIndex}
|
||||||
|
modifyColumnData={modifyColumnData}
|
||||||
{...params}
|
{...params}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -45,6 +45,29 @@ const GroupTitle = ({ column, cellValue, originalCellValue }) => {
|
|||||||
const optionName = selectedOption ? selectedOption.name : deletedOptionTip;
|
const optionName = selectedOption ? selectedOption.name : deletedOptionTip;
|
||||||
return (<div className="sf-metadata-single-select-option" style={style} key={cellValue} title={optionName}>{optionName}</div>);
|
return (<div className="sf-metadata-single-select-option" style={style} key={cellValue} title={optionName}>{optionName}</div>);
|
||||||
}
|
}
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
|
const options = getColumnOptions(column);
|
||||||
|
if (options.length === 0 || !Array.isArray(originalCellValue) || originalCellValue.length === 0) return emptyTip;
|
||||||
|
const selectedOptions = options.filter((option) => originalCellValue.includes(option.id) || originalCellValue.includes(option.name));
|
||||||
|
const invalidOptionIds = originalCellValue.filter(optionId => optionId && !options.find(o => o.id === optionId || o.name === optionId));
|
||||||
|
const invalidOptions = invalidOptionIds.map(optionId => ({
|
||||||
|
id: optionId,
|
||||||
|
name: deletedOptionTip,
|
||||||
|
color: DELETED_OPTION_BACKGROUND_COLOR,
|
||||||
|
}));
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{selectedOptions.map(option => {
|
||||||
|
const style = { backgroundColor: option.color, color: option.textColor };
|
||||||
|
return (<div className="sf-metadata-multiple-select-option" style={style} key={option.id} title={option.name}>{option.name}</div>);
|
||||||
|
})}
|
||||||
|
{invalidOptions.map(option => {
|
||||||
|
const style = { backgroundColor: option.color };
|
||||||
|
return (<div className="sf-metadata-multiple-select-option" style={style} key={option.id} title={option.name}>{option.name}</div>);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return cellValue || emptyTip;
|
return cellValue || emptyTip;
|
||||||
}
|
}
|
||||||
|
@@ -213,6 +213,14 @@
|
|||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.canvas-groups-rows .group-title .sf-metadata-multiple-select-option {
|
||||||
|
margin: 0 10px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas-groups-rows .group-title .sf-metadata-multiple-select-option:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.canvas-groups-rows .collaborator-avatar,
|
.canvas-groups-rows .collaborator-avatar,
|
||||||
.canvas-groups-rows .sf-metadata-ui.collaborator-item .collaborator-avatar {
|
.canvas-groups-rows .sf-metadata-ui.collaborator-item .collaborator-avatar {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
@@ -181,6 +181,16 @@ const HeaderDropdownMenu = ({ column, renameColumn, modifyColumnData, deleteColu
|
|||||||
/> */}
|
/> */}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{type === CellType.MULTIPLE_SELECT && (
|
||||||
|
<DropdownItem
|
||||||
|
disabled={!canModifyColumnData}
|
||||||
|
target="sf-metadata-edit-column-options"
|
||||||
|
iconName="multiple-select"
|
||||||
|
title={gettext('Edit multiple select')}
|
||||||
|
tip={gettext('You do not have permission')}
|
||||||
|
onChange={openOptionPopover}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{type === CellType.NUMBER && (
|
{type === CellType.NUMBER && (
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
disabled={!canModifyColumnData}
|
disabled={!canModifyColumnData}
|
||||||
@@ -194,7 +204,7 @@ const HeaderDropdownMenu = ({ column, renameColumn, modifyColumnData, deleteColu
|
|||||||
{type === CellType.DATE && (
|
{type === CellType.DATE && (
|
||||||
<>{renderDateFormat(canModifyColumnData)}</>
|
<>{renderDateFormat(canModifyColumnData)}</>
|
||||||
)}
|
)}
|
||||||
{[CellType.DATE, CellType.SINGLE_SELECT, CellType.NUMBER].includes(column.type) && (
|
{[CellType.DATE, CellType.SINGLE_SELECT, CellType.NUMBER, CellType.MULTIPLE_SELECT].includes(column.type) && (
|
||||||
<DefaultDropdownItem key="divider-item" divider />
|
<DefaultDropdownItem key="divider-item" divider />
|
||||||
)}
|
)}
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
|
@@ -1,45 +1,17 @@
|
|||||||
import { CellType, DEFAULT_DATE_FORMAT, generatorCellOption, getCollaboratorsName, getOptionName, getDateDisplayString,
|
import { CellType, DEFAULT_DATE_FORMAT, generatorCellOption, getCollaboratorsName, getOptionName, getDateDisplayString,
|
||||||
PREDEFINED_COLUMN_KEYS, getFloatNumber, getNumberDisplayString, formatStringToNumber, isNumber, getColumnOptions,
|
PREDEFINED_COLUMN_KEYS, getFloatNumber, getNumberDisplayString, formatStringToNumber, isNumber, getColumnOptions,
|
||||||
|
generatorCellOptions,
|
||||||
|
getColumnOptionNamesByIds,
|
||||||
} from '../_basic';
|
} from '../_basic';
|
||||||
import { formatTextToDate } from './date';
|
import { formatTextToDate } from './date';
|
||||||
|
|
||||||
const SUPPORT_PASTE_FROM_COLUMN = {
|
const SUPPORT_PASTE_FROM_COLUMN = {
|
||||||
|
[CellType.MULTIPLE_SELECT]: [CellType.MULTIPLE_SELECT, CellType.TEXT, CellType.SINGLE_SELECT],
|
||||||
[CellType.NUMBER]: [CellType.TEXT, CellType.NUMBER],
|
[CellType.NUMBER]: [CellType.TEXT, CellType.NUMBER],
|
||||||
};
|
};
|
||||||
|
|
||||||
const reg_chinese_date_format = /(\d{4})年(\d{1,2})月(\d{1,2})日$/;
|
const reg_chinese_date_format = /(\d{4})年(\d{1,2})月(\d{1,2})日$/;
|
||||||
|
|
||||||
function convertCellValue(cellValue, oldCellValue, targetColumn, fromColumn) {
|
|
||||||
const { type: fromColumnType, data: fromColumnData } = fromColumn;
|
|
||||||
const { type: targetColumnType, data: targetColumnData } = targetColumn;
|
|
||||||
switch (targetColumnType) {
|
|
||||||
case CellType.CHECKBOX: {
|
|
||||||
return convert2Checkbox(cellValue, oldCellValue, fromColumnType);
|
|
||||||
}
|
|
||||||
case CellType.NUMBER: {
|
|
||||||
return convert2Number(cellValue, oldCellValue, fromColumnType, targetColumnData);
|
|
||||||
}
|
|
||||||
case CellType.DATE: {
|
|
||||||
return convert2Date(cellValue, oldCellValue, fromColumnType, fromColumnData, targetColumnData);
|
|
||||||
}
|
|
||||||
case CellType.SINGLE_SELECT: {
|
|
||||||
return convert2SingleSelect(cellValue, oldCellValue, fromColumn, targetColumn);
|
|
||||||
}
|
|
||||||
case CellType.LONG_TEXT: {
|
|
||||||
return convert2LongText(cellValue, oldCellValue, fromColumn);
|
|
||||||
}
|
|
||||||
case CellType.TEXT: {
|
|
||||||
return convert2Text(cellValue, oldCellValue, fromColumn);
|
|
||||||
}
|
|
||||||
case CellType.COLLABORATOR: {
|
|
||||||
return convert2Collaborator(cellValue, oldCellValue, fromColumnType);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return oldCellValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function convert2Checkbox(cellValue, oldCellValue, fromColumnType) {
|
function convert2Checkbox(cellValue, oldCellValue, fromColumnType) {
|
||||||
switch (fromColumnType) {
|
switch (fromColumnType) {
|
||||||
case CellType.CHECKBOX: {
|
case CellType.CHECKBOX: {
|
||||||
@@ -138,6 +110,12 @@ function convert2SingleSelect(cellValue, oldCellValue, fromColumn, targetColumn)
|
|||||||
fromOptionName = getOptionName(fromOptions, cellValue) || '';
|
fromOptionName = getOptionName(fromOptions, cellValue) || '';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
|
const copiedOptions = getColumnOptions(fromColumn);
|
||||||
|
const copiedCellVal = cellValue[0];
|
||||||
|
fromOptionName = getOptionName(copiedOptions, copiedCellVal) || '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
case CellType.TEXT: {
|
case CellType.TEXT: {
|
||||||
fromOptionName = cellValue;
|
fromOptionName = cellValue;
|
||||||
break;
|
break;
|
||||||
@@ -292,4 +270,94 @@ function convert2Collaborator(cellValue, oldCellValue, fromColumnType) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _getPasteMultipleSelect = (copiedCellVal, pasteCellVal, copiedColumn, pasteColumn) => {
|
||||||
|
const { type: copiedColumnType } = copiedColumn;
|
||||||
|
if (!copiedCellVal ||
|
||||||
|
(Array.isArray(copiedCellVal) && copiedCellVal.length === 0) ||
|
||||||
|
!SUPPORT_PASTE_FROM_COLUMN[CellType.MULTIPLE_SELECT].includes(copiedColumnType)
|
||||||
|
) {
|
||||||
|
return { selectedOptionIds: pasteCellVal };
|
||||||
|
}
|
||||||
|
let copiedOptionNames = [];
|
||||||
|
if (copiedColumnType === CellType.MULTIPLE_SELECT) {
|
||||||
|
const copiedOptions = getColumnOptions(copiedColumn);
|
||||||
|
copiedOptionNames = copiedOptions.filter((option) => copiedCellVal.includes(option.id) || copiedCellVal.includes(option.name))
|
||||||
|
.map((option) => option.name);
|
||||||
|
} else if (copiedColumnType === CellType.TEXT) {
|
||||||
|
const sCopiedCellVal = String(copiedCellVal);
|
||||||
|
|
||||||
|
// Pass excel test, wps test failed
|
||||||
|
copiedOptionNames = sCopiedCellVal.split('\n');
|
||||||
|
|
||||||
|
// get option names from string like 'a, b, c'
|
||||||
|
if (copiedOptionNames.length === 1) {
|
||||||
|
copiedOptionNames = sCopiedCellVal.split(',');
|
||||||
|
}
|
||||||
|
copiedOptionNames = copiedOptionNames.map(name => name.trim())
|
||||||
|
.filter(name => name !== '');
|
||||||
|
} else if (copiedColumnType === CellType.SINGLE_SELECT) {
|
||||||
|
const copiedOptions = getColumnOptions(copiedColumn);
|
||||||
|
copiedOptionNames = copiedOptions.filter((option) => option.id === copiedCellVal)
|
||||||
|
.map((option) => option.name);
|
||||||
|
}
|
||||||
|
if (copiedOptionNames.length === 0) {
|
||||||
|
return { selectedOptionIds: pasteCellVal };
|
||||||
|
}
|
||||||
|
|
||||||
|
const pasteOptions = getColumnOptions(pasteColumn);
|
||||||
|
const { cellOptions: newCellOptions, selectedOptionIds } = generatorCellOptions(pasteOptions, copiedOptionNames);
|
||||||
|
return { pasteOptions, newCellOptions, selectedOptionIds };
|
||||||
|
};
|
||||||
|
|
||||||
|
const convert2MultipleSelect = (copiedCellVal, pasteCellVal, copiedColumn, pasteColumn, api) => {
|
||||||
|
const { newCellOptions, pasteOptions, selectedOptionIds } = _getPasteMultipleSelect(copiedCellVal, pasteCellVal, copiedColumn, pasteColumn);
|
||||||
|
let newColumn = pasteColumn;
|
||||||
|
|
||||||
|
// the target column have no options with the same name
|
||||||
|
if (newCellOptions) {
|
||||||
|
if (!window.sfMetadataContext.canModifyColumnData(pasteColumn)) return null;
|
||||||
|
const updatedPasteOptions = [...pasteOptions, ...newCellOptions];
|
||||||
|
if (!newColumn.data) {
|
||||||
|
newColumn.data = {};
|
||||||
|
}
|
||||||
|
newColumn.data.options = updatedPasteOptions;
|
||||||
|
api.modifyColumnData(pasteColumn.key, { options: updatedPasteOptions }, pasteColumn.data);
|
||||||
|
}
|
||||||
|
return getColumnOptionNamesByIds(newColumn, selectedOptionIds);
|
||||||
|
};
|
||||||
|
|
||||||
|
function convertCellValue(cellValue, oldCellValue, targetColumn, fromColumn, api) {
|
||||||
|
const { type: fromColumnType, data: fromColumnData } = fromColumn;
|
||||||
|
const { type: targetColumnType, data: targetColumnData } = targetColumn;
|
||||||
|
switch (targetColumnType) {
|
||||||
|
case CellType.CHECKBOX: {
|
||||||
|
return convert2Checkbox(cellValue, oldCellValue, fromColumnType);
|
||||||
|
}
|
||||||
|
case CellType.NUMBER: {
|
||||||
|
return convert2Number(cellValue, oldCellValue, fromColumnType, targetColumnData);
|
||||||
|
}
|
||||||
|
case CellType.DATE: {
|
||||||
|
return convert2Date(cellValue, oldCellValue, fromColumnType, fromColumnData, targetColumnData);
|
||||||
|
}
|
||||||
|
case CellType.SINGLE_SELECT: {
|
||||||
|
return convert2SingleSelect(cellValue, oldCellValue, fromColumn, targetColumn);
|
||||||
|
}
|
||||||
|
case CellType.MULTIPLE_SELECT: {
|
||||||
|
return convert2MultipleSelect(cellValue, oldCellValue, fromColumn, targetColumn, api);
|
||||||
|
}
|
||||||
|
case CellType.LONG_TEXT: {
|
||||||
|
return convert2LongText(cellValue, oldCellValue, fromColumn);
|
||||||
|
}
|
||||||
|
case CellType.TEXT: {
|
||||||
|
return convert2Text(cellValue, oldCellValue, fromColumn);
|
||||||
|
}
|
||||||
|
case CellType.COLLABORATOR: {
|
||||||
|
return convert2Collaborator(cellValue, oldCellValue, fromColumnType);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return oldCellValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export { convertCellValue };
|
export { convertCellValue };
|
||||||
|
@@ -107,7 +107,7 @@ class GridUtils {
|
|||||||
const copiedColumnName = getColumnOriginName(copiedColumn);
|
const copiedColumnName = getColumnOriginName(copiedColumn);
|
||||||
const pasteCellValue = Object.prototype.hasOwnProperty.call(pasteRecord, pasteColumnName) ? getCellValueByColumn(pasteRecord, pasteColumn) : null;
|
const pasteCellValue = Object.prototype.hasOwnProperty.call(pasteRecord, pasteColumnName) ? getCellValueByColumn(pasteRecord, pasteColumn) : null;
|
||||||
const copiedCellValue = Object.prototype.hasOwnProperty.call(copiedRecord, copiedColumnName) ? getCellValueByColumn(copiedRecord, copiedColumn) : null;
|
const copiedCellValue = Object.prototype.hasOwnProperty.call(copiedRecord, copiedColumnName) ? getCellValueByColumn(copiedRecord, copiedColumn) : null;
|
||||||
const update = convertCellValue(copiedCellValue, pasteCellValue, pasteColumn, copiedColumn);
|
const update = convertCellValue(copiedCellValue, pasteCellValue, pasteColumn, copiedColumn, this.api);
|
||||||
if (update === pasteCellValue) {
|
if (update === pasteCellValue) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user