UNPKG

@macrostrat/column-components

Version:

React rendering primitives for stratigraphic columns

164 lines (145 loc) 3.89 kB
import { useContext, useMemo, useEffect, useRef, useState } from "react"; import h from "../hyper"; import { useNoteLayout } from "./layout"; import { NoteEditorContext } from "./editor"; import type { NoteData } from "./types"; import { NotePositioner, NoteConnector, NodeConnectorOptions, } from "./connector"; import { useColumn } from "../context"; type NoteListProps = NodeConnectorOptions & { inEditMode?: boolean; editable?: boolean; onClickNote?: (note: NoteData) => void; editHandler?: Function; }; export function NotesList(props: NoteListProps) { let { inEditMode: editable, onClickNote, ...rest } = props; if (editable == null) { editable = false; } const { notes, nodes: nodeIndex, updateHeight, noteComponent, scale, } = useNoteLayout(); const { pixelHeight: columnHeight } = useColumn(); const notesInfo = useMemo(() => { let notes1 = notes.map((note) => { const node = nodeIndex[note.id]; const pixelHeight = node?.width ?? 10; const pixelOffset = node?.currentPos ?? scale(note.top_height); return { note, node, pixelOffset, pixelHeight, spacing: { above: pixelOffset - pixelHeight, below: columnHeight - pixelOffset, }, }; }); // Adjust spacing to account for nearby nodes for (let i = 0; i < notes1.length; i++) { const { spacing, pixelOffset } = notes1[i]; if (i > 0) { const prevNote = notes1[i - 1]; // Get distance from the previous note's bottom // to the current note's top spacing.above = pixelOffset - prevNote.pixelOffset - prevNote.pixelHeight; prevNote.spacing.below = spacing.above; } } return notes1; }, [notes, nodeIndex, scale]); return h( "g", notesInfo.map(({ note, pixelOffset, pixelHeight, spacing }) => { // If the note has a bad pixelOffset, skip it if (pixelOffset == null || pixelHeight == null) { return null; } return h(Note, { key: note.id, note, pixelOffset, pixelHeight, editable, updateHeight, onClick: onClickNote, noteBodyComponent: noteComponent, spacing, ...rest, }); }), ); } type NodeSpacing = { above: number; below: number; }; type NodeInfo = any; interface NoteProps { editable: boolean; note: NoteData; editHandler?: Function; style?: object; deltaConnectorAttachment?: number; pixelOffset?: number; pixelHeight?: number; updateHeight?: (id: string | number, height: number) => void; onClick?: (note: NoteData) => void; noteBodyComponent: (props: { note: NoteData; spacing?: NodeSpacing }) => any; spacing?: NodeSpacing; } function Note(props: NoteProps) { const { note, pixelOffset, pixelHeight, updateHeight, deltaConnectorAttachment, noteBodyComponent, spacing, onClick, } = props; const ref = useRef<HTMLElement>(null); useEffect(() => { if (ref.current) { const newHeight = ref.current.offsetHeight; if (newHeight !== pixelHeight) { updateHeight(note.id, newHeight); } } }, [note, pixelHeight, updateHeight]); const offsetY = pixelOffset; const noteHeight = pixelHeight; const { setEditingNote, editingNote } = useContext(NoteEditorContext) as any; const onClick_ = onClick ?? setEditingNote; const _onClickHandler = (evt) => { onClick_(note); }; if (editingNote === note) { return null; } return h("g.note", [ h(NoteConnector, { note, deltaConnectorAttachment }), h( NotePositioner, { offsetY, noteHeight, ref, onClick: _onClickHandler, }, h(noteBodyComponent, { note, spacing }), ), ]); } export { NotePositioner, NoteConnector };