From fa9f0d56198088e7eedd335e71355d7bcb576f16 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Tue, 3 Sep 2024 11:43:24 +0200 Subject: [PATCH 01/16] chore: update ItemGrid to current standard and fix missing dep in useEffect --- i18n/en.pot | 7 +++-- src/pages/view/ItemGrid.js | 52 +++++++++++++++----------------------- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index c5f2aef0b..b030c572f 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-03-19T12:31:03.302Z\n" -"PO-Revision-Date: 2024-03-19T12:31:03.302Z\n" +"POT-Creation-Date: 2024-09-03T09:43:28.012Z\n" +"PO-Revision-Date: 2024-09-03T09:43:28.014Z\n" msgid "Untitled dashboard" msgstr "Untitled dashboard" @@ -410,6 +410,9 @@ msgstr "Start of dashboard" msgid "Print" msgstr "Print" +msgid "Present" +msgstr "Present" + msgid "dashboard layout" msgstr "dashboard layout" diff --git a/src/pages/view/ItemGrid.js b/src/pages/view/ItemGrid.js index 0a112be77..7ce648fb8 100644 --- a/src/pages/view/ItemGrid.js +++ b/src/pages/view/ItemGrid.js @@ -1,9 +1,8 @@ import i18n from '@dhis2/d2-i18n' import cx from 'classnames' -import PropTypes from 'prop-types' import React, { useState, useEffect } from 'react' import { Responsive as ResponsiveReactGridLayout } from 'react-grid-layout' -import { connect } from 'react-redux' +import { useSelector } from 'react-redux' import { Item } from '../../components/Item/Item.js' import NoContentMessage from '../../components/NoContentMessage.js' import ProgressiveLoadingContainer from '../../components/ProgressiveLoadingContainer.js' @@ -34,7 +33,9 @@ import classes from './styles/ItemGrid.module.css' const EXPANDED_HEIGHT = 19 const EXPANDED_HEIGHT_SM = 15 -const ResponsiveItemGrid = ({ dashboardId, dashboardItems }) => { +const ResponsiveItemGrid = () => { + const dashboardId = useSelector(sGetSelectedId) + const dashboardItems = useSelector(sGetSelectedDashboardItems) const { width } = useWindowDimensions() const [expandedItems, setExpandedItems] = useState({}) const [displayItems, setDisplayItems] = useState(dashboardItems) @@ -45,6 +46,22 @@ const ResponsiveItemGrid = ({ dashboardId, dashboardItems }) => { const firstOfTypes = getFirstOfTypes(dashboardItems) useEffect(() => { + const getItemsWithAdjustedHeight = (items) => + items.map((item) => { + const expandedItem = expandedItems[item.id] + + if (expandedItem && expandedItem === true) { + const expandedHeight = isSmallScreen(width) + ? EXPANDED_HEIGHT_SM + : EXPANDED_HEIGHT + return Object.assign({}, item, { + h: item.h + expandedHeight, + smallOriginalH: getProportionalHeight(item, width), + }) + } + + return item + }) setLayoutSm( getItemsWithAdjustedHeight(getSmallLayout(dashboardItems, width)) ) @@ -68,23 +85,6 @@ const ResponsiveItemGrid = ({ dashboardId, dashboardItems }) => { setExpandedItems(newExpandedItems) } - const getItemsWithAdjustedHeight = (items) => - items.map((item) => { - const expandedItem = expandedItems[item.id] - - if (expandedItem && expandedItem === true) { - const expandedHeight = isSmallScreen(width) - ? EXPANDED_HEIGHT_SM - : EXPANDED_HEIGHT - return Object.assign({}, item, { - h: item.h + expandedHeight, - smallOriginalH: getProportionalHeight(item, width), - }) - } - - return item - }) - const getItemComponent = (item) => { if (!layoutSm.length) { return
@@ -157,14 +157,4 @@ const ResponsiveItemGrid = ({ dashboardId, dashboardItems }) => { ) } -ResponsiveItemGrid.propTypes = { - dashboardId: PropTypes.string, - dashboardItems: PropTypes.array, -} - -const mapStateToProps = (state) => ({ - dashboardItems: sGetSelectedDashboardItems(state), - dashboardId: sGetSelectedId(state), -}) - -export default connect(mapStateToProps)(ResponsiveItemGrid) +export default ResponsiveItemGrid From 3d3990e4aa463e6157ce223c9eed0a8c8ccaf010 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Tue, 3 Sep 2024 13:17:46 +0200 Subject: [PATCH 02/16] feat: fullscreen for visualization items --- i18n/en.pot | 10 +-- src/actions/presentDashboard.js | 6 ++ src/components/Item/VisualizationItem/Item.js | 4 +- .../Visualization/IframePlugin.js | 9 ++- .../Visualization/Visualization.js | 4 + src/pages/view/ItemGrid.js | 80 ++++++++++++++++++- src/pages/view/TitleBar/ActionsBar.js | 13 +++ .../TitleBar/styles/ActionsBar.module.css | 1 + src/reducers/index.js | 2 + src/reducers/presentDashboard.js | 13 +++ 10 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 src/actions/presentDashboard.js create mode 100644 src/reducers/presentDashboard.js diff --git a/i18n/en.pot b/i18n/en.pot index b030c572f..581e847fc 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-09-03T09:43:28.012Z\n" -"PO-Revision-Date: 2024-09-03T09:43:28.014Z\n" +"POT-Creation-Date: 2024-09-03T11:14:05.234Z\n" +"PO-Revision-Date: 2024-09-03T11:14:05.235Z\n" msgid "Untitled dashboard" msgstr "Untitled dashboard" @@ -410,9 +410,6 @@ msgstr "Start of dashboard" msgid "Print" msgstr "Print" -msgid "Present" -msgstr "Present" - msgid "dashboard layout" msgstr "dashboard layout" @@ -532,6 +529,9 @@ msgstr "Edit" msgid "Share" msgstr "Share" +msgid "Present" +msgstr "Present" + msgid "Clear dashboard filters?" msgstr "Clear dashboard filters?" diff --git a/src/actions/presentDashboard.js b/src/actions/presentDashboard.js new file mode 100644 index 000000000..ed4195c36 --- /dev/null +++ b/src/actions/presentDashboard.js @@ -0,0 +1,6 @@ +import { SET_PRESENT_DASHBOARD } from '../reducers/presentDashboard.js' + +export const acSetPresentDashboard = (isPresent) => ({ + type: SET_PRESENT_DASHBOARD, + value: isPresent, +}) diff --git a/src/components/Item/VisualizationItem/Item.js b/src/components/Item/VisualizationItem/Item.js index 93b09b14b..9e340c8cb 100644 --- a/src/components/Item/VisualizationItem/Item.js +++ b/src/components/Item/VisualizationItem/Item.js @@ -203,7 +203,7 @@ class Item extends Component { } render() { - const { item, dashboardMode, itemFilters } = this.props + const { item, dashboardMode, itemFilters, isFS } = this.props const { showFooter, showNoFiltersOverlay } = this.state const originalType = getItemTypeForVis(item) const activeType = this.getActiveType() @@ -295,6 +295,7 @@ class Item extends Component { {(dimensions) => ( { const dispatch = useDispatch() const iframePluginStatus = useSelector(sGetIframePluginStatus) @@ -232,6 +233,9 @@ const IframePlugin = ({ ) } + const width = isFS ? '100%' : style.width || '100%' + const height = isFS ? '90vh' : style.height || '100%' // TODO - get the height right for FS + return (
{iframeSrc ? ( @@ -240,8 +244,8 @@ const IframePlugin = ({ src={iframeSrc} // preserve dimensions if provided style={{ - width: style.width || '100%', - height: style.height || '100%', + width, + height, border: 'none', }} > @@ -255,6 +259,7 @@ IframePlugin.propTypes = { dashboardId: PropTypes.string, dashboardMode: PropTypes.string, filterVersion: PropTypes.string, + isFS: PropTypes.bool, isFirstOfType: PropTypes.bool, itemId: PropTypes.string, itemType: PropTypes.string, diff --git a/src/components/Item/VisualizationItem/Visualization/Visualization.js b/src/components/Item/VisualizationItem/Visualization/Visualization.js index 33e5a6fb7..50761bad0 100644 --- a/src/components/Item/VisualizationItem/Visualization/Visualization.js +++ b/src/components/Item/VisualizationItem/Visualization/Visualization.js @@ -40,6 +40,7 @@ const Visualization = ({ originalType, showNoFiltersOverlay, onClickNoFiltersOverlay, + isFS, ...rest }) => { const dashboardId = useSelector(sGetSelectedId) @@ -141,6 +142,7 @@ const Visualization = ({ ) } @@ -191,6 +193,7 @@ const Visualization = ({ ) } @@ -229,6 +232,7 @@ Visualization.propTypes = { availableWidth: PropTypes.number, dashboardMode: PropTypes.string, gridWidth: PropTypes.number, + isFS: PropTypes.bool, item: PropTypes.object, itemFilters: PropTypes.object, originalType: PropTypes.string, diff --git a/src/pages/view/ItemGrid.js b/src/pages/view/ItemGrid.js index 7ce648fb8..3baecd2e8 100644 --- a/src/pages/view/ItemGrid.js +++ b/src/pages/view/ItemGrid.js @@ -1,9 +1,12 @@ import i18n from '@dhis2/d2-i18n' import cx from 'classnames' -import React, { useState, useEffect } from 'react' +import sortBy from 'lodash/sortBy.js' +import React, { useState, useEffect, useRef } from 'react' import { Responsive as ResponsiveReactGridLayout } from 'react-grid-layout' -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' +import { acSetPresentDashboard } from '../../actions/presentDashboard.js' import { Item } from '../../components/Item/Item.js' +import { getGridItemElement } from '../../components/Item/VisualizationItem/getGridItemElement.js' import NoContentMessage from '../../components/NoContentMessage.js' import ProgressiveLoadingContainer from '../../components/ProgressiveLoadingContainer.js' import { useWindowDimensions } from '../../components/WindowDimensionsProvider.js' @@ -24,6 +27,7 @@ import { } from '../../modules/gridUtil.js' import { getBreakpoint, isSmallScreen } from '../../modules/smallScreen.js' import { useCacheableSection } from '../../modules/useCacheableSection.js' +import { sGetPresentDashboard } from '../../reducers/presentDashboard.js' import { sGetSelectedId, sGetSelectedDashboardItems, @@ -34,8 +38,11 @@ const EXPANDED_HEIGHT = 19 const EXPANDED_HEIGHT_SM = 15 const ResponsiveItemGrid = () => { + const dispatch = useDispatch() const dashboardId = useSelector(sGetSelectedId) const dashboardItems = useSelector(sGetSelectedDashboardItems) + const isPresentMode = useSelector(sGetPresentDashboard) + const { width } = useWindowDimensions() const [expandedItems, setExpandedItems] = useState({}) const [displayItems, setDisplayItems] = useState(dashboardItems) @@ -45,6 +52,10 @@ const ResponsiveItemGrid = () => { const { recordingState } = useCacheableSection(dashboardId) const firstOfTypes = getFirstOfTypes(dashboardItems) + // for slideshow + const [fsItemIndex, setFsItemIndex] = useState(null) + const sItems = useRef([]) + useEffect(() => { const getItemsWithAdjustedHeight = (items) => items.map((item) => { @@ -74,6 +85,65 @@ const ResponsiveItemGrid = () => { } }, [recordingState]) + useEffect(() => { + const sortedItems = sortBy(displayItems, ['y', 'x']) + sItems.current = sortedItems + }, [displayItems]) + + useEffect(() => { + if (isPresentMode && document.fullscreenElement === null) { + setFsItemIndex(0) + const el = getGridItemElement(sItems.current[0].id) + el.requestFullscreen() + } + }, [isPresentMode]) + + useEffect(() => { + const handleKeyDown = (event) => { + if (document.fullscreenElement) { + let nextElementIndex + + if (event.key === 'ArrowRight' || event.key === 'ArrowDown') { + nextElementIndex = fsItemIndex + 1 + } else if ( + event.key === 'ArrowLeft' || + event.key === 'ArrowUp' + ) { + if (fsItemIndex === null || fsItemIndex === 0) { + nextElementIndex = sItems.current.length - 1 + } else { + nextElementIndex = fsItemIndex - 1 + } + } + const el = getGridItemElement( + sItems.current[nextElementIndex].id + ) + setFsItemIndex(nextElementIndex) + el.requestFullscreen() + } + } + + const handleFullscreenChange = () => { + if (!document.fullscreenElement) { + dispatch(acSetPresentDashboard(false)) + setFsItemIndex(null) + } + } + + // Attach the event listener to the window object + window.addEventListener('keydown', handleKeyDown) + document.addEventListener('fullscreenchange', handleFullscreenChange) + + // Clean up the event listener when the component is unmounted + return () => { + window.removeEventListener('keydown', handleKeyDown) + document.removeEventListener( + 'fullscreenchange', + handleFullscreenChange + ) + } + }, [fsItemIndex, dispatch]) + const onToggleItemExpanded = (clickedId) => { const isExpanded = typeof expandedItems[clickedId] === 'boolean' @@ -111,6 +181,12 @@ const ResponsiveItemGrid = () => { dashboardMode={VIEW} isRecording={forceLoad} onToggleItemExpanded={onToggleItemExpanded} + isFS={ + !!( + Number.isInteger(fsItemIndex) && + sItems.current[fsItemIndex].id === item.id + ) + } /> ) diff --git a/src/pages/view/TitleBar/ActionsBar.js b/src/pages/view/TitleBar/ActionsBar.js index 24da7ec8b..f0b6da581 100644 --- a/src/pages/view/TitleBar/ActionsBar.js +++ b/src/pages/view/TitleBar/ActionsBar.js @@ -18,6 +18,7 @@ import { connect } from 'react-redux' import { Link, Redirect } from 'react-router-dom' import { acSetDashboardStarred } from '../../../actions/dashboards.js' import { acClearItemFilters } from '../../../actions/itemFilters.js' +import { acSetPresentDashboard } from '../../../actions/presentDashboard.js' import { acSetShowDescription } from '../../../actions/showDescription.js' import { apiPostShowDescription } from '../../../api/description.js' import ConfirmActionDialog from '../../../components/ConfirmActionDialog.js' @@ -41,6 +42,7 @@ const ViewActions = ({ showDescription, starred, setDashboardStarred, + setPresentDashboard, updateShowDescription, removeAllFilters, restrictFilters, @@ -260,6 +262,15 @@ const ViewActions = ({ allowedFilters={allowedFilters} restrictFilters={restrictFilters} /> + + + {getMoreButton(classes.moreButton, false)} {getMoreButton(classes.moreButtonSmall, true)}
@@ -294,6 +305,7 @@ ViewActions.propTypes = { removeAllFilters: PropTypes.func, restrictFilters: PropTypes.bool, setDashboardStarred: PropTypes.func, + setPresentDashboard: PropTypes.func, showDescription: PropTypes.bool, starred: PropTypes.bool, updateShowDescription: PropTypes.func, @@ -314,6 +326,7 @@ const mapStateToProps = (state) => { export default connect(mapStateToProps, { setDashboardStarred: acSetDashboardStarred, + setPresentDashboard: acSetPresentDashboard, removeAllFilters: acClearItemFilters, updateShowDescription: acSetShowDescription, })(ViewActions) diff --git a/src/pages/view/TitleBar/styles/ActionsBar.module.css b/src/pages/view/TitleBar/styles/ActionsBar.module.css index a7005736e..652954217 100644 --- a/src/pages/view/TitleBar/styles/ActionsBar.module.css +++ b/src/pages/view/TitleBar/styles/ActionsBar.module.css @@ -19,6 +19,7 @@ @media only screen and (max-width: 480px) { .strip .editButton, + .strip .presentButton, .strip .shareButton { display: none; } diff --git a/src/reducers/index.js b/src/reducers/index.js index 9f839cbb2..f62cb9570 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -10,6 +10,7 @@ import itemActiveTypes from './itemActiveTypes.js' import itemFilters from './itemFilters.js' import messages from './messages.js' import passiveViewRegistered from './passiveViewRegistered.js' +import presentDashboard from './presentDashboard.js' import printDashboard from './printDashboard.js' import selected from './selected.js' import showDescription from './showDescription.js' @@ -29,6 +30,7 @@ export default combineReducers({ activeModalDimension, passiveViewRegistered, showDescription, + presentDashboard, itemActiveTypes, iframePluginStatus, }) diff --git a/src/reducers/presentDashboard.js b/src/reducers/presentDashboard.js new file mode 100644 index 000000000..0cdf4b5c7 --- /dev/null +++ b/src/reducers/presentDashboard.js @@ -0,0 +1,13 @@ +export const SET_PRESENT_DASHBOARD = 'SET_PRESENT_DASHBOARD' + +export default (state = false, action) => { + switch (action.type) { + case SET_PRESENT_DASHBOARD: { + return action.value + } + default: + return state + } +} + +export const sGetPresentDashboard = (state) => state.presentDashboard From 6f604b08260201e1c24f61db5a819b7c2a6a0eb2 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Wed, 4 Sep 2024 11:47:46 +0200 Subject: [PATCH 03/16] feat: add the navigation buttons and keep track of current index in ItemGrid --- i18n/en.pot | 14 ++- src/components/Item/VisualizationItem/Item.js | 55 ++++++++---- .../ItemContextMenu/ItemContextMenu.js | 15 ++-- src/pages/view/ItemGrid.js | 86 ++++++++++++------- src/pages/view/TitleBar/ActionsBar.js | 12 +-- src/reducers/presentDashboard.js | 2 +- 6 files changed, 118 insertions(+), 66 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 581e847fc..b40b48647 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-09-03T11:14:05.234Z\n" -"PO-Revision-Date: 2024-09-03T11:14:05.235Z\n" +"POT-Creation-Date: 2024-09-04T09:41:55.706Z\n" +"PO-Revision-Date: 2024-09-04T09:41:55.707Z\n" msgid "Untitled dashboard" msgstr "Untitled dashboard" @@ -80,6 +80,12 @@ msgstr "Some filters not applied" msgid "There was a problem loading this dashboard item" msgstr "There was a problem loading this dashboard item" +msgid "Previous" +msgstr "Previous" + +msgid "Next" +msgstr "Next" + msgid "Hide details and interpretations" msgstr "Hide details and interpretations" @@ -529,8 +535,8 @@ msgstr "Edit" msgid "Share" msgstr "Share" -msgid "Present" -msgstr "Present" +msgid "Slideshow" +msgstr "Slideshow" msgid "Clear dashboard filters?" msgstr "Clear dashboard filters?" diff --git a/src/components/Item/VisualizationItem/Item.js b/src/components/Item/VisualizationItem/Item.js index 9e340c8cb..9be861555 100644 --- a/src/components/Item/VisualizationItem/Item.js +++ b/src/components/Item/VisualizationItem/Item.js @@ -4,11 +4,12 @@ import { DIMENSION_ID_ORGUNIT, } from '@dhis2/analytics' import i18n from '@dhis2/d2-i18n' -import { Tag, Tooltip } from '@dhis2/ui' +import { Button, Tag, Tooltip } from '@dhis2/ui' import PropTypes from 'prop-types' import React, { Component } from 'react' import { connect } from 'react-redux' import { acSetItemActiveType } from '../../../actions/itemActiveTypes.js' +import { acSetPresentDashboard } from '../../../actions/presentDashboard.js' import { acAddVisualization } from '../../../actions/visualizations.js' import { apiPostDataStatistics } from '../../../api/dataStatistics.js' import { apiFetchVisualization } from '../../../api/fetchVisualization.js' @@ -35,6 +36,7 @@ import { sGetItemFiltersRoot, DEFAULT_STATE_ITEM_FILTERS, } from '../../../reducers/itemFilters.js' +import { sGetPresentDashboard } from '../../../reducers/presentDashboard.js' import { sGetVisualization } from '../../../reducers/visualizations.js' import { SystemSettingsCtx } from '../../SystemSettingsProvider.js' import { WindowDimensionsCtx } from '../../WindowDimensionsProvider.js' @@ -131,22 +133,7 @@ class Item extends Component { onClickNoFiltersOverlay = () => this.setState({ showNoFiltersOverlay: false }) - - onToggleFullscreen = () => { - if (!isElementFullscreen(this.props.item.id)) { - const el = getGridItemElement(this.props.item.id) - if (el?.requestFullscreen) { - el.requestFullscreen() - } else if (el?.webkitRequestFullscreen) { - el.webkitRequestFullscreen() - } - } else { - document.exitFullscreen - ? document.exitFullscreen() - : document.webkitExitFullscreen() - } - } - + ß onToggleFooter = () => { this.setState( { showFooter: !this.state.showFooter }, @@ -203,7 +190,16 @@ class Item extends Component { } render() { - const { item, dashboardMode, itemFilters, isFS } = this.props + const { + item, + dashboardMode, + itemFilters, + isFS, + setPresent, + sortPosition, + numSortItems, + exitFullscreen, + } = this.props const { showFooter, showNoFiltersOverlay } = this.state const originalType = getItemTypeForVis(item) const activeType = this.getActiveType() @@ -216,11 +212,13 @@ class Item extends Component { visualization={this.props.visualization} onSelectActiveType={this.setActiveType} onToggleFooter={this.onToggleFooter} - onToggleFullscreen={this.onToggleFullscreen} + enterFullscreen={() => setPresent(sortPosition - 1)} + exitFullscreen={exitFullscreen} activeType={activeType} activeFooter={showFooter} fullscreenSupported={this.isFullscreenSupported()} loadItemFailed={this.state.loadItemFailed} + isFS={isFS} /> ) : null @@ -319,6 +317,17 @@ class Item extends Component { )}
+ {isFS && ( + <> + + {`${sortPosition}/${numSortItems}`} + + + )} {isViewMode(dashboardMode) && showFooter ? ( ) : null} @@ -332,15 +341,21 @@ Item.propTypes = { apps: PropTypes.array, dashboardMode: PropTypes.string, engine: PropTypes.object, + exitFullscreen: PropTypes.func, gridWidth: PropTypes.number, isEditing: PropTypes.bool, isFS: PropTypes.bool, isRecording: PropTypes.bool, item: PropTypes.object, itemFilters: PropTypes.object, + nextItem: PropTypes.func, + numSortItems: PropTypes.number, + prevItem: PropTypes.func, setActiveType: PropTypes.func, + setPresent: PropTypes.func, setVisualization: PropTypes.func, settings: PropTypes.object, + sortPosition: PropTypes.number, visualization: PropTypes.object, onToggleItemExpanded: PropTypes.func, } @@ -364,12 +379,14 @@ const mapStateToProps = (state, ownProps) => { state, getVisualizationId(ownProps.item) ), + presentDashboard: sGetPresentDashboard(state), } } const mapDispatchToProps = { setActiveType: acSetItemActiveType, setVisualization: acAddVisualization, + setPresent: acSetPresentDashboard, } const ItemWithSettings = (props) => ( diff --git a/src/components/Item/VisualizationItem/ItemContextMenu/ItemContextMenu.js b/src/components/Item/VisualizationItem/ItemContextMenu/ItemContextMenu.js index 44372943f..35c011124 100644 --- a/src/components/Item/VisualizationItem/ItemContextMenu/ItemContextMenu.js +++ b/src/components/Item/VisualizationItem/ItemContextMenu/ItemContextMenu.js @@ -31,7 +31,6 @@ import { isSmallScreen } from '../../../../modules/smallScreen.js' import MenuItem from '../../../MenuItemWithTooltip.js' import { useSystemSettings } from '../../../SystemSettingsProvider.js' import { useWindowDimensions } from '../../../WindowDimensionsProvider.js' -import { isElementFullscreen } from '../isElementFullscreen.js' import ViewAsMenuItems from './ViewAsMenuItems.js' const ItemContextMenu = (props) => { @@ -65,8 +64,8 @@ const ItemContextMenu = (props) => { } } - const toggleFullscreen = () => { - props.onToggleFullscreen() + const enterFullscreen = () => { + props.enterFullscreen() closeMenu() } @@ -100,8 +99,8 @@ const ItemContextMenu = (props) => { getVisualizationId(item) )}` - return isElementFullscreen(item.id) ? ( - ) : null} - + {getMoreButton(classes.moreButton, false)} {getMoreButton(classes.moreButtonSmall, true)} diff --git a/src/reducers/presentDashboard.js b/src/reducers/presentDashboard.js index 0cdf4b5c7..6b5af95d4 100644 --- a/src/reducers/presentDashboard.js +++ b/src/reducers/presentDashboard.js @@ -1,6 +1,6 @@ export const SET_PRESENT_DASHBOARD = 'SET_PRESENT_DASHBOARD' -export default (state = false, action) => { +export default (state = null, action) => { switch (action.type) { case SET_PRESENT_DASHBOARD: { return action.value From f25c305c9e0e72d2a4839f1f2e8607a99af672b0 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Wed, 4 Sep 2024 11:50:04 +0200 Subject: [PATCH 04/16] fix: remove stray char --- src/components/Item/VisualizationItem/Item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Item/VisualizationItem/Item.js b/src/components/Item/VisualizationItem/Item.js index 9be861555..4e9c360a5 100644 --- a/src/components/Item/VisualizationItem/Item.js +++ b/src/components/Item/VisualizationItem/Item.js @@ -133,7 +133,7 @@ class Item extends Component { onClickNoFiltersOverlay = () => this.setState({ showNoFiltersOverlay: false }) - ß + onToggleFooter = () => { this.setState( { showFooter: !this.state.showFooter }, From 6033db147f2d9b7b26d8f63b8143edb95d04656f Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Thu, 5 Sep 2024 16:14:43 +0200 Subject: [PATCH 05/16] chore: full of debug comments --- src/components/Item/VisualizationItem/Item.js | 10 +-- .../ItemContextMenu/ItemContextMenu.js | 5 +- .../Visualization/IframePlugin.js | 9 ++- .../Visualization/Visualization.js | 8 +-- .../VisualizationItem/isElementFullscreen.js | 11 +++ src/pages/view/ItemGrid.js | 71 ++++++++++++------- 6 files changed, 74 insertions(+), 40 deletions(-) diff --git a/src/components/Item/VisualizationItem/Item.js b/src/components/Item/VisualizationItem/Item.js index 4e9c360a5..a7c276d79 100644 --- a/src/components/Item/VisualizationItem/Item.js +++ b/src/components/Item/VisualizationItem/Item.js @@ -194,7 +194,7 @@ class Item extends Component { item, dashboardMode, itemFilters, - isFS, + // isFS, setPresent, sortPosition, numSortItems, @@ -218,7 +218,7 @@ class Item extends Component { activeFooter={showFooter} fullscreenSupported={this.isFullscreenSupported()} loadItemFailed={this.state.loadItemFailed} - isFS={isFS} + // isFS={isFS} /> ) : null @@ -293,7 +293,7 @@ class Item extends Component { {(dimensions) => ( - {isFS && ( + {isElementFullscreen(item.id) && ( <> - {`${sortPosition}/${numSortItems}`} - - - )} {isViewMode(dashboardMode) && showFooter ? ( ) : null} diff --git a/src/components/Item/VisualizationItem/ItemContextMenu/ItemContextMenu.js b/src/components/Item/VisualizationItem/ItemContextMenu/ItemContextMenu.js index 340269184..a92ac94c9 100644 --- a/src/components/Item/VisualizationItem/ItemContextMenu/ItemContextMenu.js +++ b/src/components/Item/VisualizationItem/ItemContextMenu/ItemContextMenu.js @@ -100,13 +100,14 @@ const ItemContextMenu = (props) => { getVisualizationId(item) )}` - return isElementFullscreen(item.id) ? ( - - ) : ( + // return isElementFullscreen(item.id) ? ( + // + // ) : ( + return ( <>
+ + +
+ + ) : ( + + {getItemComponents(displayItems)} + + )} + ) } From aa5bf3318229c1a284bb1fc7d40e211d4ffeb319 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Fri, 6 Sep 2024 11:49:47 +0200 Subject: [PATCH 07/16] feat: sizing of the iframe as well as positioning of the nav buttons [skip ci] --- src/components/Item/VisualizationItem/Item.js | 27 +--- .../ItemContextMenu/ItemContextMenu.js | 11 -- .../Visualization/IframePlugin.js | 21 +-- .../Visualization/Visualization.js | 8 +- .../VisualizationItem/isElementFullscreen.js | 21 --- src/pages/view/ItemGrid.js | 127 ++++++++---------- src/pages/view/styles/ItemGrid.module.css | 65 +++++++++ 7 files changed, 144 insertions(+), 136 deletions(-) delete mode 100644 src/components/Item/VisualizationItem/isElementFullscreen.js diff --git a/src/components/Item/VisualizationItem/Item.js b/src/components/Item/VisualizationItem/Item.js index 27a78996f..ba256f954 100644 --- a/src/components/Item/VisualizationItem/Item.js +++ b/src/components/Item/VisualizationItem/Item.js @@ -4,7 +4,7 @@ import { DIMENSION_ID_ORGUNIT, } from '@dhis2/analytics' import i18n from '@dhis2/d2-i18n' -import { Button, Tag, Tooltip } from '@dhis2/ui' +import { Tag, Tooltip } from '@dhis2/ui' import PropTypes from 'prop-types' import React, { Component } from 'react' import { connect } from 'react-redux' @@ -43,7 +43,6 @@ import { WindowDimensionsCtx } from '../../WindowDimensionsProvider.js' import ItemHeader from '../ItemHeader/ItemHeader.js' import FatalErrorBoundary from './FatalErrorBoundary.js' import { getGridItemElement } from './getGridItemElement.js' -import { isElementFullscreen } from './isElementFullscreen.js' import ItemContextMenu from './ItemContextMenu/ItemContextMenu.js' import ItemFooter from './ItemFooter.js' import memoizeOne from './memoizeOne.js' @@ -153,16 +152,7 @@ class Item extends Component { return this.props.activeType || getItemTypeForVis(this.props.item) } - getAvailableHeight = ({ width, height }) => { - if (isElementFullscreen(this.props.item.id)) { - return ( - height - - this.headerRef.current.clientHeight - - this.itemHeaderTotalMargin - - this.itemContentPadding - ) - } - + getAvailableHeight = ({ width }) => { const calculatedHeight = getItemHeightPx(this.props.item, width) - this.headerRef.current.clientHeight - @@ -194,10 +184,9 @@ class Item extends Component { item, dashboardMode, itemFilters, - // isFS, + isFS, setPresent, sortPosition, - numSortItems, exitFullscreen, } = this.props const { showFooter, showNoFiltersOverlay } = this.state @@ -206,7 +195,8 @@ class Item extends Component { const actionButtons = pluginIsAvailable(activeType || item.type, this.props.apps) && - isViewMode(dashboardMode) ? ( + isViewMode(dashboardMode) && + isFS !== true ? ( ( { @@ -100,13 +98,6 @@ const ItemContextMenu = (props) => { getVisualizationId(item) )}` - // return isElementFullscreen(item.id) ? ( - // - // ) : ( return ( <>
@@ -181,9 +172,7 @@ ItemContextMenu.propTypes = { activeFooter: PropTypes.bool, activeType: PropTypes.string, enterFullscreen: PropTypes.func, - exitFullscreen: PropTypes.func, fullscreenSupported: PropTypes.bool, - // isFS: PropTypes.bool, item: PropTypes.object, loadItemFailed: PropTypes.bool, visualization: PropTypes.object, diff --git a/src/components/Item/VisualizationItem/Visualization/IframePlugin.js b/src/components/Item/VisualizationItem/Visualization/IframePlugin.js index 662462d1d..4addf540e 100644 --- a/src/components/Item/VisualizationItem/Visualization/IframePlugin.js +++ b/src/components/Item/VisualizationItem/Visualization/IframePlugin.js @@ -21,7 +21,6 @@ import { sGetIframePluginStatus, } from '../../../../reducers/iframePluginStatus.js' import { useUserSettings } from '../../../UserSettingsProvider.js' -import { isElementFullscreen } from '../isElementFullscreen.js' import MissingPluginMessage from './MissingPluginMessage.js' import { getPluginLaunchUrl } from './plugin.js' import classes from './styles/IframePlugin.module.css' @@ -37,7 +36,7 @@ const IframePlugin = ({ itemId, itemType, isFirstOfType, - // isFS, + isFS, }) => { const dispatch = useDispatch() const iframePluginStatus = useSelector(sGetIframePluginStatus) @@ -234,10 +233,18 @@ const IframePlugin = ({ ) } - const isFS = isElementFullscreen(itemId) - const width = isFS ? '100%' : style.width || '100%' - const height = isFS ? '90vh' : style.height || '100%' // TODO - get the height right for FS + const height = isFS ? '100vh' : style.height || '100%' + // TODO - get the height right for FS that includes space for header + // here's the implementation from Item.js + // if (isFS) { + // return ( + // height - + // this.headerRef.current.clientHeight - + // this.itemHeaderTotalMargin - + // this.itemContentPadding + // ) + // } return (
@@ -247,8 +254,6 @@ const IframePlugin = ({ src={iframeSrc} // preserve dimensions if provided style={{ - // width: style.width || '100%', - // height: style.height || '100%', width, height, border: 'none', @@ -264,7 +269,7 @@ IframePlugin.propTypes = { dashboardId: PropTypes.string, dashboardMode: PropTypes.string, filterVersion: PropTypes.string, - // isFS: PropTypes.bool, + isFS: PropTypes.bool, isFirstOfType: PropTypes.bool, itemId: PropTypes.string, itemType: PropTypes.string, diff --git a/src/components/Item/VisualizationItem/Visualization/Visualization.js b/src/components/Item/VisualizationItem/Visualization/Visualization.js index 3b69da97b..50761bad0 100644 --- a/src/components/Item/VisualizationItem/Visualization/Visualization.js +++ b/src/components/Item/VisualizationItem/Visualization/Visualization.js @@ -40,7 +40,7 @@ const Visualization = ({ originalType, showNoFiltersOverlay, onClickNoFiltersOverlay, - // isFS, + isFS, ...rest }) => { const dashboardId = useSelector(sGetSelectedId) @@ -142,7 +142,7 @@ const Visualization = ({ ) } @@ -193,7 +193,7 @@ const Visualization = ({ ) } @@ -232,7 +232,7 @@ Visualization.propTypes = { availableWidth: PropTypes.number, dashboardMode: PropTypes.string, gridWidth: PropTypes.number, - // isFS: PropTypes.bool, + isFS: PropTypes.bool, item: PropTypes.object, itemFilters: PropTypes.object, originalType: PropTypes.string, diff --git a/src/components/Item/VisualizationItem/isElementFullscreen.js b/src/components/Item/VisualizationItem/isElementFullscreen.js deleted file mode 100644 index 1c34dc83a..000000000 --- a/src/components/Item/VisualizationItem/isElementFullscreen.js +++ /dev/null @@ -1,21 +0,0 @@ -import { getGridItemDomElementClassName } from '../../../modules/getGridItemDomElementClassName.js' - -export const isElementFullscreen = (itemId) => { - const fullscreenElement = - document.fullscreenElement || document.webkitFullscreenElement - - // if (!fullscreenElement) { - // console.log('jj no fs element') - // } - - const isFullscreen = fullscreenElement?.classList.contains( - getGridItemDomElementClassName(itemId) - ) - if (isFullscreen) { - console.log('jj fs element', fullscreenElement.id) - } - - return fullscreenElement?.classList.contains( - getGridItemDomElementClassName(itemId) - ) -} diff --git a/src/pages/view/ItemGrid.js b/src/pages/view/ItemGrid.js index 877b47557..645095bbf 100644 --- a/src/pages/view/ItemGrid.js +++ b/src/pages/view/ItemGrid.js @@ -1,4 +1,5 @@ import i18n from '@dhis2/d2-i18n' +import { IconChevronRight16, IconChevronLeft16, colors } from '@dhis2/ui' import cx from 'classnames' import sortBy from 'lodash/sortBy.js' import React, { useState, useEffect, useRef, useCallback } from 'react' @@ -6,7 +7,6 @@ import { Responsive as ResponsiveReactGridLayout } from 'react-grid-layout' import { useDispatch, useSelector } from 'react-redux' import { acSetPresentDashboard } from '../../actions/presentDashboard.js' import { Item } from '../../components/Item/Item.js' -import { getGridItemElement } from '../../components/Item/VisualizationItem/getGridItemElement.js' import NoContentMessage from '../../components/NoContentMessage.js' import ProgressiveLoadingContainer from '../../components/ProgressiveLoadingContainer.js' import { useWindowDimensions } from '../../components/WindowDimensionsProvider.js' @@ -95,15 +95,10 @@ const ResponsiveItemGrid = () => { // Handle Present button or Item Fullscreen button clicked useEffect(() => { if (Number.isInteger(fsItemStartingIndex)) { - // const el = getGridItemElement( - // sItems.current[fsItemStartingIndex].id - // ) const el = fsElement?.current - console.log('jj set starting fs element', el) el?.requestFullscreen() setFsItemIndex(fsItemStartingIndex) } else { - console.log('jj document.exitFS here') document.exitFullscreen().then(() => { setFsItemIndex(null) }) @@ -112,7 +107,6 @@ const ResponsiveItemGrid = () => { const exitFullscreen = () => { if (document.fullscreenElement) { - console.log('jj document.exitFullscreen') document.exitFullscreen().then(() => { dispatch(acSetPresentDashboard(null)) }) @@ -120,34 +114,18 @@ const ResponsiveItemGrid = () => { } const nextItem = useCallback(() => { - // const el = fsElement.current if (fsItemIndex === sItems.current.length - 1) { - // const el = getGridItemElement(sItems.current[0].id) - // el?.requestFullscreen().then(() => { setFsItemIndex(0) - // }) } else { - // const el = getGridItemElement(sItems.current[fsItemIndex + 1].id) - // el?.requestFullscreen().then(() => { setFsItemIndex(fsItemIndex + 1) - // }) } }, [fsItemIndex]) const prevItem = useCallback(() => { - // const el = fsElement.current if (fsItemIndex === 0) { - // const el = getGridItemElement( - // sItems.current[sItems.current.length - 1].id - // ) - // el?.requestFullscreen().then(() => { setFsItemIndex(sItems.current.length - 1) - // }) } else { - // const el = getGridItemElement(sItems.current[fsItemIndex - 1].id) - // el.requestFullscreen().then(() => { setFsItemIndex(fsItemIndex - 1) - // }) } }, [fsItemIndex]) @@ -164,7 +142,6 @@ const ResponsiveItemGrid = () => { } const handleFullscreenChange = () => { - console.log('jj handleFullscreenChange', document.fullscreenElement) if (!document.fullscreenElement) { setFsItemIndex(null) dispatch(acSetPresentDashboard(null)) @@ -205,13 +182,21 @@ const ResponsiveItemGrid = () => { item.firstOfType = true } + const isFS = Number.isInteger(fsItemIndex) + ? sItems.current[fsItemIndex].id === item.id + : null + return ( { dashboardMode={VIEW} isRecording={forceLoad} onToggleItemExpanded={onToggleItemExpanded} - // isFS={ - // !!( - // Number.isInteger(fsItemIndex) && - // sItems.current[fsItemIndex]?.id === item.id - // ) - // } + isFS={isFS} sortPosition={ sItems.current.findIndex((i) => i.id === item.id) + 1 } - numSortItems={sItems.current.length} nextItem={nextItem} prevItem={prevItem} exitFullscreen={exitFullscreen} @@ -254,51 +233,55 @@ const ResponsiveItemGrid = () => { ) } - console.log('jj render ItemGrid with fsItemIndex:', fsItemIndex) - return (
- {Number.isInteger(fsItemIndex) ? ( - <> - Fullscreen container -
- {getItemComponent(sItems.current[fsItemIndex])} -
-
- - - -
- - ) : ( - - {getItemComponents(displayItems)} - - )} + + {getItemComponents(displayItems)} + + +
+ + {` ${fsItemIndex + 1} / ${ + sItems.current.length + } `} + + +
+
) } diff --git a/src/pages/view/styles/ItemGrid.module.css b/src/pages/view/styles/ItemGrid.module.css index b0a2e1368..ca9718a04 100644 --- a/src/pages/view/styles/ItemGrid.module.css +++ b/src/pages/view/styles/ItemGrid.module.css @@ -1,3 +1,68 @@ .grid { margin-top: var(--spacers-dp16); } + +.fscreen>.grid { + margin-top: 0 +} + +#fullscreen-container { + position: relative +} + +.fscreen { + cursor: pointer; +} + +#fullscreen-container.fscreen { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.fullscreenNav { + display: none; +} + +.hideItem { + display: none; +} + +.fscreenItem { + width: 100% !important; + height: 100vh !important; + transform: none !important; +} + +.fullscreenNav.fscreen { + display: flex; + justify-content: center; + position: absolute; + bottom: 50px; + z-index: 1000; + width: 100%; +} + +.buttons { + padding: var(--spacers-dp4); + background-color: var(--colors-grey800); + color: var(--colors-white); +} + +.buttons button { + cursor: pointer; + color: var(--colors-white); +} + +.fullscreenNav button { + border: none; + background-color: transparent; + +} \ No newline at end of file From 7116a28565f94609cb4005f42b3f003ff3c75559 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Fri, 6 Sep 2024 11:56:25 +0200 Subject: [PATCH 08/16] chore: remove props no longer used --- src/components/Item/VisualizationItem/Item.js | 3 --- src/pages/view/ItemGrid.js | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/components/Item/VisualizationItem/Item.js b/src/components/Item/VisualizationItem/Item.js index ba256f954..0d70850aa 100644 --- a/src/components/Item/VisualizationItem/Item.js +++ b/src/components/Item/VisualizationItem/Item.js @@ -187,7 +187,6 @@ class Item extends Component { isFS, setPresent, sortPosition, - exitFullscreen, } = this.props const { showFooter, showNoFiltersOverlay } = this.state const originalType = getItemTypeForVis(item) @@ -203,7 +202,6 @@ class Item extends Component { onSelectActiveType={this.setActiveType} onToggleFooter={this.onToggleFooter} enterFullscreen={() => setPresent(sortPosition - 1)} - exitFullscreen={exitFullscreen} activeType={activeType} activeFooter={showFooter} fullscreenSupported={this.isFullscreenSupported()} @@ -319,7 +317,6 @@ Item.propTypes = { apps: PropTypes.array, dashboardMode: PropTypes.string, engine: PropTypes.object, - exitFullscreen: PropTypes.func, gridWidth: PropTypes.number, isEditing: PropTypes.bool, isFS: PropTypes.bool, diff --git a/src/pages/view/ItemGrid.js b/src/pages/view/ItemGrid.js index 645095bbf..7c7de2965 100644 --- a/src/pages/view/ItemGrid.js +++ b/src/pages/view/ItemGrid.js @@ -211,9 +211,6 @@ const ResponsiveItemGrid = () => { sortPosition={ sItems.current.findIndex((i) => i.id === item.id) + 1 } - nextItem={nextItem} - prevItem={prevItem} - exitFullscreen={exitFullscreen} />
) From 2c79ce390b2150cb0444d35008a6b017d6d616d5 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Mon, 9 Sep 2024 13:25:29 +0200 Subject: [PATCH 09/16] chore: override display for LL --- src/pages/view/styles/ItemGrid.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/view/styles/ItemGrid.module.css b/src/pages/view/styles/ItemGrid.module.css index ca9718a04..72c1f3c33 100644 --- a/src/pages/view/styles/ItemGrid.module.css +++ b/src/pages/view/styles/ItemGrid.module.css @@ -32,7 +32,7 @@ } .hideItem { - display: none; + display: none !important; } .fscreenItem { From 41ffd1268b57b7d221cf89fb8903bfec4d49159d Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Mon, 9 Sep 2024 15:01:02 +0200 Subject: [PATCH 10/16] chore: fs for LL items --- src/components/Item/VisualizationItem/Item.js | 12 ++++++++++++ .../Visualization/IframePlugin.js | 19 ++----------------- .../Visualization/Visualization.js | 1 + 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/components/Item/VisualizationItem/Item.js b/src/components/Item/VisualizationItem/Item.js index 0d70850aa..c5a6c7ed8 100644 --- a/src/components/Item/VisualizationItem/Item.js +++ b/src/components/Item/VisualizationItem/Item.js @@ -49,6 +49,8 @@ import memoizeOne from './memoizeOne.js' import { pluginIsAvailable } from './Visualization/plugin.js' import Visualization from './Visualization/Visualization.js' +const MIN_CLIENT_HEIGHT = 16 + class Item extends Component { state = { showFooter: false, @@ -153,6 +155,13 @@ class Item extends Component { } getAvailableHeight = ({ width }) => { + if (this.props.isFS) { + const totalHeaderHeight = + (this.headerRef.current.clientHeight || MIN_CLIENT_HEIGHT) + + this.itemHeaderTotalMargin + + this.itemContentPadding + return `calc(100vh - ${totalHeaderHeight}px)` + } const calculatedHeight = getItemHeightPx(this.props.item, width) - this.headerRef.current.clientHeight - @@ -168,6 +177,9 @@ class Item extends Component { } getAvailableWidth = () => { + if (this.props.isFS) { + return '100%' + } const rect = getGridItemElement( this.props.item.id )?.getBoundingClientRect() diff --git a/src/components/Item/VisualizationItem/Visualization/IframePlugin.js b/src/components/Item/VisualizationItem/Visualization/IframePlugin.js index 4addf540e..f0a0b5209 100644 --- a/src/components/Item/VisualizationItem/Visualization/IframePlugin.js +++ b/src/components/Item/VisualizationItem/Visualization/IframePlugin.js @@ -36,7 +36,6 @@ const IframePlugin = ({ itemId, itemType, isFirstOfType, - isFS, }) => { const dispatch = useDispatch() const iframePluginStatus = useSelector(sGetIframePluginStatus) @@ -233,19 +232,6 @@ const IframePlugin = ({ ) } - const width = isFS ? '100%' : style.width || '100%' - const height = isFS ? '100vh' : style.height || '100%' - // TODO - get the height right for FS that includes space for header - // here's the implementation from Item.js - // if (isFS) { - // return ( - // height - - // this.headerRef.current.clientHeight - - // this.itemHeaderTotalMargin - - // this.itemContentPadding - // ) - // } - return (
{iframeSrc ? ( @@ -254,8 +240,8 @@ const IframePlugin = ({ src={iframeSrc} // preserve dimensions if provided style={{ - width, - height, + width: style.width || '100%', + height: style.height || '100%', border: 'none', }} > @@ -269,7 +255,6 @@ IframePlugin.propTypes = { dashboardId: PropTypes.string, dashboardMode: PropTypes.string, filterVersion: PropTypes.string, - isFS: PropTypes.bool, isFirstOfType: PropTypes.bool, itemId: PropTypes.string, itemType: PropTypes.string, diff --git a/src/components/Item/VisualizationItem/Visualization/Visualization.js b/src/components/Item/VisualizationItem/Visualization/Visualization.js index 50761bad0..720a5a3c3 100644 --- a/src/components/Item/VisualizationItem/Visualization/Visualization.js +++ b/src/components/Item/VisualizationItem/Visualization/Visualization.js @@ -171,6 +171,7 @@ const Visualization = ({ ) From 609b249895517be2edb6d7ec508a86201dc6adbc Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Mon, 9 Sep 2024 15:23:12 +0200 Subject: [PATCH 11/16] fix: only show Slideshow if fullscreen allowed in sys settings --- i18n/en.pot | 13 ++++++++----- src/pages/view/TitleBar/ActionsBar.js | 22 +++++++++++++--------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 70486b6c1..a12a1a9df 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-09-05T14:25:29.856Z\n" -"PO-Revision-Date: 2024-09-05T14:25:29.857Z\n" +"POT-Creation-Date: 2024-09-09T13:19:23.392Z\n" +"PO-Revision-Date: 2024-09-09T13:19:23.393Z\n" msgid "Untitled dashboard" msgstr "Untitled dashboard" @@ -62,6 +62,12 @@ msgstr "Text box" msgid "Add text here" msgstr "Add text here" +msgid "Open in {{appName}}" +msgstr "Open in {{appName}}" + +msgid "View fullscreen" +msgstr "View fullscreen" + msgid "Back to all interpretations" msgstr "Back to all interpretations" @@ -89,9 +95,6 @@ msgstr "Show details and interpretations" msgid "Open in {{appName}} app" msgstr "Open in {{appName}} app" -msgid "View fullscreen" -msgstr "View fullscreen" - msgid "This map can't be displayed as a chart" msgstr "This map can't be displayed as a chart" diff --git a/src/pages/view/TitleBar/ActionsBar.js b/src/pages/view/TitleBar/ActionsBar.js index 40d0eb85e..69b889bb2 100644 --- a/src/pages/view/TitleBar/ActionsBar.js +++ b/src/pages/view/TitleBar/ActionsBar.js @@ -24,6 +24,7 @@ import { apiPostShowDescription } from '../../../api/description.js' import ConfirmActionDialog from '../../../components/ConfirmActionDialog.js' import DropdownButton from '../../../components/DropdownButton/DropdownButton.js' import MenuItem from '../../../components/MenuItemWithTooltip.js' +import { useSystemSettings } from '../../../components/SystemSettingsProvider.js' import { useCacheableSection } from '../../../modules/useCacheableSection.js' import { orObject } from '../../../modules/util.js' import { sGetDashboardStarred } from '../../../reducers/dashboards.js' @@ -59,6 +60,7 @@ const ViewActions = ({ const { isDisconnected: offline } = useDhis2ConnectionStatus() const { lastUpdated, isCached, startRecording, remove } = useCacheableSection(id) + const { allowVisFullscreen } = useSystemSettings().systemSettings const { show } = useAlert( ({ msg }) => msg, @@ -258,15 +260,17 @@ const ViewActions = ({ ) : null} - - - + {allowVisFullscreen ? ( + + + + ) : null} Date: Mon, 9 Sep 2024 15:23:57 +0200 Subject: [PATCH 12/16] fix: filter out SPACER and MESSAGES items from sorted items list --- src/pages/view/ItemGrid.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/view/ItemGrid.js b/src/pages/view/ItemGrid.js index 7c7de2965..2edbbb434 100644 --- a/src/pages/view/ItemGrid.js +++ b/src/pages/view/ItemGrid.js @@ -25,6 +25,7 @@ import { getGridWidth, getProportionalHeight, } from '../../modules/gridUtil.js' +import { SPACER, MESSAGES } from '../../modules/itemTypes.js' import { getBreakpoint, isSmallScreen } from '../../modules/smallScreen.js' import { useCacheableSection } from '../../modules/useCacheableSection.js' import { sGetPresentDashboard } from '../../reducers/presentDashboard.js' @@ -87,8 +88,9 @@ const ResponsiveItemGrid = () => { }, [recordingState]) useEffect(() => { - const sortedItems = sortBy(displayItems, ['y', 'x']) - // TODO - remove the spacer and message items + const sortedItems = sortBy(displayItems, ['y', 'x']).filter( + (i) => [SPACER, MESSAGES].indexOf(i.type) === -1 + ) sItems.current = sortedItems }, [displayItems]) From 3df05ba82ae802cabf1a593a031bf8386704ee7b Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Mon, 9 Sep 2024 17:26:08 +0200 Subject: [PATCH 13/16] feat: styling and show/hide controls --- i18n/en.pot | 13 ++--- src/pages/view/ItemGrid.js | 62 +++++++++++++-------- src/pages/view/styles/ItemGrid.module.css | 66 +++++++++++++++-------- 3 files changed, 90 insertions(+), 51 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index a12a1a9df..3f7c8212c 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-09-09T13:19:23.392Z\n" -"PO-Revision-Date: 2024-09-09T13:19:23.393Z\n" +"POT-Creation-Date: 2024-09-09T15:15:02.457Z\n" +"PO-Revision-Date: 2024-09-09T15:15:02.458Z\n" msgid "Untitled dashboard" msgstr "Untitled dashboard" @@ -62,12 +62,6 @@ msgstr "Text box" msgid "Add text here" msgstr "Add text here" -msgid "Open in {{appName}}" -msgstr "Open in {{appName}}" - -msgid "View fullscreen" -msgstr "View fullscreen" - msgid "Back to all interpretations" msgstr "Back to all interpretations" @@ -95,6 +89,9 @@ msgstr "Show details and interpretations" msgid "Open in {{appName}} app" msgstr "Open in {{appName}} app" +msgid "View fullscreen" +msgstr "View fullscreen" + msgid "This map can't be displayed as a chart" msgstr "This map can't be displayed as a chart" diff --git a/src/pages/view/ItemGrid.js b/src/pages/view/ItemGrid.js index 2edbbb434..222c2e396 100644 --- a/src/pages/view/ItemGrid.js +++ b/src/pages/view/ItemGrid.js @@ -1,5 +1,5 @@ import i18n from '@dhis2/d2-i18n' -import { IconChevronRight16, IconChevronLeft16, colors } from '@dhis2/ui' +import { IconChevronRight24, IconChevronLeft24, colors } from '@dhis2/ui' import cx from 'classnames' import sortBy from 'lodash/sortBy.js' import React, { useState, useEffect, useRef, useCallback } from 'react' @@ -57,6 +57,8 @@ const ResponsiveItemGrid = () => { const fsItemStartingIndex = useSelector(sGetPresentDashboard) const [fsItemIndex, setFsItemIndex] = useState(null) const fsElement = useRef(null) + const hideControlsTimeout = useRef(null) + const controlsRef = useRef(null) useEffect(() => { const getItemsWithAdjustedHeight = (items) => @@ -100,20 +102,23 @@ const ResponsiveItemGrid = () => { const el = fsElement?.current el?.requestFullscreen() setFsItemIndex(fsItemStartingIndex) - } else { + } else if (document.fullscreenElement) { document.exitFullscreen().then(() => { setFsItemIndex(null) }) + } else { + setFsItemIndex(null) } }, [fsItemStartingIndex]) - const exitFullscreen = () => { - if (document.fullscreenElement) { - document.exitFullscreen().then(() => { - dispatch(acSetPresentDashboard(null)) - }) - } - } + // TODO - this is for an eventual Exit button + // const exitFullscreen = () => { + // if (document.fullscreenElement) { + // document.exitFullscreen().then(() => { + // dispatch(acSetPresentDashboard(null)) + // }) + // } + // } const nextItem = useCallback(() => { if (fsItemIndex === sItems.current.length - 1) { @@ -121,6 +126,7 @@ const ResponsiveItemGrid = () => { } else { setFsItemIndex(fsItemIndex + 1) } + showControls() }, [fsItemIndex]) const prevItem = useCallback(() => { @@ -129,8 +135,17 @@ const ResponsiveItemGrid = () => { } else { setFsItemIndex(fsItemIndex - 1) } + showControls() }, [fsItemIndex]) + const showControls = () => { + clearTimeout(hideControlsTimeout.current) + controlsRef.current?.classList.add(classes.visible) + hideControlsTimeout.current = setTimeout(() => { + controlsRef.current?.classList.remove(classes.visible) + }, 1000) + } + // This effect handles the keyboard navigation for the fullscreen mode useEffect(() => { const handleKeyDown = (event) => { @@ -154,6 +169,8 @@ const ResponsiveItemGrid = () => { window.addEventListener('keydown', handleKeyDown) document.addEventListener('fullscreenchange', handleFullscreenChange) + document.addEventListener('mousemove', showControls) + // Clean up the event listener when the component is unmounted return () => { window.removeEventListener('keydown', handleKeyDown) @@ -161,6 +178,7 @@ const ResponsiveItemGrid = () => { 'fullscreenchange', handleFullscreenChange ) + document.removeEventListener('mousemove', showControls) } }, [dispatch, nextItem, prevItem]) @@ -263,24 +281,24 @@ const ResponsiveItemGrid = () => { > {getItemComponents(displayItems)} - -
+ {Number.isInteger(fsItemIndex) && ( +
- {` ${fsItemIndex + 1} / ${ - sItems.current.length - } `} + {`${ + fsItemIndex + 1 + } / ${sItems.current.length}`} -
- + )}
) } diff --git a/src/pages/view/styles/ItemGrid.module.css b/src/pages/view/styles/ItemGrid.module.css index 72c1f3c33..7ea99b480 100644 --- a/src/pages/view/styles/ItemGrid.module.css +++ b/src/pages/view/styles/ItemGrid.module.css @@ -10,10 +10,6 @@ position: relative } -.fscreen { - cursor: pointer; -} - #fullscreen-container.fscreen { position: fixed; top: 0; @@ -27,10 +23,6 @@ align-items: center; } -.fullscreenNav { - display: none; -} - .hideItem { display: none !important; } @@ -41,28 +33,60 @@ transform: none !important; } -.fullscreenNav.fscreen { +/* Control buttons */ +.fullscreenNav { + display: none; +} + +.fullscreenNav.visible { display: flex; justify-content: center; - position: absolute; - bottom: 50px; - z-index: 1000; + position: fixed; + bottom: 20px; + z-index: 10; width: 100%; } -.buttons { - padding: var(--spacers-dp4); - background-color: var(--colors-grey800); - color: var(--colors-white); +.controls { + background: #202124; + padding: 8px; + border-radius: 5px; + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + visibility: hidden; + transition: opacity 0.5s; + z-index: 10; } -.buttons button { - cursor: pointer; - color: var(--colors-white); +.controls.visible { + opacity: 1; + visibility: visible; } -.fullscreenNav button { +.controls button { + color: var(--colors-white); + background-color: #000; + background: #000; + border-radius: 3px; border: none; - background-color: transparent; + width: 32px; + height: 32px; + padding: 4px; + cursor: pointer; + } +.pageCounter { + font-family: "Roboto", sans-serif; + font-weight: 400; + font-style: normal; + color: white; + font-size: 14px; + margin: 0 10px; + user-select: none; } \ No newline at end of file From 09345905f1214935e114d5bc55810535df2652a7 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Tue, 8 Oct 2024 15:33:10 +0200 Subject: [PATCH 14/16] chore: fix jest tests --- .../__tests__/ItemContextMenu.offline.spec.js | 31 +------------------ .../__tests__/ItemContextMenu.spec.js | 31 +------------------ 2 files changed, 2 insertions(+), 60 deletions(-) diff --git a/src/components/Item/VisualizationItem/ItemContextMenu/__tests__/ItemContextMenu.offline.spec.js b/src/components/Item/VisualizationItem/ItemContextMenu/__tests__/ItemContextMenu.offline.spec.js index e1891d484..cdc63879a 100644 --- a/src/components/Item/VisualizationItem/ItemContextMenu/__tests__/ItemContextMenu.offline.spec.js +++ b/src/components/Item/VisualizationItem/ItemContextMenu/__tests__/ItemContextMenu.offline.spec.js @@ -1,7 +1,6 @@ import { fireEvent } from '@testing-library/dom' -import { render, waitFor, screen } from '@testing-library/react' +import { render, waitFor } from '@testing-library/react' import React from 'react' -import { getGridItemDomElementClassName } from '../../../../../modules/getGridItemDomElementClassName.js' import { useSystemSettings } from '../../../../SystemSettingsProvider.js' import WindowDimensionsProvider from '../../../../WindowDimensionsProvider.js' import ItemContextMenu from '../ItemContextMenu.js' @@ -66,34 +65,6 @@ test('renders just the button when menu closed', () => { expect(queryByText('View fullscreen')).toBeNull() }) -test('renders exit fullscreen button', () => { - useSystemSettings.mockReturnValue(mockSystemSettingsDefault) - const gridItemClassName = getGridItemDomElementClassName( - defaultProps.item.id - ) - - const { rerender } = render( - -
- -
-
- ) - - document.fullscreenElement = document.querySelector(`.${gridItemClassName}`) - - rerender( - -
- -
-
- ) - - document.fullscreenElement = null - expect(screen.getByTestId('exit-fullscreen-button')).toBeTruthy() -}) - test('renders popover menu for BAR chart', async () => { useSystemSettings.mockReturnValue(mockSystemSettingsDefault) const props = Object.assign({}, defaultProps, { diff --git a/src/components/Item/VisualizationItem/ItemContextMenu/__tests__/ItemContextMenu.spec.js b/src/components/Item/VisualizationItem/ItemContextMenu/__tests__/ItemContextMenu.spec.js index 185b54be8..5f9eeacc8 100644 --- a/src/components/Item/VisualizationItem/ItemContextMenu/__tests__/ItemContextMenu.spec.js +++ b/src/components/Item/VisualizationItem/ItemContextMenu/__tests__/ItemContextMenu.spec.js @@ -1,7 +1,6 @@ import { fireEvent } from '@testing-library/dom' -import { render, waitFor, screen } from '@testing-library/react' +import { render, waitFor } from '@testing-library/react' import React from 'react' -import { getGridItemDomElementClassName } from '../../../../../modules/getGridItemDomElementClassName.js' import { useSystemSettings } from '../../../../SystemSettingsProvider.js' import WindowDimensionsProvider from '../../../../WindowDimensionsProvider.js' import ItemContextMenu from '../ItemContextMenu.js' @@ -66,34 +65,6 @@ test('renders just the button when menu closed', () => { expect(queryByText('View fullscreen')).toBeNull() }) -test('renders exit fullscreen button', () => { - useSystemSettings.mockReturnValue(mockSystemSettingsDefault) - const gridItemClassName = getGridItemDomElementClassName( - defaultProps.item.id - ) - - const { rerender } = render( - -
- -
-
- ) - - document.fullscreenElement = document.querySelector(`.${gridItemClassName}`) - - rerender( - -
- -
-
- ) - - document.fullscreenElement = null - expect(screen.getByTestId('exit-fullscreen-button')).toBeTruthy() -}) - test('renders popover menu for BAR chart', async () => { useSystemSettings.mockReturnValue(mockSystemSettingsDefault) const props = Object.assign({}, defaultProps, { From ed77aaca14a4ad19d54db8fe4fd5e8de1b82cdb7 Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Tue, 8 Oct 2024 15:53:43 +0200 Subject: [PATCH 15/16] feat: add exit button and always show controls on hover --- src/pages/view/ItemGrid.js | 60 +++++++++++++---------- src/pages/view/styles/ItemGrid.module.css | 20 ++------ 2 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/pages/view/ItemGrid.js b/src/pages/view/ItemGrid.js index 222c2e396..833e0b6bd 100644 --- a/src/pages/view/ItemGrid.js +++ b/src/pages/view/ItemGrid.js @@ -1,5 +1,10 @@ import i18n from '@dhis2/d2-i18n' -import { IconChevronRight24, IconChevronLeft24, colors } from '@dhis2/ui' +import { + IconChevronRight24, + IconChevronLeft24, + IconCross24, + colors, +} from '@dhis2/ui' import cx from 'classnames' import sortBy from 'lodash/sortBy.js' import React, { useState, useEffect, useRef, useCallback } from 'react' @@ -111,14 +116,13 @@ const ResponsiveItemGrid = () => { } }, [fsItemStartingIndex]) - // TODO - this is for an eventual Exit button - // const exitFullscreen = () => { - // if (document.fullscreenElement) { - // document.exitFullscreen().then(() => { - // dispatch(acSetPresentDashboard(null)) - // }) - // } - // } + const exitFullscreen = () => { + if (document.fullscreenElement) { + document.exitFullscreen().then(() => { + dispatch(acSetPresentDashboard(null)) + }) + } + } const nextItem = useCallback(() => { if (fsItemIndex === sItems.current.length - 1) { @@ -140,6 +144,7 @@ const ResponsiveItemGrid = () => { const showControls = () => { clearTimeout(hideControlsTimeout.current) + controlsRef.current?.classList.add(classes.visible) hideControlsTimeout.current = setTimeout(() => { controlsRef.current?.classList.remove(classes.visible) @@ -282,22 +287,27 @@ const ResponsiveItemGrid = () => { {getItemComponents(displayItems)} {Number.isInteger(fsItemIndex) && ( -
- - {`${ - fsItemIndex + 1 - } / ${sItems.current.length}`} - -
+ <> +
+ + {`${ + fsItemIndex + 1 + } / ${sItems.current.length}`} + + +
+ )}
) diff --git a/src/pages/view/styles/ItemGrid.module.css b/src/pages/view/styles/ItemGrid.module.css index adb09c53e..68d4a1877 100644 --- a/src/pages/view/styles/ItemGrid.module.css +++ b/src/pages/view/styles/ItemGrid.module.css @@ -33,28 +33,13 @@ transform: none !important; } -/* Control buttons */ -.fullscreenNav { - display: none; -} - -.fullscreenNav.visible { - display: flex; - justify-content: center; - position: fixed; - inset-block-end: 20px; - z-index: 10; - inline-size: 100%; -} - .controls { background: #202124; padding: 8px; border-radius: 5px; position: fixed; - inset-block-end: 20px; - inset-inline-start: 50%; - transform: translateX(-50%); + inset-block-start: 20px; + inset-inline-end: 20px; display: flex; align-items: center; justify-content: center; @@ -64,6 +49,7 @@ z-index: 10; } +.controls:hover, .controls.visible { opacity: 1; visibility: visible; From 6c4e86bc5710ec73fb3afbf21f53393893a8a34a Mon Sep 17 00:00:00 2001 From: Jen Jones Arnesen Date: Tue, 8 Oct 2024 16:00:23 +0200 Subject: [PATCH 16/16] chore: consistent class names --- src/pages/view/ItemGrid.js | 8 ++++---- src/pages/view/styles/ItemGrid.module.css | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/pages/view/ItemGrid.js b/src/pages/view/ItemGrid.js index 833e0b6bd..c5a36d204 100644 --- a/src/pages/view/ItemGrid.js +++ b/src/pages/view/ItemGrid.js @@ -219,8 +219,8 @@ const ResponsiveItemGrid = () => { 'view', getGridItemDomElementClassName(item.id), { - [classes.hideItem]: isFS === false, - [classes.fscreenItem]: isFS, + [classes.hiddenItem]: isFS === false, + [classes.fullscreenItem]: isFS, } )} itemId={item.id} @@ -259,7 +259,7 @@ const ResponsiveItemGrid = () => {
@@ -289,7 +289,7 @@ const ResponsiveItemGrid = () => { {Number.isInteger(fsItemIndex) && ( <>