Skip to content

Commit

Permalink
Containers and portal refactor (#2799)
Browse files Browse the repository at this point in the history
  • Loading branch information
KenanYusuf authored May 29, 2024
1 parent be3d76b commit 23c3250
Show file tree
Hide file tree
Showing 33 changed files with 1,871 additions and 1,872 deletions.
14 changes: 14 additions & 0 deletions .changeset/giant-carrots-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"victory": minor
"victory-brush-container": minor
"victory-core": minor
"victory-create-container": minor
"victory-cursor-container": minor
"victory-native": minor
"victory-selection-container": minor
"victory-tooltip": minor
"victory-voronoi-container": minor
"victory-zoom-container": minor
---

Refactor containers and portal to function components
347 changes: 175 additions & 172 deletions packages/victory-brush-container/src/victory-brush-container.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from "react";
import {
VictoryContainer,
Selection,
Rect,
DomainTuple,
VictoryContainerProps,
VictoryContainer,
VictoryEventHandler,
} from "victory-core";
import { BrushHelpers } from "./brush-helpers";
import { defaults } from "lodash";
Expand Down Expand Up @@ -37,186 +38,188 @@ export interface VictoryBrushContainerProps extends VictoryContainerProps {
) => void;
}

type ComponentClass<TProps> = { new (props: TProps): React.Component<TProps> };
interface VictoryBrushContainerMutatedProps extends VictoryBrushContainerProps {
domain: { x: DomainTuple; y: DomainTuple };
currentDomain: { x: DomainTuple; y: DomainTuple } | undefined;
cachedBrushDomain: { x: DomainTuple; y: DomainTuple } | undefined;
}

