• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geosolutions-it / MapStore2 / 5264504433

pending completion
5264504433

push

github

web-flow
Fix #9176 Make Map Views tool progress bar more evident (#9187) (#9221)

27530 of 41891 branches covered (65.72%)

10 of 10 new or added lines in 2 files covered. (100.0%)

34268 of 43636 relevant lines covered (78.53%)

43.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

40.23
/web/client/components/mapviews/MapViewsSupport.jsx
1
/*
2
 * Copyright 2022, GeoSolutions Sas.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under the BSD-style license found in the
6
 * LICENSE file in the root directory of this source tree.
7
 */
8

9
import React, { useRef, Suspense, lazy, useState, useCallback, useEffect, useReducer } from 'react';
10
import {
11
    ButtonGroup,
12
    Glyphicon,
13
    Checkbox,
14
    ButtonToolbar
15
} from 'react-bootstrap';
16
import uuid from 'uuid';
17
import max from 'lodash/max';
18
import undoable from 'redux-undo';
19
import identity from 'lodash/identity';
20

21
import MapViewsList from './MapViewsList';
22
import MapViewsProgressBar from './MapViewsProgressBar';
23
import FormControl from '../misc/DebouncedFormControl';
24
import { DefaultViewValues } from '../../utils/MapViewsUtils';
25
import Message from '../I18N/Message';
26
import Loader from '../misc/Loader';
27
import ButtonMS from '../misc/Button';
28
import tooltip from '../misc/enhancers/tooltip';
29
const Button = tooltip(ButtonMS);
1✔
30

31
const mapViewSupports = {
1✔
32
    leaflet: lazy(() => import(/* webpackChunkName: 'supports/leafletMapViews' */ '../map/leaflet/MapViewsSupport')),
×
33
    openlayers: lazy(() => import(/* webpackChunkName: 'supports/olMapViews' */ '../map/openlayers/MapViewsSupport')),
×
34
    cesium: lazy(() => import(/* webpackChunkName: 'supports/cesiumMapViews' */ '../map/cesium/MapViewsSupport'))
1✔
35
};
36

37
const MapViewSettings = lazy(() => import('./MapViewSettings'));
1✔
38

39
const UPDATE_VIEWS_STATE = 'UPDATE_VIEWS_STATE';
1✔
40
const UNDO_VIEWS_STATE = 'UNDO_VIEWS_STATE';
1✔
41
const REDO_VIEWS_STATE = 'REDO_VIEWS_STATE';
1✔
42

43
const handlers = {
1✔
44
    [UPDATE_VIEWS_STATE]: (state, newState) => ({
4✔
45
        ...state,
46
        ...newState
47
    })
48
};
49

50
const reducer = (state, action) => {
1✔
51
    return (handlers[action.type] || identity)(state, action.payload);
4!
52
};
53

54
const historyMapViewsStateReducer = undoable(reducer, {
1✔
55
    limit: 20,
56
    undoType: UNDO_VIEWS_STATE,
57
    redoType: REDO_VIEWS_STATE,
58
    jumpType: '',
59
    jumpToPastType: '',
60
    jumpToFutureType: '',
61
    clearHistoryType: ''
62
});
63

64
const computeDurationSum = (views) => views?.reduce((sum, view) => sum + (view?.duration ?? DefaultViewValues.DURATION) * 1000, 0) ?? 0;
16!
65

