-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
330 additions
and
285 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
144 changes: 74 additions & 70 deletions
144
packages/geoview-core/src/core/components/legend/legend-layer-container.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 |
---|---|---|
@@ -1,70 +1,74 @@ | ||
import { useTheme } from '@mui/material'; | ||
import { memo } from 'react'; | ||
import { Box, Collapse, List } from '@/ui'; | ||
import { TypeLegendLayer } from '@/core/components/layers/types'; | ||
import { getSxClasses } from './legend-styles'; | ||
import { logger } from '@/core/utils/logger'; | ||
import { CV_CONST_LAYER_TYPES } from '@/api/config/types/config-constants'; | ||
import { ItemsList } from './legend-layer-items'; | ||
|
||
// Define component types and interfaces | ||
type LegendLayerType = React.FC<{ layer: TypeLegendLayer }>; | ||
|
||
interface CollapsibleContentProps { | ||
layer: TypeLegendLayer; | ||
legendExpanded: boolean; | ||
initLightBox: (imgSrc: string, title: string, index: number, total: number) => void; | ||
childLayers: TypeLegendLayer[]; | ||
items: TypeLegendLayer['items']; | ||
LegendLayerComponent: LegendLayerType; | ||
} | ||
|
||
// CollapsibleContent component moved after LegendLayer | ||
export const CollapsibleContent = memo(function CollapsibleContent({ | ||
layer, | ||
legendExpanded, | ||
initLightBox, | ||
childLayers, | ||
items, | ||
LegendLayerComponent, | ||
}: CollapsibleContentProps) { | ||
logger.logDebug('legend1 collapsible', layer, childLayers, items); | ||
const theme = useTheme(); | ||
const sxClasses = getSxClasses(theme); | ||
|
||
if (layer.type === CV_CONST_LAYER_TYPES.WMS && layer.icons.length && layer.icons[0].iconImage && layer.icons[0].iconImage !== 'no data') { | ||
const imgSrc = layer.icons[0].iconImage; | ||
return ( | ||
<Collapse in={legendExpanded} sx={sxClasses.collapsibleContainer} timeout="auto"> | ||
<Box | ||
component="img" | ||
tabIndex={0} | ||
src={imgSrc} | ||
sx={{ maxWidth: '100%', cursor: 'pointer' }} | ||
onClick={() => initLightBox(imgSrc, '', 0, 2)} | ||
onKeyDown={(e) => (e.code === 'Space' || e.code === 'Enter' ? initLightBox(imgSrc, '', 0, 2) : null)} | ||
/> | ||
</Collapse> | ||
); | ||
} | ||
|
||
// if (!(childLayers?.length > 1 || items?.length > 1)) { | ||
// return null; | ||
// } | ||
|
||
// TODO: childslayers always empty... seems to be use for items | ||
return ( | ||
<Collapse in={legendExpanded} sx={sxClasses.collapsibleContainer} timeout="auto"> | ||
{layer.children?.length > 0 && ( | ||
<List sx={{ width: '100%', padding: '20px', margin: '20px 0px' }}> | ||
{layer.children | ||
.filter((d) => !['error', 'processing'].includes(d.layerStatus ?? '')) | ||
.map((item) => ( | ||
<LegendLayerComponent layer={item} key={item.layerPath} /> | ||
))} | ||
</List> | ||
)} | ||
<ItemsList items={items} /> | ||
</Collapse> | ||
); | ||
}); | ||
import { useTheme } from '@mui/material'; | ||
import { memo } from 'react'; | ||
import { Box, Collapse, List } from '@/ui'; | ||
import { TypeLegendLayer } from '@/core/components/layers/types'; | ||
import { getSxClasses } from './legend-styles'; | ||
import { logger } from '@/core/utils/logger'; | ||
import { CV_CONST_LAYER_TYPES } from '@/api/config/types/config-constants'; | ||
import { ItemsList } from './legend-layer-items'; | ||
|
||
// Define component types and interfaces | ||
type LegendLayerType = React.FC<{ layer: TypeLegendLayer }>; | ||
|
||
interface CollapsibleContentProps { | ||
layer: TypeLegendLayer; | ||
legendExpanded: boolean; | ||
initLightBox: (imgSrc: string, title: string, index: number, total: number) => void; | ||
childLayers: TypeLegendLayer[]; | ||
items: TypeLegendLayer['items']; | ||
LegendLayerComponent: LegendLayerType; | ||
} | ||
|
||
// CollapsibleContent component moved after LegendLayer | ||
export const CollapsibleContent = memo(function CollapsibleContent({ | ||
layer, | ||
legendExpanded, | ||
initLightBox, | ||
childLayers, | ||
items, | ||
LegendLayerComponent, | ||
}: CollapsibleContentProps) { | ||
logger.logDebug('legend1 collapsible', layer, childLayers, items); | ||
const theme = useTheme(); | ||
const sxClasses = getSxClasses(theme); | ||
|
||
// If it is a WMS and there is a legend image, add it with the lightbox handlers | ||
if (layer.type === CV_CONST_LAYER_TYPES.WMS && layer.icons.length && layer.icons[0].iconImage && layer.icons[0].iconImage !== 'no data') { | ||
const imgSrc = layer.icons[0].iconImage; | ||
return ( | ||
<Collapse in={legendExpanded} sx={sxClasses.collapsibleContainer} timeout="auto"> | ||
<Box | ||
component="img" | ||
tabIndex={0} | ||
src={imgSrc} | ||
sx={{ maxWidth: '100%', cursor: 'pointer' }} | ||
onClick={() => initLightBox(imgSrc, '', 0, 2)} | ||
onKeyDown={(e) => (e.code === 'Space' || e.code === 'Enter' ? initLightBox(imgSrc, '', 0, 2) : null)} | ||
/> | ||
</Collapse> | ||
); | ||
} | ||
|
||
// If there is only one item or no childlayer, do not create the component | ||
if (childLayers?.length === 0 && items?.length === 1) { | ||
return null; | ||
} | ||
|
||
if (layer.children?.length > 0) layer.children.every((item) => logger.logDebug('TTT item', item.layerPath)); | ||
|
||
// Render list of items (layer class) or there is a child layer so render a new legend-layer component | ||
return ( | ||
<Collapse in={legendExpanded} sx={sxClasses.collapsibleContainer} timeout="auto"> | ||
{layer.children?.length > 0 && ( | ||
<List sx={{ width: '100%', padding: '20px', margin: '20px 0px' }}> | ||
{layer.children | ||
.filter((d) => !['error', 'processing'].includes(d.layerStatus ?? '')) | ||
.map((item) => ( | ||
<LegendLayerComponent layer={item} key={item.layerPath} /> | ||
))} | ||
</List> | ||
)} | ||
<ItemsList items={items} /> | ||
</Collapse> | ||
); | ||
}); |
222 changes: 111 additions & 111 deletions
222
packages/geoview-core/src/core/components/legend/legend-layer-ctrl.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 |
---|---|---|
@@ -1,111 +1,111 @@ | ||
import { useTheme } from '@mui/material'; | ||
import { memo, useCallback, useState } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { | ||
Box, | ||
IconButton, | ||
Stack, | ||
VisibilityOutlinedIcon, | ||
HighlightOutlinedIcon, | ||
ZoomInSearchIcon, | ||
Typography, | ||
VisibilityOffOutlinedIcon, | ||
HighlightIcon, | ||
} from '@/ui'; | ||
import { useLayerHighlightedLayer, useLayerStoreActions } from '@/core/stores/store-interface-and-intial-values/layer-state'; | ||
import { TypeLegendLayer } from '@/core/components/layers/types'; | ||
import { useMapStoreActions } from '@/core/stores/'; | ||
import { getSxClasses } from './legend-styles'; | ||
import { logger } from '@/core/utils/logger'; | ||
|
||
interface SecondaryControlsProps { | ||
layer: TypeLegendLayer; | ||
layerStatus: string; | ||
itemsLength: number; | ||
childLayers: TypeLegendLayer[]; | ||
} | ||
|
||
// SecondaryControls component | ||
export const SecondaryControls = memo(function SecondaryControls({ layer, layerStatus, itemsLength, childLayers }: SecondaryControlsProps) { | ||
logger.logDebug('legend1 - ctrl', layer, layerStatus, itemsLength, childLayers); | ||
// Hooks | ||
const { t } = useTranslation(); | ||
const theme = useTheme(); | ||
const sxClasses = getSxClasses(theme); | ||
|
||
// Stores | ||
const highlightedLayer = useLayerHighlightedLayer(); | ||
const { getVisibilityFromOrderedLayerInfo, setOrToggleLayerVisibility } = useMapStoreActions(); | ||
const { setHighlightLayer, zoomToLayerExtent } = useLayerStoreActions(); | ||
|
||
const [visibility, setVisibility] = useState(getVisibilityFromOrderedLayerInfo(layer.layerPath)); | ||
const isLayerVisible = layer.controls?.visibility ?? false; | ||
|
||
// #region Handlers | ||
const handleToggleVisibility = useCallback( | ||
(e: React.MouseEvent<HTMLButtonElement>): void => { | ||
e.stopPropagation(); | ||
setOrToggleLayerVisibility(layer.layerPath); | ||
setVisibility(getVisibilityFromOrderedLayerInfo(layer.layerPath)); | ||
}, | ||
[layer.layerPath, setOrToggleLayerVisibility] | ||
); | ||
|
||
const handleHighlightLayer = useCallback( | ||
(e: React.MouseEvent<HTMLButtonElement>): void => { | ||
e.stopPropagation(); | ||
setHighlightLayer(layer.layerPath); | ||
}, | ||
[layer.layerPath, setHighlightLayer] | ||
); | ||
|
||
const handleZoomTo = useCallback( | ||
(e: React.MouseEvent<HTMLButtonElement>): void => { | ||
e.stopPropagation(); | ||
zoomToLayerExtent(layer.layerPath).catch((error) => { | ||
logger.logPromiseFailed('in zoomToLayerExtent in legend-layer.handleZoomTo', error); | ||
}); | ||
}, | ||
[layer.layerPath, zoomToLayerExtent] | ||
); | ||
// #endregion Handlers | ||
|
||
if (!['processed', 'loaded'].includes(layerStatus)) { | ||
return <Box />; | ||
} | ||
|
||
let subTitle = ''; | ||
if (childLayers.length) { | ||
subTitle = t('legend.subLayersCount').replace('{count}', childLayers.length.toString()); | ||
} else if (itemsLength > 1) { | ||
subTitle = t('legend.itemsCount').replace('{count}', itemsLength.toString()).replace('{totalCount}', itemsLength.toString()); | ||
} | ||
|
||
return ( | ||
<Stack direction="row" alignItems="center" sx={sxClasses.layerStackIcons}> | ||
{!!subTitle.length && <Typography fontSize={14}>{subTitle}</Typography>} | ||
<Box> | ||
<IconButton | ||
edge="end" | ||
tooltip="layers.toggleVisibility" | ||
className="buttonOutline" | ||
onClick={handleToggleVisibility} | ||
disabled={!isLayerVisible} | ||
> | ||
{visibility ? <VisibilityOffOutlinedIcon /> : <VisibilityOutlinedIcon />} | ||
</IconButton> | ||
<IconButton | ||
tooltip="legend.highlightLayer" | ||
sx={{ marginTop: '-0.3125rem' }} | ||
className="buttonOutline" | ||
onClick={handleHighlightLayer} | ||
> | ||
{highlightedLayer === layer.layerPath ? <HighlightIcon /> : <HighlightOutlinedIcon />} | ||
</IconButton> | ||
<IconButton tooltip="legend.zoomTo" className="buttonOutline" onClick={handleZoomTo}> | ||
<ZoomInSearchIcon /> | ||
</IconButton> | ||
</Box> | ||
</Stack> | ||
); | ||
}); | ||
import { useTheme } from '@mui/material'; | ||
import { useCallback, useMemo } from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { | ||
Box, | ||
IconButton, | ||
Stack, | ||
VisibilityOutlinedIcon, | ||
HighlightOutlinedIcon, | ||
ZoomInSearchIcon, | ||
Typography, | ||
VisibilityOffOutlinedIcon, | ||
HighlightIcon, | ||
} from '@/ui'; | ||
import { useLayerHighlightedLayer, useLayerStoreActions } from '@/core/stores/store-interface-and-intial-values/layer-state'; | ||
import { TypeLegendLayer } from '@/core/components/layers/types'; | ||
import { useMapStoreActions } from '@/core/stores/'; | ||
import { getSxClasses } from './legend-styles'; | ||
import { logger } from '@/core/utils/logger'; | ||
|
||
interface SecondaryControlsProps { | ||
layer: TypeLegendLayer; | ||
layerStatus: string; | ||
itemsLength: number; | ||
childLayers: TypeLegendLayer[]; | ||
visibility: boolean; | ||
} | ||
|
||
// SecondaryControls component | ||
export function SecondaryControls({ layer, layerStatus, itemsLength, childLayers, visibility }: SecondaryControlsProps): JSX.Element { | ||
// Hooks | ||
const { t } = useTranslation(); | ||
const theme = useTheme(); | ||
const sxClasses = useMemo(() => getSxClasses(theme), [theme]); | ||
|
||
// Stores | ||
const highlightedLayer = useLayerHighlightedLayer(); | ||
const { setOrToggleLayerVisibility } = useMapStoreActions(); | ||
const { setHighlightLayer, zoomToLayerExtent } = useLayerStoreActions(); | ||
|
||
// Is button disabled? | ||
const isLayerVisible = layer.controls?.visibility ?? false; | ||
|
||
// #region Handlers Callbacks | ||
const handleToggleVisibility = useCallback( | ||
(e: React.MouseEvent<HTMLButtonElement>): void => { | ||
e.stopPropagation(); | ||
setOrToggleLayerVisibility(layer.layerPath); | ||
}, | ||
[layer.layerPath, setOrToggleLayerVisibility] | ||
); | ||
|
||
const handleHighlightLayer = useCallback( | ||
(e: React.MouseEvent<HTMLButtonElement>): void => { | ||
e.stopPropagation(); | ||
setHighlightLayer(layer.layerPath); | ||
}, | ||
[layer.layerPath, setHighlightLayer] | ||
); | ||
|
||
const handleZoomTo = useCallback( | ||
(e: React.MouseEvent<HTMLButtonElement>): void => { | ||
e.stopPropagation(); | ||
zoomToLayerExtent(layer.layerPath).catch((error) => { | ||
logger.logPromiseFailed('in zoomToLayerExtent in legend-layer.handleZoomTo', error); | ||
}); | ||
}, | ||
[layer.layerPath, zoomToLayerExtent] | ||
); | ||
// #endregion Handlers | ||
|
||
if (!['processed', 'loaded'].includes(layerStatus)) { | ||
return <Box />; | ||
} | ||
|
||
// Calculate subtitle after the condition | ||
let subTitle = ''; | ||
if (childLayers.length) { | ||
subTitle = t('legend.subLayersCount').replace('{count}', childLayers.length.toString()); | ||
} else if (itemsLength > 1) { | ||
subTitle = t('legend.itemsCount').replace('{count}', itemsLength.toString()).replace('{totalCount}', itemsLength.toString()); | ||
} | ||
|
||
return ( | ||
<Stack direction="row" alignItems="center" sx={sxClasses.layerStackIcons}> | ||
{!!subTitle.length && <Typography fontSize={14}>{subTitle}</Typography>} | ||
<Box sx={sxClasses.subtitle}> | ||
<IconButton | ||
edge="end" | ||
tooltip="layers.toggleVisibility" | ||
className="buttonOutline" | ||
onClick={handleToggleVisibility} | ||
disabled={!isLayerVisible} | ||
> | ||
{visibility ? <VisibilityOutlinedIcon /> : <VisibilityOffOutlinedIcon />} | ||
</IconButton> | ||
<IconButton | ||
tooltip="legend.highlightLayer" | ||
sx={{ marginTop: '-0.3125rem' }} | ||
className="buttonOutline" | ||
onClick={handleHighlightLayer} | ||
> | ||
{highlightedLayer === layer.layerPath ? <HighlightIcon /> : <HighlightOutlinedIcon />} | ||
</IconButton> | ||
<IconButton tooltip="legend.zoomTo" className="buttonOutline" onClick={handleZoomTo}> | ||
<ZoomInSearchIcon /> | ||
</IconButton> | ||
</Box> | ||
</Stack> | ||
); | ||
} |
Oops, something went wrong.