mirror of
https://github.com/muerwre/orchidmap-front.git
synced 2025-04-25 19:16:41 +07:00
basic UI for row editing
This commit is contained in:
parent
3ea920c1f8
commit
32e1b4c3fd
13 changed files with 248 additions and 65 deletions
|
@ -4,15 +4,15 @@ const { generateGuest, generateRandomUrl } = require('./guest');
|
||||||
module.exports = async (req, res) => {
|
module.exports = async (req, res) => {
|
||||||
const { id, token } = req.query;
|
const { id, token } = req.query;
|
||||||
|
|
||||||
const user = await User.findOne({ _id: id, token })
|
const user = await User.findOne({ _id: id, token });
|
||||||
.populate({
|
// .populate({
|
||||||
path: 'routes',
|
// path: 'routes',
|
||||||
select: '_id title distance owner updated_at',
|
// select: '_id title distance owner updated_at',
|
||||||
options: {
|
// options: {
|
||||||
limit: 200,
|
// limit: 200,
|
||||||
sort: { updated_at: -1 },
|
// sort: { updated_at: -1 },
|
||||||
}
|
// }
|
||||||
});
|
// })
|
||||||
|
|
||||||
const random_url = await generateRandomUrl();
|
const random_url = await generateRandomUrl();
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,12 @@ module.exports = async (req, res) => {
|
||||||
{
|
{
|
||||||
...criteria,
|
...criteria,
|
||||||
},
|
},
|
||||||
'_id title distance owner updated_at',
|
'_id title distance owner updated_at is_public',
|
||||||
{
|
{
|
||||||
limit: 500,
|
limit: 500,
|
||||||
sort: { updated_at: -1 },
|
sort: { updated_at: -1 },
|
||||||
}
|
}
|
||||||
).populate('owner');
|
).populate('owner', '_id');
|
||||||
|
|
||||||
list = list.filter(item => (
|
list = list.filter(item => (
|
||||||
!author || item.owner._id === author
|
!author || item.owner._id === author
|
||||||
|
|
|
@ -4,7 +4,6 @@ import classnames from 'classnames';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
active: Boolean,
|
active: Boolean,
|
||||||
|
|
||||||
onPress: Function,
|
onPress: Function,
|
||||||
}
|
}
|
||||||
export const Switch = ({ active, onPress = () => {} }: Props) => (
|
export const Switch = ({ active, onPress = () => {} }: Props) => (
|
||||||
|
|
|
@ -28,8 +28,11 @@ type Props = {
|
||||||
author: String,
|
author: String,
|
||||||
distance: Array<Number>,
|
distance: Array<Number>,
|
||||||
tab: Array<string>,
|
tab: Array<string>,
|
||||||
}
|
min: number,
|
||||||
|
max: number,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
marks: { [x: number]: string },
|
||||||
editing: Boolean,
|
editing: Boolean,
|
||||||
routes_sorted: Array<string>,
|
routes_sorted: Array<string>,
|
||||||
|
|
||||||
|
@ -39,7 +42,18 @@ type Props = {
|
||||||
setDialogActive: Function,
|
setDialogActive: Function,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Component extends React.Component<Props> {
|
type State = {
|
||||||
|
editing_item: ?string,
|
||||||
|
}
|
||||||
|
|
||||||
|
class Component extends React.Component<Props, State> {
|
||||||
|
state = {
|
||||||
|
editing_item: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
startEditing = editing_item => this.setState({ editing_item });
|
||||||
|
stopEditing = () => this.setState({ editing_item: null });
|
||||||
|
|
||||||
setTitle = ({ target: { value } }) => {
|
setTitle = ({ target: { value } }) => {
|
||||||
this.props.searchSetTitle(value);
|
this.props.searchSetTitle(value);
|
||||||
};
|
};
|
||||||
|
@ -67,6 +81,8 @@ class Component extends React.Component<Props> {
|
||||||
marks,
|
marks,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const { editing_item } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dialog-content">
|
<div className="dialog-content">
|
||||||
{ list.length === 0 && loading &&
|
{ list.length === 0 && loading &&
|
||||||
|
@ -136,9 +152,12 @@ class Component extends React.Component<Props> {
|
||||||
<RouteRow
|
<RouteRow
|
||||||
editing={editing}
|
editing={editing}
|
||||||
{...route}
|
{...route}
|
||||||
key={route._id}
|
|
||||||
openRoute={this.openRoute}
|
openRoute={this.openRoute}
|
||||||
tab={tab}
|
tab={tab}
|
||||||
|
is_editing={(editing_item === route._id)}
|
||||||
|
startEditing={this.startEditing}
|
||||||
|
stopEditing={this.stopEditing}
|
||||||
|
key={route._id}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,42 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Icon } from '$components/panels/Icon';
|
import { Icon } from '$components/panels/Icon';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { RouteRowEditor } from '$components/maps/RouteRowEditor';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title: String,
|
_id: string,
|
||||||
distance: Number,
|
title: string,
|
||||||
created_at: String,
|
distance: number,
|
||||||
_id: String,
|
tab: string,
|
||||||
editing: Boolean,
|
is_editing: boolean,
|
||||||
|
is_public: boolean,
|
||||||
|
|
||||||
openRoute: Function,
|
|
||||||
|
openRoute: (_id: string) => {},
|
||||||
|
startEditing: (_id: string) => {},
|
||||||
|
stopEditing: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RouteRow = ({
|
export const RouteRow = ({
|
||||||
title, distance, _id, openRoute, tab,
|
title, distance, _id, openRoute, tab, is_editing, startEditing, stopEditing, is_public
|
||||||
}: Props) => (
|
}: Props) => (
|
||||||
<div className="route-row-wrapper">
|
<div className={classnames('route-row-wrapper', { is_editing })}>
|
||||||
<div
|
{
|
||||||
className="route-row"
|
tab === 'mine' &&
|
||||||
onClick={() => openRoute(_id)}
|
<div className="route-row-edit" onClick={() => startEditing(_id)}>
|
||||||
>
|
|
||||||
<div className="route-row-edit">
|
|
||||||
<Icon icon="icon-edit-1" />
|
<Icon icon="icon-edit-1" />
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!is_editing
|
||||||
|
?
|
||||||
|
<div
|
||||||
|
className="route-row"
|
||||||
|
>
|
||||||
|
<div onClick={() => openRoute(_id)}>
|
||||||
<div className="route-title">
|
<div className="route-title">
|
||||||
{title || _id}
|
<span>{(title || _id)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="route-description">
|
<div className="route-description">
|
||||||
<span>
|
<span>
|
||||||
|
@ -37,5 +49,19 @@ export const RouteRow = ({
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="route-row-panel">
|
||||||
|
<div className="">
|
||||||
|
<Icon icon="icon-trash-4" size={24} />
|
||||||
|
Удалить
|
||||||
|
</div>
|
||||||
|
<div className="flex_1 justify-end" onClick={() => startEditing(_id)}>
|
||||||
|
<Icon icon="icon-edit-1" size={24} />
|
||||||
|
Правка
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: <RouteRowEditor title={title} is_public={is_public} distance={distance} _id={_id} />
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
81
src/components/maps/RouteRowEditor.jsx
Normal file
81
src/components/maps/RouteRowEditor.jsx
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { Icon } from '$components/panels/Icon';
|
||||||
|
import { Switch } from '$components/Switch';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
title: string,
|
||||||
|
is_public: boolean,
|
||||||
|
distance: number,
|
||||||
|
_id: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
title: string,
|
||||||
|
is_public: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class RouteRowEditor extends React.PureComponent<Props, State> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
title: props.title,
|
||||||
|
is_public: props.is_public,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEditing = () => console.log();
|
||||||
|
setPublic = () => this.setState({ is_public: !this.state.is_public });
|
||||||
|
setTitle = ({ target: { value } }: { target: { value: string } }) => this.setState({ title: value });
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
state: { title, is_public },
|
||||||
|
props: { distance, _id }
|
||||||
|
} = this;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="route-row"
|
||||||
|
>
|
||||||
|
<div className="route-title">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={title}
|
||||||
|
onChange={this.setTitle}
|
||||||
|
placeholder="Введите название"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="route-description">
|
||||||
|
<span>
|
||||||
|
<Icon icon="icon-link-1" />
|
||||||
|
{_id}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<Icon icon="icon-cycle-1" />
|
||||||
|
{(distance && `${distance} km`) || '0 km'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="route-row-editor">
|
||||||
|
<div className="route-row-buttons">
|
||||||
|
<div className="flex_1" onClick={this.setPublic}>
|
||||||
|
<Switch
|
||||||
|
active={is_public}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
is_public
|
||||||
|
? ' В каталоге карт'
|
||||||
|
: ' Только по ссылке'
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className="button primary" onClick={this.stopEditing}>
|
||||||
|
OK
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,8 +92,6 @@ export class Component extends React.PureComponent<Props, void> {
|
||||||
state: { menuOpened },
|
state: { menuOpened },
|
||||||
} = this;
|
} = this;
|
||||||
|
|
||||||
const route_count = Object.keys(user.routes).length;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="panel active panel-user">
|
<div className="panel active panel-user">
|
||||||
|
@ -114,7 +112,6 @@ export class Component extends React.PureComponent<Props, void> {
|
||||||
<div className="control-bar">
|
<div className="control-bar">
|
||||||
<button
|
<button
|
||||||
className={classnames({
|
className={classnames({
|
||||||
disabled: route_count <= 0,
|
|
||||||
active: dialog_active && (dialog === DIALOGS.MAP_LIST)
|
active: dialog_active && (dialog === DIALOGS.MAP_LIST)
|
||||||
})}
|
})}
|
||||||
onClick={this.openMapsDialog}
|
onClick={this.openMapsDialog}
|
||||||
|
|
|
@ -228,7 +228,7 @@ export class Editor {
|
||||||
};
|
};
|
||||||
|
|
||||||
setData = ({
|
setData = ({
|
||||||
route = [], stickers = [], owner, title, address, provider = DEFAULT_PROVIDER, logo = DEFAULT_LOGO, public: is_public,
|
route = [], stickers = [], owner, title, address, provider = DEFAULT_PROVIDER, logo = DEFAULT_LOGO, is_public,
|
||||||
}) => {
|
}) => {
|
||||||
this.setTitle(title || '');
|
this.setTitle(title || '');
|
||||||
const { id } = this.getUser();
|
const { id } = this.getUser();
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
setSaveError,
|
setSaveError,
|
||||||
setSaveOverwrite, setSaveSuccess, setTitle,
|
setSaveOverwrite, setSaveSuccess, setTitle,
|
||||||
searchSetTab,
|
searchSetTab,
|
||||||
setUser, setDialog, setPublic, setAddressOrigin, setProvider, changeProvider,
|
setUser, setDialog, setPublic, setAddressOrigin, setProvider, changeProvider, openMapDialog,
|
||||||
} from '$redux/user/actions';
|
} from '$redux/user/actions';
|
||||||
import { getUrlData, parseQuery, pushLoaderState, pushNetworkInitError, pushPath, replacePath } from '$utils/history';
|
import { getUrlData, parseQuery, pushLoaderState, pushNetworkInitError, pushPath, replacePath } from '$utils/history';
|
||||||
import { editor } from '$modules/Editor';
|
import { editor } from '$modules/Editor';
|
||||||
|
@ -70,8 +70,6 @@ function* startEmptyEditorSaga() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function* startEditingSaga() {
|
function* startEditingSaga() {
|
||||||
// yield put(setEditing(true));
|
|
||||||
// yield editor.startEditing();
|
|
||||||
const { path } = getUrlData();
|
const { path } = getUrlData();
|
||||||
yield pushPath(`/${path}/edit`);
|
yield pushPath(`/${path}/edit`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -375,5 +375,5 @@
|
||||||
</svg>
|
</svg>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
<use xlink:href="#icon-edit-1" />
|
<use xlink:href="#icon-sad-1" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
@ -4,7 +4,7 @@
|
||||||
@router_line: #4597d0;
|
@router_line: #4597d0;
|
||||||
|
|
||||||
@bar_background: #333333;
|
@bar_background: #333333;
|
||||||
@dialog_background: #222222;
|
@dialog_background: #271535;
|
||||||
|
|
||||||
@location_line: #ff3344;
|
@location_line: #ff3344;
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
background: #271535;
|
background: @dialog_background;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -163,21 +163,40 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.route-row-wrapper {
|
.route-row-wrapper {
|
||||||
overflow: hidden;
|
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
|
position: relative;
|
||||||
&:hover {
|
|
||||||
.route-row {
|
|
||||||
transform: translateX(-48px);
|
|
||||||
}
|
|
||||||
.route-row-edit {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.route-row {
|
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
transition: all 500ms;
|
||||||
|
|
||||||
|
//&:hover {
|
||||||
|
// .route-row { transform: translateX(-58px); }
|
||||||
|
// .route-row-edit { transform: translateX(-58px); }
|
||||||
|
//
|
||||||
|
// &.is_editing {
|
||||||
|
// .route-row { transform: translateX(0); }
|
||||||
|
// .route-row-edit { transform: translateX(0); }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
&.is_editing {
|
||||||
|
//transform: translateY(-2px);
|
||||||
|
.route-row { background: rgba(255, 100, 100, 0.2); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-row-editor {
|
||||||
|
color: white;
|
||||||
|
padding: 20px 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-row-buttons {
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-row {
|
||||||
background: rgba(255, 255, 255, 0.05);
|
background: rgba(255, 255, 255, 0.05);
|
||||||
padding: 10px 10px 5px 10px;
|
padding: 10px 10px 5px 10px;
|
||||||
color: white;
|
color: white;
|
||||||
|
@ -188,23 +207,63 @@
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
|
||||||
|
.route-row-panel {
|
||||||
|
transform: scaleY(1);
|
||||||
|
pointer-events: all;
|
||||||
|
touch-action: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-row-panel {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
height: 32px;
|
||||||
|
width: 100%;
|
||||||
|
left: 0;
|
||||||
|
background: mix(@dialog_background, white, 80%);
|
||||||
|
border-radius: 0 0 @panel_radius @panel_radius;
|
||||||
|
z-index: 1;
|
||||||
|
transform: scaleY(0);
|
||||||
|
pointer-events: none;
|
||||||
|
touch-action: none;
|
||||||
|
transition: transform 250ms;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
padding: 0 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
fill: white;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.route-row-edit {
|
.route-row-edit {
|
||||||
fill: rgba(0, 0, 0, 0.5);
|
fill: rgba(255, 255, 255, 0.3);
|
||||||
left: 100%;
|
right: -48px;
|
||||||
|
padding-left: 0px;
|
||||||
stroke: none;
|
stroke: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 58px;
|
width: 58px;
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
opacity: 0;
|
|
||||||
transition: all 500ms;
|
transition: all 500ms;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
fill: @green_secondary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.route-title {
|
.route-title {
|
||||||
|
|
|
@ -153,3 +153,7 @@ input {
|
||||||
.relative {
|
.relative {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.justify-end {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue