diff --git a/frontend/src/domain/shared_slices/Global.ts b/frontend/src/domain/shared_slices/Global.ts index ddab01d44..76417e28a 100644 --- a/frontend/src/domain/shared_slices/Global.ts +++ b/frontend/src/domain/shared_slices/Global.ts @@ -4,6 +4,7 @@ import { createSlice, type PayloadAction } from '@reduxjs/toolkit' import type { MapToolType } from '../entities/map/constants' +// TODO Refactor this entire concept to make it more generic. export enum ReportingContext { MAP = 'map', SIDE_WINDOW = 'sideWindow' @@ -27,7 +28,7 @@ type Toast = { } /* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */ -type GlobalStateType = { +export type GlobalState = { // state entry for every component /menu displayed on map whose visibility should be controlled displayMissionMenuButton: boolean displayDrawModal: boolean @@ -73,7 +74,7 @@ type GlobalStateType = { toast: Toast | undefined } -const initialState: GlobalStateType = { +const initialState: GlobalState = { // state entry for every component /menu displayed on map whose visibility should be controlled displayMissionMenuButton: true, displayDrawModal: false, @@ -127,20 +128,20 @@ const globalSlice = createSlice({ initialState, name: 'global', reducers: { - hideSideButtons(state) { + hideDialogs(state) { state.isControlUnitDialogVisible = false state.isControlUnitListDialogVisible = false + state.isMapToolVisible = undefined + state.isSearchMissionsVisible = false state.isSearchReportingsVisible = false state.isSearchSemaphoreVisible = false - state.isSearchMissionsVisible = false - state.isMapToolVisible = undefined }, removeToast(state) { state.toast = undefined }, - setDisplayedItems(state, action: PayloadAction>) { + setDisplayedItems(state, action: PayloadAction>) { return { ...state, ...action.payload } }, @@ -175,7 +176,7 @@ const globalSlice = createSlice({ }) export const { - hideSideButtons, + hideDialogs, removeToast, setDisplayedItems, setHealthcheckTextWarning, diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx index 85b7a2432..bb682ea04 100644 --- a/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx @@ -13,6 +13,7 @@ import { globalActions } from '../../../../domain/shared_slices/Global' import { useAppDispatch } from '../../../../hooks/useAppDispatch' import { useAppSelector } from '../../../../hooks/useAppSelector' import { FrontendError } from '../../../../libs/FrontendError' +import { MainWindowSideDialog } from '../../../MainWindow/components/MainWindowSideDialog' export function ControlUnitDialog() { const dispatch = useAppDispatch() @@ -25,6 +26,7 @@ export function ControlUnitDialog() { const [updateControlUnit] = useUpdateControlUnitMutation() const close = useCallback(() => { + dispatch(globalActions.hideDialogs()) dispatch( globalActions.setDisplayedItems({ isControlUnitDialogVisible: false @@ -55,7 +57,7 @@ export function ControlUnitDialog() { } return ( - + {controlUnit.name} ({controlUnit.administration.name}) @@ -69,20 +71,10 @@ export function ControlUnitDialog() { - + ) } -const Wrapper = styled(MapMenuDialog.Container)` - height: 640px; - max-height: 640px; - position: absolute; - right: 50px; - top: 82px; - z-index: 2; - width: 500px; -` - const StyledMapMenuDialogBody = styled(MapMenuDialog.Body)` background-color: ${p => p.theme.color.gainsboro}; diff --git a/frontend/src/features/ControlUnit/components/ControlUnitListDialog/Item.tsx b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/Item.tsx index bc00da362..67707f39e 100644 --- a/frontend/src/features/ControlUnit/components/ControlUnitListDialog/Item.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitListDialog/Item.tsx @@ -14,7 +14,7 @@ export type ItemProps = { export function Item({ controlUnit }: ItemProps) { const dispatch = useAppDispatch() - const edit = useCallback(() => { + const open = useCallback(() => { dispatch(controlUnitDialogActions.setControlUnitId(controlUnit.id)) dispatch( globalActions.setDisplayedItems({ @@ -25,7 +25,7 @@ export function Item({ controlUnit }: ItemProps) { }, [controlUnit.id, dispatch]) return ( - + {controlUnit.name} {controlUnit.administration.name} {displayControlUnitResourcesFromControlUnit(controlUnit)} diff --git a/frontend/src/features/LocateOnMap/index.tsx b/frontend/src/features/LocateOnMap/index.tsx index a0ebfab56..0a64e7f5c 100644 --- a/frontend/src/features/LocateOnMap/index.tsx +++ b/frontend/src/features/LocateOnMap/index.tsx @@ -3,19 +3,22 @@ import { transformExtent } from 'ol/proj' import { useState } from 'react' import styled from 'styled-components' +import { getVisibilityState } from './utils' import { getPlaceCoordinates, useGooglePlacesAPI } from '../../api/googlePlacesAPI/googlePlacesAPI' import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../domain/entities/map/constants' -import { ReportingContext, VisibilityState } from '../../domain/shared_slices/Global' +import { VisibilityState } from '../../domain/shared_slices/Global' import { setFitToExtent } from '../../domain/shared_slices/Map' import { useAppDispatch } from '../../hooks/useAppDispatch' import { useAppSelector } from '../../hooks/useAppSelector' export function LocateOnMap() { const dispatch = useAppDispatch() - const { reportingFormVisibility } = useAppSelector(state => state.global) + const global = useAppSelector(state => state.global) const [searchedLocation, setSearchedLocation] = useState('') const results = useGooglePlacesAPI(searchedLocation) + const visibilityState = getVisibilityState(global) + const handleSelectLocation = async placeId => { if (!placeId) { return @@ -27,13 +30,7 @@ export function LocateOnMap() { } return ( - + ` +const Wrapper = styled.div<{ $visibilityState: VisibilityState }>` position: absolute; top: 10px; right: ${p => { - switch (p.$reportingFormVisibility) { + switch (p.$visibilityState) { case VisibilityState.VISIBLE: return '512' case VisibilityState.VISIBLE_LEFT: diff --git a/frontend/src/features/LocateOnMap/utils.ts b/frontend/src/features/LocateOnMap/utils.ts new file mode 100644 index 000000000..693ecd908 --- /dev/null +++ b/frontend/src/features/LocateOnMap/utils.ts @@ -0,0 +1,16 @@ +import { ReportingContext, VisibilityState, type GlobalState } from '../../domain/shared_slices/Global' + +export function getVisibilityState(global: GlobalState): VisibilityState { + if ( + global.reportingFormVisibility.context === ReportingContext.MAP && + global.reportingFormVisibility.visibility !== VisibilityState.NONE + ) { + return global.reportingFormVisibility.visibility + } + + if (global.isControlUnitDialogVisible) { + return VisibilityState.VISIBLE + } + + return VisibilityState.NONE +} diff --git a/frontend/src/features/MainWindow/components/MainWindowSideDialog.tsx b/frontend/src/features/MainWindow/components/MainWindowSideDialog.tsx new file mode 100644 index 000000000..1148e1bf1 --- /dev/null +++ b/frontend/src/features/MainWindow/components/MainWindowSideDialog.tsx @@ -0,0 +1,43 @@ +import { MapMenuDialog } from '@mtes-mct/monitor-ui' +import { useEffect, type ReactNode } from 'react' +import styled from 'styled-components' + +import { useAppDispatch } from '../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../hooks/useAppSelector' +import { mainWindowActions } from '../slice' + +type MainWindowSideDialogProps = { + children?: ReactNode +} +export function MainWindowSideDialog({ children }: MainWindowSideDialogProps) { + const mainWindow = useAppSelector(state => state.mainWindow) + const dispatch = useAppDispatch() + + useEffect(() => { + dispatch(mainWindowActions.setIsSideWindowOpen(true)) + + return () => { + dispatch(mainWindowActions.setIsSideWindowOpen(false)) + } + }, [dispatch]) + + return {children},? +} + +export const Wrapper = styled(MapMenuDialog.Container)<{ + $isShifted: boolean +}>` + background-color: ${p => p.theme.color.white}; + bottom: 0; + display: flex; + max-height: none; + overflow: hidden; + margin: 0; + position: absolute; + right: ${p => (p.$isShifted ? '60px' : '8px')}; + top: 0; + transition: right 0.5s ease-out, top 0.5s ease-out; + width: 500px; + /* TODO Normalize a strategy for z-indexes between dialogs, modals, menus, etc. */ + z-index: 1; +` diff --git a/frontend/src/features/MainWindow/components/RightMenu/ControlUnitListButton.tsx b/frontend/src/features/MainWindow/components/RightMenu/ControlUnitListButton.tsx index dfff856ed..0154289d0 100644 --- a/frontend/src/features/MainWindow/components/RightMenu/ControlUnitListButton.tsx +++ b/frontend/src/features/MainWindow/components/RightMenu/ControlUnitListButton.tsx @@ -13,7 +13,7 @@ export function ControlUnitListButton() { const { isControlUnitListDialogVisible } = useAppSelector(state => state.global) const toggleDialog = useCallback(() => { - dispatch(globalActions.hideSideButtons()) + dispatch(globalActions.hideDialogs()) dispatch(globalActions.setDisplayedItems({ isControlUnitListDialogVisible: !isControlUnitListDialogVisible })) }, [dispatch, isControlUnitListDialogVisible]) diff --git a/frontend/src/features/MainWindow/slice.ts b/frontend/src/features/MainWindow/slice.ts index 2b3016699..86eb0e011 100644 --- a/frontend/src/features/MainWindow/slice.ts +++ b/frontend/src/features/MainWindow/slice.ts @@ -14,6 +14,12 @@ interface MainWindowState { isConfirmationModalOpen: boolean isDialogOpen: boolean + + // ----------------------------------- + // Layout + + isRightMenuHovered: boolean + isSideDialogOpen: boolean } const INITIAL_STATE: MainWindowState = { confirmationModal: undefined, @@ -23,7 +29,13 @@ const INITIAL_STATE: MainWindowState = { // Components Visibility isConfirmationModalOpen: false, - isDialogOpen: false + isDialogOpen: false, + + // ----------------------------------- + // Layout + + isRightMenuHovered: false, + isSideDialogOpen: false } const mainWindowSlice = createSlice({ @@ -40,6 +52,14 @@ const mainWindowSlice = createSlice({ state.isDialogOpen = false }, + enterSideMenu(state) { + state.isRightMenuHovered = true + }, + + leaveSideMenu(state) { + state.isRightMenuHovered = false + }, + openConfirmationModal(state, action: PayloadAction) { state.confirmationModal = action.payload state.isConfirmationModalOpen = true @@ -48,6 +68,10 @@ const mainWindowSlice = createSlice({ openDialog(state, action: PayloadAction) { state.dialog = action.payload state.isDialogOpen = true + }, + + setIsSideWindowOpen(state, action: PayloadAction) { + state.isSideDialogOpen = action.payload } } }) diff --git a/frontend/src/features/Reportings/ReportingForm/Form.tsx b/frontend/src/features/Reportings/ReportingForm/Form.tsx index ec0845de0..06e2ebdab 100644 --- a/frontend/src/features/Reportings/ReportingForm/Form.tsx +++ b/frontend/src/features/Reportings/ReportingForm/Form.tsx @@ -13,7 +13,7 @@ import { SubThemesSelector } from './FormComponents/ThemeSelector/SubThemesSelec import { Validity } from './FormComponents/Validity' import { type Reporting, ReportingTypeEnum, ReportingTypeLabels } from '../../../domain/entities/reporting' import { - hideSideButtons, + hideDialogs, setReportingFormVisibility, ReportingContext, VisibilityState @@ -83,7 +83,7 @@ export function ReportingForm({ reducedReportingsOnContext, selectedReporting, s const reduceOrExpandReporting = () => { if (isMapContext) { - dispatch(hideSideButtons()) + dispatch(hideDialogs()) } dispatch(reduceOrExpandReportingForm(reportingContext)) } diff --git a/frontend/src/features/Reportings/ReportingsButton/index.tsx b/frontend/src/features/Reportings/ReportingsButton/index.tsx index 595ad0b48..9f5fd41b5 100644 --- a/frontend/src/features/Reportings/ReportingsButton/index.tsx +++ b/frontend/src/features/Reportings/ReportingsButton/index.tsx @@ -10,25 +10,26 @@ import { MenuWithCloseButton } from '../../commonStyles/map/MenuWithCloseButton' export function ReportingsButton() { const dispatch = useAppDispatch() - const { isSearchReportingsVisible, reportingFormVisibility } = useAppSelector(state => state.global) + const global = useAppSelector(state => state.global) + const mainWindow = useAppSelector(state => state.mainWindow) const toggleSearchReportings = () => { - dispatch(globalActions.hideSideButtons()) + dispatch(globalActions.hideDialogs()) dispatch(reduceReportingFormOnMap()) - dispatch(globalActions.setDisplayedItems({ isSearchReportingsVisible: !isSearchReportingsVisible })) + dispatch(globalActions.setDisplayedItems({ isSearchReportingsVisible: !global.isSearchReportingsVisible })) } return ( - {isSearchReportingsVisible && } + {global.isSearchReportingsVisible && } ` +const Wrapper = styled.div<{ + $isShrinked: boolean +}>` position: absolute; top: 130px; - right: ${p => (p.reportingFormVisibility === VisibilityState.VISIBLE ? '0' : '10')}px; + right: ${p => (p.$isShrinked ? 0 : '10px')}; display: flex; justify-content: flex-end; transition: right 0.3s ease-out; diff --git a/frontend/src/features/Reportings/index.tsx b/frontend/src/features/Reportings/index.tsx index e5be09032..b2694d98a 100644 --- a/frontend/src/features/Reportings/index.tsx +++ b/frontend/src/features/Reportings/index.tsx @@ -6,7 +6,7 @@ import styled from 'styled-components' import { ReportingFormWithContext } from './ReportingForm' import { StyledChevronIcon, StyledHeader, StyledHeaderButtons, StyledTitle } from './style' import { getReportingTitle } from './utils' -import { hideSideButtons, ReportingContext, VisibilityState } from '../../domain/shared_slices/Global' +import { hideDialogs, ReportingContext, VisibilityState } from '../../domain/shared_slices/Global' import { closeReporting } from '../../domain/use_cases/reporting/closeReporting' import { reduceOrExpandReportingForm } from '../../domain/use_cases/reporting/reduceOrExpandReportingForm' import { switchReporting } from '../../domain/use_cases/reporting/switchReporting' @@ -39,7 +39,7 @@ export function Reportings({ context }: { context: ReportingContext }) { } if (reporting.context === ReportingContext.MAP) { - dispatch(hideSideButtons()) + dispatch(hideDialogs()) } return dispatch(switchReporting(id, context)) diff --git a/frontend/src/features/Semaphores/SearchSemaphoreButton/index.tsx b/frontend/src/features/Semaphores/SearchSemaphoreButton/index.tsx index 6e529ab92..e06aaf2fc 100644 --- a/frontend/src/features/Semaphores/SearchSemaphoreButton/index.tsx +++ b/frontend/src/features/Semaphores/SearchSemaphoreButton/index.tsx @@ -10,24 +10,25 @@ import { MenuWithCloseButton } from '../../commonStyles/map/MenuWithCloseButton' export function SearchSemaphoreButton() { const dispatch = useAppDispatch() - const { isSearchSemaphoreVisible, reportingFormVisibility } = useAppSelector(state => state.global) + const global = useAppSelector(state => state.global) + const mainWindow = useAppSelector(state => state.mainWindow) const openOrCloseSearchSemaphore = () => { - dispatch(globalActions.hideSideButtons()) + dispatch(globalActions.hideDialogs()) dispatch(reduceReportingFormOnMap()) - dispatch(globalActions.setDisplayedItems({ isSearchSemaphoreVisible: !isSearchSemaphoreVisible })) + dispatch(globalActions.setDisplayedItems({ isSearchSemaphoreVisible: !global.isSearchSemaphoreVisible })) } return ( - {isSearchSemaphoreVisible && } + {global.isSearchSemaphoreVisible && } ` +const Wrapper = styled.div<{ + $isShrinked: boolean +}>` position: absolute; top: 178px; - right: ${p => (p.reportingFormVisibility === VisibilityState.VISIBLE ? '0' : '10')}px; + right: ${p => (p.$isShrinked ? 0 : '10px')}; display: flex; justify-content: flex-end; transition: right 0.3s ease-out; diff --git a/frontend/src/features/map/layers/InterestPointLayer.tsx b/frontend/src/features/map/layers/InterestPointLayer.tsx index 8d1f9649f..642886241 100644 --- a/frontend/src/features/map/layers/InterestPointLayer.tsx +++ b/frontend/src/features/map/layers/InterestPointLayer.tsx @@ -119,7 +119,7 @@ export function InterestPointLayer({ map }: BaseMapChildrenProps) { const modifyInterestPoint = useCallback( uuid => { - dispatch(globalActions.hideSideButtons()) + dispatch(globalActions.hideDialogs()) dispatch(editInterestPoint(uuid)) dispatch(globalActions.setIsMapToolVisible(MapToolType.INTEREST_POINT)) }, diff --git a/frontend/src/features/map/shared/RightMenuOnHoverArea.tsx b/frontend/src/features/map/shared/RightMenuOnHoverArea.tsx index 21e484e6f..3c52703a7 100644 --- a/frontend/src/features/map/shared/RightMenuOnHoverArea.tsx +++ b/frontend/src/features/map/shared/RightMenuOnHoverArea.tsx @@ -1,55 +1,83 @@ import { useEffect, useRef } from 'react' import styled from 'styled-components' -import { setReportingFormVisibility, ReportingContext, VisibilityState } from '../../../domain/shared_slices/Global' +import { globalActions, ReportingContext, VisibilityState } from '../../../domain/shared_slices/Global' import { useAppDispatch } from '../../../hooks/useAppDispatch' import { useAppSelector } from '../../../hooks/useAppSelector' import { useClickOutsideWhenOpened } from '../../../hooks/useClickOutsideWhenOpened' +import { mainWindowActions } from '../../MainWindow/slice' +// TODO Refactor this entire concept to make it more generic. export function RightMenuOnHoverArea() { const dispatch = useAppDispatch() - const { - reportingFormVisibility: { context, visibility } - } = useAppSelector(state => state.global) + const global = useAppSelector(state => state.global) + const mainWindow = useAppSelector(state => state.mainWindow) - const isReportingFormVisible = visibility === VisibilityState.VISIBLE || visibility === VisibilityState.VISIBLE_LEFT + const isReportingFormVisible = global.reportingFormVisibility.visibility !== VisibilityState.NONE const areaRef = useRef(null) - const clickedOutsideComponent = useClickOutsideWhenOpened(areaRef, isReportingFormVisible) - useEffect(() => { - if (clickedOutsideComponent && context === ReportingContext.MAP && visibility === VisibilityState.VISIBLE_LEFT) { + const clickedOutsideComponent = useClickOutsideWhenOpened( + areaRef, + isReportingFormVisible || global.isControlUnitDialogVisible + ) + + const handleMouseEnter = () => { + dispatch(mainWindowActions.enterSideMenu()) + + if ( + global.reportingFormVisibility.context === ReportingContext.MAP && + global.reportingFormVisibility.visibility === VisibilityState.VISIBLE + ) { dispatch( - setReportingFormVisibility({ + globalActions.setReportingFormVisibility({ context: ReportingContext.MAP, - visibility: VisibilityState.VISIBLE + visibility: VisibilityState.VISIBLE_LEFT }) ) } + } - // to prevent re-render - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [clickedOutsideComponent]) + const handleMouseLeave = () => { + dispatch(mainWindowActions.leaveSideMenu()) + } - const onMouseEnter = () => { - if (context === ReportingContext.MAP && visibility === VisibilityState.VISIBLE) { + useEffect(() => { + if ( + clickedOutsideComponent && + global.reportingFormVisibility.context === ReportingContext.MAP && + global.reportingFormVisibility.visibility === VisibilityState.VISIBLE_LEFT + ) { dispatch( - setReportingFormVisibility({ + globalActions.setReportingFormVisibility({ context: ReportingContext.MAP, - visibility: VisibilityState.VISIBLE_LEFT + visibility: VisibilityState.VISIBLE }) ) } - } - return isReportingFormVisible ? : null + // to prevent re-render + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [clickedOutsideComponent]) + + return global.isControlUnitDialogVisible || isReportingFormVisible ? ( + + ) : null } -const Area = styled.div` +const Area = styled.div<{ + $isHovered: boolean +}>` + background-color: green; height: 500px; - right: 0; - width: 60px; - opacity: 0; position: absolute; + right: 0; top: 56px; + width: ${p => (p.$isHovered ? '60px' : '8px')}; + z-index: ${p => (p.$isHovered ? 0 : 999)}; ` diff --git a/frontend/src/features/map/tools/measurements/MeasurementMapButton.tsx b/frontend/src/features/map/tools/measurements/MeasurementMapButton.tsx index adcc09053..23ea80aa3 100644 --- a/frontend/src/features/map/tools/measurements/MeasurementMapButton.tsx +++ b/frontend/src/features/map/tools/measurements/MeasurementMapButton.tsx @@ -41,7 +41,7 @@ export function MeasurementMapButton() { }, [dispatch, isOpen, isMeasurementToolOpen, measurementTypeToAdd]) const makeMeasurement = nextMeasurementTypeToAdd => { - dispatch(globalActions.hideSideButtons()) + dispatch(globalActions.hideDialogs()) dispatch(setMeasurementTypeToAdd(nextMeasurementTypeToAdd)) dispatch(globalActions.setIsMapToolVisible(MapToolType.MEASUREMENT)) } @@ -62,7 +62,7 @@ export function MeasurementMapButton() { dispatch(setMeasurementTypeToAdd(undefined)) dispatch(globalActions.setIsMapToolVisible(undefined)) } else { - dispatch(globalActions.hideSideButtons()) + dispatch(globalActions.hideDialogs()) dispatch(globalActions.setIsMapToolVisible(MapToolType.MEASUREMENT_MENU)) } dispatch(reduceReportingFormOnMap()) diff --git a/frontend/src/features/missions/MissionsButton/index.tsx b/frontend/src/features/missions/MissionsButton/index.tsx index 5cd1b021a..bd4e6c2f9 100644 --- a/frontend/src/features/missions/MissionsButton/index.tsx +++ b/frontend/src/features/missions/MissionsButton/index.tsx @@ -15,9 +15,8 @@ import { sideWindowActions, SideWindowStatus } from '../../SideWindow/slice' export function MissionsMenu() { const dispatch = useAppDispatch() - const { displayMissionsLayer, isSearchMissionsVisible, reportingFormVisibility } = useAppSelector( - state => state.global - ) + const global = useAppSelector(state => state.global) + const mainWindow = useAppSelector(state => state.mainWindow) const { sideWindow } = useAppSelector(state => state) const isMissionButtonIsActive = useMemo( @@ -31,7 +30,7 @@ export function MissionsMenu() { } const toggleMissionsLayer = () => { - dispatch(setDisplayedItems({ displayMissionsLayer: !displayMissionsLayer })) + dispatch(setDisplayedItems({ displayMissionsLayer: !global.displayMissionsLayer })) } const toggleMissionsMenu = e => { @@ -40,7 +39,7 @@ export function MissionsMenu() { setDisplayedItems({ isControlUnitDialogVisible: false, isControlUnitListDialogVisible: false, - isSearchMissionsVisible: !isSearchMissionsVisible, + isSearchMissionsVisible: !global.isSearchMissionsVisible, isSearchReportingsVisible: false, isSearchSemaphoreVisible: false }) @@ -53,20 +52,20 @@ export function MissionsMenu() { return ( - {isSearchMissionsVisible && ( + {global.isSearchMissionsVisible && ( Missions et contrĂ´les @@ -93,10 +92,12 @@ export function MissionsMenu() { ) } -const Wrapper = styled.div<{ reportingFormVisibility: VisibilityState }>` +const Wrapper = styled.div<{ + $isShrinked: boolean +}>` position: absolute; top: 82px; - right: ${p => (p.reportingFormVisibility === VisibilityState.VISIBLE ? '0' : '10')}px; + right: ${p => (p.$isShrinked ? 0 : '10px')}; display: flex; justify-content: flex-end; transition: right 0.3s ease-out;