export function brushContainerMixin<
TBase extends ComponentClass<TProps>,
TProps extends VictoryBrushContainerProps,
>(Base: TBase) {
// @ts-expect-error "TS2545: A mixin class must have a constructor with a single rest parameter of type 'any[]'."
return class VictoryBrushContainer extends Base {
static displayName = "VictoryBrushContainer";
static defaultProps = {
...VictoryContainer.defaultProps,
allowDrag: true,
allowDraw: true,
allowResize: true,
brushComponent: <Rect />,
brushStyle: {
stroke: "transparent",
fill: "black",
fillOpacity: 0.1,
},
handleComponent: <Rect />,
handleStyle: {
stroke: "transparent",
fill: "transparent",
},
handleWidth: 8,
mouseMoveThreshold: 0,
};
export const VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS = {
allowDrag: true,
allowDraw: true,
allowResize: true,
brushComponent: <Rect />,
brushStyle: {
stroke: "transparent",
fill: "black",
fillOpacity: 0.1,
},
handleComponent: <Rect />,
handleStyle: {
stroke: "transparent",
fill: "transparent",
},
handleWidth: 8,
mouseMoveThreshold: 0,
};

static defaultEvents(props) {
return [
{
target: "parent",
eventHandlers: {
onMouseDown: (evt, targetProps) => {
return props.disable
? {}
: BrushHelpers.onMouseDown(evt, targetProps);
},
onTouchStart: (evt, targetProps) => {
return props.disable
? {}
: BrushHelpers.onMouseDown(evt, targetProps);
},
onGlobalMouseMove: (evt, targetProps) => {
return props.disable ||
(!targetProps.isPanning && !targetProps.isSelecting)
? {}
: BrushHelpers.onGlobalMouseMove(evt, targetProps);
},
onGlobalTouchMove: (evt, targetProps) => {
return props.disable ||
(!targetProps.isPanning && !targetProps.isSelecting)
? {}
: BrushHelpers.onGlobalMouseMove(evt, targetProps);
},
onGlobalMouseUp: (evt, targetProps) => {
return props.disable
? {}
: BrushHelpers.onGlobalMouseUp(evt, targetProps);
},
onGlobalTouchEnd: (evt, targetProps) => {
return props.disable
? {}
: BrushHelpers.onGlobalMouseUp(evt, targetProps);
},
onGlobalTouchCancel: (evt, targetProps) => {
return props.disable
? {}
: BrushHelpers.onGlobalMouseUp(evt, targetProps);
},
},
},
];
}
export const useVictoryBrushContainer = (
initialProps: VictoryBrushContainerProps,
) => {
const props = {
...VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS,
...(initialProps as VictoryBrushContainerMutatedProps),
};
const { children } = props;

getSelectBox(props, coordinates) {
const { x, y } = coordinates;
const { brushStyle, brushComponent, name } = props;
const brushComponentStyle =
brushComponent.props && brushComponent.props.style;
const cursor = !props.allowDrag && !props.allowResize ? "auto" : "move";
return x[0] !== x[1] && y[0] !== y[1]
? React.cloneElement(brushComponent, {
key: `${name}-brush`,
width: Math.abs(x[1] - x[0]) || 1,
height: Math.abs(y[1] - y[0]) || 1,
x: Math.min(x[0], x[1]),
y: Math.min(y[0], y[1]),
cursor,
style: defaults({}, brushComponentStyle, brushStyle),
})
: null;
}
const getSelectBox = (coordinates) => {
const { x, y } = coordinates;
const { brushStyle, brushComponent, name } = props;
const brushComponentStyle =
brushComponent.props && brushComponent.props.style;
const cursor = !props.allowDrag && !props.allowResize ? "auto" : "move";
return x[0] !== x[1] && y[0] !== y[1]
? React.cloneElement(brushComponent, {
key: `${name}-brush`,
width: Math.abs(x[1] - x[0]) || 1,
height: Math.abs(y[1] - y[0]) || 1,
x: Math.min(x[0], x[1]),
y: Math.min(y[0], y[1]),
cursor,
style: defaults({}, brushComponentStyle, brushStyle),
})
: null;
};

getCursorPointers(props) {
const cursors = {
yProps: "ns-resize",
xProps: "ew-resize",
};
if (!props.allowResize && props.allowDrag) {
cursors.xProps = "move";
cursors.yProps = "move";
} else if (!props.allowResize && !props.allowDrag) {
cursors.xProps = "auto";
cursors.yProps = "auto";
}
return cursors;
const getCursorPointers = () => {
const cursors = {
yProps: "ns-resize",
xProps: "ew-resize",
};
if (!props.allowResize && props.allowDrag) {
cursors.xProps = "move";
cursors.yProps = "move";
} else if (!props.allowResize && !props.allowDrag) {
cursors.xProps = "auto";
cursors.yProps = "auto";
}
return cursors;
};

getHandles(props, domain) {
const { handleWidth, handleStyle, handleComponent, name } = props;
const domainBox = BrushHelpers.getDomainBox(props, domain);
const { x1, x2, y1, y2 } = domainBox;
const { top, bottom, left, right } = BrushHelpers.getHandles(
props,
domainBox,
);
const width = Math.abs(x2 - x1) || 1;
const height = Math.abs(y2 - y1) || 1;
const handleComponentStyle =
(handleComponent.props && handleComponent.props.style) || {};
const style = defaults({}, handleComponentStyle, handleStyle);
const getHandles = (domain) => {
const { handleWidth, handleStyle, handleComponent, name } = props;
const domainBox = BrushHelpers.getDomainBox(props, domain);
const { x1, x2, y1, y2 } = domainBox;
const { top, bottom, left, right } = BrushHelpers.getHandles(
props,
domainBox,
);
const width = Math.abs(x2 - x1) || 1;
const height = Math.abs(y2 - y1) || 1;
const handleComponentStyle =
(handleComponent.props && handleComponent.props.style) || {};
const style = defaults({}, handleComponentStyle, handleStyle);

const cursors = this.getCursorPointers(props);
const yProps = {
style,
width,
height: handleWidth,
cursor: cursors.yProps,
};
const xProps = {
style,
width: handleWidth,
height,
cursor: cursors.xProps,
};
const cursors = getCursorPointers();
const yProps = {
style,
width,
height: handleWidth,
cursor: cursors.yProps,
};
const xProps = {
style,
width: handleWidth,
height,
cursor: cursors.xProps,
};

const handleProps = {
top: top && Object.assign({ x: top.x1, y: top.y1 }, yProps),
bottom: bottom && Object.assign({ x: bottom.x1, y: bottom.y1 }, yProps),
left: left && Object.assign({ y: left.y1, x: left.x1 }, xProps),
right: right && Object.assign({ y: right.y1, x: right.x1 }, xProps),
};
const handles = ["top", "bottom", "left", "right"].reduce(
(memo, curr) =>
handleProps[curr]
? memo.concat(
React.cloneElement(
handleComponent,
Object.assign(
{ key: `${name}-handle-${curr}` },
handleProps[curr],
),
const handleProps = {
top: top && Object.assign({ x: top.x1, y: top.y1 }, yProps),
bottom: bottom && Object.assign({ x: bottom.x1, y: bottom.y1 }, yProps),
left: left && Object.assign({ y: left.y1, x: left.x1 }, xProps),
right: right && Object.assign({ y: right.y1, x: right.x1 }, xProps),
};
const handles = ["top", "bottom", "left", "right"].reduce(
(memo, curr) =>
handleProps[curr]
? memo.concat(
React.cloneElement(
handleComponent,
Object.assign(
{ key: `${name}-handle-${curr}` },
handleProps[curr],
),
)
: memo,
[] as React.ReactElement[],
);
return handles.length ? handles : null;
}
),
)
: memo,
[] as React.ReactElement[],
);
return handles.length ? handles : null;
};

getRect(props) {
const { currentDomain, cachedBrushDomain } = props;
const brushDomain = defaults({}, props.brushDomain, props.domain);
const domain = isEqual(brushDomain, cachedBrushDomain)
? defaults({}, currentDomain, brushDomain)
: brushDomain;
const coordinates = Selection.getDomainCoordinates(props, domain);
const selectBox = this.getSelectBox(props, coordinates);
return selectBox ? [selectBox, this.getHandles(props, domain)] : [];
}
const getRect = () => {
const { currentDomain, cachedBrushDomain } = props;
const brushDomain = defaults({}, props.brushDomain, props.domain);
const domain = isEqual(brushDomain, cachedBrushDomain)
? defaults({}, currentDomain, brushDomain)
: brushDomain;
const coordinates = Selection.getDomainCoordinates(props, domain);
const selectBox = getSelectBox(coordinates);
return selectBox ? [selectBox, getHandles(domain)] : [];
};

// Overrides method in VictoryContainer
getChildren(props) {
return [
...React.Children.toArray(props.children),
...this.getRect(props),
];
}
return {
props,
children: [
...React.Children.toArray(children),
...getRect(),
] as React.ReactElement[],
};
}
export const VictoryBrushContainer = brushContainerMixin(VictoryContainer);
};

export const VictoryBrushContainer = (
initialProps: VictoryBrushContainerProps,
) => {
const { props, children } = useVictoryBrushContainer(initialProps);
return <VictoryContainer {...props}>{children}</VictoryContainer>;
};

VictoryBrushContainer.role = "container";

VictoryBrushContainer.defaultEvents = (
initialProps: VictoryBrushContainerProps,
) => {
const props = { ...VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS, ...initialProps };
const createEventHandler =
(
handler: VictoryEventHandler,
isDisabled?: (targetProps: any) => boolean,
): VictoryEventHandler =>
// eslint-disable-next-line max-params
(event, targetProps, eventKey, context) =>
props.disable || isDisabled?.(targetProps)
? {}
: handler(event, { ...props, ...targetProps }, eventKey, context);

return [
{
target: "parent",
eventHandlers: {
onMouseDown: createEventHandler(BrushHelpers.onMouseDown),
onTouchStart: createEventHandler(BrushHelpers.onMouseDown),
onGlobalMouseMove: createEventHandler(
BrushHelpers.onGlobalMouseMove,
(targetProps) => !targetProps.isPanning && !targetProps.isSelecting,
),
onGlobalTouchMove: createEventHandler(
BrushHelpers.onGlobalMouseMove,
(targetProps) => !targetProps.isPanning && !targetProps.isSelecting,
),
onGlobalMouseUp: createEventHandler(BrushHelpers.onGlobalMouseUp),
onGlobalTouchEnd: createEventHandler(BrushHelpers.onGlobalMouseUp),
onGlobalTouchCancel: createEventHandler(BrushHelpers.onGlobalMouseUp),
},
},
];
};
Loading

0 comments on commit 23c3250

Please sign in to comment.