@macrostrat/column-views 3.8.0 → 3.9.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.
- package/CHANGELOG.md +100 -0
- package/dist/animated-age-window.cjs +51 -0
- package/dist/animated-age-window.cjs.map +1 -0
- package/dist/animated-age-window.d.ts +51 -0
- package/dist/animated-age-window.js +51 -0
- package/dist/animated-age-window.js.map +1 -0
- package/dist/column.cjs +11 -1
- package/dist/column.cjs.map +1 -1
- package/dist/column.d.ts +11 -0
- package/dist/column.js +11 -1
- package/dist/column.js.map +1 -1
- package/dist/correlation-chart/main.cjs +24 -12
- package/dist/correlation-chart/main.cjs.map +1 -1
- package/dist/correlation-chart/main.d.ts +8 -1
- package/dist/correlation-chart/main.js +24 -12
- package/dist/correlation-chart/main.js.map +1 -1
- package/dist/data-provider/store.cjs +17 -4
- package/dist/data-provider/store.cjs.map +1 -1
- package/dist/data-provider/store.d.ts +6 -1
- package/dist/data-provider/store.js +17 -4
- package/dist/data-provider/store.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/prepare-units/composite-scale.cjs +94 -8
- package/dist/prepare-units/composite-scale.cjs.map +1 -1
- package/dist/prepare-units/composite-scale.d.ts +39 -0
- package/dist/prepare-units/composite-scale.js +96 -10
- package/dist/prepare-units/composite-scale.js.map +1 -1
- package/dist/prepare-units/helpers.cjs +0 -1
- package/dist/prepare-units/helpers.cjs.map +1 -1
- package/dist/prepare-units/helpers.js +0 -1
- package/dist/prepare-units/helpers.js.map +1 -1
- package/dist/prepare-units/index.cjs +33 -12
- package/dist/prepare-units/index.cjs.map +1 -1
- package/dist/prepare-units/index.js +34 -13
- package/dist/prepare-units/index.js.map +1 -1
- package/dist/prepare-units/types.cjs.map +1 -1
- package/dist/prepare-units/types.d.ts +13 -0
- package/dist/prepare-units/types.js.map +1 -1
- package/dist/section.cjs +4 -2
- package/dist/section.cjs.map +1 -1
- package/dist/section.d.ts +4 -1
- package/dist/section.js +4 -2
- package/dist/section.js.map +1 -1
- package/dist/units/boxes.cjs +19 -36
- package/dist/units/boxes.cjs.map +1 -1
- package/dist/units/boxes.js +21 -38
- package/dist/units/boxes.js.map +1 -1
- package/dist/units/composite.cjs +12 -12
- package/dist/units/composite.cjs.map +1 -1
- package/dist/units/composite.js +12 -12
- package/dist/units/composite.js.map +1 -1
- package/package.json +6 -6
- package/src/animated-age-window.ts +120 -0
- package/src/column.ts +24 -0
- package/src/correlation-chart/main.ts +30 -8
- package/src/data-provider/store.ts +21 -2
- package/src/index.ts +1 -0
- package/src/prepare-units/composite-scale.ts +210 -10
- package/src/prepare-units/helpers.ts +0 -2
- package/src/prepare-units/index.ts +68 -14
- package/src/prepare-units/types.ts +13 -0
- package/src/section.ts +6 -0
- package/src/units/boxes.ts +30 -52
- package/src/units/composite.ts +21 -12
package/dist/units/composite.js
CHANGED
|
@@ -14,22 +14,22 @@ function LabelTrackerProvider(props) {
|
|
|
14
14
|
const { children, units, findUnitID = findMacrostratUnitID } = props;
|
|
15
15
|
const [unlabeledDivisions, setUnlabeledDivisions] = useState(null);
|
|
16
16
|
const labelTrackerRef = useRef({});
|
|
17
|
+
const recompute = useCallback(() => {
|
|
18
|
+
const tracker = labelTrackerRef.current;
|
|
19
|
+
const allReported = units.every((d) => findUnitID(d) in tracker);
|
|
20
|
+
if (!allReported) return;
|
|
21
|
+
setUnlabeledDivisions(units.filter((d) => tracker[findUnitID(d)] === false));
|
|
22
|
+
}, [units, findUnitID]);
|
|
17
23
|
const trackLabelVisibility = useCallback(
|
|
18
24
|
(div, visible) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (Object.keys(labelTrackerRef.current).length == units.length) {
|
|
22
|
-
setUnlabeledDivisions(
|
|
23
|
-
// @ts-ignore
|
|
24
|
-
units.filter((d) => {
|
|
25
|
-
const id2 = findUnitID(d);
|
|
26
|
-
return labelTrackerRef.current[id2] == false;
|
|
27
|
-
})
|
|
28
|
-
);
|
|
29
|
-
}
|
|
25
|
+
labelTrackerRef.current[findUnitID(div)] = visible;
|
|
26
|
+
recompute();
|
|
30
27
|
},
|
|
31
|
-
[
|
|
28
|
+
[recompute, findUnitID]
|
|
32
29
|
);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
recompute();
|
|
32
|
+
}, [recompute]);
|
|
33
33
|
const value = trackLabelVisibility;
|
|
34
34
|
return h(
|
|
35
35
|
LabelTrackerContext.Provider,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composite.js","sources":["../../src/units/composite.ts"],"sourcesContent":["import { hyperStyled } from \"@macrostrat/hyper\";\nimport {\n ColumnContext,\n LithologyColumn,\n SVG,\n} from \"@macrostrat/column-components\";\nimport { defaultNameFunction, UnitNamesColumn } from \"./names\";\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { BaseUnit } from \"@macrostrat/api-types\";\nimport { LabeledUnit, UnitBoxes } from \"./boxes\";\nimport styles from \"./composite.module.sass\";\nimport {\n useCompositeScale,\n useMacrostratColumnData,\n useUnitSelectionDispatch,\n} from \"../data-provider\";\n\nconst h = hyperStyled(styles);\n\ninterface LabelTracker {\n [key: number]: boolean;\n}\n\nconst LabelTrackerContext = createContext(null);\nconst UnlabeledDivisionsContext = createContext(null);\n\nconst findMacrostratUnitID = (u) => u.unit_id;\n\nexport function LabelTrackerProvider(props) {\n /** Tracker for units to handle state for whether their labels fit inline or not.\n * Designed to work for single or composite columns.\n */\n const { children, units, findUnitID = findMacrostratUnitID } = props;\n const [unlabeledDivisions, setUnlabeledDivisions] = useState<\n BaseUnit[] | null\n >(null);\n const labelTrackerRef = useRef<LabelTracker>({});\n const
|
|
1
|
+
{"version":3,"file":"composite.js","sources":["../../src/units/composite.ts"],"sourcesContent":["import { hyperStyled } from \"@macrostrat/hyper\";\nimport {\n ColumnContext,\n LithologyColumn,\n SVG,\n} from \"@macrostrat/column-components\";\nimport { defaultNameFunction, UnitNamesColumn } from \"./names\";\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { BaseUnit } from \"@macrostrat/api-types\";\nimport { LabeledUnit, UnitBoxes } from \"./boxes\";\nimport styles from \"./composite.module.sass\";\nimport {\n useCompositeScale,\n useMacrostratColumnData,\n useUnitSelectionDispatch,\n} from \"../data-provider\";\n\nconst h = hyperStyled(styles);\n\ninterface LabelTracker {\n [key: number]: boolean;\n}\n\nconst LabelTrackerContext = createContext(null);\nconst UnlabeledDivisionsContext = createContext(null);\n\nconst findMacrostratUnitID = (u) => u.unit_id;\n\nexport function LabelTrackerProvider(props) {\n /** Tracker for units to handle state for whether their labels fit inline or not.\n * Designed to work for single or composite columns.\n */\n const { children, units, findUnitID = findMacrostratUnitID } = props;\n const [unlabeledDivisions, setUnlabeledDivisions] = useState<\n BaseUnit[] | null\n >(null);\n const labelTrackerRef = useRef<LabelTracker>({});\n\n const recompute = useCallback(() => {\n // Recompute over the *current* unit set (which changes as the column zooms).\n // The tracker ref accumulates entries for units seen at any zoom level, so\n // we gate on whether every currently-visible unit has reported — not on the\n // total number of tracked units, which would go stale and freeze the notes.\n const tracker = labelTrackerRef.current;\n const allReported = units.every((d) => findUnitID(d) in tracker);\n if (!allReported) return;\n setUnlabeledDivisions(units.filter((d) => tracker[findUnitID(d)] === false));\n }, [units, findUnitID]);\n\n const trackLabelVisibility = useCallback(\n (div, visible) => {\n labelTrackerRef.current[findUnitID(div)] = visible;\n recompute();\n },\n [recompute, findUnitID],\n );\n\n // Re-sync when the visible unit set changes (e.g. on zoom), so a unit whose\n // label now fits inline stops also rendering as a note, and vice versa.\n useEffect(() => {\n recompute();\n }, [recompute]);\n\n const value = trackLabelVisibility;\n return h(\n LabelTrackerContext.Provider,\n { value },\n h(\n UnlabeledDivisionsContext.Provider,\n { value: unlabeledDivisions },\n children,\n ),\n );\n}\n\nfunction TrackedLabeledUnit({\n division,\n nameForDivision = defaultNameFunction,\n ...rest\n}) {\n const trackLabelVisibility = useContext(LabelTrackerContext);\n return h(LabeledUnit, {\n division,\n label: nameForDivision(division),\n onLabelUpdated(label, visible) {\n // If there is al LabelTrackerContext, update the label visibility\n trackLabelVisibility?.(division, visible);\n },\n ...rest,\n });\n}\n\nexport const CompositeUnitComponent = TrackedLabeledUnit;\n\ntype BaseUnitProps = {\n width: number;\n showLabels?: boolean;\n columnWidth?: number;\n clipToFrame?: boolean;\n};\n\ntype ICompositeUnitProps = BaseUnitProps & {\n gutterWidth?: number;\n labelOffset?: number;\n paddingLeft?: number;\n nameForDivision?: (division: BaseUnit) => string;\n children?: React.ReactNode;\n unitComponent?: React.FC<any>;\n unitComponentProps?: any;\n noteMode?: \"labeled\" | \"unlabeled\";\n showLabelColumn?: boolean;\n noteComponent?: React.FC<any>;\n shouldRenderNote?: (d: any) => boolean;\n};\n\ninterface CompositeUnitProps {\n unitComponent: React.FC<any>;\n unitComponentProps?: any;\n width: number;\n showLabels?: boolean;\n clipToFrame?: boolean;\n}\n\nexport function CompositeUnitsColumn(props: CompositeUnitProps) {\n /*\n A column with units and names either\n overlapping or offset to the right\n */\n const {\n width,\n unitComponent = TrackedLabeledUnit,\n unitComponentProps,\n clipToFrame,\n ...rest\n } = props;\n\n return h(LithologyColumn, { width, clipToFrame }, [\n h(UnitBoxes, {\n unitComponent,\n unitComponentProps,\n ...rest,\n }),\n ]);\n}\n\nexport function AnnotatedUnitsColumn(props: ICompositeUnitProps) {\n useEffect(() => {\n console.warn(\n \"AnnotatedUnitsColumn is deprecated and currently renders a no-op for transition purposes\",\n );\n }, []);\n return null;\n}\n\nexport function SectionLabelsColumn(props: ICompositeUnitProps) {\n // Section with \"squishy\" time scale\n const {\n width = 300,\n noteMode = \"unlabeled\",\n labelOffset = 30,\n noteComponent,\n shouldRenderNote,\n } = props;\n\n const { totalHeight, axisType } = useMacrostratColumnData();\n const _compositeScale = useCompositeScale();\n\n const unlabeledUnits = useContext(UnlabeledDivisionsContext);\n const unitsToLabel = noteMode == \"unlabeled\" ? unlabeledUnits : undefined;\n\n const selectUnit = useUnitSelectionDispatch();\n\n return h(\"div.section-labels-column\", [\n h(\n ColumnNotesProvider,\n {\n units: [],\n scale: _compositeScale,\n totalHeight,\n axisType,\n },\n h(\n SVG,\n {\n height: totalHeight,\n innerWidth: width - 4,\n paddingH: 1,\n paddingLeft: 3,\n },\n h(UnitNamesColumn, {\n divisions: unitsToLabel,\n paddingLeft: labelOffset,\n width,\n noteComponent,\n shouldRenderNote,\n scale: _compositeScale,\n onClickNote(note) {\n selectUnit(note.data, null, null);\n },\n }),\n ),\n ),\n ]);\n}\n\nexport function ColumnNotesProvider(props) {\n // A fake column axis provider that allows scales to cross\n const { children, scale, totalHeight, pixelScale } = props;\n\n const { axisType } = useMacrostratColumnData();\n\n return h(\n ColumnContext.Provider,\n {\n value: {\n divisions: [],\n scale,\n scaleClamped: scale,\n pixelHeight: totalHeight,\n pixelsPerMeter: pixelScale,\n axisType,\n zoom: 1,\n },\n },\n children,\n );\n}\n\nexport { TrackedLabeledUnit, ICompositeUnitProps };\n"],"names":["styles"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@macrostrat/column-views",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"description": "Data views for Macrostrat stratigraphic columns",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -48,19 +48,19 @@
|
|
|
48
48
|
"@blueprintjs/core": "^6.14.0",
|
|
49
49
|
"@macrostrat/api-types": "^1.2.0",
|
|
50
50
|
"@macrostrat/color-utils": "^1.2.2",
|
|
51
|
-
"@macrostrat/column-components": "^2.0
|
|
52
|
-
"@macrostrat/data-components": "^1.5.
|
|
51
|
+
"@macrostrat/column-components": "^2.1.0",
|
|
52
|
+
"@macrostrat/data-components": "^1.5.1",
|
|
53
53
|
"@macrostrat/data-provider": "^1.2.0",
|
|
54
54
|
"@macrostrat/hyper": "^3.1.1",
|
|
55
55
|
"@macrostrat/map-interface": "^2.3.1",
|
|
56
|
-
"@macrostrat/map-styles": "^2.2.
|
|
56
|
+
"@macrostrat/map-styles": "^2.2.7",
|
|
57
57
|
"@macrostrat/mapbox-react": "^3.3.3",
|
|
58
58
|
"@macrostrat/mapbox-utils": "^1.8.0",
|
|
59
59
|
"@macrostrat/scoped-store": "^1.0.1",
|
|
60
60
|
"@macrostrat/stratigraphy-utils": "^1.3.0",
|
|
61
61
|
"@macrostrat/svg-map-components": "^2.0.3",
|
|
62
|
-
"@macrostrat/timescale": "^3.
|
|
63
|
-
"@macrostrat/ui-components": "^5.
|
|
62
|
+
"@macrostrat/timescale": "^3.2.0",
|
|
63
|
+
"@macrostrat/ui-components": "^5.2.0",
|
|
64
64
|
"@turf/boolean-point-in-polygon": "^7.3.2",
|
|
65
65
|
"@turf/buffer": "^7.3.2",
|
|
66
66
|
"@turf/centroid": "^7.3.2",
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
import { AgeDomain, useAnimatedDomain } from "@macrostrat/timescale";
|
|
3
|
+
|
|
4
|
+
/** A rendered age window, ordered as the column props expect:
|
|
5
|
+
* `t_age` is the younger (top) bound, `b_age` the older (bottom) bound. */
|
|
6
|
+
export interface AgeWindow {
|
|
7
|
+
t_age: number;
|
|
8
|
+
b_age: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AnimateWindowOptions {
|
|
12
|
+
duration?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface UseAnimatedAgeWindowOptions {
|
|
16
|
+
/** The full data extent — the window to reset to, and the initial window.
|
|
17
|
+
* `null` until the data (and hence extent) is known. */
|
|
18
|
+
fullExtent: AgeWindow | null;
|
|
19
|
+
/** Animation duration in ms (default 750). */
|
|
20
|
+
duration?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AnimatedAgeWindow {
|
|
24
|
+
/** The current animated window to feed to `t_age`/`b_age` column props
|
|
25
|
+
* (`null` until `fullExtent` is known). */
|
|
26
|
+
window: AgeWindow | null;
|
|
27
|
+
isAnimating: boolean;
|
|
28
|
+
/** True when the window is (approximately) the full extent. */
|
|
29
|
+
isFullExtent: boolean;
|
|
30
|
+
/** Pan-and-contract to an explicit window (clamped to the full extent).
|
|
31
|
+
* Density (`pixelScale`) is left untouched — this is not a zoom. */
|
|
32
|
+
zoomToWindow(target: AgeWindow, opts?: AnimateWindowOptions): void;
|
|
33
|
+
/** Pan-and-contract to a timescale interval, using `eag`/`lag`. */
|
|
34
|
+
zoomToInterval(
|
|
35
|
+
interval: { eag: number; lag: number },
|
|
36
|
+
opts?: AnimateWindowOptions,
|
|
37
|
+
): void;
|
|
38
|
+
/** Ease back to the full extent. */
|
|
39
|
+
reset(opts?: AnimateWindowOptions): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Animate the rendered age window (`t_age`/`b_age`) of a column or correlation
|
|
44
|
+
* chart. This is the "animate the current zoom approach" model: narrowing the
|
|
45
|
+
* window re-lays-out the column with explicit clipping — units past the bounds
|
|
46
|
+
* get a zig-zag edge, unconformities keep their fixed `unconformityHeight`, and
|
|
47
|
+
* `pixelScale` (density) is untouched, so a sub-interval click **pans and
|
|
48
|
+
* contracts** at constant density rather than zooming. Joint domain/range
|
|
49
|
+
* change (true zoom) stays under the user's direct control via `pixelScale`.
|
|
50
|
+
*
|
|
51
|
+
* The animation itself is `useAnimatedDomain` from `@macrostrat/timescale` —
|
|
52
|
+
* the same state machine that drives the timescale's `useZoomableScale`. This
|
|
53
|
+
* hook only maps an `[older, younger]` domain onto `{ b_age, t_age }`; it
|
|
54
|
+
* animates the window you ask for and nothing more. Revealing a margin of the
|
|
55
|
+
* neighboring column is the *layout's* job, via the `windowPadding` prop (px),
|
|
56
|
+
* which is resolved against real section heights rather than predicted here.
|
|
57
|
+
*/
|
|
58
|
+
export function useAnimatedAgeWindow(
|
|
59
|
+
options: UseAnimatedAgeWindowOptions,
|
|
60
|
+
): AnimatedAgeWindow {
|
|
61
|
+
const { fullExtent, duration } = options;
|
|
62
|
+
|
|
63
|
+
const extent = useMemo(() => toDomain(fullExtent), [
|
|
64
|
+
fullExtent?.t_age,
|
|
65
|
+
fullExtent?.b_age,
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
const anim = useAnimatedDomain({ extent, duration });
|
|
69
|
+
|
|
70
|
+
const zoomToWindow = useCallback(
|
|
71
|
+
(target: AgeWindow, opts: AnimateWindowOptions = {}) => {
|
|
72
|
+
anim.animateTo([target.b_age, target.t_age], {
|
|
73
|
+
duration: opts.duration,
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
[anim.animateTo],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const zoomToInterval = useCallback(
|
|
80
|
+
(
|
|
81
|
+
interval: { eag: number; lag: number },
|
|
82
|
+
opts: AnimateWindowOptions = {},
|
|
83
|
+
) => {
|
|
84
|
+
zoomToWindow({ t_age: interval.lag, b_age: interval.eag }, opts);
|
|
85
|
+
},
|
|
86
|
+
[zoomToWindow],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const reset = useCallback(
|
|
90
|
+
(opts: AnimateWindowOptions = {}) => {
|
|
91
|
+
if (fullExtent == null) return;
|
|
92
|
+
zoomToWindow(fullExtent, opts);
|
|
93
|
+
},
|
|
94
|
+
[zoomToWindow, fullExtent?.t_age, fullExtent?.b_age],
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
window: toWindow(anim.domain),
|
|
99
|
+
isAnimating: anim.isAnimating,
|
|
100
|
+
isFullExtent: anim.isFullExtent,
|
|
101
|
+
zoomToWindow,
|
|
102
|
+
zoomToInterval,
|
|
103
|
+
reset,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** `{t_age, b_age}` ↔ the core's `[older, younger]` domain. */
|
|
108
|
+
function toDomain(w: AgeWindow): AgeDomain;
|
|
109
|
+
function toDomain(w: AgeWindow | null): AgeDomain | null;
|
|
110
|
+
function toDomain(w: AgeWindow | null): AgeDomain | null {
|
|
111
|
+
if (w == null) return null;
|
|
112
|
+
return [w.b_age, w.t_age];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function toWindow(d: AgeDomain): AgeWindow;
|
|
116
|
+
function toWindow(d: AgeDomain | null): AgeWindow | null;
|
|
117
|
+
function toWindow(d: AgeDomain | null): AgeWindow | null {
|
|
118
|
+
if (d == null) return null;
|
|
119
|
+
return { t_age: d[1], b_age: d[0] };
|
|
120
|
+
}
|
package/src/column.ts
CHANGED
|
@@ -45,6 +45,10 @@ import {
|
|
|
45
45
|
ColumnHeightScaleOptions,
|
|
46
46
|
} from "./prepare-units";
|
|
47
47
|
import { UnitLong } from "@macrostrat/api-types";
|
|
48
|
+
import type {
|
|
49
|
+
TimescaleClickHandler,
|
|
50
|
+
IntervalStyleBuilder,
|
|
51
|
+
} from "@macrostrat/timescale";
|
|
48
52
|
import { NonIdealState } from "@blueprintjs/core";
|
|
49
53
|
import { DataField } from "@macrostrat/data-components";
|
|
50
54
|
import { ScaleContinuousNumeric } from "d3-scale";
|
|
@@ -61,6 +65,10 @@ interface BaseColumnProps extends Omit<
|
|
|
61
65
|
// Timescale properties
|
|
62
66
|
showTimescale?: boolean;
|
|
63
67
|
timescaleLevels?: number | [number, number];
|
|
68
|
+
/** Called when a timescale interval is clicked (e.g. to zoom the age range). */
|
|
69
|
+
onClickTimescaleInterval?: TimescaleClickHandler;
|
|
70
|
+
/** Per-interval style for the timescale (e.g. to bold the selected interval). */
|
|
71
|
+
timescaleIntervalStyle?: IntervalStyleBuilder;
|
|
64
72
|
unconformityLabels?: boolean | UnconformityLabelPlacement;
|
|
65
73
|
onMouseOver?: (
|
|
66
74
|
unit: UnitLong | null,
|
|
@@ -87,6 +95,12 @@ export interface ColumnProps
|
|
|
87
95
|
// Unconformity height in pixels
|
|
88
96
|
unconformityHeight?: number;
|
|
89
97
|
scale?: ScaleContinuousNumeric<number, number>;
|
|
98
|
+
/** True while the age window is animating (e.g. from `useAnimatedAgeWindow`),
|
|
99
|
+
* so per-frame label/pattern recalculation can be skipped. */
|
|
100
|
+
isTransitioning?: boolean;
|
|
101
|
+
/** Hide unit labels while transitioning (perf escape hatch; default false —
|
|
102
|
+
* labels stay visible through the animation). */
|
|
103
|
+
hideLabelsWhileTransitioning?: boolean;
|
|
90
104
|
ref?: RefObject<ColumnRef>;
|
|
91
105
|
}
|
|
92
106
|
|
|
@@ -108,11 +122,14 @@ export function Column(props: ColumnProps) {
|
|
|
108
122
|
pixelScale,
|
|
109
123
|
minPixelScale = 0.2,
|
|
110
124
|
minSectionHeight = 50,
|
|
125
|
+
windowPadding = 0,
|
|
111
126
|
collapseSmallUnconformities = true,
|
|
112
127
|
allowUnitSelection = false,
|
|
113
128
|
hybridScale,
|
|
114
129
|
scale,
|
|
115
130
|
axisType,
|
|
131
|
+
isTransitioning,
|
|
132
|
+
hideLabelsWhileTransitioning,
|
|
116
133
|
ref,
|
|
117
134
|
...rest
|
|
118
135
|
} = props;
|
|
@@ -152,6 +169,7 @@ export function Column(props: ColumnProps) {
|
|
|
152
169
|
pixelScale,
|
|
153
170
|
minPixelScale: _minPixelScale,
|
|
154
171
|
minSectionHeight: _minSectionHeight,
|
|
172
|
+
windowPadding,
|
|
155
173
|
collapseSmallUnconformities,
|
|
156
174
|
// TODO: consider unifying scale and hybridScale options
|
|
157
175
|
scale,
|
|
@@ -179,6 +197,8 @@ export function Column(props: ColumnProps) {
|
|
|
179
197
|
allowUnitSelection: showUnitPopover || allowUnitSelection,
|
|
180
198
|
onUnitSelected,
|
|
181
199
|
selectedUnit,
|
|
200
|
+
isTransitioning,
|
|
201
|
+
hideLabelsWhileTransitioning,
|
|
182
202
|
ref,
|
|
183
203
|
},
|
|
184
204
|
h(ColumnInner, { ageAxisComponent, ...rest }, [
|
|
@@ -218,6 +238,8 @@ function ColumnInner(props: ColumnInnerProps) {
|
|
|
218
238
|
timescaleLevels,
|
|
219
239
|
maxInternalColumns,
|
|
220
240
|
onMouseOver,
|
|
241
|
+
onClickTimescaleInterval,
|
|
242
|
+
timescaleIntervalStyle,
|
|
221
243
|
ageAxisComponent = CompositeAgeAxis,
|
|
222
244
|
} = props;
|
|
223
245
|
|
|
@@ -262,6 +284,8 @@ function ColumnInner(props: ColumnInnerProps) {
|
|
|
262
284
|
h.if(_showTimescale)(CompositeTimescale, {
|
|
263
285
|
levels: timescaleLevels,
|
|
264
286
|
unconformityLabels: _timescaleUnconformityLabels,
|
|
287
|
+
onClickInterval: onClickTimescaleInterval,
|
|
288
|
+
intervalStyle: timescaleIntervalStyle,
|
|
265
289
|
}),
|
|
266
290
|
h(SectionsColumn, {
|
|
267
291
|
unitComponent,
|
|
@@ -69,6 +69,13 @@ export interface CorrelationChartProps extends CorrelationChartSettings {
|
|
|
69
69
|
/** Content rendered above the timescale axis (top-left), beside the column
|
|
70
70
|
* headers — e.g. a zoom/filter indicator. */
|
|
71
71
|
axisTopContent?: React.ReactNode;
|
|
72
|
+
/** True while the age window is animating (e.g. from `useAnimatedAgeWindow`).
|
|
73
|
+
* Propagated to each column's context so per-frame label/pattern work can be
|
|
74
|
+
* skipped during the transition. */
|
|
75
|
+
isTransitioning?: boolean;
|
|
76
|
+
/** Hide unit labels while transitioning (perf escape hatch; default false —
|
|
77
|
+
* labels stay visible through the animation). */
|
|
78
|
+
hideLabelsWhileTransitioning?: boolean;
|
|
72
79
|
onUnitSelected?: (unitID: number | null, unit: BaseUnit | null) => void;
|
|
73
80
|
/** Called when a timescale interval is clicked (e.g. to zoom the age range) */
|
|
74
81
|
onClickTimescaleInterval?: TimescaleClickHandler;
|
|
@@ -83,6 +90,14 @@ export interface CorrelationChartProps extends CorrelationChartSettings {
|
|
|
83
90
|
* this so that they line up with the columns beneath them. */
|
|
84
91
|
const chartPaddingH = 4;
|
|
85
92
|
|
|
93
|
+
/** Default scale settings for the correlation chart. */
|
|
94
|
+
const defaultCorrelationChartScaleProps = {
|
|
95
|
+
targetUnitHeight: 10,
|
|
96
|
+
unconformityHeight: 60,
|
|
97
|
+
minSectionHeight: 60,
|
|
98
|
+
collapseSmallUnconformities: true,
|
|
99
|
+
};
|
|
100
|
+
|
|
86
101
|
function MainChartArea({ children }) {
|
|
87
102
|
const columnRef = useColumnRef();
|
|
88
103
|
return h("div.main-chart", { ref: columnRef }, children);
|
|
@@ -109,19 +124,14 @@ export function CorrelationChart({
|
|
|
109
124
|
onClickTimescaleInterval,
|
|
110
125
|
onColumnMouseOver,
|
|
111
126
|
onColumnClick,
|
|
127
|
+
isTransitioning = false,
|
|
128
|
+
hideLabelsWhileTransitioning = false,
|
|
112
129
|
...scaleProps
|
|
113
130
|
}: CorrelationChartProps) {
|
|
114
|
-
const defaultScaleProps = {
|
|
115
|
-
targetUnitHeight: 10,
|
|
116
|
-
unconformityHeight: 60,
|
|
117
|
-
minSectionHeight: 60,
|
|
118
|
-
collapseSmallUnconformities: true,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
131
|
const chartData = useMemo(() => {
|
|
122
132
|
if (!data) return null;
|
|
123
133
|
return buildCorrelationChartData(data, {
|
|
124
|
-
...
|
|
134
|
+
...defaultCorrelationChartScaleProps,
|
|
125
135
|
...scaleProps,
|
|
126
136
|
});
|
|
127
137
|
}, [data, ...Object.values(scaleProps)]);
|
|
@@ -180,6 +190,8 @@ export function CorrelationChart({
|
|
|
180
190
|
scale,
|
|
181
191
|
unitComponent,
|
|
182
192
|
onColumnMouseOver,
|
|
193
|
+
isTransitioning,
|
|
194
|
+
hideLabelsWhileTransitioning,
|
|
183
195
|
});
|
|
184
196
|
}),
|
|
185
197
|
),
|
|
@@ -215,6 +227,8 @@ function Package({
|
|
|
215
227
|
pixelScale,
|
|
216
228
|
scale,
|
|
217
229
|
onColumnMouseOver,
|
|
230
|
+
isTransitioning,
|
|
231
|
+
hideLabelsWhileTransitioning,
|
|
218
232
|
}) {
|
|
219
233
|
return h("g.package", { transform: `translate(0 ${offset})` }, [
|
|
220
234
|
// Disable the SVG overlay for now
|
|
@@ -232,6 +246,8 @@ function Package({
|
|
|
232
246
|
scale,
|
|
233
247
|
offsetLeft: i * (columnWidth + columnSpacing),
|
|
234
248
|
onColumnMouseOver,
|
|
249
|
+
isTransitioning,
|
|
250
|
+
hideLabelsWhileTransitioning,
|
|
235
251
|
});
|
|
236
252
|
}),
|
|
237
253
|
]),
|
|
@@ -253,6 +269,8 @@ interface ColumnProps {
|
|
|
253
269
|
pixelScale: number;
|
|
254
270
|
scale?: ScaleContinuousNumeric<number, number>;
|
|
255
271
|
onColumnMouseOver?: (columnID: number | null) => void;
|
|
272
|
+
isTransitioning?: boolean;
|
|
273
|
+
hideLabelsWhileTransitioning?: boolean;
|
|
256
274
|
}
|
|
257
275
|
|
|
258
276
|
function Column(props: ColumnProps) {
|
|
@@ -266,6 +284,8 @@ function Column(props: ColumnProps) {
|
|
|
266
284
|
scale,
|
|
267
285
|
unitComponent = ColoredUnitComponent,
|
|
268
286
|
onColumnMouseOver,
|
|
287
|
+
isTransitioning,
|
|
288
|
+
hideLabelsWhileTransitioning,
|
|
269
289
|
} = props;
|
|
270
290
|
|
|
271
291
|
const columnWidth = width;
|
|
@@ -297,6 +317,8 @@ function Column(props: ColumnProps) {
|
|
|
297
317
|
scale,
|
|
298
318
|
pixelsPerMeter: pixelScale, // Actually pixels per myr
|
|
299
319
|
axisType: ColumnAxisType.AGE,
|
|
320
|
+
isTransitioning,
|
|
321
|
+
hideLabelsWhileTransitioning,
|
|
300
322
|
},
|
|
301
323
|
h(UnitBoxes, {
|
|
302
324
|
unitComponent,
|
|
@@ -118,6 +118,11 @@ export interface MacrostratColumnDataContext<T extends BaseUnit> {
|
|
|
118
118
|
totalHeight?: number;
|
|
119
119
|
axisType?: ColumnAxisType;
|
|
120
120
|
allowUnitSelection?: boolean;
|
|
121
|
+
/** True while the age window is animating; propagated to each section's
|
|
122
|
+
* `ColumnProvider` so per-frame label/pattern work can be skipped. */
|
|
123
|
+
isTransitioning?: boolean;
|
|
124
|
+
/** Hide unit labels while transitioning (perf escape hatch; default false). */
|
|
125
|
+
hideLabelsWhileTransitioning?: boolean;
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
export interface ColumnDataProviderProps<T extends BaseUnit>
|
|
@@ -138,6 +143,8 @@ export function MacrostratColumnDataProvider<T extends BaseUnit>({
|
|
|
138
143
|
allowUnitSelection,
|
|
139
144
|
onUnitSelected,
|
|
140
145
|
selectedUnit,
|
|
146
|
+
isTransitioning,
|
|
147
|
+
hideLabelsWhileTransitioning,
|
|
141
148
|
ref,
|
|
142
149
|
}: ColumnDataProviderProps<T>) {
|
|
143
150
|
/** Internal provider for Macrostrat column data.
|
|
@@ -158,8 +165,17 @@ export function MacrostratColumnDataProvider<T extends BaseUnit>({
|
|
|
158
165
|
sections,
|
|
159
166
|
totalHeight,
|
|
160
167
|
axisType,
|
|
168
|
+
isTransitioning,
|
|
169
|
+
hideLabelsWhileTransitioning,
|
|
161
170
|
};
|
|
162
|
-
}, [
|
|
171
|
+
}, [
|
|
172
|
+
units,
|
|
173
|
+
sections,
|
|
174
|
+
totalHeight,
|
|
175
|
+
axisType,
|
|
176
|
+
isTransitioning,
|
|
177
|
+
hideLabelsWhileTransitioning,
|
|
178
|
+
]);
|
|
163
179
|
|
|
164
180
|
return h(
|
|
165
181
|
MacrostratColumnStateProvider,
|
|
@@ -242,7 +258,8 @@ export function MacrostratColumnProvider(props) {
|
|
|
242
258
|
* generic concepts to Macrostrat-specific ones.
|
|
243
259
|
*/
|
|
244
260
|
|
|
245
|
-
const { axisType } =
|
|
261
|
+
const { axisType, isTransitioning, hideLabelsWhileTransitioning } =
|
|
262
|
+
useMacrostratColumnData();
|
|
246
263
|
const { units, domain, pixelScale, scale, children } = props;
|
|
247
264
|
return h(
|
|
248
265
|
ColumnProvider,
|
|
@@ -252,6 +269,8 @@ export function MacrostratColumnProvider(props) {
|
|
|
252
269
|
range: domain,
|
|
253
270
|
pixelsPerMeter: pixelScale,
|
|
254
271
|
scale,
|
|
272
|
+
isTransitioning,
|
|
273
|
+
hideLabelsWhileTransitioning,
|
|
255
274
|
},
|
|
256
275
|
children,
|
|
257
276
|
);
|
package/src/index.ts
CHANGED