@macrostrat/column-views 1.0.3 → 2.0.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.
Files changed (242) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/esm/_shared.f7eec55a.js +9 -0
  3. package/dist/esm/_shared.f7eec55a.js.map +1 -0
  4. package/dist/esm/age-axis.4a8cf5e6.js +98 -0
  5. package/dist/esm/age-axis.4a8cf5e6.js.map +1 -0
  6. package/dist/esm/age-axis.module.03773a80.css +33 -0
  7. package/dist/esm/age-axis.module.03773a80.css.map +1 -0
  8. package/dist/esm/age-axis.module.3cc91335.js +22 -0
  9. package/dist/esm/age-axis.module.3cc91335.js.map +1 -0
  10. package/dist/esm/age-model-overlay.c7226855.js +57 -0
  11. package/dist/esm/age-model-overlay.c7226855.js.map +1 -0
  12. package/dist/esm/age-model-overlay.module.5d9e78fb.css +14 -0
  13. package/dist/esm/age-model-overlay.module.5d9e78fb.css.map +1 -0
  14. package/dist/esm/age-model-overlay.module.cd985dcb.js +16 -0
  15. package/dist/esm/age-model-overlay.module.cd985dcb.js.map +1 -0
  16. package/dist/esm/base.910b80b4.js +288 -0
  17. package/dist/esm/base.910b80b4.js.map +1 -0
  18. package/dist/esm/boxes.8292cff2.js +258 -0
  19. package/dist/esm/boxes.8292cff2.js.map +1 -0
  20. package/dist/esm/boxes.module.2b5fb18e.css +26 -0
  21. package/dist/esm/boxes.module.2b5fb18e.css.map +1 -0
  22. package/dist/esm/boxes.module.4ebc7796.js +19 -0
  23. package/dist/esm/boxes.module.4ebc7796.js.map +1 -0
  24. package/dist/esm/{carbon-isotopes.3b502a9e.js → carbon-isotopes.10db8285.js} +2 -2
  25. package/dist/esm/{carbon-isotopes.3b502a9e.js.map → carbon-isotopes.10db8285.js.map} +1 -1
  26. package/dist/esm/colors.395c6db5.js +40 -0
  27. package/dist/esm/colors.395c6db5.js.map +1 -0
  28. package/dist/esm/column-correlation.c8dcfe98.js +9 -0
  29. package/dist/esm/column-correlation.c8dcfe98.js.map +1 -0
  30. package/dist/esm/column-nav.5414677c.js +55 -0
  31. package/dist/esm/column-nav.5414677c.js.map +1 -0
  32. package/dist/esm/column-navigation.37cd3245.js +9 -0
  33. package/dist/esm/column-navigation.37cd3245.js.map +1 -0
  34. package/dist/esm/column.01d68dd8.js +127 -0
  35. package/dist/esm/column.01d68dd8.js.map +1 -0
  36. package/dist/esm/{column.db058031.js → column.a25f8ebd.js} +4 -4
  37. package/dist/esm/column.a25f8ebd.js.map +1 -0
  38. package/dist/esm/column.module.1b776e15.js +46 -0
  39. package/dist/esm/column.module.1b776e15.js.map +1 -0
  40. package/dist/esm/column.module.a67a6b4c.css +159 -0
  41. package/dist/esm/column.module.a67a6b4c.css.map +1 -0
  42. package/dist/esm/composite-scale.577bf64a.js +222 -0
  43. package/dist/esm/composite-scale.577bf64a.js.map +1 -0
  44. package/dist/esm/composite.039e4798.js +163 -0
  45. package/dist/esm/composite.039e4798.js.map +1 -0
  46. package/dist/esm/{core.4db8e452.js → core.843310d3.js} +11 -12
  47. package/dist/esm/core.843310d3.js.map +1 -0
  48. package/dist/esm/core.91ede078.js +206 -0
  49. package/dist/esm/core.91ede078.js.map +1 -0
  50. package/dist/esm/correlation-chart.abd8b346.js +9 -0
  51. package/dist/esm/correlation-chart.abd8b346.js.map +1 -0
  52. package/dist/esm/data-provider.1506eb64.js.map +1 -1
  53. package/dist/esm/data-provider.f10c6d0b.js +13 -0
  54. package/dist/esm/data-provider.f10c6d0b.js.map +1 -0
  55. package/dist/esm/fetch.99aec438.js +152 -0
  56. package/dist/esm/fetch.99aec438.js.map +1 -0
  57. package/dist/esm/hash-string.285b37fb.js +39 -0
  58. package/dist/esm/hash-string.285b37fb.js.map +1 -0
  59. package/dist/esm/helpers.2f0a0cb7.js +141 -0
  60. package/dist/esm/helpers.2f0a0cb7.js.map +1 -0
  61. package/dist/esm/index.d.ts +797 -130
  62. package/dist/esm/index.d.ts.map +1 -1
  63. package/dist/esm/index.js +13 -9
  64. package/dist/esm/index.js.map +1 -1
  65. package/dist/esm/inset-map.3c18106d.js +66 -0
  66. package/dist/esm/inset-map.3c18106d.js.map +1 -0
  67. package/dist/esm/keyboard-navigation.6713401b.js +48 -0
  68. package/dist/esm/keyboard-navigation.6713401b.js.map +1 -0
  69. package/dist/esm/keyboard-navigation.a9fc80dd.js +112 -0
  70. package/dist/esm/keyboard-navigation.a9fc80dd.js.map +1 -0
  71. package/dist/esm/layers.033d3fb0.js +170 -0
  72. package/dist/esm/layers.033d3fb0.js.map +1 -0
  73. package/dist/esm/main.2e0375a5.js +221 -0
  74. package/dist/esm/main.2e0375a5.js.map +1 -0
  75. package/dist/esm/main.module.3dd9500b.css +134 -0
  76. package/dist/esm/main.module.3dd9500b.css.map +1 -0
  77. package/dist/esm/main.module.c09154b1.js +70 -0
  78. package/dist/esm/main.module.c09154b1.js.map +1 -0
  79. package/dist/esm/mapbox.cac42eed.js +167 -0
  80. package/dist/esm/mapbox.cac42eed.js.map +1 -0
  81. package/dist/esm/maps.bcdea9c1.js +11 -0
  82. package/dist/esm/maps.bcdea9c1.js.map +1 -0
  83. package/dist/esm/{names.f6a4e9a4.js → names.a8f2b452.js} +5 -5
  84. package/dist/esm/names.a8f2b452.js.map +1 -0
  85. package/dist/esm/panel.598f76a5.js +420 -0
  86. package/dist/esm/panel.598f76a5.js.map +1 -0
  87. package/dist/esm/panel.module.386166cc.js +58 -0
  88. package/dist/esm/panel.module.386166cc.js.map +1 -0
  89. package/dist/esm/panel.module.3d2ea3c6.css +111 -0
  90. package/dist/esm/panel.module.3d2ea3c6.css.map +1 -0
  91. package/dist/esm/popover.a810b7bc.js +66 -0
  92. package/dist/esm/popover.a810b7bc.js.map +1 -0
  93. package/dist/esm/popover.module.c4ba4189.js +22 -0
  94. package/dist/esm/popover.module.c4ba4189.js.map +1 -0
  95. package/dist/esm/{selection-popover.module.0a701258.css → popover.module.c4d6bfb0.css} +11 -8
  96. package/dist/esm/popover.module.c4d6bfb0.css.map +1 -0
  97. package/dist/esm/prepare-data.d4284b4a.js +125 -0
  98. package/dist/esm/prepare-data.d4284b4a.js.map +1 -0
  99. package/dist/esm/prepare-units.6c890a96.js +104 -0
  100. package/dist/esm/prepare-units.6c890a96.js.map +1 -0
  101. package/dist/esm/{resolvers.b5c82b78.js → resolvers.36013b49.js} +13 -2
  102. package/dist/esm/resolvers.36013b49.js.map +1 -0
  103. package/dist/esm/section.a34d379a.js +215 -0
  104. package/dist/esm/section.a34d379a.js.map +1 -0
  105. package/dist/esm/selection.f5b0297e.js +143 -0
  106. package/dist/esm/selection.f5b0297e.js.map +1 -0
  107. package/dist/esm/state.178ef364.js +75 -0
  108. package/dist/esm/state.178ef364.js.map +1 -0
  109. package/dist/esm/state.2952cbca.js +118 -0
  110. package/dist/esm/state.2952cbca.js.map +1 -0
  111. package/dist/esm/store.c4d7a8a4.js +52 -0
  112. package/dist/esm/store.c4d7a8a4.js.map +1 -0
  113. package/dist/esm/styles.7bc776d1.js +95 -0
  114. package/dist/esm/styles.7bc776d1.js.map +1 -0
  115. package/dist/esm/svg.4b9a96c1.js +89 -0
  116. package/dist/esm/svg.4b9a96c1.js.map +1 -0
  117. package/dist/esm/types.0f0b57c9.js +3 -0
  118. package/dist/esm/types.0f0b57c9.js.map +1 -0
  119. package/dist/esm/unit-details.071cefed.js +9 -0
  120. package/dist/esm/unit-details.071cefed.js.map +1 -0
  121. package/dist/esm/units.5a4fc058.js +98 -0
  122. package/dist/esm/units.5a4fc058.js.map +1 -0
  123. package/dist/esm/utils.d7a43a2a.js +55 -0
  124. package/dist/esm/utils.d7a43a2a.js.map +1 -0
  125. package/dist/node/index.js.map +1 -1
  126. package/package.json +24 -7
  127. package/src/age-axis.module.sass +27 -0
  128. package/src/age-axis.ts +82 -19
  129. package/src/age-model-overlay.module.sass +11 -0
  130. package/src/age-model-overlay.ts +73 -0
  131. package/src/carbon-isotopes/column.ts +4 -2
  132. package/src/carbon-isotopes/core.ts +9 -6
  133. package/src/carbon-isotopes/data-provider.ts +3 -3
  134. package/src/column.module.sass +58 -49
  135. package/src/column.ts +149 -135
  136. package/src/correlation-chart/hash-string.ts +57 -0
  137. package/src/correlation-chart/index.ts +2 -0
  138. package/src/correlation-chart/main.module.sass +122 -0
  139. package/src/correlation-chart/main.ts +305 -0
  140. package/src/correlation-chart/prepare-data.ts +217 -0
  141. package/src/correlation-chart/stories/correlation-chart.stories.ts +192 -0
  142. package/src/correlation-chart/stories/stories.module.sass +18 -0
  143. package/src/correlation-chart/unconformity-line.module.sass +8 -0
  144. package/src/correlation-chart/unconformity-line.ts +25 -0
  145. package/src/data-provider/base.ts +388 -0
  146. package/src/data-provider/column-nav.ts +53 -0
  147. package/src/data-provider/fetch.ts +231 -0
  148. package/src/data-provider/index.ts +4 -0
  149. package/src/data-provider/store.ts +72 -0
  150. package/src/index.ts +5 -3
  151. package/src/maps/_shared/index.ts +2 -0
  152. package/src/maps/_shared/inset-map.stories.ts +20 -0
  153. package/src/maps/_shared/inset-map.ts +87 -0
  154. package/src/maps/_shared/styles.ts +57 -0
  155. package/src/maps/column-correlation/column-correlation-map.stories.ts +96 -0
  156. package/src/maps/column-correlation/core.ts +225 -0
  157. package/src/maps/column-correlation/index.ts +2 -0
  158. package/src/maps/column-correlation/state.ts +192 -0
  159. package/src/maps/column-navigation/index.ts +2 -0
  160. package/src/maps/column-navigation/mapbox/column-navigation-map.stories.ts +102 -0
  161. package/src/maps/column-navigation/mapbox/index.ts +219 -0
  162. package/src/maps/column-navigation/mapbox/keyboard-navigation.ts +119 -0
  163. package/src/maps/column-navigation/mapbox/state.ts +109 -0
  164. package/src/maps/column-navigation/svg/column-navigation-svg-map.stories.ts +46 -0
  165. package/src/{map → maps/column-navigation/svg}/index.ts +18 -6
  166. package/src/{map → maps/column-navigation/svg}/layers.ts +28 -109
  167. package/src/maps/column-navigation/utils/keyboard-navigation.ts +58 -0
  168. package/src/maps/index.ts +3 -0
  169. package/src/prepare-units/composite-scale.ts +353 -0
  170. package/src/prepare-units/helpers.ts +226 -0
  171. package/src/prepare-units/index.ts +177 -0
  172. package/src/prepare-units/utils.ts +98 -0
  173. package/src/section.ts +264 -84
  174. package/src/unit-details/index.ts +2 -215
  175. package/src/unit-details/{index.module.sass → panel.module.sass} +39 -8
  176. package/src/unit-details/panel.ts +523 -0
  177. package/src/{selection-popover.module.sass → unit-details/popover.module.sass} +4 -2
  178. package/src/unit-details/popover.ts +82 -0
  179. package/src/units/boxes.module.sass +16 -0
  180. package/src/units/boxes.ts +243 -90
  181. package/src/units/colors.ts +64 -0
  182. package/src/units/composite.ts +157 -152
  183. package/src/units/index.ts +76 -9
  184. package/src/units/names.ts +8 -4
  185. package/src/units/resolvers.ts +11 -0
  186. package/src/units/selection.ts +115 -101
  187. package/src/units/types.ts +5 -16
  188. package/dist/esm/age-axis.64c509ee.js +0 -41
  189. package/dist/esm/age-axis.64c509ee.js.map +0 -1
  190. package/dist/esm/boxes.13968b76.js +0 -164
  191. package/dist/esm/boxes.13968b76.js.map +0 -1
  192. package/dist/esm/boxes.module.28873d08.css +0 -4
  193. package/dist/esm/boxes.module.28873d08.css.map +0 -1
  194. package/dist/esm/boxes.module.605bdcd6.js +0 -13
  195. package/dist/esm/boxes.module.605bdcd6.js.map +0 -1
  196. package/dist/esm/column.8dd26e1a.js +0 -149
  197. package/dist/esm/column.8dd26e1a.js.map +0 -1
  198. package/dist/esm/column.db058031.js.map +0 -1
  199. package/dist/esm/column.module.039d872c.css +0 -151
  200. package/dist/esm/column.module.039d872c.css.map +0 -1
  201. package/dist/esm/column.module.e632868a.js +0 -19
  202. package/dist/esm/column.module.e632868a.js.map +0 -1
  203. package/dist/esm/composite.608a4ca2.js +0 -153
  204. package/dist/esm/composite.608a4ca2.js.map +0 -1
  205. package/dist/esm/core.4db8e452.js.map +0 -1
  206. package/dist/esm/helpers.8a03cfc7.js +0 -94
  207. package/dist/esm/helpers.8a03cfc7.js.map +0 -1
  208. package/dist/esm/index.module.0844afd8.css +0 -65
  209. package/dist/esm/index.module.0844afd8.css.map +0 -1
  210. package/dist/esm/index.module.24fe3a2e.js +0 -37
  211. package/dist/esm/index.module.24fe3a2e.js.map +0 -1
  212. package/dist/esm/layers.ad5fd25d.js +0 -231
  213. package/dist/esm/layers.ad5fd25d.js.map +0 -1
  214. package/dist/esm/map.7aa26f17.js +0 -82
  215. package/dist/esm/map.7aa26f17.js.map +0 -1
  216. package/dist/esm/names.f6a4e9a4.js.map +0 -1
  217. package/dist/esm/providers.cd1e652f.js +0 -25
  218. package/dist/esm/providers.cd1e652f.js.map +0 -1
  219. package/dist/esm/resolvers.b5c82b78.js.map +0 -1
  220. package/dist/esm/section.cc1b1100.js +0 -118
  221. package/dist/esm/section.cc1b1100.js.map +0 -1
  222. package/dist/esm/selection-popover.17d1bd55.js +0 -60
  223. package/dist/esm/selection-popover.17d1bd55.js.map +0 -1
  224. package/dist/esm/selection-popover.module.0a701258.css.map +0 -1
  225. package/dist/esm/selection-popover.module.e22f5183.js +0 -22
  226. package/dist/esm/selection-popover.module.e22f5183.js.map +0 -1
  227. package/dist/esm/selection.bbf26d0e.js +0 -117
  228. package/dist/esm/selection.bbf26d0e.js.map +0 -1
  229. package/dist/esm/store.25eaa168.js +0 -18
  230. package/dist/esm/store.25eaa168.js.map +0 -1
  231. package/dist/esm/types.f016e032.js +0 -18
  232. package/dist/esm/types.f016e032.js.map +0 -1
  233. package/dist/esm/unit-details.56ff8b70.js +0 -209
  234. package/dist/esm/unit-details.56ff8b70.js.map +0 -1
  235. package/dist/esm/units.361088d4.js +0 -42
  236. package/dist/esm/units.361088d4.js.map +0 -1
  237. package/src/helpers.ts +0 -133
  238. package/src/providers.ts +0 -34
  239. package/src/selection-popover.ts +0 -71
  240. package/src/store.ts +0 -12
  241. package/src/units/units.scss +0 -16
  242. /package/src/{map → maps/column-navigation/svg}/animated.ts +0 -0
