map_list: initial

urls: editing now handled through urls
This commit is contained in:
muerwre 2018-12-05 14:16:23 +07:00
parent 3771e5d338
commit 0d9bad9095
23 changed files with 386 additions and 68 deletions

View file

@ -2,17 +2,17 @@
module.system.node.resolve_dirname=node_modules module.system.node.resolve_dirname=node_modules
module.system.node.resolve_dirname=src module.system.node.resolve_dirname=src
module.name_mapper='^$redux/\([-a-zA-Z0-9$_/]+\)$' -> '<PROJECT_ROOT>/src/redux/\1' module.name_mapper='^$redux/\([-a-zA-Z0-9$_/]+\)$' -> '<PROJECT_ROOT>/src/redux/\1'
module.name_mapper='^components\/\(.*\)$' ->'<PROJECT_ROOT>/src/components/\1' module.name_mapper='^$config/\([-a-zA-Z0-9$_/]+\)$' -> '<PROJECT_ROOT>/config/\1'
module.name_mapper='^containers\/\(.*\)$' ->'<PROJECT_ROOT>/src/containers/\1' module.name_mapper='^$components\/\(.*\)$' ->'<PROJECT_ROOT>/src/components/\1'
module.name_mapper='^constants\/\(.*\)$' ->'<PROJECT_ROOT>/src/constants/\1' module.name_mapper='^$containers\/\(.*\)$' ->'<PROJECT_ROOT>/src/containers/\1'
module.name_mapper='^sprites\/\(.*\)$' ->'<PROJECT_ROOT>/src/sprites/\1' module.name_mapper='^$constants\/\(.*\)$' ->'<PROJECT_ROOT>/src/constants/\1'
module.name_mapper='^config\/\(.*\)$' ->'<PROJECT_ROOT>/src/config/\1' module.name_mapper='^$sprites\/\(.*\)$' ->'<PROJECT_ROOT>/src/sprites/\1'
module.name_mapper='^styles\/\(.*\)$' ->'<PROJECT_ROOT>/src/styles/\1' module.name_mapper='^$config\/\(.*\)$' ->'<PROJECT_ROOT>/src/config/\1'
module.name_mapper='^utils\/\(.*\)$' ->'<PROJECT_ROOT>/src/utils/\1' module.name_mapper='^$styles\/\(.*\)$' ->'<PROJECT_ROOT>/src/styles/\1'
module.name_mapper='^$utils\/\(.*\)$' ->'<PROJECT_ROOT>/src/utils/\1'
[ignore] [ignore]
.*/node_modules .*/node_modules
.*/node_modules/styled-components/.*
[include] [include]
public public

View file

