@macrostrat/column-views 2.3.3 → 2.4.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 (220) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/esm/{column-views.b977da35.js → column-views.083fb787.js} +5 -5
  3. package/dist/esm/{column-views.b977da35.js.map → column-views.083fb787.js.map} +1 -1
  4. package/dist/esm/{column-views.4685ea28.js → column-views.088a0274.js} +2 -2
  5. package/dist/esm/{column-views.4685ea28.js.map → column-views.088a0274.js.map} +1 -1
  6. package/dist/esm/{column-views.a665cbb2.js → column-views.11353c2c.js} +5 -5
  7. package/dist/esm/{column-views.a665cbb2.js.map → column-views.11353c2c.js.map} +1 -1
  8. package/dist/esm/{column-views.f7cdf6be.js → column-views.18655508.js} +3 -3
  9. package/dist/esm/{column-views.f7cdf6be.js.map → column-views.18655508.js.map} +1 -1
  10. package/dist/esm/column-views.1cd55918.js +206 -0
  11. package/dist/esm/column-views.1cd55918.js.map +1 -0
  12. package/dist/esm/column-views.1ef55568.js +15 -0
  13. package/dist/esm/{column-views.04636815.js.map → column-views.1ef55568.js.map} +1 -1
  14. package/dist/esm/{column-views.e075af15.js → column-views.3090f246.js} +22 -4
  15. package/dist/esm/column-views.3090f246.js.map +1 -0
  16. package/dist/esm/{column-views.c92a359e.js → column-views.33796541.js} +2 -2
  17. package/dist/esm/{column-views.c92a359e.js.map → column-views.33796541.js.map} +1 -1
  18. package/dist/esm/{column-views.488f24b6.js → column-views.3ae8f23d.js} +2 -2
  19. package/dist/esm/{column-views.488f24b6.js.map → column-views.3ae8f23d.js.map} +1 -1
  20. package/dist/esm/{column-views.faa7e52e.js → column-views.40d3688c.js} +3 -3
  21. package/dist/esm/{column-views.faa7e52e.js.map → column-views.40d3688c.js.map} +1 -1
  22. package/dist/esm/{column-views.b9265595.js → column-views.4e9b8594.js} +11 -10
  23. package/dist/esm/column-views.4e9b8594.js.map +1 -0
  24. package/dist/esm/{column-views.8c296309.js → column-views.5e1c216f.js} +27 -6
  25. package/dist/esm/column-views.5e1c216f.js.map +1 -0
  26. package/dist/esm/{column-views.e3f0cd7b.js → column-views.5e830e7c.js} +23 -33
  27. package/dist/esm/column-views.5e830e7c.js.map +1 -0
  28. package/dist/esm/{column-views.c149f7a5.js → column-views.5ec7294d.js} +3 -3
  29. package/dist/esm/{column-views.c149f7a5.js.map → column-views.5ec7294d.js.map} +1 -1
  30. package/dist/esm/column-views.67804ce5.js +68 -0
  31. package/dist/esm/column-views.67804ce5.js.map +1 -0
  32. package/dist/esm/{column-views.3f1cdc18.js → column-views.6f0d4f3a.js} +2 -2
  33. package/dist/esm/{column-views.3f1cdc18.js.map → column-views.6f0d4f3a.js.map} +1 -1
  34. package/dist/esm/{column-views.58b891df.js → column-views.76fb9948.js} +2 -2
  35. package/dist/esm/{column-views.58b891df.js.map → column-views.76fb9948.js.map} +1 -1
  36. package/dist/esm/{column-views.3d3e00be.js → column-views.77322764.js} +2 -2
  37. package/dist/esm/{column-views.3d3e00be.js.map → column-views.77322764.js.map} +1 -1
  38. package/dist/esm/{column-views.82a5112b.js → column-views.85d57d04.js} +4 -4
  39. package/dist/esm/{column-views.82a5112b.js.map → column-views.85d57d04.js.map} +1 -1
  40. package/dist/esm/{column-views.4e6eead3.js → column-views.88436022.js} +2 -2
  41. package/dist/esm/{column-views.4e6eead3.js.map → column-views.88436022.js.map} +1 -1
  42. package/dist/esm/{column-views.2f979aee.js → column-views.88b2418b.js} +3 -3
  43. package/dist/esm/{column-views.2f979aee.js.map → column-views.88b2418b.js.map} +1 -1
  44. package/dist/esm/{column-views.d6c0b7bc.js → column-views.8ce33ec6.js} +2 -2
  45. package/dist/esm/{column-views.d6c0b7bc.js.map → column-views.8ce33ec6.js.map} +1 -1
  46. package/dist/esm/{column-views.6f96d1c3.js → column-views.91c18fe3.js} +31 -19
  47. package/dist/esm/column-views.91c18fe3.js.map +1 -0
  48. package/dist/esm/{column-views.18aac6a5.js → column-views.99cb1c41.js} +3 -3
  49. package/dist/esm/{column-views.18aac6a5.js.map → column-views.99cb1c41.js.map} +1 -1
  50. package/dist/esm/{column-views.0a010643.js → column-views.ab2cc6cb.js} +27 -14
  51. package/dist/esm/column-views.ab2cc6cb.js.map +1 -0
  52. package/dist/esm/{column-views.d5fff9b8.js → column-views.af4278f0.js} +3 -3
  53. package/dist/esm/{column-views.d5fff9b8.js.map → column-views.af4278f0.js.map} +1 -1
  54. package/dist/esm/{column-views.d1b49f5c.js → column-views.b957dc19.js} +3 -3
  55. package/dist/esm/{column-views.d1b49f5c.js.map → column-views.b957dc19.js.map} +1 -1
  56. package/dist/esm/{column-views.ad2fe46c.js → column-views.bc83a85d.js} +19 -3
  57. package/dist/esm/column-views.bc83a85d.js.map +1 -0
  58. package/dist/esm/column-views.c1e507e6.js +113 -0
  59. package/dist/esm/column-views.c1e507e6.js.map +1 -0
  60. package/dist/esm/{column-views.ce3a3ac8.js → column-views.c93b63db.js} +3 -3
  61. package/dist/esm/column-views.c93b63db.js.map +1 -0
  62. package/dist/esm/{column-views.26e0e0b6.js → column-views.ce5251f7.js} +2 -2
  63. package/dist/esm/{column-views.26e0e0b6.js.map → column-views.ce5251f7.js.map} +1 -1
  64. package/dist/esm/{column-views.8d972657.js → column-views.d4e50f74.js} +3 -3
  65. package/dist/esm/{column-views.8d972657.js.map → column-views.d4e50f74.js.map} +1 -1
  66. package/dist/esm/{column-views.75b83110.js → column-views.dd2d5a6d.js} +5 -5
  67. package/dist/esm/{column-views.75b83110.js.map → column-views.dd2d5a6d.js.map} +1 -1
  68. package/dist/esm/{column-views.73fc96cb.js → column-views.e6882667.js} +5 -5
  69. package/dist/esm/{column-views.73fc96cb.js.map → column-views.e6882667.js.map} +1 -1
  70. package/dist/esm/{column-views.ee9d71ba.js → column-views.e885211d.js} +2 -2
  71. package/dist/esm/{column-views.ee9d71ba.js.map → column-views.e885211d.js.map} +1 -1
  72. package/dist/esm/{column-views.029ea8f1.js → column-views.ee2fd38f.js} +3 -3
  73. package/dist/esm/{column-views.029ea8f1.js.map → column-views.ee2fd38f.js.map} +1 -1
  74. package/dist/esm/{column-views.9d51a5ab.js → column-views.f35c4621.js} +2 -2
  75. package/dist/esm/column-views.f35c4621.js.map +1 -0
  76. package/dist/esm/{column-views.5fff0716.js → column-views.f56e604d.js} +3 -3
  77. package/dist/esm/{column-views.5fff0716.js.map → column-views.f56e604d.js.map} +1 -1
  78. package/dist/esm/{column-views.9ffc089b.js → column-views.f904e6d7.js} +3 -3
  79. package/dist/esm/{column-views.9ffc089b.js.map → column-views.f904e6d7.js.map} +1 -1
  80. package/dist/esm/{column-views.24060b85.js → column-views.fc4860bd.js} +4 -4
  81. package/dist/esm/{column-views.24060b85.js.map → column-views.fc4860bd.js.map} +1 -1
  82. package/dist/esm/{column-views.0da4503a.js → column-views.fedccce9.js} +2 -2
  83. package/dist/esm/{column-views.0da4503a.js.map → column-views.fedccce9.js.map} +1 -1
  84. package/dist/esm/index.d.ts +91 -86
  85. package/dist/esm/index.d.ts.map +1 -1
  86. package/dist/esm/index.js +11 -11
  87. package/dist/node/{column-views.02869dc5.js → column-views.003e0c17.js} +2 -2
  88. package/dist/node/{column-views.02869dc5.js.map → column-views.003e0c17.js.map} +1 -1
  89. package/dist/node/column-views.080781ca.js +2 -0
  90. package/dist/node/column-views.080781ca.js.map +1 -0
  91. package/dist/node/{column-views.00ad98fa.js → column-views.0818254f.js} +2 -2
  92. package/dist/node/{column-views.00ad98fa.js.map → column-views.0818254f.js.map} +1 -1
  93. package/dist/node/{column-views.2577dec0.js → column-views.0d48f51d.js} +2 -2
  94. package/dist/node/{column-views.2577dec0.js.map → column-views.0d48f51d.js.map} +1 -1
  95. package/dist/node/column-views.18e86c2d.js +2 -0
  96. package/dist/node/column-views.18e86c2d.js.map +1 -0
  97. package/dist/node/{column-views.8eae7028.js → column-views.375529b1.js} +2 -2
  98. package/dist/node/{column-views.8eae7028.js.map → column-views.375529b1.js.map} +1 -1
  99. package/dist/node/{column-views.6b391d60.js → column-views.39b71a38.js} +2 -2
  100. package/dist/node/{column-views.6b391d60.js.map → column-views.39b71a38.js.map} +1 -1
  101. package/dist/node/{column-views.47440961.js → column-views.447008d0.js} +2 -2
  102. package/dist/node/{column-views.47440961.js.map → column-views.447008d0.js.map} +1 -1
  103. package/dist/node/{column-views.c4188442.js → column-views.4d573ded.js} +2 -2
  104. package/dist/node/{column-views.c4188442.js.map → column-views.4d573ded.js.map} +1 -1
  105. package/dist/node/{column-views.8f540f78.js → column-views.4d8fc443.js} +2 -2
  106. package/dist/node/{column-views.8f540f78.js.map → column-views.4d8fc443.js.map} +1 -1
  107. package/dist/node/{column-views.3a947d58.js → column-views.54f7a243.js} +2 -2
  108. package/dist/node/{column-views.3a947d58.js.map → column-views.54f7a243.js.map} +1 -1
  109. package/dist/node/column-views.58b336f1.js +2 -0
  110. package/dist/node/column-views.58b336f1.js.map +1 -0
  111. package/dist/node/{column-views.465e593b.js → column-views.5908556a.js} +2 -2
  112. package/dist/node/{column-views.465e593b.js.map → column-views.5908556a.js.map} +1 -1
  113. package/dist/node/column-views.5c02450b.js +2 -0
  114. package/dist/node/column-views.5c02450b.js.map +1 -0
  115. package/dist/node/column-views.5e73411d.js +2 -0
  116. package/dist/node/column-views.5e73411d.js.map +1 -0
  117. package/dist/node/{column-views.825d2719.js → column-views.5f3f8ccf.js} +2 -2
  118. package/dist/node/{column-views.825d2719.js.map → column-views.5f3f8ccf.js.map} +1 -1
  119. package/dist/node/{column-views.0a2596c2.js → column-views.6051e986.js} +2 -2
  120. package/dist/node/{column-views.0a2596c2.js.map → column-views.6051e986.js.map} +1 -1
  121. package/dist/node/{column-views.c921fa1a.js → column-views.64fc40d6.js} +2 -2
  122. package/dist/node/{column-views.c921fa1a.js.map → column-views.64fc40d6.js.map} +1 -1
  123. package/dist/node/{column-views.6556e004.js → column-views.66663668.js} +2 -2
  124. package/dist/node/{column-views.6556e004.js.map → column-views.66663668.js.map} +1 -1
  125. package/dist/node/{column-views.a14f0134.js → column-views.6f0cb059.js} +2 -2
  126. package/dist/node/{column-views.a14f0134.js.map → column-views.6f0cb059.js.map} +1 -1
  127. package/dist/node/{column-views.9efa91f1.js → column-views.72c3c6e7.js} +2 -2
  128. package/dist/node/{column-views.9efa91f1.js.map → column-views.72c3c6e7.js.map} +1 -1
  129. package/dist/node/column-views.74fcfb9c.js +2 -0
  130. package/dist/node/column-views.74fcfb9c.js.map +1 -0
  131. package/dist/node/{column-views.75456275.js → column-views.784bac2a.js} +2 -2
  132. package/dist/node/{column-views.75456275.js.map → column-views.784bac2a.js.map} +1 -1
  133. package/dist/node/{column-views.0f401891.js → column-views.796fc1de.js} +2 -2
  134. package/dist/node/{column-views.0f401891.js.map → column-views.796fc1de.js.map} +1 -1
  135. package/dist/node/column-views.7b6e7d82.js +2 -0
  136. package/dist/node/column-views.7b6e7d82.js.map +1 -0
  137. package/dist/node/{column-views.7c322202.js → column-views.7e895e1a.js} +2 -2
  138. package/dist/node/{column-views.7c322202.js.map → column-views.7e895e1a.js.map} +1 -1
  139. package/dist/node/column-views.87d31e0a.js +2 -0
  140. package/dist/node/column-views.87d31e0a.js.map +1 -0
  141. package/dist/node/{column-views.59ce629d.js → column-views.94c8ce3e.js} +2 -2
  142. package/dist/node/{column-views.59ce629d.js.map → column-views.94c8ce3e.js.map} +1 -1
  143. package/dist/node/{column-views.48a3cfa3.js → column-views.951e3e9a.js} +2 -2
  144. package/dist/node/{column-views.48a3cfa3.js.map → column-views.951e3e9a.js.map} +1 -1
  145. package/dist/node/column-views.a0969fc8.js +2 -0
  146. package/dist/node/column-views.a0969fc8.js.map +1 -0
  147. package/dist/node/{column-views.67e7a200.js → column-views.b6613ab0.js} +2 -2
  148. package/dist/node/{column-views.67e7a200.js.map → column-views.b6613ab0.js.map} +1 -1
  149. package/dist/node/{column-views.d529d3aa.js → column-views.b762c9a8.js} +2 -2
  150. package/dist/node/{column-views.d529d3aa.js.map → column-views.b762c9a8.js.map} +1 -1
  151. package/dist/node/{column-views.e153680b.js → column-views.bdf05ed4.js} +2 -2
  152. package/dist/node/{column-views.e153680b.js.map → column-views.bdf05ed4.js.map} +1 -1
  153. package/dist/node/{column-views.fafbc028.js → column-views.d30231cb.js} +2 -2
  154. package/dist/node/{column-views.fafbc028.js.map → column-views.d30231cb.js.map} +1 -1
  155. package/dist/node/column-views.d86e6c7d.js +2 -0
  156. package/dist/node/column-views.d86e6c7d.js.map +1 -0
  157. package/dist/node/column-views.db3377d8.js +2 -0
  158. package/dist/node/column-views.db3377d8.js.map +1 -0
  159. package/dist/node/{column-views.2fe8feb9.js → column-views.f4063bc8.js} +2 -2
  160. package/dist/node/{column-views.2fe8feb9.js.map → column-views.f4063bc8.js.map} +1 -1
  161. package/dist/node/{column-views.3d0c2bea.js → column-views.f93b4175.js} +2 -2
  162. package/dist/node/{column-views.3d0c2bea.js.map → column-views.f93b4175.js.map} +1 -1
  163. package/dist/node/{column-views.cd54f1b1.js → column-views.fa5ee6de.js} +2 -2
  164. package/dist/node/{column-views.cd54f1b1.js.map → column-views.fa5ee6de.js.map} +1 -1
  165. package/dist/node/{column-views.08bef1f6.js → column-views.fe7a5452.js} +2 -2
  166. package/dist/node/{column-views.08bef1f6.js.map → column-views.fe7a5452.js.map} +1 -1
  167. package/dist/node/column-views.ffbffec7.js +2 -0
  168. package/dist/node/column-views.ffbffec7.js.map +1 -0
  169. package/dist/node/index.js +1 -1
  170. package/dist/node/index.js.map +1 -1
  171. package/package.json +4 -2
  172. package/src/age-model-overlay.ts +4 -2
  173. package/src/column.ts +18 -41
  174. package/src/correlation-chart/main.ts +24 -13
  175. package/src/correlation-chart/stories/correlation-chart.stories.ts +38 -17
  176. package/src/data-provider/base.ts +22 -0
  177. package/src/data-provider/core.ts +89 -0
  178. package/src/data-provider/fetch.ts +35 -10
  179. package/src/data-provider/store.ts +79 -88
  180. package/src/data-provider/unit-selection.ts +265 -159
  181. package/src/maps/column-correlation/core.ts +1 -1
  182. package/src/maps/column-correlation/state.ts +16 -1
  183. package/src/unit-details/panel.ts +44 -26
  184. package/src/unit-details/popover.ts +21 -9
  185. package/dist/esm/column-views.04636815.js +0 -15
  186. package/dist/esm/column-views.0a010643.js.map +0 -1
  187. package/dist/esm/column-views.4b259f9e.js +0 -136
  188. package/dist/esm/column-views.4b259f9e.js.map +0 -1
  189. package/dist/esm/column-views.6f96d1c3.js.map +0 -1
  190. package/dist/esm/column-views.8c296309.js.map +0 -1
  191. package/dist/esm/column-views.9d51a5ab.js.map +0 -1
  192. package/dist/esm/column-views.ad2fe46c.js.map +0 -1
  193. package/dist/esm/column-views.b9265595.js.map +0 -1
  194. package/dist/esm/column-views.c44febd0.js +0 -169
  195. package/dist/esm/column-views.c44febd0.js.map +0 -1
  196. package/dist/esm/column-views.ce3a3ac8.js.map +0 -1
  197. package/dist/esm/column-views.e075af15.js.map +0 -1
  198. package/dist/esm/column-views.e3f0cd7b.js.map +0 -1
  199. package/dist/node/column-views.0beae29d.js +0 -2
  200. package/dist/node/column-views.0beae29d.js.map +0 -1
  201. package/dist/node/column-views.46efb749.js +0 -2
  202. package/dist/node/column-views.46efb749.js.map +0 -1
  203. package/dist/node/column-views.7425d37b.js +0 -2
  204. package/dist/node/column-views.7425d37b.js.map +0 -1
  205. package/dist/node/column-views.868c04dc.js +0 -2
  206. package/dist/node/column-views.868c04dc.js.map +0 -1
  207. package/dist/node/column-views.8e846c57.js +0 -2
  208. package/dist/node/column-views.8e846c57.js.map +0 -1
  209. package/dist/node/column-views.c007152b.js +0 -2
  210. package/dist/node/column-views.c007152b.js.map +0 -1
  211. package/dist/node/column-views.d818b387.js +0 -2
  212. package/dist/node/column-views.d818b387.js.map +0 -1
  213. package/dist/node/column-views.dfe9a971.js +0 -2
  214. package/dist/node/column-views.dfe9a971.js.map +0 -1
  215. package/dist/node/column-views.e264a767.js +0 -2
  216. package/dist/node/column-views.e264a767.js.map +0 -1
  217. package/dist/node/column-views.ed2fd268.js +0 -2
  218. package/dist/node/column-views.ed2fd268.js.map +0 -1
  219. package/dist/node/column-views.fee4e65d.js +0 -2
  220. package/dist/node/column-views.fee4e65d.js.map +0 -1
