Skip to content

Commit

Permalink
break layer card into separate component
Browse files Browse the repository at this point in the history
we don't keep expanded cards in state any longer.
  • Loading branch information
mbwatson committed May 4, 2024
1 parent 50c9253 commit 722f681
Showing 1 changed file with 168 additions and 148 deletions.
316 changes: 168 additions & 148 deletions src/components/trays/layers/list.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import {
Accordion,
AccordionGroup,
Expand All @@ -19,173 +20,192 @@ import {
ArrowDropDown as MoveDownArrow,
Schedule as ClockIcon,
DragIndicator as DragHandleIcon,
Opacity as OpacityIcon,
Palette as ColorRampIcon,
} from '@mui/icons-material';
import { useLayers } from '@context';
import { useToggleState } from '@hooks';

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

return (
<AccordionGroup variant="soft">
{
layers
.sort((a, b) => a.state.order - b.state.order)
.map((layer, index) => {
return (
<LayerCard
key={ `layer-${ layer.id }` }
layer={ layer }
index={ index }
/>
);
})
}
<Divider />
</AccordionGroup>
);
};

const LayerCard = ({ index, layer }) => {
const {
defaultModelLayers,
layerTypes,
removeLayer,
swapLayers,
toggleLayerVisibility,
} = useLayers();
const layers = [...defaultModelLayers];

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

const handleToggleExpansion = id => () => {
const _expandedIds = new Set([...expandedIds]);
if (_expandedIds.has(id)) {
_expandedIds.delete(id);
setExpandedIds(_expandedIds);
return;
}
_expandedIds.add(id);
setExpandedIds(_expandedIds);
};
const expanded = useToggleState(false);
const isVisible = layer.state.visible;
const LayerIcon = layerTypes[layer.properties.product_type].icon;

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

return (
<AccordionGroup variant="soft">
{
layers
.sort((a, b) => a.state.order - b.state.order)
.map((layer, index) => {
const isExpanded = expandedIds.has(layer.id);
const isVisible = layer.state.visible;
const LayerIcon = layerTypes[layer.properties.product_type].icon;
<Accordion
expanded={ expanded.enabled }
onChange={ expanded.toggle }
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',
'.actions': { filter: 'opacity(0.1)', transition: 'filter 250ms' },
'&:hover .actions': { filter: 'opacity(0.5)' },
'& .actions:hover': { filter: 'opacity(1.0)' },
}}
>
<Stack direction="column" sx={{ flex: 1 }}>
<Stack direction="row" alignItems="center" sx={{
flex: 1,
filter: isVisible ? 'opacity(1.0)' : 'opacity(0.75)',
transition: 'filter 250ms',
}}>
<Avatar sx={{ width: 36, height: 36 }}>
<LayerIcon size="lg" color="primary" />
</Avatar>
<Typography level="title-md">
{layerTypes[layer.properties.product_type].name}
</Typography>
</Stack>

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',
'.actions': { filter: 'opacity(0.1)', transition: 'filter 250ms' },
'&:hover .actions': { filter: 'opacity(0.5)' },
'& .actions:hover': { filter: 'opacity(1.0)' },
}}
>
<Stack direction="column" sx={{ flex: 1 }}>
<Stack direction="row" alignItems="center" sx={{
flex: 1,
filter: isVisible ? 'opacity(1.0)' : 'opacity(0.75)',
transition: 'filter 250ms',
}}>
<Avatar sx={{ width: 36, height: 36 }}>
<LayerIcon size="lg" color="primary" />
</Avatar>
<Typography level="title-md">
{layerTypes[layer.properties.product_type].name}
</Typography>
</Stack>
<Stack direction="row" alignItems="stretch" sx={{ flex: 1 }}>
<IconButton
size="sm"
sx={{
cursor: 'grab',
filter: 'opacity(0.5)',
transition: 'filter 250ms',
'&:hover': {
filter: 'opacity(1.0)',
},
m: 0,
}}
>
<DragHandleIcon />
</IconButton>
<Typography level="body-sm" sx={{ display: 'inline-flex', alignItems: 'center' }}>
<ClockIcon sx={{ transform: 'scale(0.66)' }} /> { new Date(layer.properties.run_date).toLocaleString() }
<Typography level="body-xs" sx={{ display: 'inline-flex', alignItems: 'center', ml: 3 }}>
Cycle { layer.properties.cycle }
</Typography>
</Typography>
</Stack>
</Stack>

<Stack direction="row" alignItems="stretch" sx={{ flex: 1 }}>
<IconButton
size="sm"
sx={{
cursor: 'grab',
filter: 'opacity(0.5)',
transition: 'filter 250ms',
'&:hover': {
filter: 'opacity(1.0)',
},
m: 0,
}}
>
<DragHandleIcon fontSize="sm" />
</IconButton>
<Typography level="body-sm" sx={{ display: 'inline-flex', alignItems: 'center' }}>
<ClockIcon sx={{ transform: 'scale(0.66)' }} /> { new Date(layer.properties.run_date).toLocaleString() }
<Typography level="body-xs" sx={{ display: 'inline-flex', alignItems: 'center', ml: 3 }}>
Cycle { layer.properties.cycle }
</Typography>
</Typography>
</Stack>
</Stack>
{/* layer card actions start */}
<Stack justifyContent="space-around" className="actions">
<Switch
size="sm"
checked={ isVisible }
onChange={ () => toggleLayerVisibility(layer.id) }
/>

{/* card actions start */}
<Stack justifyContent="space-around" className="actions">
<Switch
size="sm"
checked={ isVisible }
onChange={ () => toggleLayerVisibility(layer.id) }
/>
<IconButton
onClick={ handleClickRemove(layer.id) }
size="sm"
color="danger"
>
<RemoveIcon />
</IconButton>
</Stack>

<IconButton
onClick={ handleClickRemove(layer.id) }
size="sm"
color="danger"
>
<RemoveIcon />
</IconButton>
</Stack>
<Stack justifyContent="space-around" className="actions">
<IconButton size="sm" variant="soft" disabled>
<OpacityIcon />
</IconButton>
<IconButton size="sm" variant="soft" disabled>
<ColorRampIcon />
</IconButton>
</Stack>

<ButtonGroup
orientation="vertical"
size="sm"
className="actions"
sx={{
transform: 'scaleX(0.75)',
'.MuiIconButton-root': { flex: 1 }
}}
>
<IconButton
onClick={ () => swapLayers(index, index - 1) }
sx={{ flex: 1 }}
><MoveUpArrow /></IconButton>
<IconButton
onClick={ () => swapLayers(index, index + 1) }
sx={{ flex: 1 }}
><MoveDownArrow /></IconButton>
</ButtonGroup>

{/* layer card actions end */}

<IconButton
onClick={ expanded.toggle }
size="sm"
variant="soft"
>
<ExpandIcon
fontSize="sm"
sx={{
transform: expanded.enabled ? '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',
}}>
{ JSON.stringify(layer.properties, null, 2) }
</Box>
</AccordionDetails>
</Accordion>

<ButtonGroup
orientation="vertical"
size="sm"
className="actions"
sx={{
transform: 'scaleX(0.75)',
'.MuiIconButton-root': { flex: 1 }
}}
>
<IconButton
onClick={ () => swapLayers(index, index - 1) }
sx={{ flex: 1 }}
><MoveUpArrow /></IconButton>
<IconButton
onClick={ () => swapLayers(index, index + 1) }
sx={{ flex: 1 }}
><MoveDownArrow /></IconButton>
</ButtonGroup>
{/* card actions end */}

<IconButton onClick={ handleToggleExpansion(layer.id) } size="sm" variant="soft">
<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',
}}>
{ JSON.stringify(layer.properties, null, 2) }
</Box>
</AccordionDetails>
</Accordion>
);
})
}
<Divider />
</AccordionGroup>
);
};

LayerCard.propTypes = {
index: PropTypes.number.isRequired,
layer: PropTypes.object.isRequired,
};

0 comments on commit 722f681

Please sign in to comment.