@@ -0,0 +1,219 @@
1
+ import { useMapStyleOperator } from "@macrostrat/mapbox-react";
2
+ import h from "@macrostrat/hyper";
3
+ import { FeatureCollection } from "geojson";
4
+ import { ReactNode, useMemo, useRef } from "react";
5
+ import { buildColumnsStyle, InsetMapProps } from "../../_shared";
6
+ import { setGeoJSON } from "@macrostrat/mapbox-utils";
7
+
8
+ import {
9
+ ColumnNavigationProvider,
10
+ NavigationProviderProps,
11
+ useColumnNavigationStore,
12
+ } from "./state";
13
+ import { InsetMap } from "../../_shared";
14
+ import { geoCentroid } from "d3-geo";
15
+ import {
16
+ ColumnKeyboardNavigation,
17
+ buildKeyboardNavigationStyle,
18
+ } from "./keyboard-navigation";
19
+
20
+ export interface ColumnNavigationMapProps extends InsetMapProps {
21
+ padding?: number;
22
+ children?: ReactNode;
23
+ accessToken?: string;
24
+ selectedColumn?: number;
25
+ hoveredColumn?: number;
26
+ onSelectColumn?: (colID: number | null) => void;
27
+ onHoverColumn?: (colID: number | null) => void;
28
+ showTriangulation?: boolean;
29
+ columnColor?: string;
30
+ triangulationColor?: string;
31
+ keyboardNavigation?: boolean;
32
+ }
33
+
34
+ export function ColumnNavigationMap(
35
+ props: ColumnNavigationMapProps & NavigationProviderProps
36
+ ) {
37
+ const {
38
+ columns,
39
+ onSelectColumn,
40
+ onHoverColumn,
41
+ selectedColumn,
42
+ hoveredColumn,
43
+ projectID,
44
+ inProcess,
45
+ ...rest
46
+ } = props;
47
+
48
+ return h(
49
+ ColumnNavigationProvider,
50
+ {
51
+ columns,
52
+ onSelectColumn,
53
+ onHoverColumn,
54
+ selectedColumn,
55
+ hoveredColumn,
56
+ projectID,
57
+ inProcess,
58
+ },
59
+ h(_ColumnNavigationMap, rest)
60
+ );
61
+ }
62
+
63
+ function _ColumnNavigationMap(props: ColumnNavigationMapProps) {
64
+ const {
65
+ padding = 50,
66
+ children,
67
+ columnColor,
68
+ showTriangulation,
69
+ triangulationColor,
70
+ keyboardNavigation = true,
71
+ ...rest
72
+ } = props;
73
+
74
+ const overlayStyles = useMemo(() => {
75
+ let styles: any[] = [buildColumnsStyle(columnColor)];
76
+ if (showTriangulation) {
77
+ styles.push(buildKeyboardNavigationStyle(triangulationColor));
78
+ }
79
+ return styles;
80
+ }, [columnColor, showTriangulation, triangulationColor]);
81
+
82
+ return h(
83
+ InsetMap,
84
+ {
85
+ ...rest,
86
+ boxZoom: false,
87
+ dragRotate: false,
88
+ overlayStyles,
89
+ },
90
+ [
91
+ h(ColumnsLayer),
92
+ h.if(keyboardNavigation)(ColumnKeyboardNavigation, { showTriangulation }),
93
+ children,
94
+ ]
95
+ );
96
+ }
97
+
98
+ function ColumnsLayer({ enabled = true }) {
99
+ const columns = useColumnNavigationStore((state) => state.columns);
100
+ const selectedColumn = useColumnNavigationStore(
101
+ (state) => state.selectedColumn
102
+ );
103
+ const hoveredColumn = useColumnNavigationStore(
104
+ (state) => state.hoveredColumn
105
+ );
106
+ const selectColumn = useColumnNavigationStore((state) => state.selectColumn);
107
+ const setHoveredColumn = useColumnNavigationStore(
108
+ (state) => state.setHoveredColumn
109
+ );
110
+
111
+ useMapStyleOperator(
112
+ (map) => {
113
+ if (columns == null) {
114
+ return;
115
+ }
116
+ const data: FeatureCollection = {
117
+ type: "FeatureCollection",
118
+ features: columns,
119
+ };
120
+
121
+ setGeoJSON(map, "columns", data);
122
+
123
+ const mouseMove = (event) => {
124
+ if (event.features.length == 0) return;
125
+ const hoveredFeature = event.features[0];
126
+ setHoveredColumn(hoveredFeature.id);
127
+ map.getCanvas().style.cursor = "pointer";
128
+ };
129
+ const mouseLeave = () => {
130
+ setHoveredColumn(null);
131
+ map.getCanvas().style.cursor = "";
132
+ };
133
+
134
+ const clickHandler = (event) => {
135
+ if (event.features.length == 0) return;
136
+ const selectedColumn = event.features[0];
137
+ const id = selectedColumn.id;
138
+ selectColumn(id);
139
+ };
140
+
141
+ // Setup hover styles on map
142
+ const layers = ["columns-points", "columns-fill"];
143
+ map.on("mousemove", layers, mouseMove);
144
+ map.on("mouseleave", layers, mouseLeave);
145
+ map.on("click", layers, clickHandler);
146
+
147
+ return () => {
148
+ map.off("mouseenter", layers, mouseMove);
149
+ map.off("mouseleave", layers, mouseLeave);
150
+ map.off("click", layers, clickHandler);
151
+ };
152
+ },
153
+ [columns, enabled]
154
+ );
155
+
156
+ const hoveredColumnRef = useRef(null);
157
+ useMapStyleOperator(
158
+ (map) => {
159
+ const prevHoveredColumn = hoveredColumnRef.current;
160
+ if (hoveredColumn == prevHoveredColumn) return;
161
+ if (prevHoveredColumn != null) {
162
+ // Deselect previous column
163
+ map.setFeatureState(
164
+ { source: "columns", id: prevHoveredColumn },
165
+ { hover: false }
166
+ );
167
+ }
168
+ hoveredColumnRef.current = hoveredColumn;
169
+ // Select the current column
170
+ map.setFeatureState(
171
+ { source: "columns", id: hoveredColumn },
172
+ { hover: true }
173
+ );
174
+ },
175
+ [hoveredColumn, columns]
176
+ );
177
+
178
+ /** Set feature state for selected columns */
179
+ const selectedColumnRef = useRef(null);
180
+ const initialRenderRef = useRef(true);
181
+ useMapStyleOperator(
182
+ (map) => {
183
+ if (columns == null) return;
184
+ const prevSelectedColumn = selectedColumnRef.current;
185
+ if (selectedColumn == prevSelectedColumn) return;
186
+ if (prevSelectedColumn != null) {
187
+ // Deselect previous column
188
+ map.setFeatureState(
189
+ { source: "columns", id: prevSelectedColumn },
190
+ { selected: false }
191
+ );
192
+ }
193
+
194
+ selectedColumnRef.current = selectedColumn;
195
+
196
+ // Select the current column
197
+ map.setFeatureState(
198
+ { source: "columns", id: selectedColumn },
199
+ { selected: true }
200
+ );
201
+
202
+ // Center the selected column
203
+ const columnGeometry = columns.find(
204
+ (d) => d.id == selectedColumn
205
+ )?.geometry;
206
+ if (columnGeometry == null) return;
207
+ const center = geoCentroid(columnGeometry);
208
+
209
+ const isInitialRender = initialRenderRef.current;
210
+ map.easeTo({ center }, { duration: isInitialRender ? 0 : 500 });
211
+ if (isInitialRender) {
212
+ initialRenderRef.current = false;
213
+ }
214
+ },
215
+ [selectedColumn, columns]
216
+ );
217
+
218
+ return null;
219
+ }
@@ -0,0 +1,119 @@
1
+ import { useCallback, useEffect, useMemo } from "react";
2
+ import { buildGeoJSONSource, setGeoJSON } from "@macrostrat/mapbox-utils";
3
+ import { useMapRef, useMapStyleOperator } from "@macrostrat/mapbox-react";
4
+ import { useColumnNavigationStore } from "./state";
5
+ import {
6
+ buildTriangulation,
7
+ buildKeyMapping,
8
+ } from "../utils/keyboard-navigation";
9
+
10
+ interface KeyboardNavProps {
11
+ showTriangulation: boolean;
12
+ }
13
+
14
+ export function ColumnKeyboardNavigation(props: KeyboardNavProps) {
15
+ /** Keyboard navigation of columns using a Delaunay triangulation */
16
+ const { showTriangulation = false } = props;
17
+ const columns = useColumnNavigationStore((state) => state.columns);
18
+ const col_id = useColumnNavigationStore((state) => state.selectedColumn);
19
+ const selectColumn = useColumnNavigationStore((state) => state.selectColumn);
20
+
21
+ const voronoi = useMemo(() => {
22
+ if (!columns || columns.length == 0) return null;
23
+ return buildTriangulation(columns);
24
+ }, [columns]);
25
+
26
+ const mapRef = useMapRef();
27
+ const map = mapRef.current;
28
+
29
+ const keyMapping = useMemo(() => {
30
+ if (columns == null || voronoi == null || map == null) return null;
31
+
32
+ const projection = (d) => {
33
+ const pt = map.project(d);
34
+ return [pt.x, pt.y];
35
+ };
36
+
37
+ const currentIndex = columns.findIndex(
38
+ (d) => d.properties.col_id == col_id
39
+ );
40
+
41
+ const neighbors = voronoi.tri.delaunay.neighbors[currentIndex];
42
+ return buildKeyMapping(
43
+ neighbors,
44
+ voronoi.centroids,
45
+ currentIndex,
46
+ projection
47
+ );
48
+ }, [col_id, columns, voronoi, map]);
49
+
50
+ const onKeyDown = useCallback(
51
+ (event: KeyboardEvent) => {
52
+ if (keyMapping == null) return;
53
+ const nextColumnIx = keyMapping[event.which];
54
+ if (nextColumnIx == null) return;
55
+ event.preventDefault();
56
+ event.stopPropagation();
57
+ // @ts-ignore
58
+ selectColumn(columns[nextColumnIx].id);
59
+ return false;
60
+ },
61
+ [keyMapping, selectColumn]
62
+ );
63
+
64
+ useEffect(() => {
65
+ if (map == null) return;
66
+ const canvas = map.getCanvas();
67
+ canvas.addEventListener("keydown", onKeyDown);
68
+ return () => {
69
+ canvas.removeEventListener("keydown", onKeyDown);
70
+ };
71
+ }, [map, onKeyDown]);
72
+
73
+ useMapStyleOperator(
74
+ (map) => {
75
+ if (voronoi == null || !showTriangulation) return;
76
+ const { features } = voronoi.tri.links();
77
+
78
+ setGeoJSON(map, "column-links", {
79
+ type: "FeatureCollection",
80
+ features,
81
+ });
82
+ },
83
+ [voronoi, showTriangulation]
84
+ );
85
+
86
+ //if (neighbors == null) return null;
87
+ //const neighborFeatures = neighbors.map((d) => features[d]);
88
+
89
+ return null;
90
+ }
91
+
92
+ export function buildKeyboardNavigationStyle(color: string = "purple") {
93
+ return {
94
+ sources: {
95
+ "column-links": buildGeoJSONSource(),
96
+ //neighbors: buildGeoJSONSource(),
97
+ },
98
+ layers: [
99
+ {
100
+ id: "column-links",
101
+ type: "line",
102
+ source: "column-links",
103
+ paint: {
104
+ "line-color": color,
105
+ "line-width": 1.5,
106
+ },
107
+ },
108
+ {
109
+ id: "column-centers",
110
+ type: "circle",
111
+ source: "column-links",
112
+ paint: {
113
+ "circle-radius": 2,
114
+ "circle-color": color,
115
+ },
116
+ },
117
+ ],
118
+ };
119
+ }
@@ -0,0 +1,109 @@
1
+ import { create, StoreApi, useStore } from "zustand";
2
+ import type { ColumnGeoJSONRecordWithID } from "@macrostrat/api-types";
3
+ import {
4
+ createContext,
5
+ useState,
6
+ useContext,
7
+ ReactNode,
8
+ useEffect,
9
+ } from "react";
10
+ import h from "@macrostrat/hyper";
11
+ import {
12
+ useMacrostratColumns,
13
+ useMacrostratStore,
14
+ } from "../../../data-provider";
15
+
16
+ export interface NavigationStore {
17
+ columns: ColumnGeoJSONRecordWithID[];
18
+ selectedColumn: number | null;
19
+ hoveredColumn: number | null;
20
+ selectColumn: (columnID: number | null) => void;
21
+ setHoveredColumn: (columnID: number | null) => void;
22
+ }
23
+
24
+ export interface NavigationProviderProps {
25
+ projectID?: number;
26
+ inProcess?: boolean;
27
+ selectedColumn?: number | null;
28
+ hoveredColumn?: number | null;
29
+ columns?: ColumnGeoJSONRecordWithID[] | null;
30
+ children: ReactNode;
31
+ onSelectColumn?: (col_id: number | null, column: any) => void;
32
+ onHoverColumn?: (col_id: number | null, column: any) => void;
33
+ }
34
+
35
+ const NavigationStoreContext = createContext<StoreApi<NavigationStore> | null>(
36
+ null
37
+ );
38
+
39
+ export function ColumnNavigationProvider({
40
+ children,
41
+ columns,
42
+ selectedColumn,
43
+ projectID,
44
+ inProcess,
45
+ onSelectColumn,
46
+ onHoverColumn,
47
+ }: NavigationProviderProps) {
48
+ const getColumns = useMacrostratStore((s) => s.getColumns);
49
+
50
+ const [store] = useState(() => {
51
+ return create<NavigationStore>((set, get): NavigationStore => {
52
+ return {
53
+ columns: null,
54
+ selectedColumn: null,
55
+ hoveredColumn: null,
56
+ selectColumn(columnID: number | null) {
57
+ set({ selectedColumn: columnID, hoveredColumn: null });
58
+ const { columns } = get();
59
+ const column = columns?.find((d) => d.id == columnID);
60
+ onSelectColumn?.(columnID, column);
61
+ },
62
+ setHoveredColumn(columnID: number | null) {
63
+ set({ hoveredColumn: columnID });
64
+ const { columns } = get();
65
+ const column = columns?.find((d) => d.id == columnID);
66
+ onHoverColumn?.(columnID, column);
67
+ },
68
+ };
69
+ });
70
+ });
71
+
72
+ // Set up the store
73
+ /** TODO: unify handling of columns between parts of application */
74
+ // useAsyncEffect(async () => {
75
+ // let _columns = columns ?? (await getColumns(projectID, inProcess));
76
+ // store.setState({ columns: _columns, selectedColumn });
77
+ // }, [projectID, inProcess, columns, getColumns]);
78
+
79
+ const _columns = columns ?? useMacrostratColumns(projectID, inProcess);
80
+ useEffect(() => {
81
+ if (_columns != null) {
82
+ store.setState({ columns: _columns, selectedColumn });
83
+ }
84
+ }, [_columns]);
85
+ // Update selected colun if it is changed externally
86
+
87
+ // Kind of an awkward way to do this but we need to allow the selector to run
88
+ useEffect(() => {
89
+ console.log("Selecting column", selectedColumn);
90
+ const { selectColumn, selectedColumn: _internalSelectedColumn } =
91
+ store.getState();
92
+ if (selectedColumn == _internalSelectedColumn) {
93
+ return;
94
+ }
95
+ selectColumn(selectedColumn);
96
+ }, [selectedColumn]);
97
+
98
+ return h(NavigationStoreContext.Provider, { value: store }, children);
99
+ }
100
+
101
+ export function useColumnNavigationStore(
102
+ selector: (state: NavigationStore) => any
103
+ ) {
104
+ const storeApi = useContext(NavigationStoreContext);
105
+ if (storeApi == null) {
106
+ throw new Error("Missing ColumnNavigationProvider");
107
+ }
108
+ return useStore(storeApi, selector);
109
+ }
@@ -0,0 +1,46 @@
1
+ import h from "@macrostrat/hyper";
2
+
3
+ import { Meta } from "@storybook/react";
4
+ import { ColumnNavigationSVGMap, ColumnNavigationSVGMapProps } from ".";
5
+ import { MacrostratDataProvider } from "../../../data-provider";
6
+ import { useState } from "react";
7
+
8
+ interface ColumnIndexMapProps extends ColumnNavigationSVGMapProps {
9
+ col_id: number;
10
+ }
11
+
12
+ function BasicColumnNavigationSVGMap(props: ColumnIndexMapProps) {
13
+ return h(
14
+ MacrostratDataProvider,
15
+ h(ColumnNavigationSVGMap, { style: { width: 500, height: 500 }, ...props })
16
+ );
17
+ }
18
+
19
+ // More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
20
+ export default {
21
+ title: "Column views/Maps/Column navigation (SVG)",
22
+ component: BasicColumnNavigationSVGMap,
23
+ } as Meta<typeof ColumnNavigationSVGMap>;
24
+
25
+ export const StaticColumnView = {
26
+ args: {
27
+ center: [-100, 38],
28
+ },
29
+ };
30
+
31
+ export function Interactive() {
32
+ const [currentColumn, setCurrentColumn] = useState(null);
33
+ return h(BasicColumnNavigationSVGMap, {
34
+ currentColumn,
35
+ setCurrentColumn,
36
+ });
37
+ }
38
+
39
+ export function ShowingTriangulation() {
40
+ const [currentColumn, setCurrentColumn] = useState(null);
41
+ return h(BasicColumnNavigationSVGMap, {
42
+ currentColumn,
43
+ setCurrentColumn,
44
+ showDebugLayers: true,
45
+ });
46
+ }
@@ -4,8 +4,9 @@ import { Globe, LandLayer } from "@macrostrat/svg-map-components";
4
4
  import { geoCentroid } from "d3-geo";
