From b8434c32e758d6af01f6d3bb4b491b468e9601ee Mon Sep 17 00:00:00 2001
From: Fedor Katurov <fkaturov@allmax.team>
Date: Wed, 15 Aug 2018 16:18:49 +0700
Subject: [PATCH] stickers with ability to drag them

---
 package-lock.json              |  5 +++
 package.json                   |  1 +
 src/constants/modes.js         |  5 +++
 src/constants/providers.js     | 12 ++++++
 src/constants/stickers.js      | 43 +++++++++++++++++++++
 src/containers/App.jsx         | 44 ++++++++++++++++++---
 src/index.html                 | 15 ++++++++
 src/modules/Editor.js          | 57 +++++++++++++++++++++++++++
 src/modules/Map.js             | 19 +++++++++
 src/modules/Poly.js            | 70 ++++++++++++++++++++++++++++++++++
 src/modules/Sticker.js         | 49 ++++++++++++++++++++++++
 src/modules/Stickers.js        | 40 +++++++++++++++++++
 src/modules/map.js             | 22 -----------
 src/parts/map.js               | 19 +++++----
 src/parts/poly.js              |  2 +-
 src/styles/controlsScreen.js   | 13 +++++++
 src/styles/mapScreen.js        | 60 +++++++++++++++++++++++++++++
 src/utils/leafletDomMarkers.js | 21 ++++++++++
 webpack.config.js              |  2 +-
 19 files changed, 460 insertions(+), 39 deletions(-)
 create mode 100644 src/constants/modes.js
 create mode 100644 src/constants/providers.js
 create mode 100644 src/constants/stickers.js
 create mode 100644 src/index.html
 create mode 100644 src/modules/Editor.js
 create mode 100644 src/modules/Map.js
 create mode 100644 src/modules/Poly.js
 create mode 100644 src/modules/Sticker.js
 create mode 100644 src/modules/Stickers.js
 delete mode 100644 src/modules/map.js
 create mode 100644 src/styles/controlsScreen.js
 create mode 100644 src/styles/mapScreen.js
 create mode 100644 src/utils/leafletDomMarkers.js

diff --git a/package-lock.json b/package-lock.json
index 1f57e5c..241d7ff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7413,6 +7413,11 @@
       "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.3.3.tgz",
       "integrity": "sha512-R9Cu5s0bdEXb9zh0nU17pV00IEvRh4xpWR9g1Oqz17jEDuMtkhy6DoYN1Q5WjvoDMRmq389zDVueUs9A2uWZSg=="
     },
+    "leaflet-editable": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/leaflet-editable/-/leaflet-editable-1.1.0.tgz",
+      "integrity": "sha1-93dZekCoGic/KHtIn9D+XM1gyNA="
+    },
     "levn": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
diff --git a/package.json b/package.json
index bad5f57..d57b7dd 100644
--- a/package.json
+++ b/package.json
@@ -47,6 +47,7 @@
     "clean-webpack-plugin": "^0.1.9",
     "history": "^4.7.2",
     "leaflet": "^1.3.3",
+    "leaflet-editable": "^1.1.0",
     "react": "^16.3.2",
     "react-dom": "^16.3.2",
     "react-hot-loader": "^4.1.1",
diff --git a/src/constants/modes.js b/src/constants/modes.js
new file mode 100644
index 0000000..efa6a0c
--- /dev/null
+++ b/src/constants/modes.js
@@ -0,0 +1,5 @@
+export const MODES = {
+  POLY: 'POLY',
+  STICKERS: 'STICKERS',
+  NONE: 'NONE',
+};
diff --git a/src/constants/providers.js b/src/constants/providers.js
new file mode 100644
index 0000000..7035606
--- /dev/null
+++ b/src/constants/providers.js
@@ -0,0 +1,12 @@
+// Стили карт
+export const providers = {
+  'watercolor': 'http://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg',
+  'darq': 'http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
+  '2gis': 'https://tile1.maps.2gis.com/tiles?x={x}&y={y}&z={z}&v=1',
+  'default': 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
+  'hot': 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
+  'blank': 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
+  'sat': 'http://mt0.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',
+  'ymap': 'https://vec03.maps.yandex.net/tiles?l=map&v=17.04.16-0&x={x}&y={y}&z={z}&scale=1&lang=ru_RU',
+  'ysat': 'https://sat02.maps.yandex.net/tiles?l=sat&v=3.330.0&x={x}&y={y}&z={z}&lang=ru_RU'
+};
diff --git a/src/constants/stickers.js b/src/constants/stickers.js
new file mode 100644
index 0000000..385d040
--- /dev/null
+++ b/src/constants/stickers.js
@@ -0,0 +1,43 @@
+// Стикеры
+import L from "leaflet";
+
+export const stickers = {
+  'objects': {},
+  'layers': L.layerGroup(),
+  'savedata': {},
+  'layer_to_object': {},
+  'src': [
+    {off: 5, title: 'Александр 3', title_long: 'Парк Городское Начало', latlng: [55.01275, 82.92368]},
+    {off: 9, title: 'пл.Калинина', title_long: "пл.Калинина", latlng: [55.06019, 82.91316]},
+    {off: 4, title: 'Мост', title_long: 'Мост', latlng: [55.00511, 82.93073]},
+    {off: 7, title: 'Икея', title_long: "Парковка ТЦ Мега", latlng: [54.96494, 82.93138]},
+    {off: 8, title: 'Бугринка', title_long: "Та самая коса\n(культовое место Усталых Педалек)", latlng: [54.97626, 82.95703]},
+    {off: 10, title: 'ГПНТБ', title_long: "ГПНТБ", latlng: [55.01665, 82.94629]}, // второй ряд
+    {off: 18, title: 'Оперный', title_long: "Оперный театр", latlng: [55.03027, 82.92292]},
+    {off: 1, title: 'Лес', title_long: 'Берёзовая роща', latlng: [55.04572, 82.95]}, // первый ряд
+    {off: 19, title: 'Пусто', title_long: "Пока что пусто 1"},
+    {off: 20, title: 'Пусто', title_long: "Пока что пусто 2"}, // третий ряд
+
+    {off: 2, title: 'Трасса', title_long: 'Дорога'},
+    {off: 3, title: 'Курочка', title_long: 'Курочка'},
+    {off: 6, title: 'Палатка', title_long: 'Палаточный лагерь'},
+    {off: 11, title: 'Фастфуд', title_long: "Двухколёсное ожирение"},
+    {off: 12, title: 'Пивко', title_long: "В Питере - пить!"},
+    {off: 13, title: 'Шаварма', title_long: "Вкусная шаурма"},
+    {off: 14, title: 'Камни', title_long: "Кааааммммуушшшки"},
+    {off: 15, title: 'Болото', title_long: "Пошла ты,\nтрясина грёбаная!"},
+    {off: 16, title: 'Роджер', title_long: "Может не надо?"},
+    {off: 17, title: 'Какашка', title_long: "Нехорошее место"},
+
+    {off: 21, title: 'Старт', title_long: "Старт здесь"},
+    {off: 22, title: '1', title_long: "Первая точка"},
+    {off: 23, title: '2', title_long: "Вторая точка"},
+    {off: 24, title: '3', title_long: "Третья точка"},
+    {off: 25, title: '4', title_long: "Четвёртая точка"},
+    {off: 26, title: '5', title_long: "Пятая точка"},
+    {off: 27, title: '7', title_long: "Шестая точка"},
+    {off: 28, title: 'Финиш', title_long: "Финиш здесь"},
+    {off: 29, title: 'Осторожно!', title_long: "Осторожно!"},
+    {off: 30, title: 'Вопрос', title_long: "Что тут?"}
+  ]
+};
diff --git a/src/containers/App.jsx b/src/containers/App.jsx
index a4a55d8..0defa4e 100644
--- a/src/containers/App.jsx
+++ b/src/containers/App.jsx
@@ -1,16 +1,50 @@
 import React from 'react';
 