@@ -1,55 +1,27 @@
1
- import { BaseUnit } from "@macrostrat/api-types";
2
- import h from "@macrostrat/hyper";
1
+ import { BaseUnit, UnitLong } from "@macrostrat/api-types";
3
2
  import { useKeyHandler } from "@macrostrat/ui-components";
4
- import {
5
- createContext,
6
- useContext,
7
- useEffect,
8
- useState,
9
- ReactNode,
10
- RefObject,
11
- useRef,
12
- useCallback,
13
- } from "react";
14
- import { createStore, StoreApi, useStore } from "zustand";
3
+ import { useEffect, useRef, useCallback, useMemo } from "react";
15
4
  import type { RectBounds, IUnit } from "../units/types";
5
+ import { atom } from "jotai";
6
+ import { columnUnitsMapAtom, scope } from "./core";
7
+ import { ColumnData } from "@macrostrat/column-views";
8
+ import {
9
+ AgeRangeQuantifiedDifference,
10
+ ageRangeQuantifiedDifference,
11
+ AgeRangeRelationship,
12
+ } from "@macrostrat/stratigraphy-utils";
16
13
 
17
14
  type UnitSelectDispatch = (
18
15
  unit: number | BaseUnit | null,
19
16
  target: HTMLElement | null,
20
- event: Event | null,
21
17
  ) => void;
