@macrostrat/column-views 3.11.0 → 3.12.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 (138) hide show
  1. package/CHANGELOG.md +184 -0
  2. package/dist/age-axis.cjs +1 -2
  3. package/dist/age-axis.cjs.map +1 -1
  4. package/dist/age-axis.js +1 -2
  5. package/dist/age-axis.js.map +1 -1
  6. package/dist/age-model/data.cjs +57 -0
  7. package/dist/age-model/data.cjs.map +1 -0
  8. package/dist/age-model/data.d.ts +29 -0
  9. package/dist/age-model/data.js +57 -0
  10. package/dist/age-model/data.js.map +1 -0
  11. package/dist/age-model/details.cjs +260 -0
  12. package/dist/age-model/details.cjs.map +1 -0
  13. package/dist/age-model/details.d.ts +39 -0
  14. package/dist/age-model/details.js +257 -0
  15. package/dist/age-model/details.js.map +1 -0
  16. package/dist/age-model/details.module.sass.cjs +42 -0
  17. package/dist/age-model/details.module.sass.cjs.map +1 -0
  18. package/dist/age-model/details.module.sass.js +40 -0
  19. package/dist/age-model/details.module.sass.js.map +1 -0
  20. package/dist/age-model/index.d.ts +5 -0
  21. package/dist/age-model/legacy.cjs +28 -0
  22. package/dist/age-model/legacy.cjs.map +1 -0
  23. package/dist/age-model/legacy.d.ts +6 -0
  24. package/dist/age-model/legacy.js +26 -0
  25. package/dist/age-model/legacy.js.map +1 -0
  26. package/dist/age-model/surfaces.cjs +354 -0
  27. package/dist/age-model/surfaces.cjs.map +1 -0
  28. package/dist/age-model/surfaces.d.ts +68 -0
  29. package/dist/age-model/surfaces.js +351 -0
  30. package/dist/age-model/surfaces.js.map +1 -0
  31. package/dist/age-model/surfaces.module.sass.cjs +45 -0
  32. package/dist/age-model/surfaces.module.sass.cjs.map +1 -0
  33. package/dist/age-model/surfaces.module.sass.js +43 -0
  34. package/dist/age-model/surfaces.module.sass.js.map +1 -0
  35. package/dist/age-model/types.cjs +229 -0
  36. package/dist/age-model/types.cjs.map +1 -0
  37. package/dist/age-model/types.d.ts +112 -0
  38. package/dist/age-model/types.js +229 -0
  39. package/dist/age-model/types.js.map +1 -0
  40. package/dist/column-views.css +406 -74
  41. package/dist/column.cjs +20 -13
  42. package/dist/column.cjs.map +1 -1
  43. package/dist/column.d.ts +21 -6
  44. package/dist/column.js +21 -14
  45. package/dist/column.js.map +1 -1
  46. package/dist/column.module.sass.cjs +19 -15
  47. package/dist/column.module.sass.cjs.map +1 -1
  48. package/dist/column.module.sass.js +19 -15
  49. package/dist/column.module.sass.js.map +1 -1
  50. package/dist/data-provider/store.cjs +34 -1
  51. package/dist/data-provider/store.cjs.map +1 -1
  52. package/dist/data-provider/store.d.ts +5 -0
  53. package/dist/data-provider/store.js +35 -2
  54. package/dist/data-provider/store.js.map +1 -1
  55. package/dist/index.cjs +78 -18
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.d.ts +3 -1
  58. package/dist/index.js +55 -4
  59. package/dist/index.js.map +1 -1
  60. package/dist/notes.cjs +11 -2
  61. package/dist/notes.cjs.map +1 -1
  62. package/dist/notes.d.ts +11 -1
  63. package/dist/notes.js +11 -2
  64. package/dist/notes.js.map +1 -1
  65. package/dist/prepare-units/composite-scale.cjs +18 -16
  66. package/dist/prepare-units/composite-scale.cjs.map +1 -1
  67. package/dist/prepare-units/composite-scale.js +18 -16
  68. package/dist/prepare-units/composite-scale.js.map +1 -1
  69. package/dist/prepare-units/density.cjs +83 -0
  70. package/dist/prepare-units/density.cjs.map +1 -0
  71. package/dist/prepare-units/density.d.ts +92 -0
  72. package/dist/prepare-units/density.js +83 -0
  73. package/dist/prepare-units/density.js.map +1 -0
  74. package/dist/prepare-units/index.cjs +15 -7
  75. package/dist/prepare-units/index.cjs.map +1 -1
  76. package/dist/prepare-units/index.d.ts +1 -0
  77. package/dist/prepare-units/index.js +15 -7
  78. package/dist/prepare-units/index.js.map +1 -1
  79. package/dist/prepare-units/types.cjs.map +1 -1
  80. package/dist/prepare-units/types.d.ts +34 -13
  81. package/dist/prepare-units/types.js.map +1 -1
  82. package/dist/section.cjs +108 -24
  83. package/dist/section.cjs.map +1 -1
  84. package/dist/section.d.ts +66 -6
  85. package/dist/section.js +109 -25
  86. package/dist/section.js.map +1 -1
  87. package/dist/timescale-zoom.cjs +191 -0
  88. package/dist/timescale-zoom.cjs.map +1 -0
  89. package/dist/timescale-zoom.d.ts +74 -0
  90. package/dist/timescale-zoom.js +191 -0
  91. package/dist/timescale-zoom.js.map +1 -0
  92. package/dist/unit-details/age-range.cjs +14 -43
  93. package/dist/unit-details/age-range.cjs.map +1 -1
  94. package/dist/unit-details/age-range.d.ts +1 -13
  95. package/dist/unit-details/age-range.js +6 -43
  96. package/dist/unit-details/age-range.js.map +1 -1
  97. package/dist/unit-details/utils.cjs +0 -5
  98. package/dist/unit-details/utils.cjs.map +1 -1
  99. package/dist/unit-details/utils.js +0 -5
  100. package/dist/unit-details/utils.js.map +1 -1
  101. package/dist/units/composite.cjs +4 -1
  102. package/dist/units/composite.cjs.map +1 -1
  103. package/dist/units/composite.d.ts +2 -0
  104. package/dist/units/composite.js +4 -1
  105. package/dist/units/composite.js.map +1 -1
  106. package/package.json +8 -8
  107. package/src/age-model/data.ts +98 -0
  108. package/src/age-model/details.module.sass +108 -0
  109. package/src/age-model/details.ts +333 -0
  110. package/src/age-model/index.ts +5 -0
  111. package/src/age-model/legacy.ts +30 -0
  112. package/src/age-model/surfaces.module.sass +168 -0
  113. package/src/age-model/surfaces.ts +568 -0
  114. package/src/age-model/types.ts +392 -0
  115. package/src/column.module.sass +59 -8
  116. package/src/column.ts +49 -23
  117. package/src/data-provider/store.ts +50 -1
  118. package/src/index.ts +5 -1
  119. package/src/notes.ts +20 -2
  120. package/src/prepare-units/composite-scale.ts +34 -31
  121. package/src/prepare-units/density.ts +197 -0
  122. package/src/prepare-units/index.ts +25 -6
  123. package/src/prepare-units/types.ts +34 -13
  124. package/src/section.ts +219 -33
  125. package/src/timescale-zoom.ts +341 -0
  126. package/src/unit-details/age-range.ts +12 -58
  127. package/src/units/composite.ts +11 -0
  128. package/dist/age-model-overlay.cjs +0 -50
  129. package/dist/age-model-overlay.cjs.map +0 -1
  130. package/dist/age-model-overlay.d.ts +0 -24
  131. package/dist/age-model-overlay.js +0 -48
  132. package/dist/age-model-overlay.js.map +0 -1
  133. package/dist/age-model-overlay.module.sass.cjs +0 -12
  134. package/dist/age-model-overlay.module.sass.cjs.map +0 -1
  135. package/dist/age-model-overlay.module.sass.js +0 -11
  136. package/dist/age-model-overlay.module.sass.js.map +0 -1
  137. package/src/age-model-overlay.module.sass +0 -11
  138. package/src/age-model-overlay.ts +0 -96
