mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-24 12:58:34 +00:00
Feature/location editor (#8084)
* add geolocation editor * update location with selected position * optimize * google map * optimize * optimize ui & clean up code * fix position validation * update geolocation modal zIndex --------- Co-authored-by: zhouwenxuan <aries@Mac.local>
This commit is contained in:
16
frontend/src/assets/icons/full-screen.svg
Normal file
16
frontend/src/assets/icons/full-screen.svg
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#999999;}
|
||||||
|
</style>
|
||||||
|
<path class="st0" d="M4.6,12.7c0.9,0,1.7-0.7,1.7-1.6V7.8c0-0.4,0.2-0.8,0.5-1.1s0.7-0.5,1.1-0.5h3.3c0.9,0,1.6-0.7,1.6-1.6
|
||||||
|
C12.7,3.7,12,3,11.2,3H4.6C3.7,3,3,3.7,3,4.6v6.6C3,12,3.7,12.7,4.6,12.7z M7.9,25.8c-0.4,0-0.8-0.2-1.1-0.5s-0.5-0.7-0.5-1.1v-3.3
|
||||||
|
c0-0.4-0.2-0.8-0.5-1.1s-0.7-0.5-1.1-0.5c-1,0-1.7,0.7-1.7,1.6v6.5C3,28.3,3.7,29,4.6,29h6.5c0.9,0,1.6-0.7,1.6-1.6
|
||||||
|
s-0.7-1.6-1.6-1.6C11.1,25.8,7.9,25.8,7.9,25.8z M27.3,19.2c-0.9,0-1.6,0.7-1.6,1.6v3.3c0,0.4-0.2,0.8-0.5,1.1
|
||||||
|
c-0.3,0.3-0.7,0.5-1.1,0.5h-3.3c-0.9,0-1.6,0.7-1.6,1.6v0.1c0,0.4,0.2,0.8,0.5,1.1c0.3,0.3,0.7,0.5,1.1,0.5h6.6
|
||||||
|
c0.4,0,0.8-0.2,1.1-0.5c0.3-0.3,0.5-0.7,0.5-1.1v-6.6C29,20,28.3,19.2,27.3,19.2z M20.8,3c-0.9,0-1.6,0.7-1.6,1.6v0.1
|
||||||
|
c0,0.9,0.7,1.6,1.6,1.6h3.3c0.4,0,0.8,0.2,1.1,0.5c0.3,0.3,0.5,0.7,0.5,1.1v3.3c0,0.9,0.7,1.6,1.6,1.6h0.1c0.9-0.1,1.6-0.8,1.6-1.6
|
||||||
|
V4.6C29,3.7,28.3,3,27.4,3H20.8z M21,13v6c0,1.1-0.9,2-2,2h-6c-1.1,0-2-0.9-2-2v-6c0-1.1,0.9-2,2-2h6C20.1,11,21,11.9,21,13z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@@ -6,7 +6,7 @@ import { CellType, COLUMNS_ICON_CONFIG } from '../../../metadata/constants';
|
|||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
const DetailItem = ({ readonly = true, field, className, children }) => {
|
const DetailItem = ({ id, readonly = true, field, className, children }) => {
|
||||||
const icon = useMemo(() => {
|
const icon = useMemo(() => {
|
||||||
if (field.type === 'size') {
|
if (field.type === 'size') {
|
||||||
return COLUMNS_ICON_CONFIG[CellType.NUMBER];
|
return COLUMNS_ICON_CONFIG[CellType.NUMBER];
|
||||||
@@ -15,7 +15,7 @@ const DetailItem = ({ readonly = true, field, className, children }) => {
|
|||||||
}, [field]);
|
}, [field]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames('dirent-detail-item', className)}>
|
<div id={id} className={classnames('dirent-detail-item', className)}>
|
||||||
<div className="dirent-detail-item-name d-flex">
|
<div className="dirent-detail-item-name d-flex">
|
||||||
<div><Icon className="sf-metadata-icon" symbol={icon} /></div>
|
<div><Icon className="sf-metadata-icon" symbol={icon} /></div>
|
||||||
<span className="dirent-detail-item-name-value">{field.name}</span>
|
<span className="dirent-detail-item-name-value">{field.name}</span>
|
||||||
|
@@ -0,0 +1,162 @@
|
|||||||
|
import { gettext } from '../../../../utils/constants';
|
||||||
|
|
||||||
|
const generateLabelContent = (info, isBMap = false) => {
|
||||||
|
const { location_translated, title, tag } = info;
|
||||||
|
const { address } = location_translated;
|
||||||
|
const tagContent = Array.isArray(tag) && tag.length > 0 ? tag[0] : '';
|
||||||
|
|
||||||
|
if (isBMap) {
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
class='${title ? 'selection-label-content' : 'selection-label-content simple'}'
|
||||||
|
id='selection-label-content'
|
||||||
|
>
|
||||||
|
${`
|
||||||
|
<div class="w-100 d-flex align-items-center">
|
||||||
|
${title ? `
|
||||||
|
<span class="label-title text-truncate" title="${title}">${title}</span>
|
||||||
|
<span class="close-btn" id="selection-label-close">
|
||||||
|
<i class="sf3-font sf3-font-x-01"></i>
|
||||||
|
</span>
|
||||||
|
` : `
|
||||||
|
<span class="label-address text-truncate simple" title="${address}">${address}</span>
|
||||||
|
<span class="close-btn" id="selection-label-close">
|
||||||
|
<i class="sf3-font sf3-font-x-01"></i>
|
||||||
|
</span>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
${title ? `
|
||||||
|
${tagContent ? `<span class="label-tag">${tagContent}</span>` : ''}
|
||||||
|
<span class="label-address-tip">${gettext('Address')}</span>
|
||||||
|
<span class="label-address text-truncate" title="${address}">${address}</span>
|
||||||
|
` : ''}
|
||||||
|
<div class="label-submit btn btn-primary" id="selection-label-submit">${gettext('Fill in')}</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = title ? 'selection-label-content' : 'selection-label-content simple';
|
||||||
|
container.id = 'selection-label-content';
|
||||||
|
|
||||||
|
const content = `
|
||||||
|
<div class="w-100 d-flex align-items-center">
|
||||||
|
${title ? `
|
||||||
|
<span class="label-title text-truncate" title="${title}">${title}</span>
|
||||||
|
<span class="close-btn" id="selection-label-close">
|
||||||
|
<i class="sf3-font sf3-font-x-01"></i>
|
||||||
|
</span>
|
||||||
|
` : `
|
||||||
|
<span class="label-address text-truncate simple" title="${address}">${address}</span>
|
||||||
|
<span class="close-btn" id="selection-label-close">
|
||||||
|
<i class="sf3-font sf3-font-x-01"></i>
|
||||||
|
</span>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
${title ? `
|
||||||
|
${tagContent ? `<span class="label-tag">${tagContent}</span>` : ''}
|
||||||
|
<span class="label-address-tip">${gettext('Address')}</span>
|
||||||
|
<span class="label-address text-truncate" title="${address}">${address}</span>
|
||||||
|
` : ''}
|
||||||
|
<div class="label-submit btn btn-primary" id="selection-label-submit">${gettext('Fill in')}</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
container.innerHTML = content;
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const customBMapLabel = (info) => {
|
||||||
|
const content = generateLabelContent(info, true);
|
||||||
|
const label = new window.BMapGL.Label(content, { offset: new window.BMapGL.Size(9, -5) });
|
||||||
|
const style = info.title
|
||||||
|
? { transform: 'translateY(-50%, 10%)' }
|
||||||
|
: { transform: 'translateY(-50%, 15%)' };
|
||||||
|
label.setStyle(style);
|
||||||
|
|
||||||
|
return label;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const customGMapLabel = (info, submit) => {
|
||||||
|
class Popup extends window.google.maps.OverlayView {
|
||||||
|
constructor(position, content) {
|
||||||
|
super();
|
||||||
|
this.position = position;
|
||||||
|
this.info = info;
|
||||||
|
this.containerDiv = document.createElement('div');
|
||||||
|
this.containerDiv.classList.add('popup-label-container');
|
||||||
|
this.containerDiv.appendChild(content);
|
||||||
|
|
||||||
|
const closeBtn = this.containerDiv.querySelector('#selection-label-close');
|
||||||
|
if (closeBtn) {
|
||||||
|
closeBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.onRemove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitBtn = this.containerDiv.querySelector('#selection-label-submit');
|
||||||
|
if (submitBtn) {
|
||||||
|
submitBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
submit(info);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Popup.preventMapHitsAndGesturesFrom(this.containerDiv);
|
||||||
|
}
|
||||||
|
onAdd() {
|
||||||
|
this.getPanes().floatPane.appendChild(this.containerDiv);
|
||||||
|
}
|
||||||
|
onRemove() {
|
||||||
|
if (this.containerDiv.parentElement) {
|
||||||
|
this.containerDiv.parentElement.removeChild(this.containerDiv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setPosition(position) {
|
||||||
|
this.position = position;
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
setInfo(info) {
|
||||||
|
this.info = info;
|
||||||
|
this.containerDiv.innerHTML = generateLabelContent(info, true);
|
||||||
|
const closeBtn = this.containerDiv.querySelector('#selection-label-close');
|
||||||
|
if (closeBtn) {
|
||||||
|
closeBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.onRemove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const submitBtn = this.containerDiv.querySelector('#selection-label-submit');
|
||||||
|
if (submitBtn) {
|
||||||
|
submitBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
submit(info);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
const divPosition = this.getProjection().fromLatLngToDivPixel(
|
||||||
|
this.position,
|
||||||
|
);
|
||||||
|
// Hide the popup when it is far out of view.
|
||||||
|
const display =
|
||||||
|
Math.abs(divPosition.x) < 4000 && Math.abs(divPosition.y) < 4000
|
||||||
|
? 'block'
|
||||||
|
: 'none';
|
||||||
|
|
||||||
|
if (display === 'block') {
|
||||||
|
this.containerDiv.style.left = divPosition.x + 'px';
|
||||||
|
this.containerDiv.style.top = divPosition.y + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.containerDiv.style.display !== display) {
|
||||||
|
this.containerDiv.style.display = display;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = generateLabelContent(info);
|
||||||
|
return new Popup(info.position, content);
|
||||||
|
};
|
@@ -0,0 +1,284 @@
|
|||||||
|
.sf-geolocation-editor-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 434px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container.full-screen {
|
||||||
|
width: 100%;
|
||||||
|
height: 600px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 0 10px #0000004d;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .editor-header {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 12px;
|
||||||
|
background-color: #fff;
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .editor-header .title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .editor-header .title .location-icon {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .editor-header .full-screen {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .editor-header .full-screen:hover {
|
||||||
|
background-color: var(--bs-hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-container {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 450px;
|
||||||
|
height: 38px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
left: 24px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-container .search-input {
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
padding-right: 38px;
|
||||||
|
border-radius: 3px 0 0 3px;
|
||||||
|
border-right: none;
|
||||||
|
box-shadow: 0 0 2px #0000004d;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-container .clean-btn {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
position: absolute;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
top: 7px;
|
||||||
|
right: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-container .clean-btn:hover {
|
||||||
|
background-color: var(--bs-hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-container .search-btn {
|
||||||
|
width: 12%;
|
||||||
|
height: 38px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--bs-body-tertiary-bg);
|
||||||
|
color: #666;
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
border-radius: 0 3px 3px 0;
|
||||||
|
box-shadow: 0 0 2px #0000004d;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content {
|
||||||
|
width: 225px;
|
||||||
|
height: 130px;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: 1px 2px 1px rgba(0, 0, 0, .15);
|
||||||
|
padding: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: default;
|
||||||
|
left: -115px;
|
||||||
|
top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content .close-btn {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
top: 4px;
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content .close-btn:hover {
|
||||||
|
background-color: var(--bs-hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content .label-title {
|
||||||
|
width: 90%;
|
||||||
|
color: #212529;
|
||||||
|
font-size: 18px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content .label-tag {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content .label-address-tip {
|
||||||
|
position: absolute;
|
||||||
|
top: 58px;
|
||||||
|
left: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content .label-address {
|
||||||
|
width: 90%;
|
||||||
|
position: absolute;
|
||||||
|
top: 74px;
|
||||||
|
left: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content .label-submit {
|
||||||
|
width: fit-content;
|
||||||
|
min-width: 50px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 4px;
|
||||||
|
right: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content seafile-multicolor-icon-drop-down {
|
||||||
|
position: absolute;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
top: -16px;
|
||||||
|
left: 47%;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content.simple {
|
||||||
|
height: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .selection-label-content.simple .label-address {
|
||||||
|
width: 205px;
|
||||||
|
position: absolute;
|
||||||
|
top: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .sf-map-control-container {
|
||||||
|
width: 30px;
|
||||||
|
height: fit-content;
|
||||||
|
flex-direction: column;
|
||||||
|
right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .sf-map-control-container.sf-map-geolocation-control-container {
|
||||||
|
right: 16px !important;
|
||||||
|
bottom: 96px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .sf-map-control-container.sf-map-zoom-control-container {
|
||||||
|
right: 16px !important;
|
||||||
|
bottom: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .sf-map-control-container .sf-map-control-divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .sf-map-control-container .sf-map-control-divider::before {
|
||||||
|
width: 22px;
|
||||||
|
height: 1px;
|
||||||
|
left: 4px;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-results-container {
|
||||||
|
width: 404px;
|
||||||
|
height: fit-content;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
position: absolute;
|
||||||
|
top: 62px;
|
||||||
|
left: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0 10px;
|
||||||
|
box-shadow: 0 -0 3px rgb(0 0 0 / 30%);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-result-item {
|
||||||
|
height: 56px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
padding: 8px;
|
||||||
|
border-bottom: 1px solid var(--bs-border-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-result-item:hover {
|
||||||
|
background-color: var(--bs-th-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-result-item .search-result-item-title {
|
||||||
|
position: relative;
|
||||||
|
height: 16px;
|
||||||
|
color: #212529;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-geolocation-editor-container .search-result-item .search-result-item-address {
|
||||||
|
position: relative;
|
||||||
|
color: #666;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-label-container {
|
||||||
|
cursor: auto;
|
||||||
|
height: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 200px;
|
||||||
|
}
|
@@ -0,0 +1,501 @@
|
|||||||
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { createBMapGeolocationControl, createBMapZoomControl } from '../../map-controller';
|
||||||
|
import { initMapInfo, loadMapSource } from '../../../../utils/map-utils';
|
||||||
|
import { baiduMapKey, gettext, googleMapId, googleMapKey, lang } from '../../../../utils/constants';
|
||||||
|
import { KeyCodes, MAP_TYPE } from '../../../../constants';
|
||||||
|
import Icon from '../../../../components/icon';
|
||||||
|
import IconBtn from '../../../../components/icon-btn';
|
||||||
|
import toaster from '../../../../components/toast';
|
||||||
|
import { createZoomControl } from '../../map-controller/zoom';
|
||||||
|
import { createGeolocationControl } from '../../map-controller/geolocation';
|
||||||
|
import { customBMapLabel, customGMapLabel } from './custom-label';
|
||||||
|
import { isValidPosition } from '../../../utils/validate';
|
||||||
|
import { DEFAULT_POSITION } from '../../../constants';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const GeolocationEditor = ({ position, isFullScreen, onSubmit, onFullScreen, onReadyToEraseLocation }) => {
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
const [searchResults, setSearchResults] = useState([]);
|
||||||
|
|
||||||
|
const type = useMemo(() => {
|
||||||
|
const { type } = initMapInfo({ baiduMapKey, googleMapKey });
|
||||||
|
return type;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const ref = useRef(null);
|
||||||
|
const mapRef = useRef(null);
|
||||||
|
const geocRef = useRef(null);
|
||||||
|
const markerRef = useRef(null);
|
||||||
|
const labelRef = useRef(null);
|
||||||
|
const googlePlacesRef = useRef(null);
|
||||||
|
|
||||||
|
const onChange = useCallback((e) => {
|
||||||
|
setInputValue(e.target.value);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const search = useCallback(() => {
|
||||||
|
if (type === MAP_TYPE.B_MAP) {
|
||||||
|
const options = {
|
||||||
|
onSearchComplete: (results) => {
|
||||||
|
const status = local.getStatus();
|
||||||
|
if (status !== window.BMAP_STATUS_SUCCESS) {
|
||||||
|
toaster.danger(gettext('Search failed, please enter detailed address.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let searchResults = [];
|
||||||
|
for (let i = 0; i < results.getCurrentNumPois(); i++) {
|
||||||
|
const value = results.getPoi(i);
|
||||||
|
const position = {
|
||||||
|
address: value.address || '',
|
||||||
|
title: value.title || '',
|
||||||
|
tag: value.tags || [],
|
||||||
|
lngLat: {
|
||||||
|
lng: value.point.lng,
|
||||||
|
lat: value.point.lat,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
searchResults.push(position);
|
||||||
|
}
|
||||||
|
setSearchResults(searchResults);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const local = new window.BMapGL.LocalSearch(mapRef.current, options);
|
||||||
|
local.search(inputValue);
|
||||||
|
} else if (type === MAP_TYPE.G_MAP) {
|
||||||
|
const request = {
|
||||||
|
query: inputValue,
|
||||||
|
language: lang,
|
||||||
|
};
|
||||||
|
googlePlacesRef.current.textSearch(request, (results, status) => {
|
||||||
|
if (status === 'OK' && results[0]) {
|
||||||
|
let searchResults = [];
|
||||||
|
for (let i = 0; i < results.length; i++) {
|
||||||
|
const value = {
|
||||||
|
address: results[i].formatted_address || '',
|
||||||
|
title: results[i].name || '',
|
||||||
|
tag: results[i].types || [],
|
||||||
|
lngLat: {
|
||||||
|
lng: results[i].geometry.location.lng(),
|
||||||
|
lat: results[i].geometry.location.lat(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
searchResults.push(value);
|
||||||
|
}
|
||||||
|
setSearchResults(searchResults);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [type, inputValue]);
|
||||||
|
|
||||||
|
const onKeyDown = useCallback((e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
if (e.keyCode === KeyCodes.Enter) {
|
||||||
|
search();
|
||||||
|
} else if (e.keyCode === KeyCodes.Backspace) {
|
||||||
|
setSearchResults([]);
|
||||||
|
}
|
||||||
|
}, [search]);
|
||||||
|
|
||||||
|
const clear = useCallback(() => {
|
||||||
|
setInputValue('');
|
||||||
|
setSearchResults([]);
|
||||||
|
if (type === MAP_TYPE.B_MAP) {
|
||||||
|
mapRef.current.clearOverlays();
|
||||||
|
} else {
|
||||||
|
labelRef.current.setMap(null);
|
||||||
|
labelRef.current = null;
|
||||||
|
}
|
||||||
|
onReadyToEraseLocation();
|
||||||
|
}, [type, onReadyToEraseLocation]);
|
||||||
|
|
||||||
|
const close = useCallback((e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (type === MAP_TYPE.B_MAP) {
|
||||||
|
markerRef.current.getLabel()?.remove();
|
||||||
|
} else {
|
||||||
|
labelRef.current.setMap(null);
|
||||||
|
labelRef.current = null;
|
||||||
|
}
|
||||||
|
}, [type]);
|
||||||
|
|
||||||
|
const submit = useCallback((value) => {
|
||||||
|
const { position, location_translated } = value;
|
||||||
|
const location = {
|
||||||
|
position,
|
||||||
|
location_translated,
|
||||||
|
};
|
||||||
|
onSubmit(location);
|
||||||
|
}, [onSubmit]);
|
||||||
|
|
||||||
|
const parseBMapAddress = useCallback((result) => {
|
||||||
|
let value = {};
|
||||||
|
const { surroundingPois, address, addressComponents, point } = result;
|
||||||
|
if (surroundingPois.length === 0) {
|
||||||
|
const { province, city, district, street } = addressComponents;
|
||||||
|
value.position = { lng: point.lng, lat: point.lat };
|
||||||
|
value.location_translated = {
|
||||||
|
address,
|
||||||
|
country: '',
|
||||||
|
province,
|
||||||
|
city,
|
||||||
|
district,
|
||||||
|
street,
|
||||||
|
};
|
||||||
|
value.title = '';
|
||||||
|
value.tags = [];
|
||||||
|
} else {
|
||||||
|
const position = surroundingPois[0];
|
||||||
|
const { address, title, tags, point, city, province } = position;
|
||||||
|
value.position = { lng: point.lng, lat: point.lat };
|
||||||
|
value.location_translated = {
|
||||||
|
address,
|
||||||
|
country: '',
|
||||||
|
province,
|
||||||
|
city,
|
||||||
|
district: '',
|
||||||
|
street: '',
|
||||||
|
};
|
||||||
|
value.title = title || '';
|
||||||
|
value.tags = tags || [];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const parseGMapAddress = useCallback((result) => {
|
||||||
|
const location_translated = {
|
||||||
|
country: '',
|
||||||
|
province: '',
|
||||||
|
city: '',
|
||||||
|
district: '',
|
||||||
|
street: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
result.address_components.forEach(component => {
|
||||||
|
if (component.types.includes('country')) {
|
||||||
|
location_translated.country = component.long_name;
|
||||||
|
} else if (component.types.includes('administrative_area_level_1')) {
|
||||||
|
location_translated.province = component.long_name;
|
||||||
|
} else if (component.types.includes('locality')) {
|
||||||
|
location_translated.city = component.long_name;
|
||||||
|
} else if (component.types.includes('sublocality')) {
|
||||||
|
location_translated.district = component.long_name;
|
||||||
|
} else if (component.types.includes('route')) {
|
||||||
|
location_translated.street = component.long_name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
location_translated.address = result.formatted_address;
|
||||||
|
|
||||||
|
const position = {
|
||||||
|
lng: result.geometry.location.lng(),
|
||||||
|
lat: result.geometry.location.lat()
|
||||||
|
};
|
||||||
|
|
||||||
|
return { position, location_translated };
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const addLabel = useCallback((point) => {
|
||||||
|
if (type === MAP_TYPE.B_MAP) {
|
||||||
|
markerRef.current.getLabel()?.remove();
|
||||||
|
geocRef.current.getLocation(point, (result) => {
|
||||||
|
const info = parseBMapAddress(result);
|
||||||
|
const { title, location_translated } = info;
|
||||||
|
setInputValue(title || location_translated.address);
|
||||||
|
const label = customBMapLabel(info);
|
||||||
|
markerRef.current.setLabel(label);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const label = document.getElementById('selection-label-content');
|
||||||
|
if (label) {
|
||||||
|
label.addEventListener('click', (e) => e.stopPropagation());
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeBtn = document.getElementById('selection-label-close');
|
||||||
|
if (closeBtn) {
|
||||||
|
closeBtn.addEventListener('click', close);
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitBtn = document.getElementById('selection-label-submit');
|
||||||
|
if (submitBtn) {
|
||||||
|
submitBtn.addEventListener('click', () => submit(info));
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
geocRef.current.geocode({ location: point, language: lang }, (results, status) => {
|
||||||
|
if (status === 'OK' && results[0]) {
|
||||||
|
const info = parseGMapAddress(results[0]);
|
||||||
|
labelRef.current = customGMapLabel(info, submit);
|
||||||
|
labelRef.current.setMap(mapRef.current);
|
||||||
|
setInputValue(info.location_translated.address);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [type, close, submit, parseBMapAddress, parseGMapAddress]);
|
||||||
|
|
||||||
|
const renderBaiduMap = useCallback(() => {
|
||||||
|
if (!window.BMapGL.Map) return;
|
||||||
|
|
||||||
|
mapRef.current = new window.BMapGL.Map(ref.current);
|
||||||
|
const initPos = isValidPosition(position?.lng, position?.lat) ? position : DEFAULT_POSITION;
|
||||||
|
const point = new window.BMapGL.Point(initPos.lng, initPos.lat);
|
||||||
|
mapRef.current.centerAndZoom(point, 16);
|
||||||
|
mapRef.current.enableScrollWheelZoom();
|
||||||
|
mapRef.current.clearOverlays();
|
||||||
|
|
||||||
|
const ZoomControl = createBMapZoomControl({
|
||||||
|
anchor: window.BMAP_ANCHOR_BOTTOM_RIGHT,
|
||||||
|
offset: { x: 16, y: 30 }
|
||||||
|
});
|
||||||
|
const zoomControl = new ZoomControl();
|
||||||
|
mapRef.current.addControl(zoomControl);
|
||||||
|
|
||||||
|
const GeolocationControl = createBMapGeolocationControl({
|
||||||
|
anchor: window.BMAP_ANCHOR_BOTTOM_RIGHT,
|
||||||
|
offset: { x: 16, y: 96 },
|
||||||
|
callback: (point) => {
|
||||||
|
if (mapRef.current.getOverlays().length === 0) {
|
||||||
|
mapRef.current.addOverlay(markerRef.current);
|
||||||
|
}
|
||||||
|
mapRef.current.centerAndZoom(point, 16);
|
||||||
|
markerRef.current.setPosition(point);
|
||||||
|
addLabel(point);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const geolocationControl = new GeolocationControl();
|
||||||
|
mapRef.current.addControl(geolocationControl);
|
||||||
|
|
||||||
|
markerRef.current = new window.BMapGL.Marker(point, { offset: new window.BMapGL.Size(-2, -5) });
|
||||||
|
geocRef.current = new window.BMapGL.Geocoder();
|
||||||
|
if (isValidPosition(position?.lng, position?.lat)) {
|
||||||
|
mapRef.current.addOverlay(markerRef.current);
|
||||||
|
addLabel(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapRef.current.addEventListener('click', (e) => {
|
||||||
|
if (searchResults.length > 0) {
|
||||||
|
setSearchResults([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { lng, lat } = e.latlng;
|
||||||
|
const point = new window.BMapGL.Point(lng, lat);
|
||||||
|
if (mapRef.current.getOverlays().length === 0) {
|
||||||
|
mapRef.current.addOverlay(markerRef.current);
|
||||||
|
}
|
||||||
|
markerRef.current.setPosition(point);
|
||||||
|
mapRef.current.setCenter(point);
|
||||||
|
addLabel(point);
|
||||||
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [position?.lng, position?.lat]);
|
||||||
|
|
||||||
|
const renderGoogleMap = useCallback(() => {
|
||||||
|
const isValid = isValidPosition(position?.lng, position?.lat);
|
||||||
|
const initPos = isValid ? position : DEFAULT_POSITION;
|
||||||
|
mapRef.current = new window.google.maps.Map(ref.current, {
|
||||||
|
center: initPos,
|
||||||
|
zoom: 16,
|
||||||
|
mapId: googleMapId,
|
||||||
|
zoomControl: false,
|
||||||
|
mapTypeControl: false,
|
||||||
|
scaleControl: false,
|
||||||
|
streetViewControl: false,
|
||||||
|
rotateControl: false,
|
||||||
|
fullscreenControl: false,
|
||||||
|
disableDefaultUI: true,
|
||||||
|
gestrueHandling: 'cooperative',
|
||||||
|
clickableIcons: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// control
|
||||||
|
const zoomControl = createZoomControl({ map: mapRef.current });
|
||||||
|
const geolocationControl = createGeolocationControl({
|
||||||
|
map: mapRef.current,
|
||||||
|
callback: (lngLat) => {
|
||||||
|
geocRef.current.geocode({ location: lngLat, language: lang }, (results, status) => {
|
||||||
|
if (status === 'OK' && results[0]) {
|
||||||
|
const info = parseGMapAddress(results[0]);
|
||||||
|
setInputValue(info.location_translated.address);
|
||||||
|
if (!markerRef.current) {
|
||||||
|
markerRef.current = new window.google.maps.marker.AdvancedMarkerElement({
|
||||||
|
position: lngLat,
|
||||||
|
map: mapRef.current,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markerRef.current.position = lngLat;
|
||||||
|
}
|
||||||
|
if (!labelRef.current) {
|
||||||
|
addLabel(lngLat);
|
||||||
|
} else {
|
||||||
|
labelRef.current.setPosition(lngLat);
|
||||||
|
labelRef.current.setInfo(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mapRef.current.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(zoomControl);
|
||||||
|
mapRef.current.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(geolocationControl);
|
||||||
|
|
||||||
|
// marker
|
||||||
|
if (isValid) {
|
||||||
|
markerRef.current = new window.google.maps.marker.AdvancedMarkerElement({
|
||||||
|
position,
|
||||||
|
map: mapRef.current,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// geocoder
|
||||||
|
geocRef.current = new window.google.maps.Geocoder();
|
||||||
|
isValid && addLabel(position);
|
||||||
|
|
||||||
|
googlePlacesRef.current = new window.google.maps.places.PlacesService(mapRef.current);
|
||||||
|
|
||||||
|
// map click event
|
||||||
|
window.google.maps.event.addListener(mapRef.current, 'click', (e) => {
|
||||||
|
if (searchResults.length > 0) {
|
||||||
|
setSearchResults([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const latLng = e.latLng;
|
||||||
|
const point = { lat: latLng.lat(), lng: latLng.lng() };
|
||||||
|
|
||||||
|
if (!markerRef.current) {
|
||||||
|
markerRef.current = new window.google.maps.marker.AdvancedMarkerElement({
|
||||||
|
position: point,
|
||||||
|
map: mapRef.current,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
markerRef.current.position = latLng;
|
||||||
|
}
|
||||||
|
mapRef.current.panTo(latLng);
|
||||||
|
|
||||||
|
geocRef.current.geocode({ location: point, language: lang }, (results, status) => {
|
||||||
|
if (status === 'OK' && results[0]) {
|
||||||
|
const info = parseGMapAddress(results[0]);
|
||||||
|
if (!labelRef.current) {
|
||||||
|
addLabel(point);
|
||||||
|
} else {
|
||||||
|
labelRef.current.setPosition(latLng);
|
||||||
|
labelRef.current.setInfo(info);
|
||||||
|
}
|
||||||
|
setInputValue(info.location_translated.address);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [searchResults, position, addLabel, parseGMapAddress]);
|
||||||
|
|
||||||
|
const toggleFullScreen = useCallback((e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
onFullScreen();
|
||||||
|
}, [onFullScreen]);
|
||||||
|
|
||||||
|
const onSelect = useCallback((result) => {
|
||||||
|
const { lngLat, title, address } = result;
|
||||||
|
let point = lngLat;
|
||||||
|
if (type === MAP_TYPE.B_MAP) {
|
||||||
|
const { lng, lat } = lngLat;
|
||||||
|
point = new window.BMapGL.Point(lng, lat);
|
||||||
|
if (mapRef.current.getOverlays().length === 0) {
|
||||||
|
mapRef.current.addOverlay(markerRef.current);
|
||||||
|
}
|
||||||
|
markerRef.current.setPosition(point);
|
||||||
|
mapRef.current.setCenter(point);
|
||||||
|
addLabel(point);
|
||||||
|
} else {
|
||||||
|
const point = { lat: lngLat.lat, lng: lngLat.lng };
|
||||||
|
markerRef.current.position = point;
|
||||||
|
if (!labelRef.current) {
|
||||||
|
addLabel(point);
|
||||||
|
} else {
|
||||||
|
labelRef.current.setPosition(point);
|
||||||
|
labelRef.current.setInfo({
|
||||||
|
title,
|
||||||
|
tag: [],
|
||||||
|
position: point,
|
||||||
|
location_translated: {
|
||||||
|
address,
|
||||||
|
country: '',
|
||||||
|
province: '',
|
||||||
|
city: '',
|
||||||
|
district: '',
|
||||||
|
street: '',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
mapRef.current.panTo(point);
|
||||||
|
}
|
||||||
|
setSearchResults([]);
|
||||||
|
setInputValue(title || address);
|
||||||
|
}, [type, addLabel]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (mapRef.current) return;
|
||||||
|
const { type, key } = initMapInfo({ baiduMapKey, googleMapKey });
|
||||||
|
if (type === MAP_TYPE.B_MAP) {
|
||||||
|
if (!window.BMapGL) {
|
||||||
|
window.renderBaiduMap = () => renderBaiduMap();
|
||||||
|
loadMapSource(type, key);
|
||||||
|
} else {
|
||||||
|
renderBaiduMap();
|
||||||
|
}
|
||||||
|
} else if (type === MAP_TYPE.G_MAP) {
|
||||||
|
if (!window.google?.maps.Map) {
|
||||||
|
window.renderGoogleMap = () => renderGoogleMap();
|
||||||
|
loadMapSource(type, key);
|
||||||
|
} else {
|
||||||
|
renderGoogleMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('sf-geolocation-editor-container', { 'full-screen': isFullScreen })}>
|
||||||
|
<div className="editor-header">
|
||||||
|
<div className="title">
|
||||||
|
<Icon symbol="location" size={24} className="location-icon" />
|
||||||
|
<span className="ml-2">{gettext('Address')}</span>
|
||||||
|
</div>
|
||||||
|
<IconBtn className="full-screen" symbol="full-screen" size={24} onClick={toggleFullScreen} />
|
||||||
|
</div>
|
||||||
|
<div className="w-100 h-100 position-relative">
|
||||||
|
<div className="search-container">
|
||||||
|
<div className="flex-1 d-flex position-relative">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={inputValue}
|
||||||
|
className="form-control search-input"
|
||||||
|
placeholder={gettext('Please enter the address')}
|
||||||
|
onChange={onChange}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
{inputValue && <IconBtn symbol="close" className="clean-btn" size={24} onClick={clear} />}
|
||||||
|
</div>
|
||||||
|
<span className="search-btn" onClick={search}>
|
||||||
|
<i className="sf3-font sf3-font-search"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div ref={ref} className="w-100 h-100 sf-metadata-geolocation-editor-container"></div>
|
||||||
|
{searchResults.length > 0 && (
|
||||||
|
<div className="search-results-container">
|
||||||
|
{searchResults.map((result, index) => (
|
||||||
|
<div key={index} className="search-result-item" onClick={() => onSelect(result)}>
|
||||||
|
<span className="search-result-item-title">{result.title || ''}</span>
|
||||||
|
<span className="search-result-item-address">{result.address || ''}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GeolocationEditor;
|
@@ -2,7 +2,7 @@ import classnames from 'classnames';
|
|||||||
import { Utils } from '../../../utils/utils';
|
import { Utils } from '../../../utils/utils';
|
||||||
import { wgs84_to_gcj02 } from '../../../utils/coord-transform';
|
import { wgs84_to_gcj02 } from '../../../utils/coord-transform';
|
||||||
|
|
||||||
export const createGeolocationControl = (map) => {
|
export const createGeolocationControl = ({ map, callback }) => {
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
container.className = classnames(
|
container.className = classnames(
|
||||||
'sf-map-control-container sf-map-geolocation-control-container d-flex align-items-center justify-content-center',
|
'sf-map-control-container sf-map-geolocation-control-container d-flex align-items-center justify-content-center',
|
||||||
@@ -23,6 +23,7 @@ export const createGeolocationControl = (map) => {
|
|||||||
navigator.geolocation.getCurrentPosition((userInfo) => {
|
navigator.geolocation.getCurrentPosition((userInfo) => {
|
||||||
const gcPosition = wgs84_to_gcj02(userInfo.coords.longitude, userInfo.coords.latitude);
|
const gcPosition = wgs84_to_gcj02(userInfo.coords.longitude, userInfo.coords.latitude);
|
||||||
map.setCenter(gcPosition);
|
map.setCenter(gcPosition);
|
||||||
|
callback(gcPosition);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -39,7 +40,7 @@ export function createBMapGeolocationControl({ anchor, offset, callback }) {
|
|||||||
GeolocationControl.prototype.initialize = function (map) {
|
GeolocationControl.prototype.initialize = function (map) {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
let className = classnames('sf-map-control-container sf-map-geolocation-control-container d-flex align-items-center justify-content-center', {
|
let className = classnames('sf-map-control-container sf-map-geolocation-control-container d-flex align-items-center justify-content-center', {
|
||||||
'sf-map-geolocation-control-mobile': !Utils.isDesktop()
|
'sf-map-control-container-mobile': !Utils.isDesktop()
|
||||||
});
|
});
|
||||||
|
|
||||||
const locationButton = document.createElement('div');
|
const locationButton = document.createElement('div');
|
||||||
|
@@ -37,21 +37,11 @@
|
|||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-map-control-container.sf-map-geolocation-control-container {
|
|
||||||
right: 30px !important;
|
|
||||||
bottom: 30px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sf-map-control-container .sf-map-geolocation-control {
|
.sf-map-control-container .sf-map-geolocation-control {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-map-control-container.sf-map-zoom-control-container {
|
|
||||||
right: 66px !important;
|
|
||||||
bottom: 30px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sf-map-control-container .sf-map-zoom-control {
|
.sf-map-control-container .sf-map-zoom-control {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,7 @@ const updateButtonStates = (map, zoomIn, zoomOut) => {
|
|||||||
zoomOut.className = classnames(buttonClassName, { 'disabled': zoomLevel <= MIN_ZOOM });
|
zoomOut.className = classnames(buttonClassName, { 'disabled': zoomLevel <= MIN_ZOOM });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createZoomControl = (map) => {
|
export const createZoomControl = ({ map }) => {
|
||||||
const container = createZoomContainer();
|
const container = createZoomContainer();
|
||||||
|
|
||||||
const zoomInButton = createButton('<i class="sf-map-control-icon sf3-font sf3-font-zoom-in"></i>');
|
const zoomInButton = createButton('<i class="sf-map-control-icon sf3-font sf3-font-zoom-in"></i>');
|
||||||
@@ -60,7 +60,7 @@ export const createZoomControl = (map) => {
|
|||||||
return container;
|
return container;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createBMapZoomControl(anchor, offset) {
|
export function createBMapZoomControl({ anchor, offset }) {
|
||||||
function ZoomControl() {
|
function ZoomControl() {
|
||||||
this.defaultAnchor = anchor || window.BMAP_ANCHOR_BOTTOM_RIGHT;
|
this.defaultAnchor = anchor || window.BMAP_ANCHOR_BOTTOM_RIGHT;
|
||||||
this.defaultOffset = new window.BMapGL.Size(offset?.x || 66, offset?.y || 30);
|
this.defaultOffset = new window.BMapGL.Size(offset?.x || 66, offset?.y || 30);
|
||||||
|
@@ -33,8 +33,8 @@ const MetadataDetails = ({ readOnly, tagsData }) => {
|
|||||||
if (isDir && FOLDER_NOT_DISPLAY_COLUMN_KEYS.includes(field.key)) return null;
|
if (isDir && FOLDER_NOT_DISPLAY_COLUMN_KEYS.includes(field.key)) return null;
|
||||||
const value = getCellValueByColumn(record, field);
|
const value = getCellValueByColumn(record, field);
|
||||||
|
|
||||||
if (field.key === PRIVATE_COLUMN_KEY.LOCATION && Utils.imageCheck(fileName) && value) {
|
if (field.key === PRIVATE_COLUMN_KEY.LOCATION && Utils.imageCheck(fileName)) {
|
||||||
return <Location key={field.key} position={value} record={record} />;
|
return <Location key={field.key} position={value} record={record} onChange={onChange} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let canEdit = canModifyRecord && field.editable && !readOnly;
|
let canEdit = canModifyRecord && field.editable && !readOnly;
|
||||||
|
@@ -32,3 +32,27 @@
|
|||||||
right: 10px !important;
|
right: 10px !important;
|
||||||
bottom: 16px !important;
|
bottom: 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dirent-detail-item-value-map .sf-metadata-ui.sf-metadata-geolocation-formatter {
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-metadata-record-cell-empty {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding: 7px 6px 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-metadata-record-cell-empty:empty::before {
|
||||||
|
content: attr(placeholder);
|
||||||
|
color: rgba(255, 255, 255, .7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sf-metadata-geolocation-property-detail-editor-popover .popover.show {
|
||||||
|
width: 500px;
|
||||||
|
max-width: 500px;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
transform: translateX(-140px);
|
||||||
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { Modal, Popover } from 'reactstrap';
|
||||||
import { initMapInfo, loadMapSource } from '../../../../utils/map-utils';
|
import { initMapInfo, loadMapSource } from '../../../../utils/map-utils';
|
||||||
import { wgs84_to_gcj02, gcj02_to_bd09 } from '../../../../utils/coord-transform';
|
|
||||||
import { MAP_TYPE } from '../../../../constants';
|
import { MAP_TYPE } from '../../../../constants';
|
||||||
import Loading from '../../../../components/loading';
|
import Loading from '../../../../components/loading';
|
||||||
import { gettext, baiduMapKey, googleMapKey, googleMapId } from '../../../../utils/constants';
|
import { gettext, baiduMapKey, googleMapKey, googleMapId } from '../../../../utils/constants';
|
||||||
@@ -15,6 +15,8 @@ import { createBMapZoomControl } from '../../map-controller';
|
|||||||
import { Utils } from '../../../../utils/utils';
|
import { Utils } from '../../../../utils/utils';
|
||||||
import { eventBus } from '../../../../components/common/event-bus';
|
import { eventBus } from '../../../../components/common/event-bus';
|
||||||
import { createZoomControl } from '../../map-controller/zoom';
|
import { createZoomControl } from '../../map-controller/zoom';
|
||||||
|
import ClickOutside from '../../../../components/click-outside';
|
||||||
|
import GeolocationEditor from '../../cell-editors/geolocation-editor';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
@@ -23,6 +25,7 @@ class Location extends React.Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
position: PropTypes.object,
|
position: PropTypes.object,
|
||||||
record: PropTypes.object,
|
record: PropTypes.object,
|
||||||
|
onChange: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -31,9 +34,14 @@ class Location extends React.Component {
|
|||||||
this.mapType = type;
|
this.mapType = type;
|
||||||
this.mapKey = key;
|
this.mapKey = key;
|
||||||
this.map = null;
|
this.map = null;
|
||||||
|
this.marker = null;
|
||||||
this.state = {
|
this.state = {
|
||||||
address: '',
|
latLng: this.props.position,
|
||||||
|
address: this.props.record?._location_translated?.address || '',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
isEditorShown: false,
|
||||||
|
isFullScreen: false,
|
||||||
|
isReadyToEraseLocation: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,109 +56,96 @@ class Location extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
const { position, record } = this.props;
|
const { latLng } = this.state;
|
||||||
if (!isValidPosition(position?.lng, position?.lat) || typeof record !== 'object') return;
|
if (prevProps.record._id !== this.props.record._id) {
|
||||||
if (prevProps.position?.lng === position?.lng && prevProps.position?.lat === position?.lat) return;
|
this.setState({
|
||||||
let transformedPos = wgs84_to_gcj02(position.lng, position.lat);
|
latLng: this.props.position,
|
||||||
if (this.mapType === MAP_TYPE.B_MAP) {
|
address: this.props.record?._location_translated?.address || '',
|
||||||
transformedPos = gcj02_to_bd09(transformedPos.lng, transformedPos.lat);
|
isReadyToEraseLocation: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.addMarkerByPosition(transformedPos.lng, transformedPos.lat);
|
if (!this.map) return;
|
||||||
|
if (!isValidPosition(latLng?.lng, latLng?.lat)) return;
|
||||||
|
|
||||||
this.setState({ address: record._location_translated?.address });
|
if (prevState.latLng?.lat !== latLng.lat || prevState.latLng?.lng !== latLng.lng) {
|
||||||
|
if (this.mapType === MAP_TYPE.B_MAP) {
|
||||||
|
this.marker.setPosition(latLng);
|
||||||
|
this.map.panTo(latLng);
|
||||||
|
} else if (this.mapType === MAP_TYPE.G_MAP) {
|
||||||
|
this.marker.position = latLng;
|
||||||
|
this.map.panTo(latLng);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.unsubscribeClearMapInstance();
|
this.unsubscribeClearMapInstance();
|
||||||
|
this.map = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
initMap = () => {
|
initMap = () => {
|
||||||
if (this.map) return;
|
const { record } = this.props;
|
||||||
|
const { latLng } = this.state;
|
||||||
|
if (!isValidPosition(latLng?.lng, latLng?.lat) || typeof record !== 'object') return;
|
||||||
|
|
||||||
const { position, record } = this.props;
|
this.setState({ isLoading: true }, () => {
|
||||||
if (!isValidPosition(position?.lng, position?.lat) || typeof record !== 'object') return;
|
if (this.mapType === MAP_TYPE.B_MAP) {
|
||||||
|
if (!window.BMapGL) {
|
||||||
this.setState({ isLoading: true, address: record._location_translated?.address });
|
window.renderBaiduMap = () => this.renderBaiduMap();
|
||||||
if (this.mapType === MAP_TYPE.B_MAP) {
|
loadMapSource(this.mapType, this.mapKey);
|
||||||
if (!window.BMapGL) {
|
} else {
|
||||||
window.renderBaiduMap = () => this.renderBaiduMap(position);
|
this.renderBaiduMap();
|
||||||
loadMapSource(this.mapType, this.mapKey);
|
}
|
||||||
} else {
|
} else if (this.mapType === MAP_TYPE.G_MAP) {
|
||||||
this.renderBaiduMap(position);
|
if (!window.google?.maps.Map) {
|
||||||
|
window.renderGoogleMap = () => this.renderGoogleMap();
|
||||||
|
loadMapSource(this.mapType, this.mapKey);
|
||||||
|
} else {
|
||||||
|
this.renderGoogleMap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
addMarker = () => {
|
||||||
|
const { latLng } = this.state;
|
||||||
|
if (this.mapType === MAP_TYPE.B_MAP) {
|
||||||
|
this.marker = new window.BMapGL.Marker(latLng);
|
||||||
|
this.map.addOverlay(this.marker);
|
||||||
} else if (this.mapType === MAP_TYPE.G_MAP) {
|
} else if (this.mapType === MAP_TYPE.G_MAP) {
|
||||||
if (!window.google?.maps.Map) {
|
this.marker = new window.google.maps.marker.AdvancedMarkerElement({
|
||||||
window.renderGoogleMap = () => this.renderGoogleMap(position);
|
position: latLng,
|
||||||
loadMapSource(this.mapType, this.mapKey);
|
map: this.map,
|
||||||
} else {
|
});
|
||||||
this.renderGoogleMap(position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
addMarkerByPosition = (lng, lat) => {
|
renderBaiduMap = () => {
|
||||||
if (!this.map) {
|
|
||||||
this.initMap(this.props.position);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.mapType === MAP_TYPE.B_MAP) {
|
|
||||||
if (this.lastLng === lng && this.lastLat === lat) return;
|
|
||||||
this.lastLng = lng;
|
|
||||||
this.lastLat = lat;
|
|
||||||
|
|
||||||
const point = new window.BMapGL.Point(lng, lat);
|
|
||||||
const marker = new window.BMapGL.Marker(point, { offset: new window.BMapGL.Size(-2, -5) });
|
|
||||||
this.map.clearOverlays();
|
|
||||||
this.map.addOverlay(marker);
|
|
||||||
this.map.setCenter(point);
|
|
||||||
}
|
|
||||||
if (this.mapType === MAP_TYPE.G_MAP) {
|
|
||||||
if (!this.googleMarker) {
|
|
||||||
this.googleMarker = new window.google.maps.marker.AdvancedMarkerElement({
|
|
||||||
position: { lat, lng },
|
|
||||||
map: this.map,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.googleMarker.position = { lat, lng };
|
|
||||||
this.map.setCenter({ lat, lng });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
renderBaiduMap = (position = {}) => {
|
|
||||||
this.setState({ isLoading: false }, () => {
|
this.setState({ isLoading: false }, () => {
|
||||||
if (!window.BMapGL.Map) return;
|
if (!window.BMapGL.Map) return;
|
||||||
|
|
||||||
window.mapInstance = new window.BMapGL.Map('sf-geolocation-map-container', { enableMapClick: false });
|
const { latLng } = this.state;
|
||||||
this.map = window.mapInstance;
|
this.map = new window.BMapGL.Map('sf-geolocation-map-container');
|
||||||
|
|
||||||
const gcPosition = wgs84_to_gcj02(position.lng, position.lat);
|
|
||||||
const bdPosition = gcj02_to_bd09(gcPosition.lng, gcPosition.lat);
|
|
||||||
const { lng, lat } = bdPosition;
|
|
||||||
const point = new window.BMapGL.Point(lng, lat);
|
|
||||||
this.map.centerAndZoom(point, 16);
|
|
||||||
this.map.disableScrollWheelZoom(true);
|
this.map.disableScrollWheelZoom(true);
|
||||||
|
this.map.centerAndZoom(latLng, 16);
|
||||||
|
|
||||||
const offset = { x: 10, y: Utils.isDesktop() ? 16 : 40 };
|
const offset = { x: 10, y: Utils.isDesktop() ? 16 : 40 };
|
||||||
const ZoomControl = createBMapZoomControl(window.BMapGL, { maxZoom: 21, minZoom: 3, offset });
|
const ZoomControl = createBMapZoomControl(window.BMapGL, { maxZoom: 21, minZoom: 3, offset });
|
||||||
const zoomControl = new ZoomControl();
|
const zoomControl = new ZoomControl();
|
||||||
this.map.addControl(zoomControl);
|
this.map.addControl(zoomControl);
|
||||||
this.addMarkerByPosition(lng, lat);
|
this.addMarker();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderGoogleMap = (position) => {
|
renderGoogleMap = () => {
|
||||||
|
const { latLng } = this.state;
|
||||||
this.setState({ isLoading: false }, () => {
|
this.setState({ isLoading: false }, () => {
|
||||||
if (!window.google.maps.Map) return;
|
if (!window.google.maps.Map) return;
|
||||||
|
|
||||||
const gcPosition = wgs84_to_gcj02(position.lng, position.lat);
|
this.map = new window.google.maps.Map(this.ref, {
|
||||||
const { lng, lat } = gcPosition || {};
|
|
||||||
window.mapInstance = new window.google.maps.Map(this.ref, {
|
|
||||||
zoom: 16,
|
zoom: 16,
|
||||||
center: gcPosition,
|
center: latLng,
|
||||||
mapId: googleMapId,
|
mapId: googleMapId,
|
||||||
zoomControl: false,
|
zoomControl: false,
|
||||||
mapTypeControl: false,
|
mapTypeControl: false,
|
||||||
@@ -161,39 +156,77 @@ class Location extends React.Component {
|
|||||||
disableDefaultUI: true,
|
disableDefaultUI: true,
|
||||||
gestureHandling: 'cooperative',
|
gestureHandling: 'cooperative',
|
||||||
});
|
});
|
||||||
this.map = window.mapInstance;
|
|
||||||
|
|
||||||
this.addMarkerByPosition(lng, lat);
|
this.addMarker();
|
||||||
const zoomControl = createZoomControl(this.map);
|
const zoomControl = createZoomControl({ map: this.map });
|
||||||
this.map.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(zoomControl);
|
this.map.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(zoomControl);
|
||||||
this.map.setCenter(gcPosition);
|
this.map.panTo(latLng);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
openEditor = () => {
|
||||||
|
this.setState({ isEditorShown: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
onFullScreen = () => {
|
||||||
|
this.setState({ isFullScreen: !this.state.isFullScreen });
|
||||||
|
};
|
||||||
|
|
||||||
|
closeEditor = () => {
|
||||||
|
this.setState({ isEditorShown: false });
|
||||||
|
if (this.state.isReadyToEraseLocation) {
|
||||||
|
this.props.onChange(PRIVATE_COLUMN_KEY.LOCATION_TRANSLATED, null);
|
||||||
|
this.props.onChange(PRIVATE_COLUMN_KEY.LOCATION, null);
|
||||||
|
this.setState({ latLng: null, address: '', isReadyToEraseLocation: false });
|
||||||
|
this.mapType === MAP_TYPE.B_MAP && this.map.destroy();
|
||||||
|
this.map = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onSubmit = (value) => {
|
||||||
|
const { position, location_translated } = value;
|
||||||
|
this.props.onChange(PRIVATE_COLUMN_KEY.LOCATION_TRANSLATED, location_translated);
|
||||||
|
this.props.onChange(PRIVATE_COLUMN_KEY.LOCATION, position);
|
||||||
|
this.setState({
|
||||||
|
latLng: position,
|
||||||
|
address: location_translated?.address,
|
||||||
|
isEditorShown: false,
|
||||||
|
isFullScreen: false,
|
||||||
|
}, () => {
|
||||||
|
if (!this.map) {
|
||||||
|
this.initMap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onReadyToEraseLocation = () => {
|
||||||
|
this.setState({ isReadyToEraseLocation: true });
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isLoading, address } = this.state;
|
const { isLoading, latLng, address, isEditorShown, isFullScreen } = this.state;
|
||||||
const { position } = this.props;
|
const isValid = isValidPosition(latLng?.lng, latLng?.lat);
|
||||||
const isValid = isValidPosition(position?.lng, position?.lat);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DetailItem
|
<DetailItem
|
||||||
|
id="location-item"
|
||||||
field={{
|
field={{
|
||||||
key: PRIVATE_COLUMN_KEY.LOCATION,
|
key: PRIVATE_COLUMN_KEY.LOCATION,
|
||||||
type: CellType.GEOLOCATION,
|
type: CellType.GEOLOCATION,
|
||||||
name: getColumnDisplayName(PRIVATE_COLUMN_KEY.LOCATION)
|
name: getColumnDisplayName(PRIVATE_COLUMN_KEY.LOCATION)
|
||||||
}}
|
}}
|
||||||
readonly={true}
|
readonly={false}
|
||||||
>
|
>
|
||||||
{isValid ? (
|
{isValid ? (
|
||||||
<div className="sf-metadata-ui cell-formatter-container geolocation-formatter sf-metadata-geolocation-formatter">
|
<div ref={ref => this.editorRef = ref} className="sf-metadata-ui cell-formatter-container geolocation-formatter sf-metadata-geolocation-formatter w-100 cursor-pointer" onClick={this.openEditor}>
|
||||||
{!isLoading && this.mapType && address ? (
|
{!isLoading && this.mapType && address ? (
|
||||||
<span>{address}</span>
|
<span>{address}</span>
|
||||||
) : (
|
) : (
|
||||||
<span>{getGeolocationDisplayString(position, { geo_format: GEOLOCATION_FORMAT.LNG_LAT })}</span>
|
<span>{getGeolocationDisplayString(latLng, { geo_format: GEOLOCATION_FORMAT.LNG_LAT })}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="sf-metadata-record-cell-empty" placeholder={gettext('Empty')}></div>
|
<div ref={ref => this.editorRef = ref} className="sf-metadata-property-detail-editor sf-metadata-record-cell-empty cursor-pointer" placeholder={gettext('Empty')} onClick={this.openEditor}></div>
|
||||||
)}
|
)}
|
||||||
</DetailItem>
|
</DetailItem>
|
||||||
{isLoading ? (<Loading />) : this.mapType && (
|
{isLoading ? (<Loading />) : this.mapType && (
|
||||||
@@ -201,6 +234,37 @@ class Location extends React.Component {
|
|||||||
<div className="w-100 h-100" ref={ref => this.ref = ref} id="sf-geolocation-map-container"></div>
|
<div className="w-100 h-100" ref={ref => this.ref = ref} id="sf-geolocation-map-container"></div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isEditorShown && (
|
||||||
|
!isFullScreen ? (
|
||||||
|
<ClickOutside onClickOutside={this.closeEditor}>
|
||||||
|
<Popover
|
||||||
|
target="location-item"
|
||||||
|
isOpen={true}
|
||||||
|
hideArrow={true}
|
||||||
|
fade={false}
|
||||||
|
placement="left"
|
||||||
|
className="sf-metadata-property-detail-editor-popover sf-metadata-geolocation-property-detail-editor-popover"
|
||||||
|
boundariesElement="viewport"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
border: 'none',
|
||||||
|
background: 'transparent',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GeolocationEditor position={latLng} onSubmit={this.onSubmit} onFullScreen={this.onFullScreen} onReadyToEraseLocation={this.onReadyToEraseLocation} />
|
||||||
|
</Popover>
|
||||||
|
</ClickOutside>
|
||||||
|
) : (
|
||||||
|
<Modal
|
||||||
|
size='lg'
|
||||||
|
isOpen={true}
|
||||||
|
toggle={this.onFullScreen}
|
||||||
|
zIndex={1052}
|
||||||
|
>
|
||||||
|
<GeolocationEditor position={latLng} isFullScreen={isFullScreen} onSubmit={this.onSubmit} onFullScreen={this.onFullScreen} onReadyToEraseLocation={this.onReadyToEraseLocation} />
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -93,6 +93,8 @@ export const EDITABLE_PRIVATE_COLUMN_KEYS = [
|
|||||||
PRIVATE_COLUMN_KEY.OWNER,
|
PRIVATE_COLUMN_KEY.OWNER,
|
||||||
PRIVATE_COLUMN_KEY.FILE_RATE,
|
PRIVATE_COLUMN_KEY.FILE_RATE,
|
||||||
PRIVATE_COLUMN_KEY.TAGS,
|
PRIVATE_COLUMN_KEY.TAGS,
|
||||||
|
PRIVATE_COLUMN_KEY.LOCATION,
|
||||||
|
PRIVATE_COLUMN_KEY.LOCATION_TRANSLATED,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const EDITABLE_DATA_PRIVATE_COLUMN_KEYS = [
|
export const EDITABLE_DATA_PRIVATE_COLUMN_KEYS = [
|
||||||
|
@@ -76,13 +76,13 @@ export const createBaiduMap = ({ type, center, zoom, onMapState }) => {
|
|||||||
// add controls
|
// add controls
|
||||||
const ZoomControl = createBMapZoomControl({
|
const ZoomControl = createBMapZoomControl({
|
||||||
anchor: window.BMAP_ANCHOR_BOTTOM_RIGHT,
|
anchor: window.BMAP_ANCHOR_BOTTOM_RIGHT,
|
||||||
offset: new window.BMapGL.Size(66, Utils.isDesktop() ? 30 : 90),
|
offset: { x: 66, y: Utils.isDesktop() ? 30 : 90 },
|
||||||
});
|
});
|
||||||
const zoomControl = new ZoomControl();
|
const zoomControl = new ZoomControl();
|
||||||
|
|
||||||
const GeolocationControl = createBMapGeolocationControl({
|
const GeolocationControl = createBMapGeolocationControl({
|
||||||
anchor: window.BMAP_ANCHOR_BOTTOM_RIGHT,
|
anchor: window.BMAP_ANCHOR_BOTTOM_RIGHT,
|
||||||
offset: new window.BMapGL.Size(30, Utils.isDesktop() ? 30 : 90),
|
offset: { x: 30, y: Utils.isDesktop() ? 30 : 90 },
|
||||||
callback: (point) => {
|
callback: (point) => {
|
||||||
const gcPosition = wgs84_to_gcj02(point.lng, point.lat);
|
const gcPosition = wgs84_to_gcj02(point.lng, point.lat);
|
||||||
const bdPosition = gcj02_to_bd09(gcPosition.lng, gcPosition.lat);
|
const bdPosition = gcj02_to_bd09(gcPosition.lng, gcPosition.lat);
|
||||||
|
@@ -82,8 +82,8 @@ export const createGoogleMap = ({ center, zoom, onMapState }) => {
|
|||||||
maxZoom: MAX_ZOOM,
|
maxZoom: MAX_ZOOM,
|
||||||
});
|
});
|
||||||
|
|
||||||
const zoomControl = createZoomControl(map);
|
const zoomControl = createZoomControl({ map });
|
||||||
const geolocationControl = createGeolocationControl(map);
|
const geolocationControl = createGeolocationControl({ map });
|
||||||
|
|
||||||
map.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(zoomControl);
|
map.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(zoomControl);
|
||||||
map.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(geolocationControl);
|
map.controls[window.google.maps.ControlPosition.RIGHT_BOTTOM].push(geolocationControl);
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { MAP_TYPE } from '../constants';
|
import { MAP_TYPE } from '../constants';
|
||||||
import { mediaUrl } from './constants';
|
import { lang, mediaUrl } from './constants';
|
||||||
|
|
||||||
export const initMapInfo = ({ baiduMapKey, googleMapKey, mineMapKey }) => {
|
export const initMapInfo = ({ baiduMapKey, googleMapKey, mineMapKey }) => {
|
||||||
if (baiduMapKey) return { type: MAP_TYPE.B_MAP, key: baiduMapKey };
|
if (baiduMapKey) return { type: MAP_TYPE.B_MAP, key: baiduMapKey };
|
||||||
@@ -16,7 +16,7 @@ export const loadMapSource = (type, key, callback) => {
|
|||||||
if (type === MAP_TYPE.B_MAP) {
|
if (type === MAP_TYPE.B_MAP) {
|
||||||
scriptUrl = `https://api.map.baidu.com/api?type=webgl&v=3.0&ak=${key}&callback=renderBaiduMap`;
|
scriptUrl = `https://api.map.baidu.com/api?type=webgl&v=3.0&ak=${key}&callback=renderBaiduMap`;
|
||||||
} else if (type === MAP_TYPE.G_MAP) {
|
} else if (type === MAP_TYPE.G_MAP) {
|
||||||
scriptUrl = `https://maps.googleapis.com/maps/api/js?key=${key}&libraries=marker,geometry&v=weekly&callback=renderGoogleMap`;
|
scriptUrl = `https://maps.googleapis.com/maps/api/js?key=${key}&language=${lang}&libraries=marker,geometry,core,places&v=weekly&callback=renderGoogleMap`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scriptUrl) {
|
if (scriptUrl) {
|
||||||
|
@@ -101,7 +101,6 @@ def get_unmodifiable_columns():
|
|||||||
METADATA_TABLE.columns.file_name.to_dict(),
|
METADATA_TABLE.columns.file_name.to_dict(),
|
||||||
METADATA_TABLE.columns.is_dir.to_dict(),
|
METADATA_TABLE.columns.is_dir.to_dict(),
|
||||||
METADATA_TABLE.columns.file_type.to_dict(),
|
METADATA_TABLE.columns.file_type.to_dict(),
|
||||||
METADATA_TABLE.columns.location.to_dict(),
|
|
||||||
METADATA_TABLE.columns.obj_id.to_dict(),
|
METADATA_TABLE.columns.obj_id.to_dict(),
|
||||||
METADATA_TABLE.columns.size.to_dict(),
|
METADATA_TABLE.columns.size.to_dict(),
|
||||||
METADATA_TABLE.columns.suffix.to_dict(),
|
METADATA_TABLE.columns.suffix.to_dict(),
|
||||||
|
Reference in New Issue
Block a user