mirror of
https://github.com/muerwre/orchidmap-front.git
synced 2025-04-24 18:46:40 +07:00
refactored rerendering speed
This commit is contained in:
parent
b6bf317649
commit
69d1d749cf
32 changed files with 144 additions and 2045 deletions
38
package-lock.json
generated
38
package-lock.json
generated
|
@ -778,17 +778,20 @@
|
|||
"@types/classnames": {
|
||||
"version": "2.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.7.tgz",
|
||||
"integrity": "sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg=="
|
||||
"integrity": "sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/geojson": {
|
||||
"version": "7946.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.6.tgz",
|
||||
"integrity": "sha512-f6qai3iR62QuMPPdgyH+LyiXTL2n9Rf62UniJjV7KHrbiwzLTZUKsdq0mFSTxAHbO7JvwxwC4tH0m1UnweuLrA=="
|
||||
"integrity": "sha512-f6qai3iR62QuMPPdgyH+LyiXTL2n9Rf62UniJjV7KHrbiwzLTZUKsdq0mFSTxAHbO7JvwxwC4tH0m1UnweuLrA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/leaflet": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.4.3.tgz",
|
||||
"integrity": "sha512-jFRBSsPHi1EwQSwrN0cOJLdPhwOZsRl4IMxvm/2ShLh0YM5GfCtQXCzsrv8RE7DWL+AykXdYSAd9bFLWbZT4CQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/geojson": "7946.0.6"
|
||||
}
|
||||
|
@ -796,22 +799,34 @@
|
|||
"@types/node": {
|
||||
"version": "11.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.0.tgz",
|
||||
"integrity": "sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg=="
|
||||
"integrity": "sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/prop-types": {
|
||||
"version": "15.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz",
|
||||
"integrity": "sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw=="
|
||||
"integrity": "sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/q": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz",
|
||||
"integrity": "sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA=="
|
||||
},
|
||||
"@types/ramda": {
|
||||
"version": "0.26.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.26.39.tgz",
|
||||
"integrity": "sha512-3bu32X02VpjJhsYPUWkdOQGoBXjb/UveZgGg4IYMm+SPAXio96BOYrRhVELfM4AoP00sxoi/f2tqrXdwtR4jjg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ts-toolbelt": "4.14.6"
|
||||
}
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.1.tgz",
|
||||
"integrity": "sha512-tD1ETKJcuhANOejRc/p7OgQ16DKnbGi0M3LccelKlPnUCDp2a5koVxZFoRN9HN+A+m84HB5VGN7I+r3nNhS3PA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/prop-types": "15.5.8",
|
||||
"csstype": "2.6.2"
|
||||
|
@ -4464,7 +4479,8 @@
|
|||
"csstype": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.2.tgz",
|
||||
"integrity": "sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow=="
|
||||
"integrity": "sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow==",
|
||||
"dev": true
|
||||
},
|
||||
"currently-unhandled": {
|
||||
"version": "0.4.1",
|
||||
|
@ -13015,6 +13031,12 @@
|
|||
"yn": "3.0.0"
|
||||
}
|
||||
},
|
||||
"ts-toolbelt": {
|
||||
"version": "4.14.6",
|
||||
"resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-4.14.6.tgz",
|
||||
"integrity": "sha512-SONcnRd93+LuYGfn/CZg5A5qhCODohZslAVZKHHu5bnwUxoXLqd2k2VIdwRUXYfKnY+UCeNbI2pTPz+Dno6Mpg==",
|
||||
"dev": true
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
|
||||
|
@ -13085,9 +13107,9 @@
|
|||
"integrity": "sha512-NLpRc/FY+jPfWL0aUXQzjxPyF0Xug2om6akaoRLQ18KGwP2yYNBJu9vkv2q1q+Cx/+edy2Qf6O8xXnYY/xwz1A=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3.tgz",
|
||||
"integrity": "sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A==",
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
|
||||
"integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
|
||||
"dev": true
|
||||
},
|
||||
"uglifyjs-webpack-plugin": {
|
||||
|
|
11
package.json
11
package.json
|
@ -15,6 +15,11 @@
|
|||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0-rc.1",
|
||||
"@babel/preset-env": "^7.0.0-rc.1",
|
||||
"@types/ramda": "^0.26.39",
|
||||
"@types/classnames": "^2.2.7",
|
||||
"@types/leaflet": "^1.4.3",
|
||||
"@types/node": "^11.9.0",
|
||||
"@types/react": "16.8.1",
|
||||
"awesome-typescript-loader": "^5.2.1",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-eslint": "^8.2.3",
|
||||
|
@ -43,7 +48,7 @@
|
|||
"style-loader": "^0.21.0",
|
||||
"sw-precache-webpack-plugin": "^0.11.5",
|
||||
"ts-node": "^8.0.1",
|
||||
"typescript": "^3.2.4",
|
||||
"typescript": "^3.7.4",
|
||||
"uglifyjs-webpack-plugin": "^1.3.0",
|
||||
"webpack": "^4.6.0",
|
||||
"webpack-cli": "^3.2.3",
|
||||
|
@ -52,10 +57,6 @@
|
|||
"webpack-pwa-manifest": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/classnames": "^2.2.7",
|
||||
"@types/leaflet": "^1.4.3",
|
||||
"@types/node": "^11.9.0",
|
||||
"@types/react": "16.8.1",
|
||||
"axios": "^0.18.0",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"body-parser": "^1.18.3",
|
||||
|
|
|
@ -1,462 +0,0 @@
|
|||
import { Map } from '~/modules/Map';
|
||||
import { Poly } from '~/modules/Poly';
|
||||
import { MODES } from '~/constants/modes';
|
||||
import { ILatLng, Stickers } from '~/modules/Stickers';
|
||||
import { Router } from '~/modules/Router';
|
||||
import { DEFAULT_LOGO, ILogos, LOGOS } from '~/constants/logos';
|
||||
|
||||
import { getUrlData } from '~/utils/history';
|
||||
import { store } from '~/redux/store';
|
||||
import {
|
||||
resetSaveDialog,
|
||||
setActiveSticker,
|
||||
setChanged,
|
||||
setDistance,
|
||||
setIsEmpty, setIsRouting,
|
||||
setMarkersShown,
|
||||
setMode,
|
||||
setRouterPoints, setStarred,
|
||||
} from '~/redux/user/actions';
|
||||
import {
|
||||
mapSetAddress,
|
||||
mapSetDescription,
|
||||
mapSetLogo,
|
||||
mapSetPublic,
|
||||
mapSetTitle,
|
||||
mapSetProvider,
|
||||
} from '~/redux/map/actions';
|
||||
import { DEFAULT_PROVIDER, IProvider, PROVIDERS } from '~/constants/providers';
|
||||
import { STICKERS } from '~/constants/stickers';
|
||||
import { IRootState } from "~/redux/user";
|
||||
import { DEFAULT_USER, IUser } from "~/constants/auth";
|
||||
|
||||
interface IEditor {
|
||||
map: Map;
|
||||
poly: Poly;
|
||||
stickers: Stickers;
|
||||
router: Router;
|
||||
|
||||
logo: keyof ILogos;
|
||||
owner: number;
|
||||
initialData: {
|
||||
version: number,
|
||||
title: IRootState['title'],
|
||||
owner: number,
|
||||
address: IRootState['address'],
|
||||
path: any,
|
||||
route: any,
|
||||
stickers: any,
|
||||
provider: IRootState['provider'],
|
||||
is_public: IRootState['is_public'],
|
||||
is_published: IRootState['is_published'],
|
||||
description: IRootState['description'],
|
||||
logo: IRootState['logo'],
|
||||
};
|
||||
activeSticker: IRootState['activeSticker'];
|
||||
mode: IRootState['mode'];
|
||||
provider: IProvider;
|
||||
switches: {
|
||||
[x: string]: {
|
||||
start?: () => any,
|
||||
stop?: () => any,
|
||||
toggle?: () => any,
|
||||
}
|
||||
};
|
||||
clickHandlers: {
|
||||
[x: string]: (event) => void
|
||||
};
|
||||
user: IUser;
|
||||
}
|
||||
|
||||
export class Editor {
|
||||
constructor() {
|
||||
this.logo = DEFAULT_LOGO;
|
||||
this.owner = null;
|
||||
this.map = new Map({ container: 'map' });
|
||||
this.activeSticker = {};
|
||||
this.mode = MODES.NONE;
|
||||
this.provider = PROVIDERS[DEFAULT_PROVIDER];
|
||||
|
||||
const {
|
||||
triggerOnChange, lockMapClicks, routerMoveStart, pushPolyPoints,
|
||||
map: { map }
|
||||
} = this;
|
||||
|
||||
this.poly = new Poly({
|
||||
map, routerMoveStart, lockMapClicks, setDistance: this.setDistance, triggerOnChange, editor: this,
|
||||
});
|
||||
|
||||
this.stickers = new Stickers({
|
||||
map,
|
||||
lockMapClicks,
|
||||
triggerOnChange,
|
||||
editor: this
|
||||
});
|
||||
|
||||
this.router = new Router({
|
||||
map,
|
||||
lockMapClicks,
|
||||
pushPolyPoints,
|
||||
setRouterPoints: this.setRouterPoints,
|
||||
setIsRouting: this.setIsRouting,
|
||||
});
|
||||
|
||||
this.switches = {
|
||||
[MODES.POLY]: {
|
||||
start: this.startPoly,
|
||||
stop: this.poly.stop,
|
||||
toggle: this.clearMode,
|
||||
},
|
||||
[MODES.ROUTER]: {
|
||||
toggle: this.clearMode,
|
||||
start: this.routerSetStart,
|
||||
},
|
||||
[MODES.STICKERS]: {
|
||||
toggle: this.clearSticker,
|
||||
},
|
||||
[MODES.STICKERS_SELECT]: {
|
||||
toggle: this.clearSticker,
|
||||
},
|
||||
[MODES.TRASH]: {
|
||||
toggle: this.clearMode,
|
||||
},
|
||||
[MODES.CONFIRM_CANCEL]: {
|
||||
toggle: this.cancelEditing,
|
||||
},
|
||||
[MODES.LOGO]: {
|
||||
toggle: this.clearMode,
|
||||
},
|
||||
[MODES.SAVE]: {
|
||||
toggle: this.clearMode,
|
||||
stop: this.resetSaveDialog,
|
||||
},
|
||||
[MODES.PROVIDER]: {
|
||||
toggle: this.clearMode,
|
||||
}
|
||||
};
|
||||
|
||||
this.clickHandlers = {
|
||||
[MODES.STICKERS]: this.createStickerOnClick,
|
||||
[MODES.ROUTER]: this.router.pushWaypointOnClick,
|
||||
};
|
||||
|
||||
map.addEventListener('mouseup', this.onClick);
|
||||
// map.addEventListener('touchend', this.onClick);
|
||||
map.addEventListener('dragstart', () => lockMapClicks(true));
|
||||
map.addEventListener('dragstop', () => lockMapClicks(false));
|
||||
}
|
||||
|
||||
map: IEditor['map'];
|
||||
poly: IEditor['poly'];
|
||||
stickers: IEditor['stickers'];
|
||||
router: IEditor['router'];
|
||||
|
||||
logo: IEditor['logo'] = DEFAULT_LOGO;
|
||||
owner: IEditor['owner'] = null;
|
||||
initialData: IEditor['initialData'] = {
|
||||
version: null,
|
||||
title: null,
|
||||
owner: null,
|
||||
address: null,
|
||||
path: null,
|
||||
route: null,
|
||||
stickers: null,
|
||||
provider: null,
|
||||
is_public: false,
|
||||
is_published: false,
|
||||
description: '',
|
||||
logo: null,
|
||||
};
|
||||
activeSticker: IEditor['activeSticker'];
|
||||
mode: IEditor['mode'];
|
||||
provider: IEditor['provider'];
|
||||
switches: IEditor['switches'];
|
||||
clickHandlers: IEditor['clickHandlers'];
|
||||
user: IEditor['user'] = DEFAULT_USER;
|
||||
|
||||
getState = (): IRootState => <any>store.getState().user;
|
||||
|
||||
getUser = () => this.getState().user;
|
||||
getMode = () => this.getState().mode;
|
||||
getProvider = () => this.getState().provider;
|
||||
getTitle = () => this.getState().title;
|
||||
getEditing = () => this.getState().editing;
|
||||
getChanged = () => this.getState().changed;
|
||||
getRouterPoints = () => this.getState().routerPoints;
|
||||
getDistance = () => this.getState().distance;
|
||||
getIsEmpty = () => this.getState().is_empty;
|
||||
|
||||
mapSetLogo: typeof mapSetLogo = logo => store.dispatch(mapSetLogo(logo));
|
||||
setMode: typeof setMode = value => store.dispatch(setMode(value));
|
||||
setRouterPoints: typeof setRouterPoints = value => store.dispatch(setRouterPoints(value));
|
||||
setActiveSticker: typeof setActiveSticker = value => store.dispatch(setActiveSticker(value));
|
||||
mapSetTitle: typeof mapSetTitle = value => store.dispatch(mapSetTitle(value));
|
||||
mapSetDescription: typeof mapSetDescription = value => store.dispatch(mapSetDescription(value));
|
||||
mapSetAddress: typeof mapSetAddress = value => store.dispatch(mapSetAddress(value));
|
||||
mapSetPublic: typeof mapSetPublic = value => store.dispatch(mapSetPublic(value));
|
||||
setStarred: typeof setStarred = value => store.dispatch(setStarred(value));
|
||||
setIsEmpty: typeof setIsEmpty = value => store.dispatch(setIsEmpty(value));
|
||||
setIsRouting: typeof setIsRouting = value => store.dispatch(setIsRouting(value));
|
||||
|
||||
setMarkersShown = (value: boolean): void => {
|
||||
if (this.getState().markers_shown !== value) store.dispatch(setMarkersShown(value));
|
||||
};
|
||||
|
||||
resetSaveDialog = (): void => { store.dispatch(resetSaveDialog()); };
|
||||
|
||||
setDistance = (value: number): void => {
|
||||
if (this.getDistance() !== value) store.dispatch(setDistance(value));
|
||||
};
|
||||
|
||||
setChanged = (value: boolean): void => {
|
||||
if (this.getChanged() !== value) store.dispatch(setChanged(value));
|
||||
};
|
||||
|
||||
clearMode = (): void => { this.setMode(MODES.NONE); };
|
||||
clearChanged = (): void => { store.dispatch(setChanged(false)); };
|
||||
|
||||
startPoly = (): void => {
|
||||
if (this.getRouterPoints()) this.router.clearAll();
|
||||
this.poly.continue();
|
||||
};
|
||||
|
||||
triggerOnChange = (): void => {
|
||||
if (this.isEmpty !== this.getIsEmpty()) this.setIsEmpty(this.isEmpty);
|
||||
if (this.getEditing() && !this.getChanged()) this.setChanged(true);
|
||||
};
|
||||
|
||||
createStickerOnClick = (e): void => {
|
||||
if (!e || !e.latlng || !this.activeSticker) return;
|
||||
const { latlng }: { latlng: ILatLng } = e;
|
||||
|
||||
this.stickers.createSticker({ latlng, sticker: this.activeSticker.sticker, set: this.activeSticker.set });
|
||||
this.setActiveSticker(null);
|
||||
this.setChanged(true);
|
||||
// this.setMode(MODES.STICKERS_SELECT);
|
||||
};
|
||||
|
||||
changeMode = (mode: IRootState['mode']): void => {
|
||||
if (this.mode === mode) {
|
||||
if (this.switches[mode] && this.switches[mode].toggle) {
|
||||
// if we have special function on mode when it clicked again
|
||||
this.switches[mode].toggle();
|
||||
} else {
|
||||
this.disableMode(mode);
|
||||
// this.setMode(MODES.NONE);
|
||||
this.mode = MODES.NONE;
|
||||
}
|
||||
} else {
|
||||
this.disableMode(this.mode);
|
||||
// this.setMode(mode);
|
||||
this.mode = mode;
|
||||
this.enableMode(mode);
|
||||
}
|
||||
};
|
||||
|
||||
enableMode = (mode: IRootState['mode']): void => {
|
||||
if (this.switches[mode] && this.switches[mode].start) this.switches[mode].start();
|
||||
};
|
||||
|
||||
disableMode = (mode: IRootState['mode']): void => {
|
||||
if (this.switches[mode] && this.switches[mode].stop) this.switches[mode].stop();
|
||||
};
|
||||
|
||||
onClick = (e): void => {
|
||||
if (e.originalEvent.which === 3) return; // skip right / middle click
|
||||
if (this.clickHandlers[this.mode]) this.clickHandlers[this.mode](e);
|
||||
};
|
||||
|
||||
lockMapClicks = (lock: boolean): void => {
|
||||
if (lock) {
|
||||
this.map.map.removeEventListener('mouseup', this.onClick);
|
||||
this.map.map.addEventListener('mouseup', this.unlockMapClicks);
|
||||
} else {
|
||||
this.map.map.removeEventListener('mouseup', this.unlockMapClicks);
|
||||
this.map.map.addEventListener('mouseup', this.onClick);
|
||||
}
|
||||
};
|
||||
|
||||
unlockMapClicks = (): void => {
|
||||
this.lockMapClicks(false);
|
||||
};
|
||||
|
||||
routerSetStart = (): void => {
|
||||
const { latlngs } = this.poly;
|
||||
|
||||
if (!latlngs || !latlngs.length) return;
|
||||
|
||||
this.router.startFrom(latlngs[latlngs.length - 1]);
|
||||
};
|
||||
|
||||
routerMoveStart = (): void => {
|
||||
const { _latlngs } = this.poly.poly;
|
||||
|
||||
if (_latlngs) this.router.moveStart(_latlngs[_latlngs.length - 1]);
|
||||
};
|
||||
|
||||
pushPolyPoints = (latlngs: Array<ILatLng>): void => {
|
||||
this.poly.pushPoints(latlngs);
|
||||
};
|
||||
|
||||
clearSticker = (): void => {
|
||||
if (this.activeSticker) {
|
||||
this.setActiveSticker(null);
|
||||
} else {
|
||||
this.setMode(MODES.NONE);
|
||||
}
|
||||
};
|
||||
|
||||
clearAll = (): void => {
|
||||
this.poly.clearAll();
|
||||
this.router.clearAll();
|
||||
this.stickers.clearAll();
|
||||
|
||||
this.setIsEmpty(true);
|
||||
};
|
||||
|
||||
setData = ({
|
||||
route = [],
|
||||
stickers = [],
|
||||
owner,
|
||||
title,
|
||||
address,
|
||||
provider = DEFAULT_PROVIDER,
|
||||
logo = DEFAULT_LOGO,
|
||||
is_public,
|
||||
is_published,
|
||||
description,
|
||||
}: Partial<IEditor['initialData']>): void => {
|
||||
this.mapSetTitle(title || '');
|
||||
const { id } = this.getUser();
|
||||
|
||||
if (address && id && owner && id === owner) {
|
||||
this.mapSetAddress(address);
|
||||
}
|
||||
|
||||
if (route) this.poly.setPoints(route);
|
||||
|
||||
this.stickers.clearAll();
|
||||
|
||||
if (stickers) {
|
||||
stickers.map(sticker =>
|
||||
sticker.set && STICKERS[sticker.set].url &&
|
||||
this.stickers.createSticker({
|
||||
latlng: sticker.latlng,
|
||||
angle: sticker.angle,
|
||||
sticker: sticker.sticker,
|
||||
set: sticker.set,
|
||||
text: sticker.text,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.mapSetPublic(is_public);
|
||||
this.setStarred(is_published);
|
||||
this.mapSetDescription(description);
|
||||
|
||||
this.mapSetLogo((logo && LOGOS[DEFAULT_LOGO] && logo) || DEFAULT_LOGO);
|
||||
this.setProvider((provider && PROVIDERS[provider] && provider) || DEFAULT_PROVIDER);
|
||||
|
||||
if (owner) this.owner = owner;
|
||||
};
|
||||
|
||||
fitDrawing = (): void => {
|
||||
if (!this.poly.isEmpty) {
|
||||
const poly_bounds = this.poly.poly.getBounds();
|
||||
|
||||
if (poly_bounds && Object.values(poly_bounds).length) {
|
||||
this.map.map.fitBounds(poly_bounds);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.stickers.isEmpty) {
|
||||
const stickers_bounds = this.stickers.layer.getBounds();
|
||||
|
||||
if (stickers_bounds && Object.values(stickers_bounds).length) {
|
||||
this.map.map.fitBounds(stickers_bounds);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no bounds to fit. better do something later
|
||||
};
|
||||
|
||||
setInitialData = (): void => {
|
||||
const { path } = getUrlData();
|
||||
const { id } = this.getUser();
|
||||
const { is_public, logo, is_published , description} = this.getState();
|
||||
const { route, stickers, provider } = this.dumpData();
|
||||
|
||||
this.initialData = {
|
||||
version: 2,
|
||||
title: this.getTitle(),
|
||||
owner: this.owner,
|
||||
address: (this.owner === id ? path : null),
|
||||
path,
|
||||
route,
|
||||
stickers,
|
||||
provider,
|
||||
is_public,
|
||||
logo,
|
||||
is_published,
|
||||
description,
|
||||
};
|
||||
};
|
||||
|
||||
startEditing = (): void => {
|
||||
const { id } = this.getUser();
|
||||
|
||||
this.setInitialData();
|
||||
this.owner = id;
|
||||
|
||||
this.poly.enableEditor();
|
||||
this.stickers.startEditing();
|
||||
};
|
||||
|
||||
stopEditing = (): void => {
|
||||
this.poly.poly.editor.disable();
|
||||
this.stickers.stopEditing();
|
||||
this.router.clearAll();
|
||||
};
|
||||
|
||||
cancelEditing = (): void => {
|
||||
if (this.hasEmptyHistory) {
|
||||
this.clearAll();
|
||||
this.startEditing();
|
||||
this.clearChanged();
|
||||
return;
|
||||
} else {
|
||||
this.setData(this.initialData);
|
||||
}
|
||||
|
||||
this.stopEditing();
|
||||
this.clearChanged();
|
||||
};
|
||||
|
||||
dumpData = () => ({
|
||||
route: this.poly.dumpData(),
|
||||
stickers: this.stickers.dumpData(),
|
||||
provider: this.getProvider(),
|
||||
});
|
||||
|
||||
setProvider: typeof mapSetProvider = provider => store.dispatch(mapSetProvider(provider));
|
||||
|
||||
get isEmpty(): boolean {
|
||||
const { route, stickers } = this.dumpData();
|
||||
|
||||
return (!route || route.length <= 1) && (!stickers || stickers.length <= 0);
|
||||
}
|
||||
|
||||
get hasEmptyHistory(): boolean {
|
||||
const { route, stickers } = this.initialData;
|
||||
|
||||
return (!route || route.length < 1) && (!stickers || stickers.length <= 0);
|
||||
};
|
||||
}
|
||||
|
||||
export const editor = new Editor();
|
||||
|
||||
// for debug purposes
|
||||
declare let window: any;
|
||||
window.editor = editor;
|
|
@ -1,606 +0,0 @@
|
|||
/*
|
||||
done IMPORTANT: select closest point on drag instead of first
|
||||
done add touch hint poly
|
||||
done approx radius for dragFindNearest
|
||||
*/
|
||||
|
||||
import {
|
||||
LatLngExpression,
|
||||
Marker,
|
||||
Polyline,
|
||||
PolylineOptions,
|
||||
marker,
|
||||
divIcon,
|
||||
LayerGroup,
|
||||
LatLng,
|
||||
LeafletMouseEvent,
|
||||
latLng,
|
||||
LatLngLiteral
|
||||
} from "leaflet";
|
||||
|
||||
import { distKmHaversine, distToSegment, getPolyLength, pointInArea } from "~/utils/geom";
|
||||
|
||||
interface InteractivePolylineOptions extends PolylineOptions {
|
||||
maxMarkers?: number;
|
||||
constraintsStyle?: PolylineOptions;
|
||||
kmMarksEnabled?: boolean;
|
||||
kmMarksStep?: number;
|
||||
}
|
||||
|
||||
export class InteractivePoly extends Polyline {
|
||||
constructor(
|
||||
latlngs: LatLngExpression[] | LatLngExpression[][],
|
||||
options?: InteractivePolylineOptions
|
||||
) {
|
||||
super(latlngs, options);
|
||||
|
||||
this.constraintsStyle = {
|
||||
...this.constraintsStyle,
|
||||
...options.constraintsStyle
|
||||
};
|
||||
this.maxMarkers = options.maxMarkers || this.maxMarkers;
|
||||
|
||||
this.constrLine = new Polyline([], this.constraintsStyle);
|
||||
|
||||
this.startDragHinting();
|
||||
}
|
||||
|
||||
updateTouchHinter = ({ latlngs }: { latlngs: LatLngLiteral[] }): void => {
|
||||
this.touchHinter.setLatLngs(latlngs);
|
||||
};
|
||||
|
||||
setPoints = (latlngs: LatLng[], emitEvent = false) => {
|
||||
this.setLatLngs(latlngs);
|
||||
this.recreateMarkers();
|
||||
this.recalcDistance();
|
||||
this.touchHinter.setLatLngs(latlngs);
|
||||
|
||||
if (emitEvent) {
|
||||
this.fire("latlngschange", { latlngs });
|
||||
}
|
||||
};
|
||||
|
||||
createHintMarker = (latlng: LatLng): Marker =>
|
||||
marker(latlng, {
|
||||
draggable: false,
|
||||
icon: divIcon({
|
||||
className: "leaflet-vertex-drag-helper",
|
||||
iconSize: [11, 11],
|
||||
iconAnchor: [6, 6]
|
||||
})
|
||||
});
|
||||
|
||||
createMarker = (latlng: LatLng): Marker =>
|
||||
marker(latlng, {
|
||||
draggable: true,
|
||||
icon: divIcon({
|
||||
className: "leaflet-vertex-icon",
|
||||
iconSize: [11, 11],
|
||||
iconAnchor: [6, 6]
|
||||
})
|
||||
})
|
||||
.on("contextmenu", this.dropMarker)
|
||||
.on("drag", this.onMarkerDrag)
|
||||
.on("dragstart", this.onMarkerDragStart)
|
||||
.on("dragend", this.onMarkerDragEnd)
|
||||
.addTo(this.markerLayer);
|
||||
|
||||
recreateMarkers = () => {
|
||||
this.clearAllMarkers();
|
||||
const latlngs = this.getLatLngs();
|
||||
|
||||
if (!latlngs || latlngs.length === 0) return;
|
||||
|
||||
latlngs.forEach(latlng => this.markers.push(this.createMarker(latlng)));
|
||||
|
||||
this.updateMarkers();
|
||||
};
|
||||
|
||||
clearAllMarkers = (): void => {
|
||||
this.markerLayer.clearLayers();
|
||||
this.markers = [];
|
||||
};
|
||||
|
||||
updateMarkers = (): void => {
|
||||
this.showVisibleMarkers();
|
||||
};
|
||||
|
||||
showAllMarkers = (): void => {
|
||||
if (!this.is_editing) return;
|
||||
if (this._map.hasLayer(this.markerLayer)) return;
|
||||
|
||||
this._map.addLayer(this.markerLayer);
|
||||
this.fire("allvertexshow");
|
||||
};
|
||||
|
||||
hideAllMarkers = (): void => {
|
||||
if (!this._map.hasLayer(this.markerLayer)) return;
|
||||
|
||||
this._map.removeLayer(this.markerLayer);
|
||||
this.fire("allvertexhide");
|
||||
};
|
||||
|
||||
showVisibleMarkers = (): void => {
|
||||
if (!this.is_editing) return;
|
||||
|
||||
const northEast = this._map.getBounds().getNorthEast();
|
||||
const southWest = this._map.getBounds().getSouthWest();
|
||||
|
||||
const { visible, hidden } = this.markers.reduce(
|
||||
(obj, marker) => {
|
||||
const { lat, lng } = marker.getLatLng();
|
||||
const is_hidden =
|
||||
lat > northEast.lat ||
|
||||
lng > northEast.lng ||
|
||||
lat < southWest.lat ||
|
||||
lng < southWest.lng;
|
||||
|
||||
return is_hidden
|
||||
? { ...obj, hidden: [...obj.hidden, marker] }
|
||||
: { ...obj, visible: [...obj.visible, marker] };
|
||||
},
|
||||
{ visible: [], hidden: [] }
|
||||
);
|
||||
|
||||
if (visible.length > this.maxMarkers) return this.hideAllMarkers();
|
||||
|
||||
this.showAllMarkers();
|
||||
|
||||
visible.forEach(marker => {
|
||||
if (!this.markerLayer.hasLayer(marker)) this.markerLayer.addLayer(marker);
|
||||
});
|
||||
|
||||
hidden.forEach(marker => {
|
||||
if (this.markerLayer.hasLayer(marker))
|
||||
this.markerLayer.removeLayer(marker);
|
||||
});
|
||||
};
|
||||
|
||||
editor = {
|
||||
disable: () => {
|
||||
this.hideAllMarkers();
|
||||
this.is_editing = false;
|
||||
this.stopDragHinting();
|
||||
this.stopDrawing();
|
||||
this.touchHinter.removeFrom(this._map);
|
||||
this.fire("editordisable");
|
||||
},
|
||||
enable: () => {
|
||||
this.is_editing = true;
|
||||
this.showVisibleMarkers();
|
||||
this.startDragHinting();
|
||||
this.touchHinter.addTo(this._map);
|
||||
|
||||
this.fire("editorenable");
|
||||
},
|
||||
continue: () => {
|
||||
this.is_drawing = true;
|
||||
this.drawing_direction = "forward";
|
||||
this.startDrawing();
|
||||
},
|
||||
prepend: () => {
|
||||
this.is_drawing = true;
|
||||
this.drawing_direction = "backward";
|
||||
this.startDrawing();
|
||||
}
|
||||
};
|
||||
|
||||
moveDragHint = ({ latlng }: LeafletMouseEvent): void => {
|
||||
this.hintMarker.setLatLng(latlng);
|
||||
};
|
||||
|
||||
hideDragHint = (): void => {
|
||||
if (this._map.hasLayer(this.hintMarker))
|
||||
this._map.removeLayer(this.hintMarker);
|
||||
};
|
||||
|
||||
showDragHint = (): void => {
|
||||
this._map.addLayer(this.hintMarker);
|
||||
};
|
||||
|
||||
startDragHinting = (): void => {
|
||||
this.touchHinter.on("mousemove", this.moveDragHint);
|
||||
this.touchHinter.on("mousedown", this.startDragHintMove);
|
||||
this.touchHinter.on("mouseover", this.showDragHint);
|
||||
this.touchHinter.on("mouseout", this.hideDragHint);
|
||||
};
|
||||
|
||||
stopDragHinting = (): void => {
|
||||
this.touchHinter.off("mousemove", this.moveDragHint);
|
||||
this.touchHinter.off("mousedown", this.startDragHintMove);
|
||||
this.touchHinter.off("mouseover", this.showDragHint);
|
||||
this.touchHinter.off("mouseout", this.hideDragHint);
|
||||
};
|
||||
|
||||
startDragHintMove = (event: LeafletMouseEvent): void => {
|
||||
event.originalEvent.stopPropagation();
|
||||
event.originalEvent.preventDefault();
|
||||
|
||||
if (this.is_drawing) {
|
||||
this.stopDrawing();
|
||||
this.is_drawing = true;
|
||||
}
|
||||
|
||||
const prev = this.dragHintFindNearest(event.latlng);
|
||||
|
||||
if (prev < 0) return;
|
||||
|
||||
this.hint_prev_marker = prev;
|
||||
|
||||
this.constrLine.setLatLngs([]).addTo(this._map);
|
||||
|
||||
this._map.dragging.disable();
|
||||
|
||||
this.is_dragging = true;
|
||||
|
||||
this._map.on("mousemove", this.dragHintMove);
|
||||
this._map.on("mouseup", this.dragHintAddMarker);
|
||||
this._map.on("mouseout", this.stopDragHintMove);
|
||||
};
|
||||
|
||||
stopDragHintMove = (): void => {
|
||||
this._map.dragging.enable();
|
||||
|
||||
this.constrLine.removeFrom(this._map);
|
||||
|
||||
this._map.off("mousemove", this.dragHintMove);
|
||||
this._map.off("mouseup", this.dragHintAddMarker);
|
||||
this._map.off("mouseout", this.stopDragHintMove);
|
||||
|
||||
if (this.is_drawing) this.startDrawing();
|
||||
|
||||
setTimeout(() => {
|
||||
this.is_dragging = false;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
dragHintAddMarker = ({ latlng }: LeafletMouseEvent): void => {
|
||||
this.dragHintChangeDistance(this.hint_prev_marker, latlng);
|
||||
|
||||
this.markers.splice(
|
||||
this.hint_prev_marker + 1,
|
||||
0,
|
||||
this.createMarker(latlng)
|
||||
);
|
||||
this.insertLatLng(latlng, this.hint_prev_marker + 1);
|
||||
this.hideDragHint();
|
||||
this.stopDragHintMove();
|
||||
};
|
||||
|
||||
dragHintChangeDistance = (index: number, current: LatLngLiteral): void => {
|
||||
const prev = this.markers[index];
|
||||
const next = this.markers[index + 1];
|
||||
|
||||
const initial_distance = distKmHaversine(prev.getLatLng(), next.getLatLng());
|
||||
|
||||
const current_distance =
|
||||
((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
|
||||
((next && distKmHaversine(next.getLatLng(), current)) || 0);
|
||||
|
||||
this.distance += current_distance - initial_distance;
|
||||
|
||||
this.fire("distancechange", { distance: this.distance });
|
||||
};
|
||||
|
||||
dragHintFindNearest = (latlng: LatLng): any => {
|
||||
const latlngs = this.getLatLngs() as LatLng[];
|
||||
|
||||
const neighbours = latlngs
|
||||
.filter((current, index) => {
|
||||
const next = latlngs[index + 1] as LatLng;
|
||||
|
||||
return next && pointInArea(current, next, latlng);
|
||||
})
|
||||
.map(el => latlngs.indexOf(el))
|
||||
.sort(
|
||||
(a, b) =>
|
||||
distToSegment(latlngs[a], latlngs[a + 1], latlng) -
|
||||
distToSegment(latlngs[b], latlngs[b + 1], latlng)
|
||||
);
|
||||
|
||||
return neighbours.length > 0 ? neighbours[0] : -1;
|
||||
};
|
||||
|
||||
dragHintMove = (event: LeafletMouseEvent): void => {
|
||||
event.originalEvent.stopPropagation();
|
||||
event.originalEvent.preventDefault();
|
||||
|
||||
this.setConstraints([
|
||||
this.markers[this.hint_prev_marker].getLatLng(),
|
||||
event.latlng,
|
||||
this.markers[this.hint_prev_marker + 1].getLatLng()
|
||||
]);
|
||||
};
|
||||
|
||||
onMarkerDrag = ({ target }: { target: Marker }) => {
|
||||
const coords = new Array(0)
|
||||
.concat(
|
||||
(this.vertex_index > 0 &&
|
||||
this.markers[this.vertex_index - 1].getLatLng()) ||
|
||||
[]
|
||||
)
|
||||
.concat(target.getLatLng())
|
||||
.concat(
|
||||
(this.vertex_index < this.markers.length - 1 &&
|
||||
this.markers[this.vertex_index + 1].getLatLng()) ||
|
||||
[]
|
||||
);
|
||||
|
||||
this.setConstraints(coords);
|
||||
|
||||
this.fire("vertexdrag", { index: this.vertex_index, vertex: target });
|
||||
};
|
||||
|
||||
onMarkerDragStart = ({ target }: { target: Marker }) => {
|
||||
if (this.is_drawing) {
|
||||
this.stopDrawing();
|
||||
this.is_drawing = true;
|
||||
}
|
||||
|
||||
if (this.is_dragging) this.stopDragHintMove();
|
||||
|
||||
this.vertex_index = this.markers.indexOf(target);
|
||||
|
||||
this.is_dragging = true;
|
||||
this.constrLine.addTo(this._map);
|
||||
|
||||
this.fire("vertexdragstart", { index: this.vertex_index, vertex: target });
|
||||
};
|
||||
|
||||
onMarkerDragEnd = ({ target }: { target: Marker }): void => {
|
||||
const latlngs = this.getLatLngs() as LatLngLiteral[];
|
||||
this.markerDragChangeDistance(
|
||||
this.vertex_index,
|
||||
latlngs[this.vertex_index],
|
||||
target.getLatLng()
|
||||
);
|
||||
|
||||
this.replaceLatlng(target.getLatLng(), this.vertex_index);
|
||||
|
||||
this.is_dragging = false;
|
||||
this.constrLine.removeFrom(this._map);
|
||||
|
||||
this.vertex_index = null;
|
||||
|
||||
if (this.is_drawing) this.startDrawing();
|
||||
|
||||
this.fire("vertexdragend", { index: this.vertex_index, vertex: target });
|
||||
};
|
||||
|
||||
markerDragChangeDistance = (
|
||||
index: number,
|
||||
initial: LatLngLiteral,
|
||||
current: LatLngLiteral
|
||||
): void => {
|
||||
const prev = index > 0 ? this.markers[index - 1] : null;
|
||||
const next =
|
||||
index <= this.markers.length + 1 ? this.markers[index + 1] : null;
|
||||
|
||||
const initial_distance =
|
||||
((prev && distKmHaversine(prev.getLatLng(), initial)) || 0) +
|
||||
((next && distKmHaversine(next.getLatLng(), initial)) || 0);
|
||||
|
||||
const current_distance =
|
||||
((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
|
||||
((next && distKmHaversine(next.getLatLng(), current)) || 0);
|
||||
|
||||
this.distance += current_distance - initial_distance;
|
||||
|
||||
this.fire("distancechange", { distance: this.distance });
|
||||
};
|
||||
|
||||
startDrawing = (): void => {
|
||||
this.is_drawing = true;
|
||||
this.setConstraints([]);
|
||||
this.constrLine.addTo(this._map);
|
||||
this._map.on("mousemove", this.onDrawingMove);
|
||||
this._map.on("click", this.onDrawingClick);
|
||||
};
|
||||
|
||||
stopDrawing = (): void => {
|
||||
this.constrLine.removeFrom(this._map);
|
||||
this._map.off("mousemove", this.onDrawingMove);
|
||||
this._map.off("click", this.onDrawingClick);
|
||||
this.is_drawing = false;
|
||||
};
|
||||
|
||||
onDrawingMove = ({ latlng }: LeafletMouseEvent): void => {
|
||||
if (this.markers.length === 0) {
|
||||
this.setConstraints([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._map.hasLayer(this.constrLine))
|
||||
this._map.addLayer(this.constrLine);
|
||||
|
||||
const marker =
|
||||
this.drawing_direction === "forward"
|
||||
? this.markers[this.markers.length - 1]
|
||||
: this.markers[0];
|
||||
|
||||
this.setConstraints([latlng, marker.getLatLng()]);
|
||||
};
|
||||
|
||||
onDrawingClick = (event: LeafletMouseEvent): void => {
|
||||
if (this.is_dragging) return;
|
||||
|
||||
const { latlng } = event;
|
||||
|
||||
this.stopDrawing();
|
||||
|
||||
const latlngs = this.getLatLngs() as any[];
|
||||
|
||||
this.drawingChangeDistance(latlng);
|
||||
|
||||
if (this.drawing_direction === "forward") {
|
||||
latlngs.push(latlng);
|
||||
this.markers.push(this.createMarker(latlng));
|
||||
} else {
|
||||
latlngs.unshift(latlng);
|
||||
this.markers.unshift(this.createMarker(latlng));
|
||||
}
|
||||
|
||||
this.setLatLngs(latlngs);
|
||||
this.fire("latlngschange", { latlngs });
|
||||
this.showVisibleMarkers();
|
||||
this.startDrawing();
|
||||
};
|
||||
|
||||
drawingChangeDistance = (latlng: LatLngLiteral): void => {
|
||||
const latlngs = this.getLatLngs() as LatLngLiteral[];
|
||||
|
||||
if (latlngs.length < 1) {
|
||||
this.distance = 0;
|
||||
this.fire("distancechange", { distance: this.distance });
|
||||
return;
|
||||
}
|
||||
|
||||
const point =
|
||||
this.drawing_direction === "forward"
|
||||
? latlngs[latlngs.length - 1]
|
||||
: latlngs[0];
|
||||
|
||||
this.distance += distKmHaversine(point, latlng);
|
||||
this.fire("distancechange", { distance: this.distance });
|
||||
};
|
||||
|
||||
replaceLatlng = (latlng: LatLng, index: number): void => {
|
||||
const latlngs = this.getLatLngs() as LatLngLiteral[];
|
||||
latlngs.splice(index, 1, latlng);
|
||||
this.setLatLngs(latlngs);
|
||||
this.fire("latlngschange", { latlngs });
|
||||
};
|
||||
|
||||
insertLatLng = (latlng, index): void => {
|
||||
const latlngs = this.getLatLngs();
|
||||
latlngs.splice(index, 0, latlng);
|
||||
this.setLatLngs(latlngs);
|
||||
this.fire("latlngschange", { latlngs });
|
||||
};
|
||||
|
||||
setConstraints = (coords: LatLng[]) => {
|
||||
this.constrLine.setLatLngs(coords);
|
||||
};
|
||||
|
||||
dropMarker = ({ target }: LeafletMouseEvent): void => {
|
||||
const index = this.markers.indexOf(target);
|
||||
const latlngs = this.getLatLngs();
|
||||
|
||||
if (typeof index === "undefined" || latlngs.length <= 2) return;
|
||||
|
||||
this.dropMarkerDistanceChange(index);
|
||||
this._map.removeLayer(this.markers[index]);
|
||||
this.markers.splice(index, 1);
|
||||
latlngs.splice(index, 1);
|
||||
|
||||
this.setLatLngs(latlngs);
|
||||
this.fire("latlngschange", { latlngs });
|
||||
};
|
||||
|
||||
dropMarkerDistanceChange = (index: number): void => {
|
||||
const latlngs = this.getLatLngs() as LatLngLiteral[];
|
||||
|
||||
const prev = index > 0 ? latlngs[index - 1] : null;
|
||||
const current = latlngs[index];
|
||||
const next = index <= latlngs.length + 1 ? latlngs[index + 1] : null;
|
||||
|
||||
const initial_distance =
|
||||
((prev && distKmHaversine(prev, current)) || 0) +
|
||||
((next && distKmHaversine(next, current)) || 0);
|
||||
|
||||
const current_distance = (prev && next && distKmHaversine(prev, next)) || 0;
|
||||
|
||||
this.distance += current_distance - initial_distance;
|
||||
|
||||
this.fire("distancechange", { distance: this.distance });
|
||||
};
|
||||
|
||||
recalcDistance = () => {
|
||||
const latlngs = this.getLatLngs() as LatLngLiteral[];
|
||||
this.distance = getPolyLength(latlngs);
|
||||
|
||||
this.fire("distancechange", { distance: this.distance });
|
||||
};
|
||||
|
||||
markers: Marker[] = [];
|
||||
maxMarkers: InteractivePolylineOptions["maxMarkers"] = 2;
|
||||
markerLayer: LayerGroup = new LayerGroup();
|
||||
|
||||
constraintsStyle: InteractivePolylineOptions["constraintsStyle"] = {
|
||||
weight: 6,
|
||||
color: "red",
|
||||
dashArray: "10, 12",
|
||||
opacity: 0.5,
|
||||
interactive: false
|
||||
};
|
||||
|
||||
touchHinter: Polyline = new Polyline([], {
|
||||
weight: 24,
|
||||
smoothFactor: 3,
|
||||
className: "touch-hinter-poly"
|
||||
});
|
||||
|
||||
hintMarker: Marker = this.createHintMarker(latLng({ lat: 0, lng: 0 }));
|
||||
|
||||
constrLine: Polyline;
|
||||
|
||||
is_editing: boolean = true;
|
||||
is_dragging: boolean = false;
|
||||
is_drawing: boolean = false;
|
||||
|
||||
drawing_direction: "forward" | "backward" = "forward";
|
||||
vertex_index?: number = null;
|
||||
|
||||
hint_prev_marker: number = null;
|
||||
distance: number = 0;
|
||||
}
|
||||
|
||||
InteractivePoly.addInitHook(function() {
|
||||
this.once("add", event => {
|
||||
if (event.target instanceof InteractivePoly) {
|
||||
this.map = event.target._map;
|
||||
|
||||
this.markerLayer.addTo(event.target._map);
|
||||
this.hintMarker.addTo(event.target._map);
|
||||
this.constrLine.addTo(event.target._map);
|
||||
this.touchHinter.addTo(event.target._map);
|
||||
|
||||
this.map.on("moveend", this.updateMarkers);
|
||||
|
||||
this.on("latlngschange", this.updateTouchHinter);
|
||||
|
||||
if (window.innerWidth < 768) {
|
||||
this.touchHinter.setStyle({ weight: 32 });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.once("remove", event => {
|
||||
if (event.target instanceof InteractivePoly) {
|
||||
this.markerLayer.removeFrom(this._map);
|
||||
this.hintMarker.removeFrom(this._map);
|
||||
this.constrLine.removeFrom(this._map);
|
||||
this.touchHinter.removeFrom(this._map);
|
||||
|
||||
this.map.off("moveend", this.updateMarkers);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// export const InteractivePoly = Component;
|
||||
/*
|
||||
events:
|
||||
vertexdragstart,
|
||||
vertexdragend,
|
||||
vertexdrag,
|
||||
|
||||
allvertexhide
|
||||
allvertexshow
|
||||
|
||||
editordisable
|
||||
editorenable
|
||||
|
||||
distancechange
|
||||
|
||||
latlngschange
|
||||
*/
|
|
@ -1,156 +0,0 @@
|
|||
import { divIcon, LatLngLiteral, Layer, LayerGroup, Map, marker, Marker } from "leaflet";
|
||||
import { arrowClusterIcon, createArrow } from "~/utils/arrow";
|
||||
import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js';
|
||||
import { allwaysPositiveAngleDeg, angleBetweenPoints, distKmHaversine } from "~/utils/geom";
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface KmMarksOptions {
|
||||
showMiddleMarkers: boolean,
|
||||
showEndMarker: boolean,
|
||||
kmMarksStep: number,
|
||||
}
|
||||
|
||||
class Component extends LayerGroup {
|
||||
constructor(latlngs?: LatLngLiteral[], options?: KmMarksOptions){
|
||||
super();
|
||||
|
||||
this.options = {
|
||||
showMiddleMarkers: true,
|
||||
showEndMarker: true,
|
||||
kmMarksStep: 10,
|
||||
...(options || {}),
|
||||
} as KmMarksOptions;
|
||||
}
|
||||
|
||||
setLatLngs = (latlngs: LatLngLiteral[]): void => {
|
||||
if (!this.map) return;
|
||||
this.marksLayer.clearLayers();
|
||||
this.endMarker.clearLayers();
|
||||
|
||||
this.distance = 0;
|
||||
|
||||
if (latlngs.length <= 1) return;
|
||||
|
||||
if (this.options.showMiddleMarkers) this.drawMiddleMarkers(latlngs);
|
||||
if (this.options.showEndMarker) this.drawEndMarker(latlngs);
|
||||
};
|
||||
|
||||
drawMiddleMarkers = (latlngs: LatLngLiteral[]) => {
|
||||
const kmMarks = {};
|
||||
let last_km_mark = 0;
|
||||
|
||||
this.distance = latlngs.reduce((dist, current, index) => {
|
||||
if (index >= latlngs.length - 1) return dist;
|
||||
|
||||
const next = latlngs[index + 1];
|
||||
const diff = distKmHaversine(current, next);
|
||||
const sum = dist + diff;
|
||||
const rounded = Math.floor(sum / this.options.kmMarksStep) * this.options.kmMarksStep;
|
||||
const count = Math.floor((rounded - last_km_mark) / this.options.kmMarksStep);
|
||||
|
||||
if (rounded > last_km_mark) {
|
||||
const angle = angleBetweenPoints(
|
||||
this.map.latLngToContainerPoint(current),
|
||||
this.map.latLngToContainerPoint(next),
|
||||
);
|
||||
|
||||
for (let i = 1; i <= count; i += 1) {
|
||||
const step = last_km_mark + (i * this.options.kmMarksStep);
|
||||
const shift = (step - dist) / diff;
|
||||
|
||||
const coords = {
|
||||
lat: current.lat - ((current.lat - next.lat) * shift),
|
||||
lng: current.lng - ((current.lng - next.lng) * shift),
|
||||
};
|
||||
|
||||
kmMarks[step] = { ...coords, angle };
|
||||
this.marksLayer.addLayer(this.createMiddleMarker(coords, angle, step));
|
||||
}
|
||||
|
||||
last_km_mark = rounded;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}, 0);
|
||||
|
||||
};
|
||||
|
||||
createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, {
|
||||
draggable: false,
|
||||
interactive: false,
|
||||
icon: divIcon({
|
||||
html: `
|
||||
<div class="leaflet-km-dist" style="transform: translate(-50%, -50%) rotate(${allwaysPositiveAngleDeg(angle)}deg);">
|
||||
${distance}
|
||||
</div>
|
||||
`,
|
||||
className: 'leaflet-km-marker',
|
||||
iconSize: [11, 11],
|
||||
iconAnchor: [6, 6]
|
||||
})
|
||||
});
|
||||
|
||||
createEndMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, {
|
||||
draggable: false,
|
||||
interactive: false,
|
||||
icon: divIcon({
|
||||
html: `
|
||||
<div class="leaflet-km-dist">
|
||||
${parseFloat(distance.toFixed(1))}
|
||||
</div>
|
||||
`,
|
||||
className: classNames('leaflet-km-marker end-marker', { right: (angle > -90 && angle < 90) }),
|
||||
iconSize: [11, 11],
|
||||
iconAnchor: [6, 6]
|
||||
}),
|
||||
zIndexOffset: -100,
|
||||
});
|
||||
|
||||
drawEndMarker = (latlngs: LatLngLiteral[]): void => {
|
||||
this.endMarker.clearLayers();
|
||||
|
||||
const current = latlngs[latlngs.length - 2];
|
||||
const next = latlngs[latlngs.length - 1
|
||||
];
|
||||
|
||||
const angle = angleBetweenPoints(
|
||||
this.map.latLngToContainerPoint(current),
|
||||
this.map.latLngToContainerPoint(next),
|
||||
);
|
||||
|
||||
this.endMarker.addLayer(this.createEndMarker(next, angle, this.distance));
|
||||
};
|
||||
|
||||
options: KmMarksOptions;
|
||||
map: Map;
|
||||
marksLayer: MarkerClusterGroup = new MarkerClusterGroup({
|
||||
spiderfyOnMaxZoom: false,
|
||||
showCoverageOnHover: false,
|
||||
zoomToBoundsOnClick: false,
|
||||
animate: false,
|
||||
maxClusterRadius: 120,
|
||||
iconCreateFunction: arrowClusterIcon,
|
||||
});
|
||||
endMarker: LayerGroup = new LayerGroup();
|
||||
distance: number = 0;
|
||||
}
|
||||
|
||||
|
||||
Component.addInitHook(function () {
|
||||
this.once('add', (event) => {
|
||||
if (event.target instanceof KmMarks) {
|
||||
this.map = event.target._map;
|
||||
this.marksLayer.addTo(this.map);
|
||||
this.endMarker.addTo(this.map);
|
||||
}
|
||||
});
|
||||
|
||||
this.once('remove', (event) => {
|
||||
if (event.target instanceof KmMarks) {
|
||||
this.marksLayer.removeFrom(this.map);
|
||||
this.endMarker.removeFrom(this.map);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export const KmMarks = Component;
|
|
@ -1,36 +0,0 @@
|
|||
import {
|
||||
Map as MapInterface,
|
||||
map,
|
||||
tileLayer,
|
||||
TileLayer,
|
||||
} from 'leaflet';
|
||||
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import { PROVIDER } from '~/config/frontend';
|
||||
import { DEFAULT_PROVIDER, PROVIDERS } from '~/constants/providers';
|
||||
|
||||
interface Props {
|
||||
container: string
|
||||
}
|
||||
|
||||
export class Map {
|
||||
constructor({ container }: Props) {
|
||||
this.map = map(container).setView([55.0153275, 82.9071235], 13);
|
||||
// todo: change coords?
|
||||
|
||||
this.tileLayer.addTo(this.map);
|
||||
}
|
||||
|
||||
map: MapInterface;
|
||||
tileLayer: TileLayer = tileLayer(PROVIDER.url, {
|
||||
attribution: 'Независимое Велосообщество',
|
||||
maxNativeZoom: 18,
|
||||
maxZoom: 18,
|
||||
});
|
||||
|
||||
setProvider = (provider: string): void => {
|
||||
const { url } = (provider && PROVIDERS[provider] && PROVIDERS[provider]) || PROVIDERS[DEFAULT_PROVIDER];
|
||||
|
||||
this.tileLayer.setUrl(url);
|
||||
}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
import { Map, LatLng } from 'leaflet';
|
||||
import { simplify } from '~/utils/simplify';
|
||||
import { editor, Editor } from "~/modules/Editor";
|
||||
import { ILatLng } from "~/modules/Stickers";
|
||||
import { InteractivePoly } from "~/modules/InteractivePoly";
|
||||
import { Arrows } from "~/modules/Arrows";
|
||||
import { KmMarks } from "~/modules/KmMarks";
|
||||
import { isMobile } from "~/utils/window";
|
||||
|
||||
interface Props {
|
||||
map: Map;
|
||||
editor: Editor;
|
||||
routerMoveStart: typeof editor.routerMoveStart,
|
||||
lockMapClicks: typeof editor.lockMapClicks,
|
||||
setDistance: typeof editor.setDistance,
|
||||
triggerOnChange: typeof editor.triggerOnChange,
|
||||
}
|
||||
|
||||
export class Poly {
|
||||
constructor({
|
||||
map, routerMoveStart, lockMapClicks, setDistance, triggerOnChange, editor,
|
||||
}: Props) {
|
||||
this.poly = new InteractivePoly([ ], {
|
||||
color: 'url(#activePathGradient)',
|
||||
weight: 6,
|
||||
maxMarkers: isMobile() ? 20 : 100,
|
||||
smoothFactor: 3,
|
||||
})
|
||||
.on('distancechange', this.onDistanceUpdate)
|
||||
.on('allvertexhide', this.onVertexHide)
|
||||
.on('allvertexshow', this.onVertexShow)
|
||||
.on('latlngschange', this.updateMarks)
|
||||
|
||||
this.poly.addTo(map);
|
||||
this.editor = editor;
|
||||
|
||||
this.map = map;
|
||||
|
||||
this.routerMoveStart = routerMoveStart;
|
||||
this.setDistance = setDistance;
|
||||
this.triggerOnChange = triggerOnChange;
|
||||
this.lockMapClicks = lockMapClicks;
|
||||
|
||||
this.arrows = new Arrows({}).addTo(map);
|
||||
this.kmMarks = new KmMarks().addTo(map);
|
||||
}
|
||||
|
||||
onDistanceUpdate = (event) => {
|
||||
const { distance } = event as { distance: number };
|
||||
this.setDistance(parseFloat(distance.toFixed(2)));
|
||||
};
|
||||
|
||||
onVertexHide = (): void => this.editor.setMarkersShown(false);
|
||||
onVertexShow = (): void => this.editor.setMarkersShown(true);
|
||||
|
||||
updateMarks = event => {
|
||||
// this.editor.setChanged(true);
|
||||
this.editor.triggerOnChange();
|
||||
|
||||
const { latlngs } = event;
|
||||
this.arrows.setLatLngs(latlngs);
|
||||
this.kmMarks.setLatLngs(latlngs);
|
||||
};
|
||||
|
||||
continue = (): void => {
|
||||
this.poly.editor.continue();
|
||||
};
|
||||
|
||||
stop = (): void => {
|
||||
this.poly.stopDrawing();
|
||||
};
|
||||
|
||||
enableEditor = (): void => {
|
||||
this.poly.editor.enable();
|
||||
};
|
||||
|
||||
setPoints = (latlngs: Array<ILatLng>): void => {
|
||||
if (!latlngs || latlngs.length <= 1) return;
|
||||
this.poly.setPoints(latlngs);
|
||||
};
|
||||
|
||||
pushPoints = (latlngs: Array<ILatLng>): void => {
|
||||
const { map } = this;
|
||||
const simplified = simplify({ map, latlngs });
|
||||
const summary = [
|
||||
...this.poly.getLatLngs(),
|
||||
...simplified,
|
||||
];
|
||||
|
||||
this.poly.setPoints(summary);
|
||||
};
|
||||
|
||||
clearAll = (): void => {
|
||||
this.poly.setPoints([]);
|
||||
};
|
||||
|
||||
dumpData = (): Array<LatLng> => this.latlngs;
|
||||
|
||||
get latlngs(): Array<LatLng> {
|
||||
return (
|
||||
this.poly && this.poly.getLatLngs().length
|
||||
&& this.poly.getLatLngs().map(el => ({ ...el }))) || [];
|
||||
}
|
||||
|
||||
get isEmpty(): boolean {
|
||||
return (!this.latlngs || Object.values(this.latlngs).length <= 0);
|
||||
}
|
||||
|
||||
arrows;
|
||||
poly;
|
||||
kmMarks;
|
||||
|
||||
editor: Props['editor'];
|
||||
map: Props['map'];
|
||||
routerMoveStart: Props['routerMoveStart'];
|
||||
setDistance: Props['setDistance'];
|
||||
triggerOnChange: Props['triggerOnChange'];
|
||||
lockMapClicks: Props['lockMapClicks'];
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
import { Map, Marker } from 'leaflet';
|
||||
import * as Routing from 'leaflet-routing-machine/src/index';
|
||||
import { CLIENT } from '~/config/frontend';
|
||||
import { DomMarker } from '~/utils/DomMarker';
|
||||
import { editor } from "~/modules/Editor";
|
||||
|
||||
interface ILatLng {
|
||||
lat: number, lng: number
|
||||
}
|
||||
|
||||
interface IWaypoint {
|
||||
latLng: ILatLng
|
||||
}
|
||||
|
||||
interface Props {
|
||||
setIsRouting: typeof editor.setIsRouting,
|
||||
map: Map,
|
||||
setRouterPoints: typeof editor.setRouterPoints,
|
||||
pushPolyPoints: typeof editor.pushPolyPoints,
|
||||
lockMapClicks: typeof editor.lockMapClicks;
|
||||
}
|
||||
|
||||
export class Router {
|
||||
constructor({
|
||||
map, lockMapClicks, setRouterPoints, pushPolyPoints, setIsRouting,
|
||||
}: Props) {
|
||||
this.waypoints = [];
|
||||
this.lockMapClicks = lockMapClicks;
|
||||
this.setRouterPoints = setRouterPoints;
|
||||
this.pushPolyPoints = pushPolyPoints;
|
||||
this.setIsRouting = setIsRouting;
|
||||
|
||||
const routeLine = r => Routing.line(r, {
|
||||
styles: [
|
||||
{ color: 'white', opacity: 0.8, weight: 6 },
|
||||
{ color: '#4597d0', opacity: 1, weight: 4, dashArray: '15,10' }
|
||||
],
|
||||
addWaypoints: true,
|
||||
}).on('linetouched', this.lockPropagations);
|
||||
|
||||
this.router = Routing.control({
|
||||
serviceUrl: CLIENT.OSRM_URL,
|
||||
profile: CLIENT.OSRM_PROFILE,
|
||||
fitSelectedRoutes: false,
|
||||
showAlternatives: false,
|
||||
routeLine,
|
||||
altLineOptions: {
|
||||
styles: [{ color: '#4597d0', opacity: 1, weight: 3 }]
|
||||
},
|
||||
show: false,
|
||||
plan: Routing.plan([], {
|
||||
createMarker: (i, wp) => new Marker(wp.latLng, {
|
||||
draggable: true,
|
||||
icon: this.createWaypointMarker(),
|
||||
}),
|
||||
routeWhileDragging: false,
|
||||
}),
|
||||
routeWhileDragging: false,
|
||||
routingOptions: {
|
||||
geometryOnly: false,
|
||||
},
|
||||
useHints: false,
|
||||
})
|
||||
.on('routingstart', this.showSpinner)
|
||||
.on('routesfound routingerror routeselected routingzoomend', this.hideSpinner)
|
||||
.on('waypointschanged', this.updateWaypointsCount);
|
||||
|
||||
this.router.addTo(map);
|
||||
}
|
||||
|
||||
showSpinner = () => {
|
||||
this.setIsRouting(true);
|
||||
};
|
||||
|
||||
hideSpinner = () => {
|
||||
this.setIsRouting(false);
|
||||
};
|
||||
|
||||
pushWaypointOnClick = ({ latlng: { lat, lng } }: { latlng: ILatLng }): void => {
|
||||
const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
|
||||
this.router.setWaypoints([...waypoints, { lat, lng }]);
|
||||
};
|
||||
|
||||
createWaypointMarker = (): DomMarker => {
|
||||
const element = document.createElement('div');
|
||||
|
||||
element.addEventListener('mousedown', this.lockPropagations);
|
||||
element.addEventListener('mouseup', this.unlockPropagations);
|
||||
|
||||
return new DomMarker({
|
||||
element,
|
||||
className: 'router-waypoint',
|
||||
});
|
||||
};
|
||||
|
||||
lockPropagations = (): void => {
|
||||
window.addEventListener('mouseup', this.unlockPropagations);
|
||||
this.lockMapClicks(true);
|
||||
};
|
||||
|
||||
unlockPropagations = (e): void => {
|
||||
if (e && e.preventPropagations) {
|
||||
e.preventDefault();
|
||||
e.preventPropagations();
|
||||
}
|
||||
|
||||
window.removeEventListener('mouseup', this.unlockPropagations);
|
||||
setTimeout(() => this.lockMapClicks(false), 0);
|
||||
};
|
||||
|
||||
startFrom = (latlngs: ILatLng): void => {
|
||||
const waypoints = this.router.getWaypoints();
|
||||
|
||||
if (waypoints && waypoints.length) {
|
||||
waypoints[0] = { ...latlngs };
|
||||
this.router.setWaypoints(waypoints);
|
||||
return;
|
||||
}
|
||||
|
||||
this.router.setWaypoints([{ ...latlngs }]);
|
||||
};
|
||||
|
||||
moveStart = (latlng: ILatLng): void => {
|
||||
const waypoints = this.router.getWaypoints();
|
||||
const { latLng }: { latLng: ILatLng } = (waypoints[0] || {});
|
||||
|
||||
if (!latLng || !latlng) return;
|
||||
|
||||
if (
|
||||
latlng.lat.toFixed(5) === latLng.lat.toFixed(5) &&
|
||||
latlng.lng.toFixed(5) === latLng.lng.toFixed(5)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
waypoints[0] = { ...latlng };
|
||||
|
||||
this.router.setWaypoints(waypoints);
|
||||
};
|
||||
|
||||
updateWaypointsCount = (): void => {
|
||||
const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
|
||||
this.setRouterPoints(waypoints.length);
|
||||
};
|
||||
|
||||
cancelDrawing = (): void => {
|
||||
this.router.setWaypoints([]);
|
||||
};
|
||||
|
||||
submitDrawing = (): void => {
|
||||
const [route] = this.router._routes;
|
||||
if (!route) return;
|
||||
|
||||
const { coordinates } = route;
|
||||
this.pushPolyPoints(coordinates);
|
||||
|
||||
this.router.setWaypoints([]);
|
||||
|
||||
// UNCOMMENT THIS TO CONTINUE DRAWING
|
||||
// const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
|
||||
// this.router.setWaypoints(waypoints[waypoints.length - 1]);
|
||||
};
|
||||
|
||||
clearAll = (): void => {
|
||||
this.router.setWaypoints([]);
|
||||
};
|
||||
|
||||
waypoints: Array<IWaypoint> = [];
|
||||
setIsRouting: Props['setIsRouting'];
|
||||
lockMapClicks: Props['lockMapClicks'];
|
||||
setRouterPoints: Props['setRouterPoints'];
|
||||
pushPolyPoints: Props['pushPolyPoints'];
|
||||
router: Routing;
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
import { Map, Marker, marker } from 'leaflet';
|
||||
import React from 'react';
|
||||
import { DomMarker } from '~/utils/DomMarker';
|
||||
|
||||
import { STICKERS } from '~/constants/stickers';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { StickerDesc } from '~/components/StickerDesc';
|
||||
import classnames from 'classnames';
|
||||
import { getLabelDirection } from '~/utils/geom';
|
||||
import { ILatLng } from "~/modules/Stickers";
|
||||
import { IRootState } from "~/redux/user";
|
||||
import { Editor, editor } from "~/modules/Editor";
|
||||
|
||||
const getX = e => (
|
||||
e.touches && e.touches.length > 0
|
||||
? { pageX: e.touches[0].pageX, pageY: e.touches[0].pageY }
|
||||
: { pageX: e.pageX, pageY: e.pageY }
|
||||
);
|
||||
|
||||
|
||||
export interface IStickerDump {
|
||||
latlng: ILatLng,
|
||||
set: IRootState['activeSticker']['set'],
|
||||
sticker: IRootState['activeSticker']['sticker'],
|
||||
angle?: number,
|
||||
text?: string,
|
||||
}
|
||||
|
||||
interface Props {
|
||||
latlng: ILatLng;
|
||||
map: Map;
|
||||
sticker: IRootState['activeSticker']['sticker'];
|
||||
set: IRootState['activeSticker']['set'];
|
||||
angle?: number;
|
||||
text?: string;
|
||||
editor: Editor,
|
||||
|
||||
deleteSticker: (sticker: this) => void;
|
||||
|
||||
triggerOnChange: typeof editor.triggerOnChange;
|
||||
lockMapClicks: typeof editor.lockMapClicks;
|
||||
}
|
||||
|
||||
export class Sticker {
|
||||
constructor({
|
||||
latlng, deleteSticker, map, lockMapClicks, sticker, set, triggerOnChange, angle = 2.2, text = '', editor,
|
||||
}: Props) {
|
||||
this.text = text;
|
||||
this.latlng = latlng;
|
||||
this.angle = parseFloat(((angle && (angle % Math.PI)) || 2.2).toFixed(2));
|
||||
this.map = map;
|
||||
this.sticker = sticker;
|
||||
this.set = set;
|
||||
this.triggerOnChange = triggerOnChange;
|
||||
this.direction = getLabelDirection(this.angle);
|
||||
this.deleteSticker = deleteSticker;
|
||||
this.lockMapClicks = lockMapClicks;
|
||||
this.editor = editor;
|
||||
this.element = document.createElement('div');
|
||||
|
||||
ReactDOM.render(
|
||||
<React.Fragment>
|
||||
<div
|
||||
className="sticker-arrow"
|
||||
ref={el => { this.stickerArrow = el; }}
|
||||
/>
|
||||
<div
|
||||
className={classnames(`sticker-label ${this.direction}`, {})}
|
||||
ref={el => { this.stickerImage = el; }}
|
||||
>
|
||||
<StickerDesc value={this.text} onChange={this.setText} />
|
||||
<div
|
||||
className="sticker-image"
|
||||
style={{
|
||||
backgroundImage: `url('${STICKERS[set].url}`,
|
||||
backgroundPosition: `${-STICKERS[set].layers[sticker].off * 72} 50%`,
|
||||
}}
|
||||
onMouseDown={this.onDragStart}
|
||||
onMouseUp={this.onDragStop}
|
||||
onTouchStart={this.onDragStart}
|
||||
onTouchEnd={this.onDragStop}
|
||||
/>
|
||||
<div
|
||||
className="sticker-delete"
|
||||
onMouseDown={this.onDelete}
|
||||
onTouchStart={this.onDelete}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>,
|
||||
this.element
|
||||
);
|
||||
|
||||
const mark = new DomMarker({
|
||||
element: this.element,
|
||||
className: 'sticker-container',
|
||||
});
|
||||
|
||||
this.marker = marker(latlng, { icon: mark, draggable: true });
|
||||
|
||||
this.marker.on('add', this.updateModeOnAdd);
|
||||
|
||||
this.element.addEventListener('mouseup', this.onDragStop);
|
||||
this.element.addEventListener('mouseup', this.preventPropagations);
|
||||
|
||||
this.element.addEventListener('touchend', this.onDragStop);
|
||||
this.element.addEventListener('touchend', this.preventPropagations);
|
||||
|
||||
this.marker.on('dragend', this.triggerOnChange);
|
||||
|
||||
this.setAngle(this.angle);
|
||||
}
|
||||
|
||||
updateModeOnAdd = () => {
|
||||
if (this.editor.getEditing()) {
|
||||
this.startEditing();
|
||||
} else {
|
||||
this.stopEditing();
|
||||
}
|
||||
};
|
||||
|
||||
setText = (text: Props['text']): void => {
|
||||
this.text = text;
|
||||
};
|
||||
|
||||
onDelete = (): void => {
|
||||
if (!this.isDragging) this.deleteSticker(this);
|
||||
};
|
||||
|
||||
onDragStart = (e): void => {
|
||||
this.preventPropagations(e);
|
||||
this.marker.dragging.disable();
|
||||
|
||||
this.isDragging = true;
|
||||
|
||||
this.lockMapClicks(true);
|
||||
|
||||
window.addEventListener('mousemove', this.onDrag);
|
||||
window.addEventListener('touchmove', this.onDrag);
|
||||
|
||||
window.addEventListener('mouseup', this.onDragStop);
|
||||
window.addEventListener('touchend', this.onDragStop);
|
||||
};
|
||||
|
||||
preventPropagations = (e): void => {
|
||||
if (!e || !e.stopPropagation) return;
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
onDragStop = (e): void => {
|
||||
this.preventPropagations(e);
|
||||
this.marker.dragging.enable();
|
||||
|
||||
this.triggerOnChange();
|
||||
this.isDragging = false;
|
||||
|
||||
window.removeEventListener('mousemove', this.onDrag);
|
||||
window.removeEventListener('touchmove', this.onDrag);
|
||||
|
||||
window.removeEventListener('mouseup', this.onDragStop);
|
||||
window.removeEventListener('touchend', this.onDragStop);
|
||||
|
||||
this.lockMapClicks(false);
|
||||
};
|
||||
|
||||
onDrag = (e: DragEvent): void => {
|
||||
this.preventPropagations(e);
|
||||
this.estimateAngle(e);
|
||||
};
|
||||
|
||||
estimateAngle = (e): void => {
|
||||
const { x, y } = this.element.getBoundingClientRect() as DOMRect;
|
||||
const { pageX, pageY } = getX(e);
|
||||
|
||||
this.angle = parseFloat(Math.atan2((y - pageY), (x - pageX)).toFixed(2));
|
||||
|
||||
this.setAngle(this.angle);
|
||||
};
|
||||
|
||||
setAngle = (angle: Props['angle']): void => {
|
||||
if (!this.stickerImage) return;
|
||||
|
||||
const direction = getLabelDirection(angle);
|
||||
|
||||
if (direction !== this.direction) {
|
||||
this.direction = direction;
|
||||
this.stickerImage.className = `sticker-label ${direction}`;
|
||||
}
|
||||
|
||||
const rad = 56;
|
||||
|
||||
const x = ((Math.cos(angle + Math.PI) * rad) - 30);
|
||||
const y = ((Math.sin(angle + Math.PI) * rad) - 30);
|
||||
|
||||
this.stickerImage.style.left = String(6 + x);
|
||||
this.stickerImage.style.top = String(6 + y);
|
||||
|
||||
this.stickerArrow.style.transform = `rotate(${angle + Math.PI}rad)`;
|
||||
};
|
||||
|
||||
dumpData = (): IStickerDump => ({
|
||||
angle: this.angle,
|
||||
latlng: { ...this.marker.getLatLng() },
|
||||
sticker: this.sticker,
|
||||
set: this.set,
|
||||
text: this.text,
|
||||
});
|
||||
|
||||
stopEditing = (): void => {
|
||||
this.element.className = 'sticker-container inactive';
|
||||
};
|
||||
|
||||
startEditing = (): void => {
|
||||
this.element.className = 'sticker-container';
|
||||
};
|
||||
|
||||
element: HTMLDivElement = document.createElement('div');
|
||||
stickerImage: HTMLDivElement;
|
||||
stickerArrow: HTMLDivElement;
|
||||
marker: Marker;
|
||||
isDragging: boolean = false;
|
||||
direction: string;
|
||||
editor: Editor;
|
||||
|
||||
text: Props['text'];
|
||||
latlng: Props['latlng'];
|
||||
angle: Props['angle'];
|
||||
map: Props['map'];
|
||||
sticker: Props['sticker'];
|
||||
set: Props['set'];
|
||||
triggerOnChange: Props['triggerOnChange'];
|
||||
|
||||
deleteSticker: Props['deleteSticker'];
|
||||
lockMapClicks: Props['lockMapClicks'];
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
import {FeatureGroup, LayerGroup, layerGroup, Map} from 'leaflet';
|
||||
import { IStickerDump, Sticker } from '~/modules/Sticker';
|
||||
import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js';
|
||||
import { clusterIcon } from '~/utils/clusterIcon';
|
||||
import { editor, Editor } from "~/modules/Editor";
|
||||
import { STICKERS } from "~/constants/stickers";
|
||||
|
||||
export interface ILatLng {
|
||||
lat: number,
|
||||
lng: number,
|
||||
}
|
||||
|
||||
interface Props {
|
||||
editor: Editor;
|
||||
map: Map;
|
||||
|
||||
triggerOnChange: typeof editor.triggerOnChange;
|
||||
lockMapClicks: typeof editor.lockMapClicks;
|
||||
}
|
||||
|
||||
export class Stickers {
|
||||
constructor({ map, lockMapClicks, triggerOnChange, editor }: Props) {
|
||||
this.map = map;
|
||||
this.triggerOnChange = triggerOnChange;
|
||||
this.editor = editor;
|
||||
|
||||
// this.clusterLayer.addTo(map);
|
||||
// this.clusterLayer.on('animationend', this.onSpiderify);
|
||||
|
||||
this.lockMapClicks = lockMapClicks;
|
||||
this.stickers = [];
|
||||
|
||||
this.layer.addTo(this.map);
|
||||
}
|
||||
|
||||
createSticker = ({
|
||||
latlng, sticker, angle = 2.2, text = '', set
|
||||
}: IStickerDump): void => {
|
||||
|
||||
if (!STICKERS[set] || !STICKERS[set].layers || !STICKERS[set].layers[sticker]) return;
|
||||
|
||||
const marker = new Sticker({
|
||||
latlng,
|
||||
angle,
|
||||
deleteSticker: this.deleteStickerByReference,
|
||||
map: this.map,
|
||||
lockMapClicks: this.lockMapClicks,
|
||||
sticker,
|
||||
set,
|
||||
triggerOnChange: this.triggerOnChange,
|
||||
text,
|
||||
editor: this.editor,
|
||||
});
|
||||
|
||||
this.stickers.push(marker);
|
||||
|
||||
marker.marker.addTo(this.layer);
|
||||
|
||||
this.triggerOnChange();
|
||||
};
|
||||
|
||||
deleteStickerByReference = (ref: Sticker): void => {
|
||||
const index = this.stickers.indexOf(ref);
|
||||
|
||||
if (index < 0) return;
|
||||
|
||||
// this.clusterLayer.removeLayer(ref.marker);
|
||||
this.layer.removeLayer(ref.marker);
|
||||
this.stickers.splice(index, 1);
|
||||
|
||||
this.triggerOnChange();
|
||||
};
|
||||
|
||||
clearAll = (): void => {
|
||||
const target = [...this.stickers];
|
||||
target.map(sticker => {
|
||||
this.deleteStickerByReference(sticker);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
dumpData = (): Array<IStickerDump> => this.stickers.map(sticker => sticker.dumpData());
|
||||
|
||||
startEditing = (): void => {
|
||||
this.stickers.map(sticker => sticker.startEditing());
|
||||
};
|
||||
|
||||
stopEditing = (): void => {
|
||||
this.stickers.map(sticker => sticker.stopEditing());
|
||||
};
|
||||
|
||||
get isEmpty(): boolean {
|
||||
return !this.stickers || this.stickers.length === 0
|
||||
};
|
||||
// clusterLayer: LayerGroup = new LayerGroup();
|
||||
|
||||
// uncomment to enable clustering
|
||||
|
||||
// clusterLayer: MarkerClusterGroup = new MarkerClusterGroup({
|
||||
// spiderfyOnMaxZoom: false,
|
||||
// showCoverageOnHover: false,
|
||||
// zoomToBoundsOnClick: true,
|
||||
// animate: false,
|
||||
// maxClusterRadius: 8,
|
||||
// // disableClusteringAtZoom: 13,
|
||||
// iconCreateFunction: clusterIcon,
|
||||
// });
|
||||
|
||||
editor: Props['editor'];
|
||||
map: Props['map'];
|
||||
|
||||
stickers: Array<Sticker> = [];
|
||||
layer: FeatureGroup = new FeatureGroup();
|
||||
|
||||
triggerOnChange: Props['triggerOnChange'];
|
||||
lockMapClicks: Props['lockMapClicks'];
|
||||
}
|
|
@ -32,20 +32,11 @@ const mapStateToProps = ({
|
|||
user: { role },
|
||||
},
|
||||
}: IState) => {
|
||||
if (routes.filter.max >= 9999) {
|
||||
return {
|
||||
routes,
|
||||
editing,
|
||||
ready: false,
|
||||
role,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
role,
|
||||
routes,
|
||||
editing,
|
||||
ready: true,
|
||||
ready: routes.filter.max < 9999,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -89,18 +89,17 @@ const mapDispatchToProps = {
|
|||
editorRouterSubmit: EDITOR_ACTIONS.editorRouterSubmit,
|
||||
};
|
||||
|
||||
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & { };
|
||||
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||
|
||||
const RouterDialogUnconnected: FC<Props> = ({
|
||||
editor: {
|
||||
router: { waypoints },
|
||||
is_routing,
|
||||
},
|
||||
editorRouterCancel,
|
||||
editorRouterSubmit,
|
||||
}) => (
|
||||
<div className="control-dialog bottom right">
|
||||
<div className={classnames('save-loader', { active: is_routing })} />
|
||||
<div className={classnames('save-loader')} />
|
||||
|
||||
{!waypoints.length && noPoints({ editorRouterCancel })}
|
||||
{waypoints.length === 1 && firstPoint({ editorRouterCancel })}
|
||||
|
|
|
@ -1,29 +1,25 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { Icon } from '~/components/panels/Icon';
|
||||
import { MapListDialog } from "~/components/dialogs/MapListDialog";
|
||||
import { Tooltip } from "~/components/panels/Tooltip";
|
||||
import { ReactElement } from "react";
|
||||
import React, { FC, memo } from 'react';
|
||||
import { MapListDialog } from '~/components/dialogs/MapListDialog';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
interface Props {
|
||||
address: string,
|
||||
stopEditing: typeof MapListDialog.stopEditing,
|
||||
dropRoute: typeof MapListDialog.dropRoute,
|
||||
address: string;
|
||||
stopEditing: typeof MapListDialog.stopEditing;
|
||||
dropRoute: typeof MapListDialog.dropRoute;
|
||||
}
|
||||
|
||||
export const RouteRowDrop = ({
|
||||
address, stopEditing, dropRoute,
|
||||
}: Props): ReactElement<Props, null> => (
|
||||
<div
|
||||
className="route-row-drop"
|
||||
>
|
||||
<div
|
||||
className="route-row"
|
||||
>
|
||||
export const RouteRowDrop: FC<Props> = memo(({ address, stopEditing, dropRoute }) => (
|
||||
<div className="route-row-drop">
|
||||
<div className="route-row">
|
||||
<div className="button-group">
|
||||
<div className="button" onClick={dropRoute.bind(null, address)}>Удалить</div>
|
||||
<div className="button primary" onClick={stopEditing}>Отмена</div>
|
||||
<div className="button" onClick={dropRoute.bind(null, address)}>
|
||||
Удалить
|
||||
</div>
|
||||
<div className="button primary" onClick={stopEditing}>
|
||||
Отмена
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
));
|
||||
|
|
|
@ -4,27 +4,24 @@ import { Icon } from '~/components/panels/Icon';
|
|||
import { connect } from 'react-redux';
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { editorSetSpeed } from '~/redux/editor/actions';
|
||||
import { Tooltip } from "~/components/panels/Tooltip";
|
||||
import { isMobile } from "~/utils/window";
|
||||
import { Tooltip } from '~/components/panels/Tooltip';
|
||||
import { isMobile } from '~/utils/window';
|
||||
import { IState } from '~/redux/store';
|
||||
import pick from 'ramda/es/pick';
|
||||
import { selectEditor } from '~/redux/editor/selectors';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const {
|
||||
editor: { distance, estimated, speed },
|
||||
}: IState = state;
|
||||
|
||||
return { distance, estimated, speed };
|
||||
}
|
||||
const mapStateToProps = (state: IState) =>
|
||||
pick(['distance', 'estimated', 'speed'], selectEditor(state));
|
||||
|
||||
const mapDispatchToProps = { editorSetSpeed };
|
||||
|
||||
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||
|
||||
interface State {
|
||||
dialogOpened: boolean,
|
||||
dialogOpened: boolean;
|
||||
}
|
||||
|
||||
class Component extends React.PureComponent<Props, State> {
|
||||
class DistanceBarUnconnected extends React.PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
@ -36,10 +33,15 @@ class Component extends React.PureComponent<Props, State> {
|
|||
min: number = 5;
|
||||
max: number = 30;
|
||||
|
||||
marks: { [x: number]: string } = [...Array((Math.floor(this.max - this.min) / this.step) + 1)].reduce((obj, el, index) => ({
|
||||
...obj,
|
||||
[this.min + (index * this.step)]: String(this.min + (index * this.step)),
|
||||
}), { });
|
||||
marks: { [x: number]: string } = [
|
||||
...Array(Math.floor(this.max - this.min) / this.step + 1),
|
||||
].reduce(
|
||||
(obj, el, index) => ({
|
||||
...obj,
|
||||
[this.min + index * this.step]: String(this.min + index * this.step),
|
||||
}),
|
||||
{}
|
||||
);
|
||||
|
||||
toggleDialog = () => {
|
||||
if (isMobile()) return;
|
||||
|
@ -51,10 +53,12 @@ class Component extends React.PureComponent<Props, State> {
|
|||
const {
|
||||
props: { distance, estimated, speed },
|
||||
state: { dialogOpened },
|
||||
min, max, step, marks,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
marks,
|
||||
} = this;
|
||||
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="status-bar padded pointer tooltip-container" onClick={this.toggleDialog}>
|
||||
|
@ -65,8 +69,7 @@ class Component extends React.PureComponent<Props, State> {
|
|||
</span>
|
||||
<div className="desktop-only">{toHours(estimated)}</div>
|
||||
</div>
|
||||
{
|
||||
dialogOpened &&
|
||||
{dialogOpened && (
|
||||
<div className="control-dialog top left" style={{ left: 0, top: 42 }}>
|
||||
<div className="helper speed-helper">
|
||||
<Slider
|
||||
|
@ -80,13 +83,10 @@ class Component extends React.PureComponent<Props, State> {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const DistanceBar = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Component);
|
||||
export const DistanceBar = connect(mapStateToProps, mapDispatchToProps)(DistanceBarUnconnected);
|
||||
|
|
|
@ -4,7 +4,6 @@ import classnames from 'classnames';
|
|||
|
||||
import { Icon } from '~/components/panels/Icon';
|
||||
import { EditorDialog } from '~/components/panels/EditorDialog';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
editorSetMode,
|
||||
|
@ -16,10 +15,10 @@ import {
|
|||
import { Tooltip } from '~/components/panels/Tooltip';
|
||||
import { IState } from '~/redux/store';
|
||||
import { selectEditor } from '~/redux/editor/selectors';
|
||||
import pick from 'ramda/es/pick';
|
||||
|
||||
const mapStateToProps = (state: IState) => ({
|
||||
editor: selectEditor(state),
|
||||
});
|
||||
const mapStateToProps = (state: IState) =>
|
||||
pick(['mode', 'changed', 'editing', 'features'], selectEditor(state));
|
||||
|
||||
const mapDispatchToProps = {
|
||||
editorSetMode,
|
||||
|
@ -54,18 +53,15 @@ class EditorPanelUnconnected extends PureComponent<Props, void> {
|
|||
startRouterMode = () => this.props.editorSetMode(MODES.ROUTER);
|
||||
startTrashMode = () => this.props.editorSetMode(MODES.TRASH);
|
||||
startSaveMode = () => {
|
||||
// if (!this.props.changed) return;
|
||||
this.props.editorSetMode(MODES.SAVE);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
editor: {
|
||||
mode,
|
||||
changed,
|
||||
editing,
|
||||
features: { routing },
|
||||
},
|
||||
mode,
|
||||
changed,
|
||||
editing,
|
||||
features: { routing },
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
|
||||
export const Icon = ({ icon, size = 32 }: { icon: string, size?: number }) => (
|
||||
export const Icon = memo(({ icon, size = 32 }: { icon: string; size?: number }) => (
|
||||
<svg width={size} height={size} viewBox="0 0 32 32">
|
||||
<defs>
|
||||
<mask id={`icon-mask-${icon}`}>
|
||||
|
@ -9,5 +9,4 @@ export const Icon = ({ icon, size = 32 }: { icon: string, size?: number }) => (
|
|||
</defs>
|
||||
<rect x="0" y="0" width="32" height="32" stroke="none" mask={`url(#icon-mask-${icon})`} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
));
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import { UserLocation } from '~/components/UserLocation';
|
||||
import { DistanceBar } from '~/components/panels/DistanceBar';
|
||||
|
||||
export const TopLeftPanel = () => (
|
||||
export const TopLeftPanel = memo(() => (
|
||||
<div className="status-panel top left">
|
||||
<UserLocation />
|
||||
<DistanceBar />
|
||||
</div>
|
||||
);
|
||||
));
|
||||
|
|
|
@ -9,11 +9,14 @@ import { MODES } from '~/constants/modes';
|
|||
import { Tooltip } from '~/components/panels/Tooltip';
|
||||
import { selectMap } from '~/redux/map/selectors';
|
||||
import { selectEditor } from '~/redux/editor/selectors';
|
||||
import { IState } from '~/redux/store';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
map: selectMap(state),
|
||||
editor: selectEditor(state),
|
||||
});
|
||||
const mapStateToProps = (state: IState) => {
|
||||
const { provider, logo } = selectMap(state);
|
||||
const { markers_shown, editing } = selectEditor(state);
|
||||
|
||||
return { provider, logo, markers_shown, editing };
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
editorSetMode: EDITOR_ACTIONS.editorSetMode,
|
||||
|
@ -22,8 +25,10 @@ const mapDispatchToProps = {
|
|||
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||
|
||||
const TopRightPanelUnconnected = ({
|
||||
map: { provider, logo },
|
||||
editor: { markers_shown, editing },
|
||||
provider,
|
||||
logo,
|
||||
markers_shown,
|
||||
editing,
|
||||
editorSetMode,
|
||||
}: Props) => {
|
||||
const startProviderMode = useCallback(() => editorSetMode(MODES.PROVIDER), [editorSetMode]);
|
||||
|
|
|
@ -19,21 +19,18 @@ import { CLIENT } from '~/config/frontend';
|
|||
import { DIALOGS, TABS } from '~/constants/dialogs';
|
||||
import { Tooltip } from '~/components/panels/Tooltip';
|
||||
import { TitleDialog } from '~/components/dialogs/TitleDialog';
|
||||
import { IState } from '~/redux/store';
|
||||
|
||||
const mapStateToProps = ({
|
||||
user: { user },
|
||||
editor: { dialog, dialog_active },
|
||||
map: { route, stickers },
|
||||
}) => ({
|
||||
editor: {
|
||||
dialog,
|
||||
dialog_active,
|
||||
},
|
||||
user: { user },
|
||||
map: {
|
||||
route,
|
||||
stickers,
|
||||
}
|
||||
}: IState) => ({
|
||||
dialog,
|
||||
dialog_active,
|
||||
user,
|
||||
route,
|
||||
stickers,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
|
@ -100,7 +97,7 @@ export class UserPanelUnconnected extends PureComponent<Props, State> {
|
|||
openAppInfoDialog = () => {
|
||||
this.setMenuOpened();
|
||||
this.props.editorSetDialog(DIALOGS.APP_INFO);
|
||||
this.props.editorSetDialogActive(this.props.editor.dialog !== DIALOGS.APP_INFO);
|
||||
this.props.editorSetDialogActive(this.props.dialog !== DIALOGS.APP_INFO);
|
||||
};
|
||||
|
||||
openOauthFrame = () => {
|
||||
|
@ -118,7 +115,7 @@ export class UserPanelUnconnected extends PureComponent<Props, State> {
|
|||
|
||||
render() {
|
||||
const {
|
||||
props: { user: { user }, editor: { dialog, dialog_active }, map: { route, stickers } },
|
||||
props: { user, dialog, dialog_active, route, stickers },
|
||||
state: { menuOpened },
|
||||
} = this;
|
||||
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
// @flow
|
||||
import React from "react";
|
||||
import { UserPicture } from "~/components/user/UserPicture";
|
||||
import { IUser } from "~/constants/auth";
|
||||
import React, { FC, memo } from 'react';
|
||||
import { UserPicture } from '~/components/user/UserPicture';
|
||||
import { IUser } from '~/constants/auth';
|
||||
|
||||
interface Props {
|
||||
user: IUser;
|
||||
setMenuOpened: () => void;
|
||||
}
|
||||
|
||||
export const UserButton = ({
|
||||
setMenuOpened,
|
||||
user: { uid, photo, name }
|
||||
}: Props) => (
|
||||
export const UserButton: FC<Props> = memo(({ setMenuOpened, user: { uid, photo, name } }) => (
|
||||
<div className="control-bar user-bar">
|
||||
<div className="user-button" onClick={setMenuOpened}>
|
||||
<UserPicture photo={photo} />
|
||||
|
||||
<div className="user-button-fields">
|
||||
<div className="user-button-name">{name || uid || "..."}</div>
|
||||
<div className="user-button-text">{uid || "пользователь"}</div>
|
||||
<div className="user-button-name">{name || uid || '...'}</div>
|
||||
<div className="user-button-text">{uid || 'пользователь'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
));
|
||||
|
|
|
@ -32,7 +32,7 @@ type Props = {
|
|||
editorSetDialogActive: typeof editorSetDialogActive;
|
||||
};
|
||||
|
||||
const Component = (props: Props) => (
|
||||
const AppUnconnected = (props: Props) => (
|
||||
<div>
|
||||
<Fills />
|
||||
<UserPanel />
|
||||
|
@ -72,6 +72,8 @@ const mapStateToProps = ({
|
|||
set,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ editorHideRenderer, editorSetDialogActive }, dispatch);
|
||||
export const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(Component));
|
||||
const mapDispatchToProps = { editorHideRenderer, editorSetDialogActive };
|
||||
|
||||
const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(AppUnconnected));
|
||||
|
||||
export { App };
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { createElement } from 'react';
|
||||
import React, { createElement, FC, memo } from 'react';
|
||||
import { DIALOGS, IDialogs } from '~/constants/dialogs';
|
||||
import classnames from 'classnames';
|
||||
import { AppInfoDialog } from '~/components/dialogs/AppInfoDialog';
|
||||
|
@ -17,7 +17,7 @@ const LEFT_DIALOGS = {
|
|||
[DIALOGS.APP_INFO]: AppInfoDialog,
|
||||
};
|
||||
|
||||
const LeftDialog = ({ dialog, dialog_active, editorSetDialogActive }: Props) => (
|
||||
const LeftDialog: FC<Props> = memo(({ dialog, dialog_active, editorSetDialogActive }) => (
|
||||
<React.Fragment>
|
||||
{Object.keys(LEFT_DIALOGS).map(item => (
|
||||
<div
|
||||
|
@ -26,16 +26,22 @@ const LeftDialog = ({ dialog, dialog_active, editorSetDialogActive }: Props) =>
|
|||
>
|
||||
{dialog && LEFT_DIALOGS[item] && createElement(LEFT_DIALOGS[item], {})}
|
||||
|
||||
<div className="dialog-close-button desktop-only" onClick={() => editorSetDialogActive(false)}>
|
||||
<div
|
||||
className="dialog-close-button desktop-only"
|
||||
onClick={() => editorSetDialogActive(false)}
|
||||
>
|
||||
<Icon icon="icon-cancel-1" />
|
||||
</div>
|
||||
|
||||
<div className="dialog-close-button mobile-only" onClick={() => editorSetDialogActive(false)}>
|
||||
<div
|
||||
className="dialog-close-button mobile-only"
|
||||
onClick={() => editorSetDialogActive(false)}
|
||||
>
|
||||
<Icon icon="icon-chevron-down" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</React.Fragment>
|
||||
);
|
||||
));
|
||||
|
||||
export { LeftDialog };
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { FC, useEffect, memo, useState, useCallback } from 'react';
|
||||
import { IMapRoute } from '../../../redux/map/types';
|
||||
import { InteractivePoly } from '~/utils/polyline';
|
||||
import { InteractivePoly } from '~/utils/map/InteractivePoly';
|
||||
import { isMobile } from '~/utils/window';
|
||||
import { LatLng, Map, LeafletEvent } from 'leaflet';
|
||||
import { selectEditor } from '~/redux/editor/selectors';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { FC, useEffect, useMemo, useCallback, memo } from 'react';
|
||||
import { FC, useEffect, useCallback, memo } from 'react';
|
||||
import pick from 'ramda/es/pick';
|
||||
import { OsrmRouter } from '~/utils/osrm';
|
||||
import { OsrmRouter } from '~/utils/map/OsrmRouter';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectMap } from '~/redux/map/selectors';
|
||||
import { selectEditorRouter, selectEditorMode } from '~/redux/editor/selectors';
|
||||
|
|
|
@ -4,7 +4,7 @@ import { IStickerDump } from '~/redux/map/types';
|
|||
import { STICKERS } from '~/constants/stickers';
|
||||
import { StickerDesc } from '~/components/StickerDesc';
|
||||
import classNames from 'classnames';
|
||||
import { DomMarker } from '~/utils/DomMarker';
|
||||
import { DomMarker } from '~/utils/map/DomMarker';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { MapContainer, MainMap } from '~/constants/map';
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ import { LOGOS } from '~/constants/logos';
|
|||
import { loadMapFromPath } from '../map/sagas';
|
||||
import { mapClicked, mapSetRoute } from '../map/actions';
|
||||
import { MAP_ACTIONS } from '../map/constants';
|
||||
import { OsrmRouter } from '~/utils/osrm';
|
||||
import { OsrmRouter } from '~/utils/map/OsrmRouter';
|
||||
import path from 'ramda/es/path';
|
||||
import { MainMap } from '~/constants/map';
|
||||
import { EDITOR_INITIAL_STATE } from '.';
|
||||
|
@ -236,7 +236,7 @@ function* mapClick({ latlng }: ReturnType<typeof mapClicked>) {
|
|||
|
||||
function* routerSubmit() {
|
||||
const route: ReturnType<typeof selectMapRoute> = yield select(selectMapRoute);
|
||||
const latlngs = path(['_routes', 0, 'coordinates'], OsrmRouter);
|
||||
const latlngs: LatLng[] = path(['_routes', 0, 'coordinates'], OsrmRouter);
|
||||
|
||||
const coordinates = simplify({ map: MainMap, latlngs });
|
||||
|
||||
|
|
|
@ -23,11 +23,6 @@ export const createArrow = (latlng: LatLngLiteral, angle: number): Marker => mar
|
|||
export const arrowClusterIcon = (cluster): DivIcon => {
|
||||
const markers = cluster.getAllChildMarkers();
|
||||
|
||||
// search for nearest marker to cluster (slow)
|
||||
// const nearest = markers.sort((a, b) => (
|
||||
// dist2(a.getLatLng(), cluster.getLatLng()) - dist2(b.getLatLng(), cluster.getLatLng())
|
||||
// ));
|
||||
|
||||
// faster way
|
||||
cluster.setLatLng(markers[markers.length - 1].getLatLng());
|
||||
return markers[markers.length - 1].options.icon;
|
||||
|
|
|
@ -15,7 +15,7 @@ export const DomMarker = DivIcon.extend({
|
|||
|
||||
this._setIconStyles(element, 'icon');
|
||||
|
||||
return element;
|
||||
return element;
|
||||
}
|
||||
});
|
||||
|
|
@ -576,7 +576,6 @@ InteractivePoly.addInitHook(function() {
|
|||
});
|
||||
});
|
||||
|
||||
// export const InteractivePoly = Component;
|
||||
/*
|
||||
events:
|
||||
vertexdragstart,
|
|
@ -1,7 +1,7 @@
|
|||
import { Marker } from 'leaflet';
|
||||
import * as Routing from 'leaflet-routing-machine/src/index';
|
||||
import { CLIENT } from '~/config/frontend';
|
||||
import { DomMarker } from '~/utils/DomMarker';
|
||||
import { DomMarker } from '~/utils/map/DomMarker';
|
||||
import { MainMap } from '~/constants/map';
|
||||
|
||||
const createWaypointMarker = (): DomMarker => {
|
Loading…
Add table
Add a link
Reference in a new issue