@macrostrat/column-views 1.0.3 → 2.0.1

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 (258) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/esm/{data-area.daff8da0.js → column-views.062d164a.js} +1 -1
  3. package/dist/esm/{data-area.daff8da0.js.map → column-views.062d164a.js.map} +1 -1
  4. package/dist/esm/{data-provider.1506eb64.js → column-views.07521ce6.js} +1 -1
  5. package/dist/esm/column-views.07521ce6.js.map +1 -0
  6. package/dist/esm/column-views.134b2e2a.js +9 -0
  7. package/dist/esm/column-views.134b2e2a.js.map +1 -0
  8. package/dist/esm/{selection-popover.module.0a701258.css → column-views.16cc7a58.css} +11 -8
  9. package/dist/esm/column-views.16cc7a58.css.map +1 -0
  10. package/dist/esm/column-views.1c589cc4.js +141 -0
  11. package/dist/esm/column-views.1c589cc4.js.map +1 -0
  12. package/dist/esm/column-views.1ece326b.js +420 -0
  13. package/dist/esm/column-views.1ece326b.js.map +1 -0
  14. package/dist/esm/column-views.1edc7300.js +40 -0
  15. package/dist/esm/column-views.1edc7300.js.map +1 -0
  16. package/dist/esm/column-views.240ef158.css +159 -0
  17. package/dist/esm/column-views.240ef158.css.map +1 -0
  18. package/dist/esm/column-views.25471fe0.css +111 -0
  19. package/dist/esm/column-views.25471fe0.css.map +1 -0
  20. package/dist/esm/column-views.28ee5f43.js +288 -0
  21. package/dist/esm/column-views.28ee5f43.js.map +1 -0
  22. package/dist/esm/column-views.30646469.js +118 -0
  23. package/dist/esm/column-views.30646469.js.map +1 -0
  24. package/dist/esm/column-views.338a7a83.css +134 -0
  25. package/dist/esm/column-views.338a7a83.css.map +1 -0
  26. package/dist/esm/column-views.33dbc778.js +95 -0
  27. package/dist/esm/column-views.33dbc778.js.map +1 -0
  28. package/dist/esm/column-views.343b5aa2.js +98 -0
  29. package/dist/esm/column-views.343b5aa2.js.map +1 -0
  30. package/dist/esm/column-views.34636ec7.js +104 -0
  31. package/dist/esm/column-views.34636ec7.js.map +1 -0
  32. package/dist/esm/column-views.3465c8ad.js +16 -0
  33. package/dist/esm/column-views.3465c8ad.js.map +1 -0
  34. package/dist/esm/column-views.39657bbf.js +55 -0
  35. package/dist/esm/column-views.39657bbf.js.map +1 -0
  36. package/dist/esm/column-views.3b972a9b.css +4 -0
  37. package/dist/esm/{composite.module.69a38fd1.css.map → column-views.3b972a9b.css.map} +1 -1
  38. package/dist/esm/column-views.3bec70d6.js +167 -0
  39. package/dist/esm/column-views.3bec70d6.js.map +1 -0
  40. package/dist/esm/column-views.3de4dc43.js +66 -0
  41. package/dist/esm/column-views.3de4dc43.js.map +1 -0
  42. package/dist/esm/{column.db058031.js → column-views.3f4db8cb.js} +8 -8
  43. package/dist/esm/column-views.3f4db8cb.js.map +1 -0
  44. package/dist/esm/column-views.40c1f361.js +206 -0
  45. package/dist/esm/column-views.40c1f361.js.map +1 -0
  46. package/dist/esm/{composite.module.0638e7ee.js → column-views.40cd59b5.js} +1 -1
  47. package/dist/esm/{composite.module.0638e7ee.js.map → column-views.40cd59b5.js.map} +1 -1
  48. package/dist/esm/column-views.4637f4ce.js +46 -0
  49. package/dist/esm/column-views.4637f4ce.js.map +1 -0
  50. package/dist/esm/column-views.4c4c5bac.js +163 -0
  51. package/dist/esm/column-views.4c4c5bac.js.map +1 -0
  52. package/dist/esm/column-views.4d374a35.js +19 -0
  53. package/dist/esm/column-views.4d374a35.js.map +1 -0
  54. package/dist/esm/column-views.57c4ddf3.js +125 -0
  55. package/dist/esm/column-views.57c4ddf3.js.map +1 -0
  56. package/dist/esm/column-views.58b82432.js +58 -0
  57. package/dist/esm/column-views.58b82432.js.map +1 -0
  58. package/dist/esm/column-views.5b2635fd.js +13 -0
  59. package/dist/esm/column-views.5b2635fd.js.map +1 -0
  60. package/dist/esm/column-views.5c02fee2.js +258 -0
  61. package/dist/esm/column-views.5c02fee2.js.map +1 -0
  62. package/dist/esm/column-views.5cc09c1d.js +152 -0
  63. package/dist/esm/column-views.5cc09c1d.js.map +1 -0
  64. package/dist/esm/{names.f6a4e9a4.js → column-views.650bd9f5.js} +5 -5
  65. package/dist/esm/column-views.650bd9f5.js.map +1 -0
  66. package/dist/esm/column-views.68409161.js +89 -0
  67. package/dist/esm/column-views.68409161.js.map +1 -0
  68. package/dist/esm/column-views.6a954628.js +70 -0
  69. package/dist/esm/column-views.6a954628.js.map +1 -0
  70. package/dist/esm/column-views.6c670c13.css +26 -0
  71. package/dist/esm/column-views.6c670c13.css.map +1 -0
  72. package/dist/esm/column-views.6d6b73b4.js +55 -0
  73. package/dist/esm/column-views.6d6b73b4.js.map +1 -0
  74. package/dist/esm/column-views.86c4b36f.js +222 -0
  75. package/dist/esm/column-views.86c4b36f.js.map +1 -0
  76. package/dist/esm/column-views.88de2cee.js +9 -0
  77. package/dist/esm/column-views.88de2cee.js.map +1 -0
  78. package/dist/esm/{resolvers.b5c82b78.js → column-views.8dd877af.js} +16 -3
  79. package/dist/esm/column-views.8dd877af.js.map +1 -0
  80. package/dist/esm/column-views.8e51206e.js +143 -0
  81. package/dist/esm/column-views.8e51206e.js.map +1 -0
  82. package/dist/esm/{column.module.7f47d25a.js → column-views.94b5cca1.js} +1 -1
  83. package/dist/esm/{column.module.7f47d25a.js.map → column-views.94b5cca1.js.map} +1 -1
  84. package/dist/esm/column-views.9804efda.js +75 -0
  85. package/dist/esm/column-views.9804efda.js.map +1 -0
  86. package/dist/esm/column-views.9bdd477a.js +57 -0
  87. package/dist/esm/column-views.9bdd477a.js.map +1 -0
  88. package/dist/esm/column-views.9ced149e.js +98 -0
  89. package/dist/esm/column-views.9ced149e.js.map +1 -0
  90. package/dist/esm/{column.module.cbb072df.css → column-views.a7be0a4a.css} +1 -1
  91. package/dist/esm/{column.module.cbb072df.css.map → column-views.a7be0a4a.css.map} +1 -1
  92. package/dist/esm/column-views.a98214b2.js +9 -0
  93. package/dist/esm/column-views.a98214b2.js.map +1 -0
  94. package/dist/esm/column-views.aa9fcdba.js +221 -0
  95. package/dist/esm/column-views.aa9fcdba.js.map +1 -0
  96. package/dist/esm/{isotopes-column.7cb5cc45.js → column-views.ac9fb5e1.js} +3 -3
  97. package/dist/esm/{isotopes-column.7cb5cc45.js.map → column-views.ac9fb5e1.js.map} +1 -1
  98. package/dist/esm/column-views.b2e5f1d9.js +11 -0
  99. package/dist/esm/column-views.b2e5f1d9.js.map +1 -0
  100. package/dist/esm/column-views.b775c0a3.js +112 -0
  101. package/dist/esm/column-views.b775c0a3.js.map +1 -0
  102. package/dist/esm/column-views.beb54e95.js +39 -0
  103. package/dist/esm/column-views.beb54e95.js.map +1 -0
  104. package/dist/esm/{core.4db8e452.js → column-views.c58aad3f.js} +15 -16
  105. package/dist/esm/column-views.c58aad3f.js.map +1 -0
  106. package/dist/esm/{carbon-isotopes.3b502a9e.js → column-views.d1ad48eb.js} +4 -4
  107. package/dist/esm/{carbon-isotopes.3b502a9e.js.map → column-views.d1ad48eb.js.map} +1 -1
  108. package/dist/esm/column-views.dc42f5d0.js +48 -0
  109. package/dist/esm/column-views.dc42f5d0.js.map +1 -0
  110. package/dist/esm/column-views.dff52504.js +22 -0
  111. package/dist/esm/column-views.dff52504.js.map +1 -0
  112. package/dist/esm/column-views.e2b9c23c.css +33 -0
  113. package/dist/esm/column-views.e2b9c23c.css.map +1 -0
  114. package/dist/esm/column-views.e40b8bfb.css +14 -0
  115. package/dist/esm/column-views.e40b8bfb.css.map +1 -0
  116. package/dist/esm/column-views.e77b9d6b.js +170 -0
  117. package/dist/esm/column-views.e77b9d6b.js.map +1 -0
  118. package/dist/esm/column-views.ea237aa8.js +52 -0
  119. package/dist/esm/column-views.ea237aa8.js.map +1 -0
  120. package/dist/esm/column-views.edf77621.js +22 -0
  121. package/dist/esm/column-views.edf77621.js.map +1 -0
  122. package/dist/esm/column-views.f1efcc2c.js +9 -0
  123. package/dist/esm/column-views.f1efcc2c.js.map +1 -0
  124. package/dist/esm/column-views.f2e5a72f.js +66 -0
  125. package/dist/esm/column-views.f2e5a72f.js.map +1 -0
  126. package/dist/esm/column-views.f71ec4b4.js +127 -0
  127. package/dist/esm/column-views.f71ec4b4.js.map +1 -0
  128. package/dist/esm/column-views.f9dab157.js +3 -0
  129. package/dist/esm/column-views.f9dab157.js.map +1 -0
  130. package/dist/esm/column-views.fa6f3292.js +215 -0
  131. package/dist/esm/column-views.fa6f3292.js.map +1 -0
  132. package/dist/esm/column-views.fc2b4611.js +9 -0
  133. package/dist/esm/column-views.fc2b4611.js.map +1 -0
  134. package/dist/esm/index.d.ts +797 -130
  135. package/dist/esm/index.d.ts.map +1 -1
  136. package/dist/esm/index.js +13 -9
  137. package/dist/esm/index.js.map +1 -1
  138. package/dist/node/index.js +1 -1
  139. package/dist/node/index.js.map +1 -1
  140. package/package.json +29 -13
  141. package/src/age-axis.module.sass +27 -0
  142. package/src/age-axis.ts +82 -19
  143. package/src/age-model-overlay.module.sass +11 -0
  144. package/src/age-model-overlay.ts +73 -0
  145. package/src/carbon-isotopes/column.ts +4 -2
  146. package/src/carbon-isotopes/core.ts +9 -6
  147. package/src/carbon-isotopes/data-provider.ts +3 -3
  148. package/src/column.module.sass +58 -49
  149. package/src/column.ts +149 -135
  150. package/src/correlation-chart/hash-string.ts +57 -0
  151. package/src/correlation-chart/index.ts +2 -0
  152. package/src/correlation-chart/main.module.sass +122 -0
  153. package/src/correlation-chart/main.ts +305 -0
  154. package/src/correlation-chart/prepare-data.ts +217 -0
  155. package/src/correlation-chart/stories/correlation-chart.stories.ts +192 -0
  156. package/src/correlation-chart/stories/stories.module.sass +18 -0
  157. package/src/correlation-chart/unconformity-line.module.sass +8 -0
  158. package/src/correlation-chart/unconformity-line.ts +25 -0
  159. package/src/data-provider/base.ts +388 -0
  160. package/src/data-provider/column-nav.ts +53 -0
  161. package/src/data-provider/fetch.ts +231 -0
  162. package/src/data-provider/index.ts +4 -0
  163. package/src/data-provider/store.ts +72 -0
  164. package/src/index.ts +5 -3
  165. package/src/maps/_shared/index.ts +2 -0
  166. package/src/maps/_shared/inset-map.stories.ts +20 -0
  167. package/src/maps/_shared/inset-map.ts +87 -0
  168. package/src/maps/_shared/styles.ts +57 -0
  169. package/src/maps/column-correlation/column-correlation-map.stories.ts +96 -0
  170. package/src/maps/column-correlation/core.ts +225 -0
  171. package/src/maps/column-correlation/index.ts +2 -0
  172. package/src/maps/column-correlation/state.ts +192 -0
  173. package/src/maps/column-navigation/index.ts +2 -0
  174. package/src/maps/column-navigation/mapbox/column-navigation-map.stories.ts +102 -0
  175. package/src/maps/column-navigation/mapbox/index.ts +219 -0
  176. package/src/maps/column-navigation/mapbox/keyboard-navigation.ts +119 -0
  177. package/src/maps/column-navigation/mapbox/state.ts +109 -0
  178. package/src/maps/column-navigation/svg/column-navigation-svg-map.stories.ts +46 -0
  179. package/src/{map → maps/column-navigation/svg}/index.ts +18 -6
  180. package/src/{map → maps/column-navigation/svg}/layers.ts +28 -109
  181. package/src/maps/column-navigation/utils/keyboard-navigation.ts +58 -0
  182. package/src/maps/index.ts +3 -0
  183. package/src/prepare-units/composite-scale.ts +353 -0
  184. package/src/prepare-units/helpers.ts +226 -0
  185. package/src/prepare-units/index.ts +177 -0
  186. package/src/prepare-units/utils.ts +98 -0
  187. package/src/section.ts +264 -84
  188. package/src/unit-details/index.ts +2 -215
  189. package/src/unit-details/{index.module.sass → panel.module.sass} +39 -8
  190. package/src/unit-details/panel.ts +523 -0
  191. package/src/{selection-popover.module.sass → unit-details/popover.module.sass} +4 -2
  192. package/src/unit-details/popover.ts +82 -0
  193. package/src/units/boxes.module.sass +16 -0
  194. package/src/units/boxes.ts +243 -90
  195. package/src/units/colors.ts +64 -0
  196. package/src/units/composite.ts +157 -152
  197. package/src/units/index.ts +76 -9
  198. package/src/units/names.ts +8 -4
  199. package/src/units/resolvers.ts +12 -1
  200. package/src/units/selection.ts +115 -101
  201. package/src/units/types.ts +5 -16
  202. package/dist/esm/age-axis.64c509ee.js +0 -41
  203. package/dist/esm/age-axis.64c509ee.js.map +0 -1
  204. package/dist/esm/boxes.13968b76.js +0 -164
  205. package/dist/esm/boxes.13968b76.js.map +0 -1
  206. package/dist/esm/boxes.module.28873d08.css +0 -4
  207. package/dist/esm/boxes.module.28873d08.css.map +0 -1
  208. package/dist/esm/boxes.module.605bdcd6.js +0 -13
  209. package/dist/esm/boxes.module.605bdcd6.js.map +0 -1
  210. package/dist/esm/column.8dd26e1a.js +0 -149
  211. package/dist/esm/column.8dd26e1a.js.map +0 -1
  212. package/dist/esm/column.db058031.js.map +0 -1
  213. package/dist/esm/column.module.039d872c.css +0 -151
  214. package/dist/esm/column.module.039d872c.css.map +0 -1
  215. package/dist/esm/column.module.e632868a.js +0 -19
  216. package/dist/esm/column.module.e632868a.js.map +0 -1
  217. package/dist/esm/composite.608a4ca2.js +0 -153
  218. package/dist/esm/composite.608a4ca2.js.map +0 -1
  219. package/dist/esm/composite.module.69a38fd1.css +0 -4
  220. package/dist/esm/core.4db8e452.js.map +0 -1
  221. package/dist/esm/data-provider.1506eb64.js.map +0 -1
  222. package/dist/esm/helpers.8a03cfc7.js +0 -94
  223. package/dist/esm/helpers.8a03cfc7.js.map +0 -1
  224. package/dist/esm/index.module.0844afd8.css +0 -65
  225. package/dist/esm/index.module.0844afd8.css.map +0 -1
  226. package/dist/esm/index.module.24fe3a2e.js +0 -37
  227. package/dist/esm/index.module.24fe3a2e.js.map +0 -1
  228. package/dist/esm/layers.ad5fd25d.js +0 -231
  229. package/dist/esm/layers.ad5fd25d.js.map +0 -1
  230. package/dist/esm/map.7aa26f17.js +0 -82
  231. package/dist/esm/map.7aa26f17.js.map +0 -1
  232. package/dist/esm/names.f6a4e9a4.js.map +0 -1
  233. package/dist/esm/providers.cd1e652f.js +0 -25
  234. package/dist/esm/providers.cd1e652f.js.map +0 -1
  235. package/dist/esm/resolvers.b5c82b78.js.map +0 -1
  236. package/dist/esm/section.cc1b1100.js +0 -118
  237. package/dist/esm/section.cc1b1100.js.map +0 -1
  238. package/dist/esm/selection-popover.17d1bd55.js +0 -60
  239. package/dist/esm/selection-popover.17d1bd55.js.map +0 -1
  240. package/dist/esm/selection-popover.module.0a701258.css.map +0 -1
  241. package/dist/esm/selection-popover.module.e22f5183.js +0 -22
  242. package/dist/esm/selection-popover.module.e22f5183.js.map +0 -1
  243. package/dist/esm/selection.bbf26d0e.js +0 -117
  244. package/dist/esm/selection.bbf26d0e.js.map +0 -1
  245. package/dist/esm/store.25eaa168.js +0 -18
  246. package/dist/esm/store.25eaa168.js.map +0 -1
  247. package/dist/esm/types.f016e032.js +0 -18
  248. package/dist/esm/types.f016e032.js.map +0 -1
  249. package/dist/esm/unit-details.56ff8b70.js +0 -209
  250. package/dist/esm/unit-details.56ff8b70.js.map +0 -1
  251. package/dist/esm/units.361088d4.js +0 -42
  252. package/dist/esm/units.361088d4.js.map +0 -1
  253. package/src/helpers.ts +0 -133
  254. package/src/providers.ts +0 -34
  255. package/src/selection-popover.ts +0 -71
  256. package/src/store.ts +0 -12
  257. package/src/units/units.scss +0 -16
  258. /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 };