@macrostrat/column-views 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/age-axis.cjs +2 -2
  3. package/dist/age-axis.cjs.map +1 -1
  4. package/dist/age-axis.js +1 -1
  5. package/dist/column-views.css +130 -125
  6. package/dist/index.cjs +30 -19
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.js +16 -5
  9. package/dist/index.js.map +1 -1
  10. package/dist/maps/column-correlation/core.cjs +10 -30
  11. package/dist/maps/column-correlation/core.cjs.map +1 -1
  12. package/dist/maps/column-correlation/core.d.ts +1 -1
  13. package/dist/maps/column-correlation/core.js +10 -30
  14. package/dist/maps/column-correlation/core.js.map +1 -1
  15. package/dist/maps/column-navigation/mapbox/index.cjs +156 -118
  16. package/dist/maps/column-navigation/mapbox/index.cjs.map +1 -1
  17. package/dist/maps/column-navigation/mapbox/index.d.ts +23 -2
  18. package/dist/maps/column-navigation/mapbox/index.js +158 -120
  19. package/dist/maps/column-navigation/mapbox/index.js.map +1 -1
  20. package/dist/maps/column-navigation/mapbox/keyboard-navigation.cjs +7 -1
  21. package/dist/maps/column-navigation/mapbox/keyboard-navigation.cjs.map +1 -1
  22. package/dist/maps/column-navigation/mapbox/keyboard-navigation.d.ts +3 -26
  23. package/dist/maps/column-navigation/mapbox/keyboard-navigation.js +9 -3
  24. package/dist/maps/column-navigation/mapbox/keyboard-navigation.js.map +1 -1
  25. package/dist/maps/index.d.ts +2 -2
  26. package/dist/maps/{_shared/inset-map.cjs → inset-map/index.cjs} +1 -1
  27. package/dist/maps/inset-map/index.cjs.map +1 -0
  28. package/dist/maps/{_shared/inset-map.js → inset-map/index.js} +1 -1
  29. package/dist/maps/inset-map/index.js.map +1 -0
  30. package/dist/maps/layers/columns.cjs +161 -0
  31. package/dist/maps/layers/columns.cjs.map +1 -0
  32. package/dist/maps/layers/columns.d.ts +28 -0
  33. package/dist/maps/layers/columns.js +161 -0
  34. package/dist/maps/layers/columns.js.map +1 -0
  35. package/dist/maps/layers/index.d.ts +2 -0
  36. package/dist/maps/{units-overlay/index.cjs → layers/units.cjs} +41 -88
  37. package/dist/maps/layers/units.cjs.map +1 -0
  38. package/dist/maps/{units-overlay/index.d.ts → layers/units.d.ts} +1 -1
  39. package/dist/maps/{units-overlay/index.js → layers/units.js} +34 -82
  40. package/dist/maps/layers/units.js.map +1 -0
  41. package/dist/section.cjs +2 -2
  42. package/dist/section.cjs.map +1 -1
  43. package/dist/section.js +1 -1
  44. package/dist/unit-details/age-range.cjs +196 -0
  45. package/dist/unit-details/age-range.cjs.map +1 -0
  46. package/dist/unit-details/age-range.d.ts +70 -0
  47. package/dist/unit-details/age-range.js +194 -0
  48. package/dist/unit-details/age-range.js.map +1 -0
  49. package/dist/unit-details/age-range.module.sass.cjs +16 -0
  50. package/dist/unit-details/age-range.module.sass.cjs.map +1 -0
  51. package/dist/unit-details/age-range.module.sass.js +14 -0
  52. package/dist/unit-details/age-range.module.sass.js.map +1 -0
  53. package/dist/unit-details/index.d.ts +1 -0
  54. package/dist/unit-details/panel.cjs +8 -173
  55. package/dist/unit-details/panel.cjs.map +1 -1
  56. package/dist/unit-details/panel.js +3 -168
  57. package/dist/unit-details/panel.js.map +1 -1
  58. package/dist/unit-details/panel.module.sass.cjs +20 -22
  59. package/dist/unit-details/panel.module.sass.cjs.map +1 -1
  60. package/dist/unit-details/panel.module.sass.js +20 -22
  61. package/dist/unit-details/panel.module.sass.js.map +1 -1
  62. package/dist/unit-details/utils.cjs +31 -0
  63. package/dist/unit-details/utils.cjs.map +1 -0
  64. package/dist/unit-details/utils.d.ts +3 -0
  65. package/dist/unit-details/utils.js +31 -0
  66. package/dist/unit-details/utils.js.map +1 -0
  67. package/package.json +4 -4
  68. package/src/maps/column-correlation/core.ts +9 -29
  69. package/src/maps/column-navigation/mapbox/index.ts +191 -138
  70. package/src/maps/column-navigation/mapbox/keyboard-navigation.ts +17 -3
  71. package/src/maps/index.ts +2 -2
  72. package/src/maps/layers/columns.ts +210 -0
  73. package/src/maps/layers/index.ts +2 -0
  74. package/src/maps/{units-overlay/index.ts → layers/units.ts} +34 -84
  75. package/src/maps/unit-map.stories.ts +27 -12
  76. package/src/unit-details/age-range.module.sass +15 -0
  77. package/src/unit-details/age-range.ts +269 -0
  78. package/src/unit-details/index.ts +1 -0
  79. package/src/unit-details/panel.module.sass +0 -9
  80. package/src/unit-details/panel.ts +12 -226
  81. package/src/unit-details/utils.ts +33 -0
  82. package/dist/maps/_shared/index.d.ts +0 -2
  83. package/dist/maps/_shared/inset-map.cjs.map +0 -1
  84. package/dist/maps/_shared/inset-map.js.map +0 -1
  85. package/dist/maps/_shared/styles.cjs +0 -73
  86. package/dist/maps/_shared/styles.cjs.map +0 -1
  87. package/dist/maps/_shared/styles.d.ts +0 -2
  88. package/dist/maps/_shared/styles.js +0 -73
  89. package/dist/maps/_shared/styles.js.map +0 -1
  90. package/dist/maps/units-overlay/index.cjs.map +0 -1
  91. package/dist/maps/units-overlay/index.js.map +0 -1
  92. package/src/maps/_shared/index.ts +0 -2
  93. package/src/maps/_shared/styles.ts +0 -76
  94. /package/dist/maps/{_shared/inset-map.d.ts → inset-map/index.d.ts} +0 -0
  95. /package/src/maps/{_shared/inset-map.ts → inset-map/index.ts} +0 -0
  96. /package/src/maps/{_shared → inset-map}/inset-map.stories.ts +0 -0
