Skip to content

Commit

Permalink
Fix position of hover tooltip + Layer fixes (#2159)
Browse files Browse the repository at this point in the history
* fix tooltip of hover to change location

* adding animation to notifications bell

* after build changes

* fixes to code

* fix to remove document.getElementById

* removing comment
  • Loading branch information
cphelefu authored May 22, 2024
1 parent 2578e08 commit 7559348
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 13 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useTheme, Theme } from '@mui/material/styles';

import { Box } from '@/ui';
import { logger } from '@/core/utils/logger';
import { useMapHoverFeatureInfo, useMapPointerPosition } from '@/core/stores/store-interface-and-intial-values/map-state';
import { getSxClasses } from './hover-tooltip-styles';
import { useGeoViewMapId } from '@/core/stores/geoview-store';
import { useAppGeoviewHTMLElement } from '@/core/stores/store-interface-and-intial-values/app-state';

/**
* Hover tooltip component to show name field information on hover
Expand All @@ -17,13 +19,13 @@ export function HoverTooltip(): JSX.Element | null {
// logger.logTraceRender('components/hover-tooltip/hover-tooltip');

const { t } = useTranslation<string>();
const mapId = useGeoViewMapId();

const theme: Theme & {
iconImage: React.CSSProperties;
} = useTheme();

// internal component state
const [pixel, setPixel] = useState<[number, number]>([0, 0]);
const [tooltipValue, setTooltipValue] = useState<string>('');
const [tooltipIcon, setTooltipIcon] = useState<string>('');
const [showTooltip, setShowTooltip] = useState<boolean>(false);
Expand All @@ -33,6 +35,9 @@ export function HoverTooltip(): JSX.Element | null {
// store state
const hoverFeatureInfo = useMapHoverFeatureInfo();
const pointerPosition = useMapPointerPosition();
const mapElem = useAppGeoviewHTMLElement().querySelector(`[id^="mapTargetElement-${mapId}"]`) as HTMLElement;

const tooltipRef = useRef<HTMLDivElement>(null);

// Update tooltip when store value change from propagation by hover-layer-set to map-event-processor
useEffect(() => {
Expand All @@ -54,19 +59,44 @@ export function HoverTooltip(): JSX.Element | null {
setTooltipValue('');
setTooltipIcon('');
setShowTooltip(false);

if (pointerPosition !== undefined) setPixel(pointerPosition.pixel as [number, number]);
}, [pointerPosition]);

// Update tooltip position when we have the dimensions of the tooltip
useEffect(() => {
logger.logTraceUseEffect('HOVER-TOOLTIP - tooltipValue changed', tooltipValue);

if (!mapElem || !tooltipRef.current || !pointerPosition || !pointerPosition.pixel || !tooltipValue) {
return;
}

const mapRect = mapElem.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();

// Check if the tooltip is outside the map
let tooltipX = pointerPosition.pixel[0];
let tooltipY = pointerPosition.pixel[1] - 35;

if (pointerPosition.pixel[0] + tooltipRect.width > mapRect.width) {
tooltipX = pointerPosition.pixel[0] - tooltipRect.width;
}

if (pointerPosition.pixel[1] - tooltipRect.height < mapRect.top) {
tooltipY = pointerPosition.pixel[1] + 10;
}

tooltipRef.current.style.left = `${tooltipX}px`;
tooltipRef.current.style.top = `${tooltipY}px`;
}, [tooltipValue, mapElem, pointerPosition]);

if (showTooltip && !tooltipValue) {
return null;
}

return (
<Box
ref={tooltipRef}
sx={sxClasses.tooltipItem}
style={{
transform: `translate(${pixel[0]}px, ${pixel[1] - 35}px)`,
visibility: showTooltip ? 'visible' : 'hidden',
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
VisibilityOutlinedIcon,
RestartAltIcon,
Paper,
Typography,
} from '@/ui';
import { TypeLegendLayer } from '@/core/components/layers/types';
import {
Expand All @@ -37,7 +38,6 @@ import {
import { LAYER_STATUS } from '@/core/utils/constant';
import { ArrowDownwardIcon, ArrowUpIcon, TableViewIcon } from '@/ui/icons';
import { Divider } from '@/ui/divider/divider';
import { Box } from '@/ui/layout';

interface SingleLayerProps {
layer: TypeLegendLayer;
Expand Down Expand Up @@ -124,10 +124,10 @@ export function SingleLayer({ depth, layer, setIsLayersListPanelVisible, index,

if (datatableSettings[layer.layerPath]) {
return (
<Box component="span">
<Typography sx={{ color: 'unset', fontSize: 'unset' }} component="span">
{itemsLengthDesc} &nbsp;
<TableViewIcon sx={{ marginBottom: '-5px' }} fontSize="small" />
</Box>
</Typography>
);
}
return itemsLengthDesc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ export function LayerDetails(props: LayerDetailsProps): JSX.Element {
sx={{
marginTop: '10px',
color: theme.palette.geoViewColor.textColor.light[200],
fontSize: theme.palette.geoViewFontSize.xs,
fontSize: theme.palette.geoViewFontSize.sm,
textAlign: 'center',
}}
key={generateId()}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useTheme } from '@mui/material/styles';
import _ from 'lodash';
import { ClickAwayListener } from '@mui/material';
import { animated, useSpring } from '@react-spring/web';
import {
Box,
InfoIcon,
Expand All @@ -12,6 +13,7 @@ import {
CloseIcon,
IconButton,
NotificationsIcon,
NotificationsActiveIcon,
Badge,
Typography,
Popper,
Expand Down Expand Up @@ -51,13 +53,32 @@ export default function Notifications(): JSX.Element {

// internal state
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const [hasNewNotification, setHasNewNotification] = useState(false);
const [notificationsCount, setNotificationsCount] = useState(0);
const [open, setOpen] = useState(false);

// get values from the store
const notifications = useAppNotifications();
const interaction = useMapInteraction();
const { removeNotification } = useAppStoreActions();
const notificationsCount = _.sumBy(notifications, (n) => n.count);

useEffect(() => {
logger.logTraceUseEffect('Notifications - notifications list changed', notificationsCount, notifications);
const curNotificationCount = _.sumBy(notifications, (n) => n.count);
if (curNotificationCount > notificationsCount) {
setHasNewNotification(true);
}
setNotificationsCount(curNotificationCount);
}, [notifications, notificationsCount]);

useEffect(() => {
logger.logTraceUseEffect('Notifications - hasNewNotification change', hasNewNotification);
if (hasNewNotification) {
const timeoutId = setTimeout(() => setHasNewNotification(false), 1000); // Remove after 3 seconds
return () => clearTimeout(timeoutId);
}
return undefined;
}, [hasNewNotification]);

// handle open/close
const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>): void => {
Expand All @@ -71,13 +92,26 @@ export default function Notifications(): JSX.Element {
}
};

const shakeAnimation = useSpring({
from: { x: 0, scale: 1 },
to: async (next) => {
await next({ x: 2 }); // Move 10px right and scale up 10%
await next({ x: -2 }); // Move 10px left and scale down 10%
await next({ x: 0 }); // Reset position and scale
},
config: { duration: 50 }, // Adjust duration for faster shake
loop: true,
});

/**
* Remove a notification
*/
const handleRemoveNotificationClick = (notification: NotificationDetailsType): void => {
removeNotification(notification.key);
};

const AnimatedBox = animated(Box);

function getNotificationIcon(notification: NotificationDetailsType): JSX.Element {
switch (notification.notificationType) {
case 'success':
Expand Down Expand Up @@ -122,7 +156,20 @@ export default function Notifications(): JSX.Element {
className={`${interaction === 'dynamic' ? 'style3' : 'style4'} ${open ? 'active' : ''}`}
color="primary"
>
<NotificationsIcon />
{!hasNewNotification && (
<Box>
<NotificationsIcon />
</Box>
)}
{hasNewNotification && (
<AnimatedBox
style={{
...shakeAnimation,
}}
>
<NotificationsActiveIcon />
</AnimatedBox>
)}
</IconButton>
</Badge>

Expand Down

0 comments on commit 7559348

Please sign in to comment.