Skip to content

Commit

Permalink
create layer actions
Browse files Browse the repository at this point in the history
add up/down buttons to layer cards

add adjacent swap ordering buttons to layers

add layer move and delete actions
  • Loading branch information
mbwatson committed May 4, 2024
1 parent 9385b20 commit 463cd6b
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 99 deletions.
13 changes: 8 additions & 5 deletions src/components/map/default-layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ import { CircleMarker } from 'leaflet';
import { useLayers } from '@context';
import { markClicked } from '@utils/map-utils';

const newLayerDefaultState = layer => {
const newLayerDefaultState = (layer, order) => {
const { product_type } = layer.properties;

if (['obs', 'maxele63'].includes(product_type)) {
return ({
visible: true
order,
visible: true,
});
}

return ({
visible: false
order,
visible: false,
});
};

Expand Down Expand Up @@ -111,7 +113,7 @@ export const DefaultLayers = () => {
if (layer)
layer_list.push({
...layer,
state: newLayerDefaultState(layer)
state: newLayerDefaultState(layer, layer_list.length)
});

// TODO: do we really need to do this here??!
Expand Down Expand Up @@ -160,7 +162,8 @@ export const DefaultLayers = () => {
return (
<>
{defaultModelLayers
.filter(({state }) => state.visible)
.filter(({ state }) => state.visible)
.sort((a, b) => a.state.order - b.state.order)
.map((layer, index) => {
const pieces = layer.id.split('-');
const type = pieces[pieces.length-1];
Expand Down
4 changes: 3 additions & 1 deletion src/components/sidebar/tray.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
KeyboardDoubleArrowLeft as CloseTrayIcon,
} from '@mui/icons-material';

const TRAY_WIDTH = '500px';

export const Tray = ({ active, Contents, title, closeHandler }) => {
return (
<Sheet
Expand All @@ -23,7 +25,7 @@ export const Tray = ({ active, Contents, title, closeHandler }) => {
transform: active ? 'translateX(0)' : 'translateX(-120%)',
transition: 'transform 250ms',
height: '100vh',
width: '450px',
width: TRAY_WIDTH,
zIndex: 998,
filter: 'drop-shadow(0 0 8px rgba(0, 0, 0, 0.2))',
overflowX: 'hidden',
Expand Down
200 changes: 109 additions & 91 deletions src/components/trays/layers/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
AccordionGroup,
AccordionDetails,
Box,
ButtonGroup,
Divider,
IconButton,
ListItemContent,
Expand All @@ -12,13 +13,20 @@ import {
Typography,
} from '@mui/joy';
import {
DragIndicator as DragHandleIcon,
DeleteForever as RemoveIcon,
KeyboardArrowDown as ExpandIcon,
ArrowDropUp as MoveUpArrow,
ArrowDropDown as MoveDownArrow,
} from '@mui/icons-material';
import { useLayers } from '@context';

export const LayersList = () => {
const { defaultModelLayers, toggleLayerVisibility } = useLayers();
const {
defaultModelLayers,
removeLayer,
swapLayers,
toggleLayerVisibility,
} = useLayers();
const layers = [...defaultModelLayers];

// convert the product type to a readable layer name
Expand All @@ -31,8 +39,6 @@ export const LayersList = () => {
hec_ras_water_surface: "HEC/RAS Water Surface",
};

console.table(layers[0]);

const [expandedIds, setExpandedIds] = useState(new Set());

const handleToggleExpansion = id => () => {
Expand All @@ -46,106 +52,118 @@ export const LayersList = () => {
setExpandedIds(_expandedIds);
};

const handleClickRemove = id => () => {
removeLayer(id)
}

return (
<AccordionGroup variant="soft">
{
layers.map(layer => {
const isExpanded = expandedIds.has(layer.id);
const isVisible = layer.state.visible;
const layerTitle = layer_names[layer.properties.product_type] + " " +
layer.properties.run_date + " " +
layer.properties.cycle;
layers
.sort((a, b) => a.state.order - b.state.order)
.map(layer => {
const isExpanded = expandedIds.has(layer.id);
const isVisible = layer.state.visible;
const layerTitle = layer_names[layer.properties.product_type] + " " +
layer.properties.run_date + " " +
layer.properties.cycle;


return (
<Accordion
key={ `layer-${ layer.id }-${ isVisible ? 'visible' : 'hidden' }` }
expanded={ isExpanded }
onChange={ handleToggleExpansion }
sx={{ p: 0 }}
>
{/*
the usual AccordionSummary component results in a button,
but we want some buttons _inside_ the accordion summary,
so we'll build a custom component here.
*/}
<Stack
direction="row"
gap={ 1 }
sx={{
p: 1,
borderLeft: '6px solid',
borderLeftColor: isVisible ? 'primary.400' : 'primary.100',
'.MuiIconButton-root': { filter: 'opacity(0.25)', transition: 'filter 250ms' },
'.MuiSwitch-root': { filter: 'opacity(0.25)', transition: 'filter 250ms' },
'&:hover .MuiIconButton-root': { filter: 'opacity(1.0)' },
'&:hover .MuiSwitch-root': { filter: 'opacity(1.0)' },
}}
return (
<Accordion
key={ `layer-${ layer.id }-${ isVisible ? 'visible' : 'hidden' }` }
expanded={ isExpanded }
onChange={ handleToggleExpansion }
sx={{ p: 0 }}
>
<IconButton
size="sm"
{/*
the usual AccordionSummary component results in a button,
but we want some buttons _inside_ the accordion summary,
so we'll build a custom component here.
*/}
<Stack
direction="row"
gap={ 1 }
sx={{
cursor: 'grab',
filter: 'opacity(0.5)',
transition: 'filter 250ms',
'&:hover': {
filter: 'opacity(1.0)',
},
m: 0,
p: 1,
borderLeft: '6px solid',
borderLeftColor: isVisible ? 'primary.400' : 'primary.100',
'.MuiIconButton-root': { filter: 'opacity(0.1)', transition: 'filter 250ms' },
'.MuiSwitch-root': { filter: 'opacity(0.1)', transition: 'filter 250ms' },
'&:hover .MuiIconButton-root': { filter: 'opacity(0.5)' },
'&:hover .MuiSwitch-root': { filter: 'opacity(0.5)' },
'& .MuiIconButton-root:hover': { filter: 'opacity(1.0)' },
'& .MuiSwitch-root:hover': { filter: 'opacity(1.0)' },
}}
>
<DragHandleIcon fontSize="sm" />
</IconButton>
<ButtonGroup orientation="vertical" size="sm">
<IconButton
onClick={ () => swapLayers(layer.state.order, layer.state.order - 1) }
><MoveUpArrow /></IconButton>
<IconButton
onClick={ () => swapLayers(layer.state.order, layer.state.order + 1) }
><MoveDownArrow /></IconButton>
</ButtonGroup>

<ListItemContent>
<Typography level="title-md">
{layerTitle}
</Typography>
<Typography
component="div"
level="body-sm"
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
>
{ layer.layers }
</Typography>
</ListItemContent>

<ListItemContent>
<Typography level="title-md">
{layerTitle}
</Typography>
<Typography
component="div"
level="body-sm"
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
>
{ layer.layers }
</Typography>
</ListItemContent>
<Stack justifyContent="space-around">
<Switch
size="sm"
checked={ isVisible }
onChange={ () => toggleLayerVisibility(layer.id) }
/>

<Switch
size="sm"
checked={ isVisible }
onChange={ () => toggleLayerVisibility(layer.id) }
/>
<IconButton
onClick={ handleClickRemove(layer.id) }
size="sm"
color="danger"
>
<RemoveIcon />
</IconButton>
</Stack>

<IconButton onClick={ handleToggleExpansion(layer.id) }>
<ExpandIcon
fontSize="sm"
sx={{
transform: isExpanded ? 'rotate(180deg)' : 'rotate(0)',
transition: 'transform 100ms',
}}
/>
</IconButton>
</Stack>
<AccordionDetails variant="solid" sx={{
// remove default margin that doesn't work well in our situation.
marginInline: 0,
}}>
<Box component="pre" sx={{
fontSize: '75%',
color: '#def',
backgroundColor: 'transparent',
overflowX: 'auto',
<IconButton onClick={ handleToggleExpansion(layer.id) }>
<ExpandIcon
fontSize="sm"
sx={{
transform: isExpanded ? 'rotate(180deg)' : 'rotate(0)',
transition: 'transform 100ms',
}}
/>
</IconButton>
</Stack>
<AccordionDetails variant="solid" sx={{
// remove default margin that doesn't work well in our situation.
marginInline: 0,
}}>
{ JSON.stringify(layer.properties, null, 2) }
</Box>
</AccordionDetails>
</Accordion>
);
})
<Box component="pre" sx={{
fontSize: '75%',
color: '#def',
backgroundColor: 'transparent',
overflowX: 'auto',
}}>
{ JSON.stringify(layer.properties, null, 2) }
</Box>
</AccordionDetails>
</Accordion>
);
})
}
<Divider />
</AccordionGroup>
Expand Down
41 changes: 39 additions & 2 deletions src/context/map-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const LayersProvider = ({ children }) => {
const newLayers = [...defaultModelLayers];
const index = newLayers.findIndex(l => l.id === id);
if (index === -1) {
new Error('Could not locate layer', id);
console.error('Could not locate layer', id);
return;
}
const alteredLayer = newLayers[index];
Expand All @@ -29,6 +29,41 @@ export const LayersProvider = ({ children }) => {
]);
};

const swapLayers = (i, j) => {
const ithLayerIndex = defaultModelLayers
.findIndex(({ state }) => state.order === i);
const jthLayerIndex = defaultModelLayers
.findIndex(({ state }) => state.order === j);
if (ithLayerIndex === -1 || jthLayerIndex === -1) {
return defaultModelLayers;
}
const newLayers = [...defaultModelLayers] ;
newLayers[ithLayerIndex].state.order = j;
newLayers[jthLayerIndex].state.order = i;
setDefaultModelLayers(newLayers);
};

const removeLayer = id => {
const index = defaultModelLayers.findIndex(l => l.id === id)
if (index === -1) {
return
}
const thisPosition = defaultModelLayers[index].state.order
const newLayers = [...defaultModelLayers.slice(0, index), ...defaultModelLayers.slice(index + 1)]
.map(l => {
if (l.state.order > thisPosition) {
l.state.order -= 1;
}
return l
});

setDefaultModelLayers(newLayers)
/* todo: update `layer.state.order`s
layer.state.order - 1 for all layers l with l.state.order > this layer's state.order
*/
};


return (
<LayersContext.Provider
value={{
Expand All @@ -40,7 +75,9 @@ export const LayersProvider = ({ children }) => {
setFilteredModelLayers,
toggleLayerVisibility,
selectedObservations,
setSelectedObservations
setSelectedObservations,
swapLayers,
removeLayer
}}
>
{children}
Expand Down

0 comments on commit 463cd6b

Please sign in to comment.