@@ -3,7 +3,7 @@ import {
3
3
  useMacrostratColumns,
4
4
  } from "@macrostrat/data-provider";
5
5
  import { useAPIResult } from "@macrostrat/ui-components";
6
- import { useMapStyleOperator } from "@macrostrat/mapbox-react";
6
+ import { useMapStyleOperator, useOverlayStyle } from "@macrostrat/mapbox-react";
7
7
  import { FeatureCollection } from "geojson";
8
8
  import { useMemo } from "react";
9
9
  import { mergeStyles, setGeoJSON } from "@macrostrat/mapbox-utils";
@@ -15,9 +15,11 @@ import {
15
15
  loadStyleImage,
16
16
  StyleFragment,
17
17
  } from "@macrostrat/map-styles";
18
- import { asChromaColor, getCSSVariable } from "@macrostrat/color-utils";
18
+ import { asChromaColor } from "@macrostrat/color-utils";
19
19
  import { buildGeoJSONSource } from "@macrostrat/mapbox-utils";
20
20
  import pMap from "p-map";
21
+ import { BaseColumnsLayer } from "./columns";
22
+ import h from "@macrostrat/hyper";
21
23
 
22
24
  export interface UnitsOverlayProps {
23
25
  time: number;
@@ -30,6 +32,10 @@ export function MacrostratUnitsOverlay(props: UnitsOverlayProps) {
30
32
  const { time, ageSpan = 0.05, patterns = false } = props;
31
33
  const lithMap = useLithologies();
32
34
 
35
+ useOverlayStyle(() => {
36
+ return buildUnitsStyle({ patterns });
37
+ }, [patterns]);
38
+
33
39
  useMapStyleOperator(
34
40
  (map) => {
35
41
  if (!patterns) return;
@@ -60,21 +66,6 @@ export function MacrostratUnitsOverlay(props: UnitsOverlayProps) {
60
66
  const units: UnitLong[] = useAPIResult("/units", params);
61
67
  const columns = useMacrostratColumns(params.project, false);
62
68
 
63
- // Set up basic columns layer
64
- useMapStyleOperator(
65
- (map) => {
66
- if (columns == null) {
67
- return;
68
- }
69
- const data: FeatureCollection = {
70
- type: "FeatureCollection",
71
- features: columns,
72
- };
73
- setGeoJSON(map, "columns", data);
74
- },
75
- [columns],
76
- );
77
-
78
69
  useMapStyleOperator(
79
70
  (map) => {
80
71
  if (units == null || columns == null || lithMap == null) {
@@ -99,7 +90,30 @@ export function MacrostratUnitsOverlay(props: UnitsOverlayProps) {
99
90
  [units, lithMap, columns, patterns],
100
91
  );
101
92
 
102
- return null;
93
+ const isHovered = ["boolean", ["feature-state", "hover"], false];
94
+ const isShown = ["boolean", ["feature-state", "shown"], false];
95
+
96
+ const opacity = [
97
+ "case",
98
+ ["all", isHovered, isShown],
99
+ 0.9,
100
+ isShown,
101
+ 0.8,
102
+ isHovered,
103
+ 0.3,
104
+ 0.0,
105
+ ];
106
+
107
+ return h([
108
+ h(BaseColumnsLayer, {
109
+ enabled: true,
110
+ columns,
111
+ opacity,
112
+ lineOpacity: 0.2,
113
+ lineColor: "lightgray",
114
+ color: "transparent",
115
+ }),
116
+ ]);
103
117
  }
104
118
 
105
119
  function handleUnitsLayerUpdate(
@@ -238,77 +252,13 @@ type UnitsStyleOptions = {
238
252
  };
239
253
 
240
254
  export function buildUnitsStyle(opts: UnitsStyleOptions = {}): StyleFragment {
241
- const { color = null, patterns = false } = opts;
242
-
243
- let columnBaseColor: any =
244
- color ?? getCSSVariable("--text-subtle-color", "black");
245
- const columnSelectedColor = getCSSVariable("--selection-color", "purple");
255
+ const { patterns = false } = opts;
246
256
 
247
257
  // If color is in the feature state or geojson properties, use that as second choice
248
258
 
249
- let columnColor: any = [
250
- "coalesce",
251
- ["feature-state", "color"],
252
- ["get", "color"],
253
- columnBaseColor,
254
- ];
255
-
256
259
  const baseStyle: StyleFragment = {
257
- sources: {
258
- columns: buildGeoJSONSource(),
259
- },
260
+ sources: {},
260
261
  version: 8,
261
- layers: [
262
- {
263
- id: "columns-fill",
264
- type: "fill",
265
- source: "columns",
266
- paint: {
267
- "fill-color": columnColor,
268
- "fill-opacity": [
269
- "case",
270
- ["boolean", ["feature-state", "shown"], false],
271
- 0.8,
272
- ["boolean", ["feature-state", "hover"], false],
273
- 0.3,
274
- 0.1,
275
- ],
276
- },
277
- },
278
- {
279
- id: "columns-line",
280
- type: "line",
281
- source: "columns",
282
- paint: {
283
- "line-color": columnColor,
284
- "line-width": 1,
285
- "line-opacity": 0.5,
286
- },
287
- },
288
- {
289
- id: "columns-points",
290
- type: "circle",
291
- source: "columns",
292
- paint: {
293
- "circle-radius": 4,
294
- "circle-color": [
295
- "case",
296
- ["boolean", ["feature-state", "shown"], false],
297
- columnColor,
298
- columnBaseColor,
299
- ],
300
- "circle-opacity": [
301
- "case",
302
- ["boolean", ["feature-state", "shown"], false],
303
- 0.8,
304
- ["boolean", ["feature-state", "hover"], false],
305
- 0.3,
306
- 0.1,
307
- ],
308
- },
309
- filter: ["==", "$type", "Point"],
310
- },
311
- ],
312
262
  };
313
263
 
314
264
  if (!patterns) {
@@ -1,9 +1,13 @@
1
1
  import h from "@macrostrat/hyper";
2
2
  import { Meta } from "@storybook/react-vite";
3
- import { InsetMap } from "./_shared";
3
+ import { InsetMap } from "./inset-map";
4
4
  import { MacrostratDataProvider } from "@macrostrat/data-provider";
5
- import { useMemo } from "react";
6
- import { buildUnitsStyle, MacrostratUnitsOverlay } from "./units-overlay";
5
+ import { MacrostratUnitsOverlay } from "./layers/units";
6
+ import {
7
+ ColumnHoverInteraction,
8
+ ColumnSelectionManager,
9
+ } from "./column-navigation";
10
+ import { useState } from "react";
7
11
 
8
12
  // @ts-ignore
9
13
  const mapboxToken = import.meta.env.VITE_MAPBOX_API_TOKEN;
@@ -21,13 +25,7 @@ export default {
21
25
  } as Meta<typeof InsetMap>;
22
26
 
23
27
  function UnitMapComponent(props) {
24
- const overlayStyles = useMemo(() => {
25
- let styles: any[] = [
26
- buildUnitsStyle({ color: "#444", patterns: props.patterns }),
27
- ];
28
- return styles;
29
- }, []);
30
-
28
+ const { children, ...rest } = props;
31
29
  return h(
32
30
  MacrostratDataProvider,
33
31
  {
@@ -40,9 +38,8 @@ function UnitMapComponent(props) {
40
38
  {
41
39
  style: { width: "800px", height: "600px" },
42
40
  accessToken: mapboxToken,
43
- overlayStyles,
44
41
  },
45
- h(MacrostratUnitsOverlay, props),
42
+ [h(MacrostratUnitsOverlay, rest), children],
46
43
  ),
47
44
  ),
48
45
  );
@@ -62,3 +59,21 @@ export const WithPatterns = {
62
59
  patterns: true,
63
60
  },
64
61
  };
62
+
63
+ export const WithColumnSelection = {
64
+ args: {
65
+ time: 100,
66
+ ageSpan: 0.05,
67
+ patterns: false,
68
+ },
69
+ render(args) {
70
+ const [selectedColumn, setSelectedColumn] = useState(null);
71
+ return h(UnitMapComponent, args, [
72
+ h(ColumnHoverInteraction),
73
+ h(ColumnSelectionManager, {
74
+ selectedColumn,
75
+ onSelectColumn: setSelectedColumn,
76
+ }),
77
+ ]);
78
+ },
79
+ };
@@ -0,0 +1,15 @@
1
+ .interval-proportions
2
+ display: inline-flex
3
+ flex-flow: row wrap
4
+ gap: 0.5em
5
+ text-align: right
6
+ align-items: baseline
7
+
8
+ .interval-proportions, .discourage-break
9
+ align-items: baseline
10
+ white-space: nowrap
11
+ display: inline-flex
12
+ gap: 0.5em
13
+
14
+ .sep
15
+ color: var(--secondary-color)
@@ -0,0 +1,269 @@
1
+ import {
2
+ DataField,
3
+ IntervalShort,
4
+ IntervalTag,
5
+ type IntervalTagProps,
6
+ ItemList,
7
+ Value,
8
+ } from "@macrostrat/data-components";
9
+ import { useMacrostratDefs } from "@macrostrat/data-provider";
10
+ import h from "./age-range.module.sass";
11
+ import { formatProportion, formatRange } from "./utils";
12
+ import classNames from "classnames";
13
+ import { useMemo } from "react";
14
+
15
+ export function AgeField({ unit, children, ...rest }) {
16
+ const [_b_age, _t_age, _unit] = getAgeRange(unit);
17
+
18
+ return h(
19
+ DataField,
20
+ {
21
+ label: "Age",
22
+ value: formatRange(_b_age, _t_age),
23
+ unit: _unit,
24
+ ...rest,
25
+ },
26
+ children,
27
+ );
28
+ }
29
+
30
+ export function AgeRange({
31
+ data,
32
+ className,
33
+ }: {
34
+ data: {
35
+ t_age: number;
36
+ b_age: number;
37
+ };
38
+ className?: string;
39
+ }) {
40
+ const [_b_age, _t_age, _unit] = getAgeRange(data);
41
+
42
+ return h(Value, {
43
+ value: formatRange(_b_age, _t_age),
44
+ unit: _unit,
45
+ className,
46
+ });
47
+ }
48
+
49
+ export function AgeLabel({
50
+ age,
51
+ maximumFractionDigits = 2,
52
+ minimumFractionDigits = 0,
53
+ className,
54
+ }: {
55
+ age: number;
56
+ className?: string;
57
+ maximumFractionDigits?: number;
58
+ minimumFractionDigits?: number;
59
+ }) {
60
+ /** Component to display a single age value with unit conversion from
61
+ * Ma to ka or Ga as appropriate.
62
+ */
63
+ const [value, unit] = getAge(age);
64
+
65
+ const _value = value.toLocaleString("en-US", {
66
+ maximumFractionDigits,
67
+ minimumFractionDigits,
68
+ });
69
+
70
+ return h(Value, { value: _value, unit, className });
71
+ }
72
+
73
+ export function Duration({
74
+ value,
75
+ maximumFractionDigits = 2,
76
+ minimumFractionDigits = 0,
77
+ }) {
78
+ let unit = "Myr";
79
+ if (value < 0.8) {
80
+ unit = "kyr";
81
+ value *= 1000;
82
+ if (value < 5) {
83
+ unit = "yr";
84
+ value *= 1000;
85
+ }
86
+ } else if (value > 1000) {
87
+ unit = "Gyr";
88
+ value /= 1000;
89
+ }
90
+
91
+ let _value = value.toLocaleString("en-US", {
92
+ maximumFractionDigits,
93
+ minimumFractionDigits,
94
+ });
95
+
96
+ return h(Value, { value: _value, unit });
97
+ }
98
+
99
+ interface UnitIntervalConstraints {
100
+ b_int_id?: number;
101
+ t_int_id?: number;
102
+ b_prop?: number;
103
+ t_prop?: number;
104
+ b_int_name?: string;
105
+ t_int_name?: string;
106
+ b_age?: number;
107
+ t_age?: number;
108
+ }
109
+
110
+ export interface IntervalProportionsProps extends Omit<
111
+ IntervalTagProps,
112
+ "interval"
113
+ > {
114
+ unit: UnitIntervalConstraints;
115
+ showProportions?: boolean;
116
+ calculateProportionsFromAges?: boolean;
117
+ }
118
+
119
+ export function IntervalProportions({
120
+ unit,
121
+ className,
122
+ showProportions,
123
+ calculateProportionsFromAges = true,
124
+ ...rest
125
+ }: IntervalProportionsProps) {
126
+ /** Display the proportions of the unit that belong to the base and top intervals, if they are different */
127
+ if (unit.b_int_id == null && unit.t_int_id == null) return null;
128
+
129
+ let _showProps = showProportions;
130
+
131
+ const i0 = unit.b_int_id;
132
+ const i1 = unit.t_int_id;
133
+
134
+ /** Get interval information */
135
+ const intervalMap = useMacrostratDefs("intervals");
136
+ const [int0, int1] = useMemo(() => {
137
+ const int0 = intervalMap?.get(i0) ?? {};
138
+ if (i0 === i1) {
139
+ return [int0, int0];
140
+ }
141
+ const int1 = intervalMap?.get(i1) ?? {};
142
+ return [int0, int1];
143
+ }, [intervalMap, i0, i1]);
144
+
145
+ const interval0: IntervalShort = {
146
+ id: i0,
147
+ name: unit.b_int_name,
148
+ ...int0,
149
+ };
150
+
151
+ let b_prop = unit.b_prop;
152
+ let t_prop = unit.t_prop;
153
+
154
+ // If we have b_age and t_age, then we can calculate proportions if they are not given
155
+ if (b_prop == null && unit.b_age != null && calculateProportionsFromAges) {
156
+ b_prop = getProportion(unit.b_age, int0);
157
+ }
158
+ if (t_prop == null && unit.t_age != null && calculateProportionsFromAges) {
159
+ t_prop = getProportion(unit.t_age, int1);
160
+ }
161
+
162
+ // If there are no proportions given and we are not explicitly directed to show them,
163
+ // then we set them to hidden.
164
+ if (b_prop == null && t_prop == null) {
165
+ _showProps ??= false;
166
+ }
167
+
168
+ /*
169
+ Set default proportions to 0 and 1 if they are not given,
170
+ so that we can display "base" and "top" labels
171
+ */
172
+ b_prop ??= 0;
173
+ t_prop ??= 1;
174
+
175
+ let p0: any = null;
176
+ let p1: any = null;
177
+
178
+ if (_showProps !== false) {
179
+ p1 = h(Proportion, { value: t_prop });
180
+
181
+ if (i0 !== i1 || b_prop !== 0 || t_prop !== 1) {
182
+ // We have a single interval with undefined proportions
183
+ p0 = h(Proportion, { value: b_prop });
184
+ }
185
+
186
+ if (i0 === i1 && (b_prop !== 0 || t_prop !== 1)) {
187
+ p0 = h("span.joint-proportion", [p0, " ", h("span.sep", "to"), " ", p1]);
188
+ }
189
+ }
190
+
191
+ return h(
192
+ ItemList,
193
+ { className: classNames("interval-proportions", className) },
194
+ [
195
+ h(IntervalTag, {
196
+ interval: interval0,
197
+ prefix: p0,
198
+ ...rest,
199
+ }),
200
+ h.if(i0 != i1)("span.discourage-break", [
201
+ h("span.sep", " to "),
202
+ h(IntervalTag, {
203
+ interval: {
204
+ id: i1,
205
+ name: unit.t_int_name,
206
+ ...int1,
207
+ },
208
+ prefix: p1,
209
+ ...rest,
210
+ }),
211
+ ]),
212
+ ],
213
+ );
214
+ }
215
+
216
+ function getProportion(age: number, interval: IntervalShort): number | null {
217
+ /** Get proportion with the age */
218
+ console.log(age, interval);
219
+ return (interval.b_age - age) / (interval.b_age - interval.t_age);
220
+ }
221
+
222
+ function Proportion({ value }) {
223
+ let content = null;
224
+ if (value == 0) {
225
+ content = "base";
226
+ } else if (value == 1) {
227
+ content = "top";
228
+ } else {
229
+ content = formatProportion(value * 100) + "%";
230
+ }
231
+
232
+ return h("span.proportion", content);
233
+ }
234
+
235
+ export function getAgeRange(_unit) {
236
+ let b_age = _unit.b_age;
237
+ let t_age = _unit.t_age;
238
+ let unit = "Ma";
239
+
240
+ if (b_age < 0.8 && t_age < 1.2) {
241
+ b_age *= 1000;
242
+ t_age *= 1000;
243
+ unit = "ka";
244
+ } else if (b_age > 800 && t_age > 1200) {
245
+ b_age /= 1000;
246
+ t_age /= 1000;
247
+ unit = "Ga";
248
+ }
249
+
250
+ return [b_age, t_age, unit];
251
+ }
252
+
253
+ export function getAge(value) {
254
+ /** Get the age value in Ma, ka, or Ga as appropriate */
255
+ let unit = "Ma";
256
+ if (value < 0.8) {
257
+ unit = "ka";
258
+ value *= 1000;
259
+ if (value < 5) {
260
+ unit = "yr";
261
+ value *= 1000;
262
+ }
263
+ } else if (value > 1000) {
264
+ unit = "Ga";
265
+ value /= 1000;
266
+ }
267
+
268
+ return [value, unit];
269
+ }
@@ -1,3 +1,4 @@
1
1
  export * from "./panel";
2
2
  export * from "./popover";
3
3
  export * from "./modal-panel";
4
+ export * from "./age-range";
@@ -82,15 +82,6 @@
82
82
  margin: 0
83
83
  padding-left: 0
84
84
 
85
- .interval-proportions
86
- flex-grow: 1
87
- width: 100%
88
- text-align: right
89
- .discourage-break
90
- white-space: nowrap
91
- .sep
92
- margin: 0 0.3em
93
-
94
85
  .clickable
95
86
  cursor: pointer
96
87