diff --git a/src/components/datatable/BottomPanel.jsx b/src/components/datatable/BottomPanel.jsx index d28ed5769..bbdabed72 100644 --- a/src/components/datatable/BottomPanel.jsx +++ b/src/components/datatable/BottomPanel.jsx @@ -1,11 +1,13 @@ import { IconCross16 } from '@dhis2/ui' -import React, { useRef, useCallback } from 'react' +import React, { + useRef, + useCallback, + useState, + useEffect, + useLayoutEffect, +} from 'react' import { useSelector, useDispatch } from 'react-redux' import { closeDataTable, resizeDataTable } from '../../actions/dataTable.js' -import { - LAYERS_PANEL_WIDTH, - RIGHT_PANEL_WIDTH, -} from '../../constants/layout.js' import useKeyDown from '../../hooks/useKeyDown.js' import { getCssVar } from '../../util/helpers.js' import { useWindowDimensions } from '../WindowDimensionsProvider.jsx' @@ -17,26 +19,49 @@ import styles from './styles/BottomPanel.module.css' // Container for DataTable const BottomPanel = () => { const dataTableHeight = useSelector((state) => state.ui.dataTableHeight) - const layersPanelOpen = useSelector((state) => state.ui.layersPanelOpen) - const rightPanelOpen = useSelector((state) => state.ui.rightPanelOpen) const dispatch = useDispatch() - const { width, height } = useWindowDimensions() + const { height } = useWindowDimensions() const panelRef = useRef(null) - - const onResize = useCallback( - (h) => (panelRef.current.style.height = `${h}px`), - [panelRef] - ) + const [panelWidth, setPanelWidth] = useState(0) const maxHeight = height - getCssVar('--header-height') - getCssVar('--toolbar-height') const tableHeight = dataTableHeight < maxHeight ? dataTableHeight : maxHeight - const layersWidth = layersPanelOpen ? LAYERS_PANEL_WIDTH : 0 - const rightPanelWidth = rightPanelOpen ? RIGHT_PANEL_WIDTH : 0 - const tableWidth = width - layersWidth - rightPanelWidth - const dataTableControlsHeight = 20 + const onResize = useCallback((h) => { + document.documentElement.style.setProperty( + '--data-table-height', + `${h}px` + ) + }, []) + + useLayoutEffect(() => { + document.documentElement.style.setProperty( + '--data-table-height', + `${tableHeight}px` + ) + }, [tableHeight]) + + useLayoutEffect( + () => () => + document.documentElement.style.removeProperty( + '--data-table-height' + ), + [] + ) + + useEffect(() => { + const observer = new ResizeObserver(() => { + if (panelRef.current) { + setPanelWidth(panelRef.current.getBoundingClientRect().width) + } + }) + if (panelRef.current) { + observer.observe(panelRef.current) + } + return () => observer.disconnect() + }, []) useKeyDown('Escape', () => dispatch(closeDataTable()), true) @@ -44,7 +69,6 @@ const BottomPanel = () => {
{error}
@@ -246,7 +263,7 @@ const Table = ({ availableHeight, availableWidth }) => { context={tableContext} components={TableComponents} style={{ - height: availableHeight, + height: '100%', width: '100%', }} data={rows} @@ -321,7 +338,6 @@ const Table = ({ availableHeight, availableWidth }) => { } Table.propTypes = { - availableHeight: PropTypes.number, availableWidth: PropTypes.number, } diff --git a/src/components/datatable/styles/BottomPanel.module.css b/src/components/datatable/styles/BottomPanel.module.css index 19e3c0f83..68ce43eab 100644 --- a/src/components/datatable/styles/BottomPanel.module.css +++ b/src/components/datatable/styles/BottomPanel.module.css @@ -2,8 +2,18 @@ position: absolute; left: 0; bottom: 0; + width: 100%; + height: var(--data-table-height); z-index: 1040; background: #fff; + display: flex; + flex-direction: column; +} + +.tableContainer { + flex: 1; + min-height: 0; + position: relative; } .dataTableControls { diff --git a/src/components/download/OverviewMapOutline.js b/src/components/download/OverviewMapOutline.js index a0d58a9d6..3482466a6 100644 --- a/src/components/download/OverviewMapOutline.js +++ b/src/components/download/OverviewMapOutline.js @@ -1,6 +1,7 @@ import PropTypes from 'prop-types' -import { useState, useCallback, useEffect } from 'react' +import { useState, useRef, useCallback, useEffect } from 'react' import { GEOJSON_LAYER } from '../../constants/layers.js' +import { getCssColor } from '../../util/colors.js' import { getCoordinatesBounds } from '../../util/geojson.js' const layerId = 'overview-outline' @@ -30,7 +31,8 @@ const getMapOutline = (map) => { const OverviewMapOutline = ({ mainMap, overviewMap, isDark = false }) => { const [outline, setOutline] = useState(getMapOutline(mainMap)) - const [sourceId, setSourceId] = useState() + const sourceIdRef = useRef() + const layerRef = useRef() const onMainMapMove = useCallback(() => { setOutline(getMapOutline(mainMap)) @@ -45,6 +47,9 @@ const OverviewMapOutline = ({ mainMap, overviewMap, isDark = false }) => { useEffect(() => { if (outline) { + const strokeColor = isDark + ? getCssColor('--colors-grey300') + : getCssColor('--colors-grey900') const config = { type: GEOJSON_LAYER, id: layerId, @@ -52,20 +57,30 @@ const OverviewMapOutline = ({ mainMap, overviewMap, isDark = false }) => { data: [outline], style: { color: 'transparent', - strokeColor: isDark ? 'orange' : '#333', + strokeColor, weight: 3, }, } - if (!sourceId) { - const layer = overviewMap.createLayer(config) - overviewMap.addLayer(layer) - setSourceId(layer.getId()) - } else { - const source = overviewMap.getMapGL().getSource(sourceId) + if (sourceIdRef.current) { + const mapGl = overviewMap.getMapGL() + const source = mapGl.getSource(sourceIdRef.current) if (source) { source.setData(outline) } + const outlineLayerId = `${sourceIdRef.current}-outline` + if (mapGl.getLayer(outlineLayerId)) { + mapGl.setPaintProperty( + outlineLayerId, + 'line-color', + strokeColor + ) + } + } else { + const layer = overviewMap.createLayer(config) + overviewMap.addLayer(layer) + sourceIdRef.current = layer.getId() + layerRef.current = layer } // Make sure outline bounds is inside overview map bounds @@ -83,7 +98,17 @@ const OverviewMapOutline = ({ mainMap, overviewMap, isDark = false }) => { overviewMap.getMapGL().fitBounds(outlineBounds, { padding: 80 }) } } - }, [overviewMap, outline, sourceId, isDark]) + }, [overviewMap, outline, isDark]) + + useEffect(() => { + return () => { + if (layerRef.current) { + overviewMap.removeLayer(layerRef.current) + layerRef.current = undefined + sourceIdRef.current = undefined + } + } + }, [overviewMap]) return null } diff --git a/src/components/map/MapPosition.jsx b/src/components/map/MapPosition.jsx index cbc420aa3..b66119caf 100644 --- a/src/components/map/MapPosition.jsx +++ b/src/components/map/MapPosition.jsx @@ -1,5 +1,5 @@ import cx from 'classnames' -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useRef } from 'react' import { useSelector } from 'react-redux' import { getSplitViewLayer } from '../../util/helpers.js' import DownloadMapInfo from '../download/DownloadMapInfo.jsx' @@ -7,9 +7,12 @@ import NorthArrow from '../download/NorthArrow.jsx' import MapContainer from '../map/MapContainer.jsx' import styles from './styles/MapPosition.module.css' +const incrementCount = (c) => c + 1 + const MapPosition = () => { const [map, setMap] = useState() const [resizeCount, setResizeCount] = useState(0) + const outerRef = useRef(null) const { showName, showDescription, @@ -31,10 +34,11 @@ const MapPosition = () => { showOverviewMap) const isSplitView = !!getSplitViewLayer(layers) + const mapGL = map?.getMapGL() // Trigger map resize when panels are expanded, collapsed or dragged useEffect(() => { - setResizeCount((count) => count + 1) + setResizeCount(incrementCount) }, [ dataTableOpen, dataTableHeight, @@ -43,6 +47,30 @@ const MapPosition = () => { rightPanelOpen, ]) + // Trigger map resize continuously during ResizeHandle drag (CSS variable drives height, not Redux) + useEffect(() => { + if (!outerRef.current) { + return + } + let frameId = null + const observer = new ResizeObserver(() => { + if (frameId !== null) { + return + } + frameId = requestAnimationFrame(() => { + setResizeCount(incrementCount) + frameId = null + }) + }) + observer.observe(outerRef.current) + return () => { + observer.disconnect() + if (frameId !== null) { + cancelAnimationFrame(frameId) + } + } + }, []) + // Reset bearing and pitch when new map (mapId changed) useEffect(() => { if (map) { @@ -60,6 +88,10 @@ const MapPosition = () => { if (map) { const mapgl = map.getMapGL() + if (!mapgl) { + return + } + mapgl.once('resize', () => { map.fitBounds(map.getLayersBounds(), { padding: 40, @@ -77,17 +109,12 @@ const MapPosition = () => { return (