more typescripting

This commit is contained in:
muerwre 2019-02-20 17:52:42 +07:00
parent 5699a7abb7
commit 628b96c4ab
7 changed files with 144 additions and 103 deletions

View file

@ -3,7 +3,7 @@ import classnames from 'classnames';
interface Props { interface Props {
value: string; value: string;
onChange: EventHandlerNonNull; onChange: (text: string) => void;
} }
type State = { type State = {

View file

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { ISticker, STICKERS } from '$constants/stickers'; import { STICKERS } from '$constants/stickers';
type Props = { type Props = {
set: string, set: string,

View file

@ -1,6 +1,6 @@
// Стикеры // Стикеры
// import L from "leaflet"; // import L from "leaflet";
export interface ISticker { export interface IStickerItem {
off: number, off: number,
title: string, title: string,
title_long: string, title_long: string,
@ -11,7 +11,7 @@ export interface IStickerPack {
url: string, url: string,
size: number, size: number,
layers: { layers: {
[x: string]: ISticker, [x: string]: IStickerItem,
} }
} }

View file

@ -1,7 +1,7 @@
import { Map } from '$modules/Map'; import { Map } from '$modules/Map';
import { Poly } from '$modules/Poly'; import { Poly } from '$modules/Poly';
import { MODES } from '$constants/modes'; import { MODES } from '$constants/modes';
import { Stickers } from '$modules/Stickers'; import { ILatLng, Stickers } from '$modules/Stickers';
import { Router } from '$modules/Router'; import { Router } from '$modules/Router';
import { DEFAULT_LOGO, ILogos, LOGOS } from '$constants/logos'; import { DEFAULT_LOGO, ILogos, LOGOS } from '$constants/logos';
@ -32,17 +32,18 @@ interface IEditor {
router: Router; router: Router;
logo: keyof ILogos; logo: keyof ILogos;
owner: string; owner: { id: string };
initialData: { initialData: {
version: number, version: number,
title: string, title: string,
owner: string, owner: { id: string },
address: string, address: string,
path: any, path: any,
route: any, route: any,
stickers: any, stickers: any,
provider: string, provider: string,
is_public: boolean, is_public: boolean,
logo: string,
}; };
activeSticker: IRootState['activeSticker']; activeSticker: IRootState['activeSticker'];
mode: IRootState['mode']; mode: IRootState['mode'];
@ -54,7 +55,9 @@ interface IEditor {
toggle?: () => any, toggle?: () => any,
} }
}; };
clickHandlers; clickHandlers: {
[x: string]: (event) => void
};
user: IUser; user: IUser;
} }
@ -63,7 +66,6 @@ export class Editor implements IEditor {
this.logo = DEFAULT_LOGO; this.logo = DEFAULT_LOGO;
this.owner = null; this.owner = null;
this.map = new Map({ container: 'map' }); this.map = new Map({ container: 'map' });
this.initialData = {};
this.activeSticker = {}; this.activeSticker = {};
this.mode = MODES.NONE; this.mode = MODES.NONE;
this.provider = PROVIDERS[DEFAULT_PROVIDER]; this.provider = PROVIDERS[DEFAULT_PROVIDER];
@ -126,20 +128,31 @@ export class Editor implements IEditor {
map.addEventListener('dragstop', () => lockMapClicks(false)); map.addEventListener('dragstop', () => lockMapClicks(false));
} }
map; map: IEditor['map'];
poly; poly: IEditor['poly'];
stickers; stickers: IEditor['stickers'];
router; router: IEditor['router'];
logo = DEFAULT_LOGO; logo: IEditor['logo'] = DEFAULT_LOGO;
owner = null; owner: IEditor['owner'] = null;
initialData; initialData: IEditor['initialData'] = {
activeSticker; version: null,
mode; title: null,
provider; owner: null,
switches; address: null,
clickHandlers; path: null,
user = DEFAULT_USER; route: null,
stickers: null,
provider: null,
is_public: null,
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; getState = (): IRootState => <any>store.getState().user;
@ -192,7 +205,7 @@ export class Editor implements IEditor {
createStickerOnClick = (e) => { createStickerOnClick = (e) => {
if (!e || !e.latlng || !this.activeSticker) return; if (!e || !e.latlng || !this.activeSticker) return;
const { latlng } = e; const { latlng }: { latlng: ILatLng } = e;
this.stickers.createSticker({ latlng, sticker: this.activeSticker.sticker, set: this.activeSticker.set }); this.stickers.createSticker({ latlng, sticker: this.activeSticker.sticker, set: this.activeSticker.set });
this.setActiveSticker(null); this.setActiveSticker(null);
@ -281,7 +294,7 @@ export class Editor implements IEditor {
setData = ({ setData = ({
route = [], stickers = [], owner, title, address, provider = DEFAULT_PROVIDER, logo = DEFAULT_LOGO, is_public, route = [], stickers = [], owner, title, address, provider = DEFAULT_PROVIDER, logo = DEFAULT_LOGO, is_public,
}) => { }: IEditor['initialData']) => {
this.setTitle(title || ''); this.setTitle(title || '');
const { id } = this.getUser(); const { id } = this.getUser();
@ -308,6 +321,7 @@ export class Editor implements IEditor {
this.setPublic(is_public); this.setPublic(is_public);
this.setLogo((logo && LOGOS[DEFAULT_LOGO] && logo) || DEFAULT_LOGO); this.setLogo((logo && LOGOS[DEFAULT_LOGO] && logo) || DEFAULT_LOGO);
this.setProvider((provider && PROVIDERS[provider] && provider) || DEFAULT_PROVIDER); this.setProvider((provider && PROVIDERS[provider] && provider) || DEFAULT_PROVIDER);
if (owner) this.owner = owner; if (owner) this.owner = owner;
}; };
@ -321,19 +335,20 @@ export class Editor implements IEditor {
setInitialData = () => { setInitialData = () => {
const { path } = getUrlData(); const { path } = getUrlData();
const { id } = this.getUser(); const { id } = this.getUser();
const { is_public } = this.getState(); const { is_public, logo } = this.getState();
const { route, stickers, provider } = this.dumpData(); const { route, stickers, provider } = this.dumpData();
this.initialData = { this.initialData = {
version: 2, version: 2,
title: this.getTitle(), title: this.getTitle(),
owner: this.owner, owner: this.owner,
address: (this.owner === id ? path : null), address: (this.owner.id === id ? path : null),
path, path,
route, route,
stickers, stickers,
provider, provider,
is_public, is_public,
logo,
}; };
}; };

View file

@ -9,30 +9,26 @@ import 'leaflet/dist/leaflet.css';
import { PROVIDER } from '$config/frontend'; import { PROVIDER } from '$config/frontend';
import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers'; import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers';
interface IMap { interface Props {
map: MapInterface; container: string
tileLayer: TileLayer;
setProvider: (provider: string) => void;
} }
export class Map implements IMap { export class Map {
constructor({ container }) { constructor({ container }: Props) {
this.map = map(container).setView([55.0153275, 82.9071235], 13); this.map = map(container).setView([55.0153275, 82.9071235], 13);
// todo: change coords? // todo: change coords?
this.tileLayer = tileLayer(PROVIDER.url, {
attribution: 'Независимое Велосообщество',
maxNativeZoom: 18,
maxZoom: 18,
});
this.tileLayer.addTo(this.map); this.tileLayer.addTo(this.map);
} }
map; map: MapInterface;
tileLayer; tileLayer: TileLayer = tileLayer(PROVIDER.url, {
attribution: 'Независимое Велосообщество',
maxNativeZoom: 18,
maxZoom: 18,
});
setProvider = (provider) => { setProvider = (provider: string): void => {
const { url } = (provider && PROVIDERS[provider] && PROVIDERS[provider]) || PROVIDERS[DEFAULT_PROVIDER]; const { url } = (provider && PROVIDERS[provider] && PROVIDERS[provider]) || PROVIDERS[DEFAULT_PROVIDER];
this.tileLayer.setUrl(url); this.tileLayer.setUrl(url);

View file

@ -7,6 +7,8 @@ import * as ReactDOM from 'react-dom';
import { StickerDesc } from '$components/StickerDesc'; import { StickerDesc } from '$components/StickerDesc';
import classnames from 'classnames'; import classnames from 'classnames';
import { getLabelDirection } from '$utils/geom'; import { getLabelDirection } from '$utils/geom';
import { ILatLng } from "$modules/Stickers";
import { IRootState } from "$redux/user/reducer";
const getX = e => ( const getX = e => (
e.touches && e.touches.length > 0 e.touches && e.touches.length > 0
@ -14,7 +16,16 @@ const getX = e => (
: { pageX: e.pageX, pageY: e.pageY } : { pageX: e.pageX, pageY: e.pageY }
); );
interface ISticker {
export interface IStickerDump {
latlng: ILatLng,
sticker: string,
set: IRootState['activeSticker']['set'],
angle?: number,
text?: string,
}
interface IStickerson {
element: HTMLDivElement; element: HTMLDivElement;
stickerImage: HTMLDivElement; stickerImage: HTMLDivElement;
stickerArrow: HTMLDivElement; stickerArrow: HTMLDivElement;
@ -32,10 +43,22 @@ interface ISticker {
lockMapClicks: (x: boolean) => void; lockMapClicks: (x: boolean) => void;
} }
export class Sticker implements ISticker { interface Props {
latlng: ILatLng;
deleteSticker: (sticker: this) => void;
map: Map;
lockMapClicks: (status: boolean) => void;
sticker: IRootState['activeSticker']['sticker'];
set: IRootState['activeSticker']['set'];
triggerOnChange: () => void;
angle?: number;
text?: string;
}
export class Sticker {
constructor({ constructor({
latlng, deleteSticker, map, lockMapClicks, sticker, set, triggerOnChange, angle = 2.2, text = '', latlng, deleteSticker, map, lockMapClicks, sticker, set, triggerOnChange, angle = 2.2, text = '',
}) { }: Props) {
this.text = text; this.text = text;
this.latlng = latlng; this.latlng = latlng;
this.angle = parseFloat(((angle && (angle % Math.PI)) || 2.2).toFixed(2)); this.angle = parseFloat(((angle && (angle % Math.PI)) || 2.2).toFixed(2));
@ -97,15 +120,15 @@ export class Sticker implements ISticker {
this.setAngle(this.angle); this.setAngle(this.angle);
} }
setText = text => { setText = (text: Props['text']): void => {
this.text = text; this.text = text;
}; };
onDelete = () => { onDelete = (): void => {
if (!this.isDragging) this.deleteSticker(this); if (!this.isDragging) this.deleteSticker(this);
}; };
onDragStart = e => { onDragStart = (e): void => {
this.preventPropagations(e); this.preventPropagations(e);
this.marker.dragging.disable(); this.marker.dragging.disable();
@ -121,14 +144,14 @@ export class Sticker implements ISticker {
// this.marker.disableEdit(); // this.marker.disableEdit();
}; };
preventPropagations = e => { preventPropagations = (e): void => {
if (!e || !e.stopPropagation) return; if (!e || !e.stopPropagation) return;
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
}; };
onDragStop = e => { onDragStop = (e): void => {
this.preventPropagations(e); this.preventPropagations(e);
this.marker.dragging.enable(); this.marker.dragging.enable();
@ -144,20 +167,21 @@ export class Sticker implements ISticker {
this.lockMapClicks(false); this.lockMapClicks(false);
}; };
onDrag = e => { onDrag = (e: DragEvent): void => {
this.preventPropagations(e); this.preventPropagations(e);
this.estimateAngle(e); this.estimateAngle(e);
}; };
estimateAngle = e => { estimateAngle = (e): void => {
const { x, y } = this.element.getBoundingClientRect() as DOMRect; const { x, y } = this.element.getBoundingClientRect() as DOMRect;
const { pageX, pageY } = getX(e); const { pageX, pageY } = getX(e);
this.angle = parseFloat(Math.atan2((y - pageY), (x - pageX)).toFixed(2)); this.angle = parseFloat(Math.atan2((y - pageY), (x - pageX)).toFixed(2));
this.setAngle(this.angle); this.setAngle(this.angle);
}; };
setAngle = angle => { setAngle = (angle: Props['angle']): void => {
const direction = getLabelDirection(angle); const direction = getLabelDirection(angle);
if (direction !== this.direction) { if (direction !== this.direction) {
@ -176,7 +200,7 @@ export class Sticker implements ISticker {
this.stickerArrow.style.transform = `rotate(${angle + Math.PI}rad)`; this.stickerArrow.style.transform = `rotate(${angle + Math.PI}rad)`;
}; };
dumpData = () => ({ dumpData = (): IStickerDump => ({
angle: this.angle, angle: this.angle,
latlng: { ...this.marker.getLatLng() }, latlng: { ...this.marker.getLatLng() },
sticker: this.sticker, sticker: this.sticker,
@ -184,27 +208,29 @@ export class Sticker implements ISticker {
text: this.text, text: this.text,
}); });
stopEditing = () => { stopEditing = (): void => {
this.element.className = 'sticker-container inactive'; this.element.className = 'sticker-container inactive';
}; };
startEditing = () => { startEditing = (): void => {
this.element.className = 'sticker-container'; this.element.className = 'sticker-container';
}; };
element = document.createElement('div'); element: HTMLDivElement = document.createElement('div');
stickerImage; stickerImage: HTMLDivElement;
stickerArrow; stickerArrow: HTMLDivElement;
marker; marker: Marker;
text; isDragging: boolean = false;
latlng; direction: string;
angle;
isDragging = false; text: Props['text'];
map; latlng: Props['latlng'];
sticker; angle: Props['angle'];
set; map: Props['map'];
triggerOnChange; sticker: Props['sticker'];
direction; set: Props['set'];
deleteSticker; triggerOnChange: Props['triggerOnChange'];
lockMapClicks;
deleteSticker: Props['deleteSticker'];
lockMapClicks: Props['lockMapClicks'];
} }

View file

@ -1,34 +1,28 @@
import { LayerGroup, layerGroup, Map } from 'leaflet'; import { LayerGroup, layerGroup, Map } from 'leaflet';
import { Sticker } from '$modules/Sticker'; import { IStickerDump, Sticker } from '$modules/Sticker';
import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js';
import { clusterIcon } from '$utils/clusterIcon'; import { clusterIcon } from '$utils/clusterIcon';
import { Editor } from "$modules/Editor"; import { Editor } from "$modules/Editor";
interface IStickers { export interface ILatLng {
clusterLayer: MarkerClusterGroup; lat: number,
map: Map; lng: number,
stickers: Array<Sticker>; }
layer: LayerGroup;
triggerOnChange: () => void; interface Props {
editor: Editor; editor: Editor;
map: Map;
triggerOnChange: () => void;
lockMapClicks: (x: boolean) => void; lockMapClicks: (x: boolean) => void;
} }
export class Stickers implements IStickers { export class Stickers {
constructor({ map, lockMapClicks, triggerOnChange, editor }) { constructor({ map, lockMapClicks, triggerOnChange, editor }: Props) {
this.map = map; this.map = map;
this.triggerOnChange = triggerOnChange; this.triggerOnChange = triggerOnChange;
this.editor = editor; this.editor = editor;
this.clusterLayer = new MarkerClusterGroup({ this.clusterLayer.addTo(map);
spiderfyOnMaxZoom: false,
showCoverageOnHover: false,
zoomToBoundsOnClick: true,
animate: false,
maxClusterRadius: 80,
disableClusteringAtZoom: 13,
iconCreateFunction: clusterIcon,
}).addTo(map);
this.clusterLayer.on('animationend', this.onSpiderify); this.clusterLayer.on('animationend', this.onSpiderify);
@ -40,7 +34,7 @@ export class Stickers implements IStickers {
createSticker = ({ createSticker = ({
latlng, sticker, angle = 2.2, text = '', set latlng, sticker, angle = 2.2, text = '', set
}) => { }: IStickerDump): void => {
const marker = new Sticker({ const marker = new Sticker({
latlng, latlng,
angle, angle,
@ -58,10 +52,9 @@ export class Stickers implements IStickers {
marker.marker.addTo(this.clusterLayer); marker.marker.addTo(this.clusterLayer);
this.triggerOnChange(); this.triggerOnChange();
// marker.marker.enableEdit();
}; };
deleteStickerByReference = ref => { deleteStickerByReference = (ref: Sticker): void => {
const index = this.stickers.indexOf(ref); const index = this.stickers.indexOf(ref);
if (index < 0) return; if (index < 0) return;
@ -72,7 +65,7 @@ export class Stickers implements IStickers {
this.triggerOnChange(); this.triggerOnChange();
}; };
clearAll = () => { clearAll = (): void => {
const target = [...this.stickers]; const target = [...this.stickers];
target.map(sticker => { target.map(sticker => {
this.deleteStickerByReference(sticker); this.deleteStickerByReference(sticker);
@ -80,9 +73,9 @@ export class Stickers implements IStickers {
}); });
}; };
dumpData = () => this.stickers.map(sticker => sticker.dumpData()); dumpData = (): Array<IStickerDump> => this.stickers.map(sticker => sticker.dumpData());
onSpiderify = () => { onSpiderify = (): void => {
// todo: it has markers passed as argument. Update only them. // todo: it has markers passed as argument. Update only them.
if (this.editor.getEditing()) { if (this.editor.getEditing()) {
this.startEditing(); this.startEditing();
@ -91,20 +84,31 @@ export class Stickers implements IStickers {
} }
}; };
startEditing = () => { startEditing = (): void => {
this.stickers.map(sticker => sticker.startEditing()); this.stickers.map(sticker => sticker.startEditing());
}; };
stopEditing = () => { stopEditing = (): void => {
this.stickers.map(sticker => sticker.stopEditing()); this.stickers.map(sticker => sticker.stopEditing());
}; };
clusterLayer; clusterLayer: MarkerClusterGroup = new MarkerClusterGroup({
map; spiderfyOnMaxZoom: false,
stickers; showCoverageOnHover: false,
triggerOnChange; zoomToBoundsOnClick: true,
editor; animate: false,
lockMapClicks; maxClusterRadius: 80,
disableClusteringAtZoom: 13,
iconCreateFunction: clusterIcon,
});
layer = layerGroup(); editor: Props['editor'];
map: Props['map'];
stickers: Array<Sticker> = [];
layer: LayerGroup = layerGroup();
triggerOnChange: Props['triggerOnChange'];
lockMapClicks: Props['lockMapClicks'];
} }