-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathsource.ts
More file actions
151 lines (134 loc) · 4.53 KB
/
source.ts
File metadata and controls
151 lines (134 loc) · 4.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import * as React from 'react';
import {useContext, useEffect, useMemo, useState, useRef, cloneElement} from 'react';
import {MapContext} from './map';
import assert from '../utils/assert';
import {deepEqual} from '../utils/deep-equal';
import type {
SourceSpecification,
CanvasSourceSpecification,
ImageSourceSpecification,
VectorSourceSpecification
} from '../types/style-spec';
import type {MapInstance} from '../types/lib';
import type {
GeoJSONSourceImplementation,
ImageSourceImplementation,
AnySourceImplementation
} from '../types/internal';
export type SourceProps = (SourceSpecification | CanvasSourceSpecification) & {
id?: string;
children?: any;
};
let sourceCounter = 0;
function createSource(map: MapInstance, id: string, props: SourceProps) {
// @ts-ignore
if (map.style && map.style._loaded) {
const options = {...props};
delete options.id;
delete options.children;
// @ts-ignore
map.addSource(id, options);
return map.getSource(id);
}
return null;
}
/* eslint-disable complexity */
function updateSource(source: AnySourceImplementation, props: SourceProps, prevProps: SourceProps) {
assert(props.id === prevProps.id, 'source id changed');
assert(props.type === prevProps.type, 'source type changed');
let changedKey = '';
let changedKeyCount = 0;
for (const key in props) {
if (key !== 'children' && key !== 'id' && !deepEqual(prevProps[key], props[key])) {
changedKey = key;
changedKeyCount++;
}
}
if (!changedKeyCount) {
return;
}
const type = props.type;
if (type === 'geojson') {
(source as GeoJSONSourceImplementation).setData(props.data);
} else if (type === 'image') {
(source as ImageSourceImplementation).updateImage({
url: props.url,
coordinates: props.coordinates
});
} else if ('setCoordinates' in source && changedKeyCount === 1 && changedKey === 'coordinates') {
source.setCoordinates((props as unknown as ImageSourceSpecification).coordinates);
} else if ('setUrl' in source && changedKey === 'url') {
source.setUrl((props as VectorSourceSpecification).url);
} else if ('setTiles' in source && changedKey === 'tiles') {
source.setTiles((props as VectorSourceSpecification).tiles);
} else {
// eslint-disable-next-line
console.warn(`Unable to update <Source> prop: ${changedKey}`);
}
}
/* eslint-enable complexity */
export function Source(props: SourceProps) {
const map = useContext(MapContext).map.getMap();
const propsRef = useRef(props);
const [, setStyleLoaded] = useState(0);
const id = useMemo(() => props.id || `jsx-source-${sourceCounter++}`, []);
useEffect(() => {
if (map) {
/* global setTimeout */
const forceUpdate = () => setTimeout(() => setStyleLoaded(version => version + 1), 0);
map.on('styledata', forceUpdate);
forceUpdate();
return () => {
map.off('styledata', forceUpdate);
// @ts-ignore
if (map.style && map.style._loaded && map.getSource(id)) {
// Parent effects are destroyed before child ones, see
// https://github.com/facebook/react/issues/16728
// Source can only be removed after all child layers are removed
const allLayers = map.getStyle()?.layers;
if (allLayers) {
for (const layer of allLayers) {
// @ts-ignore (2339) source does not exist on all layer types
if (layer.source === id) {
map.removeLayer(layer.id);
}
}
}
/**
* There is a bug in mapbox where style.terrain.properties may be undefined.
* @see https://github.com/mapbox/mapbox-gl-js/blob/7a72385de5c7400647ea7d3539637145fdf616a7/src/terrain/terrain.ts#L380
* To prevent this component to crash, we catch it for now.
* This seems to be happening the majority of time with <StrictMode> but not only.
*/
try {
map.removeSource(id);
} catch (error) {
// eslint-disable-next-line
console.error(error);
}
}
};
}
return undefined;
}, [map]);
// @ts-ignore
let source = map && map.style && map.getSource(id);
if (source) {
updateSource(source, props, propsRef.current);
} else {
source = createSource(map, id, props);
}
propsRef.current = props;
return (
(source &&
React.Children.map(
props.children,
child =>
child &&
cloneElement(child, {
source: id
})
)) ||
null
);
}