diff --git a/.eslintrc.js b/.eslintrc.js index 0a44b62c4..9aaa90951 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -59,6 +59,8 @@ module.exports = { typedefs: false } ], + "no-invalid-this": "off", + "@typescript-eslint/no-invalid-this": ["error"], "@typescript-eslint/no-unsafe-argument": "warn", "@typescript-eslint/no-unsafe-assignment": "warn", diff --git a/package-scripts.js b/package-scripts.js index 51b0a4754..7189711d6 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -82,8 +82,8 @@ module.exports = { ci: npsUtils.series.nps( "format.ci", "lint", - "typecheck", "build-package-libs", + "typecheck", "build-package-dists", "test-node", "jest", @@ -122,8 +122,8 @@ module.exports = { "build-package-libs-vendor": "lerna exec --scope victory-vendor -- yarn build", "build-package-libs": npsUtils.series.nps( - "build-package-libs-core", - "build-package-libs-vendor" + "build-package-libs-vendor", + "build-package-libs-core" ), "build-dist-dev": "webpack --bail --config ../../config/webpack/webpack.config.dev.js", diff --git a/package.json b/package.json index 8504cbd05..45a66ecb4 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@testing-library/react-native": "^9.1.0", "@types/jest": "^27.5.1", "@types/lodash": "^4.14.149", - "@types/react": "^16.9.19", + "@types/react": "^16.14.26", "@types/react-dom": "^16.9.5", "@typescript-eslint/eslint-plugin": "^5.26.0", "@typescript-eslint/parser": "^5.26.0", @@ -107,7 +107,7 @@ "nps-utils": "^1.5.0", "prettier": "^2.6.2", "prop-types": "^15.8.1", - "react": "^16.13.1", + "react": "^16.14.0", "react-dom": "^16.13.1", "react-hot-loader": "v2.0.0-alpha-4", "react-test-renderer": "^16.13.1", @@ -122,5 +122,8 @@ "webpack-dev-server": "^4.9.0", "webpack-stats-plugin": "^1.0.3" }, + "resolutions": { + "@types/react": "^16.14.26" + }, "sideEffects": false } diff --git a/packages/victory-core/src/index.ts b/packages/victory-core/src/index.ts index 50b8376dd..6e96dae8f 100644 --- a/packages/victory-core/src/index.ts +++ b/packages/victory-core/src/index.ts @@ -6,7 +6,7 @@ export { default as VictoryContainer } from "./victory-container/victory-contain export { default as VictoryLabel } from "./victory-label/victory-label"; export { default as VictoryTransition } from "./victory-transition/victory-transition"; export { default as VictoryClipContainer } from "./victory-clip-container/victory-clip-container"; -export { default as VictoryTheme } from "./victory-theme/victory-theme"; +export * from "./victory-theme/victory-theme"; export { default as VictoryPortal } from "./victory-portal/victory-portal"; export { default as Portal } from "./victory-portal/portal"; export { default as Arc } from "./victory-primitives/arc"; diff --git a/packages/victory-core/src/victory-animation/util.js b/packages/victory-core/src/victory-animation/util.ts similarity index 98% rename from packages/victory-core/src/victory-animation/util.js rename to packages/victory-core/src/victory-animation/util.ts index ff67e3626..355e234ab 100644 --- a/packages/victory-core/src/victory-animation/util.js +++ b/packages/victory-core/src/victory-animation/util.ts @@ -82,8 +82,8 @@ export const interpolateFunction = function (a, b) { if (t >= 1) { return b; } - return function () { - /* eslint-disable no-invalid-this */ + return function (this: unknown) { + /* eslint-disable no-invalid-this, prefer-rest-params */ const aval = typeof a === "function" ? a.apply(this, arguments) : a; const bval = typeof b === "function" ? b.apply(this, arguments) : b; return interpolate(aval, bval)(t); diff --git a/packages/victory-core/src/victory-animation/victory-animation.d.ts b/packages/victory-core/src/victory-animation/victory-animation.d.ts deleted file mode 100644 index 844d166df..000000000 --- a/packages/victory-core/src/victory-animation/victory-animation.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from "react"; - -/** - * Single animation object to interpolate - */ -export type AnimationStyle = { [key: string]: string | number }; -/** - * Animation styles to interpolate - */ - -export type AnimationData = AnimationStyle | AnimationStyle[]; -export type AnimationEasing = - | "back" - | "backIn" - | "backOut" - | "backInOut" - | "bounce" - | "bounceIn" - | "bounceOut" - | "bounceInOut" - | "circle" - | "circleIn" - | "circleOut" - | "circleInOut" - | "linear" - | "linearIn" - | "linearOut" - | "linearInOut" - | "cubic" - | "cubicIn" - | "cubicOut" - | "cubicInOut" - | "elastic" - | "elasticIn" - | "elasticOut" - | "elasticInOut" - | "exp" - | "expIn" - | "expOut" - | "expInOut" - | "poly" - | "polyIn" - | "polyOut" - | "polyInOut" - | "quad" - | "quadIn" - | "quadOut" - | "quadInOut" - | "sin" - | "sinIn" - | "sinOut" - | "sinInOut"; - -export interface VictoryAnimationProps { - children?: (style: AnimationStyle) => React.ReactElement; - duration?: number; - easing?: AnimationEasing; - delay?: number; - onEnd?: () => void; - data?: AnimationData; -} - -export default class VictoryAnimation extends React.Component< - VictoryAnimationProps, - any -> {} diff --git a/packages/victory-core/src/victory-animation/victory-animation.js b/packages/victory-core/src/victory-animation/victory-animation.tsx similarity index 75% rename from packages/victory-core/src/victory-animation/victory-animation.js rename to packages/victory-core/src/victory-animation/victory-animation.tsx index 571b0f643..882b4a895 100644 --- a/packages/victory-core/src/victory-animation/victory-animation.js +++ b/packages/victory-core/src/victory-animation/victory-animation.tsx @@ -5,8 +5,81 @@ import * as d3Ease from "victory-vendor/d3-ease"; import { victoryInterpolator } from "./util"; import TimerContext from "../victory-util/timer-context"; import isEqual from "react-fast-compare"; +import type Timer from "../victory-util/timer"; -export default class VictoryAnimation extends React.Component { +/** + * Single animation object to interpolate + */ +export type AnimationStyle = { [key: string]: string | number }; +/** + * Animation styles to interpolate + */ + +export type AnimationData = AnimationStyle | AnimationStyle[]; +export type AnimationEasing = + | "back" + | "backIn" + | "backOut" + | "backInOut" + | "bounce" + | "bounceIn" + | "bounceOut" + | "bounceInOut" + | "circle" + | "circleIn" + | "circleOut" + | "circleInOut" + | "linear" + | "linearIn" + | "linearOut" + | "linearInOut" + | "cubic" + | "cubicIn" + | "cubicOut" + | "cubicInOut" + | "elastic" + | "elasticIn" + | "elasticOut" + | "elasticInOut" + | "exp" + | "expIn" + | "expOut" + | "expInOut" + | "poly" + | "polyIn" + | "polyOut" + | "polyInOut" + | "quad" + | "quadIn" + | "quadOut" + | "quadInOut" + | "sin" + | "sinIn" + | "sinOut" + | "sinInOut"; + +export interface VictoryAnimationProps { + children: (style: AnimationStyle, info: AnimationInfo) => React.ReactNode; + duration?: number; + easing?: AnimationEasing; + delay?: number; + onEnd?: () => void; + data: AnimationData; +} +export interface VictoryAnimationState { + data: AnimationStyle; + animationInfo: AnimationInfo; +} +export interface AnimationInfo { + progress: number; + animating: boolean; + terminating?: boolean; +} + +export default class VictoryAnimation extends React.Component< + VictoryAnimationProps, + VictoryAnimationState +> { static displayName = "VictoryAnimation"; static propTypes = { @@ -67,6 +140,11 @@ export default class VictoryAnimation extends React.Component { }; static contextType = TimerContext; + private interpolator: null | ((value: number) => AnimationStyle); + private queue: AnimationStyle[]; + private ease: any; + private timer: Timer; + private loopID?: number; constructor(props, context) { super(props, context); @@ -84,11 +162,6 @@ export default class VictoryAnimation extends React.Component { this.queue = Array.isArray(this.props.data) ? this.props.data.slice(1) : []; /* build easing function */ this.ease = d3Ease[this.toNewName(this.props.easing)]; - /* - There is no autobinding of this in ES6 classes - so we bind functionToBeRunEachFrame to current instance of victory animation class - */ - this.functionToBeRunEachFrame = this.functionToBeRunEachFrame.bind(this); this.timer = this.context.animationTimer; } @@ -163,13 +236,13 @@ export default class VictoryAnimation extends React.Component { setTimeout(() => { this.loopID = this.timer.subscribe( this.functionToBeRunEachFrame, - this.props.duration + this.props.duration! ); }, this.props.delay); } else { this.loopID = this.timer.subscribe( this.functionToBeRunEachFrame, - this.props.duration + this.props.duration! ); } } else if (this.props.onEnd) { @@ -177,7 +250,7 @@ export default class VictoryAnimation extends React.Component { } } /* every frame we... */ - functionToBeRunEachFrame(elapsed, duration) { + functionToBeRunEachFrame = (elapsed, duration) => { /* step can generate imprecise values, sometimes greater than 1 if this happens set the state to 1 and return, cancelling the timer @@ -186,7 +259,7 @@ export default class VictoryAnimation extends React.Component { const step = duration ? elapsed / duration : 1; if (step >= 1) { this.setState({ - data: this.interpolator(1), + data: this.interpolator!(1), animationInfo: { progress: 1, animating: false, @@ -206,13 +279,13 @@ export default class VictoryAnimation extends React.Component { interpolator, which is cached for performance whenever props are received */ this.setState({ - data: this.interpolator(this.ease(step)), + data: this.interpolator!(this.ease(step)), animationInfo: { progress: step, animating: step < 1 } }); - } + }; render() { return this.props.children(this.state.data, this.state.animationInfo); diff --git a/packages/victory-core/src/victory-label/victory-label.tsx b/packages/victory-core/src/victory-label/victory-label.tsx index f2d13c19c..735d7bb8c 100644 --- a/packages/victory-core/src/victory-label/victory-label.tsx +++ b/packages/victory-core/src/victory-label/victory-label.tsx @@ -591,7 +591,7 @@ const VictoryLabel: { const currentStyle = getSingleValue(style!, i); const capHeightPx = TextSize.convertLengthToPixels( `${capHeight}em`, - currentStyle.fontSize + currentStyle.fontSize as number ); const currentLineHeight = getSingleValue(lineHeight, i); return { @@ -599,6 +599,7 @@ const VictoryLabel: { fontSize: currentStyle.fontSize || defaultStyles.fontSize, capHeight: capHeightPx, text: line, + // @ts-expect-error TODO: This looks like a bug: textSize: TextSize.approximateTextSize(line, currentStyle), lineHeight: currentLineHeight, backgroundPadding: getSingleValue(backgroundPadding, i) diff --git a/packages/victory-core/src/victory-theme/grayscale.js b/packages/victory-core/src/victory-theme/grayscale.tsx similarity index 97% rename from packages/victory-core/src/victory-theme/grayscale.js rename to packages/victory-core/src/victory-theme/grayscale.tsx index 4faf212a5..e494cf24b 100644 --- a/packages/victory-core/src/victory-theme/grayscale.js +++ b/packages/victory-core/src/victory-theme/grayscale.tsx @@ -1,4 +1,5 @@ import { assign } from "lodash"; +import { VictoryThemeDefinition } from "./victory-theme-definition"; // * // * Colors @@ -49,7 +50,7 @@ const centeredLabelStyles = assign({ textAnchor: "middle" }, baseLabelStyles); const strokeLinecap = "round"; const strokeLinejoin = "round"; -export default { +export const grayscale: VictoryThemeDefinition = { area: assign( { style: { diff --git a/packages/victory-core/src/victory-theme/material.js b/packages/victory-core/src/victory-theme/material.tsx similarity index 97% rename from packages/victory-core/src/victory-theme/material.js rename to packages/victory-core/src/victory-theme/material.tsx index 947287bc3..4de8062d3 100644 --- a/packages/victory-core/src/victory-theme/material.js +++ b/packages/victory-core/src/victory-theme/material.tsx @@ -1,4 +1,5 @@ import { assign } from "lodash"; +import { VictoryThemeDefinition } from "./victory-theme-definition"; // * // * Colors @@ -57,7 +58,7 @@ const strokeDasharray = "10, 5"; const strokeLinecap = "round"; const strokeLinejoin = "round"; -export default { +export const material: VictoryThemeDefinition = { area: assign( { style: { diff --git a/packages/victory-core/src/victory-theme/victory-theme.d.ts b/packages/victory-core/src/victory-theme/victory-theme-definition.ts similarity index 96% rename from packages/victory-core/src/victory-theme/victory-theme.d.ts rename to packages/victory-core/src/victory-theme/victory-theme-definition.ts index 061467da3..7f79bfb0d 100644 --- a/packages/victory-core/src/victory-theme/victory-theme.d.ts +++ b/packages/victory-core/src/victory-theme/victory-theme-definition.ts @@ -1,10 +1,6 @@ import * as React from "react"; -import { - CallbackArgs, - StringOrNumberOrCallback, - VictoryCommonThemeProps, - VictoryDatableProps -} from "../index"; +import { VictoryCommonThemeProps, VictoryDatableProps } from "../index"; +import { CallbackArgs, StringOrNumberOrCallback } from "../types"; export type BlockProps = { top?: number; @@ -263,11 +259,3 @@ export interface VictoryThemeDefinition { } & VictoryCommonThemeProps & VictoryDatableProps; } - -export interface VictoryThemeInterface { - grayscale: VictoryThemeDefinition; - material: VictoryThemeDefinition; -} - -const VictoryTheme: VictoryThemeInterface; -export default VictoryTheme; diff --git a/packages/victory-core/src/victory-theme/victory-theme.js b/packages/victory-core/src/victory-theme/victory-theme.js deleted file mode 100644 index 0193e6d55..000000000 --- a/packages/victory-core/src/victory-theme/victory-theme.js +++ /dev/null @@ -1,7 +0,0 @@ -import materialTheme from "./material"; -import grayscaleTheme from "./grayscale"; - -export default { - material: materialTheme, - grayscale: grayscaleTheme -}; diff --git a/packages/victory-core/src/victory-theme/victory-theme.tsx b/packages/victory-core/src/victory-theme/victory-theme.tsx new file mode 100644 index 000000000..6569834ea --- /dev/null +++ b/packages/victory-core/src/victory-theme/victory-theme.tsx @@ -0,0 +1,5 @@ +import { grayscale } from "./grayscale"; +import { material } from "./material"; +export * from "./victory-theme-definition"; + +export const VictoryTheme = { grayscale, material }; diff --git a/packages/victory-core/src/victory-transition/victory-transition.js b/packages/victory-core/src/victory-transition/victory-transition.tsx similarity index 74% rename from packages/victory-core/src/victory-transition/victory-transition.js rename to packages/victory-core/src/victory-transition/victory-transition.tsx index 5a67cc152..03754186c 100644 --- a/packages/victory-core/src/victory-transition/victory-transition.js +++ b/packages/victory-core/src/victory-transition/victory-transition.tsx @@ -5,10 +5,44 @@ import * as Collection from "../victory-util/collection"; import * as Helpers from "../victory-util/helpers"; import TimerContext from "../victory-util/timer-context"; import * as Transitions from "../victory-util/transitions"; -import { defaults, isFunction, pick, isObject } from "lodash"; +import { defaults, isFunction, pick } from "lodash"; import isEqual from "react-fast-compare"; +import Timer from "../victory-util/timer"; -export default class VictoryTransition extends React.Component { +type VictoryTransitionChild = React.ReactElement< + // Props: + { + polar?: boolean; + domain?: number[] | { x: number[]; y: number[] }; + groupComponent?: React.ReactElement; + }, + // Type: + { + new (props: any): React.Component; + continuous?: boolean; + } +>; +interface VictoryTransitionProps { + animate?: boolean | any; + animationWhitelist?: string[]; + children: VictoryTransitionChild; +} + +interface VictoryTransitionState { + oldProps?: VictoryTransitionProps | null; + nextProps?: VictoryTransitionProps; + nodesShouldLoad?: boolean; + nodesDoneLoad?: boolean; + nodesWillExit?: boolean; + nodesWillEnter?: boolean; + nodesShouldEnter?: boolean; + childrenTransitions?: unknown; +} + +export default class VictoryTransition extends React.Component< + VictoryTransitionProps, + VictoryTransitionState +> { static displayName = "VictoryTransition"; static propTypes = { @@ -18,6 +52,9 @@ export default class VictoryTransition extends React.Component { }; static contextType = TimerContext; + private continuous: boolean; + private timer: Timer; + private transitionProps: any; constructor(props, context) { super(props, context); @@ -28,7 +65,6 @@ export default class VictoryTransition extends React.Component { const child = this.props.children; const polar = child.props.polar; this.continuous = !polar && child.type && child.type.continuous === true; - this.getTransitionState = this.getTransitionState.bind(this); this.timer = this.context.transitionTimer; } @@ -36,7 +72,7 @@ export default class VictoryTransition extends React.Component { this.setState({ nodesShouldLoad: true }); //eslint-disable-line react/no-did-mount-set-state } - shouldComponentUpdate(nextProps) { + shouldComponentUpdate(nextProps: VictoryTransitionProps) { if (!isEqual(this.props, nextProps)) { this.timer.bypassAnimation(); this.setState(this.getTransitionState(this.props, nextProps), () => @@ -50,7 +86,10 @@ export default class VictoryTransition extends React.Component { this.timer.stop(); } - getTransitionState(props, nextProps) { + private getTransitionState( + props: VictoryTransitionProps, + nextProps: VictoryTransitionProps + ): VictoryTransitionState { const { animate } = props; if (!animate) { return {}; @@ -78,7 +117,10 @@ export default class VictoryTransition extends React.Component { } } - getDomainFromChildren(props, axis) { + private getDomainFromChildren( + props: VictoryTransitionProps, + axis: "x" | "y" + ) { const getChildDomains = (children) => { return children.reduce((memo, child) => { if (child.type && isFunction(child.type.getDomain)) { @@ -94,8 +136,10 @@ export default class VictoryTransition extends React.Component { }, []); }; - const child = React.Children.toArray(props.children)[0]; - const childProps = child.props || {}; + const child = React.Children.toArray( + props.children + )[0] as VictoryTransitionChild; + const childProps: any = child.props || {}; const domain = Array.isArray(childProps.domain) ? childProps.domain : childProps.domain && childProps.domain[axis]; @@ -121,10 +165,10 @@ export default class VictoryTransition extends React.Component { : this.props; } - pickDomainProps(props) { - const parentState = isObject(props.animate) && props.animate.parentState; + private pickDomainProps(props: VictoryTransitionProps) { + const parentState = props.animate?.parentState; if (parentState && parentState.nodesWillExit) { - return this.continous || parentState.continuous + return this.continuous || parentState.continuous ? parentState.nextProps || this.state.nextProps || props : props; } @@ -146,13 +190,14 @@ export default class VictoryTransition extends React.Component { render() { const props = this.pickProps(); - const getTransitionProps = - isObject(this.props.animate) && this.props.animate.getTransitions - ? this.props.animate.getTransitions - : Transitions.getTransitionPropsFactory(props, this.state, (newState) => - this.setState(newState) - ); - const child = React.Children.toArray(props.children)[0]; + const getTransitionProps = this.props.animate?.getTransitions + ? this.props.animate.getTransitions + : Transitions.getTransitionPropsFactory(props, this.state, (newState) => + this.setState(newState) + ); + const child = React.Children.toArray( + props.children + )[0] as VictoryTransitionChild; const transitionProps = getTransitionProps(child); this.transitionProps = transitionProps; const domain = { diff --git a/packages/victory-core/src/victory-util/timer-context.js b/packages/victory-core/src/victory-util/timer-context.ts similarity index 100% rename from packages/victory-core/src/victory-util/timer-context.js rename to packages/victory-core/src/victory-util/timer-context.ts diff --git a/packages/victory-core/src/victory-util/timer.js b/packages/victory-core/src/victory-util/timer.ts similarity index 72% rename from packages/victory-core/src/victory-util/timer.js rename to packages/victory-core/src/victory-util/timer.ts index 812d07238..cd2968cdc 100644 --- a/packages/victory-core/src/victory-util/timer.js +++ b/packages/victory-core/src/victory-util/timer.ts @@ -1,10 +1,25 @@ import { timer, now } from "victory-vendor/d3-timer"; +interface D3Timer { + new (callback): void; + stop(): void; +} + +type Callback = (elapsed: number, duration: number) => void; + export default class Timer { + private shouldAnimate: boolean; + private readonly subscribers: Array<{ + callback: Callback; + startTime: number; + duration: number; + }>; + private activeSubscriptions: number; + private timer: D3Timer | null; + constructor() { this.shouldAnimate = true; this.subscribers = []; - this.loop = this.loop.bind(this); this.timer = null; this.activeSubscriptions = 0; } @@ -17,11 +32,11 @@ export default class Timer { this.shouldAnimate = true; } - loop() { + loop = () => { this.subscribers.forEach((s) => { s.callback(now() - s.startTime, s.duration); }); - } + }; start() { if (!this.timer) { @@ -36,7 +51,7 @@ export default class Timer { } } - subscribe(callback, duration) { + subscribe(callback: Callback, duration: number) { duration = this.shouldAnimate ? duration : 0; const subscriptionID = this.subscribers.push({ startTime: now(), diff --git a/yarn.lock b/yarn.lock index 71d4bb2c3..ab3948136 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4314,16 +4314,7 @@ dependencies: "@types/react" "*" -"@types/react@*": - version "18.0.9" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878" - integrity sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@^16", "@types/react@^16.9.19": +"@types/react@*", "@types/react@^16", "@types/react@^16.14.26", "@types/react@^17": version "16.14.26" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.26.tgz#82540a240ba7207ebe87d9579051bc19c9ef7605" integrity sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ== @@ -4332,15 +4323,6 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@^17": - version "17.0.45" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.45.tgz#9b3d5b661fd26365fefef0e766a1c6c30ccf7b3f" - integrity sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - "@types/retry@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -15289,7 +15271,7 @@ react-transform-hmr@^1.0.4: global "^4.3.0" react-proxy "^1.1.7" -react@^16.13.1, react@^16.8.3: +react@^16.14.0, react@^16.8.3: version "16.14.0" resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==