Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 7d477849

Přidáno uživatelem Václav Honzík před asi 2 roky(ů)

reidxing + drag jump fix

re #9741

Zobrazit rozdíly:

frontend/src/features/TrackingTool/DraggableList/DraggableList.tsx
1 1
import { memo } from 'react'
2
import { DragDropContext, Droppable, OnDragEndResponder } from 'react-beautiful-dnd'
2
import {
3
    DragDropContext,
4
    Droppable,
5
    OnDragEndResponder,
6
} from 'react-beautiful-dnd'
3 7
import { MapPoint } from '../Map/pathUtils'
4 8
import DraggableListItem from './DraggableListItem'
5 9

  
......
9 13
}
10 14

  
11 15
const DraggableList = memo(({ items, onDragEnd }: DraggableListProps) => {
16
    window.addEventListener('error', (e) => {
17
        if (
18
            e.message ===
19
                'ResizeObserver loop completed with undelivered notifications.' ||
20
            e.message === 'ResizeObserver loop limit exceeded'
21
        ) {
22
            e.stopImmediatePropagation()
23
        }
24
    })
25

  
12 26
    return (
13 27
        <DragDropContext onDragEnd={onDragEnd}>
14 28
            <Droppable droppableId="droppable-list">
......
17 31
                        {items.map((item, index) => (
18 32
                            <DraggableListItem
19 33
                                list={items}
20
                                index={index}
34
                                idx={index}
21 35
                                key={item.id}
22 36
                            />
23 37
                        ))}
frontend/src/features/TrackingTool/DraggableList/DraggableListItem.tsx
5 5
    ListItemAvatar,
6 6
    ListItemText,
7 7
} from '@mui/material'
8
import makeStyles from '@mui/material/styles/makeStyles'
9 8
import { Draggable } from 'react-beautiful-dnd'
10 9
import { MapPoint, PathVariant } from '../Map/pathUtils'
11 10
import LocationOnIcon from '@mui/icons-material/LocationOn'
12 11
import { CatalogItemDto } from '../../../swagger/data-contracts'
13 12
import { useDispatch } from 'react-redux'
14 13
import { updateMapMarker, updateMapMarkerWithId } from '../trackingToolSlice'
15
import { Fragment, useEffect, useState } from 'react'
14
import { useMemo } from 'react'
16 15

  
17 16
export type DraggableListItemProps = {
18 17
    list: PathVariant
19
    index: number
18
    idx: number
20 19
}
21 20

  
22 21
const getFormattedLocationOrEmpty = (catalogItem: CatalogItemDto) => {
......
27 26
    return `${catalogItem.latitude}°, ${catalogItem.longitude}°`
28 27
}
29 28

  
30
const DraggableListItem = ({ list, index }: DraggableListItemProps) => {
31
    const item = list[index]
29
const DraggableListItem = ({ list, idx }: DraggableListItemProps) => {
30
    const item = list[idx]
32 31
    const dispatch = useDispatch()
33
    const toggleHidden = () => {
34
        dispatch(
35
            updateMapMarkerWithId({
36
                item: {
37
                    ...item,
38
                    active: !item?.active,
39
                } as MapPoint,
40
                id: item.id,
41
            })
42
        )
43
    }
44 32

  
45
    return (
46
        <Draggable key={`${item.id}`} draggableId={`${item.id}`} index={index}>
47
            {(provided, snapshot) => (
48
                <ListItem
49
                    ref={provided.innerRef}
50
                    {...provided.draggableProps}
51
                    {...provided.dragHandleProps}
52
                    sx={{
53
                        background: snapshot.isDragging
54
                            ? 'rgb(235,235,235)'
55
                            : 'inherit',
56
                    }}
57
                >
58
                    <ListItemAvatar>
59
                        <Avatar>
60
                            <LocationOnIcon />
61
                        </Avatar>
62
                    </ListItemAvatar>
63
                    <ListItemText
64
                        primary={item.catalogItem.name ?? 'Unknown name'}
65
                        secondary={getFormattedLocationOrEmpty(
66
                            item.catalogItem
67
                        )}
68
                    />
69
                    <Checkbox checked={item.active} onChange={toggleHidden} />
70
                </ListItem>
71
            )}
72
        </Draggable>
73
    )
33
    // useMemo to prevent unnecessary re-renders which will make the list jumpy
34
    return useMemo(() => {
35
        const toggleHidden = () => {
36
            dispatch(
37
                updateMapMarkerWithId({
38
                    item: {
39
                        ...item,
40
                        active: !item?.active,
41
                    } as MapPoint,
42
                    id: item.id,
43
                })
44
            )
45
        }
46

  
47
        return (
48
            <Draggable
49
                key={`${item.id}`}
50
                draggableId={`${item.id}`}
51
                index={idx}
52
            >
53
                {(provided, snapshot) => (
54
                    <ListItem
55
                        ref={provided.innerRef}
56
                        {...provided.draggableProps}
57
                        {...provided.dragHandleProps}
58
                        sx={{
59
                            background: snapshot.isDragging
60
                                ? 'rgb(235,235,235)'
61
                                : 'inherit',
62
                        }}
63
                    >
64
                        <ListItemAvatar>
65
                            <Avatar>
66
                                <LocationOnIcon />
67
                            </Avatar>
68
                        </ListItemAvatar>
69
                        <ListItemText
70
                            primary={item.catalogItem.name ?? 'Unknown name'}
71
                            secondary={getFormattedLocationOrEmpty(
72
                                item.catalogItem
73
                            )}
74
                        />
75
                        <Checkbox
76
                            checked={item.active}
77
                            onChange={toggleHidden}
78
                        />
79
                    </ListItem>
80
                )}
81
            </Draggable>
82
        )
83
    }, [item, idx, dispatch])
74 84
}
75 85

  
76 86
export default DraggableListItem
frontend/src/features/TrackingTool/DraggableList/DraggableMarkerList.tsx
1 1
import { Paper } from '@mui/material'
2
import { useEffect, useState } from 'react'
2
import { useEffect, useState, useCallback } from 'react'
3 3
import { DropResult } from 'react-beautiful-dnd'
4 4
import { useDispatch, useSelector } from 'react-redux'
5 5
import { RootState } from '../../redux/store'
6
import { swapMapMarkerIndices } from '../trackingToolSlice'
6
import { moveMarkerToDestination } from '../trackingToolSlice'
7 7
import { PathVariant } from '../Map/pathUtils'
8 8
import DraggableList from './DraggableList'
9 9

  
......
33 33
        setPath(paths[primaryPathIdx])
34 34
    }, [paths, primaryPathIdx])
35 35

  
36
    const onDragEnd = ({ destination, source }: DropResult) => {
36
    const onDragEnd = useCallback(({ destination, source }: DropResult) => {
37 37
        if (!destination || !source || destination.index === source.index) {
38 38
            return
39 39
        }
40 40

  
41 41
        dispatch(
42
            swapMapMarkerIndices({
42
            moveMarkerToDestination({
43 43
                source: source.index,
44 44
                destination: destination.index,
45 45
            })
46 46
        )
47
    }
47
    }, [dispatch])
48 48

  
49 49
    return (
50 50
        <Paper variant="outlined">
frontend/src/features/TrackingTool/Map/MapPath.tsx
3 3
import { RootState } from '../../redux/store'
4 4
import { PathVariant, MapPoint, isMapPointDisplayable } from './pathUtils'
5 5
import TextPath from 'react-leaflet-textpath'
6
import { setPrimaryIdx, updateMapMarker, updateMapMarkerWithId } from '../trackingToolSlice'
6
import {
7
    setPrimaryIdx,
8
    updateMapMarker,
9
    updateMapMarkerWithId,
10
} from '../trackingToolSlice'
7 11
import MapMarker from './MapMarker'
8 12
import { LatLngTuple } from 'leaflet'
9 13
import { Popup, Tooltip } from 'react-leaflet'
......
75 79
            edges.push(
76 80
                <TextPath
77 81
                    // Somehow this refuses to work so let it rerender everything ...
78
                    key={`${activeMapPoints[i].id}-${activeMapPoints[i + 1].id}`}
82
                    key={`${activeMapPoints[i].id}-${
83
                        activeMapPoints[i + 1].id
84
                    }`}
79 85
                    positions={[
80 86
                        [start.latitude, start.longitude],
81 87
                        [end.latitude, end.longitude],
......
153 159
                                                onChange={() => {
154 160
                                                    dispatch(
155 161
                                                        updateMapMarker({
156
                                                            item: {
157
                                                                ...item,
158
                                                                active: !item.active,
159
                                                            },
162
                                                            ...item,
163
                                                            active: !item.active,
160 164
                                                        })
161 165
                                                    )
162 166
                                                }}
frontend/src/features/TrackingTool/trackingToolSlice.ts
81 81
            }
82 82
        },
83 83
        // Updates map marker based on its idx property
84
        updateMapMarker: (state: TrackingToolState, action: { payload: { item: MapPoint } }) => {
85
            const { item } = action.payload
84
        updateMapMarker: (state: TrackingToolState, action: { payload: MapPoint }) => {
85
            const item = action.payload
86 86
            const idx = state.primaryPathIdx
87 87
            if (!state.pathVariants || state.pathVariants.length <= idx) {
88 88
                return state
......
103 103
                })
104 104
            }
105 105
        },
106
        swapMapMarkerIndices: (state: TrackingToolState, action: { payload: { destination: number, source: number } }) => {
106
        moveMarkerToDestination: (state: TrackingToolState, action: { payload: { destination: number, source: number } }) => {
107 107
            const { destination, source } = action.payload
108 108
            if (!state.pathVariants || state.pathVariants.length === 0) {
109 109
                return state
......
124 124
                    const result = [...pathVariant]
125 125
                    const [removed] = result.splice(source, 1)
126 126
                    result.splice(destination, 0, removed)
127
                    return result
127
                    return result.map((item, idx) => ({ ...item, idx }))
128 128
                })
129 129
            }
130 130
        },
......
230 230
    clear,
231 231
    updateMapMarker,
232 232
    mergeWithCurrentPath,
233
    swapMapMarkerIndices,
233
    moveMarkerToDestination,
234 234
    updateMapMarkerWithId
235 235
} = trackingToolSlice.actions
236 236
const trackingToolReducer = trackingToolSlice.reducer

Také k dispozici: Unified diff