-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Simplified AOI Creation ✨ #1395
Open
AliceR
wants to merge
34
commits into
main
Choose a base branch
from
feat#1289-simplified-aoi-creation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+880
−1,057
Open
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
679a931
Move aoi files into same folder
AliceR cd094af
Run prettier formatting for aoi related files
AliceR c00f016
Remove unused file
AliceR e2a9df8
Remove draw controls and mbDraw references
AliceR 251897d
Rename files and components for clarity
AliceR 95f8d15
Export AOI as a single feature; display as AOI map layer
AliceR 782b9d0
Run fitBounds as effect, when aoi changes
AliceR b357eec
Clean up and simplify AOI actions
AliceR f7e7759
Fix aoi id for uploaded geojson (for id: number)
AliceR 5f6a101
Add hook for draw tools and drawing actions to control state
AliceR 24709ef
Update analysis toolbar based on drawing state
AliceR ee4f78c
Use workaround for map offset to properly fit bounds
AliceR 123069a
Remove unused file map-coords.tsx
AliceR d953cc5
Clean up dependency arrays to avoid unintended re-renders
AliceR 5187504
Move aoi fly to effect into Aoi layer component
AliceR c3d8e15
Hide unrelated buttons while drawing
AliceR f0a4586
Clarify comment on fitBounds vs. flyTo
AliceR e062920
Use USWDS buttons in AOI Control
AliceR 23dd145
Use custom styles for MapboxDraw layers
AliceR 125b724
Upgrade mapbox-gl to ^3.9.3 (from 2.15.0)
AliceR e227a9d
Update typings after upgrading mapbox-gl > 3.5
AliceR a4f7383
Update projection options in parcel-resolver-veda
AliceR 641463e
Fix type for projection parallels
AliceR 87cf6c3
Update tooltips to indicate a new AOI will be created
AliceR 6fbef33
Replace root.render in useThemedControl with React Portal
AliceR 1b6f318
fix: Replace root.render in useThemedControl with React Portal (#1414)
AliceR 045deb6
Remove unnecessary type casting
AliceR e40b575
Avoid type casting, mark metadata optional
AliceR d45e623
Update drawId type
AliceR 9d0c45d
Refactor to remove unnecessary wrapper component
AliceR 027d5c5
Extract draw tools into separate component
AliceR 5e65894
fix: Upgrade mapbox-gl (#1400)
AliceR cd1bb5a
Use fitBounds instead of flyTo after upgrading mapbox-gl
AliceR 58aa825
Catch uncaught exception in onStyleUpdate
AliceR File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
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
193 changes: 193 additions & 0 deletions
193
app/scripts/components/common/map/controls/aoi/aoi-control.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,193 @@ | ||
import React, { useState } from 'react'; | ||
import { createPortal } from 'react-dom'; | ||
import { Feature, Polygon } from 'geojson'; | ||
import styled, { css, useTheme } from 'styled-components'; | ||
|
||
import { | ||
CollecticonPencil, | ||
CollecticonTrashBin, | ||
CollecticonUpload2 | ||
} from '@devseed-ui/collecticons'; | ||
import { Toolbar, ToolbarLabel, VerticalDivider } from '@devseed-ui/toolbar'; | ||
import { themeVal, glsp, disabled } from '@devseed-ui/theme-provider'; | ||
|
||
import useMaps from '../../hooks/use-maps'; | ||
import useAois from '../hooks/use-aois'; | ||
import useThemedControl from '../hooks/use-themed-control'; | ||
import { useDrawControl } from '../hooks/use-draw-control'; | ||
import CustomAoIModal from './custom-aoi-modal'; | ||
import PresetSelector from './preset-selector'; | ||
|
||
import { computeDrawStyles } from './style'; | ||
import { DrawTools } from './draw-tools'; | ||
import { TipToolbarIconButton } from '$components/common/tip-button'; | ||
import { Tip } from '$components/common/tip'; | ||
import { USWDSButton } from '$components/common/uswds'; | ||
|
||
const AnalysisToolbar = styled(Toolbar)<{ visuallyDisabled: boolean }>` | ||
background-color: ${themeVal('color.surface')}; | ||
border-radius: ${themeVal('shape.rounded')}; | ||
padding: ${glsp(0.25)}; | ||
box-shadow: ${themeVal('boxShadow.elevationC')}; | ||
|
||
${({ visuallyDisabled }) => | ||
visuallyDisabled && | ||
css` | ||
> * { | ||
${disabled()} | ||
pointer-events: none; | ||
} | ||
`} | ||
|
||
${ToolbarLabel} { | ||
text-transform: none; | ||
} | ||
`; | ||
|
||
const FloatingBarSelf = styled.div` | ||
position: absolute; | ||
bottom: ${glsp()}; | ||
left: 50%; | ||
transform: translateX(-50%); | ||
z-index: 100; | ||
`; | ||
|
||
const AoiControl = ({ disableReason }: { disableReason?: React.ReactNode }) => { | ||
const theme = useTheme(); | ||
const { main: mapboxMap } = useMaps(); | ||
const { isDrawing, setIsDrawing, aoi, updateAoi, aoiDeleteAll } = useAois(); | ||
|
||
const [aoiModalRevealed, setAoIModalRevealed] = useState(false); | ||
const [selectedState, setSelectedState] = useState(''); | ||
|
||
const resetAoi = () => { | ||
aoiDeleteAll(); | ||
setSelectedState(''); | ||
}; | ||
|
||
const onUploadConfirm = (features: Feature<Polygon>[]) => { | ||
resetAoi(); // delete all previous AOIs and clear selections | ||
setAoIModalRevealed(false); // close modal | ||
|
||
updateAoi({ features }); | ||
}; | ||
|
||
const onPresetConfirm = (features: Feature<Polygon>[]) => { | ||
aoiDeleteAll(); // delete all previous AOIs but keep preset selection | ||
|
||
updateAoi({ features }); | ||
}; | ||
|
||
const onTrashClick = () => { | ||
resetAoi(); | ||
}; | ||
|
||
const [drawing, drawingIsValid] = useDrawControl({ | ||
mapboxMap: mapboxMap, | ||
isDrawing, | ||
styles: computeDrawStyles(theme) | ||
}); | ||
|
||
const drawingActions = { | ||
confirm() { | ||
if (drawingIsValid) { | ||
resetAoi(); // delete all previous AOIs and clear selections | ||
|
||
updateAoi({ features: drawing }); // set the drawn AOI | ||
setIsDrawing(false); // leave drawing mode | ||
} | ||
}, | ||
cancel() { | ||
setIsDrawing(false); // leave drawing mode, nothing else | ||
}, | ||
start() { | ||
setIsDrawing(true); // start drawing | ||
}, | ||
toggle() { | ||
if (isDrawing) { | ||
drawingActions.confirm(); // finish drawing | ||
} else { | ||
drawingActions.start(); // start drawing | ||
} | ||
} | ||
}; | ||
|
||
const control = ( | ||
<> | ||
<Tip disabled={!disableReason} content={disableReason} placement='bottom'> | ||
<div> | ||
<AnalysisToolbar | ||
visuallyDisabled={!!disableReason} | ||
size='small' | ||
data-tour='analysis-tour' | ||
> | ||
{isDrawing ? ( | ||
<DrawTools {...{ drawingActions, drawingIsValid }} /> | ||
) : ( | ||
<> | ||
<PresetSelector | ||
selectedState={selectedState} | ||
setSelectedState={setSelectedState} | ||
onConfirm={onPresetConfirm} | ||
resetPreset={resetAoi} | ||
/> | ||
<VerticalDivider /> | ||
<TipToolbarIconButton | ||
tipContent='Draw a new area of interest' | ||
tipProps={{ placement: 'bottom' }} | ||
onClick={drawingActions.start} | ||
> | ||
<CollecticonPencil meaningful title='Draw new AOI' /> | ||
</TipToolbarIconButton> | ||
<TipToolbarIconButton | ||
tipContent='Upload a new area of interest' | ||
tipProps={{ placement: 'bottom' }} | ||
onClick={() => setAoIModalRevealed(true)} | ||
> | ||
<CollecticonUpload2 meaningful title='Upload geoJSON' /> | ||
</TipToolbarIconButton> | ||
</> | ||
)} | ||
</AnalysisToolbar> | ||
</div> | ||
</Tip> | ||
|
||
{!isDrawing && !!aoi && ( | ||
<FloatingBar container={mapboxMap.getContainer()}> | ||
<USWDSButton | ||
onClick={onTrashClick} | ||
type='button' | ||
base | ||
size='small' | ||
className='padding-top-05 padding-right-105 padding-bottom-05 padding-left-105' | ||
> | ||
<CollecticonTrashBin title='Delete area' /> | ||
Delete area | ||
</USWDSButton> | ||
</FloatingBar> | ||
)} | ||
|
||
<CustomAoIModal | ||
revealed={aoiModalRevealed} | ||
onConfirm={onUploadConfirm} | ||
onCloseClick={() => setAoIModalRevealed(false)} | ||
/> | ||
</> | ||
); | ||
|
||
return useThemedControl(() => control, { | ||
position: 'top-left' | ||
}); | ||
}; | ||
|
||
interface FloatingBarProps { | ||
children: React.ReactNode; | ||
container: HTMLElement; | ||
} | ||
|
||
function FloatingBar(props: FloatingBarProps) { | ||
const { container, children } = props; | ||
return createPortal(<FloatingBarSelf>{children}</FloatingBarSelf>, container); | ||
} | ||
|
||
export default AoiControl; |
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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the point of having this? cant we just call
resetAoi()
directly?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is originally a leftover artifact from old code, but I find it useful to keep event handlers in one place, for better organization and overview.
That said, I’m happy to inline it if you think it would improve clarity.