22
18
 
23
- const UnitSelectionContext = createContext<StoreApi<UnitSelectionStore>>(null);
24
-
25
19
  export function useUnitSelectionDispatch(): UnitSelectDispatch {
26
- const store = useContext(UnitSelectionContext);
27
- if (store == null) {
28
- return () => {};
29
- }
30
- return useStore(store, (state) => state.onUnitSelected);
31
- }
32
-
33
- export function useUnitSelectionStore<T>(
34
- selector: (state: UnitSelectionStore) => T,
35
- ): T {
36
- const store = useContext(UnitSelectionContext);
37
- if (store == null) {
38
- return null;
39
- }
40
- return useStore(store, selector);
20
+ return scope.useSetAtom(selectedUnitAtom);
41
21
  }
42
22
 
43
23
  export function useSelectedUnit() {
44
- return useUnitSelectionStore((state) => state.selectedUnitData);
45
- }
46
-
47
- interface UnitSelectionStore {
48
- selectedUnit: number | null;
49
- selectedUnitData: BaseUnit | null;
50
- overlayPosition: RectBounds | null;
51
- onUnitSelected: UnitSelectDispatch;
52
- setSelectedUnit: (unit: number | null) => void;
24
+ return scope.useAtomValue(selectedUnitAtom);
53
25
  }
