Skip to content

Commit

Permalink
Release 11.3.0 (#408)
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkh authored Jul 9, 2024
2 parents df8fa55 + 24f8259 commit 1d7c2ba
Show file tree
Hide file tree
Showing 18 changed files with 541 additions and 280 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "visyn_core",
"description": "Core repository for datavisyn applications.",
"version": "11.2.0",
"version": "11.3.0",
"author": {
"name": "datavisyn GmbH",
"email": "[email protected]",
Expand Down
41 changes: 41 additions & 0 deletions src/hooks/CoreHooks.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import { StoryObj, Meta } from '@storybook/react';
import { Button, Center, Stack } from '@mantine/core';
import { useSetRef } from './useSetRef';

function SetRefTest() {
const [element, setElement] = React.useState<Element>();
const { setRef } = useSetRef({
register: (el) => {
setElement(el);
},
cleanup: (el) => {
setElement(undefined);
},
});
const [visible, setVisible] = React.useState(false);

const toggle = () => {
setVisible((v) => !v);
};

return (
<Center w={600} h={400}>
<Stack>
<Button onClick={toggle}>Toggle visibility</Button>

{visible ? <div ref={setRef}>Current element is {element?.nodeName}</div> : <div>No element</div>}
</Stack>
</Center>
);
}

const meta: Meta<typeof SetRefTest> = {
component: SetRefTest,
};

export default meta;

type Story = StoryObj<typeof SetRefTest>;

export const UseSetRef: Story = {};
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './useEvent';
export * from './useInitVisynApp';
export * from './useSyncedRef';
export * from './useVisynUser';
export * from './useSetRef';
37 changes: 37 additions & 0 deletions src/hooks/useSetRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from 'react';
import { useSyncedRef } from './useSyncedRef';

/**
* Hook that provides a setRef function, passable to the ref prop of a React element.
* The setRef function will register and cleanup the element with the provided callbacks.
* https://legacy.reactjs.org/docs/hooks-faq.html?source=post_page-----eb7c15198780--------------------------------#how-can-i-measure-a-dom-node
* https://medium.com/@teh_builder/ref-objects-inside-useeffect-hooks-eb7c15198780
*/
export function useSetRef<T extends Element>(props?: { cleanup: (lastElement: T) => void; register: (element: T) => void }) {
const ref = React.useRef<T>();

const callbackRef = useSyncedRef(props);

const setRef = React.useCallback(
(newElement: T | null) => {
if (ref.current) {
callbackRef.current?.cleanup(ref.current);
}

if (newElement) {
callbackRef.current?.register(newElement);
}

ref.current = newElement;
},
[callbackRef],
);

return {
// This ref is stale, do not use in useEffect dependencies
ref,

// This should be passed to the ref
setRef,
};
}
14 changes: 7 additions & 7 deletions src/vis/hexbin/Hexplot.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box, Container, Text, Center, rem } from '@mantine/core';
import { useElementSize } from '@mantine/hooks';
import { useElementSize, useMergedRef } from '@mantine/hooks';
import * as hex from 'd3-hexbin';
import { HexbinBin } from 'd3-hexbin';
import * as d3v7 from 'd3v7';
Expand Down Expand Up @@ -202,9 +202,7 @@ export function Hexplot({ config, allColumns, selectionCallback = () => null, se
config.color,
]);

const contentRef = React.useRef();

const { value } = useLasso(contentRef, {
const { setRef: lassoSetRef, value } = useLasso({
skip: config.dragMode !== EScatterSelectSettings.RECTANGLE,
onChangeEnd: (lasso) => {
if (lasso) {
Expand All @@ -231,18 +229,20 @@ export function Hexplot({ config, allColumns, selectionCallback = () => null, se
},
});

useZoom(contentRef, {
const { setRef: zoomSetRef } = useZoom({
value: transform,
onChange: setTransform,
zoomExtent: [1, 10],
});

usePan(contentRef, {
const { setRef: panSetRef } = usePan({
value: transform,
onChange: setTransform,
skip: config.dragMode !== EScatterSelectSettings.PAN,
});

const mergedRef = useMergedRef(zoomSetRef, lassoSetRef, panSetRef);

return (
<Box ref={hexRef}>
<Container
Expand All @@ -257,7 +257,7 @@ export function Hexplot({ config, allColumns, selectionCallback = () => null, se
},
}}
>
<svg id={id} width={width + margin.left + margin.right} height={height + margin.top + margin.bottom} ref={contentRef}>
<svg id={id} width={width + margin.left + margin.right} height={height + margin.top + margin.bottom} ref={mergedRef}>
{xScale ? <XAxis multiples={multiples} vertPosition={height + margin.top} yRange={[margin.top, height + margin.top]} xScale={xScale} /> : null}
{yScale ? <YAxis multiples={multiples} horizontalPosition={margin.left} xRange={[margin.left, width + margin.left]} yScale={yScale} /> : null}

Expand Down
3 changes: 3 additions & 0 deletions src/vis/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ export * from './sankey/interfaces';

// export utility functions
export * from './general/utils';

// export vis hooks
export * from './vishooks';
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,33 @@ const MIN_BRUSH_SIZE = 8;
const BORDER_CORRECTION = 1;

interface BrushProps {
brush: Brush;
value: Brush;
direction?: Direction;
onChange?: (brush: Brush) => void;
onChangeEnd?: (brush: Brush) => void;
parent: React.RefObject<SVGSVGElement>;
parent: React.RefObject<Element>;
extent?: Extent;
clearOnMouse?: boolean;
}

/**
* Brush with draggable borders
*/
export function BrushRect({ parent, brush, direction = 'xy', onChange, onChangeEnd, extent, clearOnMouse = true }: BrushProps) {
const ref = useRef(undefined);
export function SVGBrush({ parent, value, direction = 'xy', onChange, onChangeEnd, extent, clearOnMouse = true }: BrushProps) {
const id = useId();

const callbacksRef = useRef({ onChange, onChangeEnd });
callbacksRef.current = { onChange, onChangeEnd };

useInteractions(ref, {
const { setRef } = useInteractions({
onClick: () => {
if (clearOnMouse) {
callbacksRef.current.onChange?.(null);
callbacksRef.current.onChangeEnd?.(null);
}
},
onMouseUp: () => {
callbacksRef.current.onChangeEnd?.(brush);
callbacksRef.current.onChangeEnd?.(value);
},
onDrag: (event) => {
const bounds = parent.current.getBoundingClientRect();
Expand All @@ -54,64 +53,64 @@ export function BrushRect({ parent, brush, direction = 'xy', onChange, onChangeE
y: event.clientY - bounds.top,
};

const newBrush = { ...brush };
const newBrush = { ...value };

switch (event.target.id) {
case id: {
const w = brush.x2 - brush.x1;
const h = brush.y2 - brush.y1;
const w = value.x2 - value.x1;
const h = value.y2 - value.y1;
newBrush.x1 = clamp(relativePosition.x - event.anchor.x, internalExtent.x1, internalExtent.x2 - w);
newBrush.y1 = clamp(relativePosition.y - event.anchor.y, internalExtent.y1, internalExtent.y2 - h);
newBrush.x2 = newBrush.x1 + brush.x2 - brush.x1;
newBrush.y2 = newBrush.y1 + brush.y2 - brush.y1;
newBrush.x2 = newBrush.x1 + value.x2 - value.x1;
newBrush.y2 = newBrush.y1 + value.y2 - value.y1;
break;
}
case `${id}-west`:
newBrush.x1 = clamp(relativePosition.x, internalExtent.x1, brush.x2 - MIN_BRUSH_SIZE);
newBrush.x1 = clamp(relativePosition.x, internalExtent.x1, value.x2 - MIN_BRUSH_SIZE);
break;
case `${id}-ost`:
newBrush.x2 = clamp(relativePosition.x, brush.x1 + MIN_BRUSH_SIZE, internalExtent.x2);
newBrush.x2 = clamp(relativePosition.x, value.x1 + MIN_BRUSH_SIZE, internalExtent.x2);
break;
case `${id}-north`:
newBrush.y1 = clamp(relativePosition.y, internalExtent.y1, brush.y2 - MIN_BRUSH_SIZE);
newBrush.y1 = clamp(relativePosition.y, internalExtent.y1, value.y2 - MIN_BRUSH_SIZE);
break;
case `${id}-south`:
newBrush.y2 = clamp(relativePosition.y, brush.y1 + MIN_BRUSH_SIZE, extent.y2);
newBrush.y2 = clamp(relativePosition.y, value.y1 + MIN_BRUSH_SIZE, internalExtent.y2);
break;
case `${id}-northwest`:
newBrush.x1 = clamp(relativePosition.x, internalExtent.x1, brush.x2 - MIN_BRUSH_SIZE);
newBrush.y1 = clamp(relativePosition.y, internalExtent.y1, brush.y2 - MIN_BRUSH_SIZE);
newBrush.x1 = clamp(relativePosition.x, internalExtent.x1, value.x2 - MIN_BRUSH_SIZE);
newBrush.y1 = clamp(relativePosition.y, internalExtent.y1, value.y2 - MIN_BRUSH_SIZE);
break;
case `${id}-northeast`:
newBrush.x2 = clamp(relativePosition.x, brush.x1 + MIN_BRUSH_SIZE, extent.x2);
newBrush.y1 = clamp(relativePosition.y, internalExtent.y1, brush.y2 - MIN_BRUSH_SIZE);
newBrush.x2 = clamp(relativePosition.x, value.x1 + MIN_BRUSH_SIZE, internalExtent.x2);
newBrush.y1 = clamp(relativePosition.y, internalExtent.y1, value.y2 - MIN_BRUSH_SIZE);
break;
case `${id}-southwest`:
newBrush.x1 = clamp(relativePosition.x, internalExtent.x1, brush.x2 - MIN_BRUSH_SIZE);
newBrush.y2 = clamp(relativePosition.y, brush.y1 + MIN_BRUSH_SIZE, extent.y2);
newBrush.x1 = clamp(relativePosition.x, internalExtent.x1, value.x2 - MIN_BRUSH_SIZE);
newBrush.y2 = clamp(relativePosition.y, value.y1 + MIN_BRUSH_SIZE, internalExtent.y2);
break;
case `${id}-southeast`:
newBrush.x2 = clamp(relativePosition.x, brush.x1 + MIN_BRUSH_SIZE, extent.x2);
newBrush.y2 = clamp(relativePosition.y, brush.y1 + MIN_BRUSH_SIZE, extent.y2);
newBrush.x2 = clamp(relativePosition.x, value.x1 + MIN_BRUSH_SIZE, internalExtent.x2);
newBrush.y2 = clamp(relativePosition.y, value.y1 + MIN_BRUSH_SIZE, internalExtent.y2);
break;
default:
break;
}

if (newBrush.x1 !== brush.x1 || newBrush.x2 !== brush.x2 || newBrush.y1 !== brush.y1 || newBrush.y2 !== brush.y2) {
if (newBrush.x1 !== value.x1 || newBrush.x2 !== value.x2 || newBrush.y1 !== value.y1 || newBrush.y2 !== value.y2) {
callbacksRef.current.onChange?.(newBrush);
}
},
});

return (
<g ref={ref}>
<g ref={setRef}>
<rect
id={id}
x={brush.x1 + BORDER_CORRECTION}
y={brush.y1 + BORDER_CORRECTION}
width={brush.x2 - brush.x1 - BORDER_CORRECTION * 2}
height={brush.y2 - brush.y1 - BORDER_CORRECTION * 2}
x={value.x1 + BORDER_CORRECTION}
y={value.y1 + BORDER_CORRECTION}
width={value.x2 - value.x1 - BORDER_CORRECTION * 2}
height={value.y2 - value.y1 - BORDER_CORRECTION * 2}
stroke="#24292e"
fill="#24292e"
fillOpacity={0.1}
Expand All @@ -123,18 +122,18 @@ export function BrushRect({ parent, brush, direction = 'xy', onChange, onChangeE
<>
<rect
id={`${id}-south`}
x={brush.x1}
y={brush.y2 - BORDER_WIDTH / 2}
width={brush.x2 - brush.x1}
x={value.x1}
y={value.y2 - BORDER_WIDTH / 2}
width={value.x2 - value.x1}
height={BORDER_WIDTH}
fill={EDGE_COLOR}
cursor="ns-resize"
/>
<rect
id={`${id}-north`}
x={brush.x1}
y={brush.y1 - BORDER_WIDTH / 2}
width={brush.x2 - brush.x1}
x={value.x1}
y={value.y1 - BORDER_WIDTH / 2}
width={value.x2 - value.x1}
height={BORDER_WIDTH}
fill={EDGE_COLOR}
cursor="ns-resize"
Expand All @@ -146,19 +145,19 @@ export function BrushRect({ parent, brush, direction = 'xy', onChange, onChangeE
<>
<rect
id={`${id}-west`}
x={brush.x1 - BORDER_WIDTH / 2}
y={brush.y1}
x={value.x1 - BORDER_WIDTH / 2}
y={value.y1}
width={BORDER_WIDTH}
height={brush.y2 - brush.y1}
height={value.y2 - value.y1}
fill={EDGE_COLOR}
cursor="ew-resize"
/>
<rect
id={`${id}-ost`}
x={brush.x2 - BORDER_WIDTH / 2}
y={brush.y1}
x={value.x2 - BORDER_WIDTH / 2}
y={value.y1}
width={BORDER_WIDTH}
height={brush.y2 - brush.y1}
height={value.y2 - value.y1}
fill={EDGE_COLOR}
cursor="ew-resize"
/>
Expand All @@ -169,35 +168,35 @@ export function BrushRect({ parent, brush, direction = 'xy', onChange, onChangeE
<>
<rect
id={`${id}-northwest`}
x={brush.x1 - BORDER_WIDTH / 2}
y={brush.y1 - BORDER_WIDTH / 2}
x={value.x1 - BORDER_WIDTH / 2}
y={value.y1 - BORDER_WIDTH / 2}
width={BORDER_WIDTH}
height={BORDER_WIDTH}
cursor="nwse-resize"
fill={EDGE_COLOR}
/>
<rect
id={`${id}-northeast`}
x={brush.x2 - BORDER_WIDTH / 2}
y={brush.y1 - BORDER_WIDTH / 2}
x={value.x2 - BORDER_WIDTH / 2}
y={value.y1 - BORDER_WIDTH / 2}
width={BORDER_WIDTH}
height={BORDER_WIDTH}
cursor="nesw-resize"
fill={EDGE_COLOR}
/>
<rect
id={`${id}-southwest`}
x={brush.x1 - BORDER_WIDTH / 2}
y={brush.y2 - BORDER_WIDTH / 2}
x={value.x1 - BORDER_WIDTH / 2}
y={value.y2 - BORDER_WIDTH / 2}
width={BORDER_WIDTH}
height={BORDER_WIDTH}
cursor="nesw-resize"
fill={EDGE_COLOR}
/>
<rect
id={`${id}-southeast`}
x={brush.x2 - BORDER_WIDTH / 2}
y={brush.y2 - BORDER_WIDTH / 2}
x={value.x2 - BORDER_WIDTH / 2}
y={value.y2 - BORDER_WIDTH / 2}
width={BORDER_WIDTH}
height={BORDER_WIDTH}
cursor="nwse-resize"
Expand Down
20 changes: 20 additions & 0 deletions src/vis/vishooks/components/SVGLasso.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import { LassoValue, lassoToSvgPath } from '../hooks';

export function SVGLasso({
value,
strokeWidth,
fill,
stroke,
strokeDashArray,
}: {
value: LassoValue;
strokeWidth?: React.SVGAttributes<SVGPathElement>['strokeWidth'];
fill?: React.SVGAttributes<SVGPathElement>['fill'];
stroke?: React.SVGAttributes<SVGPathElement>['stroke'];
strokeDashArray?: React.SVGAttributes<SVGPathElement>['strokeDasharray'];
}) {
return (
<path d={lassoToSvgPath(value)} fill={fill ?? 'none'} stroke={stroke ?? 'black'} strokeDasharray={strokeDashArray ?? '4'} strokeWidth={strokeWidth ?? 1} />
);
}
4 changes: 2 additions & 2 deletions src/vis/vishooks/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './Brush';
export * from './Brushable';
export * from './SVGBrush';
export * from './SVGLasso';
Loading

0 comments on commit 1d7c2ba

Please sign in to comment.