66
const useMapViewsNavigation = ({
1✔
67
    currentIndex,
68
    views,
69
    onInit,
70
    onChangeView
71
}) => {
72

73
    const [play, setPlay] = useState(false);
11✔
74
    const [navigationProgress, setNavigationProgress] = useState(0);
11✔
75
    const viewsTimeTotalLength = computeDurationSum(views);
11✔
76
    const viewsTimeSegments = views.map((view, idx) => ({ view, duration: computeDurationSum(views.filter((vw, jdx) => jdx < idx)) }));
11✔
77

78
    useEffect(() => {
11✔
79
        if (!play) {
6!
80
            setNavigationProgress(Math.round((viewsTimeSegments?.[currentIndex]?.duration ?? 0) / viewsTimeTotalLength * 100));
6✔
81
        }
82
    }, [currentIndex, play]);
83

84
    useEffect(() => {
11✔
85
        function detectVisibilityChange() {
86
            if (document.visibilityState !== 'visible') {
×
87
                setPlay(false);
×
88
            }
89
        }
90

91
        let animationFrame;
92
        let stop = false;
4✔
93

94
        if (play) {
4!
95
            let startTime = Date.now();
×
96
            let index = currentIndex === -1 ? 0 : currentIndex;
×
97
            let initialDelta = viewsTimeSegments?.[index]?.duration;
×
98
            let mainStartTime = startTime;
×
99
            let currentView = views[index >= views.length ? 0 : index];
×
100
            onInit(currentView);
×
101
            const animate = () => {
×
102
                if (!stop) {
×
103
                    animationFrame = requestAnimationFrame(animate);
×
104
                    const currentTime = Date.now();
×
105
                    const delta = currentTime - startTime;
×
106
                    const duration = (currentView?.duration ?? DefaultViewValues.DURATION) * 1000;
×
107
                    // check single view duration time
108
                    if (delta >= duration) {
×
109
                        startTime = Date.now();
×
110
                        const previousIndex = index >= views.length ? 0 : index;
×
111
                        const nextIndex = (previousIndex + 1) >= views.length ? 0 : previousIndex + 1;
×
112
                        const nextView = views[nextIndex];
×
113
                        onChangeView(nextView);
×
114
                        currentView = nextView;
×
115
                        index = nextIndex;
×
116
                    }
117
                    // check global navigation duration time
118
                    const mainDelta = (currentTime + initialDelta) - mainStartTime;
×
119
                    const percentage = Math.round((mainDelta / viewsTimeTotalLength) * 100);
×
120
                    const currentViewPercentage = Math.round((delta / duration) * 100);
×
121
                    if ((currentViewPercentage % 5) === 0) {
×
122
                        setNavigationProgress(percentage);
×
123
                    }
124
                    if (mainDelta >= viewsTimeTotalLength) {
×
125
                        mainStartTime = Date.now();
×
126
                        initialDelta = 0;
×
127
                    }
128
                }
129
            };
130
            animate(index);
×
131
            document.addEventListener('visibilitychange', detectVisibilityChange);
×
132
        }
133
        return () => {
4✔
134
            stop = true;
4✔
135
            if (animationFrame) {
4!
136
                cancelAnimationFrame(animationFrame);
×
137
            }
138
            if (play) {
4!
139
                document.removeEventListener('visibilitychange', detectVisibilityChange);
×
140
            }
141
        };
142
    }, [play]);
143

144
    return {
11✔
145
        play,
146
        setPlay,
147
        navigationProgress,
148
        viewsTimeSegments,
149
        viewsTimeTotalLength
150
    };
151
};
152

