typescripted all the modules

This commit is contained in:
muerwre 2019-02-20 15:25:05 +07:00
parent 098b2ce281
commit 5699a7abb7
9 changed files with 224 additions and 298 deletions

View file

@ -1,9 +1,9 @@
import { Map } from '$modules/Map';
import { NewPoly } from '$modules/NewPoly';
import { Poly } from '$modules/Poly';
import { MODES } from '$constants/modes';
import { Stickers } from '$modules/Stickers';
import { Router } from '$modules/Router';
import { DEFAULT_LOGO, LOGOS } from '$constants/logos';
import { DEFAULT_LOGO, ILogos, LOGOS } from '$constants/logos';
import { getUrlData } from '$utils/history';
import { store } from '$redux/store';
@ -23,9 +23,42 @@ import {
import { DEFAULT_PROVIDER, IProvider, PROVIDERS } from '$constants/providers';
import { STICKERS } from '$constants/stickers';
import { IRootState } from "$redux/user/reducer";
import { DEFAULT_USER } from "$constants/auth";
import { DEFAULT_USER, IUser } from "$constants/auth";
export class Editor {
interface IEditor {
map: Map;
poly: Poly;
stickers: Stickers;
router: Router;
logo: keyof ILogos;
owner: string;
initialData: {
version: number,
title: string,
owner: string,
address: string,
path: any,
route: any,
stickers: any,
provider: string,
is_public: boolean,
};
activeSticker: IRootState['activeSticker'];
mode: IRootState['mode'];
provider: IProvider;
switches: {
[x: string]: {
start?: () => any,
stop?: () => any,
toggle?: () => any,
}
};
clickHandlers;
user: IUser;
}
export class Editor implements IEditor {
constructor() {
this.logo = DEFAULT_LOGO;
this.owner = null;
@ -40,13 +73,13 @@ export class Editor {
map: { map }
} = this;
this.poly = new NewPoly({
this.poly = new Poly({
map, routerMoveStart, lockMapClicks, setTotalDist: this.setDistance, triggerOnChange, editor: this,
});
this.stickers = new Stickers({ map, lockMapClicks, triggerOnChange, editor: this });
this.router = new Router({
map, lockMapClicks, setRouterPoints: this.setRouterPoints, changeMode, pushPolyPoints
map, lockMapClicks, setRouterPoints: this.setRouterPoints, pushPolyPoints
});
this.switches = {
@ -93,17 +126,17 @@ export class Editor {
map.addEventListener('dragstop', () => lockMapClicks(false));
}
map; // todo typecheck
poly; // todo typecheck
map;
poly;
stickers;
router;
logo: string | number = DEFAULT_LOGO;
logo = DEFAULT_LOGO;
owner = null;
initialData;
activeSticker: IRootState['activeSticker'];
mode: IRootState['mode'];
provider: IProvider;
activeSticker;
mode;
provider;
switches;
clickHandlers;
user = DEFAULT_USER;
@ -359,6 +392,6 @@ export class Editor {
export const editor = new Editor();
declare let window:any;
declare let window: any;
window.editor = editor;

View file

@ -9,7 +9,13 @@ import 'leaflet/dist/leaflet.css';
import { PROVIDER } from '$config/frontend';
import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers';
export class Map {
interface IMap {
map: MapInterface;
tileLayer: TileLayer;
setProvider: (provider: string) => void;
}
export class Map implements IMap {
constructor({ container }) {
this.map = map(container).setView([55.0153275, 82.9071235], 13);
// todo: change coords?
@ -23,10 +29,10 @@ export class Map {
this.tileLayer.addTo(this.map);
}
map: MapInterface;
tileLayer: TileLayer;
map;
tileLayer;
setProvider = (provider: string): void => {
setProvider = (provider) => {
const { url } = (provider && PROVIDERS[provider] && PROVIDERS[provider]) || PROVIDERS[DEFAULT_PROVIDER];
this.tileLayer.setUrl(url);

View file

@ -1,180 +0,0 @@
import L from 'leaflet';
import 'leaflet-geometryutil';
import { simplify } from '$utils/simplify';
import { findDistance, middleCoord } from '$utils/geom';
import { CLIENT } from '$config/frontend';
import { MODES } from '$constants/modes';
const polyStyle = {
color: 'url(#activePathGradient)',
weight: '6',
markerMid: 'url(#arrow)'
};
// const polyStyle = { color: '#ff3344', weight: '5' };
export class Poly {
constructor({
map, routerMoveStart, lockMapClicks, setTotalDist, triggerOnChange, editor,
}) {
this.poly = L.polyline([], polyStyle);
this.latlngs = [];
this.poly.addTo(map);
this.editor = editor;
this.map = map;
this.routerMoveStart = routerMoveStart;
this.setTotalDist = setTotalDist;
this.triggerOnChange = triggerOnChange;
this.lockMapClicks = lockMapClicks;
this.bindEvents();
this.arrows = new L.LayerGroup().addTo(map);
}
drawArrows = () => {
this.arrows.clearLayers();
const { latlngs } = this;
if (!latlngs || latlngs.length <= 1) return;
latlngs.map((latlng, i) => {
if (i === 0) return;
const mid = middleCoord(latlngs[i], latlngs[i - 1]);
const dist = findDistance(latlngs[i - 1].lat, latlngs[i - 1].lng, latlngs[i].lat, latlngs[i].lng);
if (dist <= 1) return;
const slide = new L.Polyline(
[
latlngs[i - 1],
[mid.lat, mid.lng]
],
{ color: 'none', weight: CLIENT.STROKE_WIDTH }
).addTo(this.arrows);
slide._path.setAttribute('marker-end', 'url(#long-arrow)');
});
};
updateMarks = () => {
const coords = this.poly.toGeoJSON().geometry.coordinates;
this.latlngs = (coords && coords.length && coords.map(([lng, lat]) => ({ lng, lat }))) || [];
const meters = (this.poly && (L.GeometryUtil.length(this.poly) / 1000)) || 0;
const kilometers = (meters && meters.toFixed(1)) || 0;
this.setTotalDist(kilometers);
this.routerMoveStart();
this.drawArrows();
if (coords.length > 1) this.triggerOnChange();
};
preventMissClicks = e => {
const mode = this.editor.getMode();
if (mode === MODES.POLY) return;
e.cancel();
if (mode === MODES.NONE) this.editor.setMode(MODES.POLY);
};
bindEvents = () => {
// Если на карте что-то меняется, пересчитать километражи
this.map.editTools.addEventListener('editable:drawing:mouseup', this.updateMarks);
this.map.editTools.addEventListener('editable:vertex:dragend', this.updateMarks);
this.map.editTools.addEventListener('editable:vertex:mouseup', this.updateMarks);
this.map.editTools.addEventListener('editable:vertex:deleted', this.updateMarks);
this.map.editTools.addEventListener('editable:vertex:new', this.updateMarks);
this.map.editTools.addEventListener('editable:vertex:click', this.preventMissClicks);
this.map.editTools.addEventListener('editable:vertex:dragstart', this.lockMap);
this.map.editTools.addEventListener('editable:vertex:dragstart', this.clearArrows);
// После удаления точки - продолжить рисование
this.map.editTools.addEventListener('editable:vertex:deleted', this.continueForward);
//
// map.editTools.addEventListener('editable:vertex:dragend', e => writeReduxData({ e, updatePolyCoords }));
// map.editTools.addEventListener('editable:vertex:new', e => writeReduxData({ e, updatePolyCoords }));
// map.editTools.addEventListener('editable:vertex:deleted', e => writeReduxData({ e, updatePolyCoords }));
// Продолжить рисование после удаления точки
// map.editTools.addEventListener('editable:vertex:deleted', e => {
// poly.editor.continueForward();
// updateMarks();
// });
// Добавлять точек в полилинию по щелчку
// map.editTools.addEventListener('editable:drawing:click', e => insertVertex({ e, updatePolyCoords }));
// map.editTools.addEventListener('editable:drawing:clicked', () => updateMarks({ updatePolyCoords }));
// Это для точек. При перетаскивании конца указателя тащим точку
// map.editTools.addEventListener('editable:vertex:drag', on_vertex_drag);
// при перетаскивании ручек убирать все отметки километров
// map.editTools.addEventListener('editable:vertex:dragstart', clearKmMarks);
};
continue = () => {
if (this.latlngs && this.latlngs.length) {
this.poly.enableEdit().continueForward();
this.poly.editor.reset();
} else {
this.poly = this.map.editTools.startPolyline();
this.poly.setStyle(polyStyle);
}
};
stop = () => {
if (this.map.editTools) this.map.editTools.stopDrawing();
};
continueForward = () => {
if (!this.poly.editor) return;
this.poly.editor.continueForward();
};
lockMap = () => {
this.lockMapClicks(true);
};
setPoints = latlngs => {
if (!latlngs || latlngs.length <= 1) return;
this.poly.setLatLngs(latlngs);
this.updateMarks();
};
pushPoints = latlngs => {
const { map } = this;
const simplified = simplify({ map, latlngs });
const summary = [
...this.poly.getLatLngs(),
...simplified,
];
this.poly.setLatLngs(summary);
this.poly.enableEdit();
this.poly.editor.reset();
this.updateMarks();
};
clearAll = () => {
this.poly.setLatLngs([]);
// this.poly.disableEdit();
this.updateMarks();
};
clearArrows = () => this.arrows.clearLayers();
dumpData = () => this.latlngs;
get isEmpty() {
return (!this.latlngs || Object.values(this.latlngs).length <= 0);
}
}

View file

@ -1,10 +1,11 @@
import L from 'leaflet';
import L, { Map, LayerGroup, Polyline } from 'leaflet';
import 'leaflet-geometryutil';
import '$utils/EditablePolyline';
import { EditablePolyline } from '$utils/EditablePolyline';
import { simplify } from '$utils/simplify';
import { findDistance, middleCoord } from '$utils/geom';
import { CLIENT } from '$config/frontend';
import { MODES } from '$constants/modes';
import { Editor } from "$modules/Editor";
const polyStyle = {
color: 'url(#activePathGradient)',
@ -12,13 +13,24 @@ const polyStyle = {
markerMid: 'url(#arrow)'
};
export class NewPoly {
interface IPoly {
poly: EditablePolyline;
editor: Editor;
map: Map;
routerMoveStart: () => void;
setTotalDist: (dist: number) => void;
triggerOnChange: () => void;
lockMapClicks: (status: boolean) => void;
arrows: LayerGroup;
}
export class Poly implements IPoly {
constructor({
map, routerMoveStart, lockMapClicks, setTotalDist, triggerOnChange, editor,
}) {
const coordinates = [];
this.poly = L.Polyline.PolylineEditor(coordinates, {
this.poly = new EditablePolyline(coordinates, {
...polyStyle,
maxMarkers: 100,
@ -42,9 +54,8 @@ export class NewPoly {
this.setTotalDist = setTotalDist;
this.triggerOnChange = triggerOnChange;
this.lockMapClicks = lockMapClicks;
// this.bindEvents();
this.arrows = new L.LayerGroup().addTo(map);
this.arrows.addTo(map);
}
setModeOnDrawing = () => {
@ -52,45 +63,46 @@ export class NewPoly {
};
drawArrows = () => {
this.arrows.clearLayers();
const { latlngs } = this;
if (!latlngs || latlngs.length <= 1) return;
latlngs.forEach((latlng, i) => {
if (i === 0) return;
const mid = middleCoord(latlngs[i], latlngs[i - 1]);
const dist = findDistance(latlngs[i - 1].lat, latlngs[i - 1].lng, latlngs[i].lat, latlngs[i].lng);
if (dist <= 1) return;
const slide = new L.Polyline(
[
latlngs[i - 1],
[mid.lat, mid.lng]
],
{ color: 'none', weight: CLIENT.STROKE_WIDTH }
).addTo(this.arrows);
slide._path.setAttribute('marker-end', 'url(#long-arrow)');
});
// todo: fix this
// this.arrows.clearLayers();
// const { latlngs } = this;
//
// if (!latlngs || latlngs.length <= 1) return;
//
// latlngs.forEach((latlng, i) => {
// if (i === 0) return;
//
// const mid = middleCoord(latlngs[i], latlngs[i - 1]);
// const dist = findDistance(latlngs[i - 1].lat, latlngs[i - 1].lng, latlngs[i].lat, latlngs[i].lng);
//
// if (dist <= 1) return;
//
// const slide = new Polyline(
// [
// latlngs[i - 1],
// [mid.lat, mid.lng]
// ],
// { color: 'none', weight: CLIENT.STROKE_WIDTH }
// ).addTo(this.arrows);
//
// // todo: uncomment and fix this:
// // slide._path.setAttribute('marker-end', 'url(#long-arrow)');
// });
};
updateMarks = () => {
// return;
const coords = this.poly.toGeoJSON().geometry.coordinates;
// this.latlngs = (coords && coords.length && coords.map(([lng, lat]) => ({ lng, lat }))) || [];
const meters = (this.poly && (L.GeometryUtil.length(this.poly) / 1000)) || 0;
const kilometers = (meters && meters.toFixed(1)) || 0;
this.setTotalDist(kilometers);
this.routerMoveStart();
this.drawArrows(); // <-- uncomment
if (coords.length > 1) this.triggerOnChange();
// todo: fix this
// const coords = this.poly.toGeoJSON().geometry.coordinates;
//
// const meters = (this.poly && (L.GeometryUtil.length(this.poly) / 1000)) || 0;
// const kilometers = (meters && meters.toFixed(1)) || 0;
//
// this.setTotalDist(kilometers);
// this.routerMoveStart();
//
// this.drawArrows(); // <-- uncomment
//
// if (coords.length > 1) this.triggerOnChange();
};
preventMissClicks = e => {
@ -157,4 +169,13 @@ export class NewPoly {
get isEmpty() {
return (!this.latlngs || Object.values(this.latlngs).length <= 0);
}
poly;
editor;
map;
routerMoveStart;
setTotalDist;
triggerOnChange;
lockMapClicks;
arrows = new LayerGroup();
}

View file

@ -1,24 +1,38 @@
import L from 'leaflet';
import Routing from 'leaflet-routing-machine/src/index';
import { Marker } from 'leaflet';
import * as Routing from 'leaflet-routing-machine/src/index';
import { CLIENT } from '$config/frontend';
import { DomMarker } from '$utils/DomMarker';
export class Router {
interface ILatLng {
lat: number, lng: number
}
interface IWaypoint {
latLng: ILatLng
}
interface IRouter {
waypoints: Array<IWaypoint>;
lockMapClicks: (status: boolean) => void;
setRouterPoints: (count: void) => void;
pushPolyPoints: (coordinates: Array<{ lat: number, lng: number }>) => void;
router: Routing;
clearAll: () => void;
}
export class Router implements IRouter {
constructor({
map, lockMapClicks, setRouterPoints, pushPolyPoints
map, lockMapClicks, setRouterPoints, pushPolyPoints
}) {
this.waypoints = [];
this.lockMapClicks = lockMapClicks;
this.setRouterPoints = setRouterPoints;
// this.changeMode = changeMode;
this.pushPolyPoints = pushPolyPoints;
const routeLine = r => Routing.line(r, {
styles: [
{ color: 'white', opacity: 0.8, weight: 6 },
{
color: '#4597d0', opacity: 1, weight: 4, dashArray: '15,10'
}
{ color: '#4597d0', opacity: 1, weight: 4, dashArray: '15,10' }
],
addWaypoints: true,
}).on('linetouched', this.lockPropagations);
@ -33,7 +47,7 @@ export class Router {
},
show: false,
plan: Routing.plan([], {
createMarker: (i, wp) => L.marker(wp.latLng, {
createMarker: (i, wp) => new Marker(wp.latLng, {
draggable: true,
icon: this.createWaypointMarker(),
}),
@ -43,18 +57,14 @@ export class Router {
}).on('waypointschanged', this.updateWaypointsCount);
this.router.addTo(map);
// this.router._line.on('mousedown', console.log);
}
// changeMode = value => store.dispatch(setMode(value));
//
pushWaypointOnClick = ({ latlng: { lat, lng } }) => {
pushWaypointOnClick = ({ latlng: { lat, lng } }: { latlng: ILatLng }): void => {
const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
this.router.setWaypoints([...waypoints, { lat, lng }]);
};
createWaypointMarker = () => {
createWaypointMarker = (): DomMarker => {
const element = document.createElement('div');
element.addEventListener('mousedown', this.lockPropagations);
@ -65,13 +75,13 @@ export class Router {
className: 'router-waypoint',
});
};
//
lockPropagations = () => {
lockPropagations = (): void => {
window.addEventListener('mouseup', this.unlockPropagations);
this.lockMapClicks(true);
};
//
unlockPropagations = e => {
unlockPropagations = (e): void => {
if (e && e.preventPropagations) {
e.preventDefault();
e.preventPropagations();
@ -81,7 +91,7 @@ export class Router {
setTimeout(() => this.lockMapClicks(false), 300);
};
startFrom = latlngs => {
startFrom = (latlngs: ILatLng): void => {
const waypoints = this.router.getWaypoints();
if (waypoints && waypoints.length) {
@ -93,9 +103,9 @@ export class Router {
this.router.setWaypoints([{ ...latlngs }]);
};
moveStart = latlng => {
moveStart = (latlng: ILatLng): void => {
const waypoints = this.router.getWaypoints();
const { latLng } = (waypoints[0] || {});
const { latLng }: { latLng: ILatLng } = (waypoints[0] || {});
if (!latLng || !latlng) return;
@ -111,18 +121,16 @@ export class Router {
this.router.setWaypoints(waypoints);
};
updateWaypointsCount = () => {
updateWaypointsCount = (): void => {
const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
this.setRouterPoints(waypoints.length);
};
cancelDrawing = () => {
cancelDrawing = (): void => {
this.router.setWaypoints([]);
// this.router.
// this.changeMode(MODES.NONE);
};
submitDrawing = () => {
submitDrawing = (): void => {
const [route] = this.router._routes;
if (!route) return;
@ -136,7 +144,13 @@ export class Router {
// this.router.setWaypoints(waypoints[waypoints.length - 1]);
};
clearAll = () => {
clearAll = (): void => {
this.router.setWaypoints([]);
}
};
waypoints: Array<IWaypoint> = [];
lockMapClicks: (status: boolean) => void;
setRouterPoints: (count: void) => void;
pushPolyPoints: (coordinates: Array<{ lat: number, lng: number }>) => void;
router: Routing;
}

View file

@ -3,7 +3,7 @@ import * as React from 'react';
import { DomMarker } from '$utils/DomMarker';
import { STICKERS } from '$constants/stickers';
import ReactDOM from 'react-dom';
import * as ReactDOM from 'react-dom';
import { StickerDesc } from '$components/StickerDesc';
import classnames from 'classnames';
import { getLabelDirection } from '$utils/geom';
@ -14,14 +14,31 @@ const getX = e => (
: { pageX: e.pageX, pageY: e.pageY }
);
export class Sticker {
interface ISticker {
element: HTMLDivElement;
stickerImage: HTMLDivElement;
stickerArrow: HTMLDivElement;
marker: Marker;
text: string;
latlng: { lat: number, lng: number };
angle: number;
isDragging: boolean;
map: Map;
sticker: string;
set: string;
triggerOnChange: () => void;
direction: string;
deleteSticker: (sticker: this) => void;
lockMapClicks: (x: boolean) => void;
}
export class Sticker implements ISticker {
constructor({
latlng, deleteSticker, map, lockMapClicks, sticker, set, triggerOnChange, angle = 2.2, text = '',
}) {
this.text = text;
this.latlng = latlng;
this.angle = parseFloat(((angle && (angle % Math.PI)) || 2.2).toFixed(2));
this.isDragging = false;
this.map = map;
this.sticker = sticker;
this.set = set;
@ -30,8 +47,6 @@ export class Sticker {
this.deleteSticker = deleteSticker;
this.lockMapClicks = lockMapClicks;
this.element = document.createElement('div');
ReactDOM.render(
<React.Fragment>
<div
@ -177,19 +192,19 @@ export class Sticker {
this.element.className = 'sticker-container';
};
element: HTMLDivElement;
stickerImage: HTMLDivElement;
stickerArrow: HTMLDivElement;
marker: Marker;
text: string;
latlng: { lat: number, lng: number };
angle: number;
isDragging: boolean;
map: Map;
sticker: string;
set: string;
triggerOnChange: () => void;
direction: string;
deleteSticker: (sticker: this) => void;
lockMapClicks: (x: boolean) => void;
element = document.createElement('div');
stickerImage;
stickerArrow;
marker;
text;
latlng;
angle;
isDragging = false;
map;
sticker;
set;
triggerOnChange;
direction;
deleteSticker;
lockMapClicks;
}

View file

@ -1,16 +1,26 @@
import L, { layerGroup } from 'leaflet';
import { LayerGroup, layerGroup, Map } from 'leaflet';
import { Sticker } from '$modules/Sticker';
import 'leaflet.markercluster';
import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js';
import { clusterIcon } from '$utils/clusterIcon';
import { Editor } from "$modules/Editor";
export class Stickers {
interface IStickers {
clusterLayer: MarkerClusterGroup;
map: Map;
stickers: Array<Sticker>;
layer: LayerGroup;
triggerOnChange: () => void;
editor: Editor;
lockMapClicks: (x: boolean) => void;
}
export class Stickers implements IStickers {
constructor({ map, lockMapClicks, triggerOnChange, editor }) {
this.map = map;
this.layer = layerGroup();
this.triggerOnChange = triggerOnChange;
this.editor = editor;
this.clusterLayer = L.markerClusterGroup({
this.clusterLayer = new MarkerClusterGroup({
spiderfyOnMaxZoom: false,
showCoverageOnHover: false,
zoomToBoundsOnClick: true,
@ -90,4 +100,11 @@ export class Stickers {
};
clusterLayer;
map;
stickers;
triggerOnChange;
editor;
lockMapClicks;
layer = layerGroup();
}

View file

@ -11,7 +11,6 @@ import {
import {
hideRenderer,
searchPutRoutes,
searchSetDistance,
searchSetLoading,
setActiveSticker,
setAddress,
@ -32,7 +31,6 @@ import {
setAddressOrigin,
setProvider,
changeProvider,
openMapDialog,
setSaveLoading,
mapsSetShift, searchChangeDistance,
} from '$redux/user/actions';
@ -246,7 +244,7 @@ function* setActiveStickerSaga({ activeSticker }: { type: string, activeSticker:
return true;
}
function* setLogoSaga({ logo }: { type: string, logo: keyof ILogos }) {
function* setLogoSaga({ logo }: { type: string, logo: string }) {
const { mode } = yield select(getState);
editor.logo = logo;

View file

@ -1,6 +1,6 @@
import L from 'leaflet';
L.Polyline.polylineEditor = L.Polyline.extend({
const EditablePolyline = L.Polyline.polylineEditor = L.Polyline.extend({
_prepareMapIfNeeded() {
const that = this;
that._changed = false;
@ -14,7 +14,7 @@ L.Polyline.polylineEditor = L.Polyline.extend({
this._map._editablePolylinesEnabled = true;
// Click anywhere on map to add a new point-polyline:
if (this._options.newPolylines) {
if (this._options && this._options.newPolylines) {
that._map.on('dblclick', (event) => {
// console.log(`click, target=${event.target == that._map} type=${event.type}`);
if (that._map.isEditablePolylinesBusy()) { return; }
@ -778,3 +778,5 @@ L.Polyline.PolylineEditor = (latlngs, options, contexts, polylineNo) => {
return result;
};
export { EditablePolyline };