-
+const LayersPanel = () => {
+ const layersPanelOpen = useSelector((state) => state.ui.layersPanelOpen)
+ const layers = useSelector((state) => [...state.map.mapViews].reverse())
+
+ const dispatch = useDispatch()
+
+ const onSort = () => dispatch(sortLayers())
+
+ return (
+
+
+ {layersPanelOpen ? (
+ <>
+
+
+
+
+ >
+ ) : null}
-
+
+
)
-
-LayersPanel.propTypes = {
- layers: PropTypes.array.isRequired,
- layersPanelOpen: PropTypes.bool.isRequired,
- sortLayers: PropTypes.func.isRequired,
}
-const mapStateToProps = (state) => ({
- layers: [...state.map.mapViews].reverse(),
- layersPanelOpen: state.ui.layersPanelOpen,
-})
-
-export default connect(mapStateToProps, { sortLayers })(LayersPanel)
+export default LayersPanel
diff --git a/src/components/layers/LayersToggle.js b/src/components/layers/LayersToggle.js
index 6b3683146..f9f31a69c 100644
--- a/src/components/layers/LayersToggle.js
+++ b/src/components/layers/LayersToggle.js
@@ -1,11 +1,11 @@
import { IconChevronLeft24, IconChevronRight24 } from '@dhis2/ui'
+import cx from 'classnames'
import PropTypes from 'prop-types'
import React from 'react'
import { connect } from 'react-redux'
import { openLayersPanel, closeLayersPanel } from '../../actions/ui.js'
import styles from './styles/LayersToggle.module.css'
-// This expand/collapse toggle is separate from LayersPanel to avoid overflow issue
const LayersToggle = ({
isOpen,
isDownload,
@@ -15,8 +15,7 @@ const LayersToggle = ({
!isDownload && (
{isOpen ? : }
diff --git a/src/components/layers/overlays/AddLayerButton.js b/src/components/layers/overlays/AddLayerButton.js
index 60459ba65..7adf7871d 100644
--- a/src/components/layers/overlays/AddLayerButton.js
+++ b/src/components/layers/overlays/AddLayerButton.js
@@ -1,7 +1,6 @@
import i18n from '@dhis2/d2-i18n'
import { IconAddCircle24 } from '@dhis2/ui'
-import React, { Fragment, useState, useRef } from 'react'
-import { MenuButton } from '../../core/index.js'
+import React, { useState, useRef } from 'react'
import AddLayerPopover from './AddLayerPopover.js'
import styles from './styles/AddLayerButton.module.css'
@@ -11,19 +10,23 @@ const AddLayerButton = () => {
const toggleDialog = () => setIsOpen(!isOpen)
return (
-
-
-
-
+ <>
+
+
{isOpen && (
)}
-
+ >
)
}
diff --git a/src/components/layers/overlays/styles/AddLayerButton.module.css b/src/components/layers/overlays/styles/AddLayerButton.module.css
index 2542ee503..529d04c8d 100644
--- a/src/components/layers/overlays/styles/AddLayerButton.module.css
+++ b/src/components/layers/overlays/styles/AddLayerButton.module.css
@@ -1,16 +1,49 @@
-.addLayerBtn {
- margin-right: var(--spacers-dp8);
- border-right: 1px solid #e0e0e0;
-}
+/* Adapted from https: //github.com/dhis2/analytics/blob/master/src/components/Toolbar/MenuButton.styles.js */
-.btnContent {
- width: 276px;
- padding-left: var(--spacers-dp8);
- text-align: left;
+.container {
+ width: var(--left-panel-width);
display: flex;
+ align-items: stretch;
+ border-right: 1px solid var(--colors-grey400);
+}
+
+.button {
+ all: unset;
+ display: inline-flex;
+ align-items: center;
+ justify-content: flex-start;
+ font-size: 14px;
+ line-height: 14px;
+ color: var(--colors-grey900);
+ cursor: pointer;
+ user-select: none;
+ padding: 0px 12px;
+ width: 100%
+}
+
+.content {
+ display: inline-flex;
+ align-items: center;
+ padding: var(--spacers-dp4) var(--spacers-dp8);
+ gap: var(--spacers-dp8);
+}
+
+.button:hover:enabled,
+.button:active {
+ background-color: var(--colors-grey200);
+}
+
+.button:focus {
+ outline: 3px solid var(--theme-focus);
+ outline-offset: -3px;
+}
+
+/* Prevent focus styles when mouse clicking */
+.button:focus:not(:focus-visible) {
+ outline: none;
}
-.btnContent svg {
- margin-right: var(--spacers-dp8);
- color: var(--colors-grey700);
+.button:disabled {
+ color: var(--colors-grey500);
+ cursor: not-allowed;
}
diff --git a/src/components/layers/styles/LayersPanel.module.css b/src/components/layers/styles/LayersPanel.module.css
new file mode 100644
index 000000000..7edd3910a
--- /dev/null
+++ b/src/components/layers/styles/LayersPanel.module.css
@@ -0,0 +1,21 @@
+.layersPanel {
+ width: var(--left-panel-width);
+}
+
+.layersPanel.collapsed {
+ width: 0px;
+}
+
+.layersPanelInner {
+ background-color: #f4f6f8;
+ border-right: 1px solid #e0e0e0;
+ box-shadow: 1px 0 1px 0 rgba(0, 0, 0, 0.2);
+ height: 100%;
+ overflow-y: auto;
+ z-index: 1190;
+ transform: translateX(0);
+}
+
+.layersPanel.collapsed .layersPanelInner {
+ transform: translateX(calc(var(--left-panel-width) * -1));
+}
diff --git a/src/components/layers/styles/LayersToggle.module.css b/src/components/layers/styles/LayersToggle.module.css
index 5ec6ae5df..e4dcfa7a4 100644
--- a/src/components/layers/styles/LayersToggle.module.css
+++ b/src/components/layers/styles/LayersToggle.module.css
@@ -1,7 +1,7 @@
.layersToggle {
position: absolute;
top: 104px;
- left: 300px;
+ left: var(--left-panel-width);
width: 24px;
height: 40px;
padding: var(--spacers-dp8) 0;
@@ -12,6 +12,10 @@
cursor: pointer;
}
+.layersToggle.collapsed {
+ left: 0;
+}
+
.layersToggle:hover {
background-color: var(--colors-grey100);
}
diff --git a/src/components/map/MapPosition.js b/src/components/map/MapPosition.js
index 7d0098d34..67a821e5e 100644
--- a/src/components/map/MapPosition.js
+++ b/src/components/map/MapPosition.js
@@ -1,12 +1,7 @@
import cx from 'classnames'
import React, { useState, useEffect } from 'react'
import { useSelector } from 'react-redux'
-import {
- APP_MENU_HEIGHT,
- DOWNLOAD_MENU_HEIGHT,
- LAYERS_PANEL_WIDTH,
- RIGHT_PANEL_WIDTH,
-} from '../../constants/layout.js'
+import { APP_MENU_HEIGHT, HEADER_HEIGHT } from '../../constants/layout.js'
import { getSplitViewLayer } from '../../util/helpers.js'
import DownloadMapInfo from '../download/DownloadMapInfo.js'
import NorthArrow from '../download/NorthArrow.js'
@@ -31,6 +26,15 @@ const MapPosition = () => {
)
const dataTableOpen = useSelector((state) => !!state.dataTable)
+ let mapHeight = `calc(100vh - ${HEADER_HEIGHT}px)`
+ if (!downloadMode) {
+ if (dataTableOpen) {
+ mapHeight = `calc(100vh - ${HEADER_HEIGHT}px - ${APP_MENU_HEIGHT}px - ${dataTableHeight}px)`
+ } else {
+ mapHeight = `calc(100vh - ${HEADER_HEIGHT}px - ${APP_MENU_HEIGHT}px)`
+ }
+ }
+
const downloadMapInfoOpen =
downloadMode &&
(showName ||
@@ -40,22 +44,15 @@ const MapPosition = () => {
const isSplitView = !!getSplitViewLayer(layers)
- const mapPosition = {
- top: downloadMode ? DOWNLOAD_MENU_HEIGHT : APP_MENU_HEIGHT,
- left: layersPanelOpen || downloadMode ? LAYERS_PANEL_WIDTH : 0,
- right: rightPanelOpen ? RIGHT_PANEL_WIDTH : 0,
- bottom: dataTableOpen ? dataTableHeight : 0,
- }
-
// Trigger map resize when panels are expanded, collapsed or dragged
useEffect(() => {
setResizeCount((count) => count + 1)
}, [
- layersPanelOpen,
- rightPanelOpen,
dataTableOpen,
dataTableHeight,
downloadMapInfoOpen,
+ layersPanelOpen,
+ rightPanelOpen,
])
// Reset bearing and pitch when new map (mapId changed)
@@ -91,7 +88,7 @@ const MapPosition = () => {
}, [map, downloadMode])
return (
-
+
{
>
diff --git a/src/components/map/styles/MapPosition.module.css b/src/components/map/styles/MapPosition.module.css
index 4c241c171..98f2f90d8 100644
--- a/src/components/map/styles/MapPosition.module.css
+++ b/src/components/map/styles/MapPosition.module.css
@@ -1,13 +1,36 @@
-.mapPosition {
- position: absolute;
+.mapPosition > div {
+ height: 100%;
}
-.mapPosition > div {
+.mapContainer {
height: 100%;
+ width: 100%;
+ position: relative;
+ overflow: hidden;
+ flex: auto
+}
+
+.mapContainer.download {
+ display: flex;
+ flex-direction: row;
+ gap: var(--spacers-dp24);
+}
+
+.mapContainerDownload {
+ flex: auto;
+}
+
+:not(.downloadMapInfoOpen) > div.download > div:first-child {
+ width: 100%;
+}
+
+.downloadMapInfoOpen > div.download > div:first-child {
+ flex: auto;
}
.mapDownload {
- border: var(--spacers-dp24) solid #fff
+ border: var(--spacers-dp24) solid #fff;
+ background-color: var(--colors-white);
}
.mapDownload :global(.dhis2-map-timeline) {
@@ -16,7 +39,7 @@
.mapDownload :global(.dhis2-map-period) {
bottom: 10px !important;
-}
+}
.mapDownload :global(.maplibregl-ctrl-group:not(:empty)) {
box-shadow: none;
@@ -32,14 +55,3 @@
border-right: none;
border-bottom: none;
}
-
-.downloadMapInfoOpen > div > div:first-child {
- width: calc(100% - 380px);
-}
-
-.mapContainer {
- height: 100%;
- width: 100%;
- position: relative;
- overflow: hidden;
-}
diff --git a/src/constants/layout.js b/src/constants/layout.js
index 99e56e748..a9128dbe4 100644
--- a/src/constants/layout.js
+++ b/src/constants/layout.js
@@ -1,5 +1,4 @@
-export const HEADER_HEIGHT = 86
-export const APP_MENU_HEIGHT = 38
-export const DOWNLOAD_MENU_HEIGHT = 44
+export const HEADER_HEIGHT = 48
+export const APP_MENU_HEIGHT = 32
export const LAYERS_PANEL_WIDTH = 300
export const RIGHT_PANEL_WIDTH = 380
diff --git a/yarn.lock b/yarn.lock
index b886043cb..11fbd9bda 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2015,17 +2015,22 @@
classnames "^2.3.1"
prop-types "^15.7.2"
-"@dhis2/analytics@^24.10.1":
- version "24.10.1"
- resolved "https://registry.yarnpkg.com/@dhis2/analytics/-/analytics-24.10.1.tgz#ad595451b724044448c22a413875f0d36df20e8d"
- integrity sha512-HhKDYzTnk8Uh11523JIjx0lhgIRgcWihCkTlT3qHoaDOhAQdQcadd5i/byjhXpeXrrtqfL0x2gn0Scy3siA2Cg==
+"@dhis2/analytics@^26.0.17":
+ version "26.0.17"
+ resolved "https://registry.yarnpkg.com/@dhis2/analytics/-/analytics-26.0.17.tgz#f6f550c266478c38872429ee422f6a890b189a82"
+ integrity sha512-681OmaLrAetgHL59iU9ldeaXZ7NLp5T3nWLxGQGAklUwCpmny32wJKj2aXsCQN6WYuGDvz9PgpqN+YrkAMChFQ==
dependencies:
- "@dhis2/d2-ui-rich-text" "^7.4.0"
+ "@dhis2/d2-ui-rich-text" "^7.4.1"
"@dhis2/multi-calendar-dates" "1.0.0"
+ "@dnd-kit/core" "^6.0.7"
+ "@dnd-kit/sortable" "^7.0.2"
+ "@dnd-kit/utilities" "^3.2.1"
+ "@react-hook/debounce" "^4.0.0"
classnames "^2.3.1"
+ crypto-js "^4.1.1"
d2-utilizr "^0.2.16"
d3-color "^1.2.3"
- highcharts "^10.2.0"
+ highcharts "^10.3.3"
lodash "^4.17.21"
mathjs "^9.4.2"
react-beautiful-dnd "^10.1.1"
@@ -2227,10 +2232,10 @@
i18next "^10.3"
moment "^2.24.0"
-"@dhis2/d2-ui-rich-text@^7.4.0":
- version "7.4.1"
- resolved "https://registry.yarnpkg.com/@dhis2/d2-ui-rich-text/-/d2-ui-rich-text-7.4.1.tgz#8764208c59c6758bf34765b1dbe01762ce435d11"
- integrity sha512-/n5nE0b4EDI/kX0/aN+vFDOswoWT5JQ3lwtHsUxailvnEHMu4/3l27Q38Z+5qhKwl+jYNB9GOFxWoSiymUgBbw==
+"@dhis2/d2-ui-rich-text@^7.4.1":
+ version "7.4.3"
+ resolved "https://registry.yarnpkg.com/@dhis2/d2-ui-rich-text/-/d2-ui-rich-text-7.4.3.tgz#a42c8e231bcc05186dd432dac86b33aed4ddc10d"
+ integrity sha512-60k/6CO2I8f4t3jU1nAic7uWONME1rckM8RcLnelhwUG20EZWq45OnDDdSfHgOWTwVDtxFnG3wspInkG/530KA==
dependencies:
babel-runtime "^6.26.0"
markdown-it "^8.4.2"
@@ -2375,6 +2380,37 @@
"@dhis2/ui-icons" "8.13.15"
prop-types "^15.7.2"
+"@dnd-kit/accessibility@^3.0.0":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz#3ccbefdfca595b0a23a5dc57d3de96bc6935641c"
+ integrity sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==
+ dependencies:
+ tslib "^2.0.0"
+
+"@dnd-kit/core@^6.0.7":
+ version "6.0.8"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-6.0.8.tgz#040ae13fea9787ee078e5f0361f3b49b07f3f005"
+ integrity sha512-lYaoP8yHTQSLlZe6Rr9qogouGUz9oRUj4AHhDQGQzq/hqaJRpFo65X+JKsdHf8oUFBzx5A+SJPUvxAwTF2OabA==
+ dependencies:
+ "@dnd-kit/accessibility" "^3.0.0"
+ "@dnd-kit/utilities" "^3.2.1"
+ tslib "^2.0.0"
+
+"@dnd-kit/sortable@^7.0.2":
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/sortable/-/sortable-7.0.2.tgz#791d550872457f3f3c843e00d159b640f982011c"
+ integrity sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==
+ dependencies:
+ "@dnd-kit/utilities" "^3.2.0"
+ tslib "^2.0.0"
+
+"@dnd-kit/utilities@^3.2.0", "@dnd-kit/utilities@^3.2.1":
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/utilities/-/utilities-3.2.1.tgz#53f9e2016fd2506ec49e404c289392cfff30332a"
+ integrity sha512-OOXqISfvBw/1REtkSK2N3Fi2EQiLMlWUlqnOK/UpOISqBZPWpE6TqL+jcPtMOkE8TqYGiURvRdPSI9hltNUjEA==
+ dependencies:
+ tslib "^2.0.0"
+
"@eslint/eslintrc@^0.4.3":
version "0.4.3"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
@@ -2887,6 +2923,13 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
+"@react-hook/debounce@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@react-hook/debounce/-/debounce-4.0.0.tgz#5da87e7bfa158cfe2830ffc997dc1b755e261379"
+ integrity sha512-706Xcg+KKWHk9BuZQUQ0ZQKp9zhv3/MbqFenWVfHcynYpSGRVwQTzJRGvPxvsdtXxJv+HfgKTY/O/hEejakwmA==
+ dependencies:
+ "@react-hook/latest" "^1.0.2"
+
"@react-hook/latest@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@react-hook/latest/-/latest-1.0.3.tgz#c2d1d0b0af8b69ec6e2b3a2412ba0768ac82db80"
@@ -6001,6 +6044,11 @@ crypto-browserify@^3.0.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
+crypto-js@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf"
+ integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
+
crypto-random-string@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
@@ -8683,7 +8731,7 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
-highcharts@^10.2.0:
+highcharts@^10.3.3:
version "10.3.3"
resolved "https://registry.yarnpkg.com/highcharts/-/highcharts-10.3.3.tgz#b8acca24f2d4b1f2f726540734166e59e07b35c4"
integrity sha512-r7wgUPQI9tr3jFDn3XT36qsNwEIZYcfgz4mkKEA6E4nn5p86y+u1EZjazIG4TRkl5/gmGRtkBUiZW81g029RIw==
@@ -15124,6 +15172,11 @@ tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+tslib@^2.0.0:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.1.tgz#fd8c9a0ff42590b25703c0acb3de3d3f4ede0410"
+ integrity sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==
+
tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"