153
function MapViewsSupport({
154
    mapType,
155
    onSelectView = () => { },
11✔
156
    onUpdateViews = () => { },
11✔
157
    onUpdateResources = () => { },
11✔
158
    onUpdateServices = () => { },
11✔
159
    views: viewsProp = [],
2✔
160
    selectedId,
161
    defaultTitle = 'Map View',
11✔
162
    layers,
163
    locale,
164
    resources: resourcesProp = [],
11✔
165
    services,
166
    selectedService,
167
    defaultServices,
168
    defaultSelectedService,
169
    edit,
170
    hide,
171
    ...props
172
}) {
173

174
    const [mapViewsStateHistory, dispatch] = useReducer(historyMapViewsStateReducer, {});
11✔
175
    const setViews = (views) => dispatch({ type: UPDATE_VIEWS_STATE, payload: { views } });
11✔
176
    const setResources = (resources) => dispatch({ type: UPDATE_VIEWS_STATE, payload: { resources } });
11✔
177
    const setMapViewsState = ({ views, resources }) => dispatch({ type: UPDATE_VIEWS_STATE, payload: { views, resources } });
11✔
178

179
    useEffect(() => {
11✔
180
        setMapViewsState({ views: viewsProp, resources: resourcesProp });
4✔
181
    }, []);
182

183
    const [triggerUpdate, setTriggerUpdate] = useState(0);
11✔
184
    const [expandedSections, setExpandedSections] = useState({});
11✔
185
    function handleHistory(historyActionType) {
186
        let nextState;
187
        if (historyActionType === UNDO_VIEWS_STATE) {
×
188
            nextState = mapViewsStateHistory?.past[mapViewsStateHistory?.past.length - 1];
×
189
        }
190
        if (historyActionType === REDO_VIEWS_STATE) {
×
191
            nextState = mapViewsStateHistory?.future[mapViewsStateHistory?.future.length - 1];
×
192
        }
193
        dispatch({ type: historyActionType });
×
194
        onUpdateViews(nextState.views);
×
195
        onUpdateResources(nextState.resources);
×
196
        setTriggerUpdate(triggerUpdate + 1);
×
197
    }
198

199
    const {
200
        views = [],
4✔
201
        resources = []
4✔
202
    } = mapViewsStateHistory?.present || {};
11✔
203

204
    const [expanded, setExpanded] = useState('');
11✔
205
    const [showDescription, setShowDescription] = useState(true);
11✔
206
    const [showViewsGeometries, setShowViewsGeometries] = useState(false);
11✔
207
    const [showClipGeometries, setShowClipGeometries] = useState(false);
11✔
208

209
    const selected = views.find(view => view.id === selectedId);
11✔
210
    const currentIndex = views.indexOf(selected);
11✔
211

212
    const [initApi, setInitApi] = useState(false);
11✔
213
    const api = useRef();
11✔
214
    function apiRef(newApi) {
215
        api.current = newApi;
3✔
216
        if (!initApi) {
3!
217
            setInitApi(true);
3✔
218
        }
219
    }
220

221
    function handleCreateView(copyView) {
222
        const currentMaxCountInTitles = views.length > 0 && max(
×
223
            views
224
                .map(view => {
225
                    const titleRegex = new RegExp(`${defaultTitle} \\(([0-9]+)\\)`);
×
226
                    const match = (view.title || '').match(titleRegex)?.[1];
×
227
                    return match ? parseFloat(match) : undefined;
×
228
                })
229
                .filter(value => value !== undefined)
×
230
        );
231
        const maxCount = currentMaxCountInTitles && !isNaN(currentMaxCountInTitles)
×
232
            ? currentMaxCountInTitles
233
            : 0;
234
        const newView = {
×
235
            duration: DefaultViewValues.DURATION,
236
            flyTo: true,
237
            ...(copyView ? copyView : api.current.getView()),
×
238
            title: `${defaultTitle} (${maxCount + 1})`,
239
            id: uuid()
240
        };
241
        const newViews = currentIndex === -1
×
242
            ? [...views, newView]
243
            : views.reduce((acc, view, idx) => idx === currentIndex ? [...acc, view, newView] : [...acc, view], []);
×
244
        setViews(newViews);
×
245
        onSelectView(newView.id);
×
246
        onUpdateViews(newViews);
×
247
        if (!services && views.length === 0) {
×
248
            onUpdateServices(defaultSelectedService);
×
249
        }
250
    }
251

252
    function handleRemoveView(view) {
253
        const newViews = views.filter((vw) => vw.id !== view.id);
×
254
        setViews(newViews);
×
255
        onUpdateViews(newViews);
×
256
    }
257

258
    function handleSelectView(view, allowFlyTo) {
259
        if (view && api?.current?.setView) {
×
260
            api.current.setView(allowFlyTo ? view : { ...view, flyTo: false });
×
261
        }
262
        onSelectView(view.id);
×
263
    }
264

265
    function handleUpdateView(newView) {
266
        const newViews = views.map((view) => view.id === newView.id ? newView : view);
×
267
        setViews(newViews);
×
268
        onUpdateViews(newViews);
×
269
    }
270
    function handleCaptureView(newView) {
271
        const newViews = views.map((view) => view.id === newView.id ? ({
×
272
            ...newView,
273
            ...api.current.getView()
274
        }) : view);
275
        setViews(newViews);
×
276
        onUpdateViews(newViews);
×
277
    }
278

279
    const prevViews = useRef();
11✔
280
    prevViews.current = views;
11✔
281

282
    const handleMove = useCallback((dragIndex, hoverIndex) => {
11✔
283
        let newViews = [...prevViews.current];
×
284
        newViews.splice(dragIndex, 1);
×
285
        newViews.splice(hoverIndex, 0, prevViews.current[dragIndex]);
×
286
        setViews(newViews);
×
287
    }, []);
288

289
    function handleMoveEnd() {
290
        onUpdateViews(views);
×
291
    }
292

293
    function handleStepMove(delta) {
294
        if (views[currentIndex + delta]) {
×
295
            handleSelectView(views[currentIndex + delta]);
×
296
        }
297
    }
298

299
    function handleChangeOnSelected(properties) {
300
        const newViews = views.map((view) => view.id === selected.id ? ({
×
301
            ...view,
302
            ...properties
303
        }) : view);
304
        setViews(newViews);
×
305
        onUpdateViews(newViews);
×
306
    }
307

308
    function handleUpdateResource(id, data) {
309
        const hasResource = !!resources.find(res => res.id === id);
×
310
        const newResources = hasResource
×
311
            ? resources.map((res) => res.id === id ? { id, data } : res)
×
312
            : [...resources, { id, data }];
313
        setResources(newResources);
×
314
        onUpdateResources(newResources);
×
315
    }
316

317
    const {
318
        play,
319
        setPlay,
320
        navigationProgress,
321
        viewsTimeSegments,
322
        viewsTimeTotalLength
323
    } = useMapViewsNavigation({
11✔
324
        currentIndex,
325
        views,
326
        onInit: (currentView) => {
327
            handleSelectView(currentView);
×
328
            setExpanded('');
×
329
        },
330
        onChangeView: (nextView) => {
331
            handleSelectView(nextView, true);
×
332
        }
333
    });
334

335
    // set the initial view
336
    // only once on mount
337
    const init = useRef(false);
11✔
338
    useEffect(() => {
11✔
339
        if (initApi && !init.current) {
7✔
340
            init.current = true;
3✔
341
            if (selected) {
3✔
342
                // delay the initial set view
343
                // waiting that the zoom from map has been completed
344
                setTimeout(() => {
2✔
345
                    api.current.setView({
2✔
346
                        ...selected,
347
                        // remove fly animation to the initial view
348
                        flyTo: false
349
                    });
350
                }, 500);
351
            }
352
        }
353
    }, [initApi]);
354

355
    const Support = mapViewSupports[mapType];
11✔
356

357
    if (!Support || !edit && views?.length === 0 || hide) {
11✔
358
        return null;
3✔
359
    }
360
    return (
8✔
361
        <>
362
            <Suspense fallback={<div />}>
363
                <Support
364
                    {...props}
365
                    selectedId={selectedId}
366
                    views={views}
367
                    apiRef={apiRef}
368
                    showViewsGeometries={showViewsGeometries}
369
                    resources={resources}
370
                    showClipGeometries={showClipGeometries}
371
                />
372
            </Suspense>
373
            {(edit && views?.length === 0)
21✔
374
                ? (
375
                    <div className="ms-map-views">
376
                        <div className="ms-map-views-wrapper">
377
                            <div className="ms-map-views-header">
378
                                <div className="ms-map-views-title">
379
                                    <Message msgId="mapViews.addInitialView" />
380
                                </div>
381
                                <ButtonToolbar>
382
                                    <ButtonGroup>
383
                                        {(mapViewsStateHistory?.past?.length || 0) > 0 && <Button
9!
384
                                            bsStyle="primary"
385
                                            className="square-button-md"
386
                                            disabled={(mapViewsStateHistory?.past?.length || 0) === 0}
×
387
                                            onClick={() => handleHistory(UNDO_VIEWS_STATE)}
×
388
                                            tooltipId="mapViews.undoChanges"
389
                                            tooltipPosition="bottom"
390
                                        >
391
                                            <Glyphicon glyph="undo" />
392
                                        </Button>}
393
                                        <Button
394
                                            bsStyle="primary"
395
                                            className="square-button-md"
396
                                            onClick={handleCreateView.bind(null, undefined)}
397
                                            tooltipId="mapViews.addNewView"
398
                                            tooltipPosition="bottom"
399
                                        >
400
                                            <Glyphicon glyph="plus" />
401
                                        </Button>
402
                                    </ButtonGroup>
403
                                </ButtonToolbar>
404
                            </div>
405
                        </div>
406
                    </div>
407
                ) : (
408
                    <div className="ms-map-views" onClick={(event) => event.stopPropagation()}>
×
409
                        <div className="ms-map-views-wrapper">
410
                            <MapViewsProgressBar
411
                                play={play}
412
                                currentIndex={currentIndex}
413
                                progress={navigationProgress}
414
                                segments={viewsTimeSegments}
415
                                totalLength={viewsTimeTotalLength}
416
                                onSelect={view => {
417
                                    if (play) {
×
418
                                        setPlay(false);
×
419
                                    }
420
                                    handleSelectView(view);
×
421
                                }}
422
                            />
423
                            <div className="ms-map-views-header">
424
                                {(selected?.description && !expanded) ?
15!
425
                                    <Button
426
                                        className="square-button-md no-border"
427
                                        style={{ borderRadius: '50%', marginRight: 4 }}
428
                                        onClick={() => setShowDescription(!showDescription)}
×
429
                                        tooltipId={showDescription ? 'mapViews.hideDescription' : 'mapViews.showDescription'}
5!
430
                                        tooltipPosition="bottom"
431
                                    >
432
                                        <Glyphicon glyph={showDescription ? "chevron-down" : "chevron-right"} />
5!
433
                                    </Button>
434
                                    : null}
435
                                <div className="ms-map-views-title">
436
                                    {expanded === 'settings'
5!
437
                                        ? (
438
                                            <FormControl
439
                                                key={`${selected?.id}-${triggerUpdate}`}
440
                                                value={selected?.title}
441
                                                onChange={val => handleChangeOnSelected({ title: val })}
×
442
                                            />
443
                                        )
444
                                        : selected?.title}
445
                                </div>
446
                                <ButtonToolbar>
447
                                    {!play && <ButtonGroup>
10✔
448
                                        <Button
449
                                            bsStyle={expanded === 'list' ? 'success' : 'primary'}
5!
450
                                            className="square-button-md"
451
                                            active={expanded === 'list'}
452
                                            onClick={() => setExpanded(expanded !== 'list' ? 'list' : '')}
×
453
                                            tooltipId={expanded === 'list' ? 'mapViews.hideViewsList' : 'mapViews.showViewsList'}
5!
454
                                            tooltipPosition="bottom"
455
                                        >
456
                                            <Glyphicon glyph="list" />
457
                                        </Button>
458
                                    </ButtonGroup>}
459
                                    {(!play && edit) && <ButtonGroup>
12✔
460
                                        <Button
461
                                            bsStyle="primary"
462
                                            className="square-button-md"
463
                                            disabled={expanded === 'settings'}
464
                                            onClick={handleCreateView.bind(null, undefined)}
465
                                            tooltipId={selected ? 'mapViews.addNewViewBelowSelected' : 'mapViews.addNewView'}
2!
466
                                            tooltipPosition="bottom"
467
                                        >
468
                                            <Glyphicon glyph="plus" />
469
                                        </Button>
470
                                        <Button
471
                                            bsStyle="primary"
472
                                            className="square-button-md"
473
                                            disabled={!selected || expanded === 'settings'}
4✔
474
                                            onClick={handleCreateView.bind(null, selected)}
475
                                            tooltipId="mapViews.copyCurrentView"
476
                                            tooltipPosition="bottom"
477
                                        >
478
                                            <Glyphicon glyph="duplicate" />
479
                                        </Button>
480
                                        <Button
481
                                            bsStyle={expanded === 'settings' ? 'success' : 'primary'}
2!
482
                                            className="square-button-md"
483
                                            active={expanded === 'settings'}
484
                                            disabled={!selected}
485
                                            onClick={() => setExpanded(expanded !== 'settings' ? 'settings' : '')}
×
486
                                            tooltipId={expanded === 'settings' ? 'mapViews.stopEdit' : 'mapViews.edit'}
2!
487
                                            tooltipPosition="bottom"
488
                                        >
489
                                            <Glyphicon glyph="pencil" />
490
                                        </Button>
491
                                        <Button
492
                                            bsStyle="primary"
493
                                            className="square-button-md"
494
                                            disabled={(mapViewsStateHistory?.past?.length || 0) === 0}
4✔
495
                                            onClick={() => handleHistory(UNDO_VIEWS_STATE)}
×
496
                                            tooltipId="mapViews.undoChanges"
497
                                            tooltipPosition="bottom"
498
                                        >
499
                                            <Glyphicon glyph="undo" />
500
                                        </Button>
501
                                        <Button
502
                                            bsStyle="primary"
503
                                            className="square-button-md"
504
                                            disabled={(mapViewsStateHistory?.future?.length || 0) === 0}
4✔
505
                                            onClick={() => handleHistory(REDO_VIEWS_STATE)}
×
506
                                            tooltipId="mapViews.redoChanges"
507
                                            tooltipPosition="bottom"
508
                                        >
509
                                            <Glyphicon glyph="redo" />
510
                                        </Button>
511
                                    </ButtonGroup>}
512
                                    <ButtonGroup>
513
                                        <Button
514
                                            bsStyle="primary"
515
                                            className="square-button-md"
516
                                            onClick={() => handleSelectView(views[0])}
×
517
                                            disabled={currentIndex === 0 || play}
5!
518
                                            tooltipId="mapViews.gotToFirstView"
519
                                            tooltipPosition="bottom"
520
                                        >
521
                                            <Glyphicon glyph="fast-backward" />
522
                                        </Button>
523
                                        <Button
524
                                            bsStyle="primary"
525
                                            className="square-button-md"
526
                                            onClick={() => handleStepMove(-1)}
×
527
                                            disabled={!views[currentIndex - 1] || play}
5!
528
                                            tooltipId="mapViews.gotToPreviousView"
529
                                            tooltipPosition="bottom"
530
                                        >
531
                                            <Glyphicon glyph="step-backward" />
532
                                        </Button>
533
                                        <Button
534
                                            bsStyle="primary"
535
                                            className="square-button-md"
536
                                            active={!!play}
537
                                            disabled={views?.length === 1}
538
                                            onClick={() => setPlay(!play)}
×
539
                                            tooltipId={play ? 'mapViews.pause' : 'mapViews.play'}
5!
540
                                            tooltipPosition="bottom"
541
                                        >
542
                                            <Glyphicon glyph={play ? 'pause' : 'play'} />
5!
543
                                        </Button>
544
                                        <Button
545
                                            bsStyle="primary"
546
                                            className="square-button-md"
547
                                            onClick={() => handleStepMove(1)}
×
548
                                            disabled={!views[currentIndex + 1] || play}
5!
549
                                            tooltipId="mapViews.gotToNextView"
550
                                            tooltipPosition="bottom"
551
                                        >
552
                                            <Glyphicon glyph="step-forward" />
553
                                        </Button>
554
                                        <Button
555
                                            bsStyle="primary"
556
                                            className="square-button-md"
557
                                            onClick={() => handleSelectView(views[views?.length - 1])}
×
558
                                            disabled={currentIndex === views?.length - 1 || play}
5!
559
                                            tooltipId="mapViews.gotToLastView"
560
                                            tooltipPosition="bottom"
561
                                        >
562
                                            <Glyphicon glyph="fast-forward" />
563
                                        </Button>
564
                                    </ButtonGroup>
565
                                </ButtonToolbar>
566
                            </div>
567
                            <div className="ms-map-views-body">
568
                                {expanded === 'list' &&
5!
569
                                    <MapViewsList
570
                                        views={views}
571
                                        edit={edit}
572
                                        selectedId={selected?.id}
573
                                        onSelect={handleSelectView}
574
                                        onMove={handleMove}
575
                                        onMoveEnd={handleMoveEnd}
576
                                        onRemove={handleRemoveView}
577
                                        options={
578
                                            <>
579
                                                {api.current?.options?.showClipGeometriesEnabled && <Checkbox checked={showViewsGeometries} onChange={() => setShowViewsGeometries(!showViewsGeometries)}>
×
580
                                                    <Message msgId="mapViews.showViewsGeometries" />
581
                                                </Checkbox>}
582
                                            </>
583
                                        }
584
                                    />
585
                                }
586
                                {(expanded === 'settings' && selected) &&
5!
587
                                    <Suspense fallback={<div style={{ display: 'flex', justifyContent: 'center', padding: 8, width: '100%' }}><Loader size={30}/></div>}>
588
                                        <MapViewSettings
589
                                            key={`${selected.id}-${triggerUpdate}`}
590
                                            expandedSections={expandedSections}
591
                                            onExpandSection={(value) =>
592
                                                setExpandedSections((prevSections) => ({ ...prevSections, ...value }))
×
593
                                            }
594
                                            view={selected}
595
                                            api={api.current}
596
                                            onChange={handleUpdateView}
597
                                            onCaptureView={handleCaptureView}
598
                                            layers={layers}
599
                                            locale={locale}
600
                                            services={services}
601
                                            selectedService={selectedService}
602
                                            resources={resources}
603
                                            onUpdateResource={handleUpdateResource}
604
                                            showClipGeometries={showClipGeometries}
605
                                            onShowClipGeometries={setShowClipGeometries}
606
                                        />
607
                                    </Suspense>
608
                                }
609
                            </div>
610
                        </div>
611
                        {(!expanded && showDescription && selected?.description) && <div
20✔
612
                            className="ms-map-views-description">
613
                            <div dangerouslySetInnerHTML={{ __html: selected.description }} />
614
                        </div>
615
                        }
616
                    </div>
617
                )}
618

619
        </>
620
    );
621
}
622

623
export default MapViewsSupport;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc