@macrostrat/column-components
Version:
React rendering primitives for stratigraphic columns
260 lines (249 loc) • 10.8 kB
JavaScript
import $f5b828bbb980a05d$export$2e2bcd8739ae039 from "./column-components.de2f942b.js";
import {hasSpan as $4c0de4777a549324$export$edcf47b10d53ec33} from "./column-components.bdaf6e51.js";
import {FlexibleNode as $a0c6e663f4b9fd09$export$bbc681c531bc8538, Force as $a0c6e663f4b9fd09$export$ab7becc5aba9dee4, Node as $a0c6e663f4b9fd09$export$85c928794f8d04d4, Renderer as $a0c6e663f4b9fd09$export$88530751e3977073} from "./column-components.410c3de3.js";
import {ColumnContext as $c7Uig$ColumnContext, ColumnLayoutProvider as $c7Uig$ColumnLayoutProvider} from "./column-components.0ccab336.js";
import {createContext as $c7Uig$createContext, useContext as $c7Uig$useContext} from "react";
import {StatefulComponent as $c7Uig$StatefulComponent} from "@macrostrat/ui-components";
import {compareAgeRanges as $c7Uig$compareAgeRanges, AgeRangeRelationship as $c7Uig$AgeRangeRelationship} from "@macrostrat/stratigraphy-utils";
const $00c4b971e86fe1d8$export$20c7bffdb69233c9 = (0, $c7Uig$createContext)(null);
const $00c4b971e86fe1d8$var$buildColumnIndex = function() {
/*
* Find out where on the X axis arrows,
* etc. should plot to aviod overlaps
*/ const heightTracker = [];
return function(note) {
let colIx = 0;
// Get column that note should render in
const nPossibleCols = heightTracker.length + 1;
for(let column = 0, end = nPossibleCols, asc = 0 <= end; asc ? column <= end : column >= end; asc ? column++ : column--){
if (heightTracker[column] == null) heightTracker[column] = note.height;
if (heightTracker[column] < note.height) {
const hy = note.top_height || note.height;
heightTracker[column] = hy;
colIx = column;
break;
}
}
return colIx;
};
};
function $00c4b971e86fe1d8$var$withinDomain(scale) {
const scaleDomain = scale.domain();
const d1 = [
Math.min(...scaleDomain),
Math.max(...scaleDomain)
];
return (d)=>{
const noteRange = [
d.height,
d.top_height ?? d.height
];
const rel = (0, $c7Uig$compareAgeRanges)(d1, noteRange);
return rel !== (0, $c7Uig$AgeRangeRelationship).Disjoint;
};
}
class $00c4b971e86fe1d8$export$c48860e5e2301a05 extends (0, $c7Uig$StatefulComponent) {
static{
this.contextType = (0, $c7Uig$ColumnContext);
}
static{
this.defaultProps = {
paddingLeft: 60,
estimatedTextHeight (note, width) {
const txt = note.note || "";
return 12;
}
};
}
constructor(props){
super(props);
this.computeContextValue = this.computeContextValue.bind(this);
this.savedRendererForWidth = this.savedRendererForWidth.bind(this);
this.generatePath = this.generatePath.bind(this);
this.createNodeForNote = this.createNodeForNote.bind(this);
this.computeForceLayout = this.computeForceLayout.bind(this);
this.updateHeight = this.updateHeight.bind(this);
this.updateNotes = this.updateNotes.bind(this);
this.componentDidMount = this.componentDidMount.bind(this);
this.componentDidUpdate = this.componentDidUpdate.bind(this);
// State is very minimal to start
const { noteComponent: noteComponent } = this.props;
this.state = {
notes: [],
elementHeights: {},
columnIndex: {},
nodes: {},
generatePath: this.generatePath,
createNodeForNote: this.createNodeForNote,
noteComponent: noteComponent
};
}
render() {
const { children: children, width: width } = this.props;
return (0, $f5b828bbb980a05d$export$2e2bcd8739ae039)($00c4b971e86fe1d8$export$20c7bffdb69233c9.Provider, {
value: this.state
}, (0, $f5b828bbb980a05d$export$2e2bcd8739ae039)((0, $c7Uig$ColumnLayoutProvider), {
width: width
}, children));
}
computeContextValue() {
const { width: width, paddingLeft: paddingLeft } = this.props;
// Clamp notes to within scale boundaries
// (we could turn this off if desired)
const { scaleClamped: scale } = this.context;
const forwardedValues = {
paddingLeft: // Forwarded values from column context
// There may be a more elegant way to do this
paddingLeft,
scale: scale,
width: width
};
// Compute force layout
const renderer = new (0, $a0c6e663f4b9fd09$export$88530751e3977073)({
direction: "right",
layerGap: paddingLeft,
nodeHeight: 5
});
return this.setState({
renderer: renderer,
updateHeight: this.updateHeight,
generatePath: this.generatePath,
...forwardedValues
});
}
savedRendererForWidth(width) {
if (this._rendererIndex == null) this._rendererIndex = {};
if (this._rendererIndex[width] == null) this._rendererIndex[width] = new (0, $a0c6e663f4b9fd09$export$88530751e3977073)({
direction: "right",
layerGap: width,
nodeHeight: 5
});
return this._rendererIndex[width];
}
generatePath(node, pixelOffset) {
const { paddingLeft: paddingLeft } = this.props;
const renderer = this.savedRendererForWidth(paddingLeft - pixelOffset);
try {
return renderer.generatePath(node);
} catch (err) {
return null;
}
}
createNodeForNote(note) {
const { notes: notes, elementHeights: elementHeights } = this.state;
let { scaleClamped: scale } = this.context;
const { id: noteID } = note;
const pixelHeight = elementHeights[noteID] || 10;
const padding = 5;
let noteHeight = scale(note.height);
if ((0, $4c0de4777a549324$export$edcf47b10d53ec33)(note)) {
const upperHeight = scale(note.top_height);
const harr = [
noteHeight - padding,
upperHeight + padding
];
if (harr[0] - harr[1] > 0) return new (0, $a0c6e663f4b9fd09$export$bbc681c531bc8538)(harr, pixelHeight);
noteHeight = (harr[0] + harr[1]) / 2;
}
return new (0, $a0c6e663f4b9fd09$export$85c928794f8d04d4)(noteHeight, pixelHeight);
}
computeForceLayout(prevProps, prevState) {
let { notes: notes, nodes: nodes, elementHeights: elementHeights } = this.state;
const { pixelHeight: pixelHeight } = this.context;
const { width: width, paddingLeft: paddingLeft, forceOptions: forceOptions } = this.props;
if (notes.length === 0) return;
// Something is wrong...
//return if elementHeights.length < notes.length
// Return if we've already computed nodes
const v1 = Object.keys(nodes).length === notes.length;
if (prevState == null) prevState = {};
const v2 = elementHeights === prevState.elementHeights || [];
if (v1 && v2) return;
const force = new (0, $a0c6e663f4b9fd09$export$ab7becc5aba9dee4)({
minPos: 0,
maxPos: pixelHeight,
nodeSpacing: 0,
...forceOptions
});
const dataNodes = notes.map(this.createNodeForNote);
force.nodes(dataNodes).compute();
const _nodes = force.nodes() ?? [];
const nodesObj = {};
for(let i = 0; i < _nodes.length; i++){
const node = _nodes[i];
const note = notes[i];
nodesObj[note.id] = node;
}
return this.updateState({
nodes: {
$set: nodesObj
}
});
}
updateHeight(id, height) {
if (height == null) return;
const { elementHeights: elementHeights } = this.state;
elementHeights[id] = height;
return this.updateState({
elementHeights: {
$set: elementHeights
}
});
}
updateNotes() {
// We received a new set of notes from props
const { scaleClamped: scaleClamped } = this.context;
const notes = this.props.notes.filter($00c4b971e86fe1d8$var$withinDomain(scaleClamped)).sort((a, b)=>a.height - b.height);
const columnIndex = notes.map($00c4b971e86fe1d8$var$buildColumnIndex());
return this.setState({
notes: notes,
columnIndex: columnIndex
});
}
/*
* Lifecycle methods
*/ componentDidMount() {
this._previousContext = null;
this.updateNotes();
return this.computeContextValue();
}
componentDidUpdate(prevProps, prevState) {
if (this.props.notes !== prevProps.notes) this.updateNotes();
// Update note component
const { noteComponent: noteComponent } = this.props;
if (noteComponent !== prevProps.noteComponent) this.setState({
noteComponent: noteComponent
});
this.computeForceLayout.call(prevProps, prevState);
if (this.props.notes === prevProps.notes) return;
if (this.context === this._previousContext) return;
this.computeContextValue();
return this._previousContext = this.context;
}
}
function $00c4b971e86fe1d8$export$b74b5a117fd60a1d(props) {
let { padding: padding, width: width, ...rest } = props;
if (padding == null) padding = 5;
const { pixelHeight: pixelHeight } = (0, $c7Uig$useContext)((0, $c7Uig$ColumnContext));
if (width == null) ({ width: width } = (0, $c7Uig$useContext)($00c4b971e86fe1d8$export$20c7bffdb69233c9));
if (isNaN(width)) return null;
return (0, $f5b828bbb980a05d$export$2e2bcd8739ae039)("rect", {
width: width + 2 * padding,
height: pixelHeight,
transform: `translate(${-padding},${-padding})`,
...rest
});
}
const $00c4b971e86fe1d8$export$53f13bca8c4c45d9 = function({ ...rest }) {
return (0, $f5b828bbb980a05d$export$2e2bcd8739ae039)($00c4b971e86fe1d8$export$b74b5a117fd60a1d, {
className: "underlay",
...rest
});
};
function $00c4b971e86fe1d8$export$77b5a6d4f65baae() {
const ctx = (0, $c7Uig$useContext)($00c4b971e86fe1d8$export$20c7bffdb69233c9);
if (ctx == null) throw new Error("useNoteLayout must be used within a NoteLayoutProvider");
return ctx;
}
export {$00c4b971e86fe1d8$export$20c7bffdb69233c9 as NoteLayoutContext, $00c4b971e86fe1d8$export$c48860e5e2301a05 as NoteLayoutProvider, $00c4b971e86fe1d8$export$b74b5a117fd60a1d as NoteRect, $00c4b971e86fe1d8$export$53f13bca8c4c45d9 as NoteUnderlay, $00c4b971e86fe1d8$export$77b5a6d4f65baae as useNoteLayout};
//# sourceMappingURL=column-components.fea107a9.js.map