basic UI for row editing

This commit is contained in:
muerwre 2019-02-11 15:36:55 +07:00
parent 3ea920c1f8
commit 32e1b4c3fd
13 changed files with 248 additions and 65 deletions

View file

@ -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();

View file

@ -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

View file

@ -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) => (

View file

@ -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}
/> />
)) ))
} }

View file

@ -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>
); );

View 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>
);
}
}

View file

@ -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}

View file

@ -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();

View file

@ -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`);
} }

View file

@ -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

Before After
Before After

View file

@ -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;

View file

@ -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 {

View file

@ -153,3 +153,7 @@ input {
.relative { .relative {
position: relative; position: relative;
} }
.justify-end {
justify-content: flex-end;
}