diff --git a/package-lock.json b/package-lock.json index 0d2bfc6..581dc5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11923,6 +11923,11 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typeface-montserrat": { + "version": "0.0.54", + "resolved": "https://registry.npmjs.org/typeface-montserrat/-/typeface-montserrat-0.0.54.tgz", + "integrity": "sha512-Typhap0PWT299+Va0G/8ZtycHMXrH4gBWKfiW977KEBx5rXUUCa70gvqLx1fdA0WAo6bhSAQmo8uc+QFAmjPww==" + }, "ua-parser-js": { "version": "0.7.18", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz", @@ -12993,6 +12998,11 @@ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", "dev": true }, + "wfk-montserrat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wfk-montserrat/-/wfk-montserrat-1.0.0.tgz", + "integrity": "sha512-XgRPn20Qp20BHs1UXyvMVQgblHSwl3bMhrNeXF921fqxIOT0ZPMynqWa60q5wU5RkRTxQfZsgv0Rt6prMSpvhQ==" + }, "whatwg-fetch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", diff --git a/package.json b/package.json index d39618c..b4ff768 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,9 @@ "scrypt": "^6.0.3", "styled-components": "^3.2.6", "styled-theming": "^2.2.0", - "webpack-git-hash": "^1.0.2" + "typeface-montserrat": "0.0.54", + "webpack-git-hash": "^1.0.2", + "wfk-montserrat": "^1.0.0" }, "flow-coverage-report": { "includeGlob": [ diff --git a/src/components/Fills.jsx b/src/components/Fills.jsx index faf3146..084c79b 100644 --- a/src/components/Fills.jsx +++ b/src/components/Fills.jsx @@ -11,7 +11,7 @@ export const Fills = () => ( - + diff --git a/src/components/panels/EditorDialog.jsx b/src/components/panels/EditorDialog.jsx new file mode 100644 index 0000000..cd2fc14 --- /dev/null +++ b/src/components/panels/EditorDialog.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { MODES } from '$constants/modes'; + +import { RouterHelper } from '$components/router/RouterHelper'; + +export const EditorDialog = ({ mode, routerPoints }) => { + const showDialog = (mode === MODES.ROUTER); + return ( + showDialog && +
+ { mode === MODES.ROUTER && } +
+ ); +}; diff --git a/src/components/panels/EditorPanel.jsx b/src/components/panels/EditorPanel.jsx index 58e3152..f8def92 100644 --- a/src/components/panels/EditorPanel.jsx +++ b/src/components/panels/EditorPanel.jsx @@ -2,7 +2,8 @@ import React from 'react'; import { MODES } from '$constants/modes'; import classnames from 'classnames'; -import { Icon } from '$components/Icon'; +import { Icon } from '$components/panels/Icon'; +import { EditorDialog } from '$components/panels/EditorDialog'; export class EditorPanel extends React.PureComponent { startPolyMode = () => this.props.editor.changeMode(MODES.POLY); @@ -14,35 +15,69 @@ export class EditorPanel extends React.PureComponent { startShotterMode = () => this.props.editor.changeMode(MODES.SHOTTER); render() { - const { mode } = this.props; + const { mode, routerPoints } = this.props; return ( -
- - - +
- + + +
+
+ +
+
+ +
+
+ + + + +
+ +
+ +
+ + + +
+ +
); } diff --git a/src/components/Icon.jsx b/src/components/panels/Icon.jsx similarity index 66% rename from src/components/Icon.jsx rename to src/components/panels/Icon.jsx index cb59aae..81f731d 100644 --- a/src/components/Icon.jsx +++ b/src/components/panels/Icon.jsx @@ -2,7 +2,7 @@ import React from 'react'; import sprite from '$sprites/icon.svg'; export const Icon = ({ icon, size = 32 }) => ( - + ); diff --git a/src/components/router/RouterHelper.jsx b/src/components/router/RouterHelper.jsx new file mode 100644 index 0000000..07b139b --- /dev/null +++ b/src/components/router/RouterHelper.jsx @@ -0,0 +1,54 @@ +import React from 'react'; + +const noPoints = () => ( +
+
+
Укажите на карте первую точку маршрута
+
Путь прокладывается по улицам, тротуарам и тропинкам
+
+
+
+ Отмена +
+
+
+); + +const firstPoint = () => ( +
+
+
Укажите на карте конечную точку маршрута
+
Вы сможете добавить уточняющие точки
+
+
+
+ Отмена +
+
+
+); + +const draggablePoints = () => ( +
+
+
Продолжите маршрут, щелкая по карте
+
Потяните линию, чтобы указать промежуточные точки
+
+
+
+ Отмена +
+
+ Применить +
+
+
+); + +export const RouterHelper = ({ routerPoints }) => ( +
+ { !routerPoints && noPoints() } + { routerPoints === 1 && firstPoint() } + { routerPoints >= 2 && draggablePoints() } +
+); diff --git a/src/containers/App.jsx b/src/containers/App.jsx index c0e5f0f..8904bb7 100644 --- a/src/containers/App.jsx +++ b/src/containers/App.jsx @@ -7,37 +7,38 @@ import { Fills } from '$components/Fills'; export class App extends React.Component { state = { mode: 'none', - editor: null, + routerPoints: 0, }; - componentDidMount() { - const container = 'map'; - const { mode } = this.state; - - const editor = new Editor({ - container, - mode, - setMode: this.setMode, - }); - - this.setState({ editor }) - } - setMode = mode => { this.setState({ mode }); }; + setRouterPoints = routerPoints => { + this.setState({ routerPoints }); + }; + + editor = new Editor({ + container: 'map', + mode: this.state.mode, + setMode: this.setMode, + setRouterPoints: this.setRouterPoints, + }); render() { const { - state: { mode, editor }, + editor, + state: { mode, routerPoints }, } = this; return (
-
- +
); } diff --git a/src/index.html b/src/index.html index fde3250..0db381b 100644 --- a/src/index.html +++ b/src/index.html @@ -11,5 +11,7 @@ +
+
diff --git a/src/index.js b/src/index.js index 58bcda2..93bc825 100644 --- a/src/index.js +++ b/src/index.js @@ -3,8 +3,7 @@ import ReactDOM from 'react-dom'; import { App } from '$containers/App'; -import '$styles/map.less'; -import '$styles/controls.less'; +import '$styles/main.less'; export const Index = () => ( diff --git a/src/modules/Editor.js b/src/modules/Editor.js index 216d729..68ddf8d 100644 --- a/src/modules/Editor.js +++ b/src/modules/Editor.js @@ -9,7 +9,8 @@ export class Editor { constructor({ container, mode, - setMode + setMode, + setRouterPoints, }) { this.map = new Map({ container }); @@ -17,7 +18,7 @@ export class Editor { this.poly = new Poly({ map, routerMoveStart, lockMapClicks }); this.stickers = new Stickers({ map, lockMapClicks }); - this.router = new Router({ map, lockMapClicks }); + this.router = new Router({ map, lockMapClicks, setRouterPoints }); this.shotter = new Shotter({ map }); this.setMode = setMode; @@ -91,7 +92,7 @@ export class Editor { if (!latlngs || !latlngs.length) return; - this.router.startFrom(latlngs.pop()); + this.router.startFrom(latlngs[latlngs.length - 1]); }; routerMoveStart = () => { diff --git a/src/modules/Map.js b/src/modules/Map.js index ce5e935..17406d8 100644 --- a/src/modules/Map.js +++ b/src/modules/Map.js @@ -8,7 +8,7 @@ export class Map { constructor({ container }) { this.map = map(container, { editable: true }).setView([55.0153275, 82.9071235], 13); - this.tileLayer = tileLayer(providers.default, { + this.tileLayer = tileLayer(providers.dgis, { attribution: 'Независимое Велосообщество', maxNativeZoom: 18, maxZoom: 18, diff --git a/src/modules/Poly.js b/src/modules/Poly.js index aa58dbf..ab9fa96 100644 --- a/src/modules/Poly.js +++ b/src/modules/Poly.js @@ -1,6 +1,7 @@ import { polyline } from "leaflet"; -const polyStyle = { color: 'url(#activePathGradient)', weight: '5' }; +const polyStyle = { color: 'url(#activePathGradient)', weight: '6' }; +// const polyStyle = { color: '#ff3344', weight: '5' }; export class Poly { constructor({ map, routerMoveStart, lockMapClicks }) { diff --git a/src/modules/Router.js b/src/modules/Router.js index bb07694..bd89613 100644 --- a/src/modules/Router.js +++ b/src/modules/Router.js @@ -4,11 +4,13 @@ import { CONFIG } from '$config'; import { DomMarker } from '$utils/DomMarker'; export class Router { - constructor({ map, lockMapClicks }) { + constructor({ map, lockMapClicks, setRouterPoints }) { 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); @@ -30,14 +32,13 @@ export class Router { routeWhileDragging: true, }), routeWhileDragging: true - }); - // .on('waypointschanged', this.updateWaypointsByEvent); + }).on('waypointschanged', this.updateWaypointsCount); this.router.addTo(map); this.waypoints = []; this.lockMapClicks = lockMapClicks; - + this.setRouterPoints = setRouterPoints; // this.router._line.on('mousedown', console.log); } // @@ -46,22 +47,7 @@ export class Router { console.log('push', waypoints); this.router.setWaypoints([...waypoints, { lat, lng }]); }; - // - // pushWaypoint = latlng => { - // this.waypoints.push(latlng); - // this.updateWaypoints(); - // }; - // - // updateWaypointsByEvent = (e) => { - // console.log('upd', e); - // // this.waypoints = waypoints.map(({ latlng }) => latlng); - // - // }; - // - // updateWaypoints = () => { - // this.router.setWaypoints(this.waypoints); - // }; - // + createWaypointMarker = () => { const element = document.createElement('div'); @@ -119,4 +105,9 @@ export class Router { this.router.setWaypoints(waypoints); }; + + updateWaypointsCount = () => { + const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng); + this.setRouterPoints(waypoints.length); + } } diff --git a/src/sprites/icon.svg b/src/sprites/icon.svg index cb98b8a..726cdc9 100644 --- a/src/sprites/icon.svg +++ b/src/sprites/icon.svg @@ -34,5 +34,6 @@ + diff --git a/src/styles/button.less b/src/styles/button.less new file mode 100644 index 0000000..eb4153a --- /dev/null +++ b/src/styles/button.less @@ -0,0 +1,35 @@ +.button { + background: #444444; + padding: 4px 16px; + height: 18px; + line-height: 1em; + border-radius: 2px; + font-family: sans-serif; + font-size: 14px; + display: inline-flex; + align-items: center; + cursor: pointer; + user-select: none; + + &.primary { + background: #3c78db; + } + + &.danger { + background: #ed2f3b; + } +} + +.button-group { + .button { + border-radius: 0; + + &:first-child { + border-radius: 4px 0 0 4px; + } + + &:last-child { + border-radius: 0 4px 4px 0; + } + } +} diff --git a/src/styles/controls.less b/src/styles/controls.less deleted file mode 100644 index 7038361..0000000 --- a/src/styles/controls.less +++ /dev/null @@ -1,35 +0,0 @@ - -#control-screen { - position: fixed; - right: 10px; - bottom: 0px; - height: 48px; - min-width: 120px; - background: #333333; - z-index: 2; - color: white; - border-radius: 4px 4px 0 0; - padding: 0 4px 0 4px; - - button { - border: none; - background: transparent; - width: 48px; - height: 48px; - padding: 8px; - outline: none; - cursor: pointer; - - &.active { - svg { - fill: url(#activeButtonGradient); - stroke: url(#activeButtonGradient); - } - } - - svg { - fill: white; - stroke: white; - } - } -} diff --git a/src/styles/controlsScreen.js b/src/styles/controlsScreen.js deleted file mode 100644 index 8d07ef4..0000000 --- a/src/styles/controlsScreen.js +++ /dev/null @@ -1,13 +0,0 @@ -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/main.less b/src/styles/main.less new file mode 100644 index 0000000..45ada3c --- /dev/null +++ b/src/styles/main.less @@ -0,0 +1,26 @@ +@import 'map.less'; +@import 'panel.less'; +@import 'router.less'; +@import 'stickers.less'; +@import 'button.less'; + +body { + font-family: sans-serif; + font-size: 14px; +} + +.gray { + opacity: 0.75; +} + +.big { + font-size: 14px; +} + +.small { + font-size: 12px; +} + +.white { + color: white; +} diff --git a/src/styles/map.less b/src/styles/map.less index 1427a16..cc45a2f 100644 --- a/src/styles/map.less +++ b/src/styles/map.less @@ -38,139 +38,3 @@ .leaflet-control-container .leaflet-routing-container-hide { display: none; } - -.router-waypoint { - width: 40px; - height: 40px; - margin-left: -20px; - margin-top: -20px; - outline: none; - z-index: 10001; - - &:after { - content: ' '; - display: block; - width: 20px; - height: 20px; - border-radius: 10px; - box-shadow: 0 0 0 2px #4597d0; - margin-left: 10px; - margin-top: 10px; - } -} - -.sticker-container { - outline: none; - position: relative; - - &:before { - content: ' '; - box-shadow: 0 0 10px 1px #ff3344; - background: #ff334422; - width: 48px; - height: 48px; - left: -24px; - top: -24px; - position: absolute; - border-radius: 40px; - opacity: 0; - transition: opacity 250ms, transform 500ms; - transform: scale(0); - } - - &:hover, &:active { - .sticker-delete { - transform: scale(1); - pointer-events: all; - } - - &:before { - opacity: 1; - transform: scale(1); - } - } -} - -.sticker-label { - width: 48px; - height: 48px; - position: absolute; - background: white; - border-radius: 32px; - left: 0; - top: 0; - outline: none; - - &:after { - content: ' '; - box-shadow: 0 0 0 1px #ff3344; - width: 80px; - height: 80px; - left: -16px; - top: -16px; - position: absolute; - border-radius: 40px; - pointer-events: none; - opacity: 0; - } -} - -.sticker-arrow { - position: absolute; - background: red; - transform-origin: 0 0; - left: 0; - top: 0; - - &:after { - content: ' '; - background: #ff3344; - width: 24px; - height: 24px; - transform-origin: 0 0; - transform: rotate(-45deg); - left: 0; - top: 0; - position: absolute; - } -} - -.sticker-delete { - position: absolute; - width: 24px; - height: 24px; - background: red; - border-radius: 24px; - transition: transform 500ms; - transform: scale(0); - opacity: 1; - pointer-events: none; - - &:hover { - transform: scale(1.2) !important; - } -} - -.leaflet-control-container .leaflet-routing-container-hide { - display: none; -} - -.router-waypoint { - width: 40px; - height: 40px; - margin-left: -20px; - margin-top: -20px; - outline: none; - z-index: 10001; - - &:after { - content: ' '; - display: block; - width: 20px; - height: 20px; - border-radius: 10px; - box-shadow: 0 0 0 2px #4597d0; - margin-left: 10px; - margin-top: 10px; - } -} diff --git a/src/styles/mapScreen.js b/src/styles/mapScreen.js deleted file mode 100644 index 5f0cfb4..0000000 --- a/src/styles/mapScreen.js +++ /dev/null @@ -1,162 +0,0 @@ -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 routerMixin = css` - .leaflet-control-container .leaflet-routing-container-hide { - display: none; - } - - .router-waypoint { - width: 40px; - height: 40px; - margin-left: -20px; - margin-top: -20px; - outline: none; - z-index: 10001; - - ::after { - content: ' '; - display: block; - width: 20px; - height: 20px; - border-radius: 10px; - box-shadow: 0 0 0 2px #4597d0; - margin-left: 10px; - margin-top: 10px; - } - } -`; - -const stickers = css` - .sticker-container { - outline: none; - position: relative; - - ::before { - content: ' '; - box-shadow: 0 0 10px 1px #ff3344; - background: #ff334422; - width: 48px; - height: 48px; - left: -24px; - top: -24px; - position: absolute; - border-radius: 40px; - opacity: 0; - transition: opacity 250ms; - } - - :hover { - .sticker-delete { - transform: scale(1); - pointer-events: all; - } - - ::before { - opacity: 1; - } - } - - - } - - .sticker-label { - width: 48px; - height: 48px; - position: absolute; - background: white; - border-radius: 32px; - left: 0; - top: 0; - outline: none; - - ::after { - content: ' '; - box-shadow: 0 0 0 1px #ff3344; - width: 80px; - height: 80px; - left: ${24 - 40}px; - top: ${24 - 40}px; - position: absolute; - border-radius: 40px; - pointer-events: none; - opacity: 0; - } - } - - .sticker-arrow { - position: absolute; - background: red; - transform-origin: 0 0; - left: 0; - top: 0; - - ::after { - content: ' '; - background: #ff3344; - width: 24px; - height: 24px; - transform-origin: 0 0; - transform: rotate(-45deg); - left: 0; - top: 0; - position: absolute; - } - } - - .sticker-delete { - position: absolute; - width: 24px; - height: 24px; - background: red; - border-radius: 24px; - transition: transform 500ms; - transform: scale(0); - opacity: 1; - pointer-events: none; - - :hover { - transform: scale(1.2) !important; - } - } -`; - -export const MapScreen = styled.div.attrs({ id: 'map' })` - width: 100%; - height: 100%; - position: absolute; - z-index: 1; - left: 0; - top: 0; - - ${vertexMixin} - ${stickers} - ${routerMixin} -`; diff --git a/src/styles/panel.less b/src/styles/panel.less new file mode 100644 index 0000000..ff029de --- /dev/null +++ b/src/styles/panel.less @@ -0,0 +1,92 @@ +.control-bar { + background: #333333; + border-radius: 3px; + display: flex; + box-shadow: rgba(0,0,0,0.3) 0 2px 0, inset rgba(255, 255, 255, 0.05) 1px 1px; +} + +.control-sep { + height: 44px; + background: #222222; + width: 3px; +} + +.panel { + position: fixed; + left: 10px; + bottom: 10px; + z-index: 3; + color: white; + display: flex; + align-items: center; + + &.right { + left: auto; + right: 10px; + } + + button { + border: none; + background: transparent; + padding: 8px; + outline: none; + cursor: pointer; + display: inline-flex; + color: white; + align-items: center; + transition: background-color 500ms; + height: 48px; + box-sizing: border-box; + + &:hover { + background: rgba(100, 100, 100, 0.2); + } + + span { + margin-right: 8px; + font-size: 14px; + font-weight: 200; + margin-left: 8px; + } + + &:last-child { + border-radius: 0 4px 4px 0; + } + + &.active { + svg { + fill: url(#activeButtonGradient); + stroke: url(#activeButtonGradient); + } + } + + &.highlighted { + background: linear-gradient(150deg, #05a4ff, #7137c8); + } + + svg { + fill: white; + stroke: white; + display: inline; + } + } +} + +.panel-separator { + height: 48px; + width: 4px; + background: #222222; +} + +#control-dialog { + background: #222222; + position: absolute; + right: 10px; + bottom: 10px; + border-radius: 3px; + z-index: 2; + color: white; + box-sizing: border-box; + padding-bottom: 48px; + box-shadow: rgba(0,0,0,0.3) 0 2px 0, inset rgba(255, 255, 255, 0.05) 1px 1px; +} diff --git a/src/styles/router.less b/src/styles/router.less new file mode 100644 index 0000000..41ff47e --- /dev/null +++ b/src/styles/router.less @@ -0,0 +1,36 @@ +.router-waypoint { + width: 40px; + height: 40px; + margin-left: -20px; + margin-top: -20px; + outline: none; + z-index: 10001; + + &:after { + content: ' '; + display: block; + width: 20px; + height: 20px; + border-radius: 10px; + box-shadow: 0 0 0 2px #4597d0; + margin-left: 10px; + margin-top: 10px; + } +} + +.router-helper { + width: 500px; + padding: 10px; + font-weight: 200; + font-size: 14px; + display: flex; +} + +.router-helper__text { + width: 100%; +} + +.router-helper__buttons { + display: flex; + align-items: center; +} diff --git a/src/styles/stickers.less b/src/styles/stickers.less new file mode 100644 index 0000000..2201b9f --- /dev/null +++ b/src/styles/stickers.less @@ -0,0 +1,95 @@ +.sticker-container { + outline: none; + position: relative; + + &:before { + content: ' '; + box-shadow: 0 0 10px 1px #ff3344; + background: #ff334422; + width: 48px; + height: 48px; + left: -24px; + top: -24px; + position: absolute; + border-radius: 40px; + opacity: 0; + transition: opacity 250ms, transform 500ms; + transform: scale(0); + } + + &:hover, &:active { + .sticker-delete { + transform: scale(1); + pointer-events: all; + } + + &:before { + opacity: 1; + transform: scale(1); + } + } +} + +.sticker-label { + width: 48px; + height: 48px; + position: absolute; + background: white; + border-radius: 32px; + left: 0; + top: 0; + outline: none; + + &:after { + content: ' '; + box-shadow: 0 0 0 1px #ff3344; + width: 80px; + height: 80px; + left: -16px; + top: -16px; + position: absolute; + border-radius: 40px; + pointer-events: none; + opacity: 0; + } +} + +.sticker-arrow { + position: absolute; + background: red; + transform-origin: 0 0; + left: 0; + top: 0; + + &:after { + content: ' '; + background: #ff3344; + width: 24px; + height: 24px; + transform-origin: 0 0; + transform: rotate(-45deg); + left: 0; + top: 0; + position: absolute; + } +} + +.sticker-delete { + position: absolute; + width: 24px; + height: 24px; + background: red; + border-radius: 24px; + transition: transform 500ms; + transform: scale(1); + opacity: 1; + pointer-events: none; + + &:hover { + transform: scale(1.2) !important; + } +} + +.leaflet-control-container .leaflet-routing-container-hide { + display: none; +}