-
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
climbing: introduce ClimbingPanel for climbing=crag (#189)
- Loading branch information
Showing
65 changed files
with
5,554 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
src/components/FeaturePanel/Climbing/ClimbingDialog.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import React, { useRef } from 'react'; | ||
import styled from 'styled-components'; | ||
import { | ||
Button, | ||
Dialog, | ||
DialogActions, | ||
DialogContent, | ||
} from '@material-ui/core'; | ||
import AddIcon from '@material-ui/icons/Add'; | ||
import Router from 'next/router'; | ||
import { ClimbingView } from './ClimbingView'; | ||
import { useClimbingContext } from './contexts/ClimbingContext'; | ||
import { ClimbingDialogHeader } from './ClimbingDialogHeader'; | ||
import { getOsmappLink } from '../../../services/helpers'; | ||
import { useFeatureContext } from '../../utils/FeatureContext'; | ||
import { useGetHandleSave } from './useGetHandleSave'; | ||
|
||
const Flex = styled.div` | ||
display: flex; | ||
justify-content: space-between; | ||
width: 100%; | ||
`; | ||
|
||
export const ClimbingDialog = () => { | ||
const contentRef = useRef(null); | ||
|
||
const { | ||
setScrollOffset, | ||
isPointMoving, | ||
setIsEditMode, | ||
isEditMode, | ||
getMachine, | ||
showDebugMenu, | ||
} = useClimbingContext(); | ||
const { feature } = useFeatureContext(); | ||
const handleSave = useGetHandleSave(setIsEditMode); | ||
|
||
const machine = getMachine(); | ||
|
||
const onScroll = (e) => { | ||
setScrollOffset({ | ||
x: e.target.scrollLeft, | ||
y: e.target.scrollTop, | ||
units: 'px', | ||
}); | ||
}; | ||
|
||
const handleClose = () => { | ||
Router.push(`${getOsmappLink(feature)}${window.location.hash}`); | ||
}; | ||
|
||
const onNewRouteCreate = () => { | ||
machine.execute('createRoute'); | ||
}; | ||
|
||
return ( | ||
<Dialog fullScreen open onClose={handleClose}> | ||
<ClimbingDialogHeader onClose={handleClose} /> | ||
|
||
<DialogContent | ||
dividers | ||
style={{ | ||
overscrollBehavior: isPointMoving ? 'none' : undefined, | ||
padding: 0, | ||
}} | ||
ref={contentRef} | ||
onScroll={onScroll} | ||
> | ||
<ClimbingView /> | ||
</DialogContent> | ||
|
||
{isEditMode && ( | ||
<DialogActions> | ||
<Flex> | ||
{isEditMode && ( | ||
<Button | ||
onClick={onNewRouteCreate} | ||
color="primary" | ||
startIcon={<AddIcon />} | ||
> | ||
Add new route | ||
</Button> | ||
)} | ||
<div> | ||
<Button autoFocus onClick={handleClose}> | ||
Cancel | ||
</Button> | ||
{showDebugMenu && ( | ||
<Button | ||
onClick={handleSave} | ||
variant="contained" | ||
color="primary" | ||
> | ||
Save | ||
</Button> | ||
)} | ||
</div> | ||
</Flex> | ||
</DialogActions> | ||
)} | ||
</Dialog> | ||
); | ||
}; |
115 changes: 115 additions & 0 deletions
115
src/components/FeaturePanel/Climbing/ClimbingDialogHeader.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import React, { useState } from 'react'; | ||
import styled from 'styled-components'; | ||
import { AppBar, Toolbar, Typography, IconButton } from '@material-ui/core'; | ||
import CloseIcon from '@material-ui/icons/Close'; | ||
import TuneIcon from '@material-ui/icons/Tune'; | ||
import { useClimbingContext } from './contexts/ClimbingContext'; | ||
import { ClimbingSettings } from './ClimbingSettings'; | ||
import { PhotoLink } from './PhotoLink'; | ||
import { useFeatureContext } from '../../utils/FeatureContext'; | ||
import { getLabel } from '../../../helpers/featureLabel'; | ||
|
||
const Title = styled.div` | ||
flex: 1; | ||
overflow: hidden; | ||
`; | ||
const PhotosContainer = styled.div` | ||
display: flex; | ||
flex-direction: row; | ||
gap: 4px; | ||
`; | ||
const PhotosTitle = styled.div` | ||
color: ${({ theme }) => theme.textSubdued}; | ||
`; | ||
const PhotoLinks = styled.div` | ||
display: flex; | ||
flex-wrap: wrap; | ||
flex-direction: row; | ||
gap: 4px; | ||
margin-bottom: 2px; | ||
`; | ||
|
||
export const ClimbingDialogHeader = ({ onClose }) => { | ||
const [isSettingsOpened, setIsSettingsOpened] = useState<boolean>(false); | ||
const [clickCounter, setClickCounter] = useState<number>(0); | ||
const { | ||
areRoutesVisible, | ||
setPhotoPath, | ||
photoPath, | ||
loadPhotoRelatedData, | ||
setAreRoutesLoading, | ||
photoPaths, | ||
setShowDebugMenu, | ||
} = useClimbingContext(); | ||
|
||
const onPhotoChange = (photo: string) => { | ||
setAreRoutesLoading(true); | ||
setPhotoPath(photo); | ||
setTimeout(() => { | ||
// @TODO fix it without timeout | ||
loadPhotoRelatedData(); | ||
}, 100); | ||
}; | ||
const { feature } = useFeatureContext(); | ||
|
||
const label = getLabel(feature); | ||
|
||
const handleOnClick = () => { | ||
setClickCounter(clickCounter + 1); | ||
if (clickCounter === 4) { | ||
setShowDebugMenu(true); | ||
setClickCounter(0); | ||
} | ||
}; | ||
|
||
return ( | ||
<AppBar position="static" color="transparent"> | ||
<Toolbar variant="dense"> | ||
<Title> | ||
<Typography | ||
noWrap | ||
variant="h6" | ||
component="div" | ||
onClick={handleOnClick} | ||
> | ||
{label} | ||
</Typography> | ||
{photoPaths?.length > 1 && ( | ||
<PhotosContainer> | ||
<PhotosTitle>Photos:</PhotosTitle> | ||
<PhotoLinks> | ||
{photoPaths.map((photo, index) => ( | ||
<PhotoLink | ||
onClick={() => onPhotoChange(photo)} | ||
isCurrentPhoto={photo === photoPath} | ||
> | ||
{index} | ||
</PhotoLink> | ||
))} | ||
</PhotoLinks> | ||
</PhotosContainer> | ||
)} | ||
</Title> | ||
|
||
<IconButton | ||
color="primary" | ||
edge="end" | ||
title={areRoutesVisible ? 'Hide routes' : 'Show routes'} | ||
onClick={() => { | ||
setIsSettingsOpened(!isSettingsOpened); | ||
}} | ||
> | ||
<TuneIcon fontSize="small" /> | ||
</IconButton> | ||
|
||
<IconButton color="primary" edge="end" onClick={onClose}> | ||
<CloseIcon fontSize="small" /> | ||
</IconButton> | ||
</Toolbar> | ||
<ClimbingSettings | ||
isSettingsOpened={isSettingsOpened} | ||
setIsSettingsOpened={setIsSettingsOpened} | ||
/> | ||
</AppBar> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import React from 'react'; | ||
import styled from 'styled-components'; | ||
import { CircularProgress } from '@material-ui/core'; | ||
import Router from 'next/router'; | ||
import { useClimbingContext } from './contexts/ClimbingContext'; | ||
import { PanelScrollbars, PanelWrapper } from '../../utils/PanelHelpers'; | ||
import { RoutesLayer } from './Editor/RoutesLayer'; | ||
import { RouteList } from './RouteList/RouteList'; | ||
import { FullscreenIconContainer, ShowFullscreen } from './ShowFullscreen'; | ||
import { useFeatureContext } from '../../utils/FeatureContext'; | ||
import { getLabel } from '../../../helpers/featureLabel'; | ||
import { getCommonsImageUrl } from '../../../services/images/getWikiImage'; | ||
import { getOsmappLink } from '../../../services/helpers'; | ||
|
||
const ThumbnailContainer = styled.div<{ height: number }>` | ||
width: 100%; | ||
height: ${({ height }) => height}px; | ||
position: relative; | ||
:hover ${FullscreenIconContainer} { | ||
visibility: visible; | ||
backdrop-filter: blur(3px); | ||
background-color: rgba(0, 0, 0, 0.5); | ||
cursor: pointer; | ||
} | ||
`; | ||
|
||
const Heading = styled.div` | ||
margin: 12px 8px 4px; | ||
font-size: 36px; | ||
line-height: 0.98; | ||
color: ${({ theme }) => theme.palette.text.panelHeading}; | ||
:hover { | ||
text-decoration: underline; | ||
cursor: pointer; | ||
} | ||
`; | ||
const Thumbnail = styled.img<{ isLoading: boolean }>` | ||
width: 100%; | ||
position: absolute; | ||
visibility: ${({ isLoading }) => (isLoading ? 'hidden' : 'visible')}; | ||
`; | ||
|
||
const LoadingContainer = styled.div` | ||
position: absolute; | ||
left: 0; | ||
top: 0; | ||
display: flex; | ||
justify-content: center; | ||
width: 100%; | ||
height: 100%; | ||
align-items: center; | ||
`; | ||
|
||
export const ClimbingPanel = ({ footer }) => { | ||
const { feature } = useFeatureContext(); | ||
const label = getLabel(feature); | ||
|
||
const { | ||
loadPhotoRelatedData, | ||
imageSize, | ||
photoPath, | ||
photoRef, | ||
preparePhotosAndSetFirst, | ||
} = useClimbingContext(); | ||
|
||
const onFullScreenClick = () => { | ||
Router.push(`${getOsmappLink(feature)}/climbing${window.location.hash}`); | ||
}; | ||
|
||
preparePhotosAndSetFirst(); | ||
|
||
const imageUrl = getCommonsImageUrl(`File:${photoPath}`, 500); | ||
|
||
const onPhotoLoad = () => { | ||
loadPhotoRelatedData(); | ||
}; | ||
|
||
const isPhotoLoading = imageSize.height === 0; | ||
return ( | ||
<> | ||
<PanelWrapper> | ||
<PanelScrollbars> | ||
{photoPath && imageUrl && ( | ||
<ThumbnailContainer | ||
height={isPhotoLoading ? 200 : imageSize.height} | ||
> | ||
{isPhotoLoading && ( | ||
<LoadingContainer> | ||
<CircularProgress /> | ||
</LoadingContainer> | ||
)} | ||
|
||
<Thumbnail | ||
src={imageUrl} | ||
ref={photoRef} | ||
onLoad={onPhotoLoad} | ||
isLoading={isPhotoLoading} | ||
/> | ||
|
||
{!isPhotoLoading && <RoutesLayer onClick={() => null} />} | ||
<ShowFullscreen onClick={onFullScreenClick} /> | ||
</ThumbnailContainer> | ||
)} | ||
<Heading onClick={onFullScreenClick}>{label}</Heading> | ||
|
||
<RouteList /> | ||
|
||
{/* @TODO unite with parent panel */} | ||
<div style={{ padding: '20px 15px 0 15px' }}>{footer}</div> | ||
</PanelScrollbars> | ||
</PanelWrapper> | ||
</> | ||
); | ||
}; |
Oops, something went wrong.