mirror of
https://github.com/muerwre/orchidmap-front.git
synced 2025-04-25 11:06:40 +07:00
added gpx dialog
This commit is contained in:
parent
947ec69e60
commit
e995b46641
33 changed files with 11687 additions and 131 deletions
147
src/components/dialogs/GpxDialog.tsx
Normal file
147
src/components/dialogs/GpxDialog.tsx
Normal file
|
@ -0,0 +1,147 @@
|
|||
import React, { FC, useCallback, ChangeEvent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import * as EDITOR_ACTIONS from '~/redux/editor/actions';
|
||||
import { IState } from '~/redux/store';
|
||||
import { selectEditorGpx } from '~/redux/editor/selectors';
|
||||
import { GpxDialogRow } from '~/components/gpx/GpxDialogRow';
|
||||
import { MainMap } from '~/constants/map';
|
||||
import { latLngBounds } from 'leaflet';
|
||||
import { Switch } from '../Switch';
|
||||
import { selectMapRoute, selectMapTitle, selectMapAddress } from '~/redux/map/selectors';
|
||||
import classNames from 'classnames';
|
||||
import uuid from 'uuid';
|
||||
import { getUrlData } from '~/utils/history';
|
||||
import { getRandomColor } from '~/utils/dom';
|
||||
|
||||
const mapStateToProps = (state: IState) => ({
|
||||
gpx: selectEditorGpx(state),
|
||||
route: selectMapRoute(state),
|
||||
title: selectMapTitle(state),
|
||||
address: selectMapAddress(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
editorDropGpx: EDITOR_ACTIONS.editorDropGpx,
|
||||
editorUploadGpx: EDITOR_ACTIONS.editorUploadGpx,
|
||||
editorSetGpx: EDITOR_ACTIONS.editorSetGpx,
|
||||
editorGetGPXTrack: EDITOR_ACTIONS.editorGetGPXTrack,
|
||||
};
|
||||
|
||||
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||
|
||||
const GpxDialogUnconnected: FC<Props> = ({
|
||||
title,
|
||||
address,
|
||||
gpx,
|
||||
route,
|
||||
editorGetGPXTrack,
|
||||
editorSetGpx,
|
||||
editorUploadGpx,
|
||||
}) => {
|
||||
const onGpxUpload = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (!event.target || !event.target.files || event.target.files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
editorUploadGpx(event.target.files[0]);
|
||||
},
|
||||
[editorUploadGpx]
|
||||
);
|
||||
|
||||
const onFocusRoute = useCallback(
|
||||
index => {
|
||||
if (!gpx.list[index] || !gpx.list[index].latlngs) return;
|
||||
|
||||
const bounds = latLngBounds(gpx.list[index].latlngs);
|
||||
MainMap.fitBounds(bounds);
|
||||
},
|
||||
[gpx, MainMap]
|
||||
);
|
||||
|
||||
const onRouteDrop = useCallback(
|
||||
index => {
|
||||
editorSetGpx({ list: gpx.list.filter((el, i) => i !== index) });
|
||||
},
|
||||
[gpx, editorSetGpx]
|
||||
);
|
||||
|
||||
const toggleGpx = useCallback(() => {
|
||||
editorSetGpx({ enabled: !gpx.enabled });
|
||||
}, [gpx, editorSetGpx]);
|
||||
|
||||
const onRouteToggle = useCallback(
|
||||
index => {
|
||||
if (!gpx.enabled) return;
|
||||
editorSetGpx({
|
||||
list: gpx.list.map((el, i) => (i !== index ? el : { ...el, enabled: !el.enabled })),
|
||||
});
|
||||
},
|
||||
[gpx, editorSetGpx]
|
||||
);
|
||||
|
||||
const addCurrent = useCallback(() => {
|
||||
if (!route.length) return;
|
||||
|
||||
const { path } = getUrlData()
|
||||
|
||||
editorSetGpx({
|
||||
list: [
|
||||
...gpx.list,
|
||||
{
|
||||
latlngs: route,
|
||||
enabled: false,
|
||||
name: title || address || path,
|
||||
id: uuid(),
|
||||
color: getRandomColor(),
|
||||
},
|
||||
],
|
||||
});
|
||||
}, [route, gpx, editorSetGpx]);
|
||||
|
||||
return (
|
||||
<div className="control-dialog control-dialog__left control-dialog__small">
|
||||
<div className="gpx-title">
|
||||
<div className="flex_1 big white upper">Треки</div>
|
||||
<Switch active={gpx.enabled} onPress={toggleGpx} />
|
||||
</div>
|
||||
|
||||
{gpx.list.map((item, index) => (
|
||||
<GpxDialogRow
|
||||
item={item}
|
||||
key={item.id}
|
||||
index={index}
|
||||
enabled={gpx.enabled}
|
||||
onRouteDrop={onRouteDrop}
|
||||
onFocusRoute={onFocusRoute}
|
||||
onRouteToggle={onRouteToggle}
|
||||
/>
|
||||
))}
|
||||
|
||||
<div className="gpx-buttons">
|
||||
<button className="button outline">
|
||||
<input type="file" onChange={onGpxUpload} />
|
||||
Загрузить GPX
|
||||
</button>
|
||||
|
||||
<div
|
||||
className={classNames('button outline', { disabled: !route.length })}
|
||||
onClick={addCurrent}
|
||||
>
|
||||
Добавить текущий
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={classNames('button success', { disabled: !route.length })}
|
||||
onClick={editorGetGPXTrack}
|
||||
>
|
||||
Скачать текущий
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const GpxDialog = connect(mapStateToProps, mapDispatchToProps)(GpxDialogUnconnected);
|
||||
|
||||
export { GpxDialog };
|
34
src/components/gpx/GpxDialogRow.tsx
Normal file
34
src/components/gpx/GpxDialogRow.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
import React, { FC } from 'react';
|
||||
import { IGpxRoute } from '~/redux/editor';
|
||||
import { Switch } from '../Switch';
|
||||
import { Icon } from '../panels/Icon';
|
||||
import classnames from 'classnames';
|
||||
|
||||
interface IProps {
|
||||
item: IGpxRoute;
|
||||
index: number
|
||||
enabled: boolean;
|
||||
|
||||
onFocusRoute: (i: number) => void
|
||||
onRouteDrop: (i: number) => void
|
||||
onRouteToggle: (i: number) => void
|
||||
}
|
||||
|
||||
const GpxDialogRow: FC<IProps> = ({ item, index, enabled, onRouteToggle, onFocusRoute, onRouteDrop }) => {
|
||||
return (
|
||||
<div className={classnames("gpx-row", { 'gpx-row_disabled': !enabled || !item.enabled })}>
|
||||
<div className="gpx-row__color" style={{ backgroundColor: item.color }}/>
|
||||
|
||||
<div className="gpx-row__title" onClick={() => onFocusRoute(index)}>
|
||||
{item.name}
|
||||
</div>
|
||||
|
||||
<div className="gpx-row__buttons">
|
||||
<div onClick={() => onRouteDrop(index)}><Icon icon="icon-trash-6" size={24} /></div>
|
||||
<div><Switch active={item.enabled} onPress={() => onRouteToggle(index)}/></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { GpxDialogRow };
|
|
@ -8,6 +8,7 @@ import { TrashDialog } from '~/components/dialogs/TrashDialog';
|
|||
import { LogoDialog } from '~/components/dialogs/LogoDialog';
|
||||
import { SaveDialog } from '~/components/dialogs/SaveDialog';
|
||||
import { CancelDialog } from '~/components/dialogs/CancelDialog';
|
||||
import { GpxDialog } from '~/components/dialogs/GpxDialog';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
|
@ -31,6 +32,7 @@ const DIALOG_CONTENTS: { [x: string]: any } = {
|
|||
[MODES.PROVIDER]: ProviderDialog,
|
||||
[MODES.SHOT_PREFETCH]: ShotPrefetchDialog,
|
||||
[MODES.POLY]: PolylineDialog,
|
||||
[MODES.GPX]: GpxDialog,
|
||||
};
|
||||
|
||||
const EditorDialogUnconnected = (props: Props) =>
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
editorSetDialogActive,
|
||||
editorGetGPXTrack,
|
||||
editorSearchNominatim,
|
||||
editorChangeMode,
|
||||
} from '~/redux/editor/actions';
|
||||
import { connect } from 'react-redux';
|
||||
import { Icon } from '~/components/panels/Icon';
|
||||
|
@ -22,6 +23,7 @@ import { Tooltip } from '~/components/panels/Tooltip';
|
|||
import { TitleDialog } from '~/components/dialogs/TitleDialog';
|
||||
import { NominatimSearchPanel } from '~/components/dialogs/NominatimSearchPanel';
|
||||
import { IState } from '~/redux/store';
|
||||
import { MODES } from '~/constants/modes';
|
||||
|
||||
const mapStateToProps = ({
|
||||
user: { user },
|
||||
|
@ -46,6 +48,7 @@ const mapDispatchToProps = {
|
|||
openMapDialog,
|
||||
editorGetGPXTrack,
|
||||
editorSearchNominatim,
|
||||
editorChangeMode,
|
||||
};
|
||||
|
||||
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||
|
@ -118,13 +121,17 @@ export class UserPanelUnconnected extends PureComponent<Props, State> {
|
|||
);
|
||||
};
|
||||
|
||||
openGpxDialog = () => {
|
||||
this.props.editorChangeMode(MODES.GPX);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
props: { user, dialog, dialog_active, route, stickers, features },
|
||||
state: { menuOpened },
|
||||
} = this;
|
||||
|
||||
const is_empty = !route.length && !stickers.length;
|
||||
// const is_empty = !route.length && !stickers.length;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -171,8 +178,9 @@ export class UserPanelUnconnected extends PureComponent<Props, State> {
|
|||
|
||||
<div className="control-bar">
|
||||
<button
|
||||
className={classnames({ inactive: is_empty })}
|
||||
onClick={this.props.editorGetGPXTrack}
|
||||
// className={classnames({ inactive: is_empty })}
|
||||
onClick={this.openGpxDialog}
|
||||
// onClick={this.props.editorGetGPXTrack}
|
||||
>
|
||||
<Tooltip>Экспорт GPX</Tooltip>
|
||||
<Icon icon="icon-gpx-1" />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue