Skip to content

Commit

Permalink
Integrate retractable menu for control unit dialog [WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangabriele committed Oct 24, 2023
1 parent f943405 commit 8cf8003
Show file tree
Hide file tree
Showing 16 changed files with 207 additions and 99 deletions.
15 changes: 8 additions & 7 deletions frontend/src/domain/shared_slices/Global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<Partial<GlobalStateType>>) {
setDisplayedItems(state, action: PayloadAction<Partial<GlobalState>>) {
return { ...state, ...action.payload }
},

Expand Down Expand Up @@ -175,7 +176,7 @@ const globalSlice = createSlice({
})

export const {
hideSideButtons,
hideDialogs,
removeToast,
setDisplayedItems,
setHealthcheckTextWarning,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -25,6 +26,7 @@ export function ControlUnitDialog() {
const [updateControlUnit] = useUpdateControlUnitMutation()

const close = useCallback(() => {
dispatch(globalActions.hideDialogs())
dispatch(
globalActions.setDisplayedItems({
isControlUnitDialogVisible: false
Expand Down Expand Up @@ -55,7 +57,7 @@ export function ControlUnitDialog() {
}

return (
<Wrapper>
<MainWindowSideDialog>
<MapMenuDialog.Header>
<MapMenuDialog.Title>
<b>{controlUnit.name}</b> ({controlUnit.administration.name})
Expand All @@ -69,20 +71,10 @@ export function ControlUnitDialog() {
<AreaNote controlUnit={controlUnit} onSubmit={updateControlUnit} />
</StyledMapMenuDialogBody>
</Formik>
</Wrapper>
</MainWindowSideDialog>
)
}

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};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -25,7 +25,7 @@ export function Item({ controlUnit }: ItemProps) {
}, [controlUnit.id, dispatch])

return (
<Wrapper data-cy="ControlUnitListDialog-control-unit" data-id={controlUnit.id} onClick={edit}>
<Wrapper data-cy="ControlUnitListDialog-control-unit" data-id={controlUnit.id} onClick={open}>
<NameText>{controlUnit.name}</NameText>
<AdministrationText>{controlUnit.administration.name}</AdministrationText>
<ResourcesAndPortsText>{displayControlUnitResourcesFromControlUnit(controlUnit)}</ResourcesAndPortsText>
Expand Down
19 changes: 8 additions & 11 deletions frontend/src/features/LocateOnMap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | undefined>('')
const results = useGooglePlacesAPI(searchedLocation)

const visibilityState = getVisibilityState(global)

const handleSelectLocation = async placeId => {
if (!placeId) {
return
Expand All @@ -27,13 +30,7 @@ export function LocateOnMap() {
}

return (
<Wrapper
$reportingFormVisibility={
reportingFormVisibility.context === ReportingContext.MAP
? reportingFormVisibility.visibility
: VisibilityState.NONE
}
>
<Wrapper $visibilityState={visibilityState}>
<StyledSearch
data-cy="location-search-input"
isLabelHidden
Expand All @@ -51,11 +48,11 @@ export function LocateOnMap() {
)
}

const Wrapper = styled.div<{ $reportingFormVisibility: VisibilityState }>`
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:
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/features/LocateOnMap/utils.ts
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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 <Wrapper $isShifted={mainWindow.isRightMenuHovered}>{children},?</Wrapper>
}

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;
`
Original file line number Diff line number Diff line change
Expand Up @@ -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])

Expand Down
26 changes: 25 additions & 1 deletion frontend/src/features/MainWindow/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ interface MainWindowState {

isConfirmationModalOpen: boolean
isDialogOpen: boolean

// -----------------------------------
// Layout

isRightMenuHovered: boolean
isSideDialogOpen: boolean
}
const INITIAL_STATE: MainWindowState = {
confirmationModal: undefined,
Expand All @@ -23,7 +29,13 @@ const INITIAL_STATE: MainWindowState = {
// Components Visibility

isConfirmationModalOpen: false,
isDialogOpen: false
isDialogOpen: false,

// -----------------------------------
// Layout

isRightMenuHovered: false,
isSideDialogOpen: false
}

const mainWindowSlice = createSlice({
Expand All @@ -40,6 +52,14 @@ const mainWindowSlice = createSlice({
state.isDialogOpen = false
},

enterSideMenu(state) {
state.isRightMenuHovered = true
},

leaveSideMenu(state) {
state.isRightMenuHovered = false
},

openConfirmationModal(state, action: PayloadAction<ConfirmationModalState>) {
state.confirmationModal = action.payload
state.isConfirmationModalOpen = true
Expand All @@ -48,6 +68,10 @@ const mainWindowSlice = createSlice({
openDialog(state, action: PayloadAction<DialogState>) {
state.dialog = action.payload
state.isDialogOpen = true
},

setIsSideWindowOpen(state, action: PayloadAction<boolean>) {
state.isSideDialogOpen = action.payload
}
}
})
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/features/Reportings/ReportingForm/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -83,7 +83,7 @@ export function ReportingForm({ reducedReportingsOnContext, selectedReporting, s

const reduceOrExpandReporting = () => {
if (isMapContext) {
dispatch(hideSideButtons())
dispatch(hideDialogs())
}
dispatch(reduceOrExpandReportingForm(reportingContext))
}
Expand Down
25 changes: 14 additions & 11 deletions frontend/src/features/Reportings/ReportingsButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Wrapper
reportingFormVisibility={
reportingFormVisibility.context === ReportingContext.MAP
? reportingFormVisibility.visibility
: VisibilityState.NONE
$isShrinked={
mainWindow.isSideDialogOpen ||
(global.reportingFormVisibility.context === ReportingContext.MAP &&
global.reportingFormVisibility.visibility !== VisibilityState.NONE)
}
>
{isSearchReportingsVisible && <SearchReportings />}
{global.isSearchReportingsVisible && <SearchReportings />}
<MenuWithCloseButton.ButtonOnMap
className={isSearchReportingsVisible ? '_active' : undefined}
className={global.isSearchReportingsVisible ? '_active' : undefined}
data-cy="reportings-button"
Icon={Icon.Report}
onClick={toggleSearchReportings}
Expand All @@ -39,10 +40,12 @@ export function ReportingsButton() {
)
}

const Wrapper = styled.div<{ reportingFormVisibility: VisibilityState }>`
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;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/features/Reportings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -39,7 +39,7 @@ export function Reportings({ context }: { context: ReportingContext }) {
}

if (reporting.context === ReportingContext.MAP) {
dispatch(hideSideButtons())
dispatch(hideDialogs())
}

return dispatch(switchReporting(id, context))
Expand Down
Loading

0 comments on commit 8cf8003

Please sign in to comment.