-import { Map } from '$modules/map';
-import { MapScreen } from "$styles/mapScreen";
+import { Editor } from '$modules/Editor';
+
+import { MapScreen } from '$styles/mapScreen';
+import { ControlsScreen } from '$styles/controlsScreen';
+import { MODES } from '$constants/modes';
 
 export class App extends React.Component {
+  state = {
+    mode: 'none',
+  };
+
   componentDidMount() {
-    this.map = new Map('map');
+    const container = 'map';
+    const { mode } = this.state;
+
+    this.editor = new Editor({
+      container,
+      mode,
+      setMode: this.setMode,
+    });
   }
 
+  setMode = mode => {
+    this.setState({ mode });
+  };
+
+  startPolyMode = () => this.editor.changeMode(MODES.POLY);
+
+  startStickerMode = () => this.editor.changeMode(MODES.STICKERS);
+
   render() {
+    const { mode } = this.state;
+
     return (
-      <MapScreen />
+      <div>
+        <MapScreen />
+        <ControlsScreen>
+          <button onClick={this.startPolyMode}>
+            {mode === MODES.POLY && '-->'}{MODES.POLY}
+          </button>
+          <button onClick={this.startStickerMode}>
+            {mode === MODES.STICKERS && '-->'}{MODES.STICKERS}
+          </button>
+        </ControlsScreen>
+      </div>
     );
   }
-};
+}
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..444bdef
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <meta name="viewport" content="initial-scale=1, maximum-scale=0.8">
+  <link rel="icon" href="favicon.png?d" type="image/png" />
+
+  <title>@MAP</title>
+
+  <link rel="shortcut icon" href="/favicon.png?wd" type="image/png">
+  <meta property="og:image" content="/misc/vk_preview.png" />
+  <meta content="/misc/vk_preview.png">
+</head>
+<body>
+  <section id="index"></section>
+</body>
diff --git a/src/modules/Editor.js b/src/modules/Editor.js
new file mode 100644
index 0000000..3ed571c
--- /dev/null
+++ b/src/modules/Editor.js
@@ -0,0 +1,57 @@
+import { Map } from '$modules/Map';
+import { Poly } from "$modules/Poly";
+import { MODES } from '$constants/modes';
+import { Stickers } from '$modules/Stickers';
+
+export class Editor {
+  constructor({
+    container,
+    mode,
+    setMode
+  }) {
+    this.map = new Map({ container });
+    this.poly = new Poly({ map: this.map.map });
+    this.stickers = new Stickers({ map: this.map.map });
+
+    this.setMode = setMode;
+    this.mode = mode;
+
+    this.switches = {
+      [MODES.POLY]: {
+        start: this.poly.continue,
+        stop: this.poly.stop,
+      }
+    };
+
+    this.clickHandlers = {
+      [MODES.STICKERS]: this.stickers.createOnClick
+    };
+
+    this.map.map.on('click', this.onClick);
+  }
+
+  changeMode = mode => {
+    if (this.mode === mode) {
+      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 => {
+    if (this.switches[mode] && this.switches[mode].start) this.switches[mode].start();
+  };
+
+  disableMode = mode => {
+    if (this.switches[mode] && this.switches[mode].stop) this.switches[mode].stop();
+  };
+
+  onClick = e => {
+    if (this.clickHandlers[this.mode]) this.clickHandlers[this.mode](e);
+  };
+}
diff --git a/src/modules/Map.js b/src/modules/Map.js
new file mode 100644
index 0000000..ee001d2
--- /dev/null
+++ b/src/modules/Map.js
@@ -0,0 +1,19 @@
+import L from 'leaflet';
+import { providers } from '$constants/providers';
+
+import 'leaflet/dist/leaflet.css';
+import 'leaflet-editable';
+
+export class Map {
+  constructor({ container }) {
+    this.map = L.map(container, { editable: true }).setView([55.0153275, 82.9071235], 13);
+
+    this.tileLayer = L.tileLayer(providers.default, {
+      attribution: 'Независимое Велосообщество',
+      maxNativeZoom: 18,
+      maxZoom: 18,
+    });
+
+    this.tileLayer.addTo(this.map);
+  }
+}
diff --git a/src/modules/Poly.js b/src/modules/Poly.js
new file mode 100644
index 0000000..dd7fabb
--- /dev/null
+++ b/src/modules/Poly.js
@@ -0,0 +1,70 @@
+import L from "leaflet";
+
+const polyStyle = { color: '#ff3333', weight: '5' };
+
+export class Poly {
+  constructor({ map }) {
+    this.poly = L.polyline([], polyStyle);
+    this.latlngs = [];
+    this.poly.addTo(map);
+    this.map = map;
+
+    this.bindEvents();
+  }
+
+  updateMarks = () => {
+    const coords = this.poly.toGeoJSON().geometry.coordinates;
+    this.latlngs = (coords && coords.length && coords.map(([lng, lat]) => ({ lng, lat }))) || [];
+  };
+
+  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:deleted', this.updateMarks);
+    this.map.editTools.addEventListener('editable:vertex:new', this.updateMarks);
+
+    // После удаления точки - продолжить рисование
+    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.options.skipMiddleMarkers = true;
+      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();
+  };
+}
diff --git a/src/modules/Sticker.js b/src/modules/Sticker.js
new file mode 100644
index 0000000..3714514
--- /dev/null
+++ b/src/modules/Sticker.js
@@ -0,0 +1,49 @@
+import L from 'leaflet';
+import 'leaflet-editable';
+
+import { DomMarker } from '$utils/leafletDomMarkers';
+
+export class Sticker {
+  constructor({ latlng, deleteSticker }) {
+    this.isDragging = false;
+
+    this.deleteSticker = deleteSticker;
+    this.element = document.createElement('div');
+
+    const stickerImage = document.createElement('div');
+    stickerImage.innerHTML = '<div class="sticker-label" />';
+
+    const stickerArrow = document.createElement('div');
+    stickerArrow.innerHTML = '<div class="sticker-arrow" />';
+
+    this.element.appendChild(stickerArrow);
+    this.element.appendChild(stickerImage);
+
+    const marker = new DomMarker({
+      element: this.element,
+    });
+
+    this.sticker = L.marker(latlng, { icon: marker });
+
+    stickerImage.addEventListener('mousedown', this.onDragStart);
+    stickerImage.addEventListener('mouseup', this.onDragStop);
+    //
+    // this.sticker.addEventListener('click', this.onDelete);
+  }
+
+  onDelete = () => {
+    if (!this.isDragging) this.deleteSticker(this);
+  };
+
+  onDragStart = e => {
+    this.isDragging = true;
+    this.sticker.disableEdit();
+    console.log('dragStart');
+  };
+
+  onDragStop = e => {
+    this.isDragging = false;
+    this.sticker.enableEdit();
+    console.log('dragStop');
+  }
+}
diff --git a/src/modules/Stickers.js b/src/modules/Stickers.js
new file mode 100644
index 0000000..532c7dc
--- /dev/null
+++ b/src/modules/Stickers.js
@@ -0,0 +1,40 @@
+import L from 'leaflet';
+import { Sticker } from '$modules/Sticker';
+
+export class Stickers {
+  constructor({ map }) {
+    this.map = map;
+    this.layer = L.layerGroup();
+
+    this.stickers = [];
+
+    this.layer.addTo(this.map);
+  }
+
+  createOnClick = e => {
+    if (!e || !e.latlng) return;
+
+    const { latlng } = e;
+    this.createSticker({ latlng });
+  };
+
+  createSticker = ({ latlng }) => {
+    const sticker = new Sticker({
+      latlng,
+      deleteSticker: this.deleteStickerByReference,
+    });
+    this.stickers.push(sticker);
+
+    sticker.sticker.addTo(this.map);
+    sticker.sticker.enableEdit();
+  };
+
+  deleteStickerByReference = ref => {
+    const index = this.stickers.indexOf(ref);
+
+    if (index < 0) return;
+
+    this.map.removeLayer(ref.sticker);
+    this.stickers.splice(index, 1);
+  };
+}
diff --git a/src/modules/map.js b/src/modules/map.js
deleted file mode 100644
index 5eface5..0000000
--- a/src/modules/map.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import L from "leaflet";
-
-import { providers } from "$constants/providers";
-
-import 'leaflet/dist/leaflet.css';
-
-export class Map {
-  constructor(container) {
-    this.map = L.map(container, {
-      editable: true,
-      layers: [
-
-      ]
-    }).setView([55.0153275, 82.9071235], 13);
-
-    this.tileLayer = L.tileLayer(providers.default, {
-      attribution: 'Независимое Велосообщество',
-      maxNativeZoom: 18,
-      maxZoom: 18,
-    }).addTo(this.map);
-  }
-}
diff --git a/src/parts/map.js b/src/parts/map.js
index 462bd7f..8bffded 100644
--- a/src/parts/map.js
+++ b/src/parts/map.js
@@ -1,16 +1,16 @@
-import L from "leaflet";
+import L from 'leaflet';
 
 import 'leaflet-editable';
 import 'leaflet.markercluster';
 import 'leaflet.markercluster.webpack';
 import 'leaflet-geometryutil';
 
-import { mapStyles } from "$constants/mapStyles";
+import { mapStyles } from '$constants/mapStyles';
 
-import { stickers } from "$constants/stickers";
+import { stickers } from '$constants/stickers';
 
-import { updateMarks } from "$utils/updater";
-import { bindPolyEvents, preparePoly } from "$utils/poly";
+import { updateMarks } from '$utils/updater';
+import { bindPolyEvents, preparePoly } from '$utils/poly';
 
 // В этой штуке мы храним точки и выноски, их связки и всё такое
 const point_array = {
@@ -25,15 +25,15 @@ const point_array = {
 
 const points = L.layerGroup();
 
-let mode = "none";
-let current_map_style = 'default';
+let mode = 'none';
+const current_map_style = 'default';
 
 // Интересные места;
 // const places_layer;
 
 export const map = L.map('map', {
   editable: true,
-  layers: [ points, point_array.points, point_array.vectors, stickers.layers ]
+  layers: [points, point_array.points, point_array.vectors, stickers.layers]
 }).setView([55.0153275, 82.9071235], 13);
 
 map.editTools.skipMiddleMarkers = true;
@@ -51,12 +51,11 @@ const prepareMapLayer = provider => {
     attribution: 'Независимое Велосообщество',
     maxNativeZoom: 18,
     maxZoom: 18,
-    //minZoom: 11
+    // minZoom: 11
   }).addTo(map);
 };
 
 const bindMapEvents = () => {
-
   // при масштабировании карты масштабировать стрелки
   // map.on('zoom', function (e) {
   //   $('.arr_mark > div').css('transform', 'scale(' + (map.getZoom()/13) + ')');
diff --git a/src/parts/poly.js b/src/parts/poly.js
index 6e26768..beac0c0 100644
--- a/src/parts/poly.js
+++ b/src/parts/poly.js
@@ -200,7 +200,7 @@ const createPoly = () => {
 };
 
 const restorePoly = latlngs => {
-  const result = L.polyline(createLatLngs(latlngs), {color: 'red'}).addTo(map);
+  const result = L.polyline(createLatLngs(latlngs), { color: 'red' }).addTo(map);
 
   result.enableEdit().continueForward();
   result.editor.options.skipMiddleMarkers = true;
diff --git a/src/styles/controlsScreen.js b/src/styles/controlsScreen.js
new file mode 100644
index 0000000..8d07ef4
--- /dev/null
+++ b/src/styles/controlsScreen.js
@@ -0,0 +1,13 @@
+import styled from 'styled-components';
+
+export const ControlsScreen = styled.div`
+  position: fixed;
+  right: 10px;
+  bottom: 10px;
+  height: 48px;
+  min-width: 120px;
+  background: #333333;
+  border-radius: 2px;
+  z-index: 2;
+  color: white;
+`;
diff --git a/src/styles/mapScreen.js b/src/styles/mapScreen.js
new file mode 100644
index 0000000..5fe381f
--- /dev/null
+++ b/src/styles/mapScreen.js
@@ -0,0 +1,60 @@
+import styled, { css } from 'styled-components';
+
+const vertexMixin = css`
+  .leaflet-vertex-icon, .leaflet-middle-icon {
+    border-radius: 10px;
+    opacity :1;
+    border: none;
+    width: 16px !important;
+    height: 16px !important;margin-left:-8px !important;margin-top:-8px !important;
+    background: transparent;
+  }
+  
+  .leaflet-vertex-icon::after, .leaflet-middle-icon::after {
+    content: ' ';
+    position:absolute;top:4px;left:4px;width:8px;height:8px;
+    background:white;border-radius: 8px;transform:scale(1);
+    transition:transform 150ms;
+  }
+  
+  .leaflet-vertex-icon:hover, .leaflet-middle-icon:hover {
+    opacity: 1 !important;
+  }
+  
+  .leaflet-vertex-icon:hover::after, .leaflet-middle-icon:hover::after,
+  .leaflet-vertex-icon:active::after, .leaflet-middle-icon:active::after  {
+    transform: scale(2);
+    box-shadow: #999 0 0 5px 2px;
+  }
+`;
+
+const stickers = css`
+  .sticker-label {
+    width: 48px;
+    height: 48px;
+    position: absolute;
+    background: white;
+    border-radius: 32px;
+    left: 0;
+    top: 0;
+  }
+  
+  .sticker-arrow {
+    width: 24px;
+    height: 24px;
+    position: absolute;
+    background: red;
+  }
+`;
+
+export const MapScreen = styled.div.attrs({ id: 'map' })`
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  z-index: 1;
+  left: 0;
+  top: 0;
+  
+  ${vertexMixin}
+  ${stickers}
+`;
diff --git a/src/utils/leafletDomMarkers.js b/src/utils/leafletDomMarkers.js
new file mode 100644
index 0000000..19b27e7
--- /dev/null
+++ b/src/utils/leafletDomMarkers.js
@@ -0,0 +1,21 @@
+import L from 'leaflet';
+
+export const DomMarker = L.DivIcon.extend({
+  initialize: function (options) {
+    this.options = options;
+  },
+
+  options: {
+    element: null // a initialized DOM element
+    // same options as divIcon except for html
+  },
+
+  createIcon: function() {
+    const { html, element } = this.options;
+
+    this._setIconStyles(element, 'icon');
+
+    return element;
+  }
+});
+
diff --git a/webpack.config.js b/webpack.config.js
index 25a3c5a..9a81f1d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -13,7 +13,7 @@ const { join } = require('path');
 const htmlPlugin = new HtmlWebPackPlugin({
   template: './src/index.html',
   filename: './index.html',
-  title: 'Ether Corners',
+  title: 'Map',
   hash: false,
 });