mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-26 15:26:19 +00:00
fix: bug
This commit is contained in:
@@ -9,7 +9,7 @@ import { siteRoot, gettext } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { PRIVATE_FILE_TYPE } from '../../constants';
|
||||
import { debounce } from '../../metadata/utils/common';
|
||||
import { EVENT_BUS_TYPE, FACE_RECOGNITION_VIEW_ID } from '../../metadata/constants';
|
||||
import { EVENT_BUS_TYPE } from '../../metadata/constants';
|
||||
import { ALL_TAGS_ID } from '../../tag/constants';
|
||||
|
||||
const propTypes = {
|
||||
@@ -126,13 +126,12 @@ class DirPath extends React.Component {
|
||||
turnViewPathToLink = (pathList) => {
|
||||
if (!Array.isArray(pathList) || pathList.length === 0) return null;
|
||||
const [, , viewId, children] = pathList;
|
||||
const isViewSupportClick = viewId === FACE_RECOGNITION_VIEW_ID && children;
|
||||
return (
|
||||
<>
|
||||
<span className="path-split">/</span>
|
||||
<span className="path-item">{gettext('Views')}</span>
|
||||
<span className="path-split">/</span>
|
||||
<span className="path-item" role={isViewSupportClick ? 'button' : null} onClick={isViewSupportClick ? this.handleRefresh : () => {}}>
|
||||
<span className="path-item" role={children ? 'button' : null} onClick={children ? this.handleRefresh : () => {}}>
|
||||
<MetadataViewName id={viewId} />
|
||||
</span>
|
||||
{children && (
|
||||
|
@@ -0,0 +1,37 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EVENT_BUS_TYPE, GALLERY_DATE_MODE, STORAGE_GALLERY_DATE_MODE_KEY } from '../../constants';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import RadioGroup from '../radio-group';
|
||||
|
||||
const DATE_MODES = [
|
||||
{ value: GALLERY_DATE_MODE.YEAR, label: gettext('Year') },
|
||||
{ value: GALLERY_DATE_MODE.MONTH, label: gettext('Month') },
|
||||
{ value: GALLERY_DATE_MODE.DAY, label: gettext('Day') },
|
||||
{ value: GALLERY_DATE_MODE.ALL, label: gettext('All') },
|
||||
];
|
||||
|
||||
const GalleryGroupBySetter = ({ view }) => {
|
||||
const [currentMode, setCurrentMode] = useState(GALLERY_DATE_MODE.DAY);
|
||||
|
||||
useEffect(() => {
|
||||
const savedValue = window.sfMetadataContext.localStorage.getItem(STORAGE_GALLERY_DATE_MODE_KEY) || GALLERY_DATE_MODE.DAY;
|
||||
setCurrentMode(savedValue);
|
||||
}, [view?._id]);
|
||||
|
||||
const handleGroupByChange = useCallback((newMode) => {
|
||||
if (currentMode === newMode) return;
|
||||
setCurrentMode(newMode);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SWITCH_GALLERY_GROUP_BY, newMode);
|
||||
}, [currentMode]);
|
||||
|
||||
return (<RadioGroup value={currentMode} options={DATE_MODES} onChange={handleGroupByChange} />);
|
||||
};
|
||||
|
||||
GalleryGroupBySetter.propTypes = {
|
||||
view: PropTypes.shape({
|
||||
_id: PropTypes.string
|
||||
})
|
||||
};
|
||||
|
||||
export default GalleryGroupBySetter;
|
@@ -1,57 +0,0 @@
|
||||
.metadata-gallery-group-by-setter {
|
||||
width: 272px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.metadata-gallery-group-by-setter .metadata-gallery-group-by-button {
|
||||
width: 66px;
|
||||
height: 28px;
|
||||
color: #212529;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.875rem;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.metadata-gallery-group-by-setter .metadata-gallery-group-by-button:hover {
|
||||
background-color: #f0f0f0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.metadata-gallery-group-by-setter .metadata-gallery-group-by-button.active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.metadata-gallery-group-by-setter .metadata-gallery-group-by-button span {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.metadata-gallery-group-by-button:not(:first-child)::before {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 22px;
|
||||
background-color: #e2e2e2;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transition: opacity 0.3s;
|
||||
left: -1px;
|
||||
}
|
||||
|
||||
.metadata-gallery-group-by-button:hover::before,
|
||||
.metadata-gallery-group-by-button.active::before,
|
||||
.metadata-gallery-group-by-button:hover + .metadata-gallery-group-by-button::before,
|
||||
.metadata-gallery-group-by-button.active + .metadata-gallery-group-by-button::before {
|
||||
opacity: 0;
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { EVENT_BUS_TYPE, GALLERY_DATE_MODE } from '../../../constants';
|
||||
import { gettext } from '../../../../utils/constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const DATE_MODE_MAP = {
|
||||
[GALLERY_DATE_MODE.YEAR]: gettext('Year'),
|
||||
[GALLERY_DATE_MODE.MONTH]: gettext('Month'),
|
||||
[GALLERY_DATE_MODE.DAY]: gettext('Day'),
|
||||
[GALLERY_DATE_MODE.ALL]: gettext('All')
|
||||
};
|
||||
|
||||
const GalleryGroupBySetter = ({ view }) => {
|
||||
const [currentMode, setCurrentMode] = useState(GALLERY_DATE_MODE.DAY);
|
||||
|
||||
useEffect(() => {
|
||||
const savedValue = window.sfMetadataContext.localStorage.getItem('gallery-group-by', GALLERY_DATE_MODE.DAY);
|
||||
setCurrentMode(savedValue || GALLERY_DATE_MODE.DAY);
|
||||
}, [view?._id]);
|
||||
|
||||
const handleGroupByChange = useCallback((newMode) => {
|
||||
setCurrentMode(newMode);
|
||||
window.sfMetadataContext.localStorage.setItem('gallery-group-by', newMode);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SWITCH_GALLERY_GROUP_BY, newMode);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="metadata-gallery-group-by-setter">
|
||||
{Object.entries(DATE_MODE_MAP).map(([dateMode, label]) => (
|
||||
<button
|
||||
key={dateMode}
|
||||
className={classnames('metadata-gallery-group-by-button', { active: currentMode === dateMode })}
|
||||
onClick={() => handleGroupByChange(dateMode)}
|
||||
>
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
GalleryGroupBySetter.propTypes = {
|
||||
view: PropTypes.shape({
|
||||
_id: PropTypes.string
|
||||
})
|
||||
};
|
||||
|
||||
export default GalleryGroupBySetter;
|
@@ -1,4 +1,4 @@
|
||||
import GalleryGroupBySetter from './gallery-group-by-setter/index';
|
||||
import GalleryGroupBySetter from './gallery-group-by-setter';
|
||||
import GallerySliderSetter from './gallery-slider-setter/index';
|
||||
import FilterSetter from './filter-setter';
|
||||
import SortSetter from './sort-setter';
|
||||
|
@@ -0,0 +1,35 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EVENT_BUS_TYPE, MAP_TYPE, STORAGE_MAP_TYPE_KEY } from '../../constants';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import RadioGroup from '../radio-group';
|
||||
|
||||
const MAP_TYPES = [
|
||||
{ value: MAP_TYPE.MAP, label: gettext('Map') },
|
||||
{ value: MAP_TYPE.SATELLITE, label: gettext('Satellite') },
|
||||
];
|
||||
|
||||
const MapTypeSetter = ({ view }) => {
|
||||
const [currentType, setCurrentType] = useState(MAP_TYPE.MAP);
|
||||
|
||||
useEffect(() => {
|
||||
const type = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_TYPE_KEY) || MAP_TYPE.MAP;
|
||||
setCurrentType(type);
|
||||
}, [view?._id]);
|
||||
|
||||
const onChange = useCallback((type) => {
|
||||
if (currentType === type) return;
|
||||
setCurrentType(type);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_MAP_TYPE, type);
|
||||
}, [currentType]);
|
||||
|
||||
return (<RadioGroup options={MAP_TYPES} value={currentType} onChange={onChange} />);
|
||||
};
|
||||
|
||||
MapTypeSetter.propTypes = {
|
||||
view: PropTypes.shape({
|
||||
_id: PropTypes.string
|
||||
})
|
||||
};
|
||||
|
||||
export default MapTypeSetter;
|
@@ -1,57 +0,0 @@
|
||||
.metadata-map-type-setter {
|
||||
width: fit-content;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.metadata-map-type-setter .metadata-map-type-button {
|
||||
width: 66px;
|
||||
height: 28px;
|
||||
color: #212529;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.875rem;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.metadata-map-type-setter .metadata-map-type-button:hover {
|
||||
background-color: #f0f0f0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.metadata-map-type-setter .metadata-map-type-button.active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.metadata-map-type-setter .metadata-map-type-button span {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.metadata-map-type-button:not(:first-child)::before {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 22px;
|
||||
background-color: #e2e2e2;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transition: opacity 0.3s;
|
||||
left: -1px;
|
||||
}
|
||||
|
||||
.metadata-map-type-button:hover::before,
|
||||
.metadata-map-type-button.active::before,
|
||||
.metadata-map-type-button:hover + .metadata-map-type-button::before,
|
||||
.metadata-map-type-button.active + .metadata-map-type-button::before {
|
||||
opacity: 0;
|
||||
}
|
@@ -1,49 +0,0 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { EVENT_BUS_TYPE, MAP_TYPE } from '../../../constants';
|
||||
import { gettext } from '../../../../utils/constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const TYPE_MAP = {
|
||||
[MAP_TYPE.MAP]: gettext('Map'),
|
||||
[MAP_TYPE.SATELLITE]: gettext('Satellite')
|
||||
};
|
||||
|
||||
const MapTypeSetter = ({ view }) => {
|
||||
const [currentType, setCurrentType] = useState(MAP_TYPE.MAP);
|
||||
|
||||
useEffect(() => {
|
||||
const savedValue = window.sfMetadataContext.localStorage.getItem('map-type', MAP_TYPE.MAP);
|
||||
setCurrentType(savedValue || MAP_TYPE.MAP);
|
||||
}, [view?._id]);
|
||||
|
||||
const handleTypeChange = useCallback((newType) => {
|
||||
setCurrentType(newType);
|
||||
window.sfMetadataContext.localStorage.setItem('map-type', newType);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SWITCH_MAP_TYPE, newType);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="metadata-map-type-setter">
|
||||
{Object.entries(TYPE_MAP).map(([type, label]) => (
|
||||
<button
|
||||
key={type}
|
||||
className={classnames('metadata-map-type-button', { active: currentType === type })}
|
||||
onClick={() => handleTypeChange(type)}
|
||||
>
|
||||
<span>{label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
MapTypeSetter.propTypes = {
|
||||
view: PropTypes.shape({
|
||||
_id: PropTypes.string
|
||||
})
|
||||
};
|
||||
|
||||
export default MapTypeSetter;
|
54
frontend/src/metadata/components/radio-group/index.css
Normal file
54
frontend/src/metadata/components/radio-group/index.css
Normal file
@@ -0,0 +1,54 @@
|
||||
.sf-metadata-radio-group {
|
||||
width: fit-content;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 3px;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
.sf-metadata-radio-group .sf-metadata-radio-group-option {
|
||||
min-width: 66px;
|
||||
width: fit-content;
|
||||
height: 28px;
|
||||
color: #212529;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.875rem;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.sf-metadata-radio-group .sf-metadata-radio-group-option:hover {
|
||||
background-color: #f0f0f0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-metadata-radio-group .sf-metadata-radio-group-option.active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.sf-metadata-radio-group .sf-metadata-radio-group-option:not(:first-child)::before {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 22px;
|
||||
background-color: #e2e2e2;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transition: opacity 0.3s;
|
||||
left: -1px;
|
||||
}
|
||||
|
||||
.sf-metadata-radio-group .sf-metadata-radio-group-option:hover::before,
|
||||
.sf-metadata-radio-group .sf-metadata-radio-group-option.active::before,
|
||||
.sf-metadata-radio-group .sf-metadata-radio-group-option:hover + .sf-metadata-radio-group-option::before,
|
||||
.sf-metadata-radio-group .sf-metadata-radio-group-option.active + .sf-metadata-radio-group-option::before {
|
||||
opacity: 0;
|
||||
}
|
||||
|
46
frontend/src/metadata/components/radio-group/index.js
Normal file
46
frontend/src/metadata/components/radio-group/index.js
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const RadioGroup = ({ value, options, className, onChange: onChangeAPI }) => {
|
||||
const selected = useMemo(() => {
|
||||
const selectedOption = options.find(o => value === o.value) || options[0];
|
||||
return selectedOption.value;
|
||||
}, [value, options]);
|
||||
|
||||
const onChange = useCallback((event) => {
|
||||
const newValue = event.target.dataset.option;
|
||||
if (selected === newValue) return;
|
||||
onChangeAPI(newValue);
|
||||
}, [selected, onChangeAPI]);
|
||||
|
||||
return (
|
||||
<div className={classnames('sf-metadata-radio-group', className)}>
|
||||
{options.map(option => {
|
||||
const { value, label } = option;
|
||||
return (
|
||||
<div
|
||||
key={value}
|
||||
data-option={value}
|
||||
className={classnames('sf-metadata-radio-group-option', { 'active': value === selected })}
|
||||
onClick={onChange}
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
RadioGroup.propTypes = {
|
||||
value: PropTypes.string,
|
||||
options: PropTypes.array,
|
||||
className: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export default RadioGroup;
|
@@ -17,17 +17,17 @@ const FaceRecognitionViewToolbar = ({ readOnly, isCustomPermission, onToggleDeta
|
||||
setShow(isShow);
|
||||
}, []);
|
||||
|
||||
const setRecognitionView = useCallback(view => {
|
||||
const resetView = useCallback(view => {
|
||||
setView(view);
|
||||
}, []);
|
||||
|
||||
const modifySorts = useCallback((sorts) => {
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.FACE_RECOGNITION_VIEW_CHANGE, { sorts });
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.UPDATE_SERVER_VIEW, { sorts });
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeToggle = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.TOGGLE_VIEW_TOOLBAR, onToggle);
|
||||
const unsubscribeView = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.FACE_RECOGNITION_VIEW, setRecognitionView);
|
||||
const unsubscribeView = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.RESET_VIEW, resetView);
|
||||
return () => {
|
||||
unsubscribeToggle && unsubscribeToggle();
|
||||
unsubscribeView && unsubscribeView();
|
||||
|
@@ -94,8 +94,8 @@ const ViewToolBar = ({ viewId, isCustomPermission, onToggleDetail, onCloseDetail
|
||||
)}
|
||||
{viewType === VIEW_TYPE.FACE_RECOGNITION && (
|
||||
<FaceRecognitionViewToolbar
|
||||
readOnly={readOnly}
|
||||
isCustomPermission={isCustomPermission}
|
||||
view={view}
|
||||
onToggleDetail={onToggleDetail}
|
||||
/>
|
||||
)}
|
||||
@@ -113,8 +113,9 @@ const ViewToolBar = ({ viewId, isCustomPermission, onToggleDetail, onCloseDetail
|
||||
)}
|
||||
{viewType === VIEW_TYPE.MAP && (
|
||||
<MapViewToolBar
|
||||
isCustomPermission={isCustomPermission}
|
||||
readOnly={readOnly}
|
||||
isCustomPermission={isCustomPermission}
|
||||
viewID={view._id}
|
||||
collaborators={collaborators}
|
||||
modifyFilters={modifyFilters}
|
||||
onToggleDetail={onToggleDetail}
|
||||
|
@@ -7,6 +7,7 @@ import { gettext } from '../../../../utils/constants';
|
||||
const MapViewToolBar = ({
|
||||
isCustomPermission,
|
||||
readOnly,
|
||||
viewID,
|
||||
collaborators,
|
||||
modifyFilters,
|
||||
onToggleDetail,
|
||||
@@ -14,7 +15,7 @@ const MapViewToolBar = ({
|
||||
const [showGalleryToolbar, setShowGalleryToolbar] = useState(false);
|
||||
const [view, setView] = useState({});
|
||||
|
||||
const viewType = useMemo(() => view.type, [view]);
|
||||
const viewType = useMemo(() => VIEW_TYPE.MAP, []);
|
||||
const viewColumns = useMemo(() => {
|
||||
if (!view) return [];
|
||||
return view.columns;
|
||||
@@ -29,69 +30,73 @@ const MapViewToolBar = ({
|
||||
}, []);
|
||||
|
||||
const modifySorts = useCallback((sorts) => {
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MAP_GALLERY_VIEW_CHANGE, { sorts });
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.UPDATE_SERVER_VIEW, { sorts });
|
||||
}, []);
|
||||
|
||||
const setMapView = useCallback(view => setView(view), []);
|
||||
const resetView = useCallback(view => {
|
||||
setView(view);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeToggleViewToolbarMode = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.TOGGLE_MAP_VIEW_TOOLBAR, onToggle);
|
||||
const unsubscribeView = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MAP_VIEW, setMapView);
|
||||
const unsubscribeToggle = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.TOGGLE_VIEW_TOOLBAR, onToggle);
|
||||
const unsubscribeView = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.RESET_VIEW, resetView);
|
||||
return () => {
|
||||
unsubscribeToggleViewToolbarMode();
|
||||
unsubscribeView();
|
||||
unsubscribeToggle && unsubscribeToggle();
|
||||
unsubscribeView && unsubscribeView();
|
||||
};
|
||||
}, [setMapView, onToggle]);
|
||||
|
||||
useEffect(() => {
|
||||
setView(window.sfMetadataStore.data.view);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{showGalleryToolbar ? (
|
||||
useEffect(() => {
|
||||
setShowGalleryToolbar(false);
|
||||
}, [viewID]);
|
||||
|
||||
if (showGalleryToolbar) {
|
||||
return (
|
||||
<>
|
||||
<div className="sf-metadata-tool-left-operations">
|
||||
<>
|
||||
<GalleryGroupBySetter view={{ _id: view._id }} />
|
||||
<GallerySliderSetter view={{ _id: view._id }} />
|
||||
<SortSetter
|
||||
isNeedSubmit={true}
|
||||
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-sort"
|
||||
target="sf-metadata-sort-popover"
|
||||
readOnly={readOnly}
|
||||
sorts={view.sorts}
|
||||
type={VIEW_TYPE.MAP}
|
||||
columns={viewColumns}
|
||||
modifySorts={modifySorts}
|
||||
/>
|
||||
</>
|
||||
<GalleryGroupBySetter view={{ _id: viewID }} />
|
||||
<GallerySliderSetter view={{ _id: viewID }} />
|
||||
<SortSetter
|
||||
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-sort"
|
||||
target="sf-metadata-sort-popover"
|
||||
readOnly={readOnly}
|
||||
sorts={view.sorts}
|
||||
type={VIEW_TYPE.MAP}
|
||||
columns={viewColumns}
|
||||
modifySorts={modifySorts}
|
||||
/>
|
||||
{!isCustomPermission && (
|
||||
<div className="cur-view-path-btn ml-2" onClick={onToggleDetail}>
|
||||
<span className="sf3-font sf3-font-info" aria-label={gettext('Properties')} title={gettext('Properties')}></span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) :
|
||||
<>
|
||||
<div className="sf-metadata-tool-left-operations">
|
||||
<MapTypeSetter view={view} />
|
||||
<FilterSetter
|
||||
isNeedSubmit={true}
|
||||
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-filter"
|
||||
filtersClassName="sf-metadata-filters"
|
||||
target="sf-metadata-filter-popover"
|
||||
readOnly={readOnly}
|
||||
filterConjunction={view.filter_conjunction}
|
||||
basicFilters={view.basic_filters}
|
||||
filters={view.filters}
|
||||
columns={filterColumns}
|
||||
modifyFilters={modifyFilters}
|
||||
collaborators={collaborators}
|
||||
viewType={viewType}
|
||||
/>
|
||||
</div>
|
||||
<div className="sf-metadata-tool-right-operations"></div>
|
||||
</>}
|
||||
<div className="sf-metadata-tool-right-operations"></div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="sf-metadata-tool-left-operations">
|
||||
<MapTypeSetter view={view} />
|
||||
<FilterSetter
|
||||
isNeedSubmit={true}
|
||||
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-filter"
|
||||
filtersClassName="sf-metadata-filters"
|
||||
target="sf-metadata-filter-popover"
|
||||
readOnly={readOnly}
|
||||
filterConjunction={view.filter_conjunction}
|
||||
basicFilters={view.basic_filters}
|
||||
filters={view.filters}
|
||||
columns={filterColumns}
|
||||
modifyFilters={modifyFilters}
|
||||
collaborators={collaborators}
|
||||
viewType={viewType}
|
||||
/>
|
||||
</div>
|
||||
<div className="sf-metadata-tool-right-operations"></div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -60,22 +60,21 @@ export const EVENT_BUS_TYPE = {
|
||||
SAVED: 'saved',
|
||||
ERROR: 'error',
|
||||
|
||||
// view
|
||||
TOGGLE_VIEW_TOOLBAR: 'toggle_view_toolbar',
|
||||
RESET_VIEW: 'reset_view',
|
||||
UPDATE_SERVER_VIEW: 'update_server_view',
|
||||
|
||||
// gallery
|
||||
MODIFY_GALLERY_ZOOM_GEAR: 'modify_gallery_zoom_gear',
|
||||
SWITCH_GALLERY_GROUP_BY: 'switch_gallery_group_by',
|
||||
|
||||
// face recognition
|
||||
TOGGLE_VIEW_TOOLBAR: 'toggle_view_toolbar',
|
||||
FACE_RECOGNITION_VIEW: 'face_recognition_view',
|
||||
FACE_RECOGNITION_VIEW_CHANGE: 'face_recognition_view_change',
|
||||
|
||||
// kanban
|
||||
TOGGLE_KANBAN_SETTINGS: 'toggle_kanban_settings',
|
||||
OPEN_KANBAN_SETTINGS: 'open_kanban_settings',
|
||||
CLOSE_KANBAN_SETTINGS: 'close_kanban_settings',
|
||||
|
||||
// map
|
||||
SWITCH_MAP_TYPE: 'switch_map_type',
|
||||
TOGGLE_MAP_VIEW_TOOLBAR: 'toggle_map_view_toolbar',
|
||||
MODIFY_MAP_TYPE: 'modify_map_type',
|
||||
MAP_VIEW: 'map_view',
|
||||
};
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import CellType from './column/type';
|
||||
import { EVENT_BUS_TYPE } from './event-bus-type';
|
||||
import TRANSFER_TYPES from './TransferTypes';
|
||||
import * as metadataZIndexes from './z-index';
|
||||
@@ -13,147 +12,8 @@ export * from './sort';
|
||||
export * from './error';
|
||||
export * from './view';
|
||||
|
||||
export const CELL_NAVIGATION_MODE = {
|
||||
NONE: 'none',
|
||||
CHANGE_ROW: 'changeRow',
|
||||
LOOP_OVER_ROW: 'loopOverRow',
|
||||
};
|
||||
|
||||
export const SEQUENCE_COLUMN_WIDTH = 80;
|
||||
|
||||
export const ROW_HEIGHT = 32;
|
||||
|
||||
export const GRID_HEADER_DEFAULT_HEIGHT = 32;
|
||||
|
||||
export const GRID_HEADER_DOUBLE_HEIGHT = 56;
|
||||
|
||||
export const GROUP_VIEW_OFFSET = 16;
|
||||
|
||||
export const GROUP_HEADER_HEIGHT = 48;
|
||||
|
||||
export const TABLE_LEFT_MARGIN = 10;
|
||||
|
||||
export const TABLE_BORDER_WIDTH = 1;
|
||||
|
||||
export const UNABLE_TO_CALCULATE = '--';
|
||||
|
||||
export const FROZEN_COLUMN_SHADOW = '2px 0 5px -2px hsla(0,0%,53.3%,.3)';
|
||||
|
||||
export const TABLE_NOT_SUPPORT_EDIT_TYPE_MAP = {
|
||||
[CellType.CREATOR]: true,
|
||||
[CellType.LAST_MODIFIER]: true,
|
||||
[CellType.CTIME]: true,
|
||||
[CellType.MTIME]: true,
|
||||
[CellType.FILE_NAME]: true,
|
||||
};
|
||||
|
||||
export const TABLE_SUPPORT_EDIT_TYPE_MAP = {
|
||||
[CellType.TEXT]: true,
|
||||
[CellType.DATE]: true,
|
||||
[CellType.NUMBER]: true,
|
||||
[CellType.SINGLE_SELECT]: true,
|
||||
[CellType.MULTIPLE_SELECT]: true,
|
||||
[CellType.COLLABORATOR]: true,
|
||||
[CellType.CHECKBOX]: true,
|
||||
[CellType.LONG_TEXT]: true,
|
||||
[CellType.LINK]: true,
|
||||
[CellType.TAGS]: true,
|
||||
};
|
||||
|
||||
export const TABLE_MOBILE_SUPPORT_EDIT_CELL_TYPE_MAP = {
|
||||
[CellType.TEXT]: true,
|
||||
};
|
||||
|
||||
export const CANVAS_RIGHT_INTERVAL = 44;
|
||||
|
||||
export const LEFT_NAV = 280;
|
||||
export const ROW_DETAIL_PADDING = 40 * 2;
|
||||
export const ROW_DETAIL_MARGIN = 20 * 2;
|
||||
export const EDITOR_PADDING = 1.5 * 16; // 1.5: 0.75 * 2
|
||||
|
||||
export const COLUMN_RATE_MAX_NUMBER = [
|
||||
{ name: 1 },
|
||||
{ name: 2 },
|
||||
{ name: 3 },
|
||||
{ name: 4 },
|
||||
{ name: 5 },
|
||||
{ name: 6 },
|
||||
{ name: 7 },
|
||||
{ name: 8 },
|
||||
{ name: 9 },
|
||||
{ name: 10 },
|
||||
];
|
||||
|
||||
export const GROUP_ROW_TYPE = {
|
||||
GROUP_CONTAINER: 'group_container',
|
||||
ROW: 'row',
|
||||
BTN_INSERT_ROW: 'btn_insert_row',
|
||||
};
|
||||
|
||||
export const INSERT_ROW_HEIGHT = 32;
|
||||
|
||||
export const CHANGE_HEADER_WIDTH = 'CHANGE_HEADER_WIDTH';
|
||||
|
||||
export const NOT_SUPPORT_DRAG_COPY_COLUMN_TYPES = [
|
||||
];
|
||||
|
||||
export const SUPPORT_PREVIEW_COLUMN_TYPES = [];
|
||||
|
||||
export const OVER_SCAN_COLUMNS = 10;
|
||||
|
||||
export const DELETED_OPTION_BACKGROUND_COLOR = '#eaeaea';
|
||||
|
||||
export const DELETED_OPTION_TIPS = 'deleted_option';
|
||||
|
||||
export const SUPPORT_BATCH_DOWNLOAD_TYPES = [];
|
||||
|
||||
export const PER_LOAD_NUMBER = 1000;
|
||||
|
||||
export const DEFAULT_RETRY_TIMES = 4;
|
||||
|
||||
export const DEFAULT_RETRY_INTERVAL = 1000;
|
||||
|
||||
export const MAX_LOAD_NUMBER = 10000;
|
||||
|
||||
export const EDITOR_TYPE = {
|
||||
PREVIEWER: 'previewer',
|
||||
ADDITION: 'addition',
|
||||
};
|
||||
|
||||
export {
|
||||
EVENT_BUS_TYPE,
|
||||
TRANSFER_TYPES,
|
||||
metadataZIndexes,
|
||||
};
|
||||
|
||||
export const DATE_TAG_HEIGHT = 44;
|
||||
|
||||
export const GALLERY_ZOOM_GEAR_MIN = -2;
|
||||
|
||||
export const GALLERY_ZOOM_GEAR_MAX = 2;
|
||||
|
||||
export const GALLERY_IMAGE_GAP = 2;
|
||||
|
||||
export const GALLERY_DATE_MODE = {
|
||||
YEAR: 'year',
|
||||
MONTH: 'month',
|
||||
DAY: 'day',
|
||||
ALL: 'all',
|
||||
};
|
||||
|
||||
export const MAP_TYPE = {
|
||||
NORMAL_MAP: 'normal_map',
|
||||
SATELLITE: 'satellite',
|
||||
};
|
||||
|
||||
export const MAP_VIEW_TOOLBAR_MODE = {
|
||||
MAP: 'map',
|
||||
GALLERY: 'gallery',
|
||||
};
|
||||
|
||||
export const UNCATEGORIZED = '_uncategorized';
|
||||
|
||||
export const PASTE_SOURCE = {
|
||||
COPY: 'copy',
|
||||
CUT: 'cut',
|
||||
};
|
||||
|
16
frontend/src/metadata/constants/view/gallery.js
Normal file
16
frontend/src/metadata/constants/view/gallery.js
Normal file
@@ -0,0 +1,16 @@
|
||||
export const DATE_TAG_HEIGHT = 44;
|
||||
|
||||
export const GALLERY_ZOOM_GEAR_MIN = -2;
|
||||
|
||||
export const GALLERY_ZOOM_GEAR_MAX = 2;
|
||||
|
||||
export const GALLERY_IMAGE_GAP = 2;
|
||||
|
||||
export const GALLERY_DATE_MODE = {
|
||||
YEAR: 'year',
|
||||
MONTH: 'month',
|
||||
DAY: 'day',
|
||||
ALL: 'all',
|
||||
};
|
||||
|
||||
export const STORAGE_GALLERY_DATE_MODE_KEY = 'gallery_date_mode';
|
@@ -1,8 +1,13 @@
|
||||
import { PRIVATE_COLUMN_KEY } from './column';
|
||||
import { FILTER_PREDICATE_TYPE } from './filter';
|
||||
import { PRIVATE_COLUMN_KEY } from '../column';
|
||||
import { FILTER_PREDICATE_TYPE } from '../filter';
|
||||
import { SORT_COLUMN_OPTIONS, GALLERY_SORT_COLUMN_OPTIONS, GALLERY_FIRST_SORT_COLUMN_OPTIONS, SORT_TYPE,
|
||||
GALLERY_SORT_PRIVATE_COLUMN_KEYS, GALLERY_FIRST_SORT_PRIVATE_COLUMN_KEYS,
|
||||
} from './sort';
|
||||
} from '../sort';
|
||||
|
||||
export * from './gallery';
|
||||
export * from './kanban';
|
||||
export * from './map';
|
||||
export * from './table';
|
||||
|
||||
export const METADATA_VIEWS_KEY = 'sf-metadata-views';
|
||||
|
1
frontend/src/metadata/constants/view/kanban.js
Normal file
1
frontend/src/metadata/constants/view/kanban.js
Normal file
@@ -0,0 +1 @@
|
||||
export const UNCATEGORIZED = '_uncategorized';
|
15
frontend/src/metadata/constants/view/map.js
Normal file
15
frontend/src/metadata/constants/view/map.js
Normal file
@@ -0,0 +1,15 @@
|
||||
export const MAP_TYPE = {
|
||||
MAP: 'map',
|
||||
SATELLITE: 'satellite',
|
||||
};
|
||||
|
||||
export const STORAGE_MAP_TYPE_KEY = 'map_type';
|
||||
|
||||
export const STORAGE_MAP_CENTER_KEY = 'map_center';
|
||||
|
||||
export const STORAGE_MAP_ZOOM_KEY = 'map_zoom';
|
||||
|
||||
export const MAP_VIEW_TOOLBAR_MODE = {
|
||||
MAP: 'map',
|
||||
GALLERY: 'gallery',
|
||||
};
|
101
frontend/src/metadata/constants/view/table.js
Normal file
101
frontend/src/metadata/constants/view/table.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import { CellType } from '../column';
|
||||
|
||||
export const CELL_NAVIGATION_MODE = {
|
||||
NONE: 'none',
|
||||
CHANGE_ROW: 'changeRow',
|
||||
LOOP_OVER_ROW: 'loopOverRow',
|
||||
};
|
||||
|
||||
export const SEQUENCE_COLUMN_WIDTH = 80;
|
||||
|
||||
export const ROW_HEIGHT = 32;
|
||||
|
||||
export const GRID_HEADER_DEFAULT_HEIGHT = 32;
|
||||
|
||||
export const GRID_HEADER_DOUBLE_HEIGHT = 56;
|
||||
|
||||
export const GROUP_VIEW_OFFSET = 16;
|
||||
|
||||
export const GROUP_HEADER_HEIGHT = 48;
|
||||
|
||||
export const TABLE_LEFT_MARGIN = 10;
|
||||
|
||||
export const TABLE_BORDER_WIDTH = 1;
|
||||
|
||||
export const UNABLE_TO_CALCULATE = '--';
|
||||
|
||||
export const FROZEN_COLUMN_SHADOW = '2px 0 5px -2px hsla(0,0%,53.3%,.3)';
|
||||
|
||||
export const TABLE_NOT_SUPPORT_EDIT_TYPE_MAP = {
|
||||
[CellType.CREATOR]: true,
|
||||
[CellType.LAST_MODIFIER]: true,
|
||||
[CellType.CTIME]: true,
|
||||
[CellType.MTIME]: true,
|
||||
[CellType.FILE_NAME]: true,
|
||||
};
|
||||
|
||||
export const TABLE_SUPPORT_EDIT_TYPE_MAP = {
|
||||
[CellType.TEXT]: true,
|
||||
[CellType.DATE]: true,
|
||||
[CellType.NUMBER]: true,
|
||||
[CellType.SINGLE_SELECT]: true,
|
||||
[CellType.MULTIPLE_SELECT]: true,
|
||||
[CellType.COLLABORATOR]: true,
|
||||
[CellType.CHECKBOX]: true,
|
||||
[CellType.LONG_TEXT]: true,
|
||||
[CellType.LINK]: true,
|
||||
[CellType.TAGS]: true,
|
||||
};
|
||||
|
||||
export const TABLE_MOBILE_SUPPORT_EDIT_CELL_TYPE_MAP = {
|
||||
[CellType.TEXT]: true,
|
||||
};
|
||||
|
||||
export const CANVAS_RIGHT_INTERVAL = 44;
|
||||
|
||||
export const LEFT_NAV = 280;
|
||||
export const ROW_DETAIL_PADDING = 40 * 2;
|
||||
export const ROW_DETAIL_MARGIN = 20 * 2;
|
||||
export const EDITOR_PADDING = 1.5 * 16; // 1.5: 0.75 * 2
|
||||
|
||||
export const GROUP_ROW_TYPE = {
|
||||
GROUP_CONTAINER: 'group_container',
|
||||
ROW: 'row',
|
||||
BTN_INSERT_ROW: 'btn_insert_row',
|
||||
};
|
||||
|
||||
export const INSERT_ROW_HEIGHT = 32;
|
||||
|
||||
export const CHANGE_HEADER_WIDTH = 'CHANGE_HEADER_WIDTH';
|
||||
|
||||
export const NOT_SUPPORT_DRAG_COPY_COLUMN_TYPES = [
|
||||
];
|
||||
|
||||
export const SUPPORT_PREVIEW_COLUMN_TYPES = [];
|
||||
|
||||
export const OVER_SCAN_COLUMNS = 10;
|
||||
|
||||
export const DELETED_OPTION_BACKGROUND_COLOR = '#eaeaea';
|
||||
|
||||
export const DELETED_OPTION_TIPS = 'deleted_option';
|
||||
|
||||
export const SUPPORT_BATCH_DOWNLOAD_TYPES = [];
|
||||
|
||||
export const PER_LOAD_NUMBER = 1000;
|
||||
|
||||
export const DEFAULT_RETRY_TIMES = 4;
|
||||
|
||||
export const DEFAULT_RETRY_INTERVAL = 1000;
|
||||
|
||||
export const MAX_LOAD_NUMBER = 10000;
|
||||
|
||||
export const EDITOR_TYPE = {
|
||||
PREVIEWER: 'previewer',
|
||||
ADDITION: 'addition',
|
||||
};
|
||||
|
||||
export const PASTE_SOURCE = {
|
||||
COPY: 'copy',
|
||||
CUT: 'cut',
|
||||
};
|
||||
|
@@ -340,6 +340,8 @@ export const MetadataViewProvider = ({
|
||||
<MetadataViewContext.Provider
|
||||
value={{
|
||||
isLoading,
|
||||
repoID,
|
||||
viewID,
|
||||
isBeingBuilt,
|
||||
errorMessage,
|
||||
metadata,
|
||||
|
@@ -130,7 +130,7 @@ const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onRemovePeo
|
||||
metadata.hasMore = false;
|
||||
}
|
||||
setMetadata(metadata);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.FACE_RECOGNITION_VIEW, metadata.view);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.RESET_VIEW, metadata.view);
|
||||
setLoading(false);
|
||||
}).catch(error => {
|
||||
const errorMessage = Utils.getErrorMsg(error);
|
||||
@@ -164,7 +164,7 @@ const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onRemovePeo
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeViewChange = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.FACE_RECOGNITION_VIEW_CHANGE, onViewChange);
|
||||
const unsubscribeViewChange = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.UPDATE_SERVER_VIEW, onViewChange);
|
||||
return () => {
|
||||
unsubscribeViewChange && unsubscribeViewChange();
|
||||
};
|
||||
|
@@ -30,17 +30,17 @@ const GalleryContextMenu = ({ metadata, selectedImages, onDelete, onDuplicate, a
|
||||
|
||||
const options = useMemo(() => {
|
||||
let validOptions = [{ value: CONTEXT_MENU_KEY.DOWNLOAD, label: gettext('Download') }];
|
||||
if (checkCanDeleteRow) {
|
||||
if (onDelete && checkCanDeleteRow) {
|
||||
validOptions.push({ value: CONTEXT_MENU_KEY.DELETE, label: selectedImages.length > 1 ? gettext('Delete') : gettext('Delete file') });
|
||||
}
|
||||
if (canDuplicateRow && selectedImages.length === 1) {
|
||||
if (onDuplicate && canDuplicateRow && selectedImages.length === 1) {
|
||||
validOptions.push({ value: CONTEXT_MENU_KEY.DUPLICATE, label: gettext('Duplicate') });
|
||||
}
|
||||
if (canRemovePhotoFromPeople) {
|
||||
if (onRemoveImage && canRemovePhotoFromPeople) {
|
||||
validOptions.push({ value: CONTEXT_MENU_KEY.REMOVE, label: gettext('Remove from this group') });
|
||||
}
|
||||
return validOptions;
|
||||
}, [checkCanDeleteRow, canDuplicateRow, canRemovePhotoFromPeople, selectedImages]);
|
||||
}, [checkCanDeleteRow, canDuplicateRow, canRemovePhotoFromPeople, selectedImages, onDuplicate, onDelete, onRemoveImage]);
|
||||
|
||||
const closeZipDialog = () => {
|
||||
setIsZipDialogOpen(false);
|
||||
|
@@ -8,7 +8,7 @@ import { useMetadataView } from '../../hooks/metadata-view';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import { getDateDisplayString, getFileNameFromRecord, getParentDirFromRecord, getRecordIdFromRecord } from '../../utils/cell';
|
||||
import { siteRoot, fileServerRoot, thumbnailSizeForGrid, thumbnailSizeForOriginal } from '../../../utils/constants';
|
||||
import { EVENT_BUS_TYPE, GALLERY_DATE_MODE, DATE_TAG_HEIGHT, GALLERY_IMAGE_GAP } from '../../constants';
|
||||
import { EVENT_BUS_TYPE, GALLERY_DATE_MODE, DATE_TAG_HEIGHT, GALLERY_IMAGE_GAP, STORAGE_GALLERY_DATE_MODE_KEY } from '../../constants';
|
||||
import { getRowById } from '../../utils/table';
|
||||
import { getEventClassName } from '../../utils/common';
|
||||
import GalleryContextmenu from './context-menu';
|
||||
@@ -129,14 +129,14 @@ const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord,
|
||||
const gear = window.sfMetadataContext.localStorage.getItem('zoom-gear', 0) || 0;
|
||||
setZoomGear(gear);
|
||||
|
||||
const mode = window.sfMetadataContext.localStorage.getItem('gallery-group-by', GALLERY_DATE_MODE.DAY) || GALLERY_DATE_MODE.DAY;
|
||||
const mode = window.sfMetadataContext.localStorage.getItem(STORAGE_GALLERY_DATE_MODE_KEY, GALLERY_DATE_MODE.DAY) || GALLERY_DATE_MODE.DAY;
|
||||
setMode(mode);
|
||||
|
||||
const switchGalleryModeSubscribe = window.sfMetadataContext.eventBus.subscribe(
|
||||
EVENT_BUS_TYPE.SWITCH_GALLERY_GROUP_BY,
|
||||
(mode) => {
|
||||
setMode(mode);
|
||||
window.sfMetadataContext.localStorage.setItem('gallery-group-by', mode);
|
||||
window.sfMetadataContext.localStorage.setItem(STORAGE_GALLERY_DATE_MODE_KEY, mode);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -193,7 +193,7 @@ const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord,
|
||||
if (!containerRef.current) return;
|
||||
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
|
||||
if (scrollTop + clientHeight >= scrollHeight - 10) {
|
||||
onLoadMore();
|
||||
onLoadMore && onLoadMore();
|
||||
} else {
|
||||
const { scrollTop, clientHeight } = containerRef.current;
|
||||
const overScanTop = Math.max(0, scrollTop - (imageSize + GALLERY_IMAGE_GAP) * OVER_SCAN_ROWS);
|
||||
|
@@ -1,54 +1,4 @@
|
||||
.sf-metadata-map-photos-container {
|
||||
padding: 0 !important;
|
||||
overflow-y: hidden !important;
|
||||
}
|
||||
|
||||
.sf-metadata-map-photos-container .sf-metadata-map-photos-header {
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.sf-metadata-map-photos-container .sf-metadata-icon-btn {
|
||||
margin-left: -4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sf-metadata-map-photos-container .sf-metadata-icon-btn:hover {
|
||||
background-color: #EFEFEF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-metadata-map-photos-container .sf-metadata-map-photos-header .sf-metadata-map-photos-header-back {
|
||||
font-size: 14px;
|
||||
height: 24px;
|
||||
min-width: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: -5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sf-metadata-map-photos-container .sf-metadata-map-photos-header .sf-metadata-map-photos-header-back:hover {
|
||||
background-color: #EFEFEF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-metadata-map-photos-container .sf-metadata-map-photos-header-back .sf3-font-arrow {
|
||||
color: #666;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.sf-metadata-map-photos-container .sf-metadata-map-photos-header .sf-metadata-map-location {
|
||||
margin-left: 4px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sf-metadata-map-photos-container .sf-metadata-gallery-container {
|
||||
height: calc(100% - 48px);
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
@@ -1,67 +1,113 @@
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import deepCopy from 'deep-copy';
|
||||
import { CenteredLoading } from '@seafile/sf-metadata-ui-component';
|
||||
import Gallery from '../../gallery/main';
|
||||
import { gettext } from '../../../../utils/constants';
|
||||
import { EVENT_BUS_TYPE } from '../../../constants';
|
||||
import metadataAPI from '../../../api';
|
||||
import { Utils } from '../../../../utils/utils';
|
||||
import toaster from '../../../../components/toast';
|
||||
import { useMetadataView } from '../../../hooks/metadata-view';
|
||||
import { getRowsByIds } from '../../../utils/table';
|
||||
import Metadata from '../../../model/metadata';
|
||||
import { sortTableRows } from '../../../utils/sort';
|
||||
import { useCollaborators } from '../../../hooks/collaborators';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const ClusterPhotos = ({ metadata, markerIds, onClose }) => {
|
||||
const { store, duplicateRecord, addFolder } = useMetadataView();
|
||||
const ClusterPhotos = ({ markerIds, onClose }) => {
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
const [metadata, setMetadata] = useState({ rows: [] });
|
||||
|
||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||
const { repoID, viewID, metadata: allMetadata, store, addFolder, deleteRecords } = useMetadataView();
|
||||
const { collaborators } = useCollaborators();
|
||||
|
||||
const clusterMetadata = useMemo(() => {
|
||||
const filteredRows = metadata.rows.filter(row => markerIds.includes(row._id));
|
||||
const rows = useMemo(() => getRowsByIds(allMetadata, markerIds), [allMetadata, markerIds]);
|
||||
const columns = useMemo(() => allMetadata?.columns || [], [allMetadata]);
|
||||
|
||||
const loadData = useCallback((view) => {
|
||||
setLoading(true);
|
||||
const orderRows = sortTableRows({ columns }, rows, view?.sorts || [], { collaborators });
|
||||
let metadata = new Metadata({ rows, columns, view });
|
||||
metadata.hasMore = false;
|
||||
metadata.row_ids = orderRows;
|
||||
metadata.view.rows = orderRows;
|
||||
setMetadata(metadata);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.RESET_VIEW, metadata.view);
|
||||
setLoading(false);
|
||||
}, [rows, columns, collaborators]);
|
||||
|
||||
const deletedByIds = useCallback((ids) => {
|
||||
if (!Array.isArray(ids) || ids.length === 0) return;
|
||||
const newMetadata = deepCopy(metadata);
|
||||
newMetadata.rows = filteredRows;
|
||||
return newMetadata;
|
||||
}, [metadata, markerIds]);
|
||||
const idNeedDeletedMap = ids.reduce((currIdNeedDeletedMap, rowId) => ({ ...currIdNeedDeletedMap, [rowId]: true }), {});
|
||||
newMetadata.rows = newMetadata.rows.filter((row) => !idNeedDeletedMap[row._id]);
|
||||
newMetadata.row_ids = newMetadata.row_ids.filter((id) => !idNeedDeletedMap[id]);
|
||||
|
||||
// delete rows in id_row_map
|
||||
ids.forEach(rowId => {
|
||||
delete newMetadata.id_row_map[rowId];
|
||||
});
|
||||
newMetadata.recordsCount = newMetadata.row_ids.length;
|
||||
setMetadata(newMetadata);
|
||||
|
||||
if (newMetadata.rows.length === 0) {
|
||||
onClose && onClose();
|
||||
}
|
||||
}, [metadata, onClose]);
|
||||
|
||||
const handelDelete = useCallback((deletedImages, { success_callback } = {}) => {
|
||||
if (!deletedImages.length) return;
|
||||
let recordIds = [];
|
||||
deletedImages.forEach((record) => recordIds.push(record.id));
|
||||
store.deleteRecords(recordIds, { success_callback });
|
||||
}, [store]);
|
||||
|
||||
const handleViewChange = useCallback((update) => {
|
||||
metadataAPI.modifyView(repoID, metadata.view._id, update).then(res => {
|
||||
deletedImages.forEach((record) => {
|
||||
const { id, parentDir, name } = record || {};
|
||||
if (parentDir && name) {
|
||||
recordIds.push(id);
|
||||
}
|
||||
});
|
||||
deleteRecords(recordIds, {
|
||||
success_callback: () => {
|
||||
success_callback();
|
||||
deletedByIds(recordIds);
|
||||
}
|
||||
});
|
||||
}, [deleteRecords, deletedByIds]);
|
||||
|
||||
const onViewChange = useCallback((update) => {
|
||||
metadataAPI.modifyView(repoID, viewID, update).then(res => {
|
||||
store.modifyLocalView(update);
|
||||
const newView = { ...metadata.view, ...update };
|
||||
loadData(newView);
|
||||
}).catch(error => {
|
||||
const errorMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errorMessage);
|
||||
});
|
||||
}, [metadata, repoID,]);
|
||||
}, [metadata, repoID, viewID, store, loadData]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeViewChange = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MAP_GALLERY_VIEW_CHANGE, handleViewChange);
|
||||
const unsubscribeViewChange = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.UPDATE_SERVER_VIEW, onViewChange);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_VIEW_TOOLBAR, true);
|
||||
return () => {
|
||||
unsubscribeViewChange();
|
||||
window?.sfMetadataContext?.eventBus?.dispatch(EVENT_BUS_TYPE.TOGGLE_VIEW_TOOLBAR, false);
|
||||
};
|
||||
}, [onViewChange]);
|
||||
|
||||
useEffect(() => {
|
||||
loadData({ sorts: allMetadata.view.sorts });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (isLoading) return (<CenteredLoading />);
|
||||
|
||||
return (
|
||||
<div className="sf-metadata-view-map sf-metadata-map-photos-container">
|
||||
<div className="sf-metadata-map-photos-header">
|
||||
<div className="sf-metadata-map-photos-header-back" onClick={onClose}>
|
||||
<i className="sf3-font sf3-font-arrow rotate-180"></i>
|
||||
</div>
|
||||
<div className="sf-metadata-map-location">{gettext('Location')}</div>
|
||||
</div>
|
||||
<Gallery metadata={clusterMetadata} onDelete={handelDelete} duplicateRecord={duplicateRecord} onAddFolder={addFolder} />
|
||||
<Gallery metadata={metadata} onDelete={handelDelete} onAddFolder={addFolder} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ClusterPhotos.propTypes = {
|
||||
metadata: PropTypes.object,
|
||||
markerIds: PropTypes.array,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
|
@@ -1,24 +1,26 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { getFileNameFromRecord, getFileTypeFromRecord, getImageLocationFromRecord, getParentDirFromRecord, getRecordIdFromRecord } from '../../utils/cell';
|
||||
import ClusterPhotos from './cluster-photos';
|
||||
import Main from './main';
|
||||
import { EVENT_BUS_TYPE, PREDEFINED_FILE_TYPE_OPTION_KEY } from '../../constants';
|
||||
import MapView from './map';
|
||||
import { PREDEFINED_FILE_TYPE_OPTION_KEY } from '../../constants';
|
||||
import { useMetadataView } from '../../hooks/metadata-view';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import { siteRoot, thumbnailSizeForGrid } from '../../../utils/constants';
|
||||
import { gettext, siteRoot, thumbnailSizeForGrid } from '../../../utils/constants';
|
||||
import { isValidPosition } from '../../utils/validate';
|
||||
import { gcj02_to_bd09, wgs84_to_gcj02 } from '../../../utils/coord-transform';
|
||||
import { PRIVATE_FILE_TYPE } from '../../../constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const Map = () => {
|
||||
const [showGallery, setShowGallery] = useState(false);
|
||||
const [markerIds, setMarkerIds] = useState([]);
|
||||
const { metadata } = useMetadataView();
|
||||
const [showCluster, setShowCluster] = useState(false);
|
||||
const { metadata, viewID, updateCurrentPath } = useMetadataView();
|
||||
|
||||
const clusterRef = useRef([]);
|
||||
|
||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||
|
||||
const validImages = useMemo(() => {
|
||||
const images = useMemo(() => {
|
||||
return metadata.rows
|
||||
.map(record => {
|
||||
const recordType = getFileTypeFromRecord(record);
|
||||
@@ -39,31 +41,28 @@ const Map = () => {
|
||||
.filter(Boolean);
|
||||
}, [repoID, metadata.rows]);
|
||||
|
||||
const openGallery = useCallback((cluster_marker_ids) => {
|
||||
setMarkerIds(cluster_marker_ids);
|
||||
setShowGallery(true);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_MAP_VIEW_TOOLBAR, true);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MAP_VIEW, metadata.view);
|
||||
}, [metadata.view]);
|
||||
const openCluster = useCallback((clusterIds) => {
|
||||
clusterRef.current = clusterIds;
|
||||
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}/${gettext('Location')}`);
|
||||
setShowCluster(true);
|
||||
}, [viewID, updateCurrentPath]);
|
||||
|
||||
const closeGallery = useCallback(() => {
|
||||
setShowGallery(false);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_MAP_VIEW_TOOLBAR, false);
|
||||
}, []);
|
||||
const closeCluster = useCallback(() => {
|
||||
clusterRef.current = [];
|
||||
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}`);
|
||||
setShowCluster(false);
|
||||
}, [viewID, updateCurrentPath]);
|
||||
|
||||
useEffect(() => {
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MAP_VIEW, metadata.view);
|
||||
}, [metadata.view]);
|
||||
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}`);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [viewID]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{showGallery ? (
|
||||
<ClusterPhotos metadata={metadata} markerIds={markerIds} onClose={closeGallery} />
|
||||
) : (
|
||||
<Main validImages={validImages} onOpen={openGallery} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
if (showCluster) {
|
||||
return (<ClusterPhotos markerIds={clusterRef.current} onClose={closeCluster} />);
|
||||
}
|
||||
|
||||
return (<MapView images={images} onOpenCluster={openCluster} />);
|
||||
};
|
||||
|
||||
export default Map;
|
||||
|
@@ -5,7 +5,7 @@ import { appAvatarURL, baiduMapKey, gettext, googleMapKey, mediaUrl } from '../.
|
||||
import { isValidPosition } from '../../utils/validate';
|
||||
import { wgs84_to_gcj02, gcj02_to_bd09 } from '../../../utils/coord-transform';
|
||||
import { MAP_TYPE as MAP_PROVIDER } from '../../../constants';
|
||||
import { EVENT_BUS_TYPE, MAP_TYPE } from '../../constants';
|
||||
import { EVENT_BUS_TYPE, MAP_TYPE, STORAGE_MAP_CENTER_KEY, STORAGE_MAP_TYPE_KEY, STORAGE_MAP_ZOOM_KEY } from '../../constants';
|
||||
import { createBMapGeolocationControl, createBMapZoomControl } from './control';
|
||||
import { customAvatarOverlay, customImageOverlay } from './overlay';
|
||||
import toaster from '../../../components/toast';
|
||||
@@ -14,7 +14,7 @@ const DEFAULT_POSITION = { lng: 104.195, lat: 35.861 };
|
||||
const DEFAULT_ZOOM = 4;
|
||||
const BATCH_SIZE = 500;
|
||||
|
||||
const Main = ({ validImages, onOpen }) => {
|
||||
const Main = ({ images, onOpenCluster }) => {
|
||||
const mapInfo = useMemo(() => initMapInfo({ baiduMapKey, googleMapKey }), []);
|
||||
|
||||
const mapRef = useRef(null);
|
||||
@@ -61,13 +61,14 @@ const Main = ({ validImages, onOpen }) => {
|
||||
);
|
||||
}, []);
|
||||
|
||||
const getMapType = useCallback((type) => {
|
||||
if (!mapRef.current) return;
|
||||
const getBMapType = useCallback((type) => {
|
||||
switch (type) {
|
||||
case MAP_TYPE.SATELLITE:
|
||||
case MAP_TYPE.SATELLITE: {
|
||||
return window.BMAP_SATELLITE_MAP;
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
return window.BMAP_NORMAL_MAP;
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -75,13 +76,13 @@ const Main = ({ validImages, onOpen }) => {
|
||||
if (!mapRef.current) return;
|
||||
const point = mapRef.current.getCenter && mapRef.current.getCenter();
|
||||
const zoom = mapRef.current.getZoom && mapRef.current.getZoom();
|
||||
window.sfMetadataContext.localStorage.setItem('map-center', point);
|
||||
window.sfMetadataContext.localStorage.setItem('map-zoom', zoom);
|
||||
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_CENTER_KEY, point);
|
||||
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_ZOOM_KEY, zoom);
|
||||
}, []);
|
||||
|
||||
const loadMapState = useCallback(() => {
|
||||
const savedCenter = window.sfMetadataContext.localStorage.getItem('map-center') || DEFAULT_POSITION;
|
||||
const savedZoom = window.sfMetadataContext.localStorage.getItem('map-zoom') || DEFAULT_ZOOM;
|
||||
const savedCenter = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_CENTER_KEY) || DEFAULT_POSITION;
|
||||
const savedZoom = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_ZOOM_KEY) || DEFAULT_ZOOM;
|
||||
return { center: savedCenter, zoom: savedZoom };
|
||||
}, []);
|
||||
|
||||
@@ -89,18 +90,18 @@ const Main = ({ validImages, onOpen }) => {
|
||||
saveMapState();
|
||||
|
||||
const imageIds = markers.map(marker => marker._imageId);
|
||||
onOpen(imageIds);
|
||||
}, [onOpen, saveMapState]);
|
||||
onOpenCluster(imageIds);
|
||||
}, [onOpenCluster, saveMapState]);
|
||||
|
||||
const renderMarkersBatch = useCallback(() => {
|
||||
if (!validImages.length || !clusterRef.current) return;
|
||||
if (!images.length || !clusterRef.current) return;
|
||||
|
||||
const startIndex = batchIndexRef.current * BATCH_SIZE;
|
||||
const endIndex = Math.min(startIndex + BATCH_SIZE, validImages.length);
|
||||
const endIndex = Math.min(startIndex + BATCH_SIZE, images.length);
|
||||
const batchMarkers = [];
|
||||
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
const image = validImages[i];
|
||||
const image = images[i];
|
||||
const { lng, lat } = image;
|
||||
const point = new window.BMap.Point(lng, lat);
|
||||
const marker = customImageOverlay(point, image, {
|
||||
@@ -110,15 +111,15 @@ const Main = ({ validImages, onOpen }) => {
|
||||
}
|
||||
clusterRef.current.addMarkers(batchMarkers);
|
||||
|
||||
if (endIndex < validImages.length) {
|
||||
if (endIndex < images.length) {
|
||||
batchIndexRef.current += 1;
|
||||
setTimeout(renderMarkersBatch, 20); // Schedule the next batch
|
||||
}
|
||||
}, [validImages, onClickMarker]);
|
||||
}, [images, onClickMarker]);
|
||||
|
||||
const initializeClusterer = useCallback(() => {
|
||||
const initializeCluster = useCallback(() => {
|
||||
if (mapRef.current && !clusterRef.current) {
|
||||
clusterRef.current = new window.BMapLib.MarkerClusterer(mapRef.current, {
|
||||
clusterRef.current = new window.BMapLib.MarkerCluster(mapRef.current, {
|
||||
callback: (e, markers) => onClickMarker(e, markers)
|
||||
});
|
||||
}
|
||||
@@ -131,7 +132,7 @@ const Main = ({ validImages, onOpen }) => {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition((userInfo) => {
|
||||
center = { lng: userInfo.coords.longitude, lat: userInfo.coords.latitude };
|
||||
window.sfMetadataContext.localStorage.setItem('map-center', center);
|
||||
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_CENTER_KEY, center);
|
||||
});
|
||||
}
|
||||
if (!isValidPosition(center?.lng, center?.lat)) return;
|
||||
@@ -145,28 +146,30 @@ const Main = ({ validImages, onOpen }) => {
|
||||
mapRef.current.centerAndZoom(point, zoom);
|
||||
mapRef.current.enableScrollWheelZoom(true);
|
||||
|
||||
const savedValue = window.sfMetadataContext.localStorage.getItem('map-type');
|
||||
mapRef.current && mapRef.current.setMapType(getMapType(savedValue));
|
||||
|
||||
const savedValue = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_TYPE_KEY);
|
||||
mapRef.current && mapRef.current.setMapType(getBMapType(savedValue));
|
||||
|
||||
addMapController();
|
||||
initializeUserMarker();
|
||||
initializeClusterer();
|
||||
initializeCluster();
|
||||
|
||||
batchIndexRef.current = 0;
|
||||
renderMarkersBatch();
|
||||
}, [addMapController, initializeClusterer, initializeUserMarker, renderMarkersBatch, getMapType, loadMapState]);
|
||||
}, [addMapController, initializeCluster, initializeUserMarker, renderMarkersBatch, getBMapType, loadMapState]);
|
||||
|
||||
useEffect(() => {
|
||||
const switchMapTypeSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.SWITCH_MAP_TYPE, (newType) => {
|
||||
window.sfMetadataContext.localStorage.setItem('map-type', newType);
|
||||
mapRef.current && mapRef.current.setMapType(getMapType(newType));
|
||||
const modifyMapTypeSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_MAP_TYPE, (newType) => {
|
||||
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_TYPE_KEY, newType);
|
||||
mapRef.current && mapRef.current.setMapType(getBMapType(newType));
|
||||
});
|
||||
|
||||
return () => {
|
||||
switchMapTypeSubscribe();
|
||||
modifyMapTypeSubscribe();
|
||||
};
|
||||
|
||||
}, [getMapType, saveMapState]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (mapInfo.type === MAP_PROVIDER.B_MAP) {
|
||||
@@ -186,8 +189,8 @@ const Main = ({ validImages, onOpen }) => {
|
||||
};
|
||||
|
||||
Main.propTypes = {
|
||||
validImages: PropTypes.array,
|
||||
onOpen: PropTypes.func,
|
||||
images: PropTypes.array,
|
||||
onOpenCluster: PropTypes.func,
|
||||
};
|
||||
|
||||
export default Main;
|
@@ -2,8 +2,7 @@ import dayjs from 'dayjs';
|
||||
import { getCellValueByColumn, getFileNameFromRecord, getRecordIdFromRecord, isCellValueChanged } from '../../../utils/cell';
|
||||
import { getColumnByIndex, getColumnOriginName } from '../../../utils/column';
|
||||
import { CellType, NOT_SUPPORT_DRAG_COPY_COLUMN_TYPES, PRIVATE_COLUMN_KEY, TRANSFER_TYPES,
|
||||
REG_NUMBER_DIGIT, REG_STRING_NUMBER_PARTS, COLUMN_RATE_MAX_NUMBER,
|
||||
PASTE_SOURCE,
|
||||
REG_NUMBER_DIGIT, REG_STRING_NUMBER_PARTS, RATE_MAX_NUMBER, PASTE_SOURCE,
|
||||
} from '../../../constants';
|
||||
import { getGroupRecordByIndex } from './group-metrics';
|
||||
import { convertCellValue } from './convert-utils';
|
||||
@@ -594,7 +593,7 @@ class GridUtils {
|
||||
}
|
||||
|
||||
_getRatingLeastSquares(numberList, data) {
|
||||
const { rate_max_number = COLUMN_RATE_MAX_NUMBER[4].name } = data || {};
|
||||
const { rate_max_number = RATE_MAX_NUMBER[4].name } = data || {};
|
||||
let slope;
|
||||
let intercept;
|
||||
let xAverage;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @fileoverview MarkerClusterer标记聚合器用来解决加载大量点要素到地图上产生覆盖现象的问题,并提高性能。
|
||||
* 主入口类是<a href="symbols/BMapLib.MarkerClusterer.html">MarkerClusterer</a>,
|
||||
* 主入口类是<a href="symbols/BMapLib.MarkerCluster.html">MarkerCluster</a>,
|
||||
* 基于Baidu Map API 1.2。
|
||||
*
|
||||
* @author Baidu Map Api Group
|
||||
@@ -97,11 +97,11 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
};
|
||||
|
||||
/**
|
||||
*@exports MarkerClusterer as BMapLib.MarkerClusterer
|
||||
*@exports MarkerCluster as BMapLib.MarkerCluster
|
||||
*/
|
||||
var MarkerClusterer =
|
||||
var MarkerCluster =
|
||||
/**
|
||||
* MarkerClusterer
|
||||
* MarkerCluster
|
||||
* @class 用来解决加载大量点要素到地图上产生覆盖现象的问题,并提高性能
|
||||
* @constructor
|
||||
* @param {Map} map 地图的一个实例。
|
||||
@@ -113,7 +113,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* isAverangeCenter {Boolean} 聚合点的落脚位置是否是所有聚合在内点的平均值,默认为否,落脚在聚合内的第一个点<br />
|
||||
* styles {Array<IconStyle>} 自定义聚合后的图标风格,请参考TextIconOverlay类<br />
|
||||
*/
|
||||
BMapLib.MarkerClusterer = function(map, options){
|
||||
BMapLib.MarkerCluster = function(map, options){
|
||||
if (!map){
|
||||
return;
|
||||
}
|
||||
@@ -151,7 +151,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
*
|
||||
* @return 无返回值。
|
||||
*/
|
||||
MarkerClusterer.prototype.addMarkers = function(markers){
|
||||
MarkerCluster.prototype.addMarkers = function(markers){
|
||||
for(var i = 0, len = markers.length; i <len ; i++){
|
||||
this._pushMarkerTo(markers[i]);
|
||||
}
|
||||
@@ -164,7 +164,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
*
|
||||
* @return 无返回值。
|
||||
*/
|
||||
MarkerClusterer.prototype._pushMarkerTo = function(marker){
|
||||
MarkerCluster.prototype._pushMarkerTo = function(marker){
|
||||
var index = indexOf(marker, this._markers);
|
||||
if(index === -1){
|
||||
marker.isInCluster = false;
|
||||
@@ -177,7 +177,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* @param {BMap.Marker} marker 要聚合的单个标记。
|
||||
* @return 无返回值。
|
||||
*/
|
||||
MarkerClusterer.prototype.addMarker = function(marker) {
|
||||
MarkerCluster.prototype.addMarker = function(marker) {
|
||||
this._pushMarkerTo(marker);
|
||||
this._createClusters();
|
||||
};
|
||||
@@ -186,7 +186,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 根据所给定的标记,创建聚合点,并且遍历所有聚合点
|
||||
* @return 无返回值
|
||||
*/
|
||||
MarkerClusterer.prototype._createClusters = function(){
|
||||
MarkerCluster.prototype._createClusters = function(){
|
||||
var mapBounds = this._map.getBounds();
|
||||
var extendedBounds = getExtendedBounds(this._map, mapBounds, this._gridSize);
|
||||
for(var i = 0, marker; marker = this._markers[i]; i++){
|
||||
@@ -209,7 +209,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
*
|
||||
* @return 无返回值。
|
||||
*/
|
||||
MarkerClusterer.prototype._addToClosestCluster = function (marker){
|
||||
MarkerCluster.prototype._addToClosestCluster = function (marker){
|
||||
var distance = 4000000;
|
||||
var clusterToAddTo = null;
|
||||
var position = marker.getPosition();
|
||||
@@ -237,7 +237,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 清除上一次的聚合的结果
|
||||
* @return 无返回值。
|
||||
*/
|
||||
MarkerClusterer.prototype._clearLastClusters = function(){
|
||||
MarkerCluster.prototype._clearLastClusters = function(){
|
||||
for(var i = 0, cluster; cluster = this._clusters[i]; i++){
|
||||
cluster.remove();
|
||||
}
|
||||
@@ -249,7 +249,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 清除某个聚合中的所有标记
|
||||
* @return 无返回值
|
||||
*/
|
||||
MarkerClusterer.prototype._removeMarkersFromCluster = function(){
|
||||
MarkerCluster.prototype._removeMarkersFromCluster = function(){
|
||||
for(var i = 0, marker; marker = this._markers[i]; i++){
|
||||
marker.isInCluster = false;
|
||||
}
|
||||
@@ -259,7 +259,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 把所有的标记从地图上清除
|
||||
* @return 无返回值
|
||||
*/
|
||||
MarkerClusterer.prototype._removeMarkersFromMap = function(){
|
||||
MarkerCluster.prototype._removeMarkersFromMap = function(){
|
||||
for(var i = 0, marker; marker = this._markers[i]; i++){
|
||||
marker.isInCluster = false;
|
||||
this._map.removeOverlay(marker);
|
||||
@@ -272,7 +272,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
*
|
||||
* @return {Boolean} 删除成功返回true,否则返回false
|
||||
*/
|
||||
MarkerClusterer.prototype._removeMarker = function(marker) {
|
||||
MarkerCluster.prototype._removeMarker = function(marker) {
|
||||
var index = indexOf(marker, this._markers);
|
||||
if (index === -1) {
|
||||
return false;
|
||||
@@ -288,7 +288,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
*
|
||||
* @return {Boolean} 删除成功返回true,否则返回false
|
||||
*/
|
||||
MarkerClusterer.prototype.removeMarker = function(marker) {
|
||||
MarkerCluster.prototype.removeMarker = function(marker) {
|
||||
var success = this._removeMarker(marker);
|
||||
if (success) {
|
||||
this._clearLastClusters();
|
||||
@@ -303,7 +303,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
*
|
||||
* @return {Boolean} 删除成功返回true,否则返回false
|
||||
*/
|
||||
MarkerClusterer.prototype.removeMarkers = function(markers) {
|
||||
MarkerCluster.prototype.removeMarkers = function(markers) {
|
||||
var success = false;
|
||||
for (var i = 0; i < markers.length; i++) {
|
||||
var r = this._removeMarker(markers[i]);
|
||||
@@ -321,7 +321,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 从地图上彻底清除所有的标记
|
||||
* @return 无返回值
|
||||
*/
|
||||
MarkerClusterer.prototype.clearMarkers = function() {
|
||||
MarkerCluster.prototype.clearMarkers = function() {
|
||||
this._clearLastClusters();
|
||||
this._removeMarkersFromMap();
|
||||
this._markers = [];
|
||||
@@ -331,7 +331,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 重新生成,比如改变了属性等
|
||||
* @return 无返回值
|
||||
*/
|
||||
MarkerClusterer.prototype._redraw = function () {
|
||||
MarkerCluster.prototype._redraw = function () {
|
||||
this._clearLastClusters();
|
||||
this._createClusters();
|
||||
};
|
||||
@@ -340,7 +340,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 获取网格大小
|
||||
* @return {Number} 网格大小
|
||||
*/
|
||||
MarkerClusterer.prototype.getGridSize = function() {
|
||||
MarkerCluster.prototype.getGridSize = function() {
|
||||
return this._gridSize;
|
||||
};
|
||||
|
||||
@@ -349,7 +349,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* @param {Number} size 网格大小
|
||||
* @return 无返回值
|
||||
*/
|
||||
MarkerClusterer.prototype.setGridSize = function(size) {
|
||||
MarkerCluster.prototype.setGridSize = function(size) {
|
||||
this._gridSize = size;
|
||||
this._redraw();
|
||||
};
|
||||
@@ -358,7 +358,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 获取聚合的最大缩放级别。
|
||||
* @return {Number} 聚合的最大缩放级别。
|
||||
*/
|
||||
MarkerClusterer.prototype.getMaxZoom = function() {
|
||||
MarkerCluster.prototype.getMaxZoom = function() {
|
||||
return this._maxZoom;
|
||||
};
|
||||
|
||||
@@ -367,7 +367,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* @param {Number} maxZoom 聚合的最大缩放级别
|
||||
* @return 无返回值
|
||||
*/
|
||||
MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
|
||||
MarkerCluster.prototype.setMaxZoom = function(maxZoom) {
|
||||
this._maxZoom = maxZoom;
|
||||
this._redraw();
|
||||
};
|
||||
@@ -376,7 +376,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 获取聚合的样式风格集合
|
||||
* @return {Array<IconStyle>} 聚合的样式风格集合
|
||||
*/
|
||||
MarkerClusterer.prototype.getStyles = function() {
|
||||
MarkerCluster.prototype.getStyles = function() {
|
||||
return this._styles;
|
||||
};
|
||||
|
||||
@@ -385,7 +385,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* @param {Array<IconStyle>} styles 样式风格数组
|
||||
* @return 无返回值
|
||||
*/
|
||||
MarkerClusterer.prototype.setStyles = function(styles) {
|
||||
MarkerCluster.prototype.setStyles = function(styles) {
|
||||
this._styles = styles;
|
||||
this._redraw();
|
||||
};
|
||||
@@ -394,7 +394,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 获取单个聚合的最小数量。
|
||||
* @return {Number} 单个聚合的最小数量。
|
||||
*/
|
||||
MarkerClusterer.prototype.getMinClusterSize = function() {
|
||||
MarkerCluster.prototype.getMinClusterSize = function() {
|
||||
return this._minClusterSize;
|
||||
};
|
||||
|
||||
@@ -403,7 +403,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* @param {Number} size 单个聚合的最小数量。
|
||||
* @return 无返回值。
|
||||
*/
|
||||
MarkerClusterer.prototype.setMinClusterSize = function(size) {
|
||||
MarkerCluster.prototype.setMinClusterSize = function(size) {
|
||||
this._minClusterSize = size;
|
||||
this._redraw();
|
||||
};
|
||||
@@ -412,7 +412,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 获取单个聚合的落脚点是否是聚合内所有标记的平均中心。
|
||||
* @return {Boolean} true或false。
|
||||
*/
|
||||
MarkerClusterer.prototype.isAverageCenter = function() {
|
||||
MarkerCluster.prototype.isAverageCenter = function() {
|
||||
return this._isAverageCenter;
|
||||
};
|
||||
|
||||
@@ -420,7 +420,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 获取聚合的Map实例。
|
||||
* @return {Map} Map的示例。
|
||||
*/
|
||||
MarkerClusterer.prototype.getMap = function() {
|
||||
MarkerCluster.prototype.getMap = function() {
|
||||
return this._map;
|
||||
};
|
||||
|
||||
@@ -428,7 +428,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 获取所有的标记数组。
|
||||
* @return {Array<Marker>} 标记数组。
|
||||
*/
|
||||
MarkerClusterer.prototype.getMarkers = function() {
|
||||
MarkerCluster.prototype.getMarkers = function() {
|
||||
return this._markers;
|
||||
};
|
||||
|
||||
@@ -436,7 +436,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* 获取聚合的总数量。
|
||||
* @return {Number} 聚合的总数量。
|
||||
*/
|
||||
MarkerClusterer.prototype.getClustersCount = function() {
|
||||
MarkerCluster.prototype.getClustersCount = function() {
|
||||
var count = 0;
|
||||
for(var i = 0, cluster; cluster = this._clusters[i]; i++){
|
||||
cluster.isReal() && count++;
|
||||
@@ -444,7 +444,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
return count;
|
||||
};
|
||||
|
||||
MarkerClusterer.prototype.getCallback = function() {
|
||||
MarkerCluster.prototype.getCallback = function() {
|
||||
return this._callback;
|
||||
}
|
||||
|
||||
@@ -453,7 +453,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
|
||||
* Cluster
|
||||
* @class 表示一个聚合对象,该聚合,包含有N个标记,这N个标记组成的范围,并有予以显示在Map上的TextIconOverlay等。
|
||||
* @constructor
|
||||
* @param {MarkerClusterer} markerClusterer 一个标记聚合器示例。
|
||||
* @param {MarkerCluster} markerClusterer 一个标记聚合器示例。
|
||||
*/
|
||||
function Cluster(markerClusterer){
|
||||
this._markerClusterer = markerClusterer;
|
||||
|
Reference in New Issue
Block a user