@@ -0,0 +1,98 @@
1
+ /** Data hooks for a column's age model. */
2
+ import { useMemo } from "react";
3
+ import { useAPIResult } from "@macrostrat/ui-components";
4
+ import { useMacrostratBaseURL } from "@macrostrat/data-provider";
5
+ import type { UnitLong } from "@macrostrat/api-types";
6
+ import { useMacrostratColumnData, useMacrostratUnits } from "../data-provider";
7
+ import {
8
+ type AgeModelBoundary,
9
+ type ColumnSurface,
10
+ surfacesFromBoundaries,
11
+ surfacesFromUnits,
12
+ } from "./types";
13
+
14
+ export const AGE_MODEL_ROUTE = "/age_model";
15
+
16
+ /** The age-model boundaries of a column, from `/age_model?col_id=` on the
17
+ * API the enclosing `MacrostratDataProvider` points at (production when there
18
+ * is none). `null` while loading or when `col_id` is not set. */
19
+ export function useColumnAgeModel(
20
+ col_id: number | null | undefined,
21
+ ): AgeModelBoundary[] | null {
22
+ const baseURL = useMacrostratBaseURL();
23
+ const params = useMemo(() => ({ col_id }), [col_id]);
24
+ let route: string | null = null;
25
+ if (col_id != null) {
26
+ route = baseURL + AGE_MODEL_ROUTE;
27
+ }
28
+ return useAPIResult(route, params, unwrapAgeModel) ?? null;
29
+ }
30
+
31
+ function unwrapAgeModel(res: any): AgeModelBoundary[] {
32
+ return res?.success?.data ?? [];
33
+ }
34
+
35
+ /** Where a column's surfaces came from */
36
+ export type SurfacesSource = "provided" | "age-model" | "derived" | "loading";
37
+
38
+ export interface UseColumnSurfacesOptions {
39
+ /** Surfaces to show. When given, nothing is fetched. */
40
+ surfaces?: ColumnSurface[] | null;
41
+ /** The column whose age model to fetch. Defaults to the column being
42
+ * rendered (read from its units). */
43
+ col_id?: number | null;
44
+ /** When the age model has no boundaries, derive surfaces from the units
45
+ * instead (default `true`). */
46
+ fallbackToUnits?: boolean;
47
+ /** Promote `modeled` surfaces that must in fact constrain the age model to
48
+ * `relative` (default `true`) — see `inferTiePointStatuses`. */
49
+ inferTiePoints?: boolean;
50
+ }
51
+
52
+ export interface ColumnSurfacesData {
53
+ surfaces: ColumnSurface[];
54
+ source: SurfacesSource;
55
+ }
56
+
57
+ /** Resolve the surfaces to show for the enclosing `Column`: the ones passed
58
+ * in, else the column's age model, else (optionally) surfaces derived from
59
+ * its units. Must be used inside a `Column`. */
60
+ export function useColumnSurfaces(
61
+ options: UseColumnSurfacesOptions = {},
62
+ ): ColumnSurfacesData {
63
+ const {
64
+ surfaces: provided,
65
+ col_id,
66
+ fallbackToUnits = true,
67
+ inferTiePoints = true,
68
+ } = options;
69
+ const units = useMacrostratUnits() as UnitLong[] | undefined;
70
+ const { axisType } = useMacrostratColumnData();
71
+
72
+ let fetchID: number | null = null;
73
+ if (provided == null) {
74
+ fetchID = col_id ?? units?.[0]?.col_id ?? null;
75
+ }
76
+ const boundaries = useColumnAgeModel(fetchID);
77
+
78
+ const fromAgeModel = useMemo(
79
+ () => surfacesFromBoundaries(boundaries, { inferTiePoints }),
80
+ [boundaries, inferTiePoints],
81
+ );
82
+
83
+ const derived = useMemo(() => {
84
+ if (!fallbackToUnits) return [];
85
+ return surfacesFromUnits(units, { axisType });
86
+ }, [units, axisType, fallbackToUnits]);
87
+
88
+ if (provided != null) {
89
+ return { surfaces: provided, source: "provided" };
90
+ }
91
+ if (fetchID != null && boundaries == null) {
92
+ return { surfaces: [], source: "loading" };
93
+ }
94
+ if (fromAgeModel.length > 0) {
95
+ return { surfaces: fromAgeModel, source: "age-model" };
96
+ }
97
+ return { surfaces: derived, source: "derived" };
98
+ }
@@ -0,0 +1,108 @@
1
+ .surface-details-panel
2
+ display: flex
3
+ flex-direction: column
4
+ min-width: 0
5
+
6
+ .surface-details-header
7
+ display: flex
8
+ flex-direction: row
9
+ align-items: center
10
+ flex-wrap: wrap
11
+ gap: 0.5em
12
+ padding: 0.5em 0
13
+ border-bottom: 1px solid var(--pz-panel-rule-color, var(--panel-rule-color, #ddd))
14
+ .spacer
15
+ flex: 1
16
+
17
+ .surface-details-title
18
+ margin: 0
19
+ font-size: 1.1em
20
+ .surface-id
21
+ font-size: 0.85em
22
+ color: var(--text-subtle-color, var(--pz-text-subtle-color))
23
+
24
+ .surface-details-tags
25
+ display: flex
26
+ gap: 0.3em
27
+
28
+ .surface-details-content
29
+ padding: 0.5em 0
30
+
31
+ .no-surface .placeholder
32
+ color: var(--text-subtle-color, var(--pz-text-subtle-color))
33
+ margin: 0
34
+
35
+ .surface-tag
36
+ // A colored dot leading the status label
37
+ &:before
38
+ content: ''
39
+ display: inline-block
40
+ width: 0.6em
41
+ height: 0.6em
42
+ border-radius: 50%
43
+ margin-right: 0.4em
44
+ background-color: var(--surface-color, currentColor)
45
+ &.surface-type-tag:before
46
+ display: none
47
+ // An inferred status is marked, so the tag doesn't pass for a recorded one
48
+ &.inferred
49
+ font-style: italic
50
+
51
+ .inferred-marker
52
+ margin-left: 0.15em
53
+ opacity: 0.7
54
+
55
+ .calibration-interval
56
+ display: flex
57
+ align-items: baseline
58
+ gap: 0.5em
59
+ flex-wrap: wrap
60
+
61
+ .calibration-range
62
+ color: var(--text-subtle-color, var(--pz-text-subtle-color))
63
+ font-size: 0.9em
64
+
65
+ .calibration-proportion
66
+ margin-top: 0.2em
67
+
68
+ .surface-units-list
69
+ list-style: none
70
+ margin: 0
71
+ padding: 0
72
+ li
73
+ display: inline
74
+ &:after
75
+ content: ', '
76
+ &:last-child:after
77
+ content: ''
78
+
79
+ .no-units, .no-calibration
80
+ color: var(--text-subtle-color, var(--pz-text-subtle-color))
81
+
82
+ .surface-status-legend
83
+ list-style: none
84
+ margin: 0
85
+ padding: 0
86
+ display: flex
87
+ flex-direction: column
88
+ gap: 0.3em
89
+ font-size: 0.85em
90
+ &.with-descriptions
91
+ gap: 0.6em
92
+
93
+ .legend-item
94
+ display: flex
95
+ flex-direction: row
96
+ align-items: baseline
97
+ gap: 0.6em
98
+ .surface-label-marker
99
+ flex: none
100
+ width: 24px
101
+ align-self: center
102
+
103
+ .legend-label
104
+ font-weight: 500
105
+
106
+ .legend-description
107
+ color: var(--text-subtle-color, var(--pz-text-subtle-color))
108
+ font-size: 0.9em
@@ -0,0 +1,333 @@
1
+ /** The inspector for a selected surface, and the small tags and legend that
2
+ * explain surface statuses and types. */
3
+ import hyper from "@macrostrat/hyper";
4
+ import classNames from "classnames";
5
+ import { type ReactNode, useMemo } from "react";
6
+ import { Button, Tag } from "@blueprintjs/core";
7
+ import {
8
+ DataField,
9
+ Identifier,
10
+ type IntervalShort,
11
+ IntervalTag,
12
+ UnitIdentifier,
13
+ Value,
14
+ } from "@macrostrat/data-components";
15
+ import { useMacrostratDefs } from "@macrostrat/data-provider";
16
+ import type { UnitLong } from "@macrostrat/api-types";
17
+ import { useColumnUnitsMap } from "../data-provider";
18
+ import { AgeLabel } from "../unit-details/age-range";
19
+ import { formatRange } from "../unit-details/utils";
20
+ import { ReferencesField } from "../unit-details/panel";
21
+ import { surfaceClasses } from "./surfaces";
22
+ import surfaceStyles from "./surfaces.module.sass";
23
+ import styles from "./details.module.sass";
24
+ import {
25
+ type BoundaryType,
26
+ type ColumnSurface,
27
+ type SurfaceStatus,
28
+ formatProportion,
29
+ surfaceStatuses,
30
+ surfaceStatusDescriptions,
31
+ surfaceStatusLabels,
32
+ } from "./types";
33
+
34
+ const h = hyper.styled({ ...surfaceStyles, ...styles });
35
+
36
+ export interface SurfaceDetailsPanelProps {
37
+ /** The surface to inspect; `null` renders the panel's empty state */
38
+ surface: ColumnSurface | null;
39
+ onClose?: () => void;
40
+ /** Called when a unit above or below the surface is clicked */
41
+ onSelectUnit?: (unitID: number) => void;
42
+ /** Extra controls for the header */
43
+ actions?: ReactNode;
44
+ /** Label for the measured-position field ("Height" or "Depth") */
45
+ positionLabel?: string;
46
+ className?: string;
47
+ }
48
+
49
+ /** Details of one surface: its age, calibration, the units it separates, and
50
+ * provenance. A companion to `UnitDetailsPanel`. */
51
+ export function SurfaceDetailsPanel(props: SurfaceDetailsPanelProps) {
52
+ const {
53
+ surface,
54
+ onClose,
55
+ onSelectUnit,
56
+ actions,
57
+ positionLabel = "Position",
58
+ className,
59
+ } = props;
60
+
61
+ // A selection that hasn't been made yet is an ordinary state for a panel
62
+ // bound to one, not a caller error
63
+ if (surface == null) {
64
+ return h(
65
+ "div.surface-details-panel.no-surface",
66
+ { className },
67
+ h("p.placeholder", "No surface selected."),
68
+ );
69
+ }
70
+
71
+ let closeButton: ReactNode = null;
72
+ if (onClose != null) {
73
+ closeButton = h(Button, {
74
+ icon: "cross",
75
+ variant: "minimal",
76
+ size: "small",
77
+ onClick: onClose,
78
+ "aria-label": "Close",
79
+ });
80
+ }
81
+
82
+ let positionField: ReactNode = null;
83
+ if (surface.position != null) {
84
+ positionField = h(DataField, {
85
+ label: positionLabel,
86
+ value: surface.position.toLocaleString("en-US", {
87
+ maximumFractionDigits: 2,
88
+ }),
89
+ unit: "m",
90
+ });
91
+ }
92
+
93
+ let sectionField: ReactNode = null;
94
+ if (surface.section_id != null) {
95
+ sectionField = h(
96
+ DataField,
97
+ { label: "Section" },
98
+ h(Identifier, { id: surface.section_id }),
99
+ );
100
+ }
101
+
102
+ let refsField: ReactNode = null;
103
+ if (surface.ref_id != null) {
104
+ refsField = h(ReferencesField, { refs: [surface.ref_id] });
105
+ }
106
+
107
+ return h(
108
+ "div.surface-details-panel",
109
+ { className: classNames(className, surfaceClasses(surface)) },
110
+ [
111
+ h("div.surface-details-header", [
112
+ h("h3.surface-details-title", [
113
+ "Surface ",
114
+ h(SurfaceIdentifier, { id: surface.id }),
115
+ ]),
116
+ h("div.surface-details-tags", [
117
+ h(SurfaceStatusTag, {
118
+ status: surface.status,
119
+ inferred: surface.statusInferred,
120
+ }),
121
+ h(SurfaceTypeTag, { type: surface.type }),
122
+ ]),
123
+ h("div.spacer"),
124
+ actions,
125
+ closeButton,
126
+ ]),
127
+ h("div.surface-details-content", [
128
+ h(DataField, { label: "Model age" }, h(AgeLabel, { age: surface.age })),
129
+ h(SurfaceCalibrationField, { surface }),
130
+ positionField,
131
+ h(SurfaceUnitsField, {
132
+ label: "Unit above",
133
+ unitIDs: surface.unitsAbove,
134
+ onSelectUnit,
135
+ }),
136
+ h(SurfaceUnitsField, {
137
+ label: "Unit below",
138
+ unitIDs: surface.unitsBelow,
139
+ onSelectUnit,
140
+ }),
141
+ sectionField,
142
+ refsField,
143
+ ]),
144
+ ],
145
+ );
146
+ }
147
+
148
+ function SurfaceIdentifier({ id }: { id: ColumnSurface["id"] }) {
149
+ if (typeof id === "number") {
150
+ return h(Identifier, { id });
151
+ }
152
+ return h("code.surface-id", String(id));
153
+ }
154
+
155
+ /** The calibration interval as a colored tag, with the surface's position in
156
+ * it: "Calymmian (1600–1400 Ma), 85% above base". */
157
+ function SurfaceCalibrationField({ surface }: { surface: ColumnSurface }) {
158
+ const { calibration, proportion } = surface;
159
+ const ids = useMemo(() => {
160
+ if (calibration == null) return [];
161
+ return [calibration.id];
162
+ }, [calibration?.id]);
163
+ // Interval colors come from the definitions table; the age model itself
164
+ // doesn't carry them
165
+ const intervalMap = useMacrostratDefs("intervals", ids, null);
166
+
167
+ if (calibration == null) {
168
+ return h(DataField, {
169
+ label: "Calibration",
170
+ value: "None",
171
+ className: "no-calibration",
172
+ });
173
+ }
174
+
175
+ const def = intervalMap?.get(calibration.id);
176
+ const interval: IntervalShort = {
177
+ id: calibration.id,
178
+ name: calibration.name,
179
+ b_age: calibration.b_age,
180
+ t_age: calibration.t_age,
181
+ color: def?.color,
182
+ rank: def?.rank,
183
+ };
184
+
185
+ return h(DataField, { label: "Calibration" }, [
186
+ h(
187
+ "div.calibration-interval",
188
+ h(IntervalTag, {
189
+ interval,
190
+ proportion,
191
+ }),
192
+ ),
193
+ ]);
194
+ }
195
+
196
+ function SurfaceUnitsField({
197
+ label,
198
+ unitIDs,
199
+ onSelectUnit,
200
+ }: {
201
+ label: string;
202
+ unitIDs: number[];
203
+ onSelectUnit?: (unitID: number) => void;
204
+ }) {
205
+ const unitsMap = useColumnUnitsMap();
206
+ if (unitIDs.length === 0) {
207
+ return h(DataField, { label, value: "None", className: "no-units" });
208
+ }
209
+ return h(
210
+ DataField,
211
+ { label, className: "surface-units-field" },
212
+ h(
213
+ "ul.surface-units-list",
214
+ unitIDs.map((unitID) => {
215
+ const unit = unitsMap?.get(unitID) as UnitLong | undefined;
216
+ let onClick: (() => void) | undefined = undefined;
217
+ if (onSelectUnit != null) {
218
+ onClick = () => onSelectUnit(unitID);
219
+ }
220
+ return h(
221
+ "li",
222
+ { key: unitID },
223
+ h(UnitIdentifier, {
224
+ unitID,
225
+ colID: unit?.col_id,
226
+ name: unit?.unit_name,
227
+ onClick,
228
+ }),
229
+ );
230
+ }),
231
+ ),
232
+ );
233
+ }
234
+
235
+ export function SurfaceStatusTag({
236
+ status,
237
+ inferred = false,
238
+ className,
239
+ }: {
240
+ status: SurfaceStatus;
241
+ /** The status was inferred rather than recorded (see
242
+ * `inferTiePointStatuses`); the tag says so. */
243
+ inferred?: boolean;
244
+ className?: string;
245
+ }) {
246
+ let label: ReactNode = surfaceStatusLabels[status] ?? status;
247
+ let title = surfaceStatusDescriptions[status];
248
+ if (inferred) {
249
+ label = [label, h("span.inferred-marker", "*")];
250
+ title = INFERRED_STATUS_DESCRIPTION;
251
+ }
252
+
253
+ return h(
254
+ Tag,
255
+ {
256
+ variant: "minimal",
257
+ className: classNames(
258
+ "surface-tag",
259
+ className,
260
+ surfaceClasses({ status }),
261
+ { inferred },
262
+ ),
263
+ title,
264
+ },
265
+ label,
266
+ );
267
+ }
268
+
269
+ const INFERRED_STATUS_DESCRIPTION =
270
+ "Recorded as modeled, but this surface cannot have been interpolated — it " +
271
+ "sits on the base or top of its interval, or is the edge of a gap-bound " +
272
+ "package — so it constrains the age model rather than falling out of it";
273
+
274
+ export function SurfaceTypeTag({
275
+ type,
276
+ className,
277
+ }: {
278
+ type: BoundaryType | null | undefined;
279
+ className?: string;
280
+ }) {
281
+ if (type == null || type === "") return null;
282
+ return h(
283
+ Tag,
284
+ {
285
+ variant: "minimal",
286
+ className: classNames("surface-tag", "surface-type-tag", className),
287
+ },
288
+ type,
289
+ );
290
+ }
291
+
292
+ export interface SurfaceStatusLegendProps {
293
+ statuses?: SurfaceStatus[];
294
+ showDescriptions?: boolean;
295
+ className?: string;
296
+ }
297
+
298
+ /** A legend of surface line styles, one entry per boundary status. */
299
+ export function SurfaceStatusLegend(props: SurfaceStatusLegendProps) {
300
+ const {
301
+ statuses = surfaceStatuses,
302
+ showDescriptions = false,
303
+ className,
304
+ } = props;
305
+ return h(
306
+ "ul.surface-status-legend",
307
+ {
308
+ className: classNames(className, {
309
+ "with-descriptions": showDescriptions,
310
+ }),
311
+ },
312
+ statuses.map((status) => {
313
+ let description: ReactNode = null;
314
+ if (showDescriptions) {
315
+ description = h(
316
+ "div.legend-description",
317
+ surfaceStatusDescriptions[status],
318
+ );
319
+ }
320
+ return h(
321
+ "li.legend-item",
322
+ { key: status, className: surfaceClasses({ status }) },
323
+ [
324
+ h("div.surface-label-marker"),
325
+ h("div.legend-text", [
326
+ h("div.legend-label", surfaceStatusLabels[status]),
327
+ description,
328
+ ]),
329
+ ],
330
+ );
331
+ }),
332
+ );
333
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./types";
2
+ export * from "./data";
3
+ export * from "./surfaces";
4
+ export * from "./details";
5
+ export * from "./legacy";
@@ -0,0 +1,30 @@
1
+ /** Earlier, in-development overlays, kept so existing consumers keep working.
2
+ * Both are now thin wrappers over `ColumnSurfaces`. */
3
+ import h from "@macrostrat/hyper";
4
+ import { useMemo } from "react";
5
+ import type { UnitLong } from "@macrostrat/api-types";
6
+ import { useMacrostratColumnData, useMacrostratUnits } from "../data-provider";
7
+ import { ColumnSurfaces } from "./surfaces";
8
+ import { surfacesFromUnits } from "./types";
9
+
10
+ /** @deprecated Use `ColumnSurfaces`, which also labels the surfaces and
11
+ * styles them by boundary status. */
12
+ export function BoundaryAgeModelOverlay() {
13
+ return h(ColumnSurfaces, {
14
+ showLabels: false,
15
+ extent: "column",
16
+ fallbackToUnits: false,
17
+ });
18
+ }
19
+
20
+ /** @deprecated Use `ColumnSurfaces` with `surfaces: surfacesFromUnits(units)`,
21
+ * or rely on its default fallback to unit-derived surfaces. */
22
+ export function ComputedSurfacesOverlay() {
23
+ const units = useMacrostratUnits() as UnitLong[];
24
+ const { axisType } = useMacrostratColumnData();
25
+ const surfaces = useMemo(
26
+ () => surfacesFromUnits(units, { axisType }),
27
+ [units, axisType],
28
+ );
29
+ return h(ColumnSurfaces, { surfaces, showLabels: false, extent: "column" });
30
+ }