Revize 7d477849
Přidáno uživatelem Václav Honzík před téměř 3 roky(ů)
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
reidxing + drag jump fix
re #9741