From 7438da922a2d8a83073990e8fa06ee778194e0bb Mon Sep 17 00:00:00 2001 From: Johann Levesque Date: Thu, 17 Oct 2024 14:08:16 -0400 Subject: [PATCH] fix(focus): Solve 2 click before map drag and simplify focus map/footer (#2554) * fix(focus): Solve 2 click before map drag and simplify focus map/footer Closes #2551 * fix comments about autoscroll * Fix tabs collapse and auto scroll * fix comments --------- Co-authored-by: jolevesq --- .../img/guide/datatable/filters_clear.svg | 1 + .../geoview-core/public/locales/en/guide.md | 39 ++++++++-------- .../public/locales/en/translation.json | 2 - .../geoview-core/public/locales/fr/guide.md | 33 ++++++++------ .../public/locales/fr/translation.json | 2 - .../core/components/footer-bar/footer-bar.tsx | 45 ++++++++----------- .../src/core/containers/containers-style.ts | 3 +- .../geoview-core/src/core/utils/utilities.ts | 16 +++++++ .../geoview-core/src/geo/map/map-viewer.ts | 24 ++++++---- packages/geoview-core/src/ui/icons/index.ts | 2 - packages/geoview-core/src/ui/tabs/tabs.tsx | 7 ++- 11 files changed, 97 insertions(+), 77 deletions(-) create mode 100644 packages/geoview-core/public/img/guide/datatable/filters_clear.svg diff --git a/packages/geoview-core/public/img/guide/datatable/filters_clear.svg b/packages/geoview-core/public/img/guide/datatable/filters_clear.svg new file mode 100644 index 00000000000..f69adc01724 --- /dev/null +++ b/packages/geoview-core/public/img/guide/datatable/filters_clear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/geoview-core/public/locales/en/guide.md b/packages/geoview-core/public/locales/en/guide.md index 3c441aebeae..abd62b4500f 100644 --- a/packages/geoview-core/public/locales/en/guide.md +++ b/packages/geoview-core/public/locales/en/guide.md @@ -163,17 +163,17 @@ Click on the **Download** button at the bottom to get the final generated map im =1!footerPanel= # Footer Bar - + The _Footer Bar_ appears below the map. The _Footer Bar_ can be expanded or collapsed by clicking on a tab in the _Footer Bar_ menu bar. To collapse the panel, simply click again on the active tab. The _Footer Bar_ menu bar has the following tabs: -- Legend -- Layers -- Details -- Data Table -- Time Slider -- Chart +- [Legend](#legendSection) +- [Layers](#layersSection) +- [Details](#detailsSection) +- [Data Table](#dataTableSection) +- [Time Slider](#timeSliderSection) +- [Chart](#chartSection) - Guide _Depending on viewer configuration some tabs may not be available_ @@ -182,8 +182,8 @@ _Note: Some tabs in the Footer Bar are also available as tools in the Side Bar,
=2!legend= - -### ![]({{assetsURL}}/img/guide/sidebar/legend_25.svg) Legend + +### ![]({{assetsURL}}/img/guide/sidebar/legend_25.svg) Legend [Top](#footerSection) The _Legend_ tab displays the symbology associated with the layers displayed on the map. @@ -195,8 +195,8 @@ The symbology for the layer can be toggled open and closed which is expanded ben
=2!layers= - -### ![]({{assetsURL}}/img/guide/footer/layers_30.svg) Layers + +### ![]({{assetsURL}}/img/guide/footer/layers_30.svg) Layers [Top](#footerSection) The _Layers_ tab has the following submenu options: @@ -294,8 +294,8 @@ To remove a layer, click on the delete icon ![]({{assetsURL}}/img/guide/layers/r
=2!details= - -### ![]({{assetsURL}}/img/guide/sidebar/details_30.svg) Details + +### ![]({{assetsURL}}/img/guide/sidebar/details_30.svg) Details [Top](#footerSection) The **Details** tab has two sections. The available layers for the map are listed in the left section, and the feature details for each layer are displayed in the right section. @@ -313,8 +313,8 @@ Check the highlight box ![]({{assetsURL}}/img/guide/layers/check.png) to keep th
=2!dataTable= - -### ![]({{assetsURL}}/img/guide/footer/data_table.svg) Data Table + +### ![]({{assetsURL}}/img/guide/footer/data_table.svg) Data Table [Top](#footerSection) The **Data Table** tab has two sections. The layers are listed on the left and the layer data on the right. Click on a layer to show the layer data in the table on the right. @@ -326,6 +326,7 @@ _Note: Some options may not be available or are preselected depending on various | Symbol | Name | Description | | ------------------------------------------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| | Clear Filters | Clear all data table filters | | | Display Filters | Toggle to show or hide filters | | | Filter switch | Apply filters to the map | | | Display Columns | Allows you to choose which columns you want visible and to pin columns to the left or right of the table | @@ -394,8 +395,8 @@ To access the buttons and/or input fields within a cell, make sure the cell is h
=2!timeSlider= - -### ![]({{assetsURL}}/img/guide/footer/time_slider_30.svg) Time Slider + +### ![]({{assetsURL}}/img/guide/footer/time_slider_30.svg) Time Slider [Top](#footerSection) The Time Slider tab on the Footer Bar has two sections. Layers with a time dimension are listed in the left section. Click on a layer to display its Time Slider in the right section: @@ -415,8 +416,8 @@ The field being filtered on is displayed in the lower left of the Time Slider se
=2!chart= - -### ![]({{assetsURL}}/img/guide/footer/chart_30.svg) Chart + +### ![]({{assetsURL}}/img/guide/footer/chart_30.svg) Chart [Top](#footerSection) The Chart tab on the Footer Bar has two sections. Layers with a chart are listed in the left section. Select a feature on the map to display its chart in the right section. diff --git a/packages/geoview-core/public/locales/en/translation.json b/packages/geoview-core/public/locales/en/translation.json index a1195c4e8ae..63a4b5e634a 100644 --- a/packages/geoview-core/public/locales/en/translation.json +++ b/packages/geoview-core/public/locales/en/translation.json @@ -219,8 +219,6 @@ "errorMessage": "Sorry, unable to find the help document!" }, "footerBar": { - "focusToMap": "Focus to map", - "focusToFooter": "Focus to footer", "resizeTooltip": "Resize", "noTab": "No tab" } diff --git a/packages/geoview-core/public/locales/fr/guide.md b/packages/geoview-core/public/locales/fr/guide.md index 66e7fa1cdaa..b62c0bb273d 100644 --- a/packages/geoview-core/public/locales/fr/guide.md +++ b/packages/geoview-core/public/locales/fr/guide.md @@ -157,17 +157,17 @@ Cliquez sur le bouton **Télécharger** au bas de la fenêtre pour générer l =1!footerPanel= # Pied de page - + Le _pied de page_ se trouve sous la carte. Vous pouvez le développer en cliquant sur un des onglets de son menu. Pour le réduire, vous n’avez qu’à recliquer sur l’onglet actif. Le menu du _pied de page_ se compose des onglets suivants : -- Légende -- Couches -- Détails -- Données -- Curseur temporel -- Graphique +- [Légende](#legendSection) +- [Couches](#layersSection) +- [Détails](#detailsSection) +- [Données](#dataTableSection) +- [Curseur temporel](#timeSliderSection) +- [Graphique](#chartSection) - Guide _Selon la configuration du visualiseur, il se pourrait que certains onglets ne s’affichent pas._ @@ -176,7 +176,8 @@ _N.B. : Certains onglets du pied de page sont aussi accessibles comme outil
=2!legend= -### ![]({{assetsURL}}/img/guide/sidebar/legend_25.svg) Légende + +### ![]({{assetsURL}}/img/guide/sidebar/legend_25.svg) Légende [Haut de page](#footerSection) L’onglet **Légende** présente les symboles des couches affichées sur la carte. @@ -188,7 +189,8 @@ Vous pouvez développer ou réduire la liste des symboles d’une couche sous so
=2!layers= -### ![]({{assetsURL}}/img/guide/footer/layers_30.svg) Couches + +### ![]({{assetsURL}}/img/guide/footer/layers_30.svg) Couches [Haut de page](#footerSection) L’onglet **Couches** comprend les sous-menus suivants : @@ -286,7 +288,8 @@ Pour retirer une couche, cliquez sur l’icône de suppression ![]({{assetsURL}}
=2!details= -### ![]({{assetsURL}}/img/guide/sidebar/details_30.svg) Détails + +### ![]({{assetsURL}}/img/guide/sidebar/details_30.svg) Détails [Haut de page](#footerSection) L’onglet **Détails** se divise en deux sections. La section de gauche dresse la liste des couches accessibles, tandis que celle de droite affiche le détail des éléments pour chaque couche. @@ -304,7 +307,8 @@ Cochez la case ![]({{assetsURL}}/img/guide/layers/check.png) pour garder l’él
=2!dataTable= -### ![]({{assetsURL}}/img/guide/footer/data_table.svg) Données + +### ![]({{assetsURL}}/img/guide/footer/data_table.svg) Données [Haut de page](#footerSection) L’onglet **Données** se divise en deux sections. La liste des couches se trouve à gauche, et les données des couches, à droite. Cliquez sur une couche pour en voir les données dans le tableau de droite. @@ -316,6 +320,7 @@ _N.B. : Selon différents facteurs, certaines options sont désactivées ou | Symbole| Nom| Description| |----------|----------|----------| +| | Effacer les filters | Effacer tout les filtres de la table.| | | Afficher/masquer les filtres| Basculer entre l’affichage et le masquage des filtres.| | | Interrupteur de filtres| Appliquer les filtres à la carte.| | | Afficher/masquer les colonnes| Choisir les colonnes visibles et épingler des colonnes à gauche ou à droite du tableau.| @@ -384,7 +389,8 @@ Pour accéder aux boutons ou aux champs de saisie d’une cellule, assurez-vous
=2!timeSlider= -### ![]({{assetsURL}}/img/guide/footer/time_slider_30.svg) Curseur temporel + +### ![]({{assetsURL}}/img/guide/footer/time_slider_30.svg) Curseur temporel [Haut de page](#footerSection) L’onglet **Curseur temporel** dans le pied de page se divise en deux sections. La liste des couches ayant un facteur temporel se trouve dans la section de gauche. Cliquez sur une couche pour afficher son curseur temporel dans la section de droite : @@ -404,7 +410,8 @@ Le champ visé par le filtre s’affiche en bas à gauche de la section du curse
=2!chart= -### ![]({{assetsURL}}/img/guide/footer/chart_30.svg) Graphique + +### ![]({{assetsURL}}/img/guide/footer/chart_30.svg) Graphique [Haut de page](#footerSection) L’onglet **Graphique** dans le pied de page se divise en deux sections. La liste des couches comportant un graphique se trouve dans la section de gauche. Sélectionnez un élément sur la carte pour en voir le graphique dans la section de droite. diff --git a/packages/geoview-core/public/locales/fr/translation.json b/packages/geoview-core/public/locales/fr/translation.json index c6bd1e56356..48440f01909 100644 --- a/packages/geoview-core/public/locales/fr/translation.json +++ b/packages/geoview-core/public/locales/fr/translation.json @@ -218,8 +218,6 @@ "errorMessage": "Désolé, impossible de trouver le document d'aide!" }, "footerBar": { - "focusToMap": "Aller à la carte", - "focusToFooter": "Aller au pied de page", "resizeTooltip": "Redimensionner", "noTab": "Pas d'onglet" } diff --git a/packages/geoview-core/src/core/components/footer-bar/footer-bar.tsx b/packages/geoview-core/src/core/components/footer-bar/footer-bar.tsx index a8c0862fdd1..27f6cb99a00 100644 --- a/packages/geoview-core/src/core/components/footer-bar/footer-bar.tsx +++ b/packages/geoview-core/src/core/components/footer-bar/footer-bar.tsx @@ -2,7 +2,7 @@ import { MutableRefObject, ReactNode, useCallback, useEffect, useMemo, useRef, u import { camelCase } from 'lodash'; import { useTheme } from '@mui/material/styles'; -import { Box, IconButton, Tabs, TypeTabs, MoveDownRoundedIcon, MoveUpRoundedIcon } from '@/ui'; +import { Box, Tabs, TypeTabs } from '@/ui'; import { Plugin } from '@/api/plugin/plugin'; import { getSxClasses } from './footer-bar-style'; import { ResizeFooterPanel } from '@/core/components/resize-footer-panel/resize-footer-panel'; @@ -33,6 +33,7 @@ import { GuidePanel } from '@/core/components/guide/guide-panel'; import { MapEventProcessor } from '@/api/event-processors/event-processor-children/map-event-processor'; import { TypeRecordOfPlugin } from '@/api/plugin/plugin-types'; import { CONTAINER_TYPE } from '@/core/utils/constant'; +import { isElementInViewport } from '@/core/utils/utilities'; interface Tab { icon: ReactNode; @@ -59,8 +60,6 @@ export function FooterBar(props: FooterBarProps): JSX.Element | null { const theme = useTheme(); const sxClasses = getSxClasses(theme); - const [isFocusToMap, setIsFocusToMap] = useState(true); - const tabsContainerRef = useRef(); // get store values and actions @@ -350,16 +349,22 @@ export function FooterBar(props: FooterBarProps): JSX.Element | null { // eslint-disable-next-line react-hooks/exhaustive-deps }, [footerBarTabsConfig, mapId]); - // Handle focus using dynamic focus button - const handleDynamicFocus = useCallback((): void => { + // Scroll the footer into view on mouse click + useEffect((): void => { // Log - logger.logTraceUseCallback('FOOTER BAR - handleDynamicFocus', isFocusToMap, mapId); - - const shell = document.getElementById(`shell-${mapId}`); - const block = isFocusToMap ? 'start' : 'end'; - shell?.scrollIntoView({ behavior: 'smooth', block }); - setIsFocusToMap(!isFocusToMap); - }, [isFocusToMap, mapId]); + logger.logTraceUseEffect('FOOTER BAR - scrollIntoViewListener'); + + if (tabsContainerRef && tabsContainerRef.current) { + const header = tabsContainerRef.current.querySelector('#footerbar-header'); + header?.addEventListener('click', () => { + // Register mouse interaction events (click). If element not in viewport, scroll the footer into view + if (!isElementInViewport(tabsContainerRef.current!)) { + const behaviorScroll = (window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'instant' : 'smooth') as ScrollBehavior; + tabsContainerRef.current?.scrollIntoView({ behavior: behaviorScroll as ScrollBehavior, block: 'center' }); + } + }); + } + }, [tabsContainerRef]); return memoFooterBarTabs.length > 0 ? ( - {!isCollapsed && isMapFullScreen && } - - {isFocusToMap ? : } - - - } + rightButtons={!isCollapsed && isMapFullScreen && } /> ) : null; diff --git a/packages/geoview-core/src/core/containers/containers-style.ts b/packages/geoview-core/src/core/containers/containers-style.ts index 4216b2ae8e8..326e678fb1d 100644 --- a/packages/geoview-core/src/core/containers/containers-style.ts +++ b/packages/geoview-core/src/core/containers/containers-style.ts @@ -78,13 +78,14 @@ export const getShellSxClasses = (theme: Theme): any => ({ }, }, shell: { + scrollMarginTop: '20px', display: 'flex', flexDirection: 'column', top: theme.spacing(0), right: theme.spacing(0), left: theme.spacing(0), bottom: theme.spacing(0), - overflow: 'hidden', + overflow: 'clip', zIndex: 0, height: '100%', }, diff --git a/packages/geoview-core/src/core/utils/utilities.ts b/packages/geoview-core/src/core/utils/utilities.ts index 015146b0059..731d34eac8e 100644 --- a/packages/geoview-core/src/core/utils/utilities.ts +++ b/packages/geoview-core/src/core/utils/utilities.ts @@ -560,3 +560,19 @@ export function handleEscapeKey(key: string, callbackId: string, isFocusTrapped? cb?.(); } } + +/** + * Check if elemetn is in viewport + * @param {Element} el - The element to check for + * @returns {Boolean} true if visible, false otherwise + */ +export function isElementInViewport(el: Element): boolean { + const rect = el.getBoundingClientRect(); + + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && + rect.right <= (window.innerWidth || document.documentElement.clientWidth) + ); +} diff --git a/packages/geoview-core/src/geo/map/map-viewer.ts b/packages/geoview-core/src/geo/map/map-viewer.ts index 2c96a816960..389eab8841b 100644 --- a/packages/geoview-core/src/geo/map/map-viewer.ts +++ b/packages/geoview-core/src/geo/map/map-viewer.ts @@ -50,7 +50,7 @@ import { Snap } from '@/geo/interaction/snap'; import { Translate } from '@/geo/interaction/translate'; import EventHelper, { EventDelegateBase } from '@/api/events/event-helper'; import { ModalApi } from '@/ui'; -import { delay, generateId, getLocalizedMessage } from '@/core/utils/utilities'; +import { delay, generateId, getLocalizedMessage, isElementInViewport } from '@/core/utils/utilities'; import { createEmptyBasemap } from '@/geo/utils/utilities'; import { logger } from '@/core/utils/logger'; import { NORTH_POLE_POSITION } from '@/core/utils/constant'; @@ -572,19 +572,25 @@ export class MapViewer { this.map.on('change:size', this.#handleMapChangeSize.bind(this)); this.map.dispatchEvent('change:size'); // dispatch event to set initial value - // Register mouse interaction events - // set autofocus/blur on mouse enter/leave the map so user can scroll (zoom) without having to click the map + // Register mouse interaction events. On mouse enter or leave, focus or blur the map container const mapHTMLElement = this.map.getTargetElement(); - mapHTMLElement.addEventListener('wheel', (event: WheelEvent) => { - event.preventDefault(); // Abort event - mapHTMLElement.focus(); + mapHTMLElement.addEventListener('mouseenter', () => { + mapHTMLElement.focus({ preventScroll: true }); }); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - mapHTMLElement.addEventListener('mouseleave', (event: MouseEvent) => { + mapHTMLElement.addEventListener('mouseleave', () => { mapHTMLElement.blur(); }); + // Register mouse interaction events (wheel). If element not in viewport, scroll the map into view + const myScrollIntoViewEvent = (): void => { + if (!isElementInViewport(mapHTMLElement)) { + const behaviorScroll = (window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'instant' : 'smooth') as ScrollBehavior; + const shell = document.getElementById(`shell-${this.mapId}`); + shell?.scrollIntoView({ behavior: behaviorScroll as ScrollBehavior, block: 'start' }); + } + }; + mapHTMLElement.addEventListener('wheel', myScrollIntoViewEvent); + // Start checking for layers result sets to be ready this.#checkLayerResultSetReady().catch((error) => { // Log diff --git a/packages/geoview-core/src/ui/icons/index.ts b/packages/geoview-core/src/ui/icons/index.ts index ec604b6e7b6..2adef3e2bea 100644 --- a/packages/geoview-core/src/ui/icons/index.ts +++ b/packages/geoview-core/src/ui/icons/index.ts @@ -73,8 +73,6 @@ export { Menu as MenuIcon, MoreHoriz as MoreHorizIcon, MoreVert as MoreVertIcon, - MoveDownRounded as MoveDownRoundedIcon, - MoveUpRounded as MoveUpRoundedIcon, Opacity as OpacityIcon, OpenInBrowser as OpenInBrowserIcon, Pause as PauseIcon, diff --git a/packages/geoview-core/src/ui/tabs/tabs.tsx b/packages/geoview-core/src/ui/tabs/tabs.tsx index 1f412f9cb6b..df292199c20 100644 --- a/packages/geoview-core/src/ui/tabs/tabs.tsx +++ b/packages/geoview-core/src/ui/tabs/tabs.tsx @@ -152,8 +152,11 @@ export function Tabs(props: TypeTabsProps): JSX.Element { */ const handleClick = useCallback( (e: MouseEvent): void => { + // Get the tab (if already created to extract the value, set -1 if tab does not exist) + // We need this information to know if we create, switch or collapse a tab const { id } = e.target as HTMLDivElement; - const index = Number(id.slice(-1)); + const tab = tabPanels.filter((item) => item !== undefined && item.id === id); + const index = tab.length > 0 ? tab[0].value : -1; // toggle on -1, so that when no tab is selected on fullscreen // and tab is selected again to open the panel. @@ -163,7 +166,7 @@ export function Tabs(props: TypeTabsProps): JSX.Element { if (activeTrap) onOpenKeyboard?.({ activeElementId: id, callbackElementId: id }); else onCloseKeyboard?.(); }, - [activeTrap, onCloseKeyboard, onOpenKeyboard, onToggleCollapse, value] + [activeTrap, onCloseKeyboard, onOpenKeyboard, onToggleCollapse, value, tabPanels] ); useEffect(() => {