victory-core
Version:
314 lines (302 loc) • 14.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addEvents = addEvents;
var _react = _interopRequireDefault(require("react"));
var _defaults = _interopRequireDefault(require("lodash/defaults"));
var _isEmpty = _interopRequireDefault(require("lodash/isEmpty"));
var _pick = _interopRequireDefault(require("lodash/pick"));
var _reactFastCompare = _interopRequireDefault(require("react-fast-compare"));
var _victoryTransition = require("../victory-transition/victory-transition");
var _collection = require("./collection");
var Events = _interopRequireWildcard(require("./events"));
var _helpers = require("./helpers");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// DISCLAIMER:
// This file is not currently tested, and it is first on the list of files
// to refactor in our current refactoring effort. Please do not make changes
// to this file without manual testing and/or refactoring and adding tests.
const datumHasXandY = datum => {
return !(0, _helpers.isNil)(datum._x) && !(0, _helpers.isNil)(datum._y);
};
// used for checking state changes. Expected components can be passed in via options
const defaultComponents = [{
name: "parent",
index: "parent"
}, {
name: "data"
}, {
name: "labels"
}];
/**
* These methods will be implemented by the Mixin,
* and are accessible to the Wrapped Component.
*
* To make your Wrapped Component type-safe, use "interface merging" like so:
* @example
* interface MyComponent extends EventsMixinClass<MyProps> {}
* class MyComponent extends React.Component<MyProps> { ... }
*/
/**
* These fields are calculated by the Mixin
*/
/**
* These are the common roles that we care about internally.
*/
/**
* A component can have any "role",
* but there are certain ones that we actually care about internally
*/
/**
* Static component fields used by Victory for common behavior
*/
/**
* This represents the class itself, including static fields
*/
function addEvents(WrappedComponent, options) {
if (options === void 0) {
options = {};
} // eslint-disable-next-line @typescript-eslint/no-empty-object-type
// @ts-expect-error "TS2545: A mixin class must have a constructor with a single rest parameter of type 'any[]'."
class AddEventsMixin extends WrappedComponent {
constructor(props) {
super(props);
this.cacheValues(this.getCalculatedValues(props));
}
state = {};
getEventState = Events.getEventState.bind(this);
getScopedEvents = Events.getScopedEvents.bind(this);
getEvents = (p, target, eventKey) => {
return Events.getEvents.call(this, p, target, eventKey, this.getScopedEvents);
};
externalMutations = this.getExternalMutations(this.props);
calculatedState = this.getStateChanges(this.props);
globalEvents = {};
prevGlobalEventKeys = [];
boundGlobalEvents = {};
shouldComponentUpdate(nextProps) {
const externalMutations = this.getExternalMutations(nextProps);
// @ts-expect-error "Property 'animating' does not exist on type EventMixinCommonProps"
const animating = this.props.animating || this.props.animate;
const newMutation = !(0, _reactFastCompare.default)(externalMutations, this.externalMutations);
if (animating || newMutation) {
this.cacheValues(this.getCalculatedValues(nextProps));
this.externalMutations = externalMutations;
this.applyExternalMutations(nextProps, externalMutations);
return true;
}
const calculatedState = this.getStateChanges(nextProps);
if (!(0, _reactFastCompare.default)(this.calculatedState, calculatedState)) {
this.cacheValues(this.getCalculatedValues(nextProps));
return true;
}
if (!(0, _reactFastCompare.default)(this.props, nextProps)) {
this.cacheValues(this.getCalculatedValues(nextProps));
return true;
}
return false;
}
componentDidMount() {
const globalEventKeys = Object.keys(this.globalEvents);
globalEventKeys.forEach(key => this.addGlobalListener(key));
this.prevGlobalEventKeys = globalEventKeys;
}
componentDidUpdate(prevProps) {
const calculatedState = this.getStateChanges(prevProps);
this.calculatedState = calculatedState;
const globalEventKeys = Object.keys(this.globalEvents);
const removedGlobalEventKeys = (0, _collection.difference)(this.prevGlobalEventKeys, globalEventKeys);
removedGlobalEventKeys.forEach(key => this.removeGlobalListener(key));
const addedGlobalEventKeys = (0, _collection.difference)(globalEventKeys, this.prevGlobalEventKeys);
addedGlobalEventKeys.forEach(key => this.addGlobalListener(key));
this.prevGlobalEventKeys = globalEventKeys;
}
componentWillUnmount() {
this.prevGlobalEventKeys.forEach(key => this.removeGlobalListener(key));
}
addGlobalListener(key) {
const boundListener = event => {
const listener = this.globalEvents[key];
return listener && listener(Events.emulateReactEvent(event));
};
this.boundGlobalEvents[key] = boundListener;
window.addEventListener(Events.getGlobalEventNameFromKey(key), boundListener);
}
removeGlobalListener(key) {
window.removeEventListener(Events.getGlobalEventNameFromKey(key), this.boundGlobalEvents[key]);
}
// compile all state changes from own and parent state. Order doesn't matter, as any state
// state change should trigger a re-render
getStateChanges(props) {
if (!this.hasEvents) {
return {};
}
const getState = (key, type) => {
const result = (0, _defaults.default)({}, this.getEventState(key, type), this.getSharedEventState(key, type));
return (0, _isEmpty.default)(result) ? undefined : result;
};
const components = options.components || defaultComponents;
const stateChanges = components.map(component => {
if (!props.standalone && component.name === "parent") {
// don't check for changes on parent props for non-standalone components
return undefined;
}
return component.index !== undefined ? getState(component.index, component.name) : this.dataKeys.map(key => getState(key, component.name)).filter(Boolean);
}).filter(Boolean);
return stateChanges;
}
applyExternalMutations(props, externalMutations) {
if (!(0, _isEmpty.default)(externalMutations)) {
const callbacks = props.externalEventMutations.reduce((memo, mutation) => (0, _helpers.isFunction)(mutation.callback) ? memo.concat(mutation.callback) : memo, []);
const compiledCallbacks = callbacks.length ? () => {
callbacks.forEach(c => c());
} : undefined;
this.setState(externalMutations, compiledCallbacks);
}
}
getCalculatedValues(props) {
const {
sharedEvents
} = props;
const components = WrappedComponent.expectedComponents;
const componentEvents = Events.getComponentEvents(props, components);
const getSharedEventState = sharedEvents && (0, _helpers.isFunction)(sharedEvents.getEventState) ? sharedEvents.getEventState : () => undefined;
const baseProps = this.getBaseProps(props, getSharedEventState);
const dataKeys = Object.keys(baseProps).filter(key => key !== "parent");
const hasEvents = props.events || props.sharedEvents || componentEvents;
const events = this.getAllEvents(props);
return {
componentEvents,
getSharedEventState,
baseProps,
dataKeys,
hasEvents,
events
};
}
getExternalMutations(props) {
const {
sharedEvents,
externalEventMutations
} = props;
return (0, _isEmpty.default)(externalEventMutations) || sharedEvents ? undefined : Events.getExternalMutations(externalEventMutations, this.baseProps, this.state);
}
cacheValues(obj) {
Object.keys(obj).forEach(key => {
this[key] = obj[key];
});
}
getBaseProps(props, getSharedEventState) {
const getSharedEventStateFunction = getSharedEventState || this.getSharedEventState.bind(this);
const sharedParentState = getSharedEventStateFunction("parent", "parent");
const parentState = this.getEventState("parent", "parent");
const baseParentProps = (0, _defaults.default)({}, parentState, sharedParentState);
const parentPropsList = baseParentProps.parentControlledProps;
const parentProps = parentPropsList ? (0, _pick.default)(baseParentProps, parentPropsList) : {};
const modifiedProps = (0, _defaults.default)({}, parentProps, props);
return typeof WrappedComponent.getBaseProps === "function" ? WrappedComponent.getBaseProps(modifiedProps) : {};
}
getAllEvents(props) {
if (Array.isArray(this.componentEvents)) {
return Array.isArray(props.events) ? this.componentEvents.concat(...props.events) : this.componentEvents;
}
return props.events;
}
getComponentProps(component, type, index) {
const name = this.props.name || WrappedComponent.role;
const key = this.dataKeys && this.dataKeys[index] || index;
const id = `${name}-${type}-${key}`;
const baseProps = this.baseProps[key] && this.baseProps[key][type] || this.baseProps[key];
if (!baseProps && !this.hasEvents) {
return undefined;
}
const currentProps = component && typeof component === "object" && "props" in component ? component.props : undefined;
if (this.hasEvents) {
const baseEvents = this.getEvents(this.props, type, key);
const componentProps = (0, _defaults.default)({
index,
key: id
}, this.getEventState(key, type), this.getSharedEventState(key, type), currentProps, baseProps, {
id
});
const events = (0, _defaults.default)({}, Events.getPartialEvents(baseEvents, key, componentProps), componentProps.events);
return Object.assign({}, componentProps, {
events
});
}
return (0, _defaults.default)({
index,
key: id
}, currentProps, baseProps, {
id
});
}
renderContainer(component, children) {
const isContainer = component.type && component.type.role === "container";
const parentProps = isContainer ? this.getComponentProps(component, "parent", "parent") : {};
if (parentProps.events) {
this.globalEvents = Events.getGlobalEvents(parentProps.events);
parentProps.events = Events.omitGlobalEvents(parentProps.events);
}
return /*#__PURE__*/_react.default.cloneElement(component, parentProps, children);
}
animateComponent(props, defaultAnimationWhitelist) {
const animationWhitelist = typeof props.animate === "object" && props.animate?.animationWhitelist || defaultAnimationWhitelist;
const Comp = this.constructor;
return /*#__PURE__*/_react.default.createElement(_victoryTransition.VictoryTransition, {
animate: props.animate,
animationWhitelist: animationWhitelist
}, /*#__PURE__*/_react.default.createElement(Comp, props));
}
// Used by `VictoryLine` and `VictoryArea`
renderContinuousData(props) {
const {
dataComponent,
labelComponent,
groupComponent
} = props;
const dataKeys = this.dataKeys.filter(value => value !== "all");
const labelComponents = dataKeys.reduce((memo, key) => {
let newMemo = memo;
const labelProps = this.getComponentProps(labelComponent, "labels", key);
if (labelProps && labelProps.text !== undefined && labelProps.text !== null) {
newMemo = newMemo.concat( /*#__PURE__*/_react.default.cloneElement(labelComponent, labelProps));
}
return newMemo;
}, []);
const dataProps = this.getComponentProps(dataComponent, "data", "all");
const children = [/*#__PURE__*/_react.default.cloneElement(dataComponent, dataProps), ...labelComponents];
return this.renderContainer(groupComponent, children);
}
renderData(props, shouldRenderDatum) {
if (shouldRenderDatum === void 0) {
shouldRenderDatum = datumHasXandY;
}
const {
dataComponent,
labelComponent,
groupComponent
} = props;
const dataComponents = this.dataKeys.reduce((validDataComponents, _dataKey, index) => {
const dataProps = this.getComponentProps(dataComponent, "data", index);
if (shouldRenderDatum(dataProps.datum)) {
validDataComponents.push( /*#__PURE__*/_react.default.cloneElement(dataComponent, dataProps));
}
return validDataComponents;
}, []);
const labelComponents = this.dataKeys.map((_dataKey, index) => {
const labelProps = this.getComponentProps(labelComponent, "labels", index);
if (labelProps.text !== undefined && labelProps.text !== null) {
return /*#__PURE__*/_react.default.cloneElement(labelComponent, labelProps);
}
return undefined;
}).filter(Boolean);
const children = [...dataComponents, ...labelComponents];
return this.renderContainer(groupComponent, children);
}
}
return AddEventsMixin;
}