@macrostrat/column-views 3.13.0 → 3.14.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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.14.0] - 2026-09-27 [_changes_](https://github.com/UW-Macrostrat/web-components/compare/@macrostrat/column-views-v3.13.0...@macrostrat/column-views-v3.14.0)
4
+
5
+ ### Minor Changes
6
+
7
+ - Remove the label-column claim (`useClaimLabelColumn`, `useLabelColumnClaimed`
8
+ [ca5ff733](https://github.com/UW-Macrostrat/web-components/commit/ca5ff73328471627d6cb3638f1b85336f9fefcc3)
9
+ and the layout-overrides context behind them). `ColumnSurfaceLabels` no longer
10
+ hides the unit labels on its own: pass `showLabelColumn: false` to the
11
+ `Column` while surface labels are shown, as the facet columns already do.
12
+
13
+ Remove height easing functions for sections with few units.
14
+
3
15
  ## [3.13.0] - 2026-09-27 [_changes_](https://github.com/UW-Macrostrat/web-components/compare/@macrostrat/column-views-v3.12.2...@macrostrat/column-views-v3.13.0)
4
16
 
5
17
  ### Minor Changes
@@ -146,7 +146,6 @@ function ColumnSurfaceLabels(props) {
146
146
  const { axisType } = store.useMacrostratColumnData();
147
147
  const scale = store.useCompositeScale();
148
148
  const intervalMap = useSurfaceIntervalDefs(surfaces);
149
- store.useClaimLabelColumn();
150
149
  const labeled = React.useMemo(() => {
151
150
  const byStatus = filterByStatus(surfaces, labelStatuses);
152
151
  return thinByPixelSpacing(
@@ -1 +1 @@
1
- {"version":3,"file":"surfaces.cjs","sources":["../../src/age-model/surfaces.ts"],"sourcesContent":["/** The surfaces view: age-model calibration surfaces drawn on a column.\n *\n * `ColumnSurfaces` is dropped into a `Column` as a child. It draws one line\n * per surface across the units (`ColumnSurfaceLines`) and a notes column of\n * labels beside them (`ColumnSurfaceLabels`), styled by `boundary_status` and\n * `boundary_type`, with hover and selection.\n */\nimport hyper from \"@macrostrat/hyper\";\nimport classNames from \"classnames\";\nimport {\n type CSSProperties,\n type RefObject,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { NoteData } from \"@macrostrat/column-components\";\nimport {\n type IntervalShort,\n IntervalTag,\n TagSize,\n} from \"@macrostrat/data-components\";\nimport { useMacrostratDefs } from \"@macrostrat/data-provider\";\nimport {\n useClaimLabelColumn,\n useCompositeScale,\n useMacrostratColumnData,\n} from \"../data-provider\";\nimport { ColumnNotes } from \"../notes\";\nimport { AgeLabel } from \"../unit-details/age-range\";\nimport columnStyles from \"../column.module.sass\";\nimport styles from \"./surfaces.module.sass\";\nimport {\n type ColumnSurface,\n type SurfaceStatus,\n formatProportion,\n surfaceLabel,\n surfacePosition,\n surfaceStatusLabels,\n surfaceToken,\n TIE_POINT_STATUSES,\n} from \"./types\";\nimport { type UseColumnSurfacesOptions, useColumnSurfaces } from \"./data\";\n\nconst h = hyper.styled(styles);\n\n/** How far the surface lines extend: across the units column only, or the\n * full width of the column including its axes. */\nexport type SurfaceLinesExtent = \"units\" | \"column\";\n\nexport type SurfaceSelectionHandler = (surface: ColumnSurface | null) => void;\n\nexport interface SurfaceSelectionProps {\n /** The selected surface's id. Pass `null` for a controlled, empty\n * selection; leave undefined for the component to manage selection. */\n selectedSurface?: ColumnSurface[\"id\"] | null;\n onSelectSurface?: SurfaceSelectionHandler;\n}\n\nexport interface ColumnSurfacesProps\n extends UseColumnSurfacesOptions, SurfaceSelectionProps {\n showLines?: boolean;\n showLabels?: boolean;\n extent?: SurfaceLinesExtent;\n /** Width of the labels column, connector included (default 200) */\n labelWidth?: number;\n labelPaddingLeft?: number;\n /** Only show surfaces with these statuses */\n statuses?: SurfaceStatus[] | null;\n /** Which surfaces get a label. Defaults to the tie points — the statuses\n * at which the age model was constrained — so interpolated (`modeled`),\n * unspecified and unit-derived surfaces are drawn as lines only. Pass\n * `null` to label every surface shown. */\n labelStatuses?: SurfaceStatus[] | null;\n /** Labels closer than this (in pixels) are thinned out, since the column\n * has finite room; the selected surface's label is always kept. */\n minLabelSpacing?: number;\n className?: string;\n}\n\nexport function ColumnSurfaces(props: ColumnSurfacesProps) {\n const {\n showLines = true,\n showLabels = true,\n extent = \"units\",\n labelWidth,\n labelPaddingLeft,\n statuses,\n labelStatuses = TIE_POINT_STATUSES,\n minLabelSpacing,\n selectedSurface,\n onSelectSurface,\n className,\n ...dataOptions\n } = props;\n\n const { surfaces: allSurfaces } = useColumnSurfaces(dataOptions);\n const surfaces = useMemo(\n () => filterByStatus(allSurfaces, statuses),\n [allSurfaces, statuses],\n );\n\n const [selectedID, select] = useSurfaceSelection(\n selectedSurface,\n onSelectSurface,\n );\n\n return h([\n h.if(showLines)(ColumnSurfaceLines, {\n surfaces,\n extent,\n selectedSurface: selectedID,\n onSelectSurface: select,\n className,\n }),\n h.if(showLabels)(ColumnSurfaceLabels, {\n surfaces,\n labelStatuses,\n minLabelSpacing,\n width: labelWidth,\n paddingLeft: labelPaddingLeft,\n selectedSurface: selectedID,\n onSelectSurface: select,\n className,\n }),\n ]);\n}\n\nexport interface ColumnSurfaceLinesProps extends SurfaceSelectionProps {\n surfaces: ColumnSurface[];\n extent?: SurfaceLinesExtent;\n className?: string;\n}\n\n/** One horizontal line per surface, overlaid on the column at the surface's\n * age (or measured position, for height and depth columns). */\nexport function ColumnSurfaceLines(props: ColumnSurfaceLinesProps) {\n const {\n surfaces,\n extent = \"units\",\n selectedSurface,\n onSelectSurface,\n className,\n } = props;\n const scale = useCompositeScale();\n const { axisType, totalHeight } = useMacrostratColumnData();\n const intervalMap = useSurfaceIntervalDefs(surfaces);\n\n const ref = useRef<HTMLDivElement>(null);\n const unitsExtent = useUnitsColumnExtent(ref, extent === \"units\");\n\n let style: CSSProperties = { height: totalHeight };\n if (unitsExtent != null) {\n style = { ...style, left: unitsExtent.left, width: unitsExtent.width };\n }\n const measuring = extent === \"units\" && unitsExtent == null;\n\n return h(\n \"div.column-surface-lines\",\n { ref, style, className: classNames(className, { measuring }) },\n surfaces.map((surface) => {\n const position = surfacePosition(surface, axisType);\n if (position == null) return null;\n const y = scale(position);\n if (y == null) return null;\n return h(SurfaceLine, {\n key: surface.id,\n surface,\n y,\n color: surfaceIntervalColor(surface, intervalMap),\n selected: surface.id === selectedSurface,\n onSelect: onSelectSurface,\n });\n }),\n );\n}\n\nfunction SurfaceLine({\n surface,\n y,\n color,\n selected,\n onSelect,\n}: {\n surface: ColumnSurface;\n y: number;\n /** The interval color, when known; otherwise the status color is used */\n color?: string | null;\n selected: boolean;\n onSelect?: SurfaceSelectionHandler;\n}) {\n const onClick = useCallback(\n (evt: MouseEvent) => {\n // Don't let the column deselect its unit\n evt.stopPropagation();\n if (selected) {\n onSelect?.(null);\n } else {\n onSelect?.(surface);\n }\n },\n [surface, selected, onSelect],\n );\n\n let style: CSSProperties = { top: y };\n if (color != null) {\n style = { ...style, \"--surface-color\": color } as CSSProperties;\n }\n\n return h(\n \"div.surface-line\",\n {\n style,\n className: classNames(surfaceClasses(surface), {\n selected,\n interactive: onSelect != null,\n }),\n title: surfaceLabel(surface),\n onClick,\n },\n h(\"div.surface-line-inner\"),\n );\n}\n\nexport interface ColumnSurfaceLabelsProps extends SurfaceSelectionProps {\n surfaces: ColumnSurface[];\n /** Which surfaces get a label (see `ColumnSurfacesProps.labelStatuses`) */\n labelStatuses?: SurfaceStatus[] | null;\n /** Minimum pixel spacing between labels (see `ColumnSurfacesProps`) */\n minLabelSpacing?: number;\n /** Total width, connector included */\n width?: number;\n paddingLeft?: number;\n className?: string;\n}\n\ninterface SurfaceNote extends NoteData {\n /** The interval color, which the note's connector picks up */\n color?: string;\n surface: ColumnSurface;\n /** The calibration interval, with color and rank from the definitions */\n interval: IntervalShort | null;\n selected: boolean;\n}\n\n/** A notes column of surface labels, laid out beside the units with the\n * collision avoidance of `NotesColumn`. It takes the label column over from\n * the unit labels while mounted: labels are one or the other. */\nexport function ColumnSurfaceLabels(props: ColumnSurfaceLabelsProps) {\n const {\n surfaces,\n labelStatuses = TIE_POINT_STATUSES,\n minLabelSpacing = 22,\n width = 200,\n paddingLeft = 24,\n selectedSurface,\n onSelectSurface,\n className,\n } = props;\n const { axisType } = useMacrostratColumnData();\n const scale = useCompositeScale();\n const intervalMap = useSurfaceIntervalDefs(surfaces);\n useClaimLabelColumn();\n\n const labeled = useMemo(() => {\n const byStatus = filterByStatus(surfaces, labelStatuses);\n return thinByPixelSpacing(\n byStatus,\n (s) => scale(surfacePosition(s, axisType) ?? NaN),\n minLabelSpacing,\n selectedSurface,\n );\n }, [\n surfaces,\n labelStatuses,\n minLabelSpacing,\n selectedSurface,\n scale,\n axisType,\n ]);\n\n const notes: SurfaceNote[] = useMemo(() => {\n const _notes: SurfaceNote[] = [];\n for (const surface of labeled) {\n const position = surfacePosition(surface, axisType);\n if (position == null) continue;\n _notes.push({\n id: surface.id,\n height: position,\n note: surfaceLabel(surface),\n // The note's color reaches its connector, so a label's leader line\n // matches the surface line it points at\n color: surfaceIntervalColor(surface, intervalMap) ?? undefined,\n surface,\n interval: calibrationInterval(surface, intervalMap),\n selected: surface.id === selectedSurface,\n });\n }\n return _notes;\n }, [labeled, axisType, selectedSurface, intervalMap]);\n\n const onClickNote = useCallback(\n (note: NoteData) => {\n const { surface, selected } = note as SurfaceNote;\n if (selected) {\n onSelectSurface?.(null);\n } else {\n onSelectSurface?.(surface);\n }\n },\n [onSelectSurface],\n );\n\n return h(\n \"div.column-surface-labels\",\n { className },\n h(ColumnNotes, {\n notes,\n width,\n paddingLeft,\n noteComponent: SurfaceNoteLabel,\n onClickNote,\n forceOptions: LABEL_FORCE_OPTIONS,\n // The leader runs from the surface line in the column to the tag body,\n // continuing a little past both ends rather than stopping in the gap —\n // so it needs no endpoint marker of its own\n connectorOverhang: LABEL_CONNECTOR_OVERHANG,\n showPointMarker: false,\n }),\n );\n}\n\n/** Breathing room between stacked labels, in pixels */\nconst LABEL_FORCE_OPTIONS = { nodeSpacing: 2 };\n\n/** How far the label's leader line continues past each end, in pixels: left\n * into the column to meet the surface line, right under the tag body. */\nconst LABEL_CONNECTOR_OVERHANG: [number, number] = [8, 6];\n\n/** A surface's label: its calibration interval as the standard interval tag\n * (with the position within the interval as the tag's detail), and the\n * modeled age beneath. Surfaces without a calibration show their status. */\nfunction SurfaceNoteLabel({ note }: { note: SurfaceNote }) {\n const { surface, interval, selected } = note;\n\n let primary: any;\n if (interval != null) {\n primary = h(IntervalTag, {\n interval,\n size: TagSize.Small,\n proportion: surface.proportion,\n className: \"surface-interval-tag\",\n });\n } else {\n primary = h(\n \"span.surface-label-status\",\n `${surfaceStatusLabels[surface.status]} surface`,\n );\n }\n\n return h(\n \"div.surface-label\",\n { className: classNames(surfaceClasses(surface), { selected }) },\n h(\"div.surface-label-primary\", primary),\n );\n}\n\n/** The interval definitions for the surfaces' calibration intervals. The age\n * model carries interval ids and names but no colors, so they come from the\n * definitions store (which holds them all, and fetches once). */\nfunction useSurfaceIntervalDefs(\n surfaces: ColumnSurface[],\n): Map<number, any> | null {\n const intervalIDs = useMemo(() => {\n const ids = new Set<number>();\n for (const surface of surfaces) {\n if (surface.calibration != null) ids.add(surface.calibration.id);\n }\n return Array.from(ids);\n }, [surfaces]);\n return useMacrostratDefs(\"intervals\", intervalIDs, null);\n}\n\n/** The color a surface is drawn in: its calibration interval's, so the line\n * and its label's leader read as the interval the surface is tied to. `null`\n * for an uncalibrated surface, or before the definitions load — the status\n * color stands in. */\nfunction surfaceIntervalColor(\n surface: ColumnSurface,\n intervalMap: Map<number, any> | null,\n): string | null {\n const id = surface.calibration?.id;\n if (id == null) return null;\n return intervalMap?.get(id)?.color ?? null;\n}\n\n/** The calibration interval in the shape the interval tag takes, colored\n * from the definitions when they have loaded. */\nfunction calibrationInterval(\n surface: ColumnSurface,\n intervalMap: Map<number, any> | null,\n): IntervalShort | null {\n const { calibration } = surface;\n if (calibration == null) return null;\n const def = intervalMap?.get(calibration.id);\n return {\n id: calibration.id,\n name: calibration.name,\n b_age: calibration.b_age,\n t_age: calibration.t_age,\n color: def?.color,\n rank: def?.rank,\n };\n}\n\n/** Class names shared by the lines, labels, legend and tags, so a status or\n * type reads the same everywhere. */\nexport function surfaceClasses(surface: {\n status?: SurfaceStatus | null;\n type?: string | null;\n}): string {\n return classNames(\n `status-${surfaceToken(surface.status)}`,\n `type-${surfaceToken(surface.type)}`,\n );\n}\n\n/** Walk the surfaces down the column and drop any whose label would sit\n * within `minSpacing` pixels of the last one kept. The selected surface is\n * always kept (and displaces a neighbor if need be). */\nfunction thinByPixelSpacing(\n surfaces: ColumnSurface[],\n pixelPosition: (s: ColumnSurface) => number | null,\n minSpacing: number,\n selectedSurface: ColumnSurface[\"id\"] | null | undefined,\n): ColumnSurface[] {\n if (minSpacing <= 0) return surfaces;\n const positioned = surfaces\n .map((surface) => ({ surface, y: pixelPosition(surface) }))\n .filter((d) => d.y != null && !Number.isNaN(d.y))\n .sort((a, b) => a.y - b.y);\n\n const kept: { surface: ColumnSurface; y: number }[] = [];\n for (const item of positioned) {\n const selected = item.surface.id === selectedSurface;\n const last = kept[kept.length - 1];\n if (last == null || item.y - last.y >= minSpacing) {\n kept.push(item);\n } else if (selected) {\n // The selection wins its slot\n kept[kept.length - 1] = item;\n }\n }\n return kept.map((d) => d.surface);\n}\n\nfunction filterByStatus(\n surfaces: ColumnSurface[],\n statuses: SurfaceStatus[] | null | undefined,\n): ColumnSurface[] {\n if (statuses == null) return surfaces;\n const allowed = new Set(statuses);\n return surfaces.filter((d) => allowed.has(d.status));\n}\n\n/** Controlled-or-uncontrolled selection: when `selectedID` is passed (even as\n * `null`) the consumer owns it; otherwise it is kept internally. */\nfunction useSurfaceSelection(\n selectedID: ColumnSurface[\"id\"] | null | undefined,\n onSelect: SurfaceSelectionHandler | undefined,\n): [ColumnSurface[\"id\"] | null, SurfaceSelectionHandler] {\n const [internal, setInternal] = useState<ColumnSurface[\"id\"] | null>(null);\n const isControlled = selectedID !== undefined;\n\n const select: SurfaceSelectionHandler = useCallback(\n (surface) => {\n setInternal(surface?.id ?? null);\n onSelect?.(surface);\n },\n [onSelect],\n );\n\n let current = internal;\n if (isControlled) {\n current = selectedID;\n }\n return [current, select];\n}\n\ninterface HorizontalExtent {\n left: number;\n width: number;\n}\n\n/** `useLayoutEffect` in the browser, `useEffect` on the server — where it\n * would warn and has nothing to measure anyway. */\nconst useMeasureEffect =\n typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/** Measure where the units column sits within the `Column`, so an overlay\n * can cover exactly the units rather than the axes. `null` while unmeasured,\n * when disabled, or when the units column can't be found. */\nfunction useUnitsColumnExtent(\n ref: RefObject<HTMLDivElement | null>,\n enabled: boolean,\n): HorizontalExtent | null {\n const [extent, setExtent] = useState<HorizontalExtent | null>(null);\n\n const measure = useCallback(() => {\n if (!enabled) return;\n const parent = ref.current?.parentElement;\n const target = parent?.querySelector<HTMLElement>(\n `.${unitsContainerClass()}`,\n );\n if (parent == null || target == null) return;\n\n const p = parent.getBoundingClientRect();\n const t = target.getBoundingClientRect();\n const next = { left: t.left - p.left, width: t.width };\n setExtent((prev) => {\n if (prev?.left === next.left && prev?.width === next.width) {\n return prev;\n }\n return next;\n });\n }, [enabled]);\n\n // Re-measure on every render as a backstop: cheap, and it catches layout\n // changes that no observer reports.\n useMeasureEffect(measure);\n\n useMeasureEffect(() => {\n if (!enabled) {\n setExtent(null);\n return;\n }\n const parent = ref.current?.parentElement;\n if (parent == null) return;\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(measure);\n // The units column *moves* when a sibling changes width — the timescale\n // widening once its intervals load, or its level count changing — and\n // that is not a resize of the units column or of the column itself, so\n // the siblings have to be watched too.\n observer.observe(parent);\n for (const child of Array.from(parent.children)) {\n observer.observe(child);\n }\n return () => observer.disconnect();\n }, [enabled, measure]);\n\n return extent;\n}\n\n/** The (module-scoped) class of the units container rendered by\n * `SectionsColumn`. */\nfunction unitsContainerClass(): string {\n const s = columnStyles as any;\n return (\n s[\"section-units-container\"] ??\n s.sectionUnitsContainer ??\n \"section-units-container\"\n );\n}\n"],"names":["hyper","styles","TIE_POINT_STATUSES","useColumnSurfaces","useMemo","useCompositeScale","useMacrostratColumnData","useRef","classNames","surfacePosition","useCallback","surfaceLabel","useClaimLabelColumn","notes","ColumnNotes","IntervalTag","TagSize","surfaceStatusLabels","useMacrostratDefs","surfaceToken","useState","useEffect","useLayoutEffect","columnStyles"],"mappings":"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"}
1
+ {"version":3,"file":"surfaces.cjs","sources":["../../src/age-model/surfaces.ts"],"sourcesContent":["/** The surfaces view: age-model calibration surfaces drawn on a column.\n *\n * `ColumnSurfaces` is dropped into a `Column` as a child. It draws one line\n * per surface across the units (`ColumnSurfaceLines`) and a notes column of\n * labels beside them (`ColumnSurfaceLabels`), styled by `boundary_status` and\n * `boundary_type`, with hover and selection. The labels sit where the unit\n * labels would, so pass `showLabelColumn: false` to the `Column` while they\n * are shown.\n */\nimport hyper from \"@macrostrat/hyper\";\nimport classNames from \"classnames\";\nimport {\n type CSSProperties,\n type RefObject,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { NoteData } from \"@macrostrat/column-components\";\nimport {\n type IntervalShort,\n IntervalTag,\n TagSize,\n} from \"@macrostrat/data-components\";\nimport { useMacrostratDefs } from \"@macrostrat/data-provider\";\nimport { useCompositeScale, useMacrostratColumnData } from \"../data-provider\";\nimport { ColumnNotes } from \"../notes\";\nimport { AgeLabel } from \"../unit-details/age-range\";\nimport columnStyles from \"../column.module.sass\";\nimport styles from \"./surfaces.module.sass\";\nimport {\n type ColumnSurface,\n type SurfaceStatus,\n formatProportion,\n surfaceLabel,\n surfacePosition,\n surfaceStatusLabels,\n surfaceToken,\n TIE_POINT_STATUSES,\n} from \"./types\";\nimport { type UseColumnSurfacesOptions, useColumnSurfaces } from \"./data\";\n\nconst h = hyper.styled(styles);\n\n/** How far the surface lines extend: across the units column only, or the\n * full width of the column including its axes. */\nexport type SurfaceLinesExtent = \"units\" | \"column\";\n\nexport type SurfaceSelectionHandler = (surface: ColumnSurface | null) => void;\n\nexport interface SurfaceSelectionProps {\n /** The selected surface's id. Pass `null` for a controlled, empty\n * selection; leave undefined for the component to manage selection. */\n selectedSurface?: ColumnSurface[\"id\"] | null;\n onSelectSurface?: SurfaceSelectionHandler;\n}\n\nexport interface ColumnSurfacesProps\n extends UseColumnSurfacesOptions, SurfaceSelectionProps {\n showLines?: boolean;\n showLabels?: boolean;\n extent?: SurfaceLinesExtent;\n /** Width of the labels column, connector included (default 200) */\n labelWidth?: number;\n labelPaddingLeft?: number;\n /** Only show surfaces with these statuses */\n statuses?: SurfaceStatus[] | null;\n /** Which surfaces get a label. Defaults to the tie points — the statuses\n * at which the age model was constrained — so interpolated (`modeled`),\n * unspecified and unit-derived surfaces are drawn as lines only. Pass\n * `null` to label every surface shown. */\n labelStatuses?: SurfaceStatus[] | null;\n /** Labels closer than this (in pixels) are thinned out, since the column\n * has finite room; the selected surface's label is always kept. */\n minLabelSpacing?: number;\n className?: string;\n}\n\nexport function ColumnSurfaces(props: ColumnSurfacesProps) {\n const {\n showLines = true,\n showLabels = true,\n extent = \"units\",\n labelWidth,\n labelPaddingLeft,\n statuses,\n labelStatuses = TIE_POINT_STATUSES,\n minLabelSpacing,\n selectedSurface,\n onSelectSurface,\n className,\n ...dataOptions\n } = props;\n\n const { surfaces: allSurfaces } = useColumnSurfaces(dataOptions);\n const surfaces = useMemo(\n () => filterByStatus(allSurfaces, statuses),\n [allSurfaces, statuses],\n );\n\n const [selectedID, select] = useSurfaceSelection(\n selectedSurface,\n onSelectSurface,\n );\n\n return h([\n h.if(showLines)(ColumnSurfaceLines, {\n surfaces,\n extent,\n selectedSurface: selectedID,\n onSelectSurface: select,\n className,\n }),\n h.if(showLabels)(ColumnSurfaceLabels, {\n surfaces,\n labelStatuses,\n minLabelSpacing,\n width: labelWidth,\n paddingLeft: labelPaddingLeft,\n selectedSurface: selectedID,\n onSelectSurface: select,\n className,\n }),\n ]);\n}\n\nexport interface ColumnSurfaceLinesProps extends SurfaceSelectionProps {\n surfaces: ColumnSurface[];\n extent?: SurfaceLinesExtent;\n className?: string;\n}\n\n/** One horizontal line per surface, overlaid on the column at the surface's\n * age (or measured position, for height and depth columns). */\nexport function ColumnSurfaceLines(props: ColumnSurfaceLinesProps) {\n const {\n surfaces,\n extent = \"units\",\n selectedSurface,\n onSelectSurface,\n className,\n } = props;\n const scale = useCompositeScale();\n const { axisType, totalHeight } = useMacrostratColumnData();\n const intervalMap = useSurfaceIntervalDefs(surfaces);\n\n const ref = useRef<HTMLDivElement>(null);\n const unitsExtent = useUnitsColumnExtent(ref, extent === \"units\");\n\n let style: CSSProperties = { height: totalHeight };\n if (unitsExtent != null) {\n style = { ...style, left: unitsExtent.left, width: unitsExtent.width };\n }\n const measuring = extent === \"units\" && unitsExtent == null;\n\n return h(\n \"div.column-surface-lines\",\n { ref, style, className: classNames(className, { measuring }) },\n surfaces.map((surface) => {\n const position = surfacePosition(surface, axisType);\n if (position == null) return null;\n const y = scale(position);\n if (y == null) return null;\n return h(SurfaceLine, {\n key: surface.id,\n surface,\n y,\n color: surfaceIntervalColor(surface, intervalMap),\n selected: surface.id === selectedSurface,\n onSelect: onSelectSurface,\n });\n }),\n );\n}\n\nfunction SurfaceLine({\n surface,\n y,\n color,\n selected,\n onSelect,\n}: {\n surface: ColumnSurface;\n y: number;\n /** The interval color, when known; otherwise the status color is used */\n color?: string | null;\n selected: boolean;\n onSelect?: SurfaceSelectionHandler;\n}) {\n const onClick = useCallback(\n (evt: MouseEvent) => {\n // Don't let the column deselect its unit\n evt.stopPropagation();\n if (selected) {\n onSelect?.(null);\n } else {\n onSelect?.(surface);\n }\n },\n [surface, selected, onSelect],\n );\n\n let style: CSSProperties = { top: y };\n if (color != null) {\n style = { ...style, \"--surface-color\": color } as CSSProperties;\n }\n\n return h(\n \"div.surface-line\",\n {\n style,\n className: classNames(surfaceClasses(surface), {\n selected,\n interactive: onSelect != null,\n }),\n title: surfaceLabel(surface),\n onClick,\n },\n h(\"div.surface-line-inner\"),\n );\n}\n\nexport interface ColumnSurfaceLabelsProps extends SurfaceSelectionProps {\n surfaces: ColumnSurface[];\n /** Which surfaces get a label (see `ColumnSurfacesProps.labelStatuses`) */\n labelStatuses?: SurfaceStatus[] | null;\n /** Minimum pixel spacing between labels (see `ColumnSurfacesProps`) */\n minLabelSpacing?: number;\n /** Total width, connector included */\n width?: number;\n paddingLeft?: number;\n className?: string;\n}\n\ninterface SurfaceNote extends NoteData {\n /** The interval color, which the note's connector picks up */\n color?: string;\n surface: ColumnSurface;\n /** The calibration interval, with color and rank from the definitions */\n interval: IntervalShort | null;\n selected: boolean;\n}\n\n/** A notes column of surface labels, laid out beside the units with the\n * collision avoidance of `NotesColumn`. It draws over the unit labels, so the\n * `Column` should hide its label column (`showLabelColumn: false`). */\nexport function ColumnSurfaceLabels(props: ColumnSurfaceLabelsProps) {\n const {\n surfaces,\n labelStatuses = TIE_POINT_STATUSES,\n minLabelSpacing = 22,\n width = 200,\n paddingLeft = 24,\n selectedSurface,\n onSelectSurface,\n className,\n } = props;\n const { axisType } = useMacrostratColumnData();\n const scale = useCompositeScale();\n const intervalMap = useSurfaceIntervalDefs(surfaces);\n\n const labeled = useMemo(() => {\n const byStatus = filterByStatus(surfaces, labelStatuses);\n return thinByPixelSpacing(\n byStatus,\n (s) => scale(surfacePosition(s, axisType) ?? NaN),\n minLabelSpacing,\n selectedSurface,\n );\n }, [\n surfaces,\n labelStatuses,\n minLabelSpacing,\n selectedSurface,\n scale,\n axisType,\n ]);\n\n const notes: SurfaceNote[] = useMemo(() => {\n const _notes: SurfaceNote[] = [];\n for (const surface of labeled) {\n const position = surfacePosition(surface, axisType);\n if (position == null) continue;\n _notes.push({\n id: surface.id,\n height: position,\n note: surfaceLabel(surface),\n // The note's color reaches its connector, so a label's leader line\n // matches the surface line it points at\n color: surfaceIntervalColor(surface, intervalMap) ?? undefined,\n surface,\n interval: calibrationInterval(surface, intervalMap),\n selected: surface.id === selectedSurface,\n });\n }\n return _notes;\n }, [labeled, axisType, selectedSurface, intervalMap]);\n\n const onClickNote = useCallback(\n (note: NoteData) => {\n const { surface, selected } = note as SurfaceNote;\n if (selected) {\n onSelectSurface?.(null);\n } else {\n onSelectSurface?.(surface);\n }\n },\n [onSelectSurface],\n );\n\n return h(\n \"div.column-surface-labels\",\n { className },\n h(ColumnNotes, {\n notes,\n width,\n paddingLeft,\n noteComponent: SurfaceNoteLabel,\n onClickNote,\n forceOptions: LABEL_FORCE_OPTIONS,\n // The leader runs from the surface line in the column to the tag body,\n // continuing a little past both ends rather than stopping in the gap —\n // so it needs no endpoint marker of its own\n connectorOverhang: LABEL_CONNECTOR_OVERHANG,\n showPointMarker: false,\n }),\n );\n}\n\n/** Breathing room between stacked labels, in pixels */\nconst LABEL_FORCE_OPTIONS = { nodeSpacing: 2 };\n\n/** How far the label's leader line continues past each end, in pixels: left\n * into the column to meet the surface line, right under the tag body. */\nconst LABEL_CONNECTOR_OVERHANG: [number, number] = [8, 6];\n\n/** A surface's label: its calibration interval as the standard interval tag\n * (with the position within the interval as the tag's detail), and the\n * modeled age beneath. Surfaces without a calibration show their status. */\nfunction SurfaceNoteLabel({ note }: { note: SurfaceNote }) {\n const { surface, interval, selected } = note;\n\n let primary: any;\n if (interval != null) {\n primary = h(IntervalTag, {\n interval,\n size: TagSize.Small,\n proportion: surface.proportion,\n className: \"surface-interval-tag\",\n });\n } else {\n primary = h(\n \"span.surface-label-status\",\n `${surfaceStatusLabels[surface.status]} surface`,\n );\n }\n\n return h(\n \"div.surface-label\",\n { className: classNames(surfaceClasses(surface), { selected }) },\n h(\"div.surface-label-primary\", primary),\n );\n}\n\n/** The interval definitions for the surfaces' calibration intervals. The age\n * model carries interval ids and names but no colors, so they come from the\n * definitions store (which holds them all, and fetches once). */\nfunction useSurfaceIntervalDefs(\n surfaces: ColumnSurface[],\n): Map<number, any> | null {\n const intervalIDs = useMemo(() => {\n const ids = new Set<number>();\n for (const surface of surfaces) {\n if (surface.calibration != null) ids.add(surface.calibration.id);\n }\n return Array.from(ids);\n }, [surfaces]);\n return useMacrostratDefs(\"intervals\", intervalIDs, null);\n}\n\n/** The color a surface is drawn in: its calibration interval's, so the line\n * and its label's leader read as the interval the surface is tied to. `null`\n * for an uncalibrated surface, or before the definitions load — the status\n * color stands in. */\nfunction surfaceIntervalColor(\n surface: ColumnSurface,\n intervalMap: Map<number, any> | null,\n): string | null {\n const id = surface.calibration?.id;\n if (id == null) return null;\n return intervalMap?.get(id)?.color ?? null;\n}\n\n/** The calibration interval in the shape the interval tag takes, colored\n * from the definitions when they have loaded. */\nfunction calibrationInterval(\n surface: ColumnSurface,\n intervalMap: Map<number, any> | null,\n): IntervalShort | null {\n const { calibration } = surface;\n if (calibration == null) return null;\n const def = intervalMap?.get(calibration.id);\n return {\n id: calibration.id,\n name: calibration.name,\n b_age: calibration.b_age,\n t_age: calibration.t_age,\n color: def?.color,\n rank: def?.rank,\n };\n}\n\n/** Class names shared by the lines, labels, legend and tags, so a status or\n * type reads the same everywhere. */\nexport function surfaceClasses(surface: {\n status?: SurfaceStatus | null;\n type?: string | null;\n}): string {\n return classNames(\n `status-${surfaceToken(surface.status)}`,\n `type-${surfaceToken(surface.type)}`,\n );\n}\n\n/** Walk the surfaces down the column and drop any whose label would sit\n * within `minSpacing` pixels of the last one kept. The selected surface is\n * always kept (and displaces a neighbor if need be). */\nfunction thinByPixelSpacing(\n surfaces: ColumnSurface[],\n pixelPosition: (s: ColumnSurface) => number | null,\n minSpacing: number,\n selectedSurface: ColumnSurface[\"id\"] | null | undefined,\n): ColumnSurface[] {\n if (minSpacing <= 0) return surfaces;\n const positioned = surfaces\n .map((surface) => ({ surface, y: pixelPosition(surface) }))\n .filter((d) => d.y != null && !Number.isNaN(d.y))\n .sort((a, b) => a.y - b.y);\n\n const kept: { surface: ColumnSurface; y: number }[] = [];\n for (const item of positioned) {\n const selected = item.surface.id === selectedSurface;\n const last = kept[kept.length - 1];\n if (last == null || item.y - last.y >= minSpacing) {\n kept.push(item);\n } else if (selected) {\n // The selection wins its slot\n kept[kept.length - 1] = item;\n }\n }\n return kept.map((d) => d.surface);\n}\n\nfunction filterByStatus(\n surfaces: ColumnSurface[],\n statuses: SurfaceStatus[] | null | undefined,\n): ColumnSurface[] {\n if (statuses == null) return surfaces;\n const allowed = new Set(statuses);\n return surfaces.filter((d) => allowed.has(d.status));\n}\n\n/** Controlled-or-uncontrolled selection: when `selectedID` is passed (even as\n * `null`) the consumer owns it; otherwise it is kept internally. */\nfunction useSurfaceSelection(\n selectedID: ColumnSurface[\"id\"] | null | undefined,\n onSelect: SurfaceSelectionHandler | undefined,\n): [ColumnSurface[\"id\"] | null, SurfaceSelectionHandler] {\n const [internal, setInternal] = useState<ColumnSurface[\"id\"] | null>(null);\n const isControlled = selectedID !== undefined;\n\n const select: SurfaceSelectionHandler = useCallback(\n (surface) => {\n setInternal(surface?.id ?? null);\n onSelect?.(surface);\n },\n [onSelect],\n );\n\n let current = internal;\n if (isControlled) {\n current = selectedID;\n }\n return [current, select];\n}\n\ninterface HorizontalExtent {\n left: number;\n width: number;\n}\n\n/** `useLayoutEffect` in the browser, `useEffect` on the server — where it\n * would warn and has nothing to measure anyway. */\nconst useMeasureEffect =\n typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/** Measure where the units column sits within the `Column`, so an overlay\n * can cover exactly the units rather than the axes. `null` while unmeasured,\n * when disabled, or when the units column can't be found. */\nfunction useUnitsColumnExtent(\n ref: RefObject<HTMLDivElement | null>,\n enabled: boolean,\n): HorizontalExtent | null {\n const [extent, setExtent] = useState<HorizontalExtent | null>(null);\n\n const measure = useCallback(() => {\n if (!enabled) return;\n const parent = ref.current?.parentElement;\n const target = parent?.querySelector<HTMLElement>(\n `.${unitsContainerClass()}`,\n );\n if (parent == null || target == null) return;\n\n const p = parent.getBoundingClientRect();\n const t = target.getBoundingClientRect();\n const next = { left: t.left - p.left, width: t.width };\n setExtent((prev) => {\n if (prev?.left === next.left && prev?.width === next.width) {\n return prev;\n }\n return next;\n });\n }, [enabled]);\n\n // Re-measure on every render as a backstop: cheap, and it catches layout\n // changes that no observer reports.\n useMeasureEffect(measure);\n\n useMeasureEffect(() => {\n if (!enabled) {\n setExtent(null);\n return;\n }\n const parent = ref.current?.parentElement;\n if (parent == null) return;\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(measure);\n // The units column *moves* when a sibling changes width — the timescale\n // widening once its intervals load, or its level count changing — and\n // that is not a resize of the units column or of the column itself, so\n // the siblings have to be watched too.\n observer.observe(parent);\n for (const child of Array.from(parent.children)) {\n observer.observe(child);\n }\n return () => observer.disconnect();\n }, [enabled, measure]);\n\n return extent;\n}\n\n/** The (module-scoped) class of the units container rendered by\n * `SectionsColumn`. */\nfunction unitsContainerClass(): string {\n const s = columnStyles as any;\n return (\n s[\"section-units-container\"] ??\n s.sectionUnitsContainer ??\n \"section-units-container\"\n );\n}\n"],"names":["hyper","styles","TIE_POINT_STATUSES","useColumnSurfaces","useMemo","useCompositeScale","useMacrostratColumnData","useRef","classNames","surfacePosition","useCallback","surfaceLabel","notes","ColumnNotes","IntervalTag","TagSize","surfaceStatusLabels","useMacrostratDefs","surfaceToken","useState","useEffect","useLayoutEffect","columnStyles"],"mappings":"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"}
@@ -55,8 +55,8 @@ export interface ColumnSurfaceLabelsProps extends SurfaceSelectionProps {
55
55
  className?: string;
56
56
  }
57
57
  /** A notes column of surface labels, laid out beside the units with the
58
- * collision avoidance of `NotesColumn`. It takes the label column over from
59
- * the unit labels while mounted: labels are one or the other. */
58
+ * collision avoidance of `NotesColumn`. It draws over the unit labels, so the
59
+ * `Column` should hide its label column (`showLabelColumn: false`). */
60
60
  export declare function ColumnSurfaceLabels(props: ColumnSurfaceLabelsProps): import('react').ReactElement<{
61
61
  className: string | undefined;
62
62
  }, string | import('react').JSXElementConstructor<any>>;
@@ -8,7 +8,7 @@ import h$3 from "../column.module.sass.js";
8
8
  import h$2 from "./surfaces.module.sass.js";
9
9
  import { TIE_POINT_STATUSES, surfacePosition, surfaceLabel, surfaceToken, surfaceStatusLabels } from "./types.js";
10
10
  import { useColumnSurfaces } from "./data.js";
11
- import { useMacrostratColumnData, useCompositeScale, useClaimLabelColumn } from "../data-provider/store.js";
11
+ import { useMacrostratColumnData, useCompositeScale } from "../data-provider/store.js";
12
12
  const h = h$1.styled(h$2);
13
13
  function ColumnSurfaces(props) {
14
14
  const {
@@ -141,7 +141,6 @@ function ColumnSurfaceLabels(props) {
141
141
  const { axisType } = useMacrostratColumnData();
142
142
  const scale = useCompositeScale();
143
143
  const intervalMap = useSurfaceIntervalDefs(surfaces);
144
- useClaimLabelColumn();
145
144
  const labeled = useMemo(() => {
146
145
  const byStatus = filterByStatus(surfaces, labelStatuses);
147
146
  return thinByPixelSpacing(
@@ -1 +1 @@
1
- {"version":3,"file":"surfaces.js","sources":["../../src/age-model/surfaces.ts"],"sourcesContent":["/** The surfaces view: age-model calibration surfaces drawn on a column.\n *\n * `ColumnSurfaces` is dropped into a `Column` as a child. It draws one line\n * per surface across the units (`ColumnSurfaceLines`) and a notes column of\n * labels beside them (`ColumnSurfaceLabels`), styled by `boundary_status` and\n * `boundary_type`, with hover and selection.\n */\nimport hyper from \"@macrostrat/hyper\";\nimport classNames from \"classnames\";\nimport {\n type CSSProperties,\n type RefObject,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { NoteData } from \"@macrostrat/column-components\";\nimport {\n type IntervalShort,\n IntervalTag,\n TagSize,\n} from \"@macrostrat/data-components\";\nimport { useMacrostratDefs } from \"@macrostrat/data-provider\";\nimport {\n useClaimLabelColumn,\n useCompositeScale,\n useMacrostratColumnData,\n} from \"../data-provider\";\nimport { ColumnNotes } from \"../notes\";\nimport { AgeLabel } from \"../unit-details/age-range\";\nimport columnStyles from \"../column.module.sass\";\nimport styles from \"./surfaces.module.sass\";\nimport {\n type ColumnSurface,\n type SurfaceStatus,\n formatProportion,\n surfaceLabel,\n surfacePosition,\n surfaceStatusLabels,\n surfaceToken,\n TIE_POINT_STATUSES,\n} from \"./types\";\nimport { type UseColumnSurfacesOptions, useColumnSurfaces } from \"./data\";\n\nconst h = hyper.styled(styles);\n\n/** How far the surface lines extend: across the units column only, or the\n * full width of the column including its axes. */\nexport type SurfaceLinesExtent = \"units\" | \"column\";\n\nexport type SurfaceSelectionHandler = (surface: ColumnSurface | null) => void;\n\nexport interface SurfaceSelectionProps {\n /** The selected surface's id. Pass `null` for a controlled, empty\n * selection; leave undefined for the component to manage selection. */\n selectedSurface?: ColumnSurface[\"id\"] | null;\n onSelectSurface?: SurfaceSelectionHandler;\n}\n\nexport interface ColumnSurfacesProps\n extends UseColumnSurfacesOptions, SurfaceSelectionProps {\n showLines?: boolean;\n showLabels?: boolean;\n extent?: SurfaceLinesExtent;\n /** Width of the labels column, connector included (default 200) */\n labelWidth?: number;\n labelPaddingLeft?: number;\n /** Only show surfaces with these statuses */\n statuses?: SurfaceStatus[] | null;\n /** Which surfaces get a label. Defaults to the tie points — the statuses\n * at which the age model was constrained — so interpolated (`modeled`),\n * unspecified and unit-derived surfaces are drawn as lines only. Pass\n * `null` to label every surface shown. */\n labelStatuses?: SurfaceStatus[] | null;\n /** Labels closer than this (in pixels) are thinned out, since the column\n * has finite room; the selected surface's label is always kept. */\n minLabelSpacing?: number;\n className?: string;\n}\n\nexport function ColumnSurfaces(props: ColumnSurfacesProps) {\n const {\n showLines = true,\n showLabels = true,\n extent = \"units\",\n labelWidth,\n labelPaddingLeft,\n statuses,\n labelStatuses = TIE_POINT_STATUSES,\n minLabelSpacing,\n selectedSurface,\n onSelectSurface,\n className,\n ...dataOptions\n } = props;\n\n const { surfaces: allSurfaces } = useColumnSurfaces(dataOptions);\n const surfaces = useMemo(\n () => filterByStatus(allSurfaces, statuses),\n [allSurfaces, statuses],\n );\n\n const [selectedID, select] = useSurfaceSelection(\n selectedSurface,\n onSelectSurface,\n );\n\n return h([\n h.if(showLines)(ColumnSurfaceLines, {\n surfaces,\n extent,\n selectedSurface: selectedID,\n onSelectSurface: select,\n className,\n }),\n h.if(showLabels)(ColumnSurfaceLabels, {\n surfaces,\n labelStatuses,\n minLabelSpacing,\n width: labelWidth,\n paddingLeft: labelPaddingLeft,\n selectedSurface: selectedID,\n onSelectSurface: select,\n className,\n }),\n ]);\n}\n\nexport interface ColumnSurfaceLinesProps extends SurfaceSelectionProps {\n surfaces: ColumnSurface[];\n extent?: SurfaceLinesExtent;\n className?: string;\n}\n\n/** One horizontal line per surface, overlaid on the column at the surface's\n * age (or measured position, for height and depth columns). */\nexport function ColumnSurfaceLines(props: ColumnSurfaceLinesProps) {\n const {\n surfaces,\n extent = \"units\",\n selectedSurface,\n onSelectSurface,\n className,\n } = props;\n const scale = useCompositeScale();\n const { axisType, totalHeight } = useMacrostratColumnData();\n const intervalMap = useSurfaceIntervalDefs(surfaces);\n\n const ref = useRef<HTMLDivElement>(null);\n const unitsExtent = useUnitsColumnExtent(ref, extent === \"units\");\n\n let style: CSSProperties = { height: totalHeight };\n if (unitsExtent != null) {\n style = { ...style, left: unitsExtent.left, width: unitsExtent.width };\n }\n const measuring = extent === \"units\" && unitsExtent == null;\n\n return h(\n \"div.column-surface-lines\",\n { ref, style, className: classNames(className, { measuring }) },\n surfaces.map((surface) => {\n const position = surfacePosition(surface, axisType);\n if (position == null) return null;\n const y = scale(position);\n if (y == null) return null;\n return h(SurfaceLine, {\n key: surface.id,\n surface,\n y,\n color: surfaceIntervalColor(surface, intervalMap),\n selected: surface.id === selectedSurface,\n onSelect: onSelectSurface,\n });\n }),\n );\n}\n\nfunction SurfaceLine({\n surface,\n y,\n color,\n selected,\n onSelect,\n}: {\n surface: ColumnSurface;\n y: number;\n /** The interval color, when known; otherwise the status color is used */\n color?: string | null;\n selected: boolean;\n onSelect?: SurfaceSelectionHandler;\n}) {\n const onClick = useCallback(\n (evt: MouseEvent) => {\n // Don't let the column deselect its unit\n evt.stopPropagation();\n if (selected) {\n onSelect?.(null);\n } else {\n onSelect?.(surface);\n }\n },\n [surface, selected, onSelect],\n );\n\n let style: CSSProperties = { top: y };\n if (color != null) {\n style = { ...style, \"--surface-color\": color } as CSSProperties;\n }\n\n return h(\n \"div.surface-line\",\n {\n style,\n className: classNames(surfaceClasses(surface), {\n selected,\n interactive: onSelect != null,\n }),\n title: surfaceLabel(surface),\n onClick,\n },\n h(\"div.surface-line-inner\"),\n );\n}\n\nexport interface ColumnSurfaceLabelsProps extends SurfaceSelectionProps {\n surfaces: ColumnSurface[];\n /** Which surfaces get a label (see `ColumnSurfacesProps.labelStatuses`) */\n labelStatuses?: SurfaceStatus[] | null;\n /** Minimum pixel spacing between labels (see `ColumnSurfacesProps`) */\n minLabelSpacing?: number;\n /** Total width, connector included */\n width?: number;\n paddingLeft?: number;\n className?: string;\n}\n\ninterface SurfaceNote extends NoteData {\n /** The interval color, which the note's connector picks up */\n color?: string;\n surface: ColumnSurface;\n /** The calibration interval, with color and rank from the definitions */\n interval: IntervalShort | null;\n selected: boolean;\n}\n\n/** A notes column of surface labels, laid out beside the units with the\n * collision avoidance of `NotesColumn`. It takes the label column over from\n * the unit labels while mounted: labels are one or the other. */\nexport function ColumnSurfaceLabels(props: ColumnSurfaceLabelsProps) {\n const {\n surfaces,\n labelStatuses = TIE_POINT_STATUSES,\n minLabelSpacing = 22,\n width = 200,\n paddingLeft = 24,\n selectedSurface,\n onSelectSurface,\n className,\n } = props;\n const { axisType } = useMacrostratColumnData();\n const scale = useCompositeScale();\n const intervalMap = useSurfaceIntervalDefs(surfaces);\n useClaimLabelColumn();\n\n const labeled = useMemo(() => {\n const byStatus = filterByStatus(surfaces, labelStatuses);\n return thinByPixelSpacing(\n byStatus,\n (s) => scale(surfacePosition(s, axisType) ?? NaN),\n minLabelSpacing,\n selectedSurface,\n );\n }, [\n surfaces,\n labelStatuses,\n minLabelSpacing,\n selectedSurface,\n scale,\n axisType,\n ]);\n\n const notes: SurfaceNote[] = useMemo(() => {\n const _notes: SurfaceNote[] = [];\n for (const surface of labeled) {\n const position = surfacePosition(surface, axisType);\n if (position == null) continue;\n _notes.push({\n id: surface.id,\n height: position,\n note: surfaceLabel(surface),\n // The note's color reaches its connector, so a label's leader line\n // matches the surface line it points at\n color: surfaceIntervalColor(surface, intervalMap) ?? undefined,\n surface,\n interval: calibrationInterval(surface, intervalMap),\n selected: surface.id === selectedSurface,\n });\n }\n return _notes;\n }, [labeled, axisType, selectedSurface, intervalMap]);\n\n const onClickNote = useCallback(\n (note: NoteData) => {\n const { surface, selected } = note as SurfaceNote;\n if (selected) {\n onSelectSurface?.(null);\n } else {\n onSelectSurface?.(surface);\n }\n },\n [onSelectSurface],\n );\n\n return h(\n \"div.column-surface-labels\",\n { className },\n h(ColumnNotes, {\n notes,\n width,\n paddingLeft,\n noteComponent: SurfaceNoteLabel,\n onClickNote,\n forceOptions: LABEL_FORCE_OPTIONS,\n // The leader runs from the surface line in the column to the tag body,\n // continuing a little past both ends rather than stopping in the gap —\n // so it needs no endpoint marker of its own\n connectorOverhang: LABEL_CONNECTOR_OVERHANG,\n showPointMarker: false,\n }),\n );\n}\n\n/** Breathing room between stacked labels, in pixels */\nconst LABEL_FORCE_OPTIONS = { nodeSpacing: 2 };\n\n/** How far the label's leader line continues past each end, in pixels: left\n * into the column to meet the surface line, right under the tag body. */\nconst LABEL_CONNECTOR_OVERHANG: [number, number] = [8, 6];\n\n/** A surface's label: its calibration interval as the standard interval tag\n * (with the position within the interval as the tag's detail), and the\n * modeled age beneath. Surfaces without a calibration show their status. */\nfunction SurfaceNoteLabel({ note }: { note: SurfaceNote }) {\n const { surface, interval, selected } = note;\n\n let primary: any;\n if (interval != null) {\n primary = h(IntervalTag, {\n interval,\n size: TagSize.Small,\n proportion: surface.proportion,\n className: \"surface-interval-tag\",\n });\n } else {\n primary = h(\n \"span.surface-label-status\",\n `${surfaceStatusLabels[surface.status]} surface`,\n );\n }\n\n return h(\n \"div.surface-label\",\n { className: classNames(surfaceClasses(surface), { selected }) },\n h(\"div.surface-label-primary\", primary),\n );\n}\n\n/** The interval definitions for the surfaces' calibration intervals. The age\n * model carries interval ids and names but no colors, so they come from the\n * definitions store (which holds them all, and fetches once). */\nfunction useSurfaceIntervalDefs(\n surfaces: ColumnSurface[],\n): Map<number, any> | null {\n const intervalIDs = useMemo(() => {\n const ids = new Set<number>();\n for (const surface of surfaces) {\n if (surface.calibration != null) ids.add(surface.calibration.id);\n }\n return Array.from(ids);\n }, [surfaces]);\n return useMacrostratDefs(\"intervals\", intervalIDs, null);\n}\n\n/** The color a surface is drawn in: its calibration interval's, so the line\n * and its label's leader read as the interval the surface is tied to. `null`\n * for an uncalibrated surface, or before the definitions load — the status\n * color stands in. */\nfunction surfaceIntervalColor(\n surface: ColumnSurface,\n intervalMap: Map<number, any> | null,\n): string | null {\n const id = surface.calibration?.id;\n if (id == null) return null;\n return intervalMap?.get(id)?.color ?? null;\n}\n\n/** The calibration interval in the shape the interval tag takes, colored\n * from the definitions when they have loaded. */\nfunction calibrationInterval(\n surface: ColumnSurface,\n intervalMap: Map<number, any> | null,\n): IntervalShort | null {\n const { calibration } = surface;\n if (calibration == null) return null;\n const def = intervalMap?.get(calibration.id);\n return {\n id: calibration.id,\n name: calibration.name,\n b_age: calibration.b_age,\n t_age: calibration.t_age,\n color: def?.color,\n rank: def?.rank,\n };\n}\n\n/** Class names shared by the lines, labels, legend and tags, so a status or\n * type reads the same everywhere. */\nexport function surfaceClasses(surface: {\n status?: SurfaceStatus | null;\n type?: string | null;\n}): string {\n return classNames(\n `status-${surfaceToken(surface.status)}`,\n `type-${surfaceToken(surface.type)}`,\n );\n}\n\n/** Walk the surfaces down the column and drop any whose label would sit\n * within `minSpacing` pixels of the last one kept. The selected surface is\n * always kept (and displaces a neighbor if need be). */\nfunction thinByPixelSpacing(\n surfaces: ColumnSurface[],\n pixelPosition: (s: ColumnSurface) => number | null,\n minSpacing: number,\n selectedSurface: ColumnSurface[\"id\"] | null | undefined,\n): ColumnSurface[] {\n if (minSpacing <= 0) return surfaces;\n const positioned = surfaces\n .map((surface) => ({ surface, y: pixelPosition(surface) }))\n .filter((d) => d.y != null && !Number.isNaN(d.y))\n .sort((a, b) => a.y - b.y);\n\n const kept: { surface: ColumnSurface; y: number }[] = [];\n for (const item of positioned) {\n const selected = item.surface.id === selectedSurface;\n const last = kept[kept.length - 1];\n if (last == null || item.y - last.y >= minSpacing) {\n kept.push(item);\n } else if (selected) {\n // The selection wins its slot\n kept[kept.length - 1] = item;\n }\n }\n return kept.map((d) => d.surface);\n}\n\nfunction filterByStatus(\n surfaces: ColumnSurface[],\n statuses: SurfaceStatus[] | null | undefined,\n): ColumnSurface[] {\n if (statuses == null) return surfaces;\n const allowed = new Set(statuses);\n return surfaces.filter((d) => allowed.has(d.status));\n}\n\n/** Controlled-or-uncontrolled selection: when `selectedID` is passed (even as\n * `null`) the consumer owns it; otherwise it is kept internally. */\nfunction useSurfaceSelection(\n selectedID: ColumnSurface[\"id\"] | null | undefined,\n onSelect: SurfaceSelectionHandler | undefined,\n): [ColumnSurface[\"id\"] | null, SurfaceSelectionHandler] {\n const [internal, setInternal] = useState<ColumnSurface[\"id\"] | null>(null);\n const isControlled = selectedID !== undefined;\n\n const select: SurfaceSelectionHandler = useCallback(\n (surface) => {\n setInternal(surface?.id ?? null);\n onSelect?.(surface);\n },\n [onSelect],\n );\n\n let current = internal;\n if (isControlled) {\n current = selectedID;\n }\n return [current, select];\n}\n\ninterface HorizontalExtent {\n left: number;\n width: number;\n}\n\n/** `useLayoutEffect` in the browser, `useEffect` on the server — where it\n * would warn and has nothing to measure anyway. */\nconst useMeasureEffect =\n typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/** Measure where the units column sits within the `Column`, so an overlay\n * can cover exactly the units rather than the axes. `null` while unmeasured,\n * when disabled, or when the units column can't be found. */\nfunction useUnitsColumnExtent(\n ref: RefObject<HTMLDivElement | null>,\n enabled: boolean,\n): HorizontalExtent | null {\n const [extent, setExtent] = useState<HorizontalExtent | null>(null);\n\n const measure = useCallback(() => {\n if (!enabled) return;\n const parent = ref.current?.parentElement;\n const target = parent?.querySelector<HTMLElement>(\n `.${unitsContainerClass()}`,\n );\n if (parent == null || target == null) return;\n\n const p = parent.getBoundingClientRect();\n const t = target.getBoundingClientRect();\n const next = { left: t.left - p.left, width: t.width };\n setExtent((prev) => {\n if (prev?.left === next.left && prev?.width === next.width) {\n return prev;\n }\n return next;\n });\n }, [enabled]);\n\n // Re-measure on every render as a backstop: cheap, and it catches layout\n // changes that no observer reports.\n useMeasureEffect(measure);\n\n useMeasureEffect(() => {\n if (!enabled) {\n setExtent(null);\n return;\n }\n const parent = ref.current?.parentElement;\n if (parent == null) return;\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(measure);\n // The units column *moves* when a sibling changes width — the timescale\n // widening once its intervals load, or its level count changing — and\n // that is not a resize of the units column or of the column itself, so\n // the siblings have to be watched too.\n observer.observe(parent);\n for (const child of Array.from(parent.children)) {\n observer.observe(child);\n }\n return () => observer.disconnect();\n }, [enabled, measure]);\n\n return extent;\n}\n\n/** The (module-scoped) class of the units container rendered by\n * `SectionsColumn`. */\nfunction unitsContainerClass(): string {\n const s = columnStyles as any;\n return (\n s[\"section-units-container\"] ??\n s.sectionUnitsContainer ??\n \"section-units-container\"\n );\n}\n"],"names":["hyper","styles","columnStyles"],"mappings":"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"}
1
+ {"version":3,"file":"surfaces.js","sources":["../../src/age-model/surfaces.ts"],"sourcesContent":["/** The surfaces view: age-model calibration surfaces drawn on a column.\n *\n * `ColumnSurfaces` is dropped into a `Column` as a child. It draws one line\n * per surface across the units (`ColumnSurfaceLines`) and a notes column of\n * labels beside them (`ColumnSurfaceLabels`), styled by `boundary_status` and\n * `boundary_type`, with hover and selection. The labels sit where the unit\n * labels would, so pass `showLabelColumn: false` to the `Column` while they\n * are shown.\n */\nimport hyper from \"@macrostrat/hyper\";\nimport classNames from \"classnames\";\nimport {\n type CSSProperties,\n type RefObject,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { NoteData } from \"@macrostrat/column-components\";\nimport {\n type IntervalShort,\n IntervalTag,\n TagSize,\n} from \"@macrostrat/data-components\";\nimport { useMacrostratDefs } from \"@macrostrat/data-provider\";\nimport { useCompositeScale, useMacrostratColumnData } from \"../data-provider\";\nimport { ColumnNotes } from \"../notes\";\nimport { AgeLabel } from \"../unit-details/age-range\";\nimport columnStyles from \"../column.module.sass\";\nimport styles from \"./surfaces.module.sass\";\nimport {\n type ColumnSurface,\n type SurfaceStatus,\n formatProportion,\n surfaceLabel,\n surfacePosition,\n surfaceStatusLabels,\n surfaceToken,\n TIE_POINT_STATUSES,\n} from \"./types\";\nimport { type UseColumnSurfacesOptions, useColumnSurfaces } from \"./data\";\n\nconst h = hyper.styled(styles);\n\n/** How far the surface lines extend: across the units column only, or the\n * full width of the column including its axes. */\nexport type SurfaceLinesExtent = \"units\" | \"column\";\n\nexport type SurfaceSelectionHandler = (surface: ColumnSurface | null) => void;\n\nexport interface SurfaceSelectionProps {\n /** The selected surface's id. Pass `null` for a controlled, empty\n * selection; leave undefined for the component to manage selection. */\n selectedSurface?: ColumnSurface[\"id\"] | null;\n onSelectSurface?: SurfaceSelectionHandler;\n}\n\nexport interface ColumnSurfacesProps\n extends UseColumnSurfacesOptions, SurfaceSelectionProps {\n showLines?: boolean;\n showLabels?: boolean;\n extent?: SurfaceLinesExtent;\n /** Width of the labels column, connector included (default 200) */\n labelWidth?: number;\n labelPaddingLeft?: number;\n /** Only show surfaces with these statuses */\n statuses?: SurfaceStatus[] | null;\n /** Which surfaces get a label. Defaults to the tie points — the statuses\n * at which the age model was constrained — so interpolated (`modeled`),\n * unspecified and unit-derived surfaces are drawn as lines only. Pass\n * `null` to label every surface shown. */\n labelStatuses?: SurfaceStatus[] | null;\n /** Labels closer than this (in pixels) are thinned out, since the column\n * has finite room; the selected surface's label is always kept. */\n minLabelSpacing?: number;\n className?: string;\n}\n\nexport function ColumnSurfaces(props: ColumnSurfacesProps) {\n const {\n showLines = true,\n showLabels = true,\n extent = \"units\",\n labelWidth,\n labelPaddingLeft,\n statuses,\n labelStatuses = TIE_POINT_STATUSES,\n minLabelSpacing,\n selectedSurface,\n onSelectSurface,\n className,\n ...dataOptions\n } = props;\n\n const { surfaces: allSurfaces } = useColumnSurfaces(dataOptions);\n const surfaces = useMemo(\n () => filterByStatus(allSurfaces, statuses),\n [allSurfaces, statuses],\n );\n\n const [selectedID, select] = useSurfaceSelection(\n selectedSurface,\n onSelectSurface,\n );\n\n return h([\n h.if(showLines)(ColumnSurfaceLines, {\n surfaces,\n extent,\n selectedSurface: selectedID,\n onSelectSurface: select,\n className,\n }),\n h.if(showLabels)(ColumnSurfaceLabels, {\n surfaces,\n labelStatuses,\n minLabelSpacing,\n width: labelWidth,\n paddingLeft: labelPaddingLeft,\n selectedSurface: selectedID,\n onSelectSurface: select,\n className,\n }),\n ]);\n}\n\nexport interface ColumnSurfaceLinesProps extends SurfaceSelectionProps {\n surfaces: ColumnSurface[];\n extent?: SurfaceLinesExtent;\n className?: string;\n}\n\n/** One horizontal line per surface, overlaid on the column at the surface's\n * age (or measured position, for height and depth columns). */\nexport function ColumnSurfaceLines(props: ColumnSurfaceLinesProps) {\n const {\n surfaces,\n extent = \"units\",\n selectedSurface,\n onSelectSurface,\n className,\n } = props;\n const scale = useCompositeScale();\n const { axisType, totalHeight } = useMacrostratColumnData();\n const intervalMap = useSurfaceIntervalDefs(surfaces);\n\n const ref = useRef<HTMLDivElement>(null);\n const unitsExtent = useUnitsColumnExtent(ref, extent === \"units\");\n\n let style: CSSProperties = { height: totalHeight };\n if (unitsExtent != null) {\n style = { ...style, left: unitsExtent.left, width: unitsExtent.width };\n }\n const measuring = extent === \"units\" && unitsExtent == null;\n\n return h(\n \"div.column-surface-lines\",\n { ref, style, className: classNames(className, { measuring }) },\n surfaces.map((surface) => {\n const position = surfacePosition(surface, axisType);\n if (position == null) return null;\n const y = scale(position);\n if (y == null) return null;\n return h(SurfaceLine, {\n key: surface.id,\n surface,\n y,\n color: surfaceIntervalColor(surface, intervalMap),\n selected: surface.id === selectedSurface,\n onSelect: onSelectSurface,\n });\n }),\n );\n}\n\nfunction SurfaceLine({\n surface,\n y,\n color,\n selected,\n onSelect,\n}: {\n surface: ColumnSurface;\n y: number;\n /** The interval color, when known; otherwise the status color is used */\n color?: string | null;\n selected: boolean;\n onSelect?: SurfaceSelectionHandler;\n}) {\n const onClick = useCallback(\n (evt: MouseEvent) => {\n // Don't let the column deselect its unit\n evt.stopPropagation();\n if (selected) {\n onSelect?.(null);\n } else {\n onSelect?.(surface);\n }\n },\n [surface, selected, onSelect],\n );\n\n let style: CSSProperties = { top: y };\n if (color != null) {\n style = { ...style, \"--surface-color\": color } as CSSProperties;\n }\n\n return h(\n \"div.surface-line\",\n {\n style,\n className: classNames(surfaceClasses(surface), {\n selected,\n interactive: onSelect != null,\n }),\n title: surfaceLabel(surface),\n onClick,\n },\n h(\"div.surface-line-inner\"),\n );\n}\n\nexport interface ColumnSurfaceLabelsProps extends SurfaceSelectionProps {\n surfaces: ColumnSurface[];\n /** Which surfaces get a label (see `ColumnSurfacesProps.labelStatuses`) */\n labelStatuses?: SurfaceStatus[] | null;\n /** Minimum pixel spacing between labels (see `ColumnSurfacesProps`) */\n minLabelSpacing?: number;\n /** Total width, connector included */\n width?: number;\n paddingLeft?: number;\n className?: string;\n}\n\ninterface SurfaceNote extends NoteData {\n /** The interval color, which the note's connector picks up */\n color?: string;\n surface: ColumnSurface;\n /** The calibration interval, with color and rank from the definitions */\n interval: IntervalShort | null;\n selected: boolean;\n}\n\n/** A notes column of surface labels, laid out beside the units with the\n * collision avoidance of `NotesColumn`. It draws over the unit labels, so the\n * `Column` should hide its label column (`showLabelColumn: false`). */\nexport function ColumnSurfaceLabels(props: ColumnSurfaceLabelsProps) {\n const {\n surfaces,\n labelStatuses = TIE_POINT_STATUSES,\n minLabelSpacing = 22,\n width = 200,\n paddingLeft = 24,\n selectedSurface,\n onSelectSurface,\n className,\n } = props;\n const { axisType } = useMacrostratColumnData();\n const scale = useCompositeScale();\n const intervalMap = useSurfaceIntervalDefs(surfaces);\n\n const labeled = useMemo(() => {\n const byStatus = filterByStatus(surfaces, labelStatuses);\n return thinByPixelSpacing(\n byStatus,\n (s) => scale(surfacePosition(s, axisType) ?? NaN),\n minLabelSpacing,\n selectedSurface,\n );\n }, [\n surfaces,\n labelStatuses,\n minLabelSpacing,\n selectedSurface,\n scale,\n axisType,\n ]);\n\n const notes: SurfaceNote[] = useMemo(() => {\n const _notes: SurfaceNote[] = [];\n for (const surface of labeled) {\n const position = surfacePosition(surface, axisType);\n if (position == null) continue;\n _notes.push({\n id: surface.id,\n height: position,\n note: surfaceLabel(surface),\n // The note's color reaches its connector, so a label's leader line\n // matches the surface line it points at\n color: surfaceIntervalColor(surface, intervalMap) ?? undefined,\n surface,\n interval: calibrationInterval(surface, intervalMap),\n selected: surface.id === selectedSurface,\n });\n }\n return _notes;\n }, [labeled, axisType, selectedSurface, intervalMap]);\n\n const onClickNote = useCallback(\n (note: NoteData) => {\n const { surface, selected } = note as SurfaceNote;\n if (selected) {\n onSelectSurface?.(null);\n } else {\n onSelectSurface?.(surface);\n }\n },\n [onSelectSurface],\n );\n\n return h(\n \"div.column-surface-labels\",\n { className },\n h(ColumnNotes, {\n notes,\n width,\n paddingLeft,\n noteComponent: SurfaceNoteLabel,\n onClickNote,\n forceOptions: LABEL_FORCE_OPTIONS,\n // The leader runs from the surface line in the column to the tag body,\n // continuing a little past both ends rather than stopping in the gap —\n // so it needs no endpoint marker of its own\n connectorOverhang: LABEL_CONNECTOR_OVERHANG,\n showPointMarker: false,\n }),\n );\n}\n\n/** Breathing room between stacked labels, in pixels */\nconst LABEL_FORCE_OPTIONS = { nodeSpacing: 2 };\n\n/** How far the label's leader line continues past each end, in pixels: left\n * into the column to meet the surface line, right under the tag body. */\nconst LABEL_CONNECTOR_OVERHANG: [number, number] = [8, 6];\n\n/** A surface's label: its calibration interval as the standard interval tag\n * (with the position within the interval as the tag's detail), and the\n * modeled age beneath. Surfaces without a calibration show their status. */\nfunction SurfaceNoteLabel({ note }: { note: SurfaceNote }) {\n const { surface, interval, selected } = note;\n\n let primary: any;\n if (interval != null) {\n primary = h(IntervalTag, {\n interval,\n size: TagSize.Small,\n proportion: surface.proportion,\n className: \"surface-interval-tag\",\n });\n } else {\n primary = h(\n \"span.surface-label-status\",\n `${surfaceStatusLabels[surface.status]} surface`,\n );\n }\n\n return h(\n \"div.surface-label\",\n { className: classNames(surfaceClasses(surface), { selected }) },\n h(\"div.surface-label-primary\", primary),\n );\n}\n\n/** The interval definitions for the surfaces' calibration intervals. The age\n * model carries interval ids and names but no colors, so they come from the\n * definitions store (which holds them all, and fetches once). */\nfunction useSurfaceIntervalDefs(\n surfaces: ColumnSurface[],\n): Map<number, any> | null {\n const intervalIDs = useMemo(() => {\n const ids = new Set<number>();\n for (const surface of surfaces) {\n if (surface.calibration != null) ids.add(surface.calibration.id);\n }\n return Array.from(ids);\n }, [surfaces]);\n return useMacrostratDefs(\"intervals\", intervalIDs, null);\n}\n\n/** The color a surface is drawn in: its calibration interval's, so the line\n * and its label's leader read as the interval the surface is tied to. `null`\n * for an uncalibrated surface, or before the definitions load — the status\n * color stands in. */\nfunction surfaceIntervalColor(\n surface: ColumnSurface,\n intervalMap: Map<number, any> | null,\n): string | null {\n const id = surface.calibration?.id;\n if (id == null) return null;\n return intervalMap?.get(id)?.color ?? null;\n}\n\n/** The calibration interval in the shape the interval tag takes, colored\n * from the definitions when they have loaded. */\nfunction calibrationInterval(\n surface: ColumnSurface,\n intervalMap: Map<number, any> | null,\n): IntervalShort | null {\n const { calibration } = surface;\n if (calibration == null) return null;\n const def = intervalMap?.get(calibration.id);\n return {\n id: calibration.id,\n name: calibration.name,\n b_age: calibration.b_age,\n t_age: calibration.t_age,\n color: def?.color,\n rank: def?.rank,\n };\n}\n\n/** Class names shared by the lines, labels, legend and tags, so a status or\n * type reads the same everywhere. */\nexport function surfaceClasses(surface: {\n status?: SurfaceStatus | null;\n type?: string | null;\n}): string {\n return classNames(\n `status-${surfaceToken(surface.status)}`,\n `type-${surfaceToken(surface.type)}`,\n );\n}\n\n/** Walk the surfaces down the column and drop any whose label would sit\n * within `minSpacing` pixels of the last one kept. The selected surface is\n * always kept (and displaces a neighbor if need be). */\nfunction thinByPixelSpacing(\n surfaces: ColumnSurface[],\n pixelPosition: (s: ColumnSurface) => number | null,\n minSpacing: number,\n selectedSurface: ColumnSurface[\"id\"] | null | undefined,\n): ColumnSurface[] {\n if (minSpacing <= 0) return surfaces;\n const positioned = surfaces\n .map((surface) => ({ surface, y: pixelPosition(surface) }))\n .filter((d) => d.y != null && !Number.isNaN(d.y))\n .sort((a, b) => a.y - b.y);\n\n const kept: { surface: ColumnSurface; y: number }[] = [];\n for (const item of positioned) {\n const selected = item.surface.id === selectedSurface;\n const last = kept[kept.length - 1];\n if (last == null || item.y - last.y >= minSpacing) {\n kept.push(item);\n } else if (selected) {\n // The selection wins its slot\n kept[kept.length - 1] = item;\n }\n }\n return kept.map((d) => d.surface);\n}\n\nfunction filterByStatus(\n surfaces: ColumnSurface[],\n statuses: SurfaceStatus[] | null | undefined,\n): ColumnSurface[] {\n if (statuses == null) return surfaces;\n const allowed = new Set(statuses);\n return surfaces.filter((d) => allowed.has(d.status));\n}\n\n/** Controlled-or-uncontrolled selection: when `selectedID` is passed (even as\n * `null`) the consumer owns it; otherwise it is kept internally. */\nfunction useSurfaceSelection(\n selectedID: ColumnSurface[\"id\"] | null | undefined,\n onSelect: SurfaceSelectionHandler | undefined,\n): [ColumnSurface[\"id\"] | null, SurfaceSelectionHandler] {\n const [internal, setInternal] = useState<ColumnSurface[\"id\"] | null>(null);\n const isControlled = selectedID !== undefined;\n\n const select: SurfaceSelectionHandler = useCallback(\n (surface) => {\n setInternal(surface?.id ?? null);\n onSelect?.(surface);\n },\n [onSelect],\n );\n\n let current = internal;\n if (isControlled) {\n current = selectedID;\n }\n return [current, select];\n}\n\ninterface HorizontalExtent {\n left: number;\n width: number;\n}\n\n/** `useLayoutEffect` in the browser, `useEffect` on the server — where it\n * would warn and has nothing to measure anyway. */\nconst useMeasureEffect =\n typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/** Measure where the units column sits within the `Column`, so an overlay\n * can cover exactly the units rather than the axes. `null` while unmeasured,\n * when disabled, or when the units column can't be found. */\nfunction useUnitsColumnExtent(\n ref: RefObject<HTMLDivElement | null>,\n enabled: boolean,\n): HorizontalExtent | null {\n const [extent, setExtent] = useState<HorizontalExtent | null>(null);\n\n const measure = useCallback(() => {\n if (!enabled) return;\n const parent = ref.current?.parentElement;\n const target = parent?.querySelector<HTMLElement>(\n `.${unitsContainerClass()}`,\n );\n if (parent == null || target == null) return;\n\n const p = parent.getBoundingClientRect();\n const t = target.getBoundingClientRect();\n const next = { left: t.left - p.left, width: t.width };\n setExtent((prev) => {\n if (prev?.left === next.left && prev?.width === next.width) {\n return prev;\n }\n return next;\n });\n }, [enabled]);\n\n // Re-measure on every render as a backstop: cheap, and it catches layout\n // changes that no observer reports.\n useMeasureEffect(measure);\n\n useMeasureEffect(() => {\n if (!enabled) {\n setExtent(null);\n return;\n }\n const parent = ref.current?.parentElement;\n if (parent == null) return;\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(measure);\n // The units column *moves* when a sibling changes width — the timescale\n // widening once its intervals load, or its level count changing — and\n // that is not a resize of the units column or of the column itself, so\n // the siblings have to be watched too.\n observer.observe(parent);\n for (const child of Array.from(parent.children)) {\n observer.observe(child);\n }\n return () => observer.disconnect();\n }, [enabled, measure]);\n\n return extent;\n}\n\n/** The (module-scoped) class of the units container rendered by\n * `SectionsColumn`. */\nfunction unitsContainerClass(): string {\n const s = columnStyles as any;\n return (\n s[\"section-units-container\"] ??\n s.sectionUnitsContainer ??\n \"section-units-container\"\n );\n}\n"],"names":["hyper","styles","columnStyles"],"mappings":"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"}
package/dist/column.cjs CHANGED
@@ -56,16 +56,6 @@ function Column(props) {
56
56
  ageAxisComponent = ageAxis.ApproximateHeightAxis;
57
57
  _axisType = columnComponents.ColumnAxisType.AGE;
58
58
  }
59
- let _sectionOptions = sectionOptions;
60
- if (_sectionOptions == void 0) {
61
- _sectionOptions = (ctx) => {
62
- const n = ctx.units.length;
63
- const expansionFactor = 8 / Math.min(Math.max(n, 1), 8);
64
- return {
65
- targetUnitHeight: targetUnitHeight * expansionFactor
66
- };
67
- };
68
- }
69
59
  const { sections, units, totalHeight } = index.usePreparedColumnUnits(rawUnits, {
70
60
  axisType: _axisType,
71
61
  t_age,
@@ -74,7 +64,7 @@ function Column(props) {
74
64
  b_pos,
75
65
  mergeSections,
76
66
  targetUnitHeight,
77
- sectionOptions: _sectionOptions,
67
+ sectionOptions,
78
68
  unconformityHeight,
79
69
  pixelScale,
80
70
  pixelsPerMyr,
@@ -150,7 +140,6 @@ function ColumnInner(props) {
150
140
  showAgeAxis = true
151
141
  } = props;
152
142
  const { axisType } = store.useMacrostratColumnData();
153
- const labelColumnClaimed = store.useLabelColumnClaimed();
154
143
  const columnRef = unitSelection.useColumnRef();
155
144
  let _timescaleUnconformityLabels = false;
156
145
  let _sectionUnconformityLabels = false;
@@ -165,7 +154,7 @@ function ColumnInner(props) {
165
154
  columnWidth = width;
166
155
  }
167
156
  let showLabelColumn = _showLabelColumn;
168
- if (columnWidth > width - 10 || labelColumnClaimed) {
157
+ if (columnWidth > width - 10) {
169
158
  showLabelColumn = false;
170
159
  }
171
160
  const _showTimescale = axisType == columnComponents.ColumnAxisType.AGE && (showTimescale ?? true);
@@ -1 +1 @@
1
- {"version":3,"file":"column.cjs","sources":["../src/column.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n Box,\n extractPadding,\n Padding,\n useDarkMode,\n} from \"@macrostrat/ui-components\";\nimport classNames from \"classnames\";\nimport {\n HTMLAttributes,\n useCallback,\n CSSProperties,\n ComponentType,\n RefObject,\n} from \"react\";\nimport h from \"./column.module.sass\";\nimport { UnitComponent } from \"./units\";\nimport {\n ColumnRef,\n UnitKeyboardNavigation,\n useColumnRef,\n useUnitSelectionDispatch,\n} from \"./data-provider\";\n\nimport {\n Identifier,\n ReferencesField,\n UnitSelectionPopover,\n} from \"./unit-details\";\nimport {\n MacrostratColumnDataProvider,\n useCompositeScale,\n useLabelColumnClaimed,\n useMacrostratColumnData,\n} from \"./data-provider\";\nimport {\n SectionSharedProps,\n CompositeTimescale,\n type ColumnIntervalStyleBuilder,\n type ColumnTimescaleClickHandler,\n type ColumnTimescaleLike,\n SectionsColumn,\n} from \"./section\";\nimport { ApproximateHeightAxis, CompositeAgeAxis } from \"./age-axis\";\nimport {\n MergeSectionsMode,\n usePreparedColumnUnits,\n HybridScaleType,\n ColumnHeightScaleOptions,\n} from \"./prepare-units\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { NonIdealState } from \"@blueprintjs/core\";\nimport { DataField } from \"@macrostrat/data-components\";\nimport { ScaleContinuousNumeric } from \"d3-scale\";\n\ninterface BaseColumnProps extends Omit<\n SectionSharedProps,\n \"unconformityLabels\"\n> {\n className?: string;\n showLabelColumn?: boolean;\n /** Suppress the label for a unit drawn thinner than this many pixels\n * (default 2) */\n labelSuppressHeight?: number;\n keyboardNavigation?: boolean;\n showLabels?: boolean;\n maxInternalColumns?: number;\n /** Draw the age (or height) axis beside the column (default). Off for a\n * column whose coordinates aren't a measure — one laid out by surface order\n * alone, say. */\n showAgeAxis?: boolean;\n // Timescale properties\n /** Draw the timescale. Defaults to on for an age column; an explicit value\n * wins over `timescaleLevels` / `timescales`, which otherwise turn it on. */\n showTimescale?: boolean;\n timescaleLevels?: number | [number, number];\n /** Regional or project timescales drawn as extra columns beside the\n * international one (see `CompositeTimescale`). Narrow `timescaleLevels` by\n * as many to swap the finest level for them rather than widen the column. */\n additionalTimescales?: number[];\n /** Every timescale to draw beside the column, in order: a Macrostrat\n * timescale ID, or a timescale with its own intervals (see\n * `CompositeTimescale`). Supersedes `timescaleLevels` and\n * `additionalTimescales`. */\n timescales?: ColumnTimescaleLike[];\n /** Draw each timescale's name above it */\n showTimescaleLabels?: boolean;\n /** Called when a timescale interval is clicked (e.g. to zoom the age range).\n * The data says which timescale it was clicked in. */\n onClickTimescaleInterval?: ColumnTimescaleClickHandler;\n /** Per-interval style for the timescale (e.g. to bold the selected\n * interval), by interval and the timescale it was drawn from. */\n timescaleIntervalStyle?: ColumnIntervalStyleBuilder;\n unconformityLabels?: boolean | UnconformityLabelPlacement;\n onMouseOver?: (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n ) => void;\n}\n\nexport type UnconformityLabelPlacement = \"minimal\" | \"prominent\" | \"none\";\n\nexport interface ColumnProps\n extends Padding, BaseColumnProps, ColumnHeightScaleOptions {\n // Macrostrat units\n units: UnitLong[];\n t_age?: number;\n b_age?: number;\n t_pos?: number;\n b_pos?: number;\n mergeSections?: MergeSectionsMode;\n showUnitPopover?: boolean;\n allowUnitSelection?: boolean;\n selectedUnit?: number | null;\n onUnitSelected?: (unitID: number | null, unit: any) => void;\n // Unconformity height in pixels\n unconformityHeight?: number;\n scale?: ScaleContinuousNumeric<number, number>;\n /** True while the age window is animating (e.g. from `useAnimatedAgeWindow`),\n * so per-frame label/pattern recalculation can be skipped. */\n isTransitioning?: boolean;\n /** Hide unit labels while transitioning (perf escape hatch; default false —\n * labels stay visible through the animation). */\n hideLabelsWhileTransitioning?: boolean;\n ref?: RefObject<ColumnRef>;\n}\n\nexport function Column(props: ColumnProps) {\n const {\n showUnitPopover = false,\n keyboardNavigation = false,\n mergeSections,\n onUnitSelected,\n selectedUnit,\n children,\n units: rawUnits,\n t_age,\n b_age,\n t_pos,\n b_pos,\n unconformityHeight = 30,\n targetUnitHeight = 20,\n sectionOptions,\n pixelScale,\n pixelsPerMyr,\n pixelsPerMeter,\n minPixelScale = 0.2,\n minSectionHeight = 50,\n windowPadding = 0,\n collapseSmallUnconformities = true,\n allowUnitSelection = false,\n hybridScale,\n scale,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n ...rest\n } = props;\n\n /* `pixelScale` and `targetUnitHeight` are alternatives: a density stated\n * outright takes over from the one a section's units imply, floors and all.\n * That is settled in `prepare-units/density`, so nothing is adjusted here. */\n\n // Handle special cases for hybrid scales (WIP, we need to regularize this)\n let _axisType = axisType ?? ColumnAxisType.AGE;\n let ageAxisComponent = CompositeAgeAxis;\n if (\n hybridScale?.type === HybridScaleType.ApproximateHeight &&\n _axisType != ColumnAxisType.AGE\n ) {\n // Use approximate height axis for non-age columns if a non-age axis type is requested\n ageAxisComponent = ApproximateHeightAxis;\n _axisType = ColumnAxisType.AGE;\n }\n\n let _sectionOptions = sectionOptions;\n if (_sectionOptions == undefined) {\n // Build our own section options function based on targetUnitHeight\n _sectionOptions = (ctx) => {\n /** Scale targetUnitHeight by the number of units, so that a section with just a few units\n * is drawn taller than a section with many units.\n */\n const n = ctx.units.length;\n const expansionFactor = 8 / Math.min(Math.max(n, 1), 8);\n\n return {\n targetUnitHeight: targetUnitHeight * expansionFactor,\n };\n };\n }\n\n const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {\n axisType: _axisType,\n t_age,\n b_age,\n t_pos,\n b_pos,\n mergeSections,\n targetUnitHeight,\n sectionOptions: _sectionOptions,\n unconformityHeight,\n pixelScale,\n pixelsPerMyr,\n pixelsPerMeter,\n minPixelScale,\n minSectionHeight,\n windowPadding,\n collapseSmallUnconformities,\n // TODO: consider unifying scale and hybridScale options\n scale,\n hybridScale,\n });\n\n if (sections.length === 0) {\n return h(\n \"div.column-container.empty\",\n h(NonIdealState, {\n title: \"Empty column\",\n description: \"No sections found in this column.\",\n icon: \"warning-sign\",\n }),\n );\n }\n\n return h(\n MacrostratColumnDataProvider,\n {\n units,\n sections,\n totalHeight,\n axisType: _axisType,\n allowUnitSelection: showUnitPopover || allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n },\n h(ColumnInner, { ageAxisComponent, ...rest }, [\n children,\n h.if(showUnitPopover)(UnitSelectionPopover),\n h.if(keyboardNavigation)(UnitKeyboardNavigation, { units }),\n ]),\n );\n}\n\ninterface ColumnInnerProps extends BaseColumnProps {\n ageAxisComponent?: ComponentType;\n}\n\nfunction ColumnInner(props: ColumnInnerProps) {\n const padding = extractPadding(props);\n\n // TODO: integrate padding vars more closely with the rest of the spacing (right now padding is a bit ad-hoc)\n const paddingVars: any = {\n \"--column-padding-top\": `${padding.paddingTop}px`,\n \"--column-padding-bottom\": `${padding.paddingBottom}px`,\n \"--column-padding-left\": `${padding.paddingLeft}px`,\n \"--column-padding-right\": `${padding.paddingRight}px`,\n };\n\n const {\n unitComponent = UnitComponent,\n unconformityLabels = \"minimal\",\n showLabels = true,\n width: _width = 300,\n columnWidth: _columnWidth = 150,\n showLabelColumn: _showLabelColumn = true,\n labelSuppressHeight,\n className,\n clipUnits = false,\n children,\n showTimescale,\n timescaleLevels,\n additionalTimescales,\n timescales,\n showTimescaleLabels,\n maxInternalColumns,\n onMouseOver,\n onClickTimescaleInterval,\n timescaleIntervalStyle,\n ageAxisComponent = CompositeAgeAxis,\n showAgeAxis = true,\n } = props;\n\n const { axisType } = useMacrostratColumnData();\n // A child layer drawing its own labels (the surfaces view) takes the label\n // column over from the unit labels\n const labelColumnClaimed = useLabelColumnClaimed();\n\n const columnRef = useColumnRef();\n\n // Coalesce unconformity label setting to a boolean\n let _timescaleUnconformityLabels = false;\n let _sectionUnconformityLabels = false;\n if (unconformityLabels === true || unconformityLabels === \"prominent\") {\n _sectionUnconformityLabels = true;\n } else if (unconformityLabels === \"minimal\") {\n _timescaleUnconformityLabels = true;\n }\n\n let width = _width;\n let columnWidth = _columnWidth;\n if (columnWidth > width) {\n columnWidth = width;\n }\n let showLabelColumn = _showLabelColumn;\n if (columnWidth > width - 10 || labelColumnClaimed) {\n showLabelColumn = false;\n }\n\n // On unless turned off outright: passing levels or timescales no longer\n // overrides an explicit `false`\n const _showTimescale = axisType == ColumnAxisType.AGE && (showTimescale ?? true);\n\n return h(\n ColumnContainer,\n {\n ...useMouseEventHandlers(onMouseOver),\n style: paddingVars,\n className,\n },\n h(\"div.column\", { ref: columnRef }, [\n h.if(showAgeAxis)(ageAxisComponent),\n h.if(_showTimescale)(CompositeTimescale, {\n levels: timescaleLevels,\n additionalTimescales,\n timescales,\n showLabels: showTimescaleLabels,\n unconformityLabels: _timescaleUnconformityLabels,\n onClickInterval: onClickTimescaleInterval,\n intervalStyle: timescaleIntervalStyle,\n }),\n h(SectionsColumn, {\n unitComponent,\n showLabels,\n width,\n columnWidth,\n showLabelColumn,\n labelSuppressHeight,\n clipUnits,\n unconformityLabels: _sectionUnconformityLabels,\n maxInternalColumns,\n }),\n children,\n ]),\n );\n}\n\ntype ColumnMouseOverHandler = (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n) => void;\n\nfunction useMouseEventHandlers(\n _onMouseOver: ColumnMouseOverHandler | null = null,\n) {\n /** Click event handler */\n\n // Click handler for unit selection\n const dispatch = useUnitSelectionDispatch();\n const onClick = useCallback(\n (evt) => {\n dispatch?.(null, null);\n },\n [dispatch],\n );\n\n /** Hover event handlers */\n const scale = useCompositeScale();\n\n const onMouseOver = useCallback(\n (evt) => {\n const height = scale.invert(\n evt.clientY - evt.currentTarget.getBoundingClientRect().top,\n );\n _onMouseOver?.(null, height, evt);\n },\n [scale, _onMouseOver],\n );\n\n const onMouseOut = useCallback(() => {\n _onMouseOver?.(null, null, null);\n }, [_onMouseOver]);\n\n if (_onMouseOver == null) {\n return {\n onClick,\n onMouseOver: undefined,\n onMouseMove: undefined,\n onMouseOut: undefined,\n };\n }\n\n return { onMouseOver, onMouseMove: onMouseOver, onMouseOut, onClick };\n}\n\nexport interface ColumnContainerProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ColumnContainer(props: ColumnContainerProps) {\n const { className, ...rest } = props;\n const darkMode = useDarkMode();\n\n return h(Box, {\n className: classNames(\n className,\n {\n \"dark-mode\": darkMode?.isEnabled ?? false,\n },\n \"column-container\",\n ),\n ...rest,\n });\n}\n\nexport function ColumnBasicInfo({\n data,\n showColumnID = true,\n showReferences = true,\n}) {\n if (data == null) return null;\n return h(\"div.column-info\", [\n h(\"div.column-title-row\", [\n h(\"h2\", data.col_name),\n h.if(showColumnID)(\"h4\", h(Identifier, { id: data.col_id })),\n ]),\n h(DataField, { row: true, label: \"Group\", value: data.col_group }),\n h.if(showReferences)(ReferencesField, {\n refs: data.refs,\n inline: false,\n row: false,\n className: \"column-refs\",\n }),\n ]);\n}\n"],"names":["ColumnAxisType","CompositeAgeAxis","HybridScaleType","ApproximateHeightAxis","usePreparedColumnUnits","h","NonIdealState","MacrostratColumnDataProvider","UnitSelectionPopover","UnitKeyboardNavigation","extractPadding","UnitComponent","useMacrostratColumnData","useLabelColumnClaimed","useColumnRef","CompositeTimescale","SectionsColumn","useUnitSelectionDispatch","useCallback","useCompositeScale","useDarkMode","Box","classNames","Identifier","DataField","ReferencesField"],"mappings":"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"}
1
+ {"version":3,"file":"column.cjs","sources":["../src/column.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n Box,\n extractPadding,\n Padding,\n useDarkMode,\n} from \"@macrostrat/ui-components\";\nimport classNames from \"classnames\";\nimport {\n HTMLAttributes,\n useCallback,\n CSSProperties,\n ComponentType,\n RefObject,\n} from \"react\";\nimport h from \"./column.module.sass\";\nimport { UnitComponent } from \"./units\";\nimport {\n ColumnRef,\n UnitKeyboardNavigation,\n useColumnRef,\n useUnitSelectionDispatch,\n} from \"./data-provider\";\n\nimport {\n Identifier,\n ReferencesField,\n UnitSelectionPopover,\n} from \"./unit-details\";\nimport {\n MacrostratColumnDataProvider,\n useCompositeScale,\n useMacrostratColumnData,\n} from \"./data-provider\";\nimport {\n SectionSharedProps,\n CompositeTimescale,\n type ColumnIntervalStyleBuilder,\n type ColumnTimescaleClickHandler,\n type ColumnTimescaleLike,\n SectionsColumn,\n} from \"./section\";\nimport { ApproximateHeightAxis, CompositeAgeAxis } from \"./age-axis\";\nimport {\n MergeSectionsMode,\n usePreparedColumnUnits,\n HybridScaleType,\n ColumnHeightScaleOptions,\n} from \"./prepare-units\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { NonIdealState } from \"@blueprintjs/core\";\nimport { DataField } from \"@macrostrat/data-components\";\nimport { ScaleContinuousNumeric } from \"d3-scale\";\n\ninterface BaseColumnProps extends Omit<\n SectionSharedProps,\n \"unconformityLabels\"\n> {\n className?: string;\n showLabelColumn?: boolean;\n /** Suppress the label for a unit drawn thinner than this many pixels\n * (default 2) */\n labelSuppressHeight?: number;\n keyboardNavigation?: boolean;\n showLabels?: boolean;\n maxInternalColumns?: number;\n /** Draw the age (or height) axis beside the column (default). Off for a\n * column whose coordinates aren't a measure — one laid out by surface order\n * alone, say. */\n showAgeAxis?: boolean;\n // Timescale properties\n /** Draw the timescale. Defaults to on for an age column; an explicit value\n * wins over `timescaleLevels` / `timescales`, which otherwise turn it on. */\n showTimescale?: boolean;\n timescaleLevels?: number | [number, number];\n /** Regional or project timescales drawn as extra columns beside the\n * international one (see `CompositeTimescale`). Narrow `timescaleLevels` by\n * as many to swap the finest level for them rather than widen the column. */\n additionalTimescales?: number[];\n /** Every timescale to draw beside the column, in order: a Macrostrat\n * timescale ID, or a timescale with its own intervals (see\n * `CompositeTimescale`). Supersedes `timescaleLevels` and\n * `additionalTimescales`. */\n timescales?: ColumnTimescaleLike[];\n /** Draw each timescale's name above it */\n showTimescaleLabels?: boolean;\n /** Called when a timescale interval is clicked (e.g. to zoom the age range).\n * The data says which timescale it was clicked in. */\n onClickTimescaleInterval?: ColumnTimescaleClickHandler;\n /** Per-interval style for the timescale (e.g. to bold the selected\n * interval), by interval and the timescale it was drawn from. */\n timescaleIntervalStyle?: ColumnIntervalStyleBuilder;\n unconformityLabels?: boolean | UnconformityLabelPlacement;\n onMouseOver?: (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n ) => void;\n}\n\nexport type UnconformityLabelPlacement = \"minimal\" | \"prominent\" | \"none\";\n\nexport interface ColumnProps\n extends Padding, BaseColumnProps, ColumnHeightScaleOptions {\n // Macrostrat units\n units: UnitLong[];\n t_age?: number;\n b_age?: number;\n t_pos?: number;\n b_pos?: number;\n mergeSections?: MergeSectionsMode;\n showUnitPopover?: boolean;\n allowUnitSelection?: boolean;\n selectedUnit?: number | null;\n onUnitSelected?: (unitID: number | null, unit: any) => void;\n // Unconformity height in pixels\n unconformityHeight?: number;\n scale?: ScaleContinuousNumeric<number, number>;\n /** True while the age window is animating (e.g. from `useAnimatedAgeWindow`),\n * so per-frame label/pattern recalculation can be skipped. */\n isTransitioning?: boolean;\n /** Hide unit labels while transitioning (perf escape hatch; default false —\n * labels stay visible through the animation). */\n hideLabelsWhileTransitioning?: boolean;\n ref?: RefObject<ColumnRef>;\n}\n\nexport function Column(props: ColumnProps) {\n const {\n showUnitPopover = false,\n keyboardNavigation = false,\n mergeSections,\n onUnitSelected,\n selectedUnit,\n children,\n units: rawUnits,\n t_age,\n b_age,\n t_pos,\n b_pos,\n unconformityHeight = 30,\n targetUnitHeight = 20,\n sectionOptions,\n pixelScale,\n pixelsPerMyr,\n pixelsPerMeter,\n minPixelScale = 0.2,\n minSectionHeight = 50,\n windowPadding = 0,\n collapseSmallUnconformities = true,\n allowUnitSelection = false,\n hybridScale,\n scale,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n ...rest\n } = props;\n\n /* `pixelScale` and `targetUnitHeight` are alternatives: a density stated\n * outright takes over from the one a section's units imply, floors and all.\n * That is settled in `prepare-units/density`, so nothing is adjusted here. */\n\n // Handle special cases for hybrid scales (WIP, we need to regularize this)\n let _axisType = axisType ?? ColumnAxisType.AGE;\n let ageAxisComponent = CompositeAgeAxis;\n if (\n hybridScale?.type === HybridScaleType.ApproximateHeight &&\n _axisType != ColumnAxisType.AGE\n ) {\n // Use approximate height axis for non-age columns if a non-age axis type is requested\n ageAxisComponent = ApproximateHeightAxis;\n _axisType = ColumnAxisType.AGE;\n }\n\n const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {\n axisType: _axisType,\n t_age,\n b_age,\n t_pos,\n b_pos,\n mergeSections,\n targetUnitHeight,\n sectionOptions,\n unconformityHeight,\n pixelScale,\n pixelsPerMyr,\n pixelsPerMeter,\n minPixelScale,\n minSectionHeight,\n windowPadding,\n collapseSmallUnconformities,\n // TODO: consider unifying scale and hybridScale options\n scale,\n hybridScale,\n });\n\n if (sections.length === 0) {\n return h(\n \"div.column-container.empty\",\n h(NonIdealState, {\n title: \"Empty column\",\n description: \"No sections found in this column.\",\n icon: \"warning-sign\",\n }),\n );\n }\n\n return h(\n MacrostratColumnDataProvider,\n {\n units,\n sections,\n totalHeight,\n axisType: _axisType,\n allowUnitSelection: showUnitPopover || allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n },\n h(ColumnInner, { ageAxisComponent, ...rest }, [\n children,\n h.if(showUnitPopover)(UnitSelectionPopover),\n h.if(keyboardNavigation)(UnitKeyboardNavigation, { units }),\n ]),\n );\n}\n\ninterface ColumnInnerProps extends BaseColumnProps {\n ageAxisComponent?: ComponentType;\n}\n\nfunction ColumnInner(props: ColumnInnerProps) {\n const padding = extractPadding(props);\n\n // TODO: integrate padding vars more closely with the rest of the spacing (right now padding is a bit ad-hoc)\n const paddingVars: any = {\n \"--column-padding-top\": `${padding.paddingTop}px`,\n \"--column-padding-bottom\": `${padding.paddingBottom}px`,\n \"--column-padding-left\": `${padding.paddingLeft}px`,\n \"--column-padding-right\": `${padding.paddingRight}px`,\n };\n\n const {\n unitComponent = UnitComponent,\n unconformityLabels = \"minimal\",\n showLabels = true,\n width: _width = 300,\n columnWidth: _columnWidth = 150,\n showLabelColumn: _showLabelColumn = true,\n labelSuppressHeight,\n className,\n clipUnits = false,\n children,\n showTimescale,\n timescaleLevels,\n additionalTimescales,\n timescales,\n showTimescaleLabels,\n maxInternalColumns,\n onMouseOver,\n onClickTimescaleInterval,\n timescaleIntervalStyle,\n ageAxisComponent = CompositeAgeAxis,\n showAgeAxis = true,\n } = props;\n\n const { axisType } = useMacrostratColumnData();\n\n const columnRef = useColumnRef();\n\n // Coalesce unconformity label setting to a boolean\n let _timescaleUnconformityLabels = false;\n let _sectionUnconformityLabels = false;\n if (unconformityLabels === true || unconformityLabels === \"prominent\") {\n _sectionUnconformityLabels = true;\n } else if (unconformityLabels === \"minimal\") {\n _timescaleUnconformityLabels = true;\n }\n\n let width = _width;\n let columnWidth = _columnWidth;\n if (columnWidth > width) {\n columnWidth = width;\n }\n let showLabelColumn = _showLabelColumn;\n if (columnWidth > width - 10) {\n showLabelColumn = false;\n }\n\n // On unless turned off outright: passing levels or timescales no longer\n // overrides an explicit `false`\n const _showTimescale =\n axisType == ColumnAxisType.AGE && (showTimescale ?? true);\n\n return h(\n ColumnContainer,\n {\n ...useMouseEventHandlers(onMouseOver),\n style: paddingVars,\n className,\n },\n h(\"div.column\", { ref: columnRef }, [\n h.if(showAgeAxis)(ageAxisComponent),\n h.if(_showTimescale)(CompositeTimescale, {\n levels: timescaleLevels,\n additionalTimescales,\n timescales,\n showLabels: showTimescaleLabels,\n unconformityLabels: _timescaleUnconformityLabels,\n onClickInterval: onClickTimescaleInterval,\n intervalStyle: timescaleIntervalStyle,\n }),\n h(SectionsColumn, {\n unitComponent,\n showLabels,\n width,\n columnWidth,\n showLabelColumn,\n labelSuppressHeight,\n clipUnits,\n unconformityLabels: _sectionUnconformityLabels,\n maxInternalColumns,\n }),\n children,\n ]),\n );\n}\n\ntype ColumnMouseOverHandler = (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n) => void;\n\nfunction useMouseEventHandlers(\n _onMouseOver: ColumnMouseOverHandler | null = null,\n) {\n /** Click event handler */\n\n // Click handler for unit selection\n const dispatch = useUnitSelectionDispatch();\n const onClick = useCallback(\n (evt) => {\n dispatch?.(null, null);\n },\n [dispatch],\n );\n\n /** Hover event handlers */\n const scale = useCompositeScale();\n\n const onMouseOver = useCallback(\n (evt) => {\n const height = scale.invert(\n evt.clientY - evt.currentTarget.getBoundingClientRect().top,\n );\n _onMouseOver?.(null, height, evt);\n },\n [scale, _onMouseOver],\n );\n\n const onMouseOut = useCallback(() => {\n _onMouseOver?.(null, null, null);\n }, [_onMouseOver]);\n\n if (_onMouseOver == null) {\n return {\n onClick,\n onMouseOver: undefined,\n onMouseMove: undefined,\n onMouseOut: undefined,\n };\n }\n\n return { onMouseOver, onMouseMove: onMouseOver, onMouseOut, onClick };\n}\n\nexport interface ColumnContainerProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ColumnContainer(props: ColumnContainerProps) {\n const { className, ...rest } = props;\n const darkMode = useDarkMode();\n\n return h(Box, {\n className: classNames(\n className,\n {\n \"dark-mode\": darkMode?.isEnabled ?? false,\n },\n \"column-container\",\n ),\n ...rest,\n });\n}\n\nexport function ColumnBasicInfo({\n data,\n showColumnID = true,\n showReferences = true,\n}) {\n if (data == null) return null;\n return h(\"div.column-info\", [\n h(\"div.column-title-row\", [\n h(\"h2\", data.col_name),\n h.if(showColumnID)(\"h4\", h(Identifier, { id: data.col_id })),\n ]),\n h(DataField, { row: true, label: \"Group\", value: data.col_group }),\n h.if(showReferences)(ReferencesField, {\n refs: data.refs,\n inline: false,\n row: false,\n className: \"column-refs\",\n }),\n ]);\n}\n"],"names":["ColumnAxisType","CompositeAgeAxis","HybridScaleType","ApproximateHeightAxis","usePreparedColumnUnits","h","NonIdealState","MacrostratColumnDataProvider","UnitSelectionPopover","UnitKeyboardNavigation","extractPadding","UnitComponent","useMacrostratColumnData","useColumnRef","CompositeTimescale","SectionsColumn","useUnitSelectionDispatch","useCallback","useCompositeScale","useDarkMode","Box","classNames","Identifier","DataField","ReferencesField"],"mappings":"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"}
package/dist/column.js CHANGED
@@ -10,7 +10,7 @@ import { usePreparedColumnUnits } from "./prepare-units/index.js";
10
10
  import { NonIdealState } from "@blueprintjs/core";
11
11
  import { Identifier, DataField } from "@macrostrat/data-components";
12
12
  import { HybridScaleType } from "./prepare-units/types.js";
13
- import { MacrostratColumnDataProvider, useMacrostratColumnData, useLabelColumnClaimed, useCompositeScale } from "./data-provider/store.js";
13
+ import { MacrostratColumnDataProvider, useMacrostratColumnData, useCompositeScale } from "./data-provider/store.js";
14
14
  import { UnitSelectionPopover } from "./unit-details/popover.js";
15
15
  import { UnitKeyboardNavigation, useColumnRef, useUnitSelectionDispatch } from "./data-provider/unit-selection.js";
16
16
  import { ReferencesField } from "./unit-details/panel.js";
@@ -52,16 +52,6 @@ function Column(props) {
52
52
  ageAxisComponent = ApproximateHeightAxis;
53
53
  _axisType = ColumnAxisType.AGE;
54
54
  }
55
- let _sectionOptions = sectionOptions;
56
- if (_sectionOptions == void 0) {
57
- _sectionOptions = (ctx) => {
58
- const n = ctx.units.length;
59
- const expansionFactor = 8 / Math.min(Math.max(n, 1), 8);
60
- return {
61
- targetUnitHeight: targetUnitHeight * expansionFactor
62
- };
63
- };
64
- }
65
55
  const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {
66
56
  axisType: _axisType,
67
57
  t_age,
@@ -70,7 +60,7 @@ function Column(props) {
70
60
  b_pos,
71
61
  mergeSections,
72
62
  targetUnitHeight,
73
- sectionOptions: _sectionOptions,
63
+ sectionOptions,
74
64
  unconformityHeight,
75
65
  pixelScale,
76
66
  pixelsPerMyr,
@@ -146,7 +136,6 @@ function ColumnInner(props) {
146
136
  showAgeAxis = true
147
137
  } = props;
148
138
  const { axisType } = useMacrostratColumnData();
149
- const labelColumnClaimed = useLabelColumnClaimed();
150
139
  const columnRef = useColumnRef();
151
140
  let _timescaleUnconformityLabels = false;
152
141
  let _sectionUnconformityLabels = false;
@@ -161,7 +150,7 @@ function ColumnInner(props) {
161
150
  columnWidth = width;
162
151
  }
163
152
  let showLabelColumn = _showLabelColumn;
164
- if (columnWidth > width - 10 || labelColumnClaimed) {
153
+ if (columnWidth > width - 10) {
165
154
  showLabelColumn = false;
166
155
  }
167
156
  const _showTimescale = axisType == ColumnAxisType.AGE && (showTimescale ?? true);
@@ -1 +1 @@
1
- {"version":3,"file":"column.js","sources":["../src/column.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n Box,\n extractPadding,\n Padding,\n useDarkMode,\n} from \"@macrostrat/ui-components\";\nimport classNames from \"classnames\";\nimport {\n HTMLAttributes,\n useCallback,\n CSSProperties,\n ComponentType,\n RefObject,\n} from \"react\";\nimport h from \"./column.module.sass\";\nimport { UnitComponent } from \"./units\";\nimport {\n ColumnRef,\n UnitKeyboardNavigation,\n useColumnRef,\n useUnitSelectionDispatch,\n} from \"./data-provider\";\n\nimport {\n Identifier,\n ReferencesField,\n UnitSelectionPopover,\n} from \"./unit-details\";\nimport {\n MacrostratColumnDataProvider,\n useCompositeScale,\n useLabelColumnClaimed,\n useMacrostratColumnData,\n} from \"./data-provider\";\nimport {\n SectionSharedProps,\n CompositeTimescale,\n type ColumnIntervalStyleBuilder,\n type ColumnTimescaleClickHandler,\n type ColumnTimescaleLike,\n SectionsColumn,\n} from \"./section\";\nimport { ApproximateHeightAxis, CompositeAgeAxis } from \"./age-axis\";\nimport {\n MergeSectionsMode,\n usePreparedColumnUnits,\n HybridScaleType,\n ColumnHeightScaleOptions,\n} from \"./prepare-units\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { NonIdealState } from \"@blueprintjs/core\";\nimport { DataField } from \"@macrostrat/data-components\";\nimport { ScaleContinuousNumeric } from \"d3-scale\";\n\ninterface BaseColumnProps extends Omit<\n SectionSharedProps,\n \"unconformityLabels\"\n> {\n className?: string;\n showLabelColumn?: boolean;\n /** Suppress the label for a unit drawn thinner than this many pixels\n * (default 2) */\n labelSuppressHeight?: number;\n keyboardNavigation?: boolean;\n showLabels?: boolean;\n maxInternalColumns?: number;\n /** Draw the age (or height) axis beside the column (default). Off for a\n * column whose coordinates aren't a measure — one laid out by surface order\n * alone, say. */\n showAgeAxis?: boolean;\n // Timescale properties\n /** Draw the timescale. Defaults to on for an age column; an explicit value\n * wins over `timescaleLevels` / `timescales`, which otherwise turn it on. */\n showTimescale?: boolean;\n timescaleLevels?: number | [number, number];\n /** Regional or project timescales drawn as extra columns beside the\n * international one (see `CompositeTimescale`). Narrow `timescaleLevels` by\n * as many to swap the finest level for them rather than widen the column. */\n additionalTimescales?: number[];\n /** Every timescale to draw beside the column, in order: a Macrostrat\n * timescale ID, or a timescale with its own intervals (see\n * `CompositeTimescale`). Supersedes `timescaleLevels` and\n * `additionalTimescales`. */\n timescales?: ColumnTimescaleLike[];\n /** Draw each timescale's name above it */\n showTimescaleLabels?: boolean;\n /** Called when a timescale interval is clicked (e.g. to zoom the age range).\n * The data says which timescale it was clicked in. */\n onClickTimescaleInterval?: ColumnTimescaleClickHandler;\n /** Per-interval style for the timescale (e.g. to bold the selected\n * interval), by interval and the timescale it was drawn from. */\n timescaleIntervalStyle?: ColumnIntervalStyleBuilder;\n unconformityLabels?: boolean | UnconformityLabelPlacement;\n onMouseOver?: (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n ) => void;\n}\n\nexport type UnconformityLabelPlacement = \"minimal\" | \"prominent\" | \"none\";\n\nexport interface ColumnProps\n extends Padding, BaseColumnProps, ColumnHeightScaleOptions {\n // Macrostrat units\n units: UnitLong[];\n t_age?: number;\n b_age?: number;\n t_pos?: number;\n b_pos?: number;\n mergeSections?: MergeSectionsMode;\n showUnitPopover?: boolean;\n allowUnitSelection?: boolean;\n selectedUnit?: number | null;\n onUnitSelected?: (unitID: number | null, unit: any) => void;\n // Unconformity height in pixels\n unconformityHeight?: number;\n scale?: ScaleContinuousNumeric<number, number>;\n /** True while the age window is animating (e.g. from `useAnimatedAgeWindow`),\n * so per-frame label/pattern recalculation can be skipped. */\n isTransitioning?: boolean;\n /** Hide unit labels while transitioning (perf escape hatch; default false —\n * labels stay visible through the animation). */\n hideLabelsWhileTransitioning?: boolean;\n ref?: RefObject<ColumnRef>;\n}\n\nexport function Column(props: ColumnProps) {\n const {\n showUnitPopover = false,\n keyboardNavigation = false,\n mergeSections,\n onUnitSelected,\n selectedUnit,\n children,\n units: rawUnits,\n t_age,\n b_age,\n t_pos,\n b_pos,\n unconformityHeight = 30,\n targetUnitHeight = 20,\n sectionOptions,\n pixelScale,\n pixelsPerMyr,\n pixelsPerMeter,\n minPixelScale = 0.2,\n minSectionHeight = 50,\n windowPadding = 0,\n collapseSmallUnconformities = true,\n allowUnitSelection = false,\n hybridScale,\n scale,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n ...rest\n } = props;\n\n /* `pixelScale` and `targetUnitHeight` are alternatives: a density stated\n * outright takes over from the one a section's units imply, floors and all.\n * That is settled in `prepare-units/density`, so nothing is adjusted here. */\n\n // Handle special cases for hybrid scales (WIP, we need to regularize this)\n let _axisType = axisType ?? ColumnAxisType.AGE;\n let ageAxisComponent = CompositeAgeAxis;\n if (\n hybridScale?.type === HybridScaleType.ApproximateHeight &&\n _axisType != ColumnAxisType.AGE\n ) {\n // Use approximate height axis for non-age columns if a non-age axis type is requested\n ageAxisComponent = ApproximateHeightAxis;\n _axisType = ColumnAxisType.AGE;\n }\n\n let _sectionOptions = sectionOptions;\n if (_sectionOptions == undefined) {\n // Build our own section options function based on targetUnitHeight\n _sectionOptions = (ctx) => {\n /** Scale targetUnitHeight by the number of units, so that a section with just a few units\n * is drawn taller than a section with many units.\n */\n const n = ctx.units.length;\n const expansionFactor = 8 / Math.min(Math.max(n, 1), 8);\n\n return {\n targetUnitHeight: targetUnitHeight * expansionFactor,\n };\n };\n }\n\n const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {\n axisType: _axisType,\n t_age,\n b_age,\n t_pos,\n b_pos,\n mergeSections,\n targetUnitHeight,\n sectionOptions: _sectionOptions,\n unconformityHeight,\n pixelScale,\n pixelsPerMyr,\n pixelsPerMeter,\n minPixelScale,\n minSectionHeight,\n windowPadding,\n collapseSmallUnconformities,\n // TODO: consider unifying scale and hybridScale options\n scale,\n hybridScale,\n });\n\n if (sections.length === 0) {\n return h(\n \"div.column-container.empty\",\n h(NonIdealState, {\n title: \"Empty column\",\n description: \"No sections found in this column.\",\n icon: \"warning-sign\",\n }),\n );\n }\n\n return h(\n MacrostratColumnDataProvider,\n {\n units,\n sections,\n totalHeight,\n axisType: _axisType,\n allowUnitSelection: showUnitPopover || allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n },\n h(ColumnInner, { ageAxisComponent, ...rest }, [\n children,\n h.if(showUnitPopover)(UnitSelectionPopover),\n h.if(keyboardNavigation)(UnitKeyboardNavigation, { units }),\n ]),\n );\n}\n\ninterface ColumnInnerProps extends BaseColumnProps {\n ageAxisComponent?: ComponentType;\n}\n\nfunction ColumnInner(props: ColumnInnerProps) {\n const padding = extractPadding(props);\n\n // TODO: integrate padding vars more closely with the rest of the spacing (right now padding is a bit ad-hoc)\n const paddingVars: any = {\n \"--column-padding-top\": `${padding.paddingTop}px`,\n \"--column-padding-bottom\": `${padding.paddingBottom}px`,\n \"--column-padding-left\": `${padding.paddingLeft}px`,\n \"--column-padding-right\": `${padding.paddingRight}px`,\n };\n\n const {\n unitComponent = UnitComponent,\n unconformityLabels = \"minimal\",\n showLabels = true,\n width: _width = 300,\n columnWidth: _columnWidth = 150,\n showLabelColumn: _showLabelColumn = true,\n labelSuppressHeight,\n className,\n clipUnits = false,\n children,\n showTimescale,\n timescaleLevels,\n additionalTimescales,\n timescales,\n showTimescaleLabels,\n maxInternalColumns,\n onMouseOver,\n onClickTimescaleInterval,\n timescaleIntervalStyle,\n ageAxisComponent = CompositeAgeAxis,\n showAgeAxis = true,\n } = props;\n\n const { axisType } = useMacrostratColumnData();\n // A child layer drawing its own labels (the surfaces view) takes the label\n // column over from the unit labels\n const labelColumnClaimed = useLabelColumnClaimed();\n\n const columnRef = useColumnRef();\n\n // Coalesce unconformity label setting to a boolean\n let _timescaleUnconformityLabels = false;\n let _sectionUnconformityLabels = false;\n if (unconformityLabels === true || unconformityLabels === \"prominent\") {\n _sectionUnconformityLabels = true;\n } else if (unconformityLabels === \"minimal\") {\n _timescaleUnconformityLabels = true;\n }\n\n let width = _width;\n let columnWidth = _columnWidth;\n if (columnWidth > width) {\n columnWidth = width;\n }\n let showLabelColumn = _showLabelColumn;\n if (columnWidth > width - 10 || labelColumnClaimed) {\n showLabelColumn = false;\n }\n\n // On unless turned off outright: passing levels or timescales no longer\n // overrides an explicit `false`\n const _showTimescale = axisType == ColumnAxisType.AGE && (showTimescale ?? true);\n\n return h(\n ColumnContainer,\n {\n ...useMouseEventHandlers(onMouseOver),\n style: paddingVars,\n className,\n },\n h(\"div.column\", { ref: columnRef }, [\n h.if(showAgeAxis)(ageAxisComponent),\n h.if(_showTimescale)(CompositeTimescale, {\n levels: timescaleLevels,\n additionalTimescales,\n timescales,\n showLabels: showTimescaleLabels,\n unconformityLabels: _timescaleUnconformityLabels,\n onClickInterval: onClickTimescaleInterval,\n intervalStyle: timescaleIntervalStyle,\n }),\n h(SectionsColumn, {\n unitComponent,\n showLabels,\n width,\n columnWidth,\n showLabelColumn,\n labelSuppressHeight,\n clipUnits,\n unconformityLabels: _sectionUnconformityLabels,\n maxInternalColumns,\n }),\n children,\n ]),\n );\n}\n\ntype ColumnMouseOverHandler = (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n) => void;\n\nfunction useMouseEventHandlers(\n _onMouseOver: ColumnMouseOverHandler | null = null,\n) {\n /** Click event handler */\n\n // Click handler for unit selection\n const dispatch = useUnitSelectionDispatch();\n const onClick = useCallback(\n (evt) => {\n dispatch?.(null, null);\n },\n [dispatch],\n );\n\n /** Hover event handlers */\n const scale = useCompositeScale();\n\n const onMouseOver = useCallback(\n (evt) => {\n const height = scale.invert(\n evt.clientY - evt.currentTarget.getBoundingClientRect().top,\n );\n _onMouseOver?.(null, height, evt);\n },\n [scale, _onMouseOver],\n );\n\n const onMouseOut = useCallback(() => {\n _onMouseOver?.(null, null, null);\n }, [_onMouseOver]);\n\n if (_onMouseOver == null) {\n return {\n onClick,\n onMouseOver: undefined,\n onMouseMove: undefined,\n onMouseOut: undefined,\n };\n }\n\n return { onMouseOver, onMouseMove: onMouseOver, onMouseOut, onClick };\n}\n\nexport interface ColumnContainerProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ColumnContainer(props: ColumnContainerProps) {\n const { className, ...rest } = props;\n const darkMode = useDarkMode();\n\n return h(Box, {\n className: classNames(\n className,\n {\n \"dark-mode\": darkMode?.isEnabled ?? false,\n },\n \"column-container\",\n ),\n ...rest,\n });\n}\n\nexport function ColumnBasicInfo({\n data,\n showColumnID = true,\n showReferences = true,\n}) {\n if (data == null) return null;\n return h(\"div.column-info\", [\n h(\"div.column-title-row\", [\n h(\"h2\", data.col_name),\n h.if(showColumnID)(\"h4\", h(Identifier, { id: data.col_id })),\n ]),\n h(DataField, { row: true, label: \"Group\", value: data.col_group }),\n h.if(showReferences)(ReferencesField, {\n refs: data.refs,\n inline: false,\n row: false,\n className: \"column-refs\",\n }),\n ]);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"column.js","sources":["../src/column.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n Box,\n extractPadding,\n Padding,\n useDarkMode,\n} from \"@macrostrat/ui-components\";\nimport classNames from \"classnames\";\nimport {\n HTMLAttributes,\n useCallback,\n CSSProperties,\n ComponentType,\n RefObject,\n} from \"react\";\nimport h from \"./column.module.sass\";\nimport { UnitComponent } from \"./units\";\nimport {\n ColumnRef,\n UnitKeyboardNavigation,\n useColumnRef,\n useUnitSelectionDispatch,\n} from \"./data-provider\";\n\nimport {\n Identifier,\n ReferencesField,\n UnitSelectionPopover,\n} from \"./unit-details\";\nimport {\n MacrostratColumnDataProvider,\n useCompositeScale,\n useMacrostratColumnData,\n} from \"./data-provider\";\nimport {\n SectionSharedProps,\n CompositeTimescale,\n type ColumnIntervalStyleBuilder,\n type ColumnTimescaleClickHandler,\n type ColumnTimescaleLike,\n SectionsColumn,\n} from \"./section\";\nimport { ApproximateHeightAxis, CompositeAgeAxis } from \"./age-axis\";\nimport {\n MergeSectionsMode,\n usePreparedColumnUnits,\n HybridScaleType,\n ColumnHeightScaleOptions,\n} from \"./prepare-units\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { NonIdealState } from \"@blueprintjs/core\";\nimport { DataField } from \"@macrostrat/data-components\";\nimport { ScaleContinuousNumeric } from \"d3-scale\";\n\ninterface BaseColumnProps extends Omit<\n SectionSharedProps,\n \"unconformityLabels\"\n> {\n className?: string;\n showLabelColumn?: boolean;\n /** Suppress the label for a unit drawn thinner than this many pixels\n * (default 2) */\n labelSuppressHeight?: number;\n keyboardNavigation?: boolean;\n showLabels?: boolean;\n maxInternalColumns?: number;\n /** Draw the age (or height) axis beside the column (default). Off for a\n * column whose coordinates aren't a measure — one laid out by surface order\n * alone, say. */\n showAgeAxis?: boolean;\n // Timescale properties\n /** Draw the timescale. Defaults to on for an age column; an explicit value\n * wins over `timescaleLevels` / `timescales`, which otherwise turn it on. */\n showTimescale?: boolean;\n timescaleLevels?: number | [number, number];\n /** Regional or project timescales drawn as extra columns beside the\n * international one (see `CompositeTimescale`). Narrow `timescaleLevels` by\n * as many to swap the finest level for them rather than widen the column. */\n additionalTimescales?: number[];\n /** Every timescale to draw beside the column, in order: a Macrostrat\n * timescale ID, or a timescale with its own intervals (see\n * `CompositeTimescale`). Supersedes `timescaleLevels` and\n * `additionalTimescales`. */\n timescales?: ColumnTimescaleLike[];\n /** Draw each timescale's name above it */\n showTimescaleLabels?: boolean;\n /** Called when a timescale interval is clicked (e.g. to zoom the age range).\n * The data says which timescale it was clicked in. */\n onClickTimescaleInterval?: ColumnTimescaleClickHandler;\n /** Per-interval style for the timescale (e.g. to bold the selected\n * interval), by interval and the timescale it was drawn from. */\n timescaleIntervalStyle?: ColumnIntervalStyleBuilder;\n unconformityLabels?: boolean | UnconformityLabelPlacement;\n onMouseOver?: (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n ) => void;\n}\n\nexport type UnconformityLabelPlacement = \"minimal\" | \"prominent\" | \"none\";\n\nexport interface ColumnProps\n extends Padding, BaseColumnProps, ColumnHeightScaleOptions {\n // Macrostrat units\n units: UnitLong[];\n t_age?: number;\n b_age?: number;\n t_pos?: number;\n b_pos?: number;\n mergeSections?: MergeSectionsMode;\n showUnitPopover?: boolean;\n allowUnitSelection?: boolean;\n selectedUnit?: number | null;\n onUnitSelected?: (unitID: number | null, unit: any) => void;\n // Unconformity height in pixels\n unconformityHeight?: number;\n scale?: ScaleContinuousNumeric<number, number>;\n /** True while the age window is animating (e.g. from `useAnimatedAgeWindow`),\n * so per-frame label/pattern recalculation can be skipped. */\n isTransitioning?: boolean;\n /** Hide unit labels while transitioning (perf escape hatch; default false —\n * labels stay visible through the animation). */\n hideLabelsWhileTransitioning?: boolean;\n ref?: RefObject<ColumnRef>;\n}\n\nexport function Column(props: ColumnProps) {\n const {\n showUnitPopover = false,\n keyboardNavigation = false,\n mergeSections,\n onUnitSelected,\n selectedUnit,\n children,\n units: rawUnits,\n t_age,\n b_age,\n t_pos,\n b_pos,\n unconformityHeight = 30,\n targetUnitHeight = 20,\n sectionOptions,\n pixelScale,\n pixelsPerMyr,\n pixelsPerMeter,\n minPixelScale = 0.2,\n minSectionHeight = 50,\n windowPadding = 0,\n collapseSmallUnconformities = true,\n allowUnitSelection = false,\n hybridScale,\n scale,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n ...rest\n } = props;\n\n /* `pixelScale` and `targetUnitHeight` are alternatives: a density stated\n * outright takes over from the one a section's units imply, floors and all.\n * That is settled in `prepare-units/density`, so nothing is adjusted here. */\n\n // Handle special cases for hybrid scales (WIP, we need to regularize this)\n let _axisType = axisType ?? ColumnAxisType.AGE;\n let ageAxisComponent = CompositeAgeAxis;\n if (\n hybridScale?.type === HybridScaleType.ApproximateHeight &&\n _axisType != ColumnAxisType.AGE\n ) {\n // Use approximate height axis for non-age columns if a non-age axis type is requested\n ageAxisComponent = ApproximateHeightAxis;\n _axisType = ColumnAxisType.AGE;\n }\n\n const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {\n axisType: _axisType,\n t_age,\n b_age,\n t_pos,\n b_pos,\n mergeSections,\n targetUnitHeight,\n sectionOptions,\n unconformityHeight,\n pixelScale,\n pixelsPerMyr,\n pixelsPerMeter,\n minPixelScale,\n minSectionHeight,\n windowPadding,\n collapseSmallUnconformities,\n // TODO: consider unifying scale and hybridScale options\n scale,\n hybridScale,\n });\n\n if (sections.length === 0) {\n return h(\n \"div.column-container.empty\",\n h(NonIdealState, {\n title: \"Empty column\",\n description: \"No sections found in this column.\",\n icon: \"warning-sign\",\n }),\n );\n }\n\n return h(\n MacrostratColumnDataProvider,\n {\n units,\n sections,\n totalHeight,\n axisType: _axisType,\n allowUnitSelection: showUnitPopover || allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n },\n h(ColumnInner, { ageAxisComponent, ...rest }, [\n children,\n h.if(showUnitPopover)(UnitSelectionPopover),\n h.if(keyboardNavigation)(UnitKeyboardNavigation, { units }),\n ]),\n );\n}\n\ninterface ColumnInnerProps extends BaseColumnProps {\n ageAxisComponent?: ComponentType;\n}\n\nfunction ColumnInner(props: ColumnInnerProps) {\n const padding = extractPadding(props);\n\n // TODO: integrate padding vars more closely with the rest of the spacing (right now padding is a bit ad-hoc)\n const paddingVars: any = {\n \"--column-padding-top\": `${padding.paddingTop}px`,\n \"--column-padding-bottom\": `${padding.paddingBottom}px`,\n \"--column-padding-left\": `${padding.paddingLeft}px`,\n \"--column-padding-right\": `${padding.paddingRight}px`,\n };\n\n const {\n unitComponent = UnitComponent,\n unconformityLabels = \"minimal\",\n showLabels = true,\n width: _width = 300,\n columnWidth: _columnWidth = 150,\n showLabelColumn: _showLabelColumn = true,\n labelSuppressHeight,\n className,\n clipUnits = false,\n children,\n showTimescale,\n timescaleLevels,\n additionalTimescales,\n timescales,\n showTimescaleLabels,\n maxInternalColumns,\n onMouseOver,\n onClickTimescaleInterval,\n timescaleIntervalStyle,\n ageAxisComponent = CompositeAgeAxis,\n showAgeAxis = true,\n } = props;\n\n const { axisType } = useMacrostratColumnData();\n\n const columnRef = useColumnRef();\n\n // Coalesce unconformity label setting to a boolean\n let _timescaleUnconformityLabels = false;\n let _sectionUnconformityLabels = false;\n if (unconformityLabels === true || unconformityLabels === \"prominent\") {\n _sectionUnconformityLabels = true;\n } else if (unconformityLabels === \"minimal\") {\n _timescaleUnconformityLabels = true;\n }\n\n let width = _width;\n let columnWidth = _columnWidth;\n if (columnWidth > width) {\n columnWidth = width;\n }\n let showLabelColumn = _showLabelColumn;\n if (columnWidth > width - 10) {\n showLabelColumn = false;\n }\n\n // On unless turned off outright: passing levels or timescales no longer\n // overrides an explicit `false`\n const _showTimescale =\n axisType == ColumnAxisType.AGE && (showTimescale ?? true);\n\n return h(\n ColumnContainer,\n {\n ...useMouseEventHandlers(onMouseOver),\n style: paddingVars,\n className,\n },\n h(\"div.column\", { ref: columnRef }, [\n h.if(showAgeAxis)(ageAxisComponent),\n h.if(_showTimescale)(CompositeTimescale, {\n levels: timescaleLevels,\n additionalTimescales,\n timescales,\n showLabels: showTimescaleLabels,\n unconformityLabels: _timescaleUnconformityLabels,\n onClickInterval: onClickTimescaleInterval,\n intervalStyle: timescaleIntervalStyle,\n }),\n h(SectionsColumn, {\n unitComponent,\n showLabels,\n width,\n columnWidth,\n showLabelColumn,\n labelSuppressHeight,\n clipUnits,\n unconformityLabels: _sectionUnconformityLabels,\n maxInternalColumns,\n }),\n children,\n ]),\n );\n}\n\ntype ColumnMouseOverHandler = (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n) => void;\n\nfunction useMouseEventHandlers(\n _onMouseOver: ColumnMouseOverHandler | null = null,\n) {\n /** Click event handler */\n\n // Click handler for unit selection\n const dispatch = useUnitSelectionDispatch();\n const onClick = useCallback(\n (evt) => {\n dispatch?.(null, null);\n },\n [dispatch],\n );\n\n /** Hover event handlers */\n const scale = useCompositeScale();\n\n const onMouseOver = useCallback(\n (evt) => {\n const height = scale.invert(\n evt.clientY - evt.currentTarget.getBoundingClientRect().top,\n );\n _onMouseOver?.(null, height, evt);\n },\n [scale, _onMouseOver],\n );\n\n const onMouseOut = useCallback(() => {\n _onMouseOver?.(null, null, null);\n }, [_onMouseOver]);\n\n if (_onMouseOver == null) {\n return {\n onClick,\n onMouseOver: undefined,\n onMouseMove: undefined,\n onMouseOut: undefined,\n };\n }\n\n return { onMouseOver, onMouseMove: onMouseOver, onMouseOut, onClick };\n}\n\nexport interface ColumnContainerProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ColumnContainer(props: ColumnContainerProps) {\n const { className, ...rest } = props;\n const darkMode = useDarkMode();\n\n return h(Box, {\n className: classNames(\n className,\n {\n \"dark-mode\": darkMode?.isEnabled ?? false,\n },\n \"column-container\",\n ),\n ...rest,\n });\n}\n\nexport function ColumnBasicInfo({\n data,\n showColumnID = true,\n showReferences = true,\n}) {\n if (data == null) return null;\n return h(\"div.column-info\", [\n h(\"div.column-title-row\", [\n h(\"h2\", data.col_name),\n h.if(showColumnID)(\"h4\", h(Identifier, { id: data.col_id })),\n ]),\n h(DataField, { row: true, label: \"Group\", value: data.col_group }),\n h.if(showReferences)(ReferencesField, {\n refs: data.refs,\n inline: false,\n row: false,\n className: \"column-refs\",\n }),\n ]);\n}\n"],"names":[],"mappings":"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"}
@@ -102,41 +102,10 @@ function MacrostratColumnDataProvider({
102
102
  },
103
103
  [
104
104
  h__default.default(ColumnRefManager, { ref }),
105
- h__default.default(
106
- MacrostratColumnDataContext.Provider,
107
- { value },
108
- h__default.default(ColumnLayoutOverridesProvider, null, children)
109
- )
105
+ h__default.default(MacrostratColumnDataContext.Provider, { value }, children)
110
106
  ]
111
107
  );
112
108
  }
113
- const ColumnLayoutOverridesContext = React.createContext({
114
- labelColumnClaims: 0,
115
- claimLabelColumn: () => () => {
116
- }
117
- });
118
- function ColumnLayoutOverridesProvider({ children }) {
119
- const [labelColumnClaims, setClaims] = React.useState(0);
120
- const claimLabelColumn = React.useCallback(() => {
121
- setClaims((n) => n + 1);
122
- return () => setClaims((n) => Math.max(n - 1, 0));
123
- }, []);
124
- const value = React.useMemo(
125
- () => ({ labelColumnClaims, claimLabelColumn }),
126
- [labelColumnClaims, claimLabelColumn]
127
- );
128
- return h__default.default(ColumnLayoutOverridesContext.Provider, { value }, children);
129
- }
130
- function useClaimLabelColumn(active = true) {
131
- const { claimLabelColumn } = React.useContext(ColumnLayoutOverridesContext);
132
- React.useEffect(() => {
133
- if (!active) return;
134
- return claimLabelColumn();
135
- }, [active, claimLabelColumn]);
136
- }
137
- function useLabelColumnClaimed() {
138
- return React.useContext(ColumnLayoutOverridesContext).labelColumnClaims > 0;
139
- }
140
109
  function ColumnRefManager({ ref }) {
141
110
  const selectedUnitElement = scope.useAtomValue(unitSelection.selectedUnitElementAtom);
142
111
  React.useEffect(() => {
@@ -220,10 +189,8 @@ exports.UnitSelectionProvider = UnitSelectionProvider;
220
189
  exports.columnUnitsAtom = columnUnitsAtom;
221
190
  exports.columnUnitsMapAtom = columnUnitsMapAtom;
222
191
  exports.scope = scope;
223
- exports.useClaimLabelColumn = useClaimLabelColumn;
224
192
  exports.useColumnUnitsMap = useColumnUnitsMap;
225
193
  exports.useCompositeScale = useCompositeScale;
226
- exports.useLabelColumnClaimed = useLabelColumnClaimed;
227
194
  exports.useMacrostratColumnData = useMacrostratColumnData;
228
195
  exports.useMacrostratUnits = useMacrostratUnits;
229
196
  //# sourceMappingURL=store.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.cjs","sources":["../../src/data-provider/store.ts"],"sourcesContent":["import {\n createContext,\n ReactNode,\n RefObject,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport {\n CompositeColumnScale,\n createCompositeScale,\n} from \"../prepare-units/composite-scale\";\nimport { ColumnAxisType, ColumnProvider } from \"@macrostrat/column-components\";\nimport type { ExtUnit, PackageLayoutData } from \"../prepare-units\";\nimport {\n allowUnitSelectionAtom,\n selectedUnitIDAtom,\n UnitSelectionCallbacks,\n UnitSelectionCallbackManager,\n selectedUnitElementAtom,\n} from \"./unit-selection\";\nimport type { BaseUnit } from \"@macrostrat/api-types\";\nimport {\n type AtomMap,\n createScopedStore,\n StateIsolation,\n} from \"@macrostrat/scoped-store\";\nimport { atom } from \"jotai\";\nimport { useEffect } from \"react\";\n\nexport const scope: StateIsolation = createScopedStore();\n\nexport const columnUnitsAtom = atom<BaseUnit[]>();\n\nexport const columnUnitsMapAtom = atom<Map<number, BaseUnit> | null>((get) => {\n const units = get(columnUnitsAtom);\n if (!units) return null;\n const unitMap = new Map<number, BaseUnit>();\n units.forEach((unit) => {\n unitMap.set(unit.unit_id, unit);\n });\n return unitMap;\n});\n\nexport interface ColumnStateProviderProps<\n T extends BaseUnit,\n> extends Partial<UnitSelectionCallbacks> {\n children: ReactNode;\n units: T[];\n selectedUnit: number | null;\n allowUnitSelection?: boolean;\n}\n\nexport function MacrostratColumnStateProvider<T extends BaseUnit>({\n children,\n units,\n allowUnitSelection = false,\n onUnitSelected,\n selectedUnit,\n}: ColumnStateProviderProps<T>) {\n /** Top-level provider for Macrostrat column data.\n * It is either provided by the Column component itself, or\n * can be hoisted higher in the tree to provide a common data context\n */\n\n /* By default, unit selection is disabled. However, if any related props are passed,\n we enable it.\n */\n const _allowSelection = useMemo(() => {\n if (allowUnitSelection) {\n return true;\n }\n return selectedUnit != null || onUnitSelected != null;\n }, []);\n\n // Check if we're already in a columnStateProvider, and render a no-op if so\n const existingUnits = scope.useAtomValueIfExists(columnUnitsAtom);\n if (existingUnits != null) {\n if (allowUnitSelection || onUnitSelected || selectedUnit != null) {\n console.warn(\n \"MacrostratColumnStateProvider: unit selection props are ignored because a provider already exists in the tree\",\n );\n }\n return children;\n }\n\n const atomMap: AtomMap = [\n [columnUnitsAtom, units],\n [allowUnitSelectionAtom, _allowSelection],\n [selectedUnitIDAtom, selectedUnit],\n ];\n\n let selectionHandlers: ReactNode = null;\n if (_allowSelection) {\n selectionHandlers = h(UnitSelectionCallbackManager, { onUnitSelected });\n }\n\n return h(\n scope.Provider,\n {\n atoms: atomMap,\n keepUpdated: true,\n },\n [selectionHandlers, children],\n );\n}\n\n/** Interface for imperative handlers that can\n * be invoked outside of the column's react context\n */\nexport interface ColumnRef {\n scrollSelectedUnitIntoView(): void;\n}\n\nexport interface MacrostratColumnDataContext<T extends BaseUnit> {\n units: T[];\n sections: PackageLayoutData[];\n totalHeight?: number;\n axisType?: ColumnAxisType;\n allowUnitSelection?: boolean;\n /** True while the age window is animating; propagated to each section's\n * `ColumnProvider` so per-frame label/pattern work can be skipped. */\n isTransitioning?: boolean;\n /** Hide unit labels while transitioning (perf escape hatch; default false). */\n hideLabelsWhileTransitioning?: boolean;\n}\n\nexport interface ColumnDataProviderProps<T extends BaseUnit>\n extends MacrostratColumnDataContext<T>, ColumnStateProviderProps<T> {\n children: ReactNode;\n ref: RefObject<ColumnRef>;\n}\n\nconst MacrostratColumnDataContext =\n createContext<MacrostratColumnDataContext<any>>(null);\n\nexport function MacrostratColumnDataProvider<T extends BaseUnit>({\n children,\n units,\n sections,\n totalHeight,\n axisType,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n}: ColumnDataProviderProps<T>) {\n /** Internal provider for Macrostrat column data.\n * As a general rule, we want to provide data and column-axis\n * height calculations through the context, since these need to\n * be accessed by any component that lays out information on the\n * column.\n *\n * Cross-axis layout and view configuration (e.g., showing and hiding\n * of different components or labels) should be handled through passing\n * props to the components themselves.\n * */\n\n const value = useMemo(() => {\n // For now, change ordinal axis types to age axis types\n return {\n units,\n sections,\n totalHeight,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n };\n }, [\n units,\n sections,\n totalHeight,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ]);\n\n return h(\n MacrostratColumnStateProvider,\n {\n units,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n },\n [\n h(ColumnRefManager, { ref }),\n h(\n MacrostratColumnDataContext.Provider,\n { value },\n h(ColumnLayoutOverridesProvider, null, children),\n ),\n ],\n );\n}\n\n/* Layout overrides requested by a column's children. A layer that draws its\n * own labels beside the units (the surfaces view) claims the label column, so\n * the unit labels give way: it is one set of labels or the other. */\n\ninterface ColumnLayoutOverrides {\n /** How many children currently claim the label column */\n labelColumnClaims: number;\n claimLabelColumn(): () => void;\n}\n\nconst ColumnLayoutOverridesContext = createContext<ColumnLayoutOverrides>({\n labelColumnClaims: 0,\n claimLabelColumn: () => () => {},\n});\n\nfunction ColumnLayoutOverridesProvider({ children }: { children: ReactNode }) {\n const [labelColumnClaims, setClaims] = useState(0);\n const claimLabelColumn = useCallback(() => {\n setClaims((n) => n + 1);\n return () => setClaims((n) => Math.max(n - 1, 0));\n }, []);\n const value = useMemo(\n () => ({ labelColumnClaims, claimLabelColumn }),\n [labelColumnClaims, claimLabelColumn],\n );\n return h(ColumnLayoutOverridesContext.Provider, { value }, children);\n}\n\n/** Claim the column's label column for this component while it is mounted:\n * the unit labels are hidden in favor of whatever the caller draws there. */\nexport function useClaimLabelColumn(active: boolean = true) {\n const { claimLabelColumn } = useContext(ColumnLayoutOverridesContext);\n useEffect(() => {\n if (!active) return;\n return claimLabelColumn();\n }, [active, claimLabelColumn]);\n}\n\n/** Whether a child of the column has claimed the label column */\nexport function useLabelColumnClaimed(): boolean {\n return useContext(ColumnLayoutOverridesContext).labelColumnClaims > 0;\n}\n\nfunction ColumnRefManager({ ref }: { ref?: RefObject<ColumnRef> }) {\n const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);\n useEffect(() => {\n if (ref == null) return;\n ref.current = {\n scrollSelectedUnitIntoView(opts?: ScrollIntoViewOptions) {\n selectedUnitElement?.scrollIntoView(\n opts ?? {\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n },\n );\n },\n };\n }, [ref, selectedUnitElement]);\n\n return null;\n}\n\nexport function UnitSelectionProvider(props: ColumnStateProviderProps<any>) {\n /* A basic unit selection provider without column data context.\n Mostly for use with the CorrelationChart component.\n */\n return h(MacrostratColumnStateProvider, {\n ...props,\n allowUnitSelection: true,\n });\n}\n\nexport function useMacrostratColumnData() {\n const ctx = useContext(MacrostratColumnDataContext);\n if (!ctx) {\n throw new Error(\n \"useMacrostratColumnData must be used within a MacrostratColumnDataProvider\",\n );\n }\n return ctx;\n}\n\nexport function useMacrostratUnits() {\n return scope.useAtomValue(columnUnitsAtom);\n}\n\nexport function useColumnUnitsMap(): Map<number, ExtUnit> | null {\n try {\n return scope.useAtomValue(columnUnitsMapAtom) as Map<number, ExtUnit>;\n } catch {\n return null;\n }\n}\n\nexport function useCompositeScale(): CompositeColumnScale {\n const ctx = useMacrostratColumnData();\n return useMemo(\n () => createCompositeScale(ctx.sections, true),\n [ctx.sections],\n );\n}\n\nexport function MacrostratColumnProvider(props) {\n /** A column provider specialized the Macrostrat API. Maps more\n * generic concepts to Macrostrat-specific ones.\n */\n\n const { axisType, isTransitioning, hideLabelsWhileTransitioning } =\n useMacrostratColumnData();\n const { units, domain, pixelScale, scale, children } = props;\n return h(\n ColumnProvider,\n {\n axisType,\n divisions: units,\n range: domain,\n pixelsPerMeter: pixelScale,\n scale,\n isTransitioning,\n hideLabelsWhileTransitioning,\n },\n children,\n );\n}\n\n/** This is now a legacy provider */\nexport function LithologiesProvider({ children }) {\n useEffect(() => {\n console.warn(\n \"LithologiesProvider is deprecated. Replace with MacrostratDataProvider\",\n );\n }, []);\n return children;\n}\n"],"names":["createScopedStore","atom","useMemo","allowUnitSelectionAtom","selectedUnitIDAtom","h","UnitSelectionCallbackManager","createContext","useState","useCallback","useContext","useEffect","selectedUnitElementAtom","createCompositeScale","ColumnProvider"],"mappings":"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"}
1
+ {"version":3,"file":"store.cjs","sources":["../../src/data-provider/store.ts"],"sourcesContent":["import {\n createContext,\n ReactNode,\n RefObject,\n useContext,\n useMemo,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport {\n CompositeColumnScale,\n createCompositeScale,\n} from \"../prepare-units/composite-scale\";\nimport { ColumnAxisType, ColumnProvider } from \"@macrostrat/column-components\";\nimport type { ExtUnit, PackageLayoutData } from \"../prepare-units\";\nimport {\n allowUnitSelectionAtom,\n selectedUnitIDAtom,\n UnitSelectionCallbacks,\n UnitSelectionCallbackManager,\n selectedUnitElementAtom,\n} from \"./unit-selection\";\nimport type { BaseUnit } from \"@macrostrat/api-types\";\nimport {\n type AtomMap,\n createScopedStore,\n StateIsolation,\n} from \"@macrostrat/scoped-store\";\nimport { atom } from \"jotai\";\nimport { useEffect } from \"react\";\n\nexport const scope: StateIsolation = createScopedStore();\n\nexport const columnUnitsAtom = atom<BaseUnit[]>();\n\nexport const columnUnitsMapAtom = atom<Map<number, BaseUnit> | null>((get) => {\n const units = get(columnUnitsAtom);\n if (!units) return null;\n const unitMap = new Map<number, BaseUnit>();\n units.forEach((unit) => {\n unitMap.set(unit.unit_id, unit);\n });\n return unitMap;\n});\n\nexport interface ColumnStateProviderProps<\n T extends BaseUnit,\n> extends Partial<UnitSelectionCallbacks> {\n children: ReactNode;\n units: T[];\n selectedUnit: number | null;\n allowUnitSelection?: boolean;\n}\n\nexport function MacrostratColumnStateProvider<T extends BaseUnit>({\n children,\n units,\n allowUnitSelection = false,\n onUnitSelected,\n selectedUnit,\n}: ColumnStateProviderProps<T>) {\n /** Top-level provider for Macrostrat column data.\n * It is either provided by the Column component itself, or\n * can be hoisted higher in the tree to provide a common data context\n */\n\n /* By default, unit selection is disabled. However, if any related props are passed,\n we enable it.\n */\n const _allowSelection = useMemo(() => {\n if (allowUnitSelection) {\n return true;\n }\n return selectedUnit != null || onUnitSelected != null;\n }, []);\n\n // Check if we're already in a columnStateProvider, and render a no-op if so\n const existingUnits = scope.useAtomValueIfExists(columnUnitsAtom);\n if (existingUnits != null) {\n if (allowUnitSelection || onUnitSelected || selectedUnit != null) {\n console.warn(\n \"MacrostratColumnStateProvider: unit selection props are ignored because a provider already exists in the tree\",\n );\n }\n return children;\n }\n\n const atomMap: AtomMap = [\n [columnUnitsAtom, units],\n [allowUnitSelectionAtom, _allowSelection],\n [selectedUnitIDAtom, selectedUnit],\n ];\n\n let selectionHandlers: ReactNode = null;\n if (_allowSelection) {\n selectionHandlers = h(UnitSelectionCallbackManager, { onUnitSelected });\n }\n\n return h(\n scope.Provider,\n {\n atoms: atomMap,\n keepUpdated: true,\n },\n [selectionHandlers, children],\n );\n}\n\n/** Interface for imperative handlers that can\n * be invoked outside of the column's react context\n */\nexport interface ColumnRef {\n scrollSelectedUnitIntoView(): void;\n}\n\nexport interface MacrostratColumnDataContext<T extends BaseUnit> {\n units: T[];\n sections: PackageLayoutData[];\n totalHeight?: number;\n axisType?: ColumnAxisType;\n allowUnitSelection?: boolean;\n /** True while the age window is animating; propagated to each section's\n * `ColumnProvider` so per-frame label/pattern work can be skipped. */\n isTransitioning?: boolean;\n /** Hide unit labels while transitioning (perf escape hatch; default false). */\n hideLabelsWhileTransitioning?: boolean;\n}\n\nexport interface ColumnDataProviderProps<T extends BaseUnit>\n extends MacrostratColumnDataContext<T>, ColumnStateProviderProps<T> {\n children: ReactNode;\n ref: RefObject<ColumnRef>;\n}\n\nconst MacrostratColumnDataContext =\n createContext<MacrostratColumnDataContext<any>>(null);\n\nexport function MacrostratColumnDataProvider<T extends BaseUnit>({\n children,\n units,\n sections,\n totalHeight,\n axisType,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n}: ColumnDataProviderProps<T>) {\n /** Internal provider for Macrostrat column data.\n * As a general rule, we want to provide data and column-axis\n * height calculations through the context, since these need to\n * be accessed by any component that lays out information on the\n * column.\n *\n * Cross-axis layout and view configuration (e.g., showing and hiding\n * of different components or labels) should be handled through passing\n * props to the components themselves.\n * */\n\n const value = useMemo(() => {\n // For now, change ordinal axis types to age axis types\n return {\n units,\n sections,\n totalHeight,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n };\n }, [\n units,\n sections,\n totalHeight,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ]);\n\n return h(\n MacrostratColumnStateProvider,\n {\n units,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n },\n [\n h(ColumnRefManager, { ref }),\n h(MacrostratColumnDataContext.Provider, { value }, children),\n ],\n );\n}\n\nfunction ColumnRefManager({ ref }: { ref?: RefObject<ColumnRef> }) {\n const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);\n useEffect(() => {\n if (ref == null) return;\n ref.current = {\n scrollSelectedUnitIntoView(opts?: ScrollIntoViewOptions) {\n selectedUnitElement?.scrollIntoView(\n opts ?? {\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n },\n );\n },\n };\n }, [ref, selectedUnitElement]);\n\n return null;\n}\n\nexport function UnitSelectionProvider(props: ColumnStateProviderProps<any>) {\n /* A basic unit selection provider without column data context.\n Mostly for use with the CorrelationChart component.\n */\n return h(MacrostratColumnStateProvider, {\n ...props,\n allowUnitSelection: true,\n });\n}\n\nexport function useMacrostratColumnData() {\n const ctx = useContext(MacrostratColumnDataContext);\n if (!ctx) {\n throw new Error(\n \"useMacrostratColumnData must be used within a MacrostratColumnDataProvider\",\n );\n }\n return ctx;\n}\n\nexport function useMacrostratUnits() {\n return scope.useAtomValue(columnUnitsAtom);\n}\n\nexport function useColumnUnitsMap(): Map<number, ExtUnit> | null {\n try {\n return scope.useAtomValue(columnUnitsMapAtom) as Map<number, ExtUnit>;\n } catch {\n return null;\n }\n}\n\nexport function useCompositeScale(): CompositeColumnScale {\n const ctx = useMacrostratColumnData();\n return useMemo(\n () => createCompositeScale(ctx.sections, true),\n [ctx.sections],\n );\n}\n\nexport function MacrostratColumnProvider(props) {\n /** A column provider specialized the Macrostrat API. Maps more\n * generic concepts to Macrostrat-specific ones.\n */\n\n const { axisType, isTransitioning, hideLabelsWhileTransitioning } =\n useMacrostratColumnData();\n const { units, domain, pixelScale, scale, children } = props;\n return h(\n ColumnProvider,\n {\n axisType,\n divisions: units,\n range: domain,\n pixelsPerMeter: pixelScale,\n scale,\n isTransitioning,\n hideLabelsWhileTransitioning,\n },\n children,\n );\n}\n\n/** This is now a legacy provider */\nexport function LithologiesProvider({ children }) {\n useEffect(() => {\n console.warn(\n \"LithologiesProvider is deprecated. Replace with MacrostratDataProvider\",\n );\n }, []);\n return children;\n}\n"],"names":["createScopedStore","atom","useMemo","allowUnitSelectionAtom","selectedUnitIDAtom","h","UnitSelectionCallbackManager","createContext","selectedUnitElementAtom","useEffect","useContext","createCompositeScale","ColumnProvider"],"mappings":"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"}
@@ -45,11 +45,6 @@ export interface ColumnDataProviderProps<T extends BaseUnit> extends MacrostratC
45
45
  ref: RefObject<ColumnRef>;
46
46
  }
47
47
  export declare function MacrostratColumnDataProvider<T extends BaseUnit>({ children, units, sections, totalHeight, axisType, allowUnitSelection, onUnitSelected, selectedUnit, isTransitioning, hideLabelsWhileTransitioning, ref, }: ColumnDataProviderProps<T>): import('react').FunctionComponentElement<ColumnStateProviderProps<BaseUnit>>;
48
- /** Claim the column's label column for this component while it is mounted:
49
- * the unit labels are hidden in favor of whatever the caller draws there. */
50
- export declare function useClaimLabelColumn(active?: boolean): void;
51
- /** Whether a child of the column has claimed the label column */
52
- export declare function useLabelColumnClaimed(): boolean;
53
48
  export declare function UnitSelectionProvider(props: ColumnStateProviderProps<any>): import('react').FunctionComponentElement<ColumnStateProviderProps<BaseUnit>>;
54
49
  export declare function useMacrostratColumnData(): MacrostratColumnDataContext<any>;
55
50
  export declare function useMacrostratUnits(): any;
@@ -1,4 +1,4 @@
1
- import { useEffect, useMemo, useContext, createContext, useState, useCallback } from "react";
1
+ import { useEffect, useMemo, useContext, createContext } from "react";
2
2
  import h from "@macrostrat/hyper";
3
3
  import { createCompositeScale } from "../prepare-units/composite-scale.js";
4
4
  import { ColumnProvider } from "@macrostrat/column-components";
@@ -98,41 +98,10 @@ function MacrostratColumnDataProvider({
98
98
  },
99
99
  [
100
100
  h(ColumnRefManager, { ref }),
101
- h(
102
- MacrostratColumnDataContext.Provider,
103
- { value },
104
- h(ColumnLayoutOverridesProvider, null, children)
105
- )
101
+ h(MacrostratColumnDataContext.Provider, { value }, children)
106
102
  ]
107
103
  );
108
104
  }
109
- const ColumnLayoutOverridesContext = createContext({
110
- labelColumnClaims: 0,
111
- claimLabelColumn: () => () => {
112
- }
113
- });
114
- function ColumnLayoutOverridesProvider({ children }) {
115
- const [labelColumnClaims, setClaims] = useState(0);
116
- const claimLabelColumn = useCallback(() => {
117
- setClaims((n) => n + 1);
118
- return () => setClaims((n) => Math.max(n - 1, 0));
119
- }, []);
120
- const value = useMemo(
121
- () => ({ labelColumnClaims, claimLabelColumn }),
122
- [labelColumnClaims, claimLabelColumn]
123
- );
124
- return h(ColumnLayoutOverridesContext.Provider, { value }, children);
125
- }
126
- function useClaimLabelColumn(active = true) {
127
- const { claimLabelColumn } = useContext(ColumnLayoutOverridesContext);
128
- useEffect(() => {
129
- if (!active) return;
130
- return claimLabelColumn();
131
- }, [active, claimLabelColumn]);
132
- }
133
- function useLabelColumnClaimed() {
134
- return useContext(ColumnLayoutOverridesContext).labelColumnClaims > 0;
135
- }
136
105
  function ColumnRefManager({ ref }) {
137
106
  const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);
138
107
  useEffect(() => {
@@ -217,10 +186,8 @@ export {
217
186
  columnUnitsAtom,
218
187
  columnUnitsMapAtom,
219
188
  scope,
220
- useClaimLabelColumn,
221
189
  useColumnUnitsMap,
222
190
  useCompositeScale,
223
- useLabelColumnClaimed,
224
191
  useMacrostratColumnData,
225
192
  useMacrostratUnits
226
193
  };
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sources":["../../src/data-provider/store.ts"],"sourcesContent":["import {\n createContext,\n ReactNode,\n RefObject,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport {\n CompositeColumnScale,\n createCompositeScale,\n} from \"../prepare-units/composite-scale\";\nimport { ColumnAxisType, ColumnProvider } from \"@macrostrat/column-components\";\nimport type { ExtUnit, PackageLayoutData } from \"../prepare-units\";\nimport {\n allowUnitSelectionAtom,\n selectedUnitIDAtom,\n UnitSelectionCallbacks,\n UnitSelectionCallbackManager,\n selectedUnitElementAtom,\n} from \"./unit-selection\";\nimport type { BaseUnit } from \"@macrostrat/api-types\";\nimport {\n type AtomMap,\n createScopedStore,\n StateIsolation,\n} from \"@macrostrat/scoped-store\";\nimport { atom } from \"jotai\";\nimport { useEffect } from \"react\";\n\nexport const scope: StateIsolation = createScopedStore();\n\nexport const columnUnitsAtom = atom<BaseUnit[]>();\n\nexport const columnUnitsMapAtom = atom<Map<number, BaseUnit> | null>((get) => {\n const units = get(columnUnitsAtom);\n if (!units) return null;\n const unitMap = new Map<number, BaseUnit>();\n units.forEach((unit) => {\n unitMap.set(unit.unit_id, unit);\n });\n return unitMap;\n});\n\nexport interface ColumnStateProviderProps<\n T extends BaseUnit,\n> extends Partial<UnitSelectionCallbacks> {\n children: ReactNode;\n units: T[];\n selectedUnit: number | null;\n allowUnitSelection?: boolean;\n}\n\nexport function MacrostratColumnStateProvider<T extends BaseUnit>({\n children,\n units,\n allowUnitSelection = false,\n onUnitSelected,\n selectedUnit,\n}: ColumnStateProviderProps<T>) {\n /** Top-level provider for Macrostrat column data.\n * It is either provided by the Column component itself, or\n * can be hoisted higher in the tree to provide a common data context\n */\n\n /* By default, unit selection is disabled. However, if any related props are passed,\n we enable it.\n */\n const _allowSelection = useMemo(() => {\n if (allowUnitSelection) {\n return true;\n }\n return selectedUnit != null || onUnitSelected != null;\n }, []);\n\n // Check if we're already in a columnStateProvider, and render a no-op if so\n const existingUnits = scope.useAtomValueIfExists(columnUnitsAtom);\n if (existingUnits != null) {\n if (allowUnitSelection || onUnitSelected || selectedUnit != null) {\n console.warn(\n \"MacrostratColumnStateProvider: unit selection props are ignored because a provider already exists in the tree\",\n );\n }\n return children;\n }\n\n const atomMap: AtomMap = [\n [columnUnitsAtom, units],\n [allowUnitSelectionAtom, _allowSelection],\n [selectedUnitIDAtom, selectedUnit],\n ];\n\n let selectionHandlers: ReactNode = null;\n if (_allowSelection) {\n selectionHandlers = h(UnitSelectionCallbackManager, { onUnitSelected });\n }\n\n return h(\n scope.Provider,\n {\n atoms: atomMap,\n keepUpdated: true,\n },\n [selectionHandlers, children],\n );\n}\n\n/** Interface for imperative handlers that can\n * be invoked outside of the column's react context\n */\nexport interface ColumnRef {\n scrollSelectedUnitIntoView(): void;\n}\n\nexport interface MacrostratColumnDataContext<T extends BaseUnit> {\n units: T[];\n sections: PackageLayoutData[];\n totalHeight?: number;\n axisType?: ColumnAxisType;\n allowUnitSelection?: boolean;\n /** True while the age window is animating; propagated to each section's\n * `ColumnProvider` so per-frame label/pattern work can be skipped. */\n isTransitioning?: boolean;\n /** Hide unit labels while transitioning (perf escape hatch; default false). */\n hideLabelsWhileTransitioning?: boolean;\n}\n\nexport interface ColumnDataProviderProps<T extends BaseUnit>\n extends MacrostratColumnDataContext<T>, ColumnStateProviderProps<T> {\n children: ReactNode;\n ref: RefObject<ColumnRef>;\n}\n\nconst MacrostratColumnDataContext =\n createContext<MacrostratColumnDataContext<any>>(null);\n\nexport function MacrostratColumnDataProvider<T extends BaseUnit>({\n children,\n units,\n sections,\n totalHeight,\n axisType,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n}: ColumnDataProviderProps<T>) {\n /** Internal provider for Macrostrat column data.\n * As a general rule, we want to provide data and column-axis\n * height calculations through the context, since these need to\n * be accessed by any component that lays out information on the\n * column.\n *\n * Cross-axis layout and view configuration (e.g., showing and hiding\n * of different components or labels) should be handled through passing\n * props to the components themselves.\n * */\n\n const value = useMemo(() => {\n // For now, change ordinal axis types to age axis types\n return {\n units,\n sections,\n totalHeight,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n };\n }, [\n units,\n sections,\n totalHeight,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ]);\n\n return h(\n MacrostratColumnStateProvider,\n {\n units,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n },\n [\n h(ColumnRefManager, { ref }),\n h(\n MacrostratColumnDataContext.Provider,\n { value },\n h(ColumnLayoutOverridesProvider, null, children),\n ),\n ],\n );\n}\n\n/* Layout overrides requested by a column's children. A layer that draws its\n * own labels beside the units (the surfaces view) claims the label column, so\n * the unit labels give way: it is one set of labels or the other. */\n\ninterface ColumnLayoutOverrides {\n /** How many children currently claim the label column */\n labelColumnClaims: number;\n claimLabelColumn(): () => void;\n}\n\nconst ColumnLayoutOverridesContext = createContext<ColumnLayoutOverrides>({\n labelColumnClaims: 0,\n claimLabelColumn: () => () => {},\n});\n\nfunction ColumnLayoutOverridesProvider({ children }: { children: ReactNode }) {\n const [labelColumnClaims, setClaims] = useState(0);\n const claimLabelColumn = useCallback(() => {\n setClaims((n) => n + 1);\n return () => setClaims((n) => Math.max(n - 1, 0));\n }, []);\n const value = useMemo(\n () => ({ labelColumnClaims, claimLabelColumn }),\n [labelColumnClaims, claimLabelColumn],\n );\n return h(ColumnLayoutOverridesContext.Provider, { value }, children);\n}\n\n/** Claim the column's label column for this component while it is mounted:\n * the unit labels are hidden in favor of whatever the caller draws there. */\nexport function useClaimLabelColumn(active: boolean = true) {\n const { claimLabelColumn } = useContext(ColumnLayoutOverridesContext);\n useEffect(() => {\n if (!active) return;\n return claimLabelColumn();\n }, [active, claimLabelColumn]);\n}\n\n/** Whether a child of the column has claimed the label column */\nexport function useLabelColumnClaimed(): boolean {\n return useContext(ColumnLayoutOverridesContext).labelColumnClaims > 0;\n}\n\nfunction ColumnRefManager({ ref }: { ref?: RefObject<ColumnRef> }) {\n const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);\n useEffect(() => {\n if (ref == null) return;\n ref.current = {\n scrollSelectedUnitIntoView(opts?: ScrollIntoViewOptions) {\n selectedUnitElement?.scrollIntoView(\n opts ?? {\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n },\n );\n },\n };\n }, [ref, selectedUnitElement]);\n\n return null;\n}\n\nexport function UnitSelectionProvider(props: ColumnStateProviderProps<any>) {\n /* A basic unit selection provider without column data context.\n Mostly for use with the CorrelationChart component.\n */\n return h(MacrostratColumnStateProvider, {\n ...props,\n allowUnitSelection: true,\n });\n}\n\nexport function useMacrostratColumnData() {\n const ctx = useContext(MacrostratColumnDataContext);\n if (!ctx) {\n throw new Error(\n \"useMacrostratColumnData must be used within a MacrostratColumnDataProvider\",\n );\n }\n return ctx;\n}\n\nexport function useMacrostratUnits() {\n return scope.useAtomValue(columnUnitsAtom);\n}\n\nexport function useColumnUnitsMap(): Map<number, ExtUnit> | null {\n try {\n return scope.useAtomValue(columnUnitsMapAtom) as Map<number, ExtUnit>;\n } catch {\n return null;\n }\n}\n\nexport function useCompositeScale(): CompositeColumnScale {\n const ctx = useMacrostratColumnData();\n return useMemo(\n () => createCompositeScale(ctx.sections, true),\n [ctx.sections],\n );\n}\n\nexport function MacrostratColumnProvider(props) {\n /** A column provider specialized the Macrostrat API. Maps more\n * generic concepts to Macrostrat-specific ones.\n */\n\n const { axisType, isTransitioning, hideLabelsWhileTransitioning } =\n useMacrostratColumnData();\n const { units, domain, pixelScale, scale, children } = props;\n return h(\n ColumnProvider,\n {\n axisType,\n divisions: units,\n range: domain,\n pixelsPerMeter: pixelScale,\n scale,\n isTransitioning,\n hideLabelsWhileTransitioning,\n },\n children,\n );\n}\n\n/** This is now a legacy provider */\nexport function LithologiesProvider({ children }) {\n useEffect(() => {\n console.warn(\n \"LithologiesProvider is deprecated. Replace with MacrostratDataProvider\",\n );\n }, []);\n return children;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sources":["../../src/data-provider/store.ts"],"sourcesContent":["import {\n createContext,\n ReactNode,\n RefObject,\n useContext,\n useMemo,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport {\n CompositeColumnScale,\n createCompositeScale,\n} from \"../prepare-units/composite-scale\";\nimport { ColumnAxisType, ColumnProvider } from \"@macrostrat/column-components\";\nimport type { ExtUnit, PackageLayoutData } from \"../prepare-units\";\nimport {\n allowUnitSelectionAtom,\n selectedUnitIDAtom,\n UnitSelectionCallbacks,\n UnitSelectionCallbackManager,\n selectedUnitElementAtom,\n} from \"./unit-selection\";\nimport type { BaseUnit } from \"@macrostrat/api-types\";\nimport {\n type AtomMap,\n createScopedStore,\n StateIsolation,\n} from \"@macrostrat/scoped-store\";\nimport { atom } from \"jotai\";\nimport { useEffect } from \"react\";\n\nexport const scope: StateIsolation = createScopedStore();\n\nexport const columnUnitsAtom = atom<BaseUnit[]>();\n\nexport const columnUnitsMapAtom = atom<Map<number, BaseUnit> | null>((get) => {\n const units = get(columnUnitsAtom);\n if (!units) return null;\n const unitMap = new Map<number, BaseUnit>();\n units.forEach((unit) => {\n unitMap.set(unit.unit_id, unit);\n });\n return unitMap;\n});\n\nexport interface ColumnStateProviderProps<\n T extends BaseUnit,\n> extends Partial<UnitSelectionCallbacks> {\n children: ReactNode;\n units: T[];\n selectedUnit: number | null;\n allowUnitSelection?: boolean;\n}\n\nexport function MacrostratColumnStateProvider<T extends BaseUnit>({\n children,\n units,\n allowUnitSelection = false,\n onUnitSelected,\n selectedUnit,\n}: ColumnStateProviderProps<T>) {\n /** Top-level provider for Macrostrat column data.\n * It is either provided by the Column component itself, or\n * can be hoisted higher in the tree to provide a common data context\n */\n\n /* By default, unit selection is disabled. However, if any related props are passed,\n we enable it.\n */\n const _allowSelection = useMemo(() => {\n if (allowUnitSelection) {\n return true;\n }\n return selectedUnit != null || onUnitSelected != null;\n }, []);\n\n // Check if we're already in a columnStateProvider, and render a no-op if so\n const existingUnits = scope.useAtomValueIfExists(columnUnitsAtom);\n if (existingUnits != null) {\n if (allowUnitSelection || onUnitSelected || selectedUnit != null) {\n console.warn(\n \"MacrostratColumnStateProvider: unit selection props are ignored because a provider already exists in the tree\",\n );\n }\n return children;\n }\n\n const atomMap: AtomMap = [\n [columnUnitsAtom, units],\n [allowUnitSelectionAtom, _allowSelection],\n [selectedUnitIDAtom, selectedUnit],\n ];\n\n let selectionHandlers: ReactNode = null;\n if (_allowSelection) {\n selectionHandlers = h(UnitSelectionCallbackManager, { onUnitSelected });\n }\n\n return h(\n scope.Provider,\n {\n atoms: atomMap,\n keepUpdated: true,\n },\n [selectionHandlers, children],\n );\n}\n\n/** Interface for imperative handlers that can\n * be invoked outside of the column's react context\n */\nexport interface ColumnRef {\n scrollSelectedUnitIntoView(): void;\n}\n\nexport interface MacrostratColumnDataContext<T extends BaseUnit> {\n units: T[];\n sections: PackageLayoutData[];\n totalHeight?: number;\n axisType?: ColumnAxisType;\n allowUnitSelection?: boolean;\n /** True while the age window is animating; propagated to each section's\n * `ColumnProvider` so per-frame label/pattern work can be skipped. */\n isTransitioning?: boolean;\n /** Hide unit labels while transitioning (perf escape hatch; default false). */\n hideLabelsWhileTransitioning?: boolean;\n}\n\nexport interface ColumnDataProviderProps<T extends BaseUnit>\n extends MacrostratColumnDataContext<T>, ColumnStateProviderProps<T> {\n children: ReactNode;\n ref: RefObject<ColumnRef>;\n}\n\nconst MacrostratColumnDataContext =\n createContext<MacrostratColumnDataContext<any>>(null);\n\nexport function MacrostratColumnDataProvider<T extends BaseUnit>({\n children,\n units,\n sections,\n totalHeight,\n axisType,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ref,\n}: ColumnDataProviderProps<T>) {\n /** Internal provider for Macrostrat column data.\n * As a general rule, we want to provide data and column-axis\n * height calculations through the context, since these need to\n * be accessed by any component that lays out information on the\n * column.\n *\n * Cross-axis layout and view configuration (e.g., showing and hiding\n * of different components or labels) should be handled through passing\n * props to the components themselves.\n * */\n\n const value = useMemo(() => {\n // For now, change ordinal axis types to age axis types\n return {\n units,\n sections,\n totalHeight,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n };\n }, [\n units,\n sections,\n totalHeight,\n axisType,\n isTransitioning,\n hideLabelsWhileTransitioning,\n ]);\n\n return h(\n MacrostratColumnStateProvider,\n {\n units,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n },\n [\n h(ColumnRefManager, { ref }),\n h(MacrostratColumnDataContext.Provider, { value }, children),\n ],\n );\n}\n\nfunction ColumnRefManager({ ref }: { ref?: RefObject<ColumnRef> }) {\n const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);\n useEffect(() => {\n if (ref == null) return;\n ref.current = {\n scrollSelectedUnitIntoView(opts?: ScrollIntoViewOptions) {\n selectedUnitElement?.scrollIntoView(\n opts ?? {\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n },\n );\n },\n };\n }, [ref, selectedUnitElement]);\n\n return null;\n}\n\nexport function UnitSelectionProvider(props: ColumnStateProviderProps<any>) {\n /* A basic unit selection provider without column data context.\n Mostly for use with the CorrelationChart component.\n */\n return h(MacrostratColumnStateProvider, {\n ...props,\n allowUnitSelection: true,\n });\n}\n\nexport function useMacrostratColumnData() {\n const ctx = useContext(MacrostratColumnDataContext);\n if (!ctx) {\n throw new Error(\n \"useMacrostratColumnData must be used within a MacrostratColumnDataProvider\",\n );\n }\n return ctx;\n}\n\nexport function useMacrostratUnits() {\n return scope.useAtomValue(columnUnitsAtom);\n}\n\nexport function useColumnUnitsMap(): Map<number, ExtUnit> | null {\n try {\n return scope.useAtomValue(columnUnitsMapAtom) as Map<number, ExtUnit>;\n } catch {\n return null;\n }\n}\n\nexport function useCompositeScale(): CompositeColumnScale {\n const ctx = useMacrostratColumnData();\n return useMemo(\n () => createCompositeScale(ctx.sections, true),\n [ctx.sections],\n );\n}\n\nexport function MacrostratColumnProvider(props) {\n /** A column provider specialized the Macrostrat API. Maps more\n * generic concepts to Macrostrat-specific ones.\n */\n\n const { axisType, isTransitioning, hideLabelsWhileTransitioning } =\n useMacrostratColumnData();\n const { units, domain, pixelScale, scale, children } = props;\n return h(\n ColumnProvider,\n {\n axisType,\n divisions: units,\n range: domain,\n pixelsPerMeter: pixelScale,\n scale,\n isTransitioning,\n hideLabelsWhileTransitioning,\n },\n children,\n );\n}\n\n/** This is now a legacy provider */\nexport function LithologiesProvider({ children }) {\n useEffect(() => {\n console.warn(\n \"LithologiesProvider is deprecated. Replace with MacrostratDataProvider\",\n );\n }, []);\n return children;\n}\n"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -165,10 +165,8 @@ exports.UnitSelectionProvider = store.UnitSelectionProvider;
165
165
  exports.columnUnitsAtom = store.columnUnitsAtom;
166
166
  exports.columnUnitsMapAtom = store.columnUnitsMapAtom;
167
167
  exports.scope = store.scope;
168
- exports.useClaimLabelColumn = store.useClaimLabelColumn;
169
168
  exports.useColumnUnitsMap = store.useColumnUnitsMap;
170
169
  exports.useCompositeScale = store.useCompositeScale;
171
- exports.useLabelColumnClaimed = store.useLabelColumnClaimed;
172
170
  exports.useMacrostratColumnData = store.useMacrostratColumnData;
173
171
  exports.useMacrostratUnits = store.useMacrostratUnits;
174
172
  exports.MeasurementDataContext = dataProvider.MeasurementDataContext;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import { HeightMethod, HybridScaleType } from "./prepare-units/types.js";
24
24
  import { INTERVAL_BOUND_TOLERANCE, TIE_POINT_STATUSES, boundaryToSurface, compareAlongAxis, formatAge, formatProportion, inferTiePointStatuses, isPositionAxis, nullifyUnitID, surfaceLabel, surfacePosition, surfaceStatusDescriptions, surfaceStatusLabels, surfaceStatuses, surfaceToken, surfacesFromBoundaries, surfacesFromUnits } from "./age-model/types.js";
25
25
  import { IsotopesColumn, IsotopesDataset } from "./facets/carbon-isotopes/isotopes-column.js";
26
26
  import { LegendPanelHeader, ReferencesField, ThicknessField, UnitDetailsFeature, UnitDetailsPanel } from "./unit-details/panel.js";
27
- import { LithologiesProvider, MacrostratColumnDataProvider, MacrostratColumnProvider, MacrostratColumnStateProvider, UnitSelectionProvider, columnUnitsAtom, columnUnitsMapAtom, scope, useClaimLabelColumn, useColumnUnitsMap, useCompositeScale, useLabelColumnClaimed, useMacrostratColumnData, useMacrostratUnits } from "./data-provider/store.js";
27
+ import { LithologiesProvider, MacrostratColumnDataProvider, MacrostratColumnProvider, MacrostratColumnStateProvider, UnitSelectionProvider, columnUnitsAtom, columnUnitsMapAtom, scope, useColumnUnitsMap, useCompositeScale, useMacrostratColumnData, useMacrostratUnits } from "./data-provider/store.js";
28
28
  import { MeasurementDataContext, MeasurementDataProvider, useMeasurementData } from "./facets/carbon-isotopes/data-provider.js";
29
29
  import { MergeSectionsMode, agesOverlap, createUnitSorter, ensureArray, ensureRealFloat, getPositionWithinUnit, getUnitHeightRange, unitsOverlap } from "./prepare-units/utils.js";
30
30
  import { PBDBFossilsColumn } from "./facets/fossils/index.js";
@@ -168,7 +168,6 @@ export {
168
168
  unitsOverlap,
169
169
  useAnimatedAgeWindow,
170
170
  useAtomOverlayPosition,
171
- useClaimLabelColumn,
172
171
  useColumnAgeModel,
173
172
  useColumnNav,
174
173
  useColumnRef,
@@ -176,7 +175,6 @@ export {
176
175
  useColumnUnitsMap,
177
176
  useCompositeScale,
178
177
  useFossilData,
179
- useLabelColumnClaimed,
180
178
  useMacrostratColumnData,
181
179
  useMacrostratUnits,
182
180
  useMeasurementData,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@macrostrat/column-views",
3
- "version": "3.13.0",
3
+ "version": "3.14.0",
4
4
  "description": "Data views for Macrostrat stratigraphic columns",
5
5
  "repository": {
6
6
  "type": "git",
@@ -3,7 +3,9 @@
3
3
  * `ColumnSurfaces` is dropped into a `Column` as a child. It draws one line
4
4
  * per surface across the units (`ColumnSurfaceLines`) and a notes column of
5
5
  * labels beside them (`ColumnSurfaceLabels`), styled by `boundary_status` and
6
- * `boundary_type`, with hover and selection.
6
+ * `boundary_type`, with hover and selection. The labels sit where the unit
7
+ * labels would, so pass `showLabelColumn: false` to the `Column` while they
8
+ * are shown.
7
9
  */
8
10
  import hyper from "@macrostrat/hyper";
9
11
  import classNames from "classnames";
@@ -24,11 +26,7 @@ import {
24
26
  TagSize,
25
27
  } from "@macrostrat/data-components";
26
28
  import { useMacrostratDefs } from "@macrostrat/data-provider";
27
- import {
28
- useClaimLabelColumn,
29
- useCompositeScale,
30
- useMacrostratColumnData,
31
- } from "../data-provider";
29
+ import { useCompositeScale, useMacrostratColumnData } from "../data-provider";
32
30
  import { ColumnNotes } from "../notes";
33
31
  import { AgeLabel } from "../unit-details/age-range";
34
32
  import columnStyles from "../column.module.sass";
@@ -247,8 +245,8 @@ interface SurfaceNote extends NoteData {
247
245
  }
248
246
 
249
247
  /** A notes column of surface labels, laid out beside the units with the
250
- * collision avoidance of `NotesColumn`. It takes the label column over from
251
- * the unit labels while mounted: labels are one or the other. */
248
+ * collision avoidance of `NotesColumn`. It draws over the unit labels, so the
249
+ * `Column` should hide its label column (`showLabelColumn: false`). */
252
250
  export function ColumnSurfaceLabels(props: ColumnSurfaceLabelsProps) {
253
251
  const {
254
252
  surfaces,
@@ -263,7 +261,6 @@ export function ColumnSurfaceLabels(props: ColumnSurfaceLabelsProps) {
263
261
  const { axisType } = useMacrostratColumnData();
264
262
  const scale = useCompositeScale();
265
263
  const intervalMap = useSurfaceIntervalDefs(surfaces);
266
- useClaimLabelColumn();
267
264
 
268
265
  const labeled = useMemo(() => {
269
266
  const byStatus = filterByStatus(surfaces, labelStatuses);
package/src/column.ts CHANGED
@@ -30,7 +30,6 @@ import {
30
30
  import {
31
31
  MacrostratColumnDataProvider,
32
32
  useCompositeScale,
33
- useLabelColumnClaimed,
34
33
  useMacrostratColumnData,
35
34
  } from "./data-provider";
36
35
  import {
@@ -175,22 +174,6 @@ export function Column(props: ColumnProps) {
175
174
  _axisType = ColumnAxisType.AGE;
176
175
  }
177
176
 
178
- let _sectionOptions = sectionOptions;
179
- if (_sectionOptions == undefined) {
180
- // Build our own section options function based on targetUnitHeight
181
- _sectionOptions = (ctx) => {
182
- /** Scale targetUnitHeight by the number of units, so that a section with just a few units
183
- * is drawn taller than a section with many units.
184
- */
185
- const n = ctx.units.length;
186
- const expansionFactor = 8 / Math.min(Math.max(n, 1), 8);
187
-
188
- return {
189
- targetUnitHeight: targetUnitHeight * expansionFactor,
190
- };
191
- };
192
- }
193
-
194
177
  const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {
195
178
  axisType: _axisType,
196
179
  t_age,
@@ -199,7 +182,7 @@ export function Column(props: ColumnProps) {
199
182
  b_pos,
200
183
  mergeSections,
201
184
  targetUnitHeight,
202
- sectionOptions: _sectionOptions,
185
+ sectionOptions,
203
186
  unconformityHeight,
204
187
  pixelScale,
205
188
  pixelsPerMyr,
@@ -286,9 +269,6 @@ function ColumnInner(props: ColumnInnerProps) {
286
269
  } = props;
287
270
 
288
271
  const { axisType } = useMacrostratColumnData();
289
- // A child layer drawing its own labels (the surfaces view) takes the label
290
- // column over from the unit labels
291
- const labelColumnClaimed = useLabelColumnClaimed();
292
272
 
293
273
  const columnRef = useColumnRef();
294
274
 
@@ -307,13 +287,14 @@ function ColumnInner(props: ColumnInnerProps) {
307
287
  columnWidth = width;
308
288
  }
309
289
  let showLabelColumn = _showLabelColumn;
310
- if (columnWidth > width - 10 || labelColumnClaimed) {
290
+ if (columnWidth > width - 10) {
311
291
  showLabelColumn = false;
312
292
  }
313
293
 
314
294
  // On unless turned off outright: passing levels or timescales no longer
315
295
  // overrides an explicit `false`
316
- const _showTimescale = axisType == ColumnAxisType.AGE && (showTimescale ?? true);
296
+ const _showTimescale =
297
+ axisType == ColumnAxisType.AGE && (showTimescale ?? true);
317
298
 
318
299
  return h(
319
300
  ColumnContainer,
@@ -2,10 +2,8 @@ import {
2
2
  createContext,
3
3
  ReactNode,
4
4
  RefObject,
5
- useCallback,
6
5
  useContext,
7
6
  useMemo,
8
- useState,
9
7
  } from "react";
10
8
  import h from "@macrostrat/hyper";
11
9
  import {
@@ -190,58 +188,11 @@ export function MacrostratColumnDataProvider<T extends BaseUnit>({
190
188
  },
191
189
  [
192
190
  h(ColumnRefManager, { ref }),
193
- h(
194
- MacrostratColumnDataContext.Provider,
195
- { value },
196
- h(ColumnLayoutOverridesProvider, null, children),
197
- ),
191
+ h(MacrostratColumnDataContext.Provider, { value }, children),
198
192
  ],
199
193
  );
200
194
  }
201
195
 
202
- /* Layout overrides requested by a column's children. A layer that draws its
203
- * own labels beside the units (the surfaces view) claims the label column, so
204
- * the unit labels give way: it is one set of labels or the other. */
205
-
206
- interface ColumnLayoutOverrides {
207
- /** How many children currently claim the label column */
208
- labelColumnClaims: number;
209
- claimLabelColumn(): () => void;
210
- }
211
-
212
- const ColumnLayoutOverridesContext = createContext<ColumnLayoutOverrides>({
213
- labelColumnClaims: 0,
214
- claimLabelColumn: () => () => {},
215
- });
216
-
217
- function ColumnLayoutOverridesProvider({ children }: { children: ReactNode }) {
218
- const [labelColumnClaims, setClaims] = useState(0);
219
- const claimLabelColumn = useCallback(() => {
220
- setClaims((n) => n + 1);
221
- return () => setClaims((n) => Math.max(n - 1, 0));
222
- }, []);
223
- const value = useMemo(
224
- () => ({ labelColumnClaims, claimLabelColumn }),
225
- [labelColumnClaims, claimLabelColumn],
226
- );
227
- return h(ColumnLayoutOverridesContext.Provider, { value }, children);
228
- }
229
-
230
- /** Claim the column's label column for this component while it is mounted:
231
- * the unit labels are hidden in favor of whatever the caller draws there. */
232
- export function useClaimLabelColumn(active: boolean = true) {
233
- const { claimLabelColumn } = useContext(ColumnLayoutOverridesContext);
234
- useEffect(() => {
235
- if (!active) return;
236
- return claimLabelColumn();
237
- }, [active, claimLabelColumn]);
238
- }
239
-
240
- /** Whether a child of the column has claimed the label column */
241
- export function useLabelColumnClaimed(): boolean {
242
- return useContext(ColumnLayoutOverridesContext).labelColumnClaims > 0;
243
- }
244
-
245
196
  function ColumnRefManager({ ref }: { ref?: RefObject<ColumnRef> }) {
246
197
  const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);
247
198
  useEffect(() => {