@ -13,6 +13,7 @@ const RouteSchema = new Schema(
owner: { type: Schema.Types.ObjectId, ref: 'User' }, owner: { type: Schema.Types.ObjectId, ref: 'User' },
logo: { type: String, default: 'DEFAULT' }, logo: { type: String, default: 'DEFAULT' },
distance: { type: Number, default: 0 }, distance: { type: Number, default: 0 },
public: { type: Boolean, default: true },
}, },
{ {
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }

View file

@ -1,3 +1,4 @@
const { generateRandomUrl } = require('../auth/guest');
const { Route } = require('../../models'); const { Route } = require('../../models');
module.exports = async (req, res) => { module.exports = async (req, res) => {
@ -9,6 +10,7 @@ module.exports = async (req, res) => {
if (!exists) return res.send({ success: false, mode: 'not_found_2' }); if (!exists) return res.send({ success: false, mode: 'not_found_2' });
const data = exists.toObject(); const data = exists.toObject();
const random_url = await generateRandomUrl();
return res.send({ return res.send({
success: true, success: true,
@ -17,7 +19,8 @@ module.exports = async (req, res) => {
owner: { owner: {
...data.owner, ...data.owner,
id: data.owner._id, id: data.owner._id,
} },
random_url,
}); });
}; };

View file

@ -0,0 +1,35 @@
// @flow
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { RouteRow } from '$components/maps/RouteRow';
import type { Route } from '$constants/types';
type Props = {
routes: { [id: String]: Route },
editing: Boolean,
};
const Component = ({ routes, editing }: Props) => (
<div className="dialog-maplist">
{
Object.keys(routes).map(id => (
<RouteRow
editing={editing}
{...routes[id]}
key={id}
/>
))
}
</div>
);
const mapStateToProps = ({ user: { editing, user: { routes } } }) => ({
routes, editing,
});
const mapDispatchToProps = dispatch => bindActionCreators({
}, dispatch);
export const MapListDialog = connect(mapStateToProps, mapDispatchToProps)(Component);

View file

@ -0,0 +1,38 @@
// @flow
import React from 'react';
import { Icon } from '$components/panels/Icon';
import { pushPath } from '$utils/history';
type Props = {
title: String,
distance: Number,
created_at: String,
_id: String,
editing: Boolean,
};
export const RouteRow = ({
title, distance, created_at, _id, editing
}: Props) => (
<div
className="route-row"
onClick={() => pushPath(`/${_id}/${editing ? 'edit' : ''}`)}
>
<div className="route-title">
{title || _id}
</div>
<div className="route-description">
<span>
<Icon icon="icon-link-1" />
{_id}
</span>
</div>
<div className="route-description">
<span>
<Icon icon="icon-cycle-1" />
{(distance && `${distance} km`) || 'N/A'}
</span>
</div>
</div>
);

View file

@ -4,7 +4,7 @@ import { GuestButton } from '$components/user/GuestButton';
import { DEFAULT_USER, ROLES } from '$constants/auth'; import { DEFAULT_USER, ROLES } from '$constants/auth';
import { UserButton } from '$components/user/UserButton'; import { UserButton } from '$components/user/UserButton';
import { UserMenu } from '$components/user/UserMenu'; import { UserMenu } from '$components/user/UserMenu';
import { setUser, userLogout, takeAShot } from '$redux/user/actions'; import { setUser, userLogout, takeAShot, setDialog } from '$redux/user/actions';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import type { UserType } from '$constants/types'; import type { UserType } from '$constants/types';
@ -12,12 +12,14 @@ import { Icon } from '$components/panels/Icon';
import classnames from 'classnames'; import classnames from 'classnames';
import { CLIENT } from '$config/frontend'; import { CLIENT } from '$config/frontend';
import { DIALOGS } from '$constants/dialogs';
type Props = { type Props = {
user: UserType, user: UserType,
userLogout: Function, userLogout: Function,
setUser: Function, setUser: Function,
takeAShot: Function, setDialog: Function,
dialog: String,
}; };
export class Component extends React.PureComponent<Props, void> { export class Component extends React.PureComponent<Props, void> {
@ -54,6 +56,9 @@ export class Component extends React.PureComponent<Props, void> {
} }
setMenuOpened = () => this.setState({ menuOpened: !this.state.menuOpened }); setMenuOpened = () => this.setState({ menuOpened: !this.state.menuOpened });
openMapsDialog = () => {
this.props.setDialog({ dialog: DIALOGS.MAP_LIST });
};
openOauthFrame = () => { openOauthFrame = () => {
const width = parseInt(window.innerWidth, 10); const width = parseInt(window.innerWidth, 10);
@ -78,7 +83,7 @@ export class Component extends React.PureComponent<Props, void> {
return ( return (
<div> <div>
<div className="panel active"> <div className="panel active panel-user">
<div className="user-panel"> <div className="user-panel">
{ {
!user || user.role === ROLES.guest !user || user.role === ROLES.guest
@ -96,7 +101,7 @@ export class Component extends React.PureComponent<Props, void> {
<div className="control-bar"> <div className="control-bar">
<button <button
className={classnames({ disabled: route_count <= 0 })} className={classnames({ disabled: route_count <= 0 })}
// onClick={this.props.takeAShot} onClick={this.openMapsDialog}
> >
<Icon icon="icon-folder-1" /> <Icon icon="icon-folder-1" />
</button> </button>
@ -108,7 +113,12 @@ export class Component extends React.PureComponent<Props, void> {
} }
const mapStateToProps = ({ user: { user } }) => ({ user }); const mapStateToProps = ({ user: { dialog, user } }) => ({ dialog, user });
const mapDispatchToProps = dispatch => bindActionCreators({ setUser, userLogout, takeAShot }, dispatch); const mapDispatchToProps = dispatch => bindActionCreators({
setUser,
userLogout,
takeAShot,
setDialog,
}, dispatch);
export const UserPanel = connect(mapStateToProps, mapDispatchToProps)(Component); export const UserPanel = connect(mapStateToProps, mapDispatchToProps)(Component);

5
src/constants/dialogs.js Normal file
View file

@ -0,0 +1,5 @@
// @flow
export const DIALOGS = ({
NONE: 'NONE',
MAP_LIST: 'MAP_LIST',
}: { [key: String]: String });

View file

@ -1,4 +1,6 @@
export const MODES = { // @flow
export const MODES = ({
POLY: 'POLY', POLY: 'POLY',
STICKERS: 'STICKERS', STICKERS: 'STICKERS',
ROUTER: 'ROUTER', ROUTER: 'ROUTER',
@ -9,4 +11,4 @@ export const MODES = {
SAVE: 'SAVE', SAVE: 'SAVE',
CONFIRM_CANCEL: 'CONFIRM_CANCEL', CONFIRM_CANCEL: 'CONFIRM_CANCEL',
PROVIDER: 'PROVIDER', PROVIDER: 'PROVIDER',
}; }: { [key: String]: String });

View file

@ -14,3 +14,18 @@ export type UserType = {
photo: String, photo: String,
} }
}; };
type Path = Array<{ lat: Number, lng: Number }>;
type Stickers = Array<Object>;
export type Route = {
_id: String,
title: String,
version: Number,
stickers: Array<Stickers>,
route: Array<Path>,
logo: String,
distance: Number,
created_at: String,
updated_at: String,
}

View file

@ -12,11 +12,15 @@ import { hot } from 'react-hot-loader';
import { Renderer } from '$components/renderer/Renderer'; import { Renderer } from '$components/renderer/Renderer';
import { hideRenderer } from '$redux/user/actions'; import { hideRenderer } from '$redux/user/actions';
import { Cursor } from '$components/Cursor'; import { Cursor } from '$components/Cursor';
import { LeftDialog } from '$containers/LeftDialog';
import { DIALOGS } from '$constants/dialogs';
type Props = { type Props = {
renderer_active: Boolean, renderer_active: Boolean,
hideRenderer: Function, hideRenderer: Function,
mode: String, mode: String,
dialog: String,
dialog_active: Boolean,
} }
const Component = (props: Props) => ( const Component = (props: Props) => (
@ -26,15 +30,20 @@ const Component = (props: Props) => (
<UserPanel /> <UserPanel />
<EditorPanel /> <EditorPanel />
<Cursor mode={props.mode} /> <Cursor mode={props.mode} />
<LeftDialog dialog={props.dialog} dialog_active={props.dialog_active} />
{ props.renderer_active && <Renderer onClick={props.hideRenderer} /> } { props.renderer_active &&
<Renderer onClick={props.hideRenderer} />
}
</div> </div>
); );
const mapStateToProps = ({ user: { mode, dialog, dialog_active, renderer } }) => ({
const mapStateToProps = ({ user }) => ({ renderer_active: renderer.renderer_active,
renderer_active: user.renderer.renderer_active, mode,
mode: user.mode, dialog,
dialog_active,
}); });
const mapDispatchToProps = dispatch => bindActionCreators({ hideRenderer }, dispatch); const mapDispatchToProps = dispatch => bindActionCreators({ hideRenderer }, dispatch);
export const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(Component)); export const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(Component));

View file

@ -0,0 +1,16 @@
// @flow
import React from 'react';
import { DIALOGS } from '$constants/dialogs';
import { MapListDialog } from '$components/dialogs/MapListDialog';
import classnames from 'classnames';
type Props = {
dialog: String,
dialog_active: Boolean,
}
export const LeftDialog = ({ dialog, dialog_active }: Props) => (
<div className={classnames('dialog', { active: dialog_active })}>
{ dialog === DIALOGS.MAP_LIST && <MapListDialog /> }
</div>
);

View file

@ -218,7 +218,6 @@ export class Editor {
this.stickers.clearAll(); this.stickers.clearAll();
}; };
setData = ({ setData = ({
route, stickers, version = 1, owner, title, address route, stickers, version = 1, owner, title, address
}) => { }) => {
@ -272,19 +271,21 @@ export class Editor {
this.setInitialData(); this.setInitialData();
const url = (this.owner && this.owner.id === id) ? path : random_url; // const url = (this.owner && this.owner.id === id) ? path : random_url;
this.owner = { id };
pushPath(`/${url}/edit`); // console.log('PATH IS', path);
// pushPath(`/${url}/edit`);
if (this.poly.latlngs && this.poly.latlngs.length > 1) this.poly.poly.enableEdit(); if (this.poly.latlngs && this.poly.latlngs.length > 1) this.poly.poly.enableEdit();
this.stickers.startEditing(); this.stickers.startEditing();
}; };
stopEditing = () => { stopEditing = () => {
const { path } = getUrlData(); // const { path } = getUrlData();
pushPath(`/${(this.initialData && this.initialData.path) || path}`); // console.log('PATH IS', path, this.initialData.path);
// pushPath(`/${(this.initialData && this.initialData.path) || path}`);
this.poly.poly.disableEdit(); this.poly.poly.disableEdit();
this.stickers.stopEditing(); this.stickers.stopEditing();

View file

@ -6,6 +6,8 @@ import createSagaMiddleware from 'redux-saga';
import { userReducer } from '$redux/user/reducer'; import { userReducer } from '$redux/user/reducer';
import { userSaga } from '$redux/user/sagas'; import { userSaga } from '$redux/user/sagas';
import { createBrowserHistory } from 'history';
import { locationChanged } from '$redux/user/actions';
const userPersistConfig = { const userPersistConfig = {
key: 'user', key: 'user',
@ -42,3 +44,8 @@ export function configureStore() {
return { store, persistor }; return { store, persistor };
} }
export const history = createBrowserHistory();
export const historyListener = history.listen(location => {
store.dispatch(locationChanged(location.pathname));
});

View file

@ -39,3 +39,7 @@ export const takeAShot = () => ({ type: ACTIONS.TAKE_A_SHOT });
export const cropAShot = payload => ({ type: ACTIONS.CROP_A_SHOT, ...payload }); export const cropAShot = payload => ({ type: ACTIONS.CROP_A_SHOT, ...payload });
export const setProvider = provider => ({ type: ACTIONS.SET_PROVIDER, provider }); export const setProvider = provider => ({ type: ACTIONS.SET_PROVIDER, provider });
export const setDialog = ({ dialog, dialog_active }) => ({ type: ACTIONS.SET_DIALOG, dialog });
export const locationChanged = location => ({ type: ACTIONS.LOCATION_CHANGED, location });
export const setReady = ready => ({ type: ACTIONS.SET_READY, ready });

View file

@ -1,4 +1,6 @@
export const ACTIONS = { // @flow
export const ACTIONS = ({
SET_USER: 'SET_USER', SET_USER: 'SET_USER',
USER_LOGOUT: 'USER_LOGOUT', USER_LOGOUT: 'USER_LOGOUT',
@ -38,4 +40,8 @@ export const ACTIONS = {
CROP_A_SHOT: 'CROP_A_SHOT', CROP_A_SHOT: 'CROP_A_SHOT',
SET_PROVIDER: 'SET_PROVIDER', SET_PROVIDER: 'SET_PROVIDER',
};
SET_DIALOG: 'SET_DIALOG',
LOCATION_CHANGED: 'LOCATION_CHANGED',
SET_READY: 'SET_READY',
}: { [key: String]: String });

View file

@ -1,3 +1,4 @@
// @flow
import { createReducer } from 'reduxsauce'; import { createReducer } from 'reduxsauce';
import { ACTIONS } from '$redux/user/constants'; import { ACTIONS } from '$redux/user/constants';
import { DEFAULT_USER } from '$constants/auth'; import { DEFAULT_USER } from '$constants/auth';
@ -5,6 +6,7 @@ import { MODES } from '$constants/modes';
import { DEFAULT_LOGO } from '$constants/logos'; import { DEFAULT_LOGO } from '$constants/logos';
import { TIPS } from '$constants/tips'; import { TIPS } from '$constants/tips';
import { DEFAULT_PROVIDER } from '$constants/providers'; import { DEFAULT_PROVIDER } from '$constants/providers';
import { DIALOGS } from '$constants/dialogs';
const getEstimated = distance => { const getEstimated = distance => {
const time = (distance && (distance / 15)) || 0; const time = (distance && (distance / 15)) || 0;
@ -73,7 +75,18 @@ const setRenderer = (state, { payload }) => ({
const setProvider = (state, { provider }) => ({ ...state, provider }); const setProvider = (state, { provider }) => ({ ...state, provider });
const HANDLERS = { const setDialog = (state, { dialog, dialog_active }) => ({
...state,
dialog: dialog || state.dialog,
dialog_active: typeof dialog_active !== 'undefined' ? dialog_active : !state.dialog_active,
});
const setReady = (state, { ready = true }) => ({
...state,
ready,
});
const HANDLERS = ({
[ACTIONS.SET_USER]: setUser, [ACTIONS.SET_USER]: setUser,
[ACTIONS.SET_EDITING]: setEditing, [ACTIONS.SET_EDITING]: setEditing,
[ACTIONS.SET_CHANGED]: setChanged, [ACTIONS.SET_CHANGED]: setChanged,
@ -96,9 +109,13 @@ const HANDLERS = {
[ACTIONS.SET_RENDERER]: setRenderer, [ACTIONS.SET_RENDERER]: setRenderer,
[ACTIONS.SET_PROVIDER]: setProvider, [ACTIONS.SET_PROVIDER]: setProvider,
};
[ACTIONS.SET_DIALOG]: setDialog,
[ACTIONS.SET_READY]: setReady,
}: { [key: String]: Function });
export const INITIAL_STATE = { export const INITIAL_STATE = {
ready: false,
user: { ...DEFAULT_USER }, user: { ...DEFAULT_USER },
editing: false, editing: false,
mode: MODES.NONE, mode: MODES.NONE,
@ -117,6 +134,9 @@ export const INITIAL_STATE = {
save_overwriting: false, save_overwriting: false,
save_processing: false, save_processing: false,
dialog: DIALOGS.NONE,
dialog_active: false,
renderer: { renderer: {
data: '', data: '',
width: 0, width: 0,

View file

@ -7,7 +7,7 @@ import {
setActiveSticker, setAddress, setActiveSticker, setAddress,
setChanged, setChanged,
setEditing, setEditing,
setMode, setRenderer, setMode, setReady, setRenderer,
setSaveError, setSaveError,
setSaveOverwrite, setSaveSuccess, setTitle, setSaveOverwrite, setSaveSuccess, setTitle,
setUser setUser
@ -62,12 +62,15 @@ function* startEmptyEditorSaga() {
} }
function* startEditingSaga() { function* startEditingSaga() {
yield editor.startEditing(); // yield put(setEditing(true));
yield put(setEditing(true)); // yield editor.startEditing();
const { path } = getUrlData();
yield pushPath(`/${path}/edit`);
} }
function* stopEditingSaga() { function* stopEditingSaga() {
const { changed, editing, mode } = yield select(getState); const { changed, editing, mode } = yield select(getState);
const { path } = getUrlData();
if (!editing) return; if (!editing) return;
if (changed && mode !== MODES.CONFIRM_CANCEL) { if (changed && mode !== MODES.CONFIRM_CANCEL) {
@ -76,40 +79,59 @@ function* stopEditingSaga() {
} }
yield editor.cancelEditing(); yield editor.cancelEditing();
yield put(setMode(MODES.NONE)); yield put(setMode(MODES.NONE));
yield put(setChanged(false)); yield put(setChanged(false));
yield put(setEditing(editor.hasEmptyHistory)); // don't close editor if no previous map yield put(setEditing(editor.hasEmptyHistory)); // don't close editor if no previous map
yield pushPath(`/${path}/`);
}
function* loadMapSaga(path) {
const map = yield call(getStoredMap, { name: path });
if (map) {
yield editor.setData(map);
yield editor.fitDrawing();
yield put(setChanged(false));
}
return map;
} }
function* mapInitSaga() { function* mapInitSaga() {
const { path, mode } = getUrlData(); const { path, mode } = getUrlData();
if (path) { if (path) {
const map = yield call(getStoredMap, { name: path }); const map = yield call(loadMapSaga, path);
// const map = yield call(getStoredMap, { name: path });
if (map) { if (map) {
yield editor.setData(map); // yield editor.setData(map);
yield editor.fitDrawing(); // yield editor.fitDrawing();
yield put(setChanged(false)); // yield put(setChanged(false));
if (mode && mode === 'edit') { if (mode && mode === 'edit') {
yield call(startEditingSaga); yield put(setEditing(true));
editor.startEditing();
// yield call(startEditingSaga); // <-- this is working
// yield put(setEditing(true)); // yield put(setEditing(true));
// editor.startEditing(); // editor.startEditing();
} else { } else {
yield put(setEditing(false)); // yield put(setEditing(false)); // <-- this is working
// yield call(stopEditingSaga); // yield call(stopEditingSaga);
yield put(setEditing(false));
editor.stopEditing(); editor.stopEditing();
} }
return hideLoader(); yield put(setReady(true));
hideLoader();
return true;
} }
} }
return yield call(startEmptyEditorSaga); yield call(startEmptyEditorSaga);
yield put(setReady(true));
return true;
} }
function* authChechSaga() { function* authChechSaga() {
@ -120,7 +142,6 @@ function* authChechSaga() {
if (user) { if (user) {
yield put(setUser(user)); yield put(setUser(user));
return yield call(mapInitSaga); return yield call(mapInitSaga);
} }
} }
@ -294,9 +315,37 @@ function setProviderSaga({ provider }) {
return put(setMode(MODES.NONE)); return put(setMode(MODES.NONE));
} }
export function* userSaga() { function* locationChangeSaga({ location }) {
// ASYNCHRONOUS!!! :-) const { address, editing, ready, user: { id, random_url } } = yield select(getState);
if (!ready) return;
const { path, mode } = getUrlData(location);
if (address !== path) {
const map = yield call(loadMapSaga, path);
if (map && map.owner && mode === 'edit' && map.owner.id !== id) {
pushPath(`/${map.random_url}/edit`);
return;
}
} else if (mode === 'edit' && editor.owner.id !== id) {
pushPath(`/${random_url}/edit`);
return;
}
if (editing !== (mode === 'edit')) {
if (mode === 'edit') {
yield put(setEditing(true));
editor.startEditing();
} else {
yield put(setEditing(false));
editor.stopEditing();
}
}
}
export function* userSaga() {
yield takeLatest(REHYDRATE, authChechSaga); yield takeLatest(REHYDRATE, authChechSaga);
yield takeEvery(ACTIONS.SET_MODE, setModeSaga); yield takeEvery(ACTIONS.SET_MODE, setModeSaga);
@ -322,4 +371,5 @@ export function* userSaga() {
yield takeLatest(ACTIONS.CROP_A_SHOT, cropAShotSaga); yield takeLatest(ACTIONS.CROP_A_SHOT, cropAShotSaga);
yield takeEvery(ACTIONS.SET_PROVIDER, setProviderSaga); yield takeEvery(ACTIONS.SET_PROVIDER, setProviderSaga);
yield takeLatest(ACTIONS.LOCATION_CHANGED, locationChangeSaga);
} }

View file

@ -118,6 +118,22 @@
</g> </g>
</g> </g>
<g id="icon-cycle-2">
<path fill="black" d="M0 0h32v32H0z"/>
<g id="g5604" fill="#fff" transform="matrix(-.36615 0 0 .36615 24.543 7.457)">
<g id="g5527">
<g id="g5495">
<path id="path5483" d="M37.687 24.66c-4.945 0-8.973 4.025-8.973 8.975 0 4.95 4.026 8.974 8.973 8.974 4.95 0 8.977-4.024 8.977-8.974s-4.027-8.975-8.977-8.975zm0 15.384a6.417 6.417 0 0 1-6.409-6.409 6.418 6.418 0 0 1 6.409-6.41 6.417 6.417 0 0 1 6.411 6.41 6.417 6.417 0 0 1-6.411 6.409z"/>
<path id="path5485" d="M23.588 15.501v.01h8.047c-2.375-2.4-5.273-5.251-5.99-5.578-1.257-.575-3.966 1.462-4.547 1.748-.532.283-.954.699-1.043 1.236l-2.112 5.927-6.167.069c-1.811.485-.465 2.065-.465 2.065l3.097.041-.295 1.405-1.803 2.989a8.914 8.914 0 0 0-3.333-.648C4.027 24.765 0 28.792 0 33.741c0 4.947 4.026 8.974 8.977 8.974 4.948 0 8.975-4.026 8.975-8.974a8.956 8.956 0 0 0-3.423-7.038l2.005-3.326.487-2.322 2.41.03c.479-.136.833-.538.912-1.029l2.603-4.074zM15.386 33.74a6.417 6.417 0 0 1-6.409 6.409c-3.534 0-6.411-2.876-6.411-6.409a6.419 6.419 0 0 1 6.411-6.411c.694 0 1.36.114 1.986.32l-3.579 5.939 2.197 1.323 3.607-5.989a6.386 6.386 0 0 1 2.198 4.818z"/>
<path id="path5487" d="M32.24 23.139s2.468-2.578 2.691-2.968c.225-.392.229-.872.007-1.265 0 0-.725-.76-1.771-1.832v.014h-8.949l4.281 3.716-6.367 9.947a1.264 1.264 0 0 0-.104 1.045l2.563 7.692 3.072-.17-1.741-7.787z"/>
<circle id="circle5489" cx="19.703" cy="7.384" r="3.435"/>
<path id="path5491" d="M32.845 5.919c-1.06-.744-2.366-.789-3.123-.034l-1.484 1.487c-.756.756-.711 2.062.03 3.124z"/>
<path id="path5493" d="M33.007 15.317c1.116 1.116 2.73 1.311 3.607.436l1.485-1.488c.877-.876.685-2.491-.434-3.606l-4.081-4.081-4.659 4.658z"/>
</g>
</g>
</g>
</g>
<g id="icon-logo"> <g id="icon-logo">
<path fill="black" stroke="none" d="M0 0h32v32H0z"/> <path fill="black" stroke="none" d="M0 0h32v32H0z"/>
<g transform="matrix(1 0 0 1 2 -1)"> <g transform="matrix(1 0 0 1 2 -1)">
@ -289,28 +305,34 @@
</g> </g>
<g id="icon-poly-4" stroke="none"> <g id="icon-poly-4" stroke="none">
<path stroke-opacity=".941" stroke-width=".265" d="M0 0h32v32H0z" fill="black"/> <path stroke="none" fill="black"/>
<path xmlns="http://www.w3.org/2000/svg" d="M23 8c0 1.1-.9 2-2 2-.18 0-.35-.02-.51-.07l-3.56 3.55c.05.16.07.34.07.52 0 1.1-.9 2-2 2s-2-.9-2-2c0-.18.02-.36.07-.52l-2.55-2.55c-.16.05-.34.07-.52.07s-.36-.02-.52-.07l-4.55 4.56c.05.16.07.33.07.51 0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2c.18 0 .35.02.51.07l4.56-4.55C8.02 9.36 8 9.18 8 9c0-1.1.9-2 2-2s2 .9 2 2c0 .18-.02.36-.07.52l2.55 2.55c.16-.05.34-.07.52-.07s.36.02.52.07l3.55-3.56C19.02 8.35 19 8.18 19 8c0-1.1.9-2 2-2s2 .9 2 2zm0 0c0 1.1-.9 2-2 2-.18 0-.35-.02-.51-.07l-3.56 3.55c.05.16.07.34.07.52 0 1.1-.9 2-2 2s-2-.9-2-2c0-.18.02-.36.07-.52l-2.55-2.55c-.16.05-.34.07-.52.07s-.36-.02-.52-.07l-4.55 4.56c.05.16.07.33.07.51 0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2c.18 0 .35.02.51.07l4.56-4.55C8.02 9.36 8 9.18 8 9c0-1.1.9-2 2-2s2 .9 2 2c0 .18-.02.36-.07.52l2.55 2.55c.16-.05.34-.07.52-.07s.36.02.52.07l3.55-3.56C19.02 8.35 19 8.18 19 8c0-1.1.9-2 2-2s2 .9 2 2z" fill="white" stroke="white" stroke-width="1" transform="translate(4 4)"/> <path xmlns="http://www.w3.org/2000/svg" d="M23 8c0 1.1-.9 2-2 2-.18 0-.35-.02-.51-.07l-3.56 3.55c.05.16.07.34.07.52 0 1.1-.9 2-2 2s-2-.9-2-2c0-.18.02-.36.07-.52l-2.55-2.55c-.16.05-.34.07-.52.07s-.36-.02-.52-.07l-4.55 4.56c.05.16.07.33.07.51 0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2c.18 0 .35.02.51.07l4.56-4.55C8.02 9.36 8 9.18 8 9c0-1.1.9-2 2-2s2 .9 2 2c0 .18-.02.36-.07.52l2.55 2.55c.16-.05.34-.07.52-.07s.36.02.52.07l3.55-3.56C19.02 8.35 19 8.18 19 8c0-1.1.9-2 2-2s2 .9 2 2zm0 0c0 1.1-.9 2-2 2-.18 0-.35-.02-.51-.07l-3.56 3.55c.05.16.07.34.07.52 0 1.1-.9 2-2 2s-2-.9-2-2c0-.18.02-.36.07-.52l-2.55-2.55c-.16.05-.34.07-.52.07s-.36-.02-.52-.07l-4.55 4.56c.05.16.07.33.07.51 0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2c.18 0 .35.02.51.07l4.56-4.55C8.02 9.36 8 9.18 8 9c0-1.1.9-2 2-2s2 .9 2 2c0 .18-.02.36-.07.52l2.55 2.55c.16-.05.34-.07.52-.07s.36.02.52.07l3.55-3.56C19.02 8.35 19 8.18 19 8c0-1.1.9-2 2-2s2 .9 2 2z" fill="white" stroke="white" stroke-width="1" transform="translate(4 4)"/>
</g> </g>
<g id="icon-get-1" stroke="none"> <g id="icon-get-1" stroke="none">
<path stroke-opacity=".941" stroke-width=".265" d="M0 0h32v32H0z" fill="black"/> <path stroke="none" fill="black"/>
<path xmlns="http://www.w3.org/2000/svg" d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z" fill="white" stroke="white" stroke-width="1" transform="translate(4 4)"/> <path xmlns="http://www.w3.org/2000/svg" d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z" fill="white" stroke="white" stroke-width="1" transform="translate(4 4)"/>
</g> </g>
<g id="icon-folder-1" stroke="none"> <g id="icon-folder-1" stroke="none">
<path stroke-opacity=".941" stroke-width=".265" d="M0 0h32v32H0z" fill="black"/> <path stroke="none" fill="black"/>
<path xmlns="http://www.w3.org/2000/svg" d="M9.17 6l2 2H20v10H4V6h5.17M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z" fill="white" stroke="white" stroke-width="0" transform="translate(4 4)"/> <path xmlns="http://www.w3.org/2000/svg" d="M9.17 6l2 2H20v10H4V6h5.17M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z" fill="white" stroke="white" stroke-width="0" transform="translate(4 4)"/>
</g> </g>
<g id="icon-folder-1" stroke="none"> <g id="icon-folder-1" stroke="none">
<path stroke-opacity=".941" stroke-width=".265" d="M0 0h32v32H0z" fill="black"/> <path stroke="none" fill="black"/>
<path xmlns="http://www.w3.org/2000/svg" d="M9.17 6l2 2H20v10H4V6h5.17M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z" fill="white" stroke="white" stroke-width="0" transform="translate(4 4)"/> <path xmlns="http://www.w3.org/2000/svg" d="M9.17 6l2 2H20v10H4V6h5.17M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z" fill="white" stroke="white" stroke-width="0" transform="translate(4 4)"/>
</g> </g>
<g id="icon-cycle-1" stroke="none">
<path stroke="none" fill="black"/>
<path xmlns="http://www.w3.org/2000/svg" d="M15.5 5.5c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zM5 12c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 8.5c-1.9 0-3.5-1.6-3.5-3.5s1.6-3.5 3.5-3.5 3.5 1.6 3.5 3.5-1.6 3.5-3.5 3.5zm5.8-10l2.4-2.4.8.8c1.3 1.3 3 2.1 5.1 2.1V9c-1.5 0-2.7-.6-3.6-1.5l-1.9-1.9c-.5-.4-1-.6-1.6-.6s-1.1.2-1.4.6L7.8 8.4c-.4.4-.6.9-.6 1.4 0 .6.2 1.1.6 1.4L11 14v5h2v-6.2l-2.2-2.3zM19 12c-2.8 0-5 2.2-5 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 8.5c-1.9 0-3.5-1.6-3.5-3.5s1.6-3.5 3.5-3.5 3.5 1.6 3.5 3.5-1.6 3.5-3.5 3.5z" fill="white" stroke="white" stroke-width="0" transform="translate(4 4)"/>
</g>
<g id="icon-link-1" stroke="none">
<path stroke="none" fill="black"/>
<path xmlns="http://www.w3.org/2000/svg" d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z" fill="white" stroke="white" stroke-width="0" transform="translate(4 4)"/>
</g>
</svg> </svg>
</defs> </defs>

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

View file

@ -3,7 +3,6 @@
@router_line: #4597d0; @router_line: #4597d0;
@bar_shadow: rgba(0,0,0,0.3) 0 2px 0, inset rgba(255, 255, 255, 0.05) 1px 1px;
@bar_background: #333333; @bar_background: #333333;
@dialog_background: #222222; @dialog_background: #222222;
@ -21,3 +20,6 @@
@color_primary: #4597d0; @color_primary: #4597d0;
@color_success: #7cd766; @color_success: #7cd766;
@color_danger: #ff3344; @color_danger: #ff3344;
@bar_shadow: rgba(0,0,0,0.3) 0 2px 0, inset rgba(255, 255, 255, 0.05) 1px 1px;
@dialog_shadow: rgba(0,0,0,0.3) 2px 0 0;

73
src/styles/dialogs.less Normal file
View file

@ -0,0 +1,73 @@
.dialog {
background: #222222;
//background: linear-gradient(130deg, #320523, #020d2b);
position: fixed;
left: 0;
top: 0;
width: 360px;
height: 100%;
z-index: 3;
box-shadow: @dialog_shadow;
padding: 10px 10px 72px 10px;
overflow: hidden;
box-sizing: border-box;
transform: translate3d(-100%, 0, 0);
pointer-events: none;
transition: transform 500ms;
&.active {
transform: translate3d(0, 0, 0);
pointer-events: all;
}
&::after {
content: ' ';
height: 100px;
width: 100%;
left: 0;
bottom: 0;
//background: linear-gradient(0deg, rgba(2, 13, 43, 1) 50%, rgba(2,13,43,0));
background: linear-gradient(0deg, rgba(34, 34, 34, 1) 50%, rgba(34, 34, 34, 0));
position: absolute;
z-index: 5;
}
}
.route-row {
margin-bottom: 10px;
background: rgba(255, 255, 255, 0.05);
padding: 10px 10px 5px 10px;
color: white;
user-select: none;
cursor: pointer;
transition: background-color 250ms;
&:hover {
background: rgba(255, 255, 255, 0.1);
}
}
.route-title {
margin-bottom: 5px;
font-weight: bold;
}
.route-description {
font-size: 0.9em;
opacity: 0.3;
margin-bottom: 5px;
svg {
width: 20px;
height: 20px;
fill: white;
vertical-align: text-bottom;
margin-bottom: -2px;
}
span {
padding-right: 10px;
}
}

View file

@ -9,6 +9,7 @@
@import 'user-button.less'; @import 'user-button.less';
@import 'save.less'; @import 'save.less';
@import 'renderer.less'; @import 'renderer.less';
@import 'dialogs.less';
body { body {
font-family: 'Rubik', sans-serif; font-family: 'Rubik', sans-serif;
@ -54,8 +55,8 @@ body {
.cursor-tooltip { .cursor-tooltip {
position: fixed; position: fixed;
top: 2px; top: 4px;
left: 2px; left: 4px;
width: 20px; width: 20px;
height: 20px; height: 20px;
z-index: 10; z-index: 10;

View file

@ -40,7 +40,7 @@
position: fixed; position: fixed;
left: 10px; left: 10px;
bottom: 10px; bottom: 10px;
z-index: 3; z-index: 6;
color: white; color: white;
display: flex; display: flex;
align-items: center; align-items: center;

View file

@ -1,12 +1,10 @@
export const getPath = () => (window.location && window.location.pathname && import { history } from '$redux/store';
window.location.pathname.replace(/^\//, ''));
export const pushPath = url => window.history.pushState(url, 'Редактирование маршрута', url); export const getPath = () => (window.location && window.location.pathname);
export const pushPath = url => history.push(url);
export const getUrlData = () => { export const getUrlData = (url = getPath()) => {
const url = getPath(); const [, path, mode] = url.split('/');
const [path, mode] = url.split('/');
const { host } = window.location; const { host } = window.location;
return { path, mode, host }; return { path, mode, host };