@macrostrat/column-views 1.0.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 (109) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +3 -0
  3. package/dist/age-axis.7ce58d66.js +41 -0
  4. package/dist/age-axis.7ce58d66.js.map +1 -0
  5. package/dist/boxes.6d3192e6.js +164 -0
  6. package/dist/boxes.6d3192e6.js.map +1 -0
  7. package/dist/boxes.module.998ff312.js +13 -0
  8. package/dist/boxes.module.998ff312.js.map +1 -0
  9. package/dist/boxes.module.dd631fe3.css +4 -0
  10. package/dist/boxes.module.dd631fe3.css.map +1 -0
  11. package/dist/carbon-isotopes.6d9bddcf.js +11 -0
  12. package/dist/carbon-isotopes.6d9bddcf.js.map +1 -0
  13. package/dist/column.e35a471f.js +149 -0
  14. package/dist/column.e35a471f.js.map +1 -0
  15. package/dist/column.e901b5d4.js +117 -0
  16. package/dist/column.e901b5d4.js.map +1 -0
  17. package/dist/column.module.2e29259c.js +22 -0
  18. package/dist/column.module.2e29259c.js.map +1 -0
  19. package/dist/column.module.35184ea7.css +157 -0
  20. package/dist/column.module.35184ea7.css.map +1 -0
  21. package/dist/column.module.35ba04e7.js +34 -0
  22. package/dist/column.module.35ba04e7.js.map +1 -0
  23. package/dist/column.module.c9df6edf.css +138 -0
  24. package/dist/column.module.c9df6edf.css.map +1 -0
  25. package/dist/composite.494beffc.js +153 -0
  26. package/dist/composite.494beffc.js.map +1 -0
  27. package/dist/composite.module.5cc0e747.css +4 -0
  28. package/dist/composite.module.5cc0e747.css.map +1 -0
  29. package/dist/composite.module.6359deba.js +13 -0
  30. package/dist/composite.module.6359deba.js.map +1 -0
  31. package/dist/core.f3824dd8.js +89 -0
  32. package/dist/core.f3824dd8.js.map +1 -0
  33. package/dist/data-area.f9bb9ad9.js +86 -0
  34. package/dist/data-area.f9bb9ad9.js.map +1 -0
  35. package/dist/data-provider.61ef7f9a.js +25 -0
  36. package/dist/data-provider.61ef7f9a.js.map +1 -0
  37. package/dist/helpers.2af05ee4.js +94 -0
  38. package/dist/helpers.2af05ee4.js.map +1 -0
  39. package/dist/index.d.ts +363 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +19 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/index.module.8fad0020.js +37 -0
  44. package/dist/index.module.8fad0020.js.map +1 -0
  45. package/dist/index.module.de76feb2.css +65 -0
  46. package/dist/index.module.de76feb2.css.map +1 -0
  47. package/dist/isotopes-column.b5442b4d.js +163 -0
  48. package/dist/isotopes-column.b5442b4d.js.map +1 -0
  49. package/dist/layers.946aaa37.js +231 -0
  50. package/dist/layers.946aaa37.js.map +1 -0
  51. package/dist/map.e782010f.js +82 -0
  52. package/dist/map.e782010f.js.map +1 -0
  53. package/dist/names.ab14789e.js +65 -0
  54. package/dist/names.ab14789e.js.map +1 -0
  55. package/dist/providers.abcb2cbe.js +25 -0
  56. package/dist/providers.abcb2cbe.js.map +1 -0
  57. package/dist/resolvers.2949c4c0.js +83 -0
  58. package/dist/resolvers.2949c4c0.js.map +1 -0
  59. package/dist/section.6f6fc929.js +118 -0
  60. package/dist/section.6f6fc929.js.map +1 -0
  61. package/dist/selection-popover.7e008544.js +60 -0
  62. package/dist/selection-popover.7e008544.js.map +1 -0
  63. package/dist/selection-popover.module.2732ebc0.css +34 -0
  64. package/dist/selection-popover.module.2732ebc0.css.map +1 -0
  65. package/dist/selection-popover.module.fdbfb40e.js +22 -0
  66. package/dist/selection-popover.module.fdbfb40e.js.map +1 -0
  67. package/dist/selection.add9f8b0.js +117 -0
  68. package/dist/selection.add9f8b0.js.map +1 -0
  69. package/dist/store.c5271371.js +18 -0
  70. package/dist/store.c5271371.js.map +1 -0
  71. package/dist/types.ff938b22.js +18 -0
  72. package/dist/types.ff938b22.js.map +1 -0
  73. package/dist/unit-details.0c1edd00.js +209 -0
  74. package/dist/unit-details.0c1edd00.js.map +1 -0
  75. package/dist/units.5ec6a44c.js +42 -0
  76. package/dist/units.5ec6a44c.js.map +1 -0
  77. package/package.json +66 -0
  78. package/src/age-axis.ts +42 -0
  79. package/src/carbon-isotopes/column.module.sass +136 -0
  80. package/src/carbon-isotopes/column.ts +131 -0
  81. package/src/carbon-isotopes/core.ts +78 -0
  82. package/src/carbon-isotopes/data-area.ts +96 -0
  83. package/src/carbon-isotopes/data-provider.ts +19 -0
  84. package/src/carbon-isotopes/index.ts +3 -0
  85. package/src/carbon-isotopes/isotopes-column.ts +214 -0
  86. package/src/column.module.sass +114 -0
  87. package/src/column.ts +185 -0
  88. package/src/helpers.ts +133 -0
  89. package/src/index.ts +7 -0
  90. package/src/map/animated.ts +78 -0
  91. package/src/map/index.ts +86 -0
  92. package/src/map/layers.ts +316 -0
  93. package/src/providers.ts +34 -0
  94. package/src/section.ts +129 -0
  95. package/src/selection-popover.module.sass +27 -0
  96. package/src/selection-popover.ts +71 -0
  97. package/src/store.ts +12 -0
  98. package/src/unit-details/index.module.sass +51 -0
  99. package/src/unit-details/index.ts +215 -0
  100. package/src/units/boxes.module.sass +3 -0
  101. package/src/units/boxes.ts +221 -0
  102. package/src/units/composite.module.sass +2 -0
  103. package/src/units/composite.ts +266 -0
  104. package/src/units/index.ts +36 -0
  105. package/src/units/names.ts +118 -0
  106. package/src/units/resolvers.ts +111 -0
  107. package/src/units/selection.ts +167 -0
  108. package/src/units/types.ts +26 -0
  109. package/src/units/units.scss +16 -0
@@ -0,0 +1,215 @@
1
+ import hyper from "@macrostrat/hyper";
2
+ import styles from "./index.module.sass";
3
+ import { JSONView } from "@macrostrat/ui-components";
4
+ import { Button } from "@blueprintjs/core";
5
+ import { useState } from "react";
6
+ import {
7
+ DataField,
8
+ EnvironmentsList,
9
+ Interval,
10
+ ItemList,
11
+ LithologyList,
12
+ } from "@macrostrat/data-components";
13
+ import { useUnitSelectionDispatch } from "../units/selection";
14
+ import { useMacrostratUnits } from "../store";
15
+
16
+ const h = hyper.styled(styles);
17
+
18
+ export function UnitDetailsPanel({
19
+ unit,
20
+ onClose,
21
+ }: {
22
+ unit: any;
23
+ onClose?: any;
24
+ }) {
25
+ const [showJSON, setShowJSON] = useState(false);
26
+
27
+ let content = null;
28
+ if (showJSON) {
29
+ content = h(JSONView, { data: unit, showRoot: false });
30
+ } else {
31
+ content = h(UnitDetailsContent, { unit });
32
+ }
33
+
34
+ return h("div.unit-details-panel", [
35
+ h(LegendPanelHeader, {
36
+ onClose,
37
+ title: unit.unit_name,
38
+ id: unit.unit_id,
39
+ actions: [
40
+ h(Button, {
41
+ icon: "code",
42
+ small: true,
43
+ minimal: true,
44
+ className: "json-view-toggle",
45
+ onClick() {
46
+ setShowJSON(!showJSON);
47
+ },
48
+ }),
49
+ ],
50
+ }),
51
+ h("div.unit-details-content", [content]),
52
+ ]);
53
+ }
54
+
55
+ export function LegendPanelHeader({ title, id, onClose, actions = null }) {
56
+ return h("header.legend-panel-header", [
57
+ h.if(title != null)("h3", title),
58
+ h("div.spacer"),
59
+ h.if(id != null)("code", id),
60
+ actions,
61
+ h.if(onClose != null)(Button, {
62
+ icon: "cross",
63
+ minimal: true,
64
+ small: true,
65
+ onClick() {
66
+ onClose();
67
+ },
68
+ }),
69
+ ]);
70
+ }
71
+
72
+ function UnitDetailsContent({ unit }) {
73
+ let outcrop = unit.outcrop;
74
+ if (outcrop == "both") {
75
+ outcrop = "surface and subsurface";
76
+ }
77
+
78
+ return h("div.unit-details-content", [
79
+ h(DataField, {
80
+ label: "Thickness",
81
+ value: `${unit.min_thick}–${unit.max_thick}`,
82
+ unit: "m",
83
+ }),
84
+ h(LithologyList, { lithologies: unit.lith }),
85
+ h(DataField, {
86
+ label: "Age range",
87
+ value: `${unit.b_age}–${unit.t_age}`,
88
+ unit: "Ma",
89
+ }),
90
+ h(IntervalField, { unit }),
91
+ h(EnvironmentsList, { environments: unit.environ }),
92
+ h(DataField, { label: "Outcrop", value: outcrop }),
93
+ h(
94
+ DataField,
95
+ {
96
+ label: "Stratigraphic name",
97
+ },
98
+ h("span.strat-name-id", unit.strat_name_id)
99
+ ),
100
+ h(
101
+ DataField,
102
+ { label: "Above" },
103
+ h(UnitIDList, { units: unit.units_above })
104
+ ),
105
+ h(
106
+ DataField,
107
+ { label: "Below" },
108
+ h(UnitIDList, { units: unit.units_below })
109
+ ),
110
+ h(
111
+ DataField,
112
+ { label: "Color" },
113
+ h("span.color-swatch", { style: { backgroundColor: unit.color } })
114
+ ),
115
+ ]);
116
+ }
117
+
118
+ function UnitIDList({ units }) {
119
+ const u1 = units.filter((d) => d != 0);
120
+ const dispatch = useUnitSelectionDispatch();
121
+ const allUnits = useMacrostratUnits();
122
+
123
+ if (u1.length === 0) {
124
+ return h("span.no-units", "None");
125
+ }
126
+
127
+ let onClickHandler = null;
128
+ let tag = "span";
129
+ if (dispatch != null) {
130
+ onClickHandler = (id: number) => (evt) => {
131
+ console.log(units);
132
+ const unit = allUnits.find((d) => d.unit_id == id);
133
+ dispatch(unit, null, null);
134
+ evt.stopPropagation();
135
+ };
136
+ tag = "a";
137
+ }
138
+
139
+ return h(ItemList, { className: "unit-id-list" }, [
140
+ u1.map((unit) => {
141
+ return h(
142
+ tag,
143
+ { className: "unit-id", onClick: onClickHandler?.(unit) },
144
+ unit
145
+ );
146
+ }),
147
+ ]);
148
+ }
149
+
150
+ function IntervalField({ unit }) {
151
+ return h([
152
+ h(
153
+ DataField,
154
+ {
155
+ label: "Intervals",
156
+ },
157
+ h(IntervalProportions, { unit })
158
+ ),
159
+ ]);
160
+ }
161
+
162
+ function IntervalProportions({ unit }) {
163
+ const i0 = unit.b_int_id;
164
+ const i1 = unit.t_int_id;
165
+ let b_prop = unit.b_prop ?? 0;
166
+ let t_prop = unit.t_prop ?? 1;
167
+
168
+ if (i0 === i1 && b_prop === 0 && t_prop === 1) {
169
+ // We have a single interval with undefined proportions
170
+ return h(Interval, {
171
+ interval: {
172
+ id: i0,
173
+ name: unit.b_int_name,
174
+ },
175
+ });
176
+ }
177
+
178
+ return h(ItemList, { className: "interval-proportions" }, [
179
+ h(Proportion, { value: b_prop }),
180
+ h.if(i0 != i1)(Interval, {
181
+ interval: {
182
+ id: i0,
183
+ name: unit.b_int_name,
184
+ },
185
+ proportion: b_prop,
186
+ }),
187
+ h("span.sep", "to"),
188
+ h(Proportion, { value: t_prop }),
189
+ h(Interval, {
190
+ interval: {
191
+ id: i1,
192
+ name: unit.t_int_name,
193
+ },
194
+ proportion: t_prop,
195
+ }),
196
+ ]);
197
+ }
198
+
199
+ const formatProportion = (d) => {
200
+ if (d == null) return null;
201
+ return d.toFixed(1);
202
+ };
203
+
204
+ function Proportion({ value }) {
205
+ let content = null;
206
+ if (value == 0) {
207
+ content = "base";
208
+ } else if (value == 1) {
209
+ content = "top";
210
+ } else {
211
+ content = formatProportion(value * 100) + "%";
212
+ }
213
+
214
+ return h("span.proportion", content);
215
+ }
@@ -0,0 +1,3 @@
1
+ .unit-label-container
2
+ pointer-events: none
3
+
@@ -0,0 +1,221 @@
1
+ import {
2
+ ColumnAxisType,
3
+ ColumnContext,
4
+ ColumnLayoutContext,
5
+ ForeignObject,
6
+ PatternDefsProvider,
7
+ useColumn,
8
+ useGeologicPattern,
9
+ } from "@macrostrat/column-components";
10
+ import { SizeAwareLabel, Clickable } from "@macrostrat/ui-components";
11
+ import hyper from "@macrostrat/hyper";
12
+ import {
13
+ ReactNode,
14
+ useCallback,
15
+ useContext,
16
+ useEffect,
17
+ useMemo,
18
+ useRef,
19
+ } from "react";
20
+ import { resolveID, scalePattern } from "./resolvers";
21
+ import { useSelectedUnit, useUnitSelectionDispatch } from "./selection";
22
+ import { IUnit, transformAxisType } from "./types";
23
+ import styles from "./boxes.module.sass";
24
+
25
+ const h = hyper.styled(styles);
26
+
27
+ export interface RectBounds {
28
+ x: number;
29
+ y: number;
30
+ width: number;
31
+ height: number;
32
+ }
33
+
34
+ interface UnitRectOptions {
35
+ widthFraction?: number;
36
+ axisType?: ColumnAxisType;
37
+ }
38
+
39
+ interface UnitProps extends Clickable, Partial<RectBounds>, UnitRectOptions {
40
+ division: IUnit;
41
+ resolveID?(IUnit): string;
42
+ UUID?: string;
43
+ defaultFill?: string;
44
+ widthFraction?: number;
45
+ children?: ReactNode;
46
+ className?: string;
47
+ }
48
+
49
+ export interface LabeledUnitProps
50
+ extends UnitRectOptions,
51
+ Clickable,
52
+ Partial<RectBounds> {
53
+ division: IUnit;
54
+ label: string;
55
+ onLabelUpdated?(label: string, shown: boolean);
56
+ halfWidth?: boolean;
57
+ }
58
+
59
+ function useUnitRect(
60
+ division: IUnit,
61
+ options: UnitRectOptions = {}
62
+ ): RectBounds {
63
+ const { widthFraction = 1, axisType = ColumnAxisType.AGE } = options;
64
+ const { scale } = useContext(ColumnContext);
65
+ const { width } = useContext(ColumnLayoutContext);
66
+ const macrostratAxisKey = transformAxisType(axisType);
67
+ const t_key = "t_" + macrostratAxisKey;
68
+ const b_key = "b_" + macrostratAxisKey;
69
+
70
+ const topHeight = division[t_key];
71
+ const bottomHeight = division[b_key];
72
+ if (topHeight == null && bottomHeight == null) {
73
+ console.warn(
74
+ `Missing keys ${t_key} and ${b_key} for ${division.unit_id} (${division.unit_name})`
75
+ );
76
+ }
77
+
78
+ const y = scale(topHeight);
79
+ const height = Math.abs(scale(bottomHeight) - y);
80
+
81
+ return {
82
+ x: width * (1 - widthFraction),
83
+ y,
84
+ height,
85
+ width: widthFraction * width,
86
+ };
87
+ }
88
+
89
+ function Unit(props: UnitProps) {
90
+ const {
91
+ division: d,
92
+ children,
93
+ defaultFill = "transparent",
94
+ className,
95
+ widthFraction = 1,
96
+ ...baseBounds
97
+ } = props;
98
+
99
+ const { axisType } = useColumn();
100
+ const bounds = {
101
+ ...useUnitRect(d, { widthFraction, axisType }),
102
+ ...baseBounds,
103
+ };
104
+ const patternID = resolveID(d);
105
+ const fill = useGeologicPattern(patternID, defaultFill);
106
+ // Allow us to select this unit if in the proper context
107
+
108
+ const ref = useRef<HTMLElement>();
109
+
110
+ const [selected, onClick] = useUnitSelectionManager(ref, d);
111
+
112
+ return h("g.unit", { className }, [
113
+ h("rect.unit", {
114
+ ref,
115
+ ...bounds,
116
+ fill,
117
+ onClick,
118
+ }),
119
+ h.if(selected)("rect.selection-overlay", bounds),
120
+ children,
121
+ ]);
122
+ }
123
+
124
+ function useUnitSelectionManager(
125
+ ref: React.RefObject<HTMLElement>,
126
+ unit: IUnit
127
+ ): [boolean, (evt: Event) => void] {
128
+ const selectedUnit = useSelectedUnit();
129
+ const selected = selectedUnit?.unit_id == unit.unit_id;
130
+
131
+ const dispatch = useUnitSelectionDispatch();
132
+
133
+ const onClick = useCallback(
134
+ (evt: Event) => {
135
+ dispatch(unit, ref.current, evt);
136
+ evt.stopPropagation();
137
+ },
138
+ [unit, ref, dispatch]
139
+ );
140
+
141
+ useEffect(() => {
142
+ if (!selected) return;
143
+ // In case we haven't set the position of the unit (if we don't have a target), set the selected unit
144
+ dispatch(unit, ref.current, null);
145
+
146
+ // Scroll the unit into view
147
+ ref.current?.scrollIntoView({
148
+ behavior: "smooth",
149
+ block: "center",
150
+ inline: "nearest",
151
+ });
152
+ }, [selected]);
153
+
154
+ return [selected, onClick];
155
+ }
156
+
157
+ function LabeledUnit(props: LabeledUnitProps) {
158
+ const { division, label, onLabelUpdated, widthFraction, ...baseBounds } =
159
+ props;
160
+
161
+ const { axisType } = useColumn();
162
+ const bounds = {
163
+ ...useUnitRect(division, { widthFraction, axisType }),
164
+ ...baseBounds,
165
+ };
166
+ const { width, height } = bounds;
167
+ return h(Unit, { className: "labeled-unit", division, ...bounds }, [
168
+ h(
169
+ ForeignObject,
170
+ { ...bounds, className: "unit-label-container" },
171
+ h(SizeAwareLabel, {
172
+ className: "unit-overlay",
173
+ labelClassName: "unit-label",
174
+ style: { width, height },
175
+ label,
176
+ onVisibilityChanged(viz) {
177
+ onLabelUpdated(label, viz);
178
+ },
179
+ })
180
+ ),
181
+ ]);
182
+ }
183
+
184
+ function UnitBoxes<T>(props: {
185
+ unitComponent: React.FunctionComponent<{ division: T }>;
186
+ unitComponentProps?: any;
187
+ transformDivisions?(divisions: T[]): T[];
188
+ transformDivision?: (
189
+ division: any,
190
+ index: number,
191
+ divisions: any[]
192
+ ) => T | null;
193
+ }) {
194
+ const { unitComponent = Unit, unitComponentProps = {} } = props;
195
+ const { divisions } = useContext(ColumnContext);
196
+
197
+ if (divisions.length == 0) {
198
+ console.warn("No divisions found in column context");
199
+ return null;
200
+ }
201
+
202
+ const children = useMemo(() => {
203
+ return divisions.map((division: any, i) => {
204
+ // @ts-ignore
205
+ return h(unitComponent, {
206
+ division,
207
+ key: division.unit_id,
208
+ axisType: ColumnAxisType.HEIGHT,
209
+ ...unitComponentProps,
210
+ });
211
+ });
212
+ }, [divisions, unitComponent, unitComponentProps]);
213
+
214
+ return h(
215
+ PatternDefsProvider,
216
+ { resolveID, scalePattern },
217
+ h("g.divisions", children)
218
+ );
219
+ }
220
+
221
+ export { LabeledUnit, Unit, UnitBoxes, UnitProps };
@@ -0,0 +1,2 @@
1
+ .unit-label
2
+ text-align: center
@@ -0,0 +1,266 @@
1
+ import { hyperStyled } from "@macrostrat/hyper";
2
+ import {
3
+ LithologyColumn,
4
+ useColumn,
5
+ ColumnLayoutContext,
6
+ ColumnAxisType,
7
+ } from "@macrostrat/column-components";
8
+ import { defaultNameFunction, UnitNamesColumn } from "./names";
9
+ import {
10
+ createContext,
11
+ useContext,
12
+ useState,
13
+ useRef,
14
+ useCallback,
15
+ memo,
16
+ } from "react";
17
+ import { BaseUnit } from "@macrostrat/api-types";
18
+ import { LabeledUnit, UnitBoxes } from "./boxes";
19
+ import styles from "./composite.module.sass";
20
+
21
+ const h = hyperStyled(styles);
22
+
23
+ interface LabelTracker {
24
+ [key: number]: boolean;
25
+ }
26
+
27
+ const LabelTrackerContext = createContext(null);
28
+ const UnlabeledDivisionsContext = createContext(null);
29
+
30
+ function LabelTrackerProvider(props) {
31
+ const { children } = props;
32
+ const { divisions } = useColumn();
33
+ const [unlabeledDivisions, setUnlabeledDivisions] = useState<
34
+ BaseUnit[] | null
35
+ >(null);
36
+ const labelTrackerRef = useRef<LabelTracker>({});
37
+ const trackLabelVisibility = useCallback(
38
+ (div, visible) => {
39
+ labelTrackerRef.current[div.unit_id] = visible;
40
+ if (Object.keys(labelTrackerRef.current).length == divisions.length) {
41
+ setUnlabeledDivisions(
42
+ // @ts-ignore
43
+ divisions.filter((d) => labelTrackerRef.current[d.unit_id] == false)
44
+ );
45
+ }
46
+ },
47
+ [labelTrackerRef, divisions]
48
+ );
49
+
50
+ const value = trackLabelVisibility;
51
+ return h(
52
+ LabelTrackerContext.Provider,
53
+ { value },
54
+ h(
55
+ UnlabeledDivisionsContext.Provider,
56
+ { value: unlabeledDivisions },
57
+ children
58
+ )
59
+ );
60
+ }
61
+
62
+ function TrackedLabeledUnit({
63
+ division,
64
+ nameForDivision = defaultNameFunction,
65
+ axisType,
66
+ ...rest
67
+ }) {
68
+ const trackLabelVisibility = useContext(LabelTrackerContext);
69
+ return h(LabeledUnit, {
70
+ division,
71
+ //halfWidth: div.bottomOverlap,
72
+ label: nameForDivision(division),
73
+ onLabelUpdated(label, visible) {
74
+ trackLabelVisibility(division, visible);
75
+ },
76
+ ...rest,
77
+ });
78
+ }
79
+
80
+ function UnlabeledUnitNames(props) {
81
+ // Returns only unlabeled divisions
82
+ const divisions = useContext(UnlabeledDivisionsContext);
83
+ if (divisions == null) return null;
84
+ return h(UnitNamesColumn, { divisions, ...props });
85
+ }
86
+
87
+ function _BaseUnitsColumn(
88
+ props: React.PropsWithChildren<{
89
+ width: number;
90
+ unitComponent?: React.FC<any>;
91
+ unitComponentProps?: any;
92
+ clipToFrame?: boolean;
93
+ }>
94
+ ) {
95
+ /*
96
+ A column with units and names either
97
+ overlapping or offset to the right
98
+ */
99
+ const {
100
+ width,
101
+ children,
102
+ unitComponent = TrackedLabeledUnit,
103
+ unitComponentProps,
104
+ clipToFrame,
105
+ ...rest
106
+ } = props;
107
+
108
+ return h(LabelTrackerProvider, [
109
+ h(LithologyColumn, { width, clipToFrame }, [
110
+ h(UnitBoxes, {
111
+ unitComponent,
112
+ unitComponentProps,
113
+ ...rest,
114
+ }),
115
+ ]),
116
+ children,
117
+ ]);
118
+ }
119
+
120
+ type BaseUnitProps = {
121
+ width: number;
122
+ showLabels?: boolean;
123
+ columnWidth?: number;
124
+ clipToFrame?: boolean;
125
+ };
126
+
127
+ type ICompositeUnitProps = BaseUnitProps & {
128
+ gutterWidth?: number;
129
+ labelOffset?: number;
130
+ paddingLeft?: number;
131
+ nameForDivision?: (division: BaseUnit) => string;
132
+ children?: React.ReactNode;
133
+ unitComponent?: React.FC<any>;
134
+ unitComponentProps?: any;
135
+ noteMode?: "labeled" | "unlabeled";
136
+ showLabelColumn?: boolean;
137
+ noteComponent?: React.FC<any>;
138
+ shouldRenderNote?: (d: BaseUnit) => boolean;
139
+ };
140
+
141
+ type AnnotatedUnitProps = ICompositeUnitProps & {
142
+ minimumLabelHeight?: number;
143
+ axisType: ColumnAxisType;
144
+ };
145
+
146
+ function AnnotatedUnitsColumn(props: AnnotatedUnitProps) {
147
+ /*
148
+ A column with units and names either
149
+ overlapping or offset to the right
150
+ */
151
+ const {
152
+ columnWidth,
153
+ width = 100,
154
+ gutterWidth = 10,
155
+ labelOffset = 30,
156
+ showLabels = true,
157
+ nameForDivision,
158
+ minimumLabelHeight = 0,
159
+ axisType,
160
+ ...rest
161
+ } = props;
162
+
163
+ return h(
164
+ _BaseUnitsColumn,
165
+ {
166
+ width: showLabels ? columnWidth : width,
167
+ unitComponentProps: { nameForDivision },
168
+ },
169
+ [
170
+ h.if(showLabels)(UnlabeledUnitNames, {
171
+ transform: `translate(${columnWidth + gutterWidth})`,
172
+ paddingLeft: labelOffset,
173
+ width: width - columnWidth - gutterWidth,
174
+ minimumHeight: minimumLabelHeight,
175
+ nameForDivision,
176
+ ...rest,
177
+ }),
178
+ // h(UnitDataColumn, {
179
+ // transform: `translate(${columnWidth + gutterWidth})`,
180
+ // paddingLeft: labelOffset,
181
+ // width: width - columnWidth - gutterWidth,
182
+ // ...rest
183
+ // })
184
+ ]
185
+ );
186
+ }
187
+
188
+ function CompositeUnitsColumn(props: ICompositeUnitProps) {
189
+ /*
190
+ A column with units and names either
191
+ overlapping or offset to the right
192
+ */
193
+ const {
194
+ width = 100,
195
+ gutterWidth = 10,
196
+ labelOffset = 30,
197
+ noteMode = "unlabeled",
198
+ showLabels = true,
199
+ showLabelColumn = true,
200
+ noteComponent,
201
+ shouldRenderNote,
202
+ ...rest
203
+ } = props;
204
+
205
+ let { columnWidth = width } = props;
206
+
207
+ const labelColumnComponent =
208
+ noteMode == "unlabeled" ? UnlabeledUnitNames : UnitNamesColumn;
209
+
210
+ return h(_BaseUnitsColumn, { width: columnWidth, ...rest }, [
211
+ h.if(showLabelColumn)(ColumnLabel, {
212
+ showLabels,
213
+ component: labelColumnComponent,
214
+ columnWidth,
215
+ gutterWidth,
216
+ labelOffset,
217
+ width,
218
+ noteComponent,
219
+ showNote: shouldRenderNote,
220
+ }),
221
+ ]);
222
+ }
223
+
224
+ function _ColumnLabel(props) {
225
+ const {
226
+ showLabels,
227
+ component,
228
+ columnWidth,
229
+ gutterWidth,
230
+ labelOffset,
231
+ width,
232
+ noteComponent,
233
+ showNote,
234
+ } = props;
235
+ if (!showLabels) return null;
236
+ return h(component, {
237
+ transform: `translate(${columnWidth + gutterWidth})`,
238
+ paddingLeft: labelOffset,
239
+ width: width - columnWidth - gutterWidth,
240
+ noteComponent,
241
+ shouldRenderNote: showNote,
242
+ });
243
+ }
244
+
245
+ const ColumnLabel = memo(_ColumnLabel);
246
+
247
+ export function CompositeUnitComponent({ division, nColumns = 2, ...rest }) {
248
+ // This comes from CompositeUnits
249
+ const { width } = useContext(ColumnLayoutContext);
250
+
251
+ //const nCols = Math.min(nColumns, division.overlappingUnits.length+1)
252
+ //console.log(division);
253
+ return h(TrackedLabeledUnit, {
254
+ division,
255
+ ...rest,
256
+ width: division.overlappingUnits.length > 0 ? width / nColumns : width,
257
+ x: (division.column * width) / nColumns,
258
+ });
259
+ }
260
+
261
+ export {
262
+ CompositeUnitsColumn,
263
+ AnnotatedUnitsColumn,
264
+ TrackedLabeledUnit,
265
+ ICompositeUnitProps,
266
+ };