@macrostrat/column-views 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/age-axis.cjs +2 -2
- package/dist/age-axis.cjs.map +1 -1
- package/dist/age-axis.js +1 -1
- package/dist/column-views.css +130 -125
- package/dist/index.cjs +30 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/dist/maps/column-correlation/core.cjs +10 -30
- package/dist/maps/column-correlation/core.cjs.map +1 -1
- package/dist/maps/column-correlation/core.d.ts +1 -1
- package/dist/maps/column-correlation/core.js +10 -30
- package/dist/maps/column-correlation/core.js.map +1 -1
- package/dist/maps/column-navigation/mapbox/index.cjs +156 -118
- package/dist/maps/column-navigation/mapbox/index.cjs.map +1 -1
- package/dist/maps/column-navigation/mapbox/index.d.ts +23 -2
- package/dist/maps/column-navigation/mapbox/index.js +158 -120
- package/dist/maps/column-navigation/mapbox/index.js.map +1 -1
- package/dist/maps/column-navigation/mapbox/keyboard-navigation.cjs +7 -1
- package/dist/maps/column-navigation/mapbox/keyboard-navigation.cjs.map +1 -1
- package/dist/maps/column-navigation/mapbox/keyboard-navigation.d.ts +3 -26
- package/dist/maps/column-navigation/mapbox/keyboard-navigation.js +9 -3
- package/dist/maps/column-navigation/mapbox/keyboard-navigation.js.map +1 -1
- package/dist/maps/index.d.ts +2 -2
- package/dist/maps/{_shared/inset-map.cjs → inset-map/index.cjs} +1 -1
- package/dist/maps/inset-map/index.cjs.map +1 -0
- package/dist/maps/{_shared/inset-map.js → inset-map/index.js} +1 -1
- package/dist/maps/inset-map/index.js.map +1 -0
- package/dist/maps/layers/columns.cjs +161 -0
- package/dist/maps/layers/columns.cjs.map +1 -0
- package/dist/maps/layers/columns.d.ts +28 -0
- package/dist/maps/layers/columns.js +161 -0
- package/dist/maps/layers/columns.js.map +1 -0
- package/dist/maps/layers/index.d.ts +2 -0
- package/dist/maps/{units-overlay/index.cjs → layers/units.cjs} +41 -88
- package/dist/maps/layers/units.cjs.map +1 -0
- package/dist/maps/{units-overlay/index.d.ts → layers/units.d.ts} +1 -1
- package/dist/maps/{units-overlay/index.js → layers/units.js} +34 -82
- package/dist/maps/layers/units.js.map +1 -0
- package/dist/section.cjs +2 -2
- package/dist/section.cjs.map +1 -1
- package/dist/section.js +1 -1
- package/dist/unit-details/age-range.cjs +196 -0
- package/dist/unit-details/age-range.cjs.map +1 -0
- package/dist/unit-details/age-range.d.ts +70 -0
- package/dist/unit-details/age-range.js +194 -0
- package/dist/unit-details/age-range.js.map +1 -0
- package/dist/unit-details/age-range.module.sass.cjs +16 -0
- package/dist/unit-details/age-range.module.sass.cjs.map +1 -0
- package/dist/unit-details/age-range.module.sass.js +14 -0
- package/dist/unit-details/age-range.module.sass.js.map +1 -0
- package/dist/unit-details/index.d.ts +1 -0
- package/dist/unit-details/panel.cjs +8 -173
- package/dist/unit-details/panel.cjs.map +1 -1
- package/dist/unit-details/panel.js +3 -168
- package/dist/unit-details/panel.js.map +1 -1
- package/dist/unit-details/panel.module.sass.cjs +20 -22
- package/dist/unit-details/panel.module.sass.cjs.map +1 -1
- package/dist/unit-details/panel.module.sass.js +20 -22
- package/dist/unit-details/panel.module.sass.js.map +1 -1
- package/dist/unit-details/utils.cjs +31 -0
- package/dist/unit-details/utils.cjs.map +1 -0
- package/dist/unit-details/utils.d.ts +3 -0
- package/dist/unit-details/utils.js +31 -0
- package/dist/unit-details/utils.js.map +1 -0
- package/package.json +4 -4
- package/src/maps/column-correlation/core.ts +9 -29
- package/src/maps/column-navigation/mapbox/index.ts +191 -138
- package/src/maps/column-navigation/mapbox/keyboard-navigation.ts +17 -3
- package/src/maps/index.ts +2 -2
- package/src/maps/layers/columns.ts +210 -0
- package/src/maps/layers/index.ts +2 -0
- package/src/maps/{units-overlay/index.ts → layers/units.ts} +34 -84
- package/src/maps/unit-map.stories.ts +27 -12
- package/src/unit-details/age-range.module.sass +15 -0
- package/src/unit-details/age-range.ts +269 -0
- package/src/unit-details/index.ts +1 -0
- package/src/unit-details/panel.module.sass +0 -9
- package/src/unit-details/panel.ts +12 -226
- package/src/unit-details/utils.ts +33 -0
- package/dist/maps/_shared/index.d.ts +0 -2
- package/dist/maps/_shared/inset-map.cjs.map +0 -1
- package/dist/maps/_shared/inset-map.js.map +0 -1
- package/dist/maps/_shared/styles.cjs +0 -73
- package/dist/maps/_shared/styles.cjs.map +0 -1
- package/dist/maps/_shared/styles.d.ts +0 -2
- package/dist/maps/_shared/styles.js +0 -73
- package/dist/maps/_shared/styles.js.map +0 -1
- package/dist/maps/units-overlay/index.cjs.map +0 -1
- package/dist/maps/units-overlay/index.js.map +0 -1
- package/src/maps/_shared/index.ts +0 -2
- package/src/maps/_shared/styles.ts +0 -76
- /package/dist/maps/{_shared/inset-map.d.ts → inset-map/index.d.ts} +0 -0
- /package/src/maps/{_shared/inset-map.ts → inset-map/index.ts} +0 -0
- /package/src/maps/{_shared → inset-map}/inset-map.stories.ts +0 -0
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
import { useMapStyleOperator } from "@macrostrat/mapbox-react";
|
|
1
|
+
import { useMapRef, useMapStyleOperator } from "@macrostrat/mapbox-react";
|
|
2
2
|
import h from "@macrostrat/hyper";
|
|
3
|
-
import {
|
|
4
|
-
import { ReactNode, useMemo, useRef } from "react";
|
|
5
|
-
import { buildColumnsStyle, InsetMapProps } from "../../_shared";
|
|
6
|
-
import { setGeoJSON } from "@macrostrat/mapbox-utils";
|
|
3
|
+
import { ReactNode, useCallback, useEffect, useMemo, useRef } from "react";
|
|
7
4
|
|
|
8
5
|
import {
|
|
9
6
|
ColumnNavigationProvider,
|
|
10
7
|
NavigationProviderProps,
|
|
11
8
|
useColumnNavigationStore,
|
|
12
9
|
} from "./state";
|
|
13
|
-
import { InsetMap } from "../../
|
|
10
|
+
import { InsetMap, type InsetMapProps } from "../../inset-map";
|
|
14
11
|
import { geoCentroid } from "d3-geo";
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} from "./keyboard-navigation";
|
|
12
|
+
import { BaseColumnsLayer, SelectedColumnOverlay } from "../../layers";
|
|
13
|
+
import { ColumnKeyboardNavigation } from "./keyboard-navigation";
|
|
14
|
+
import type { ColumnFeature } from "../../layers";
|
|
19
15
|
|
|
20
16
|
export interface ColumnNavigationMapProps extends InsetMapProps {
|
|
21
17
|
padding?: number;
|
|
@@ -27,14 +23,18 @@ export interface ColumnNavigationMapProps extends InsetMapProps {
|
|
|
27
23
|
onHoverColumn?: (colID: number | null) => void;
|
|
28
24
|
showTriangulation?: boolean;
|
|
29
25
|
columnColor?: string;
|
|
30
|
-
triangulationColor?: string;
|
|
31
|
-
keyboardNavigation?: boolean;
|
|
32
26
|
}
|
|
33
27
|
|
|
34
28
|
export function ColumnNavigationMap(
|
|
35
29
|
props: ColumnNavigationMapProps & NavigationProviderProps,
|
|
36
30
|
) {
|
|
37
31
|
const {
|
|
32
|
+
padding = 50,
|
|
33
|
+
children,
|
|
34
|
+
columnColor,
|
|
35
|
+
showTriangulation,
|
|
36
|
+
triangulationColor,
|
|
37
|
+
keyboardNavigation = true,
|
|
38
38
|
columns,
|
|
39
39
|
columnIDs,
|
|
40
40
|
onSelectColumn,
|
|
@@ -42,182 +42,235 @@ export function ColumnNavigationMap(
|
|
|
42
42
|
selectedColumn,
|
|
43
43
|
hoveredColumn,
|
|
44
44
|
projectID,
|
|
45
|
-
children,
|
|
46
45
|
inProcess,
|
|
47
46
|
...rest
|
|
48
47
|
} = props;
|
|
49
48
|
|
|
50
|
-
return h(
|
|
51
|
-
ColumnNavigationProvider,
|
|
52
|
-
{
|
|
53
|
-
columns,
|
|
54
|
-
columnIDs,
|
|
55
|
-
onSelectColumn,
|
|
56
|
-
onHoverColumn,
|
|
57
|
-
selectedColumn,
|
|
58
|
-
hoveredColumn,
|
|
59
|
-
projectID,
|
|
60
|
-
inProcess,
|
|
61
|
-
},
|
|
62
|
-
h(_ColumnNavigationMap, props),
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function _ColumnNavigationMap(props: ColumnNavigationMapProps) {
|
|
67
|
-
const {
|
|
68
|
-
padding = 50,
|
|
69
|
-
children,
|
|
70
|
-
columnColor,
|
|
71
|
-
showTriangulation,
|
|
72
|
-
triangulationColor,
|
|
73
|
-
keyboardNavigation = true,
|
|
74
|
-
...rest
|
|
75
|
-
} = props;
|
|
76
|
-
|
|
77
|
-
const overlayStyles = useMemo(() => {
|
|
78
|
-
let styles: any[] = [buildColumnsStyle(columnColor)];
|
|
79
|
-
if (showTriangulation) {
|
|
80
|
-
styles.push(buildKeyboardNavigationStyle(triangulationColor));
|
|
81
|
-
}
|
|
82
|
-
return styles;
|
|
83
|
-
}, [columnColor, showTriangulation, triangulationColor]);
|
|
84
|
-
|
|
85
49
|
return h(
|
|
86
50
|
InsetMap,
|
|
87
51
|
{
|
|
88
52
|
...rest,
|
|
89
53
|
boxZoom: false,
|
|
90
54
|
dragRotate: false,
|
|
91
|
-
overlayStyles,
|
|
92
55
|
},
|
|
93
56
|
[
|
|
94
|
-
h(
|
|
57
|
+
h(ColumnsNavigationLayer, {
|
|
58
|
+
color: columnColor,
|
|
59
|
+
columns,
|
|
60
|
+
columnIDs,
|
|
61
|
+
onSelectColumn,
|
|
62
|
+
onHoverColumn,
|
|
63
|
+
selectedColumn,
|
|
64
|
+
hoveredColumn,
|
|
65
|
+
projectID,
|
|
66
|
+
inProcess,
|
|
67
|
+
keyboardNavigation,
|
|
68
|
+
showTriangulation,
|
|
69
|
+
triangulationColor,
|
|
70
|
+
}),
|
|
95
71
|
children,
|
|
96
|
-
h.if(keyboardNavigation)(ColumnKeyboardNavigation, { showTriangulation }),
|
|
97
72
|
],
|
|
98
73
|
);
|
|
99
74
|
}
|
|
100
75
|
|
|
101
|
-
|
|
76
|
+
interface ColumnLayerProps {
|
|
77
|
+
enabled?: boolean;
|
|
78
|
+
color?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface ColumnKeyboardNavigationProps {
|
|
82
|
+
triangulationColor?: string;
|
|
83
|
+
keyboardNavigation?: boolean;
|
|
84
|
+
showTriangulation?: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function ColumnsNavigationLayer(
|
|
88
|
+
props: NavigationProviderProps &
|
|
89
|
+
ColumnLayerProps &
|
|
90
|
+
ColumnKeyboardNavigationProps,
|
|
91
|
+
) {
|
|
92
|
+
const {
|
|
93
|
+
enabled,
|
|
94
|
+
color,
|
|
95
|
+
keyboardNavigation,
|
|
96
|
+
showTriangulation,
|
|
97
|
+
triangulationColor,
|
|
98
|
+
...providerProps
|
|
99
|
+
} = props;
|
|
100
|
+
|
|
101
|
+
return h(ColumnNavigationProvider, providerProps, [
|
|
102
|
+
h(_ColumnsNavigationLayer, { enabled, color }),
|
|
103
|
+
h.if(keyboardNavigation)(ColumnKeyboardNavigation, {
|
|
104
|
+
showTriangulation,
|
|
105
|
+
triangulationColor,
|
|
106
|
+
}),
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const columnLayers = ["columns-points", "columns-fill"];
|
|
111
|
+
|
|
112
|
+
function _ColumnsNavigationLayer({ enabled = true, color }) {
|
|
102
113
|
const columns = useColumnNavigationStore((state) => state.columns);
|
|
103
|
-
const
|
|
114
|
+
const selectedColumnID = useColumnNavigationStore(
|
|
104
115
|
(state) => state.selectedColumn,
|
|
105
116
|
);
|
|
106
|
-
const hoveredColumn = useColumnNavigationStore(
|
|
107
|
-
(state) => state.hoveredColumn,
|
|
108
|
-
);
|
|
109
117
|
const selectColumn = useColumnNavigationStore((state) => state.selectColumn);
|
|
110
118
|
const setHoveredColumn = useColumnNavigationStore(
|
|
111
119
|
(state) => state.setHoveredColumn,
|
|
112
120
|
);
|
|
113
121
|
|
|
122
|
+
const onHover = useCallback(
|
|
123
|
+
(event) => setHoveredColumn(event.features?.[0]?.id),
|
|
124
|
+
[setHoveredColumn],
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
/** Convert features into column IDs to pass to the selection manager */
|
|
128
|
+
const onSelectColumn = useCallback(
|
|
129
|
+
(column) => selectColumn(column.id),
|
|
130
|
+
[selectColumn],
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const selectedColumnFeature = useMemo(() => {
|
|
134
|
+
if (columns == null || selectedColumnID == null) return null;
|
|
135
|
+
return columns.find((d) => d.id == selectedColumnID);
|
|
136
|
+
}, [selectedColumnID, columns]);
|
|
137
|
+
|
|
138
|
+
return h([
|
|
139
|
+
h(BaseColumnsLayer, { enabled, color, columns }),
|
|
140
|
+
h(ColumnSelectionManager, {
|
|
141
|
+
selectedColumn: selectedColumnFeature,
|
|
142
|
+
onSelectColumn,
|
|
143
|
+
}),
|
|
144
|
+
h(ColumnHoverInteraction, { onHover }),
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function ColumnSelectionManager({
|
|
149
|
+
selectedColumn,
|
|
150
|
+
onSelectColumn,
|
|
151
|
+
centerOnSelect = true,
|
|
152
|
+
}: {
|
|
153
|
+
selectedColumn: ColumnFeature;
|
|
154
|
+
onSelectColumn: (column: ColumnFeature) => void;
|
|
155
|
+
centerOnSelect?: boolean;
|
|
156
|
+
}) {
|
|
157
|
+
const onClick = useCallback(
|
|
158
|
+
(event) => {
|
|
159
|
+
if (event.features.length == 0) return;
|
|
160
|
+
const selectedColumn = event.features[0];
|
|
161
|
+
onSelectColumn(selectedColumn);
|
|
162
|
+
},
|
|
163
|
+
[onSelectColumn],
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return h([
|
|
167
|
+
h.if(centerOnSelect)(SelectedColumnCenterer, {
|
|
168
|
+
selectedColumn,
|
|
169
|
+
}),
|
|
170
|
+
h(SelectedColumnOverlay, { selectedColumn }),
|
|
171
|
+
h(ClickInteraction, {
|
|
172
|
+
onClick,
|
|
173
|
+
layers: columnLayers,
|
|
174
|
+
}),
|
|
175
|
+
]);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function ColumnHoverInteraction({ onHover }) {
|
|
179
|
+
return h(HoverInteraction, { onHover, layers: columnLayers });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function ClickInteraction({ onClick, layers }) {
|
|
114
183
|
useMapStyleOperator(
|
|
115
184
|
(map) => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
type: "FeatureCollection",
|
|
121
|
-
features: columns,
|
|
185
|
+
// Setup hover styles on map
|
|
186
|
+
map.on("click", layers, onClick);
|
|
187
|
+
return () => {
|
|
188
|
+
map.off("click", layers, onClick);
|
|
122
189
|
};
|
|
190
|
+
},
|
|
191
|
+
[layers, onClick],
|
|
192
|
+
);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
123
195
|
|
|
124
|
-
|
|
196
|
+
function HoverInteraction({ onHover, layers }) {
|
|
197
|
+
const hoveredFeaturesRef = useRef([]);
|
|
198
|
+
const updateFeatureState = useCallback((map, features) => {
|
|
199
|
+
// Reset hover state for previously hovered features
|
|
200
|
+
for (const f of hoveredFeaturesRef.current) {
|
|
201
|
+
map.setFeatureState(
|
|
202
|
+
{ source: f.source, id: f.id, sourceLayer: f.sourceLayer },
|
|
203
|
+
{ hover: false },
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
// Set hover state for new features
|
|
207
|
+
for (const f of features) {
|
|
208
|
+
map.setFeatureState(
|
|
209
|
+
{ source: f.source, id: f.id, sourceLayer: f.sourceLayer },
|
|
210
|
+
{ hover: true },
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
hoveredFeaturesRef.current = features;
|
|
214
|
+
}, []);
|
|
125
215
|
|
|
216
|
+
useMapStyleOperator(
|
|
217
|
+
(map) => {
|
|
126
218
|
const mouseMove = (event) => {
|
|
127
219
|
if (event.features.length == 0) return;
|
|
128
|
-
|
|
129
|
-
|
|
220
|
+
onHover?.(event);
|
|
221
|
+
updateFeatureState(map, event.features);
|
|
130
222
|
map.getCanvas().style.cursor = "pointer";
|
|
131
223
|
};
|
|
132
|
-
const mouseLeave = () => {
|
|
133
|
-
|
|
224
|
+
const mouseLeave = (event) => {
|
|
225
|
+
onHover?.(event);
|
|
226
|
+
updateFeatureState(map, event.features ?? []);
|
|
134
227
|
map.getCanvas().style.cursor = "";
|
|
135
228
|
};
|
|
136
229
|
|
|
137
|
-
const clickHandler = (event) => {
|
|
138
|
-
if (event.features.length == 0) return;
|
|
139
|
-
const selectedColumn = event.features[0];
|
|
140
|
-
const id = selectedColumn.id;
|
|
141
|
-
selectColumn(id);
|
|
142
|
-
};
|
|
143
|
-
|
|
144
230
|
// Setup hover styles on map
|
|
145
|
-
const layers = ["columns-points", "columns-fill"];
|
|
146
231
|
map.on("mousemove", layers, mouseMove);
|
|
147
232
|
map.on("mouseleave", layers, mouseLeave);
|
|
148
|
-
map.on("click", layers, clickHandler);
|
|
149
233
|
|
|
150
234
|
return () => {
|
|
151
235
|
map.off("mouseenter", layers, mouseMove);
|
|
152
236
|
map.off("mouseleave", layers, mouseLeave);
|
|
153
|
-
map.off("click", layers, clickHandler);
|
|
154
237
|
};
|
|
155
238
|
},
|
|
156
|
-
[
|
|
239
|
+
[layers, onHover],
|
|
157
240
|
);
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
158
243
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
hoveredColumnRef.current = hoveredColumn;
|
|
172
|
-
// Select the current column
|
|
173
|
-
map.setFeatureState(
|
|
174
|
-
{ source: "columns", id: hoveredColumn },
|
|
175
|
-
{ hover: true },
|
|
176
|
-
);
|
|
177
|
-
},
|
|
178
|
-
[hoveredColumn, columns],
|
|
179
|
-
);
|
|
244
|
+
function SelectedColumnCenterer({
|
|
245
|
+
selectedColumn,
|
|
246
|
+
easeDuration = 500,
|
|
247
|
+
}: {
|
|
248
|
+
selectedColumn: ColumnFeature;
|
|
249
|
+
easeDuration?: number;
|
|
250
|
+
}) {
|
|
251
|
+
const columnCenter = useMemo(() => {
|
|
252
|
+
const columnGeometry = selectedColumn?.geometry;
|
|
253
|
+
if (columnGeometry == null) return null;
|
|
254
|
+
return geoCentroid(columnGeometry);
|
|
255
|
+
}, [selectedColumn]);
|
|
180
256
|
|
|
181
|
-
|
|
182
|
-
const
|
|
257
|
+
const mapRef = useMapRef();
|
|
258
|
+
const map = mapRef.current;
|
|
183
259
|
const initialRenderRef = useRef(true);
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// Select the current column
|
|
200
|
-
map.setFeatureState(
|
|
201
|
-
{ source: "columns", id: selectedColumn },
|
|
202
|
-
{ selected: true },
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
// Center the selected column
|
|
206
|
-
const columnGeometry = columns.find(
|
|
207
|
-
(d) => d.id == selectedColumn,
|
|
208
|
-
)?.geometry;
|
|
209
|
-
if (columnGeometry == null) return;
|
|
210
|
-
const center = geoCentroid(columnGeometry);
|
|
211
|
-
|
|
212
|
-
const isInitialRender = initialRenderRef.current;
|
|
213
|
-
|
|
214
|
-
map.easeTo({ center }, { duration: isInitialRender ? 0 : 500 });
|
|
215
|
-
if (isInitialRender) {
|
|
216
|
-
initialRenderRef.current = false;
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
[selectedColumn, columns],
|
|
220
|
-
);
|
|
260
|
+
// Center the map on the selected column when it changes
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
if (map == null) return;
|
|
263
|
+
const isInitialRender = initialRenderRef.current;
|
|
264
|
+
if (isInitialRender) initialRenderRef.current = false;
|
|
265
|
+
// If this is the first render of the map, jump to the location. Otherwise, ease to it.
|
|
266
|
+
if (columnCenter == null) return;
|
|
267
|
+
if (isInitialRender) {
|
|
268
|
+
map.setCenter(columnCenter);
|
|
269
|
+
initialRenderRef.current = false;
|
|
270
|
+
} else {
|
|
271
|
+
map.easeTo({ center: columnCenter }, { duration: easeDuration });
|
|
272
|
+
}
|
|
273
|
+
}, [mapRef.current, columnCenter]);
|
|
221
274
|
|
|
222
275
|
return null;
|
|
223
276
|
}
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo } from "react";
|
|
2
2
|
import { buildGeoJSONSource, setGeoJSON } from "@macrostrat/mapbox-utils";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
useMapRef,
|
|
5
|
+
useMapStyleOperator,
|
|
6
|
+
useOverlayStyle,
|
|
7
|
+
} from "@macrostrat/mapbox-react";
|
|
4
8
|
import { useColumnNavigationStore } from "./state";
|
|
5
9
|
import {
|
|
6
10
|
buildTriangulation,
|
|
7
11
|
buildKeyMapping,
|
|
8
12
|
} from "../utils/keyboard-navigation";
|
|
13
|
+
import { StyleSpecification } from "mapbox-gl";
|
|
9
14
|
|
|
10
15
|
interface KeyboardNavProps {
|
|
11
16
|
showTriangulation: boolean;
|
|
17
|
+
triangulationColor?: string;
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
export function ColumnKeyboardNavigation(props: KeyboardNavProps) {
|
|
15
21
|
/** Keyboard navigation of columns using a Delaunay triangulation */
|
|
16
|
-
const { showTriangulation = false } = props;
|
|
22
|
+
const { showTriangulation = false, triangulationColor } = props;
|
|
17
23
|
const columns = useColumnNavigationStore((state) => state.columns);
|
|
18
24
|
const col_id = useColumnNavigationStore((state) => state.selectedColumn);
|
|
19
25
|
const selectColumn = useColumnNavigationStore((state) => state.selectColumn);
|
|
@@ -26,6 +32,14 @@ export function ColumnKeyboardNavigation(props: KeyboardNavProps) {
|
|
|
26
32
|
const mapRef = useMapRef();
|
|
27
33
|
const map = mapRef.current;
|
|
28
34
|
|
|
35
|
+
// Set up overlay style for triangulation
|
|
36
|
+
useOverlayStyle(() => {
|
|
37
|
+
if (!showTriangulation) return null;
|
|
38
|
+
return buildKeyboardNavigationStyle(
|
|
39
|
+
triangulationColor,
|
|
40
|
+
) as Partial<StyleSpecification>;
|
|
41
|
+
}, [triangulationColor, showTriangulation]);
|
|
42
|
+
|
|
29
43
|
const keyMapping = useMemo(() => {
|
|
30
44
|
if (columns == null || voronoi == null || map == null) return null;
|
|
31
45
|
|
|
@@ -115,5 +129,5 @@ export function buildKeyboardNavigationStyle(color: string = "purple") {
|
|
|
115
129
|
},
|
|
116
130
|
},
|
|
117
131
|
],
|
|
118
|
-
}
|
|
132
|
+
} as Partial<StyleSpecification>;
|
|
119
133
|
}
|
package/src/maps/index.ts
CHANGED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { useMapStyleOperator, useOverlayStyle } from "@macrostrat/mapbox-react";
|
|
2
|
+
import { setGeoJSON } from "@macrostrat/mapbox-utils";
|
|
3
|
+
import GeoJSON, { FeatureCollection } from "geojson";
|
|
4
|
+
import { GeoJSONFeature } from "mapbox-gl";
|
|
5
|
+
import { getCSSVariable } from "@macrostrat/color-utils";
|
|
6
|
+
import { buildGeoJSONSource } from "@macrostrat/mapbox-utils";
|
|
7
|
+
import { StyleFragment } from "@macrostrat/mapbox-react";
|
|
8
|
+
|
|
9
|
+
export interface ColumnsStyleOptions {
|
|
10
|
+
color?: string | any[];
|
|
11
|
+
selectedColor?: string | any[];
|
|
12
|
+
opacity?: number | any[];
|
|
13
|
+
circleRadius?: number | any[];
|
|
14
|
+
lineOpacity?: number | any[];
|
|
15
|
+
lineWidth?: number | any[];
|
|
16
|
+
lineColor?: string | any[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ColumnLayerOptions extends ColumnsStyleOptions {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
columns: FeatureCollection | GeoJSONFeature[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function BaseColumnsLayer({
|
|
25
|
+
enabled = true,
|
|
26
|
+
columns,
|
|
27
|
+
...rest
|
|
28
|
+
}: ColumnLayerOptions) {
|
|
29
|
+
useOverlayStyle(() => (enabled ? buildColumnsStyle(rest) : null), [enabled]);
|
|
30
|
+
|
|
31
|
+
useMapStyleOperator(
|
|
32
|
+
(map) => {
|
|
33
|
+
if (columns == null) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
setGeoJSON(map, "columns", promoteToFeatureCollection(columns));
|
|
38
|
+
},
|
|
39
|
+
[columns, enabled],
|
|
40
|
+
);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function promoteToFeatureCollection(
|
|
45
|
+
data: FeatureCollection | GeoJSONFeature | GeoJSONFeature[] | null,
|
|
46
|
+
): FeatureCollection | null {
|
|
47
|
+
if (data == null) return null;
|
|
48
|
+
if ("type" in data && data.type == "Feature") {
|
|
49
|
+
return {
|
|
50
|
+
type: "FeatureCollection",
|
|
51
|
+
features: [data],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (
|
|
55
|
+
"type" in data &&
|
|
56
|
+
data.type == "FeatureCollection" &&
|
|
57
|
+
"features" in data
|
|
58
|
+
) {
|
|
59
|
+
return data;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
type: "FeatureCollection",
|
|
63
|
+
features: data,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function buildColumnsStyle(
|
|
68
|
+
opts: ColumnsStyleOptions = {},
|
|
69
|
+
): StyleFragment {
|
|
70
|
+
const { color } = opts;
|
|
71
|
+
const columnBaseColor =
|
|
72
|
+
color ?? getCSSVariable("--text-subtle-color", "black");
|
|
73
|
+
let columnColor: any = columnBaseColor;
|
|
74
|
+
const columnSelectedColor =
|
|
75
|
+
opts.selectedColor ?? getCSSVariable("--selection-color", "purple");
|
|
76
|
+
|
|
77
|
+
const lineColor = opts.lineColor ?? columnBaseColor;
|
|
78
|
+
const isHovered = ["boolean", ["feature-state", "hover"], false];
|
|
79
|
+
// If color is in the feature state or geojson properties, use that as second choice
|
|
80
|
+
if (typeof columnColor === "string") {
|
|
81
|
+
columnColor = [
|
|
82
|
+
"coalesce",
|
|
83
|
+
["feature-state", "color"],
|
|
84
|
+
["get", "color"],
|
|
85
|
+
columnColor,
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const fillOpacity = opts.opacity ?? 0.1;
|
|
90
|
+
|
|
91
|
+
const lineOpacity = opts.lineOpacity ?? 0.5;
|
|
92
|
+
const lineWidth = opts.lineWidth ?? 2;
|
|
93
|
+
|
|
94
|
+
const circleRadius = opts.circleRadius ?? 4;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
sources: {
|
|
98
|
+
columns: buildGeoJSONSource(),
|
|
99
|
+
},
|
|
100
|
+
version: 8,
|
|
101
|
+
layers: [
|
|
102
|
+
{
|
|
103
|
+
id: "columns-fill",
|
|
104
|
+
type: "fill",
|
|
105
|
+
source: "columns",
|
|
106
|
+
paint: {
|
|
107
|
+
"fill-color": columnColor,
|
|
108
|
+
"fill-opacity": fillOpacity,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: "columns-points",
|
|
113
|
+
type: "circle",
|
|
114
|
+
source: "columns",
|
|
115
|
+
paint: {
|
|
116
|
+
"circle-radius": circleRadius,
|
|
117
|
+
"circle-color": columnColor,
|
|
118
|
+
"circle-opacity": fillOpacity,
|
|
119
|
+
},
|
|
120
|
+
filter: ["==", "$type", "Point"],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "columns-line",
|
|
124
|
+
type: "line",
|
|
125
|
+
source: "columns",
|
|
126
|
+
paint: {
|
|
127
|
+
"line-color": ["case", isHovered, columnSelectedColor, lineColor],
|
|
128
|
+
"line-width": lineWidth,
|
|
129
|
+
"line-opacity": ["case", isHovered, 1.0, lineOpacity],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export type ColumnFeature = GeoJSON.Feature & { id: number };
|
|
137
|
+
|
|
138
|
+
const selectedColumnSourceID = "selectedColumn";
|
|
139
|
+
|
|
140
|
+
export function SelectedColumnOverlay({
|
|
141
|
+
selectedColumn,
|
|
142
|
+
...styleOpts
|
|
143
|
+
}: { selectedColumn: ColumnFeature | null } & SelectedColumnStyleOpts) {
|
|
144
|
+
useOverlayStyle(() => buildSelectedColumnStyle(styleOpts), []);
|
|
145
|
+
|
|
146
|
+
useMapStyleOperator(
|
|
147
|
+
(map) => {
|
|
148
|
+
setGeoJSON(
|
|
149
|
+
map,
|
|
150
|
+
selectedColumnSourceID,
|
|
151
|
+
promoteToFeatureCollection(selectedColumn as GeoJSONFeature | null),
|
|
152
|
+
);
|
|
153
|
+
},
|
|
154
|
+
[selectedColumn],
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface SelectedColumnStyleOpts {
|
|
161
|
+
color?: string | any[];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildSelectedColumnStyle(
|
|
165
|
+
opts: SelectedColumnStyleOpts = {},
|
|
166
|
+
): StyleFragment {
|
|
167
|
+
const columnSelectedColor: any =
|
|
168
|
+
opts.color ?? getCSSVariable("--selection-color", "purple");
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
sources: {
|
|
172
|
+
selectedColumn: buildGeoJSONSource(),
|
|
173
|
+
},
|
|
174
|
+
version: 8,
|
|
175
|
+
layers: [
|
|
176
|
+
{
|
|
177
|
+
id: "selected-column-highlight",
|
|
178
|
+
type: "fill",
|
|
179
|
+
source: selectedColumnSourceID,
|
|
180
|
+
paint: {
|
|
181
|
+
"fill-color": columnSelectedColor,
|
|
182
|
+
"fill-opacity": 0.5,
|
|
183
|
+
},
|
|
184
|
+
filter: ["==", "$type", "Polygon"],
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: "selected-column-edge",
|
|
188
|
+
type: "line",
|
|
189
|
+
source: selectedColumnSourceID,
|
|
190
|
+
paint: {
|
|
191
|
+
"line-color": columnSelectedColor,
|
|
192
|
+
"line-width": 2,
|
|
193
|
+
"line-opacity": 1.0,
|
|
194
|
+
},
|
|
195
|
+
filter: ["==", "$type", "Polygon"],
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: "selected-column-point",
|
|
199
|
+
type: "circle",
|
|
200
|
+
source: selectedColumnSourceID,
|
|
201
|
+
paint: {
|
|
202
|
+
"circle-radius": 6,
|
|
203
|
+
"circle-color": columnSelectedColor,
|
|
204
|
+
"circle-opacity": 1.0,
|
|
205
|
+
},
|
|
206
|
+
filter: ["==", "$type", "Point"],
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
};
|
|
210
|
+
}
|