54
26
 
55
27
  export interface ColumnClickData {
@@ -60,134 +32,141 @@ export interface ColumnClickData {
60
32
  // Room for boundary IDs eventually
61
33
  }
62
34
 
63
- export function UnitSelectionProvider<T extends BaseUnit>(props: {
64
- children: ReactNode;
65
- columnRef?: RefObject<HTMLElement>;
66
- units: T[];
67
- selectedUnit: number | null;
35
+ export interface UnitSelectionCallbacks {
36
+ // It's sort of unfortunate that we need to pass in the column ref here
68
37
  onClickedColumn?: (columnClickData: ColumnClickData, event: Event) => void;
69
- onUnitSelected?: (unitID: number | null, unit: T | null) => void;
70
- }) {
71
- const [store] = useState(() =>
72
- createStore<UnitSelectionStore>((set) => ({
73
- selectedUnit: null,
74
- selectedUnitData: null,
75
- overlayPosition: null,
76
- setSelectedUnit(selectedUnit: number | null | BaseUnit) {
77
- if (selectedUnit == null) {
78
- set({ selectedUnit: null, selectedUnitData: null });
79
- return;
80
- // If it's a number, set the selected unit
81
- } else if (typeof selectedUnit === "number") {
82
- set({ selectedUnit, selectedUnitData: null });
83
- } else if ("unit_id" in selectedUnit) {
84
- set({
85
- selectedUnit: selectedUnit.unit_id,
86
- selectedUnitData: selectedUnit,
87
- });
88
- }
89
- },
90
- onUnitSelected: (
91
- input: number | T | null,
92
- target: HTMLElement = null,
93
- event: PointerEvent = null,
94
- ) => {
95
- if (input == null) {
96
- props.onUnitSelected?.(null, null);
97
- return set({
98
- selectedUnit: null,
99
- selectedUnitData: null,
100
- overlayPosition: null,
101
- });
102
- }
103
-
104
- let unit: T | null = null;
105
- if (typeof input === "number") {
106
- unit = props.units.find((u) => u.unit_id === input) || null;
107
- } else {
108
- unit = input;
109
- }
110
-
111
- const el = props.columnRef?.current;
112
- let overlayPosition = null;
113
-
114
- /** This is not the natural place to get positions within the column,
115
- * but it will work for now.
116
- */
117
- if (props.onClickedColumn) {
118
- // Infer height from top and bottom height of unit (because that's passed back with the call)
119
- //const py = event.y;
120
- //const [bottom, top] = getUnitHeightRange(unit, axisType);
121
- //const height = Math.abs(bottom - top);
122
-
123
- /** Ideally this would be defined at the column level */
124
- const columnClickData: ColumnClickData = {
125
- unitID: unit?.unit_id,
126
- unit,
127
- target,
128
- height: el?.getBoundingClientRect().height || 0,
129
- };
130
- props.onClickedColumn(columnClickData, event);
131
- }
132
-
133
- if (unit != null && el != null && target != null) {
134
- const rect = el.getBoundingClientRect();
135
- const targetRect = target.getBoundingClientRect();
136
-
137
- overlayPosition = {
138
- x: targetRect.left - rect.left,
139
- y: targetRect.top - rect.top,
140
- width: targetRect.width,
141
- height: targetRect.height,
142
- };
143
- }
144
- props.onUnitSelected?.(unit?.unit_id, unit);
145
-
146
- return set({
147
- selectedUnit: unit?.unit_id,
148
- selectedUnitData: unit,
149
- overlayPosition,
150
- });
151
- },
152
- })),
153
- );
38
+ onUnitSelected?: <T extends BaseUnit>(
39
+ unitID: number | null,
40
+ unit: T | null,
41
+ ) => void;
42
+ }
154
43
 
155
- const { units, selectedUnit } = props;
44
+ export const allowUnitSelectionAtom = atom<boolean>(true);
156
45
 
157
- useEffect(() => {
158
- // Synchronize store with provided props
159
- if (selectedUnit != null) {
160
- const unitData = units?.find((u) => u.unit_id === selectedUnit);
161
- store.setState({ selectedUnit, selectedUnitData: unitData });
162
- } else {
163
- store.setState({ selectedUnit: null, selectedUnitData: null });
46
+ export const selectedUnitIDAtom = atom<number | null>();
47
+
48
+ const overlayPositionAtom = atom<RectBounds | null>();
49
+
50
+ const columnRefAtom = atom<{ current: HTMLElement | null }>({ current: null });
51
+
52
+ export function useColumnRef() {
53
+ return scope.useAtomValue(columnRefAtom);
54
+ }
55
+
56
+ const selectedUnitAtom = atom(
57
+ (get) => {
58
+ if (!get(allowUnitSelectionAtom)) return null;
59
+
60
+ const unitID = get(selectedUnitIDAtom);
61
+ if (unitID == null) return null;
62
+ const unitsMap = get(columnUnitsMapAtom);
63
+ return unitsMap?.get(unitID) || null;
64
+ },
65
+ (
66
+ get,
67
+ set,
68
+ selectedUnit: number | BaseUnit | null,
69
+ target: HTMLElement | null = null,
70
+ ): BaseUnit | null => {
71
+ if (!get(allowUnitSelectionAtom)) {
72
+ throw new Error("Unit selection is disabled.");
164
73
  }
165
- }, [selectedUnit, units]);
166
74
 
167
- return h(UnitSelectionContext.Provider, { value: store }, props.children);
75
+ let unitID: number | null;
76
+ let unit: BaseUnit | null = null;
77
+ if (selectedUnit == null) {
78
+ unitID = null;
79
+ } else if (typeof selectedUnit === "number") {
80
+ unitID = selectedUnit;
81
+ } else if ("unit_id" in selectedUnit) {
82
+ unitID = selectedUnit.unit_id;
83
+ }
84
+
85
+ if (unitID != null) {
86
+ // Verify that the unit exists in the current colum, else throw
87
+ const unitsMap = get(columnUnitsMapAtom);
88
+ if (!unitsMap?.has(unitID)) {
89
+ throw new Error(
90
+ `Unit with ID ${unitID} not found in current column units.`,
91
+ );
92
+ }
93
+ unit = unitsMap.get(unitID) ?? null;
94
+ }
95
+
96
+ let overlayPosition: RectBounds | null = null;
97
+
98
+ const columnEl = get(columnRefAtom)?.current;
99
+
100
+ if (unit != null && columnEl != null && target != null) {
101
+ const rect = columnEl.getBoundingClientRect();
102
+ const targetRect = target.getBoundingClientRect();
103
+ overlayPosition = {
104
+ x: targetRect.left - rect.left,
105
+ y: targetRect.top - rect.top,
106
+ width: targetRect.width,
107
+ height: targetRect.height,
108
+ };
109
+ }
110
+
111
+ set(selectedUnitIDAtom, unitID);
112
+ set(overlayPositionAtom, overlayPosition);
113
+
114
+ return unit;
115
+ },
116
+ );
117
+
118
+ export function useAtomOverlayPosition() {
119
+ return scope.useAtomValue(overlayPositionAtom);
168
120
  }
169
121
 
122
+ export function UnitSelectionCallbackManager({
123
+ onUnitSelected,
124
+ }: UnitSelectionCallbacks) {
125
+ const selectedUnit = scope.useAtomValue(selectedUnitAtom);
126
+ useEffect(() => {
127
+ onUnitSelected?.(selectedUnit?.unit_id ?? null, selectedUnit ?? null);
128
+ }, [selectedUnit?.unit_id]);
129
+ return null;
130
+ }
131
+
132
+ /** TODO: figure out if onClickedColumn is still needed
133
+ /** This is not the natural place to get positions within the column,
134
+ * but it will work for now.
135
+ if (props.onClickedColumn) {
136
+ // Infer height from top and bottom height of unit (because that's passed back with the call)
137
+ //const py = event.y;
138
+ //const [bottom, top] = getUnitHeightRange(unit, axisType);
139
+ //const height = Math.abs(bottom - top);
140
+
141
+ const columnClickData: ColumnClickData = {
142
+ unitID: unit?.unit_id,
143
+ unit,
144
+ target,
145
+ height: el?.getBoundingClientRect().height || 0,
146
+ };
147
+ props.onClickedColumn(columnClickData, event);
148
+ }*/
149
+
170
150
  export function useUnitSelectionTarget(
171
151
  unit: IUnit,
172
152
  ): [React.RefObject<HTMLElement>, boolean, (evt: Event) => void] {
173
153
  const ref = useRef<HTMLElement>();
174
154
  const selectedUnit = useSelectedUnit();
175
- const onSelectUnit = useUnitSelectionStore((state) => state.onUnitSelected);
155
+ const selectUnit = useUnitSelectionDispatch();
176
156
  const selected = selectedUnit?.unit_id == unit.unit_id;
177
157
 
178
158
  const onClick = useCallback(
179
159
  (evt: Event) => {
180
- if (onSelectUnit == null) return;
181
- onSelectUnit(unit, ref.current, evt);
160
+ selectUnit?.(unit, ref.current);
182
161
  evt.stopPropagation();
183
162
  },
184
- [unit, onSelectUnit],
163
+ [unit, selectUnit],
185
164
  );
186
165
 
187
166
  useEffect(() => {
188
- if (!selected || onSelectUnit == null) return;
167
+ if (!selected || selectUnit == null) return;
189
168
  // In case we haven't set the position of the unit (if we don't have a target), set the selected unit
190
- onSelectUnit(unit, ref.current, null);
169
+ selectUnit(unit, ref.current);
191
170
 
192
171
  // Scroll the unit into view
193
172
  ref.current?.scrollIntoView({
@@ -195,34 +174,161 @@ export function useUnitSelectionTarget(
195
174
  block: "center",
196
175
  inline: "nearest",
197
176
  });
198
- }, [selected, onSelectUnit]);
177
+ }, [selected, selectUnit]);
199
178
 
200
179
  return [ref, selected, onClick];
201
180
  }
202
181
 
203
- export function UnitKeyboardNavigation<T extends BaseUnit>({
182
+ export function UnitKeyboardNavigation({
183
+ columnData,
204
184
  units,
185
+ allowHorizontalNavigation,
205
186
  }: {
206
- units: T[];
187
+ columnData?: ColumnData[];
188
+ units?: UnitLong[];
189
+ allowHorizontalNavigation?: boolean;
207
190
  }) {
208
- const selectedUnit = useSelectedUnit();
209
- const selectUnit = useUnitSelectionDispatch();
191
+ if (units == null && columnData == null) {
192
+ throw new Error("Either units or columnData must be provided.");
193
+ }
194
+
195
+ let _columnData: ColumnData[] = useMemo(() => {
196
+ if (columnData != null) return columnData;
197
+ // Build column data from units
198
+ const colMap = new Map<number, UnitLong[]>();
199
+ for (const unit of units!) {
200
+ if (!colMap.has(unit.col_id)) {
201
+ colMap.set(unit.col_id, []);
202
+ }
203
+ colMap.get(unit.col_id)!.push(unit);
204
+ }
205
+ const cols: ColumnData[] = [];
206
+ for (const [colID, units] of colMap) {
207
+ cols.push({ columnID: colID, units });
208
+ }
209
+ // Sort columns by columnID
210
+ cols.sort((a, b) => a.columnID - b.columnID);
211
+ return cols;
212
+ }, [columnData, units]);
210
213
 
211
- const ix = units.findIndex((unit) => unit.unit_id === selectedUnit?.unit_id);
214
+ // Default to allowing horizontal navigation only if columnData is provided
215
+ const _allowHorizontalNavigation =
216
+ allowHorizontalNavigation ?? columnData != null;
212
217
 
213
- const keyMap = {
214
- 38: ix - 1,
215
- 40: ix + 1,
218
+ const selectedUnit = useSelectedUnit() as UnitLong | null;
219
+ const selectUnit = useUnitSelectionDispatch();
220
+
221
+ const keyMap: Record<number, Direction> = {
222
+ 38: "up",
223
+ 40: "down",
216
224
  };
225
+ if (_allowHorizontalNavigation) {
226
+ keyMap[37] = "left";
227
+ keyMap[39] = "right";
228
+ }
217
229
 
218
230
  useKeyHandler(
219
231
  (event) => {
220
- const nextIx = keyMap[event.keyCode];
221
- if (nextIx == null || nextIx < 0 || nextIx >= units.length) return;
222
- selectUnit(units[nextIx], null, null);
232
+ const direction = keyMap[event.keyCode];
233
+ if (direction == null) return;
234
+ const nextUnit = getBestUnit(_columnData, selectedUnit, direction);
235
+ if (nextUnit == null) return;
236
+ selectUnit(nextUnit, null);
223
237
  event.stopPropagation();
224
238
  },
225
- [units, ix],
239
+ [_columnData, selectedUnit],
240
+ );
241
+ return null;
242
+ }
243
+
244
+ type Direction = "up" | "down" | "left" | "right";
245
+
246
+ function getBestUnit(
247
+ columnData: ColumnData[],
248
+ targetUnit: UnitLong,
249
+ direction: Direction,
250
+ ): UnitLong | null {
251
+ const thisColIndex = columnData.findIndex(
252
+ (col) => col.columnID === targetUnit.col_id,
226
253
  );
254
+ if (thisColIndex === -1) return null;
255
+
256
+ // If up or down, stay in the same column
257
+ if (direction === "up" || direction === "down") {
258
+ const units = columnData[thisColIndex].units;
259
+ const ix = units.findIndex((unit) => unit.unit_id === targetUnit.unit_id);
260
+ if (ix === -1) return null;
261
+ if (direction === "up") {
262
+ return units[ix - 1] || null;
263
+ } else {
264
+ return units[ix + 1] || null;
265
+ }
266
+ }
267
+
268
+ // If left or right, move to adjacent column
269
+ let adjacentColIndex: number;
270
+ if (direction === "left") {
271
+ adjacentColIndex =
272
+ (thisColIndex - 1 + columnData.length) % columnData.length;
273
+ } else {
274
+ adjacentColIndex = (thisColIndex + 1) % columnData.length;
275
+ }
276
+
277
+ const adjacentColUnits = columnData[adjacentColIndex].units;
278
+ return getMostOverlappingUnit(targetUnit, adjacentColUnits);
279
+ }
280
+
281
+ type UnitAgeRangeRelationship = AgeRangeQuantifiedDifference & {
282
+ unit: UnitLong;
283
+ };
284
+
285
+ function getMostOverlappingUnit(
286
+ targetUnit: UnitLong,
287
+ candidateUnits: UnitLong[],
288
+ ): UnitLong | null {
289
+ const targetAgeRange = [targetUnit.b_age, targetUnit.t_age];
290
+
291
+ const overlaps: UnitAgeRangeRelationship[] = [];
292
+ for (const candidate of candidateUnits) {
293
+ const candidateAgeRange = [candidate.b_age, candidate.t_age];
294
+
295
+ const rel = ageRangeQuantifiedDifference(targetAgeRange, candidateAgeRange);
296
+ if (rel.type === AgeRangeRelationship.Identical) {
297
+ return candidate;
298
+ }
299
+ overlaps.push({ ...rel, unit: candidate });
300
+ }
301
+
302
+ let bestOverlaps = overlaps.filter(
303
+ (d) => d.type === AgeRangeRelationship.Containing,
304
+ );
305
+ bestOverlaps.sort((a, b) => b.overlap - a.overlap);
306
+ if (bestOverlaps.length > 0) {
307
+ return bestOverlaps[0].unit;
308
+ }
309
+
310
+ bestOverlaps = overlaps.filter(
311
+ (d) => d.type === AgeRangeRelationship.Contained,
312
+ );
313
+ bestOverlaps.sort((a, b) => b.overlap - a.overlap);
314
+ if (bestOverlaps.length > 0) {
315
+ return bestOverlaps[0].unit;
316
+ }
317
+
318
+ bestOverlaps = overlaps.filter(
319
+ (d) => d.type === AgeRangeRelationship.PartialOverlap,
320
+ );
321
+ bestOverlaps.sort((a, b) => b.overlap - a.overlap);
322
+ if (bestOverlaps.length > 0) {
323
+ return bestOverlaps[0].unit;
324
+ }
325
+
326
+ bestOverlaps = overlaps.filter(
327
+ (d) => d.type === AgeRangeRelationship.Disjoint,
328
+ );
329
+ bestOverlaps.sort((a, b) => a.distance - b.distance);
330
+ if (bestOverlaps.length > 0) {
331
+ return bestOverlaps[0].unit;
332
+ }
227
333
  return null;
228
334
  }
@@ -21,7 +21,7 @@ export interface CorrelationMapProps extends InsetMapProps {
21
21
  }
22
22
 
23
23
  export function ColumnCorrelationMap(props: CorrelationMapProps) {
24
- const { padding = 50, children, columnColor, ...rest } = props;
24
+ const { padding = 50, children, columnColor, projectID, ...rest } = props;
25
25
 
26
26
  const overlayStyles: Partial<Style>[] = useMemo(() => {
27
27
  return [
@@ -16,6 +16,8 @@ import {
16
16
  import h from "@macrostrat/hyper";
17
17
  import { createComputed } from "zustand-computed";
18
18
  import { useMacrostratColumns } from "../../data-provider/base";
19
+ import { buffer } from "@turf/buffer";
20
+ import { booleanPointInPolygon } from "@turf/boolean-point-in-polygon";
19
21
 
20
22
  export interface CorrelationMapInput {
21
23
  columns: ColumnGeoJSONRecord[];
@@ -65,6 +67,7 @@ export function ColumnCorrelationProvider({
65
67
  computed((set, get): CorrelationMapStore => {
66
68
  return {
67
69
  focusedLine,
70
+ projectID,
68
71
  columns: null,
69
72
  onClickMap(event: mapboxgl.MapMouseEvent, point: Point) {
70
73
  const state = get();
@@ -139,7 +142,17 @@ function computeIntersectingColumns(
139
142
  return [];
140
143
  }
141
144
 
142
- return columns.filter((col) => {
145
+ /** eODP-focused process. Find buffers around line and then find columns intersecting that buffer */
146
+ const bufferedLine = buffer(line, 1, { units: "degrees" });
147
+
148
+ const nearbyPoints = columns.filter((col) => {
149
+ if (col.geometry?.type != "Point") {
150
+ return false;
151
+ }
152
+ return booleanPointInPolygon(col.geometry, bufferedLine);
153
+ });
154
+
155
+ const intersectingPolygons = columns.filter((col) => {
143
156
  const poly = col.geometry;
144
157
 
145
158
  // Some in-process datasets seem to have null geometries
@@ -149,6 +162,8 @@ function computeIntersectingColumns(
149
162
  const intersection = lineIntersect(line, poly);
150
163
  return intersection.features.length > 0;
151
164
  });
165
+
166
+ return [...intersectingPolygons, ...nearbyPoints];
152
167
  }
153
168
 
154
169
  interface FocusedColumnGeoJSONRecord extends ColumnGeoJSONRecord {
@@ -16,6 +16,7 @@ import {
16
16
  } from "@macrostrat/data-components";
17
17
  import {
18
18
  useColumnUnitsMap,
19
+ useMacrostratColumnInfo,
19
20
  useMacrostratData,
20
21
  useMacrostratDefs,
21
22
  } from "../data-provider";
@@ -25,13 +26,24 @@ import type {
25
26
  UnitLongFull,
26
27
  Lithology,
27
28
  Interval,
28
- StratUnit,
29
29
  } from "@macrostrat/api-types";
30
30
  import { defaultNameFunction } from "../units/names";
31
31
  import classNames from "classnames";
32
32
 
33
33
  const h = hyper.styled(styles);
34
34
 
35
+ export interface UnitDetailsPanelProps {
36
+ unit: any;
37
+ onClose?: any;
38
+ className?: string;
39
+ actions?: ReactNode;
40
+ hiddenActions?: ReactNode;
41
+ features?: Set<UnitDetailsFeature>;
42
+ lithologyFeatures?: Set<LithologyTagFeature>;
43
+ onSelectUnit?: (unitID: number) => void;
44
+ onClickItem?: MacrostratItemClickHandler;
45
+ }
46
+
35
47
  export function UnitDetailsPanel({
36
48
  unit,
37
49
  onClose,
@@ -46,21 +58,8 @@ export function UnitDetailsPanel({
46
58
  actions,
47
59
  hiddenActions = null,
48
60
  onSelectUnit,
49
- columnUnits,
50
61
  onClickItem,
51
- }: {
52
- unit: any;
53
- onClose?: any;
54
- showLithologyProportions?: boolean;
55
- className?: string;
56
- actions?: ReactNode;
57
- hiddenActions?: ReactNode;
58
- features?: Set<UnitDetailsFeature>;
59
- lithologyFeatures?: Set<LithologyTagFeature>;
60
- columnUnits?: UnitLong[];
61
- onSelectUnit?: (unitID: number) => void;
62
- onClickItem?: MacrostratItemClickHandler;
63
- }) {
62
+ }: UnitDetailsPanelProps) {
64
63
  const [showJSON, setShowJSON] = useState(false);
65
64
 
66
65
  let content = null;
@@ -148,6 +147,7 @@ export enum UnitDetailsFeature {
148
147
  OutcropType = "outcrop-type",
149
148
  JSONToggle = "json-toggle",
150
149
  DepthRange = "depth-range",
150
+ ColumnName = "column-name",
151
151
  }
152
152
 
153
153
  export type MacrostratItemClickHandler = (
@@ -163,7 +163,6 @@ export type MacrostratItemClickHandler = (
163
163
  function UnitDetailsContent({
164
164
  unit,
165
165
  onSelectUnit,
166
- columnUnits,
167
166
  lithologyFeatures = new Set([
168
167
  LithologyTagFeature.Proportion,
169
168
  LithologyTagFeature.Attributes,
@@ -177,7 +176,6 @@ function UnitDetailsContent({
177
176
  }: {
178
177
  unit: UnitLong;
179
178
  onSelectUnit?: (unitID: number) => void;
180
- columnUnits?: UnitLong[];
181
179
  lithologyFeatures?: Set<LithologyTagFeature>;
182
180
  features?: Set<UnitDetailsFeature>;
183
181
  onClickItem?: MacrostratItemClickHandler;
@@ -189,15 +187,6 @@ function UnitDetailsContent({
189
187
  const environments = enhanceEnvironments(unit.environ, envMap);
190
188
  const lithologies = enhanceLithologies(unit.lith ?? [], lithMap);
191
189
 
192
- // Create a lookup table of units if provided
193
- const columnUnitsMap = useMemo(() => {
194
- const map = new Map<number, UnitLong>();
195
- for (const colUnit of columnUnits ?? []) {
196
- map.set(colUnit.unit_id, colUnit);
197
- }
198
- return map;
199
- }, [columnUnits]);
200
-
201
190
  let outcropField = null;
202
191
  if (features.has(UnitDetailsFeature.OutcropType)) {
203
192
  // Determine outcrop type
@@ -250,6 +239,9 @@ function UnitDetailsContent({
250
239
  }
251
240
 
252
241
  return h("div.unit-details-content", [
242
+ h.if(features.has(UnitDetailsFeature.ColumnName))(ColumnNameField, {
243
+ col_id: unit.col_id,
244
+ }),
253
245
  thicknessOrHeightRange,
254
246
  h(LithologyList, {
255
247
  label: "Lithology",
@@ -299,6 +291,32 @@ function UnitDetailsContent({
299
291
  ]);
300
292
  }
301
293
 
294
+ function ColumnNameField({
295
+ col_id,
296
+ showIdentifier = false,
297
+ }: {
298
+ col_id: number;
299
+ showIdentifier?: boolean;
300
+ }) {
301
+ const colData = useMacrostratColumnInfo(col_id);
302
+ let inner: any = h(Identifier, { id: col_id });
303
+ const name = colData?.col_name;
304
+ if (name != null) {
305
+ inner = h("span.value", [
306
+ h("span.col-name", name),
307
+ h.if(showIdentifier)([" ", h(Parenthetical, inner)]),
308
+ ]);
309
+ }
310
+
311
+ return h(
312
+ DataField,
313
+ {
314
+ label: "Column",
315
+ },
316
+ inner,
317
+ );
318
+ }
319
+
302
320
  export function ReferencesField({ refs, className = null, ...rest }) {
303
321
  if (refs == null || refs.length === 0) {
304
322
  return null;