5
5
  import { useElementSize } from "@macrostrat/ui-components";
6
6
  import { Columns, CurrentColumn } from "./layers";
7
+ import type { LngLatLike } from "mapbox-gl";
7
8
 
8
- function ResizableMapFrame(props) {
9
+ export function ResizableMapFrame(props) {
9
10
  const { center, children, style, margin, className, allowZoom, ...rest } =
10
11
  props;
11
12
  let { scale } = props;
@@ -45,7 +46,10 @@ function ResizableMapFrame(props) {
45
46
  ]);
46
47
  }
47
48
 
48
- const ColumnNavigatorMap = (props) => {
49
+ type ColumnNavigationSVGMapProps = any;
50
+ export type { ColumnNavigationSVGMapProps };
51
+
52
+ export function ColumnNavigationSVGMap(props) {
49
53
  const {
50
54
  currentColumn,
51
55
  setCurrentColumn,
@@ -55,19 +59,28 @@ const ColumnNavigatorMap = (props) => {
55
59
  scale,
56
60
  className,
57
61
  showInProcessColumns = false,
62
+ showDebugLayers = false,
63
+ center,
58
64
  ...rest
59
65
  } = props;
60
66
 
61
- const columnCenter = geoCentroid(currentColumn);
67
+ let _center: LngLatLike | null = center ?? [-100, 38];
68
+ // Override center if currentColumn is set
69
+ if (currentColumn != null) {
70
+ _center = geoCentroid(currentColumn);
71
+ }
72
+
73
+ console.log(currentColumn);
62
74
 
63
75
  return h(
64
76
  ResizableMapFrame,
65
- { center: columnCenter, style, margin, scale, className },
77
+ { center: _center, style, margin, scale, className },
66
78
  [
67
79
  children,
68
80
  h(Columns, {
69
81
  onChange: setCurrentColumn,
70
82
  col_id: currentColumn?.properties.col_id,
83
+ showDebugLayers,
71
84
  ...rest,
72
85
  }),
73
86
  h.if(showInProcessColumns)(Columns, {
@@ -80,7 +93,6 @@ const ColumnNavigatorMap = (props) => {
80
93
  h.if(currentColumn != null)(CurrentColumn, { feature: currentColumn }),
81
94
  ]
82
95
  );
83
- };
96
+ }
84
97
 
85
98
  export * from "./layers";
86
- export { ColumnNavigatorMap, ResizableMapFrame };