Projekt

Obecné

Profil

Stáhnout (7.41 KB) Statistiky
| Větev: | Tag: | Revize:
1
import { Fragment, FunctionComponent, useEffect, useState } from 'react'
2
import { useDispatch, useSelector } from 'react-redux'
3
import { RootState } from '../../redux/store'
4
import {
5
    MapPoint,
6
    isMapPointDisplayable,
7
    MapPointType,
8
} from '../trackingToolUtils'
9
import TextPath from 'react-leaflet-textpath'
10
import { updateMapPoint } from '../trackingToolSlice'
11
import MapMarker from './MapMarker'
12
import { LatLngTuple } from 'leaflet'
13
import { Popup, Tooltip } from 'react-leaflet'
14
import { Checkbox, FormControlLabel, Stack, Typography } from '@mui/material'
15
import { formatHtmlStringToReactDom } from '../../../utils/Formatting/HtmlUtils'
16
import { CatalogItemDetailDialog as CatalogItemDetailDialog } from '../../Catalog/CatalogItemDetail'
17

    
18
type EdgeElement = any
19

    
20
const ProcessedTextMapMarker: FunctionComponent<{ item: MapPoint }> = ({
21
    item,
22
}) => {
23
    const dispatch = useDispatch()
24
    const [draggingEnabled, setDraggingEnabled] = useState(false)
25

    
26
    return (
27
        <MapMarker
28
            position={[
29
                item.catalogItem.latitude as number,
30
                item.catalogItem.longitude as number,
31
            ]}
32
            mapPoint={item}
33
            draggable={draggingEnabled}
34
            updatePositionCallbackFn={(position: LatLngTuple) => {
35
                dispatch(
36
                    updateMapPoint({
37
                        ...item,
38
                        catalogItem: {
39
                            ...item.catalogItem,
40
                            latitude: position[0],
41
                            longitude: position[1],
42
                        },
43
                    })
44
                )
45
            }}
46
        >
47
            <Fragment>
48
                <Tooltip>{item.catalogItem.name ?? ''}</Tooltip>
49
                <Popup>
50
                    <Fragment>
51
                        <Stack direction="column" sx={{ m: 0 }}>
52
                            <Typography
53
                                variant="h6"
54
                                fontWeight="bold"
55
                                fontSize={16}
56
                            >
57
                                {formatHtmlStringToReactDom(
58
                                    item.catalogItem.name as string
59
                                )}
60
                            </Typography>
61
                            <FormControlLabel
62
                                control={
63
                                    <Checkbox
64
                                        checked={item.addToPath}
65
                                        onChange={() => {
66
                                            dispatch(
67
                                                updateMapPoint({
68
                                                    ...item,
69
                                                    addToPath: !item.addToPath,
70
                                                })
71
                                            )
72
                                        }}
73
                                    />
74
                                }
75
                                labelPlacement="end"
76
                                label="Active"
77
                            />
78
                            <FormControlLabel
79
                                control={
80
                                    <Checkbox
81
                                        checked={draggingEnabled}
82
                                        onChange={() => {
83
                                            setDraggingEnabled((prev) => !prev)
84
                                        }}
85
                                    />
86
                                }
87
                                labelPlacement="end"
88
                                label="Move"
89
                            />
90
                            <CatalogItemDetailDialog
91
                                itemId={item.catalogItem.id ?? ''}
92
                            />
93
                        </Stack>
94
                    </Fragment>
95
                </Popup>
96
            </Fragment>
97
        </MapMarker>
98
    )
99
}
100

    
101
export const buildEdges = (
102
    displayableMapPoints: MapPoint[],
103
    setEdges: (edges: EdgeElement[]) => void,
104
    pathColor: string,
105
    pathThickness = 17
106
) => {
107
    // Get all active map points
108
    const activeMapPoints = displayableMapPoints.filter(
109
        (item) => item.addToPath && !item.hidden
110
    )
111
    if (activeMapPoints.length < 2) {
112
        setEdges([])
113
        return
114
    }
115

    
116
    // Keep track of already built edges so we do not render excessive amount of them
117
    // We can use a simple hack where we concatenate id of both and check if it is already in the set
118
    const existingPaths = new Set<string>()
119
    const edges = []
120
    setEdges([])
121
    for (let i = 0; i < activeMapPoints.length - 1; i += 1) {
122
        const edgeId = `${activeMapPoints[i].id}${activeMapPoints[i + 1].id}`
123
        if (existingPaths.has(edgeId)) {
124
            continue
125
        }
126
        existingPaths.add(edgeId)
127
        const [start, end] = [
128
            activeMapPoints[i].catalogItem,
129
            activeMapPoints[i + 1].catalogItem,
130
        ]
131
        edges.push(
132
            <TextPath
133
                key={edgeId}
134
                positions={[
135
                    [start.latitude, start.longitude],
136
                    [end.latitude, end.longitude],
137
                ]}
138
                text="►"
139
                attributes={{
140
                    'font-size': pathThickness,
141
                    fill: pathColor,
142
                }}
143
                repeat
144
                center
145
                weight={0}
146
            />
147
        )
148
    }
149
    setEdges(edges)
150
}
151

    
152
const ProcessedTextPath = () => {
153
    const dispatch = useDispatch()
154

    
155
    // Displayed path contains all map points
156
    const path = useSelector(
157
        (state: RootState) => state.trackingTool.displayedPath
158
    )
159

    
160
    // Feature enabled
161
    const featureEnabled = useSelector(
162
        (state: RootState) =>
163
            state.trackingTool.toggleables[MapPointType.ProcessedText]
164
    )
165

    
166
    // Color of the path
167
    const pathColor = '#346eeb'
168

    
169
    // List of all active map points
170
    const [displayableMapPoints, setDisplayableMapPoints] = useState<
171
        MapPoint[]
172
    >([])
173
    useEffect(() => {
174
        if (!path || !featureEnabled) {
175
            setDisplayableMapPoints([])
176
            return
177
        }
178

    
179
        // Set all displayable vertices
180
        setDisplayableMapPoints(
181
            path.filter((mapPoint) => isMapPointDisplayable(mapPoint))
182
        )
183
    }, [path, featureEnabled])
184

    
185
    // List of all edges in the path
186
    const [edges, setEdges] = useState<EdgeElement[]>([])
187
    useEffect(() => {
188
        buildEdges(displayableMapPoints, setEdges, pathColor)
189
    }, [dispatch, displayableMapPoints])
190

    
191
    // List of vertices to display
192
    const [vertices, setVertices] = useState<JSX.Element[]>([])
193
    useEffect(() => {
194
        // Iterate over all displayable map points and map them to MapMarker
195
        const uniqueMapPoints = new Set<string>()
196
        setVertices(
197
            displayableMapPoints
198
                .filter((mapPoint) => {
199
                    if (uniqueMapPoints.has(mapPoint.id)) {
200
                        return false
201
                    }
202
                    uniqueMapPoints.add(mapPoint.id)
203
                    return true
204
                })
205
                .map((item) => (
206
                    <ProcessedTextMapMarker key={item.id} item={item} />
207
                ))
208
        )
209
        
210
    }, [dispatch, displayableMapPoints])
211

    
212
    return (
213
        <Fragment>
214
            {vertices}
215
            {edges}
216
        </Fragment>
217
    )
218
}
219

    
220
export default ProcessedTextPath
(6-6/6)