diff --git a/i18n/en.pot b/i18n/en.pot index 2ff7557e9..fbce2a3d2 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -499,9 +499,6 @@ msgstr "Style by group set" msgid "Indicator group" msgstr "Indicator group" -msgid "Interpretations" -msgstr "Interpretations" - msgid "Collapse" msgstr "Collapse" diff --git a/package.json b/package.json index 90d10a274..66e6d4cd0 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "start-server-and-test": "^1.15.4" }, "dependencies": { - "@dhis2/analytics": "^24.10.1", + "@dhis2/analytics": "^26.0.17", "@dhis2/app-runtime": "^3.9.4", "@dhis2/app-runtime-adapter-d2": "^1.1.0", "@dhis2/app-service-alerts": "^3.9.4", diff --git a/src/components/app/App.css b/src/components/app/App.css new file mode 100644 index 000000000..c938f8e6f --- /dev/null +++ b/src/components/app/App.css @@ -0,0 +1,7 @@ +:root { + --left-panel-width: 300px; + --right-panel-width: 380px; + + --header-height: 48px; + --toolbar-height: 32px; +} diff --git a/src/components/app/App.js b/src/components/app/App.js index 1e8dce8a2..762d13b8a 100644 --- a/src/components/app/App.js +++ b/src/components/app/App.js @@ -12,7 +12,8 @@ import { CURRENT_AO_KEY } from '../../util/analyticalObject.js' import { getUrlParameter } from '../../util/requests.js' import { useSystemSettings } from '../SystemSettingsProvider.js' import AppLayout from './AppLayout.js' -import styles from './styles/App.module.css' +import './App.css' +import './styles/App.module.css' const App = () => { const systemSettings = useSystemSettings() @@ -50,10 +51,10 @@ const App = () => { }, [systemSettings, dispatch]) return !isEmpty(systemSettings) ? ( -
+ <> -
+ ) : null } diff --git a/src/components/app/AppLayout.js b/src/components/app/AppLayout.js index f7db05188..21b42a987 100644 --- a/src/components/app/AppLayout.js +++ b/src/components/app/AppLayout.js @@ -1,49 +1,65 @@ import cx from 'classnames' -import React from 'react' +import React, { useState } from 'react' import { useSelector } from 'react-redux' import AlertStack from '../alerts/AlertStack.js' import BottomPanel from '../datatable/BottomPanel.js' -import DownloadMode from '../download/DownloadMode.js' +import DownloadModeMenu from '../download/DownloadMenubar.js' +import DownloadSettings from '../download/DownloadSettings.js' import LayerEdit from '../edit/LayerEdit.js' -import Interpretations from '../interpretations/Interpretations.js' import LayersPanel from '../layers/LayersPanel.js' -import LayersToggle from '../layers/LayersToggle.js' import LayersLoader from '../loaders/LayersLoader.js' import ContextMenu from '../map/ContextMenu.js' import MapPosition from '../map/MapPosition.js' import OpenAsMapDialog from '../openAs/OpenAsMapDialog.js' -import OrgUnitProfile from '../orgunits/OrgUnitProfile.js' import AppMenu from './AppMenu.js' +import DetailsPanel from './DetailsPanel.js' import styles from './styles/AppLayout.module.css' const AppLayout = () => { - const downloadMode = useSelector((state) => state.download.downloadMode) + const [interpretationsRenderCount, setInterpretationsRenderCount] = + useState(1) + + const dataTableOpen = useSelector((state) => !!state.dataTable) + const downloadModeOpen = useSelector( + (state) => !!state.download.downloadMode + ) + const detailsPanelOpen = useSelector( + (state) => state.ui.rightPanelOpen && !state.orgUnitProfile + ) + + const onFileMenuAction = () => + detailsPanelOpen && + setInterpretationsRenderCount(interpretationsRenderCount + 1) return ( -
- {downloadMode ? ( - + <> + {downloadModeOpen ? ( + ) : ( - <> - - - - - + )} - - - - +
+ {downloadModeOpen ? : } +
+ + {dataTableOpen && } +
+ {!downloadModeOpen && ( + + )} +
+ + - -
+ ) } diff --git a/src/components/app/AppMenu.js b/src/components/app/AppMenu.js index 8b4ba49f9..51eac1306 100644 --- a/src/components/app/AppMenu.js +++ b/src/components/app/AppMenu.js @@ -1,17 +1,24 @@ +import { Toolbar, HoverMenuBar } from '@dhis2/analytics' +import PropTypes from 'prop-types' import React from 'react' import DownloadButton from '../download/DownloadButton.js' import InterpretationsToggle from '../interpretations/InterpretationsToggle.js' import AddLayerButton from '../layers/overlays/AddLayerButton.js' import FileMenu from './FileMenu.js' -import styles from './styles/AppMenu.module.css' -const AppMenu = () => ( -
+const AppMenu = ({ onFileMenuAction }) => ( + - - + + + + -
+ ) +AppMenu.propTypes = { + onFileMenuAction: PropTypes.func.isRequired, +} + export default AppMenu diff --git a/src/components/app/DetailsPanel.js b/src/components/app/DetailsPanel.js new file mode 100644 index 000000000..ddb88d10e --- /dev/null +++ b/src/components/app/DetailsPanel.js @@ -0,0 +1,40 @@ +import cx from 'classnames' +import PropTypes from 'prop-types' +import React from 'react' +import { useSelector } from 'react-redux' +import Interpretations from '../interpretations/Interpretations.js' +import OrgUnitProfile from '../orgunits/OrgUnitProfile.js' +import styles from './styles/DetailsPanel.module.css' + +const DetailsPanel = ({ interpretationsRenderCount }) => { + const detailsPanelOpen = useSelector((state) => state.ui.rightPanelOpen) + const viewOrgUnitProfile = useSelector((state) => state.orgUnitProfile) + + const getContent = () => { + if (!detailsPanelOpen) { + return null + } + + return viewOrgUnitProfile ? ( + + ) : ( + + ) + } + + return ( +
+ {getContent()} +
+ ) +} + +DetailsPanel.propTypes = { + interpretationsRenderCount: PropTypes.number.isRequired, +} + +export default DetailsPanel diff --git a/src/components/app/FileMenu.js b/src/components/app/FileMenu.js index 81e52efa6..382d8c5de 100644 --- a/src/components/app/FileMenu.js +++ b/src/components/app/FileMenu.js @@ -5,7 +5,7 @@ import { useAlert } from '@dhis2/app-service-alerts' import i18n from '@dhis2/d2-i18n' import PropTypes from 'prop-types' import React from 'react' -import { connect } from 'react-redux' +import { useSelector, useDispatch } from 'react-redux' import { newMap, tOpenMap, setMapProps } from '../../actions/map.js' import { ALERT_CRITICAL, @@ -53,11 +53,12 @@ const getSaveFailureMessage = (message) => nsSeparator: ';', }) -const FileMenu = ({ map, newMap, tOpenMap, setMapProps }) => { +const FileMenu = ({ onFileMenuAction }) => { const { d2 } = useD2() const engine = useDataEngine() + const map = useSelector((state) => state.map) + const dispatch = useDispatch() const { keyDefaultBaseMap } = useSystemSettings() - //alerts const saveAlert = useAlert(ALERT_MESSAGE_DYNAMIC, ALERT_OPTIONS_DYNAMIC) const saveAsAlert = useAlert(ALERT_MESSAGE_DYNAMIC, ALERT_OPTIONS_DYNAMIC) const deleteAlert = useAlert( @@ -112,7 +113,7 @@ const FileMenu = ({ map, newMap, tOpenMap, setMapProps }) => { } const openMap = async (id) => { - const error = await tOpenMap(id, keyDefaultBaseMap, engine) + const error = await dispatch(tOpenMap(id, keyDefaultBaseMap, engine)) if (error) { openMapErrorAlert.show({ msg: i18n.t(`Error while opening map: ${error.message}`, { @@ -154,7 +155,7 @@ const FileMenu = ({ map, newMap, tOpenMap, setMapProps }) => { delete newMapConfig.basemap delete newMapConfig.mapViews - setMapProps(newMapConfig) + dispatch(setMapProps(newMapConfig)) saveAsAlert.show({ msg: getSavedMessage(config.name) }) } else { @@ -165,39 +166,38 @@ const FileMenu = ({ map, newMap, tOpenMap, setMapProps }) => { } } - const onRename = ({ name, description }) => - setMapProps({ name: getMapName(name), description }) + const onRename = ({ name, description }) => { + dispatch(setMapProps({ name: getMapName(name), description })) + onFileMenuAction() + } const onDelete = () => { - newMap() + onNew() deleteAlert.show() } + const onNew = () => dispatch(newMap()) + return ( ) } FileMenu.propTypes = { - map: PropTypes.object.isRequired, - newMap: PropTypes.func.isRequired, - setMapProps: PropTypes.func.isRequired, - tOpenMap: PropTypes.func.isRequired, + onFileMenuAction: PropTypes.func.isRequired, } -export default connect(({ map }) => ({ map }), { - newMap, - tOpenMap, - setMapProps, -})(FileMenu) +export default FileMenu diff --git a/src/components/app/styles/App.module.css b/src/components/app/styles/App.module.css index 9555647be..c8ccb4856 100644 --- a/src/components/app/styles/App.module.css +++ b/src/components/app/styles/App.module.css @@ -19,11 +19,6 @@ max-height: calc(100vh - 64px) } -.app { - font-family: 'Roboto', sans-serif; - height: 100vh; -} - /* Scrollbar width */ ::-webkit-scrollbar { width: 6px; diff --git a/src/components/app/styles/AppLayout.module.css b/src/components/app/styles/AppLayout.module.css index 43edee537..be2ac384e 100644 --- a/src/components/app/styles/AppLayout.module.css +++ b/src/components/app/styles/AppLayout.module.css @@ -1,11 +1,15 @@ -.appLayout { - position: absolute; - top: 48px; - bottom: 0; - left: 0; - right: 0 +.content { + display: flex; + flex-direction: row; + height: calc(100vh - var(--header-height) - var(--toolbar-height)); } -.downloadMode { - top: 0; +.downloadContent { + margin-top: var(--header-height); + height: calc(100vh - var(--header-height)); +} + +.appMapAndTable { + flex: auto; + position: relative; } diff --git a/src/components/app/styles/AppMenu.module.css b/src/components/app/styles/AppMenu.module.css deleted file mode 100644 index 493141a01..000000000 --- a/src/components/app/styles/AppMenu.module.css +++ /dev/null @@ -1,9 +0,0 @@ -.appMenu { - display: flex; - position: absolute; - width: 100%; - height: 38px; - z-index: 1200; - box-shadow: 0 1px 1px 0 #9e9e9e; - padding-top: 1px; -} diff --git a/src/components/app/styles/DetailsPanel.module.css b/src/components/app/styles/DetailsPanel.module.css new file mode 100644 index 000000000..42e2cc665 --- /dev/null +++ b/src/components/app/styles/DetailsPanel.module.css @@ -0,0 +1,7 @@ +.detailsPanel { + width: var(--right-panel-width); +} + +.detailsPanel.collapsed { + width: 0px; +} diff --git a/src/components/core/styles/Drawer.module.css b/src/components/core/styles/Drawer.module.css index 2c108b63e..ade930198 100644 --- a/src/components/core/styles/Drawer.module.css +++ b/src/components/core/styles/Drawer.module.css @@ -1,27 +1,15 @@ .drawer { - position: absolute; - top: 38px; - bottom: 0; background-color: #f4f6f8; - height: auto; - max-height: 100%; - overflow-x: hidden; + height: 100%; overflow-y: auto; - z-index: 1190; } .left { - left: 0; - right: auto; border-right: 1px solid #e0e0e0; box-shadow: 1px 0 1px 0 rgba(0, 0, 0, 0.2); - width: 300px; } .right { - left: auto; - right: 0; border-left: 1px solid #e0e0e0; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2); - width: 380px; } diff --git a/src/components/datatable/BottomPanel.js b/src/components/datatable/BottomPanel.js index d1fb19eba..24f005bf0 100644 --- a/src/components/datatable/BottomPanel.js +++ b/src/components/datatable/BottomPanel.js @@ -1,10 +1,10 @@ import { IconCross16 } from '@dhis2/ui' -import PropTypes from 'prop-types' import React, { useRef, useCallback } from 'react' -import { connect } from 'react-redux' +import { useSelector, useDispatch } from 'react-redux' import { closeDataTable, resizeDataTable } from '../../actions/dataTable.js' import { HEADER_HEIGHT, + APP_MENU_HEIGHT, LAYERS_PANEL_WIDTH, RIGHT_PANEL_WIDTH, } from '../../constants/layout.js' @@ -14,69 +14,47 @@ import ResizeHandle from './ResizeHandle.js' import styles from './styles/BottomPanel.module.css' // Container for DataTable -const BottomPanel = ({ - layersPanelOpen, - rightPanelOpen, - dataTableOpen, - dataTableHeight, - resizeDataTable, - closeDataTable, -}) => { +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 node = useRef(null) + const panelRef = useRef(null) const onResize = useCallback( - (h) => (node.current.style.height = `${h}px`), - [node] + (h) => (panelRef.current.style.height = `${h}px`), + [panelRef] ) - if (dataTableOpen) { - const maxHeight = height - HEADER_HEIGHT - 20 - 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 style = { - height: tableHeight, - left: layersWidth, - right: rightPanelWidth, - } - - return ( -
- - - - - -
- ) - } - - return null -} - -BottomPanel.propTypes = { - closeDataTable: PropTypes.func.isRequired, - dataTableHeight: PropTypes.number.isRequired, - dataTableOpen: PropTypes.bool.isRequired, - layersPanelOpen: PropTypes.bool.isRequired, - resizeDataTable: PropTypes.func.isRequired, - rightPanelOpen: PropTypes.bool.isRequired, + const maxHeight = height - HEADER_HEIGHT - APP_MENU_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 + + return ( +
+ dispatch(closeDataTable())} + > + + + dispatch(resizeDataTable(height))} + /> + +
+ ) } -export default connect( - ({ dataTable, ui }) => ({ - dataTableOpen: !!dataTable, - dataTableHeight: ui.dataTableHeight, - layersPanelOpen: ui.layersPanelOpen, - rightPanelOpen: ui.rightPanelOpen, - }), - { closeDataTable, resizeDataTable } -)(BottomPanel) +export default BottomPanel diff --git a/src/components/datatable/styles/BottomPanel.module.css b/src/components/datatable/styles/BottomPanel.module.css index 345eedba3..cd3dff206 100644 --- a/src/components/datatable/styles/BottomPanel.module.css +++ b/src/components/datatable/styles/BottomPanel.module.css @@ -1,6 +1,6 @@ .bottomPanel { position: absolute; - right: 0; + left: 0; bottom: 0; z-index: 1040; background: #fff; diff --git a/src/components/download/DownloadButton.js b/src/components/download/DownloadButton.js index 24649834d..b9e738dc6 100644 --- a/src/components/download/DownloadButton.js +++ b/src/components/download/DownloadButton.js @@ -2,17 +2,18 @@ import i18n from '@dhis2/d2-i18n' import React from 'react' import { useDispatch } from 'react-redux' import { setDownloadMode } from '../../actions/download.js' -import { MenuButton } from '../core/index.js' +import styles from './styles/DownloadButton.module.css' const DownloadButton = () => { const dispatch = useDispatch() return ( - <> - dispatch(setDownloadMode(true))}> - {i18n.t('Download')} - - + ) } diff --git a/src/components/download/DownloadMode.js b/src/components/download/DownloadMenubar.js similarity index 50% rename from src/components/download/DownloadMode.js rename to src/components/download/DownloadMenubar.js index 17a0b9567..a6b7c476a 100644 --- a/src/components/download/DownloadMode.js +++ b/src/components/download/DownloadMenubar.js @@ -3,10 +3,9 @@ import { Button, IconChevronLeft24, colors } from '@dhis2/ui' import React, { useEffect } from 'react' import { useDispatch } from 'react-redux' import { setDownloadMode } from '../../actions/download.js' -import DownloadSettings from './DownloadSettings.js' -import styles from './styles/DownloadMode.module.css' +import styles from './styles/DownloadMenubar.module.css' -const DownloadMode = () => { +const DownloadMenubar = () => { const dispatch = useDispatch() useEffect(() => { @@ -18,16 +17,13 @@ const DownloadMode = () => { }, []) return ( - <> -
- -
- - +
+ +
) } -export default DownloadMode +export default DownloadMenubar diff --git a/src/components/download/DownloadSettings.js b/src/components/download/DownloadSettings.js index e2e54f1ad..533752a47 100644 --- a/src/components/download/DownloadSettings.js +++ b/src/components/download/DownloadSettings.js @@ -76,138 +76,144 @@ const DownloadSettings = () => { const showMarginsCheckbox = false // Not in use return ( - -
-

{i18n.t('Download map')}

-
- {isSupported ? ( - <> - - dispatch( - setDownloadConfig({ showName: value }) - ) - } - /> - - dispatch( - setDownloadConfig({ - showDescription: value, - }) - ) - } - tooltip={ - description - ? i18n.t( - 'Description can be changed from File > Rename menu' - ) - : i18n.t( - 'Set the map description when you save the map or from File > Rename menu' - ) - } - /> - - dispatch( - setDownloadConfig({ - showLegend: value, - }) - ) - } - /> - {showLegend && legendLayers.length > 1 && ( - + +
+

{i18n.t('Download map')}

+
+ {isSupported ? ( + <> + + dispatch( + setDownloadConfig({ + showName: value, + }) + ) + } /> - )} - - dispatch( - setDownloadConfig({ - showOverviewMap: value, - }) - ) - } - /> - - dispatch( - setDownloadConfig({ - showNorthArrow: value, - }) - ) - } - /> - {showNorthArrow && !isSplitView && ( - dispatch( setDownloadConfig({ - northArrowPosition: value, + showDescription: value, }) ) } + tooltip={ + description + ? i18n.t( + 'Description can be changed from File > Rename menu' + ) + : i18n.t( + 'Set the map description when you save the map or from File > Rename menu' + ) + } /> - )} - {showMarginsCheckbox && ( dispatch( setDownloadConfig({ - includeMargins: value, + showLegend: value, }) ) } /> - )} - - {i18n.t( - 'Resize your browser window to change the map dimensions.' + {showLegend && legendLayers.length > 1 && ( + )} - - - ) : ( - i18n.t( - 'Map download is not supported by your browser. Try Google Chrome or Firefox.' - ) - )} -
-
- - - {isSupported && ( - + + dispatch( + setDownloadConfig({ + showOverviewMap: value, + }) + ) + } + /> + + dispatch( + setDownloadConfig({ + showNorthArrow: value, + }) + ) + } + /> + {showNorthArrow && !isSplitView && ( + + dispatch( + setDownloadConfig({ + northArrowPosition: value, + }) + ) + } + /> + )} + {showMarginsCheckbox && ( + + dispatch( + setDownloadConfig({ + includeMargins: value, + }) + ) + } + /> + )} + + {i18n.t( + 'Resize your browser window to change the map dimensions.' + )} + + + ) : ( + i18n.t( + 'Map download is not supported by your browser. Try Google Chrome or Firefox.' + ) )} - +
+
+ + + {isSupported && ( + + )} + +
-
- + +
) } diff --git a/src/components/download/__tests__/DownloadButton.spec.js b/src/components/download/__tests__/DownloadButton.spec.js deleted file mode 100644 index 17027f162..000000000 --- a/src/components/download/__tests__/DownloadButton.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import { render } from '@testing-library/react' -import React from 'react' -import { Provider } from 'react-redux' -import configureMockStore from 'redux-mock-store' -import DownloadButton from '../DownloadButton.js' - -jest.mock('../../../actions/download', () => { - return { - setDownloadMode: jest - .fn() - .mockReturnValue({ type: 'DOWNLOAD_MODE_SET' }), - } -}) - -/* eslint-disable react/prop-types */ -jest.mock('../../core/index.js', () => { - return { - MenuButton: function Mock(props) { - return - }, - } -}) -/* eslint-enable react/prop-types */ - -const mockStore = configureMockStore() - -describe('DownloadButton', () => { - it('renders a download button', () => { - const store = {} - - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() - }) -}) diff --git a/src/components/download/__tests__/__snapshots__/DownloadButton.spec.js.snap b/src/components/download/__tests__/__snapshots__/DownloadButton.spec.js.snap deleted file mode 100644 index c82530ec6..000000000 --- a/src/components/download/__tests__/__snapshots__/DownloadButton.spec.js.snap +++ /dev/null @@ -1,12 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`DownloadButton renders a download button 1`] = ` -
- -
-`; - diff --git a/src/components/download/styles/DownloadButton.module.css b/src/components/download/styles/DownloadButton.module.css new file mode 100644 index 000000000..13c63b6e8 --- /dev/null +++ b/src/components/download/styles/DownloadButton.module.css @@ -0,0 +1,34 @@ +/* Based on https: //github.com/dhis2/analytics/blob/master/src/components/Toolbar/MenuButton.styles.js */ + +.button { + all: unset; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 14px; + line-height: 14px; + padding: 0 var(--spacers-dp12); + color: var(--colors-grey900); + transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + cursor: pointer; +} + +.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; +} + +.button:disabled { + color: var(--colors-grey500); + cursor: not-allowed; +} diff --git a/src/components/download/styles/DownloadMapInfo.module.css b/src/components/download/styles/DownloadMapInfo.module.css index 60bdc42a9..f45f313df 100644 --- a/src/components/download/styles/DownloadMapInfo.module.css +++ b/src/components/download/styles/DownloadMapInfo.module.css @@ -1,11 +1,7 @@ .downloadMapInfo { - padding-left: var(--spacers-dp24); background-color: #fff; height: 100%; - width: 380px; - position: absolute; - top: 0; - right: 0; + width: calc(var(--right-panel-width) - var(--spacers-dp24)); box-sizing: border-box; display: flex; flex-direction: column; diff --git a/src/components/download/styles/DownloadMode.module.css b/src/components/download/styles/DownloadMenubar.module.css similarity index 76% rename from src/components/download/styles/DownloadMode.module.css rename to src/components/download/styles/DownloadMenubar.module.css index d154aacaf..fccd907d2 100644 --- a/src/components/download/styles/DownloadMode.module.css +++ b/src/components/download/styles/DownloadMenubar.module.css @@ -1,18 +1,19 @@ .downloadModeMenu { display: flex; position: absolute; + align-items: center; width: 100%; - height: 44px; + height: var(--header-height); z-index: 1200; - padding-top: 1px; background-color: var(--colors-grey600); + left: 0; + top: 0 } .downloadModeMenu button { - margin: 3px 0 0 var(--spacers-dp16); + margin-left: var(--spacers-dp16); } -:global(.downloadModeMenu) header, :global(.dhis2-map-download .dhis2-map-ctrl-measure-actions), :global(.dhis2-map-download .dhis2-map-popup-orgunit button), :global(.dhis2-map-downloading .maplibregl-popup-close-button) { diff --git a/src/components/download/styles/DownloadSettings.module.css b/src/components/download/styles/DownloadSettings.module.css index 705cfd0f4..c78eae801 100644 --- a/src/components/download/styles/DownloadSettings.module.css +++ b/src/components/download/styles/DownloadSettings.module.css @@ -1,3 +1,8 @@ +.downloadSettingsPanel { + width: var(--left-panel-width); + overflow-y: auto; +} + .downloadSettings { padding: var(--spacers-dp8) var(--spacers-dp16); background-color: #f4f6f8; diff --git a/src/components/interpretations/Interpretations.js b/src/components/interpretations/Interpretations.js index 6c8b7022d..ef7cb361c 100644 --- a/src/components/interpretations/Interpretations.js +++ b/src/components/interpretations/Interpretations.js @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types' import React, { useEffect } from 'react' import { useSelector, useDispatch } from 'react-redux' import { setInterpretation } from '../../actions/interpretations.js' @@ -5,14 +6,11 @@ import { openInterpretationsPanel } from '../../actions/ui.js' import { getUrlParameter } from '../../util/requests.js' import InterpretationsPanel from './InterpretationsPanel.js' -const Interpretations = () => { +const Interpretations = ({ renderCount }) => { const isMapLoaded = useSelector( (state) => state.map.id && !state.map.mapViews.find((layer) => !layer.isLoaded) ) - const isPanelOpen = useSelector( - (state) => state.ui.rightPanelOpen && !state.orgUnitProfile - ) const dispatch = useDispatch() useEffect(() => { @@ -26,7 +24,13 @@ const Interpretations = () => { } }, [isMapLoaded, dispatch]) - return isPanelOpen && isMapLoaded ? : null + return isMapLoaded ? ( + + ) : null +} + +Interpretations.propTypes = { + renderCount: PropTypes.number.isRequired, } export default Interpretations diff --git a/src/components/interpretations/InterpretationsPanel.js b/src/components/interpretations/InterpretationsPanel.js index 1fdcedb14..e1e92ddda 100644 --- a/src/components/interpretations/InterpretationsPanel.js +++ b/src/components/interpretations/InterpretationsPanel.js @@ -11,7 +11,12 @@ import { setInterpretation } from '../../actions/interpretations.js' import Drawer from '../core/Drawer.js' import InterpretationMap from './InterpretationMap.js' -const InterpretationsPanel = ({ interpretationId, map, setInterpretation }) => { +const InterpretationsPanel = ({ + interpretationId, + map, + setInterpretation, + renderCount, +}) => { const [initialFocus, setInitialFocus] = useState(false) const interpretationsUnitRef = useRef() const { d2 } = useD2() @@ -39,7 +44,7 @@ const InterpretationsPanel = ({ interpretationId, map, setInterpretation }) => { return ( <> - + { InterpretationsPanel.propTypes = { map: PropTypes.object.isRequired, + renderCount: PropTypes.number.isRequired, setInterpretation: PropTypes.func.isRequired, interpretationId: PropTypes.string, } diff --git a/src/components/interpretations/InterpretationsToggle.js b/src/components/interpretations/InterpretationsToggle.js index d1d16f959..a47cce286 100644 --- a/src/components/interpretations/InterpretationsToggle.js +++ b/src/components/interpretations/InterpretationsToggle.js @@ -1,54 +1,33 @@ -import i18n from '@dhis2/d2-i18n' -import { IconChevronLeft24, IconChevronRight24 } from '@dhis2/ui' -import PropTypes from 'prop-types' -import React from 'react' -import { connect } from 'react-redux' +import { InterpretationsAndDetailsToggler as AnalyticsInterpretationsAndDetailsToggler } from '@dhis2/analytics' +import React, { useCallback } from 'react' +import { useSelector, useDispatch } from 'react-redux' import { openInterpretationsPanel, closeInterpretationsPanel, } from '../../actions/ui.js' -import { MenuButton } from '../core/index.js' -import styles from './styles/InterpretationsToggle.module.css' -const InterpretationsToggle = ({ - interpretationsOpen, - interpretationsEnabled, - openInterpretationsPanel, - closeInterpretationsPanel, -}) => ( -
- - {interpretationsOpen ? ( - - ) : ( - - )} - {i18n.t('Interpretations')} - -
-) +const InterpretationsToggle = () => { + const interpretationsEnabled = useSelector((state) => Boolean(state.map.id)) + const interpretationsOpen = useSelector( + (state) => state.ui.rightPanelOpen && !state.orgUnitProfile + ) + const dispatch = useDispatch() -InterpretationsToggle.propTypes = { - closeInterpretationsPanel: PropTypes.func.isRequired, - interpretationsEnabled: PropTypes.bool.isRequired, - interpretationsOpen: PropTypes.bool.isRequired, - openInterpretationsPanel: PropTypes.func.isRequired, + const onClick = useCallback(() => { + if (interpretationsOpen) { + dispatch(closeInterpretationsPanel()) + } else { + dispatch(openInterpretationsPanel()) + } + }, [dispatch, interpretationsOpen]) + + return ( + + ) } -export default connect( - (state) => ({ - interpretationsOpen: state.ui.rightPanelOpen && !state.orgUnitProfile, - interpretationsEnabled: Boolean(state.map.id), - }), - { - openInterpretationsPanel, - closeInterpretationsPanel, - } -)(InterpretationsToggle) +export default InterpretationsToggle diff --git a/src/components/layers/LayersPanel.js b/src/components/layers/LayersPanel.js index e358bc79c..dba967aa1 100644 --- a/src/components/layers/LayersPanel.js +++ b/src/components/layers/LayersPanel.js @@ -1,11 +1,12 @@ -import PropTypes from 'prop-types' +import cx from 'classnames' import React from 'react' -import { connect } from 'react-redux' +import { useSelector, useDispatch } from 'react-redux' import { SortableContainer, SortableElement } from 'react-sortable-hoc' import { sortLayers } from '../../actions/layers.js' -import Drawer from '../core/Drawer.js' import BasemapCard from '../layers/basemaps/BasemapCard.js' +import LayersToggle from '../layers/LayersToggle.js' import OverlayCard from './overlays/OverlayCard.js' +import styles from './styles/LayersPanel.module.css' const SortableLayer = SortableElement(OverlayCard) @@ -18,29 +19,37 @@ const SortableLayersList = SortableContainer(({ layers }) => ( )) -const LayersPanel = ({ layersPanelOpen, layers, sortLayers }) => - layersPanelOpen && ( - - -
- +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"