diff --git a/frontend/package-lock.json b/frontend/package-lock.json index df67ea5658..19634fe14e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,7 +19,7 @@ "@seafile/sdoc-editor": "1.0.35", "@seafile/seafile-calendar": "0.0.12", "@seafile/seafile-editor": "1.0.107", - "@seafile/sf-metadata-ui-component": "0.0.13", + "@seafile/sf-metadata-ui-component": "0.0.14", "@uiw/codemirror-extensions-langs": "^4.19.4", "@uiw/react-codemirror": "^4.19.4", "chart.js": "2.9.4", @@ -4971,9 +4971,9 @@ } }, "node_modules/@seafile/sf-metadata-ui-component": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.13.tgz", - "integrity": "sha512-AVKM/4wKcXkc0UWhWHLfvWtfC0Ox18VfX0QBoypY/RxPLXS1mQecw1DMdXVgnZNN22KdmiyZiNRkagmP1ARNhg==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.14.tgz", + "integrity": "sha512-4IUQ5LahSMfEEMoBIr76OAYjpS2KdLuZs5+17asRJVImmqJPLB/HwbiqfsGsmCsAcOefiB9MFAu19i5ai3mngQ==", "dependencies": { "@seafile/seafile-calendar": "0.0.24", "classnames": "2.3.2", @@ -32200,9 +32200,9 @@ } }, "@seafile/sf-metadata-ui-component": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.13.tgz", - "integrity": "sha512-AVKM/4wKcXkc0UWhWHLfvWtfC0Ox18VfX0QBoypY/RxPLXS1mQecw1DMdXVgnZNN22KdmiyZiNRkagmP1ARNhg==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.14.tgz", + "integrity": "sha512-4IUQ5LahSMfEEMoBIr76OAYjpS2KdLuZs5+17asRJVImmqJPLB/HwbiqfsGsmCsAcOefiB9MFAu19i5ai3mngQ==", "requires": { "@seafile/seafile-calendar": "0.0.24", "classnames": "2.3.2", diff --git a/frontend/package.json b/frontend/package.json index 990d38080a..d85111faa9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,7 @@ "@seafile/sdoc-editor": "1.0.35", "@seafile/seafile-calendar": "0.0.12", "@seafile/seafile-editor": "1.0.107", - "@seafile/sf-metadata-ui-component": "0.0.13", + "@seafile/sf-metadata-ui-component": "0.0.14", "@uiw/codemirror-extensions-langs": "^4.19.4", "@uiw/react-codemirror": "^4.19.4", "chart.js": "2.9.4", diff --git a/frontend/src/assets/icons/location.svg b/frontend/src/assets/icons/location.svg new file mode 100644 index 0000000000..03785bdfe0 --- /dev/null +++ b/frontend/src/assets/icons/location.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/metadata/metadata-view/_basic/constants/column/format.js b/frontend/src/metadata/metadata-view/_basic/constants/column/format.js index 9b7e97dffa..d1bbbdd508 100644 --- a/frontend/src/metadata/metadata-view/_basic/constants/column/format.js +++ b/frontend/src/metadata/metadata-view/_basic/constants/column/format.js @@ -75,6 +75,15 @@ const DATE_DEFAULT_TYPES = { DAYS_AFTER: 'days_after', }; +const GEOLOCATION_FORMAT = { + LNG_LAT: 'lng_lat', + COUNTRY_REGION: 'country_region', + PROVINCE: 'province', + PROVINCE_CITY: 'province_city', + PROVINCE_CITY_DISTRICT: 'province_city_district', + MAP_SELECTION: 'map_selection', +}; + export { COLLABORATOR_COLUMN_TYPES, DATE_COLUMN_OPTIONS, @@ -89,4 +98,5 @@ export { NOT_SUPPORT_EDIT_COLUMN_TYPE_MAP, MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP, SINGLE_CELL_VALUE_COLUMN_TYPE_MAP, + GEOLOCATION_FORMAT, }; diff --git a/frontend/src/metadata/metadata-view/_basic/constants/column/icon.js b/frontend/src/metadata/metadata-view/_basic/constants/column/icon.js index 279d75db63..a1d9b33935 100644 --- a/frontend/src/metadata/metadata-view/_basic/constants/column/icon.js +++ b/frontend/src/metadata/metadata-view/_basic/constants/column/icon.js @@ -14,6 +14,7 @@ const COLUMNS_ICON_CONFIG = { [CellType.LONG_TEXT]: 'long-text', [CellType.SINGLE_SELECT]: 'single-select', [CellType.NUMBER]: 'number', + [CellType.GEOLOCATION]: 'location', }; const COLUMNS_ICON_NAME = { @@ -30,6 +31,7 @@ const COLUMNS_ICON_NAME = { [CellType.LONG_TEXT]: 'Long text', [CellType.SINGLE_SELECT]: 'Single select', [CellType.NUMBER]: 'Number', + [CellType.GEOLOCATION]: 'Geolocation', }; export { diff --git a/frontend/src/metadata/metadata-view/_basic/constants/column/index.js b/frontend/src/metadata/metadata-view/_basic/constants/column/index.js index 063108bc51..f056524dd2 100644 --- a/frontend/src/metadata/metadata-view/_basic/constants/column/index.js +++ b/frontend/src/metadata/metadata-view/_basic/constants/column/index.js @@ -18,6 +18,7 @@ export { NOT_SUPPORT_EDIT_COLUMN_TYPE_MAP, MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP, SINGLE_CELL_VALUE_COLUMN_TYPE_MAP, + GEOLOCATION_FORMAT, } from './format'; export { diff --git a/frontend/src/metadata/metadata-view/_basic/constants/column/private.js b/frontend/src/metadata/metadata-view/_basic/constants/column/private.js index 510d3164ac..ef72ea42bc 100644 --- a/frontend/src/metadata/metadata-view/_basic/constants/column/private.js +++ b/frontend/src/metadata/metadata-view/_basic/constants/column/private.js @@ -21,6 +21,7 @@ export const PRIVATE_COLUMN_KEY = { FILE_SUMMARY: '_summary', FILE_EXPIRED: '_expired', FILE_STATUS: '_status', + LOCATION: '_location', }; export const PRIVATE_COLUMN_KEYS = [ @@ -43,4 +44,5 @@ export const PRIVATE_COLUMN_KEYS = [ PRIVATE_COLUMN_KEY.FILE_SUMMARY, PRIVATE_COLUMN_KEY.FILE_EXPIRED, PRIVATE_COLUMN_KEY.FILE_STATUS, + PRIVATE_COLUMN_KEY.LOCATION, ]; diff --git a/frontend/src/metadata/metadata-view/_basic/constants/column/type.js b/frontend/src/metadata/metadata-view/_basic/constants/column/type.js index 34b7e6ad29..51cae4d209 100644 --- a/frontend/src/metadata/metadata-view/_basic/constants/column/type.js +++ b/frontend/src/metadata/metadata-view/_basic/constants/column/type.js @@ -12,6 +12,7 @@ const CellType = { LONG_TEXT: 'long-text', SINGLE_SELECT: 'single-select', NUMBER: 'number', + GEOLOCATION: 'geolocation', }; export default CellType; diff --git a/frontend/src/metadata/metadata-view/_basic/constants/index.js b/frontend/src/metadata/metadata-view/_basic/constants/index.js index 07aa524184..7855f0d936 100644 --- a/frontend/src/metadata/metadata-view/_basic/constants/index.js +++ b/frontend/src/metadata/metadata-view/_basic/constants/index.js @@ -23,6 +23,7 @@ export { NOT_DISPLAY_COLUMN_KEYS, VIEW_NOT_DISPLAY_COLUMN_KEYS, PREDEFINED_COLUMN_KEYS, + GEOLOCATION_FORMAT, } from './column'; export { FILTER_CONJUNCTION_TYPE, diff --git a/frontend/src/metadata/metadata-view/_basic/index.js b/frontend/src/metadata/metadata-view/_basic/index.js index 6ee24a5e86..1294ca30c0 100644 --- a/frontend/src/metadata/metadata-view/_basic/index.js +++ b/frontend/src/metadata/metadata-view/_basic/index.js @@ -147,4 +147,6 @@ export { debounce, throttle, isRegExpression, + getGeolocationDisplayString, + getGeolocationByGranularity, } from './utils'; diff --git a/frontend/src/metadata/metadata-view/_basic/utils/cell/column/geolocation.js b/frontend/src/metadata/metadata-view/_basic/utils/cell/column/geolocation.js new file mode 100644 index 0000000000..0d65cff4c5 --- /dev/null +++ b/frontend/src/metadata/metadata-view/_basic/utils/cell/column/geolocation.js @@ -0,0 +1,82 @@ +import { GROUP_GEOLOCATION_GRANULARITY, GEOLOCATION_FORMAT } from '../../../constants'; +import { isValidPosition } from '../../validate'; + +/** + * Get formatted geolocation + * @param {object} loc + * @param {object} formats , e.g. { geo_format, ... } + * @param {bool} isBaiduMap Determine the way to connect latitude and longitude. Default as true + * @param {string} hyphen Used as a connector between province, city, district and detail. Default as empty string + * @returns formatted geolocation, string + */ +const getGeolocationDisplayString = (loc, formats, { isBaiduMap = true, hyphen = '' } = {}) => { + if (!loc) return ''; + const { geo_format } = formats || {}; + switch (geo_format) { + case GEOLOCATION_FORMAT.LNG_LAT: { + const { lng, lat } = loc; + if (!isValidPosition(lng, lat)) return ''; + return isBaiduMap ? `${lng}, ${lat}` : `${lat}, ${lng}`; + } + case GEOLOCATION_FORMAT.COUNTRY_REGION: { + const { country_region } = loc; + return country_region || ''; + } + case GEOLOCATION_FORMAT.PROVINCE: { + const { province } = loc; + return province || ''; + } + case GEOLOCATION_FORMAT.PROVINCE_CITY: { + const { province, city } = loc; + return (`${province || ''}${hyphen}${city || ''}`).trim(); + } + case GEOLOCATION_FORMAT.PROVINCE_CITY_DISTRICT: { + const { province, city, district } = loc; + return (`${province || ''}${hyphen}${city || ''}${hyphen}${district || ''}`).trim(); + } + case GEOLOCATION_FORMAT.MAP_SELECTION: { + const { address, title } = loc; + return (`${address || ''}${hyphen}${title || ''}`).trim(); + } + default: { + // default as 'geolocation' + const { + province, city, district, detail, + } = loc; + if (!province && !city && !district && !detail) return ''; + return (`${province || ''}${hyphen}${city || ''}${hyphen}${district || ''}${hyphen}${detail || ''}`).trim(); + } + } +}; + +/** + * Get geolocation by granularity + * @param {object} geolocation e.g. { province, ... } + * @param {string} granularity + * @returns geolocation string + */ +const getGeolocationByGranularity = (geolocation, granularity) => { + if (!geolocation) return ''; + const { + province, city, district, country_region, + } = geolocation; + switch (granularity) { + case GROUP_GEOLOCATION_GRANULARITY.CITY: { + return city || ''; + } + case GROUP_GEOLOCATION_GRANULARITY.DISTRICT: { + return district || ''; + } + case GROUP_GEOLOCATION_GRANULARITY.COUNTRY: { + return country_region || ''; + } + default: { + return province || ''; + } + } +}; + +export { + getGeolocationDisplayString, + getGeolocationByGranularity, +}; diff --git a/frontend/src/metadata/metadata-view/_basic/utils/cell/column/index.js b/frontend/src/metadata/metadata-view/_basic/utils/cell/column/index.js index a82580410b..56cd156f9f 100644 --- a/frontend/src/metadata/metadata-view/_basic/utils/cell/column/index.js +++ b/frontend/src/metadata/metadata-view/_basic/utils/cell/column/index.js @@ -19,3 +19,7 @@ export { getCollaboratorEmailsByNames, } from './collaborator'; export { getLongtextDisplayString } from './long-text'; +export { + getGeolocationDisplayString, + getGeolocationByGranularity, +} from './geolocation'; diff --git a/frontend/src/metadata/metadata-view/_basic/utils/cell/index.js b/frontend/src/metadata/metadata-view/_basic/utils/cell/index.js index 48977141ed..05e5177a70 100644 --- a/frontend/src/metadata/metadata-view/_basic/utils/cell/index.js +++ b/frontend/src/metadata/metadata-view/_basic/utils/cell/index.js @@ -19,4 +19,6 @@ export { getCollaboratorsName, getCollaboratorEmailsByNames, getLongtextDisplayString, + getGeolocationDisplayString, + getGeolocationByGranularity, } from './column'; diff --git a/frontend/src/metadata/metadata-view/_basic/utils/index.js b/frontend/src/metadata/metadata-view/_basic/utils/index.js index a57a34a61f..47073b0da1 100644 --- a/frontend/src/metadata/metadata-view/_basic/utils/index.js +++ b/frontend/src/metadata/metadata-view/_basic/utils/index.js @@ -19,6 +19,8 @@ export { getCollaboratorsName, getCollaboratorEmailsByNames, getLongtextDisplayString, + getGeolocationDisplayString, + getGeolocationByGranularity, } from './cell'; export { getColumnType, diff --git a/frontend/src/metadata/metadata-view/_basic/utils/validate/geolocation.js b/frontend/src/metadata/metadata-view/_basic/utils/validate/geolocation.js new file mode 100644 index 0000000000..cb423fb5f7 --- /dev/null +++ b/frontend/src/metadata/metadata-view/_basic/utils/validate/geolocation.js @@ -0,0 +1,5 @@ +const isValidPosition = (lng, lat) => (lng || lng === 0) && (lat || lat === 0); + +export { + isValidPosition, +}; diff --git a/frontend/src/metadata/metadata-view/_basic/utils/validate/index.js b/frontend/src/metadata/metadata-view/_basic/utils/validate/index.js index 45cd4da56f..6b85ed6269 100644 --- a/frontend/src/metadata/metadata-view/_basic/utils/validate/index.js +++ b/frontend/src/metadata/metadata-view/_basic/utils/validate/index.js @@ -3,3 +3,6 @@ export { ValidateFilter, DATE_MODIFIERS_REQUIRE_TERM, } from './filter'; +export { + isValidPosition, +} from './geolocation'; diff --git a/frontend/src/metadata/metadata-view/components/table/table-main/records/record/cell/index.css b/frontend/src/metadata/metadata-view/components/table/table-main/records/record/cell/index.css index 18964b752e..93a415086a 100644 --- a/frontend/src/metadata/metadata-view/components/table/table-main/records/record/cell/index.css +++ b/frontend/src/metadata/metadata-view/components/table/table-main/records/record/cell/index.css @@ -10,6 +10,10 @@ justify-content: flex-start; } +.sf-metadata-result-table-cell:not(.table-cell-uneditable):hover { + cursor: pointer; +} + .sf-metadata-result-table-cell.index { width: 90px; height: 100%; diff --git a/frontend/src/metadata/metadata-view/components/table/table-main/records/record/cell/index.js b/frontend/src/metadata/metadata-view/components/table/table-main/records/record/cell/index.js index 93c63cf9fe..92ca54138b 100644 --- a/frontend/src/metadata/metadata-view/components/table/table-main/records/record/cell/index.js +++ b/frontend/src/metadata/metadata-view/components/table/table-main/records/record/cell/index.js @@ -29,7 +29,7 @@ const Cell = React.memo(({ const { type } = column; const canEditable = window.sfMetadataContext.canModifyCell(column); return classnames('sf-metadata-result-table-cell', `sf-metadata-result-table-${type}-cell`, { - 'table-cell-uneditable': !canEditable && TABLE_SUPPORT_EDIT_TYPE_MAP[type], + 'table-cell-uneditable': !canEditable || !TABLE_SUPPORT_EDIT_TYPE_MAP[type], [highlightClassName]: highlightClassName, 'last-cell': isLastCell, 'table-last--frozen': isLastFrozenCell, diff --git a/frontend/src/metadata/metadata-view/constants/index.js b/frontend/src/metadata/metadata-view/constants/index.js index ec49afe8a0..cf9fbf511b 100644 --- a/frontend/src/metadata/metadata-view/constants/index.js +++ b/frontend/src/metadata/metadata-view/constants/index.js @@ -38,6 +38,11 @@ export const TABLE_NOT_SUPPORT_EDIT_TYPE_MAP = { export const TABLE_SUPPORT_EDIT_TYPE_MAP = { [CellType.TEXT]: true, + [CellType.DATE]: true, + [CellType.NUMBER]: true, + [CellType.SINGLE_SELECT]: true, + [CellType.COLLABORATOR]: true, + [CellType.CHECKBOX]: true, }; export const TABLE_MOBILE_SUPPORT_EDIT_CELL_TYPE_MAP = { diff --git a/frontend/src/metadata/metadata-view/utils/column-utils.js b/frontend/src/metadata/metadata-view/utils/column-utils.js index 6c0b46bf82..6a7a3a4887 100644 --- a/frontend/src/metadata/metadata-view/utils/column-utils.js +++ b/frontend/src/metadata/metadata-view/utils/column-utils.js @@ -202,6 +202,8 @@ export const getColumnName = (key, name) => { return gettext('Is expired'); case PRIVATE_COLUMN_KEY.FILE_STATUS: return gettext('File status'); + case PRIVATE_COLUMN_KEY.LOCATION: + return gettext('Image location'); default: return name; } @@ -237,6 +239,8 @@ const getColumnType = (key, type) => { return CellType.CHECKBOX; case PRIVATE_COLUMN_KEY.FILE_STATUS: return CellType.SINGLE_SELECT; + case PRIVATE_COLUMN_KEY.LOCATION: + return CellType.GEOLOCATION; default: return type; }