UNPKG

victory-create-container

Version:
110 lines (108 loc) 4.91 kB
import React from "react"; import forOwn from "lodash/forOwn"; import groupBy from "lodash/groupBy"; import isEmpty from "lodash/isEmpty"; import toPairs from "lodash/toPairs"; import { VictoryZoomContainer, useVictoryZoomContainer } from "victory-zoom-container"; import { VictorySelectionContainer, useVictorySelectionContainer } from "victory-selection-container"; import { VictoryContainer } from "victory-core"; import { VictoryVoronoiContainer, useVictoryVoronoiContainer } from "victory-voronoi-container"; import { VictoryCursorContainer, useVictoryCursorContainer } from "victory-cursor-container"; import { VictoryBrushContainer, useVictoryBrushContainer } from "victory-brush-container"; function ensureArray(thing) { if (!thing) { return []; } else if (!Array.isArray(thing)) { return [thing]; } return thing; } const combineEventHandlers = eventHandlersArray => { // takes an array of event handler objects and produces one eventHandlers object // creates a custom combinedHandler() for events with multiple conflicting handlers return eventHandlersArray.reduce((localHandlers, finalHandlers) => { forOwn(localHandlers, (localHandler, eventName) => { const existingHandler = finalHandlers[eventName]; if (existingHandler) { // create new handler for event that concats the existing handler's mutations with new ones finalHandlers[eventName] = function combinedHandler() { // named for debug clarity // sometimes handlers return undefined; use empty array instead, for concat() const existingMutations = ensureArray(existingHandler(...arguments)); const localMutations = ensureArray(localHandler(...arguments)); return existingMutations.concat(localMutations); }; } else { finalHandlers[eventName] = localHandler; } }); return finalHandlers; }); }; const combineDefaultEvents = defaultEvents => { // takes a defaultEvents array and returns one equal or lesser length, // by combining any events that have the same target const eventsByTarget = groupBy(defaultEvents, "target"); const events = toPairs(eventsByTarget).map(_ref => { let [target, eventsArray] = _ref; const newEventsArray = eventsArray.filter(Boolean); return isEmpty(newEventsArray) ? null : { target, eventHandlers: combineEventHandlers(eventsArray.map(event => event.eventHandlers)) // note: does not currently handle eventKey or childName }; }); return events.filter(Boolean); }; /** * Container hooks are used to provide the container logic to the container components through props and a modified children object * - These hooks contain shared logic for both web and Victory Native containers. * - In this utility, we call multiple of these hooks with the props returned by the previous to combine the container logic. */ const CONTAINER_HOOKS = { zoom: useVictoryZoomContainer, selection: useVictorySelectionContainer, brush: useVictoryBrushContainer, cursor: useVictoryCursorContainer, voronoi: useVictoryVoronoiContainer }; /** * Container hooks are wrappers that return a VictoryContainer with the props provided by their respective hooks, and the modified children. * - These containers are specific to the web. Victory Native has its own container components. * - For this utility, we only need the container components to extract the defaultEvents. */ const CONTAINER_COMPONENTS_WEB = { zoom: VictoryZoomContainer, selection: VictorySelectionContainer, brush: VictoryBrushContainer, cursor: VictoryCursorContainer, voronoi: VictoryVoronoiContainer }; export function makeCreateContainerFunction(containerComponents, VictoryContainerBase) { // Helper type to support backwards compatibility with old types return function combineContainers(containerA, containerB) { const ContainerA = containerComponents[containerA]; const ContainerB = containerComponents[containerB]; const useContainerA = CONTAINER_HOOKS[containerA]; const useContainerB = CONTAINER_HOOKS[containerB]; const CombinedContainer = props => { const { children: childrenA, props: propsA } = useContainerA(props); const { children: combinedChildren, props: combinedProps } = useContainerB({ ...propsA, children: childrenA }); return /*#__PURE__*/React.createElement(VictoryContainerBase, combinedProps, combinedChildren); }; CombinedContainer.displayName = `Victory${containerA}${containerB}Container`; CombinedContainer.role = "container"; CombinedContainer.defaultEvents = props => combineDefaultEvents([...ContainerA.defaultEvents(props), ...ContainerB.defaultEvents(props)]); return CombinedContainer; }; } export const createContainer = makeCreateContainerFunction(CONTAINER_COMPONENTS_WEB, VictoryContainer);