@macrostrat/column-components
Version:
React rendering primitives for stratigraphic columns
243 lines (223 loc) • 5.36 kB
text/typescript
import { ComponentType, useCallback, useContext, useState } from "react";
import h from "../hyper";
import { NotesList } from "./note";
import NoteDefs from "./defs";
import { NoteData } from "./types";
import { useModelEditor, ColumnContext } from "../context";
import { NoteLayoutProvider, NoteUnderlay } from "./layout";
import {
NoteEditor,
NoteTextEditor,
NoteEditorContext,
NoteEditorProvider,
} from "./editor";
import { NewNotePositioner } from "./new";
import { NodeConnectorOptions } from "./connector";
export * from "./types";
import type { ReactNode } from "react";
interface NoteComponentProps {
visibility: string;
note: NoteData;
onClick: Function;
}
function NoteComponent(props: NoteComponentProps) {
const { visibility, note, onClick } = props;
const text = note.note;
return h(
"p.col-note-label",
{
style: { visibility },
onClick,
},
text,
);
}
const CancelEditUnderlay = function () {
const { setEditingNote } = useContext(NoteEditorContext) as any;
return h(NoteUnderlay, {
onClick(evt) {
setEditingNote(null);
evt.stopPropagation();
},
});
};
interface NotesColumnBaseProps extends NodeConnectorOptions {
width?: number;
paddingLeft?: number;
transform?: string;
notes?: NoteData[];
noteComponent?: ComponentType<any>;
onClickNote?: (note: NoteData) => void;
children?: ReactNode;
forceOptions?: object;
}
interface EditableNotesColumnProps extends NotesColumnBaseProps {
inEditMode?: boolean;
onUpdateNote?: (n: NoteData) => void;
onDeleteNote?: (n: NoteData) => void;
onCreateNote?: Function;
noteEditor?: ComponentType<any>;
allowPositionEditing?: boolean;
}
interface FocusedNotesColumnProps extends NotesColumnBaseProps {
focusedNote?: NoteData | null;
onFocusNote?: (note: NoteData | null) => void;
focusedNoteComponent?: ComponentType<any>;
}
function EditableNotesColumn(props: EditableNotesColumnProps) {
const {
width,
paddingLeft = 60,
transform,
notes,
inEditMode = false,
onUpdateNote,
onDeleteNote,
onCreateNote,
noteComponent = NoteComponent,
noteEditor = NoteTextEditor,
allowPositionEditing = false,
forceOptions,
onClickNote,
} = props;
const innerWidth = width - paddingLeft;
return h(
NoteLayoutProvider,
{
notes,
width: innerWidth,
paddingLeft,
noteComponent,
forceOptions,
},
[
h(
NoteEditorProvider,
{
inEditMode,
noteEditor,
onCreateNote,
onUpdateNote,
onDeleteNote,
},
[
h("g.section-log", { transform, className: "focusable editable" }, [
h(NoteDefs),
h(CancelEditUnderlay),
h(NotesList, {
onClickNote,
}),
h(NewNotePositioner),
h(NoteEditor, { allowPositionEditing }),
]),
],
),
],
);
}
function FocusableNoteColumn(props: FocusedNotesColumnProps) {
/** A notes column with selection capabilities. */
const {
width,
paddingLeft = 60,
transform,
notes,
forceOptions,
noteComponent = NoteComponent,
focusedNoteComponent = NoteComponent,
deltaConnectorAttachment,
onClickNote,
} = props;
const innerWidth = width - paddingLeft;
return h(
NoteLayoutProvider,
{
notes,
width: innerWidth,
paddingLeft,
noteComponent,
forceOptions,
},
[
h(
NoteEditorProvider,
{
inEditMode: true,
noteEditor: focusedNoteComponent,
},
[
h("g.section-log", { transform, className: "focusable" }, [
h(NoteDefs),
h(CancelEditUnderlay),
h(NotesList, {
onClickNote,
deltaConnectorAttachment,
}),
h(NoteEditor, { allowPositionEditing: false }),
]),
],
),
],
);
}
function StaticNotesColumn(props: NotesColumnBaseProps) {
/** A non-editable notes column. */
const {
width,
paddingLeft = 60,
transform,
notes,
noteComponent = NoteComponent,
deltaConnectorAttachment,
onClickNote,
forceOptions,
children,
} = props;
const innerWidth = width - paddingLeft;
return h(
NoteLayoutProvider,
{
notes,
width: innerWidth,
paddingLeft,
noteComponent,
forceOptions,
},
[
h("g.section-log", { transform }, [
h(NoteDefs),
h(NotesList, {
deltaConnectorAttachment,
onClickNote,
}),
children,
]),
],
);
}
function NotesColumn(props: NotesColumnProps) {
const { editable = false, ...rest } = props;
const ctx = useContext(ColumnContext);
// not sure why we have this here.
if (ctx?.scaleClamped == null) return null;
let c: ComponentType = StaticNotesColumn;
if (editable) {
c = EditableNotesColumn;
} else if (rest.focusedNoteComponent != null) {
c = FocusableNoteColumn;
}
return h(c, rest);
}
interface NotesColumnProps
extends FocusedNotesColumnProps, EditableNotesColumnProps {
editable?: boolean;
}
export {
NotesColumn,
NoteComponent,
NoteTextEditor,
NoteEditor,
NoteEditorContext,
NotesColumnProps,
StaticNotesColumn,
};