import React from "react";
import defaults from "lodash/defaults";
import isEmpty from "lodash/isEmpty";
import {
CategoryPropType,
DomainPropType,
EventPropTypeInterface,
StringOrNumberOrCallback,
VictoryCommonProps,
VictoryMultiLabelableProps,
VictoryStyleInterface,
Helpers,
Hooks,
UserProps,
VictoryComponentConfiguration,
VictoryContainer,
VictoryTheme,
Wrapper,
} from "victory-core";
import { VictorySharedEvents } from "victory-shared-events";
import { getChildren, useMemoizedProps } from "./helper-methods";
import isEqual from "react-fast-compare";
const fallbackProps = {
width: 450,
height: 300,
padding: 50,
};
export type VictoryStackTTargetType = "data" | "labels" | "parent";
export interface VictoryStackProps
extends VictoryCommonProps,
VictoryMultiLabelableProps {
bins?: number | number[] | Date[];
categories?: CategoryPropType;
children?: React.ReactNode | React.ReactNode[];
domain?: DomainPropType;
events?: EventPropTypeInterface<
VictoryStackTTargetType,
StringOrNumberOrCallback
>[];
eventKey?: StringOrNumberOrCallback;
fillInMissingData?: boolean;
style?: VictoryStyleInterface;
xOffset?: number;
}
const defaultProps = {
containerComponent: ,
groupComponent: ,
standalone: true,
theme: VictoryTheme.grayscale,
fillInMissingData: true,
};
const VictoryStackBase = (initialProps: VictoryStackProps) => {
const { role } = VictoryStack;
const propsWithDefaults = React.useMemo(
() => defaults({}, initialProps, defaultProps),
[initialProps],
);
const { setAnimationState, getAnimationProps, getProps } =
Hooks.useAnimationState();
const props = getProps(propsWithDefaults);
const modifiedProps = Helpers.modifyProps(props, fallbackProps, role);
const {
eventKey,
containerComponent,
standalone,
groupComponent,
externalEventMutations,
width,
height,
theme,
polar,
horizontal,
name,
} = modifiedProps;
const childComponents = React.Children.toArray(modifiedProps.children);
const calculatedProps = useMemoizedProps(modifiedProps);
const { domain, scale, style } = calculatedProps;
const newChildren = React.useMemo(() => {
const children = getChildren(props, childComponents, calculatedProps);
const orderedChildren = children.map((child, index) => {
const childProps = Object.assign(
{ animate: getAnimationProps(props, child, index) },
child.props,
);
return React.cloneElement(child, childProps);
});
/*
reverse render order for children of `VictoryStack` so that higher children in the stack
are rendered behind lower children. This looks nicer for stacked bars with cornerRadius, and
areas with strokes
*/
return orderedChildren.reverse();
}, [props, childComponents, calculatedProps, getAnimationProps]);
const containerProps = React.useMemo(() => {
if (standalone) {
return {
domain,
scale,
width,
height,
standalone,
theme,
style: style.parent,
horizontal,
polar,
name,
};
}
return {};
}, [
standalone,
domain,
scale,
width,
height,
theme,
style,
horizontal,
polar,
name,
]);
const userProps = React.useMemo(
() => UserProps.getSafeUserProps(propsWithDefaults),
[propsWithDefaults],
);
const container = React.useMemo(() => {
if (standalone) {
const defaultContainerProps = defaults(
{},
containerComponent.props,
containerProps,
userProps,
);
return React.cloneElement(containerComponent, defaultContainerProps);
}
return React.cloneElement(groupComponent, userProps);
}, [
groupComponent,
standalone,
containerComponent,
containerProps,
userProps,
]);
const events = React.useMemo(() => {
return Wrapper.getAllEvents(props);
}, [props]);
const previousProps = Hooks.usePreviousProps(propsWithDefaults);
React.useEffect(() => {
// This is called before dismount to keep state in sync
return () => {
if (propsWithDefaults.animate) {
setAnimationState(previousProps, propsWithDefaults);
}
};
}, [setAnimationState, previousProps, propsWithDefaults]);
if (!isEmpty(events)) {
return (
{newChildren}
);
}
return React.cloneElement(container, container.props, newChildren);
};
const componentConfig: VictoryComponentConfiguration = {
role: "stack",
expectedComponents: [
"groupComponent",
"containerComponent",
"labelComponent",
],
getChildren,
};
export const VictoryStack = Object.assign(
React.memo(VictoryStackBase, isEqual),
componentConfig,
);
VictoryStack.displayName = "VictoryStack";