Skip to content

Commit

Permalink
Streamline column selection
Browse files Browse the repository at this point in the history
  • Loading branch information
davenquinn committed Jan 31, 2025
1 parent e8f5d93 commit 27a7325
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 25 deletions.
6 changes: 4 additions & 2 deletions packages/column-views/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Timescale, TimescaleOrientation } from "@macrostrat/timescale";
import { useDarkMode } from "@macrostrat/ui-components";
import classNames from "classnames";
import { group } from "d3-array";
import { useContext, useMemo } from "react";
import { RefObject, useContext, useMemo } from "react";
import { AgeAxis } from "./age-axis";
import styles from "./column.module.sass";
import { CompositeUnitsColumn, TrackedLabeledUnit } from "./units";
Expand Down Expand Up @@ -225,6 +225,7 @@ function Column(
className?: string;
mergeOverlappingSections?: boolean;
showLabelColumn?: boolean;
columnRef?: RefObject<HTMLDivElement>;
}
) {
const {
Expand All @@ -237,6 +238,7 @@ function Column(
className: baseClassName,
showLabelColumn = true,
mergeOverlappingSections = true,
columnRef,
children,
...rest
} = props;
Expand All @@ -257,7 +259,7 @@ function Column(
return h(
"div.column-container",
{ className },
h("div.column", [
h("div.column", { ref: columnRef }, [
h("div.age-axis-label", "Age (Ma)"),
h(
"div.main-column",
Expand Down
6 changes: 4 additions & 2 deletions packages/column-views/src/selection-popover.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import classNames from "classnames";
import hyper from "@macrostrat/hyper";
import { Button, Popover } from "@blueprintjs/core";
import { DOMElement } from "react";
Expand Down Expand Up @@ -58,5 +57,8 @@ export function UnitSelectionPopover(props) {
return null;
}

return h("div.unit-selection-popover", JSONView({ data: unit }));
return h(
"div.unit-popover-container",
h(UnitDetailsPopover, h(JSONView, { data: unit, showRoot: false }))
);
}
2 changes: 2 additions & 0 deletions packages/column-views/src/units/boxes.module.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.unit-label-container
pointer-events: none
30 changes: 23 additions & 7 deletions packages/column-views/src/units/boxes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,21 @@ import {
useColumn,
useGeologicPattern,
} from "@macrostrat/column-components";
import h from "@macrostrat/hyper";
import { ReactNode, useContext, useEffect, useMemo, useRef } from "react";
import hyper from "@macrostrat/hyper";
import {
ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
} from "react";
import { resolveID, scalePattern } from "./resolvers";
import { useSelectedUnit, useUnitSelector } from "./selection";
import { IUnit, transformAxisType } from "./types";
import styles from "./boxes.module.sass";

const h = hyper.styled(styles);

interface RectBounds {
x: number;
Expand Down Expand Up @@ -121,7 +131,15 @@ function useUnitSelectionManager(
): [boolean, () => void] {
const selectedUnit = useSelectedUnit();
const selected = selectedUnit?.unit_id == unit.unit_id;
const onClick = useUnitSelector(unit);
const selectUnit = useUnitSelector(unit);

const onClick = useCallback(
(evt: Event) => {
console.log(ref.current, evt);
selectUnit(ref.current, evt);
},
[unit, ref, selectUnit]
);

useEffect(() => {
if (!selected) return;
Expand All @@ -144,17 +162,15 @@ function LabeledUnit(props: LabeledUnitProps) {
...useUnitRect(division, { widthFraction, axisType }),
...baseBounds,
};
const onClick = useUnitSelector(division);
const { width, height } = bounds;
return h(Unit, { className: "labeled-unit", division, onClick, ...bounds }, [
return h(Unit, { className: "labeled-unit", division, ...bounds }, [
h(
ForeignObject,
bounds,
{ ...bounds, className: "unit-label-container" },
h(SizeAwareLabel, {
className: "unit-overlay",
labelClassName: "unit-label",
style: { width, height },
onClick,
label,
onVisibilityChanged(viz) {
onLabelUpdated(label, viz);
Expand Down
45 changes: 32 additions & 13 deletions packages/column-views/src/units/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,30 @@ import {
useEffect,
useMemo,
useState,
ReactNode,
RefObject,
useCallback,
} from "react";
import { IUnit } from "@macrostrat/column-views";

type UnitSelectDispatch = Dispatch<SetStateAction<BaseUnit | null>>;
type UnitSelectDispatch = (
unit: BaseUnit | null,
target: HTMLElement,
event: Event
) => void;

const UnitSelectionContext = createContext<BaseUnit | null>(null);
const DispatchContext = createContext<UnitSelectDispatch | null>(null);

export function useUnitSelector(u: BaseUnit | null) {
const dispatch = useContext(DispatchContext);
return (evt: Event) => {
dispatch?.(u);
return (target: HTMLElement, evt: Event) => {
console.log("Dispatch", u, target, evt);
dispatch?.(u, target, evt);
evt.stopPropagation();
};
}

export function useUnitSelectionDispatch() {
return useContext(DispatchContext);
}

export function useSelectedUnit() {
return useContext(UnitSelectionContext);
}
Expand All @@ -36,39 +41,53 @@ interface UnitSelectionProps<T extends BaseUnit> {
children: React.ReactNode;
unit: T | null;
setUnit: Dispatch<SetStateAction<T>>;
onUnitSelected?: (unit: T, target: HTMLElement, event: Event) => void;
}

export function UnitSelectionProvider<T extends BaseUnit>(
props: Partial<UnitSelectionProps<T>>
) {
const { unit, setUnit, children } = props;
const { unit, setUnit, onUnitSelected, children } = props;

if (unit == null && setUnit == null) {
return h(StatefulUnitSelectionProvider, props);
}

return h(BaseUnitSelectionProvider, { unit, setUnit }, children);
return h(
BaseUnitSelectionProvider,
{ unit, setUnit, onUnitSelected },
children
);
}

function StatefulUnitSelectionProvider<T extends BaseUnit>(props: {
children: React.ReactNode;
children: ReactNode;
onUnitSelected?: (unit: T, target: HTMLElement, event: Event) => void;
}) {
const { children } = props;
const [unit, setUnit] = useState<T | null>(null);

return h(BaseUnitSelectionProvider, { children, unit, setUnit });
return h(BaseUnitSelectionProvider, { ...props, unit, setUnit });
}

function BaseUnitSelectionProvider<T extends BaseUnit>({
children,
unit,
setUnit,
onUnitSelected,
}: UnitSelectionProps<T>) {
const value = useMemo(() => unit, [unit?.unit_id]);

const _onUnitSelected = useCallback(
(unit: T, target: HTMLElement, event: Event) => {
setUnit(unit);
onUnitSelected?.(unit, target, event);
},
[setUnit, onUnitSelected]
);

return h(
DispatchContext.Provider,
{ value: setUnit },
{ value: _onUnitSelected },
h(UnitSelectionContext.Provider, { value }, children)
);
}
Expand Down
48 changes: 47 additions & 1 deletion packages/column-views/stories/column.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
import { Spinner } from "@blueprintjs/core";
import { PatternProvider } from "@macrostrat/column-components/stories/base-section";
import "@macrostrat/style-system";
import { UnitSelectionPopover } from "../src/selection-popover";
import { createRef, DOMElement, useEffect, useState } from "react";

const h = hyper.styled(styles);

Expand Down Expand Up @@ -135,9 +137,53 @@ export function BasicUnitViewer() {
return h("div.unit-viewer", JSONView({ data: unit, showRoot: false }));
}

export function WithUnitSelection() {
export function WithBasicUnitSelection() {
return h(
UnitSelectionProvider,
h(BasicColumn, { id: 432, showLabelColumn: true }, [h(BasicUnitViewer)])
);
}

interface ItemPosition {
x: number;
y: number;
width: number;
height: number;
}

export function WithUnitSelectionPopover() {
const ref = createRef<HTMLElement>();
// Selected item position
const [position, setPosition] = useState<ItemPosition | null>(null);

useEffect(() => {
if (position == null) return;
console.log("Position", position);
}, [position]);

return h(
UnitSelectionProvider,
{
onUnitSelected: (unit, target: SVGElement | HTMLElement) => {
console.log(target);
if (unit == null) {
setPosition(null);
return;
}
const el: HTMLElement = ref.current;
if (el == null) return;
const rect = el.getBoundingClientRect();
const targetRect = target.getBoundingClientRect();
setPosition({
x: targetRect.left - rect.left,
y: targetRect.top - rect.top,
width: targetRect.width,
height: targetRect.height,
});
},
},
h(BasicColumn, { id: 432, showLabelColumn: true, columnRef: ref }, [
h(UnitSelectionPopover),
])
);
}

0 comments on commit 27a7325

Please sign in to comment.