From cb16ebe3fa0dca948a65d914070eb4b2d7936811 Mon Sep 17 00:00:00 2001 From: Brenton Simpson Date: Sun, 8 Oct 2017 18:36:59 -0700 Subject: [PATCH] [refactored] math operators to have shorthand signatures Summary: This makes the common case (a single value and no options) more ergonomic. Part of https://github.com/material-motion/material-motion-js/issues/230 Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, vietanh Reviewed By: #material_motion, vietanh Tags: #material_motion Differential Revision: http://codereview.cc/D3421 --- packages/core/src/interactions/Swipeable.ts | 6 ++-- .../src/operators/__tests__/addedBy.test.ts | 32 +++++++++++++++++++ .../src/operators/__tests__/dividedBy.test.ts | 31 ++++++++++++++++++ .../operators/__tests__/multipliedBy.test.ts | 31 ++++++++++++++++++ .../operators/__tests__/subtractedBy.test.ts | 32 +++++++++++++++++++ packages/core/src/operators/addedBy.ts | 13 +++++++- packages/core/src/operators/dividedBy.ts | 13 +++++++- packages/core/src/operators/multipliedBy.ts | 12 ++++++- packages/core/src/operators/subtractedBy.ts | 12 ++++++- 9 files changed, 175 insertions(+), 7 deletions(-) diff --git a/packages/core/src/interactions/Swipeable.ts b/packages/core/src/interactions/Swipeable.ts index a0c05478..a8a25083 100644 --- a/packages/core/src/interactions/Swipeable.ts +++ b/packages/core/src/interactions/Swipeable.ts @@ -155,7 +155,7 @@ export class Swipeable { subscribe({ sink: tossable.resistanceFactor$, source: when(tossableIsAtRest$).rewriteTo({ - value$: tossable.resistanceBasis$.dividedBy({ value$: Swipeable.VISUAL_THRESHOLD }), + value$: tossable.resistanceBasis$.dividedBy(Swipeable.VISUAL_THRESHOLD), onlyDispatchWithUpstream: true, }) }); @@ -246,7 +246,7 @@ export class Swipeable { }), }); - const destinationDistance$ = width$.addedBy({ value$: this.destinationMargin$ }); + const destinationDistance$ = width$.addedBy(this.destinationMargin$); subscribe({ sink: spring.destination$, @@ -254,7 +254,7 @@ export class Swipeable { x: this.swipeState$.rewrite({ mapping: { [SwipeState.NONE]: 0, - [SwipeState.LEFT]: destinationDistance$.multipliedBy({ value$: -1 }), + [SwipeState.LEFT]: destinationDistance$.multipliedBy(-1), [SwipeState.RIGHT]: destinationDistance$, } }), diff --git a/packages/core/src/operators/__tests__/addedBy.test.ts b/packages/core/src/operators/__tests__/addedBy.test.ts index 7724dd05..634d8921 100644 --- a/packages/core/src/operators/__tests__/addedBy.test.ts +++ b/packages/core/src/operators/__tests__/addedBy.test.ts @@ -88,5 +88,37 @@ describe('motionObservable.addedBy', expect(listener).to.have.been.calledOnce.and.to.have.been.calledWith({x: 10, y: 15 }); } ); + + it('should have a shorthand signature for numeric constants', + () => { + subject.addedBy(10).subscribe(listener); + + subject.next(3); + + expect(listener).to.have.been.calledWith(13); + } + ); + + it('should have a shorthand signature for Point2D constants', + () => { + subject.addedBy({ x : 10, y: 20 }).subscribe(listener); + + subject.next({x: 100, y: -40 }); + + expect(listener).to.have.been.calledWith({x: 110, y: -20 }); + } + ); + + it('should have a shorthand signature for streams', + () => { + subject.addedBy(value$).subscribe(listener); + + subject.next(3); + value$.next(10); + value$.next(20); + + expect(listener).to.have.been.calledTwice.and.to.have.been.calledWith(13).and.to.have.been.calledWith(23); + } + ); } ); diff --git a/packages/core/src/operators/__tests__/dividedBy.test.ts b/packages/core/src/operators/__tests__/dividedBy.test.ts index 4e300c04..52a22c64 100644 --- a/packages/core/src/operators/__tests__/dividedBy.test.ts +++ b/packages/core/src/operators/__tests__/dividedBy.test.ts @@ -76,5 +76,36 @@ describe('motionObservable.dividedBy', expect(listener).to.have.been.calledOnce.and.to.have.been.calledWith({ x: 15, y: 20 }); } ); + + it('should have a shorthand signature for numeric constants', + () => { + subject.dividedBy(5).subscribe(listener); + + subject.next(10); + + expect(listener).to.have.been.calledWith(2); + } + ); + + it('should have a shorthand signature for Point2D constants', + () => { + subject.dividedBy({ x : 10, y: 20 }).subscribe(listener); + + subject.next({x: 100, y: 40 }); + + expect(listener).to.have.been.calledWith({x: 10, y: 2 }); + } + ); + + it('should have a shorthand signature for streams', + () => { + subject.dividedBy(value$).subscribe(listener); + + subject.next({ x: 30, y: 60 }); + value$.next({ x: 2, y: 3 }); + + expect(listener).to.have.been.calledOnce.and.to.have.been.calledWith({ x: 15, y: 20 }); + } + ); } ); diff --git a/packages/core/src/operators/__tests__/multipliedBy.test.ts b/packages/core/src/operators/__tests__/multipliedBy.test.ts index 87568a05..5509d406 100644 --- a/packages/core/src/operators/__tests__/multipliedBy.test.ts +++ b/packages/core/src/operators/__tests__/multipliedBy.test.ts @@ -76,5 +76,36 @@ describe('motionObservable.multipliedBy', expect(listener).to.have.been.calledOnce.and.to.have.been.calledWith({ x: 12, y: 40 }); } ); + + it('should have a shorthand signature for numeric constants', + () => { + subject.multipliedBy(10).subscribe(listener); + + subject.next(3); + + expect(listener).to.have.been.calledWith(30); + } + ); + + it('should have a shorthand signature for Point2D constants', + () => { + subject.multipliedBy({ x : 10, y: 20 }).subscribe(listener); + + subject.next({ x: 2, y: 4 }); + + expect(listener).to.have.been.calledWith({ x: 20, y: 80 }); + } + ); + + it('should have a shorthand signature for streams', + () => { + subject.multipliedBy(value$).subscribe(listener); + + subject.next({ x: 3, y: 4 }); + value$.next({ x: 4, y: 10 }); + + expect(listener).to.have.been.calledOnce.and.to.have.been.calledWith({ x: 12, y: 40 }); + } + ); } ); diff --git a/packages/core/src/operators/__tests__/subtractedBy.test.ts b/packages/core/src/operators/__tests__/subtractedBy.test.ts index c75fb588..c8b19f5b 100644 --- a/packages/core/src/operators/__tests__/subtractedBy.test.ts +++ b/packages/core/src/operators/__tests__/subtractedBy.test.ts @@ -89,5 +89,37 @@ describe('motionObservable.subtractedBy', expect(listener).to.have.been.calledOnce.and.to.have.been.calledWith({ x: 10, y: -15 }); } ); + + it('should have a shorthand signature for numeric constants', + () => { + subject.subtractedBy(10).subscribe(listener); + + subject.next(3); + + expect(listener).to.have.been.calledWith(-7); + } + ); + + it('should have a shorthand signature for Point2D constants', + () => { + subject.subtractedBy({ x: 10, y: 20 }).subscribe(listener); + + subject.next({ x: 100, y: -40 }); + + expect(listener).to.have.been.calledWith({ x: 90, y: -60 }); + } + ); + + it('should have a shorthand signature for streams', + () => { + subject.subtractedBy(value$).subscribe(listener); + + subject.next({ x: 100, y: -40 }); + subject.next({ x: 10, y: 0 }); + value$.next({ x: 0, y: 15 }); + + expect(listener).to.have.been.calledOnce.and.to.have.been.calledWith({ x: 10, y: -15 }); + } + ); } ); diff --git a/packages/core/src/operators/addedBy.ts b/packages/core/src/operators/addedBy.ts index 38065a79..5379fe00 100644 --- a/packages/core/src/operators/addedBy.ts +++ b/packages/core/src/operators/addedBy.ts @@ -15,6 +15,7 @@ */ import { + isDefined, isPoint2D, } from '../typeGuards'; @@ -30,8 +31,10 @@ import { _ReactiveMapOptions, } from './foundation/_reactiveMap'; +export type AddedByValue = U | Observable; + export type AddedByArgs = _ReactiveMapOptions & { - value$: U | Observable + value$: AddedByValue, }; export interface MotionAddable { @@ -42,7 +45,9 @@ export interface MotionAddable { // To work around this, we overload the method signature. When `T` is a // number, we explicitly return an `Observable`. Otherwise, we can // use the type variable `U`. + addedBy(value$: AddedByValue): ObservableWithMotionOperators; addedBy(kwargs: AddedByArgs): ObservableWithMotionOperators; + addedBy(value$: AddedByValue): ObservableWithMotionOperators; addedBy(kwargs: AddedByArgs): ObservableWithMotionOperators; } @@ -51,7 +56,13 @@ export function withAddedBy>>(sup /** * Adds the provided value to the upstream value and dispatches the result. */ + addedBy(value$: AddedByValue): ObservableWithMotionOperators; + addedBy(kwargs: AddedByArgs): ObservableWithMotionOperators; addedBy({ value$, ...reactiveMapOptions }: AddedByArgs): ObservableWithMotionOperators { + if (!isDefined(value$)) { + value$ = arguments[0]; + } + return this._mathOperator({ operation: (upstream, value) => upstream + value, value$, diff --git a/packages/core/src/operators/dividedBy.ts b/packages/core/src/operators/dividedBy.ts index 3c724e19..e802d1dd 100644 --- a/packages/core/src/operators/dividedBy.ts +++ b/packages/core/src/operators/dividedBy.ts @@ -15,6 +15,7 @@ */ import { + isDefined, isPoint2D, } from '../typeGuards'; @@ -30,8 +31,10 @@ import { _ReactiveMapOptions, } from './foundation/_reactiveMap'; +export type DividedByValue = U | Observable; + export type DividedByArgs = _ReactiveMapOptions & { - value$: U | Observable + value$: DividedByValue, }; export interface MotionDivisible { @@ -43,7 +46,9 @@ export interface MotionDivisible { // To work around this, we overload the method signature. When `T` is a // number, we explicitly return an `Observable`. Otherwise, we can // use the type variable `U`. + dividedBy(value$: DividedByValue): ObservableWithMotionOperators; dividedBy(kwargs: DividedByArgs): ObservableWithMotionOperators; + dividedBy(value$: DividedByValue): ObservableWithMotionOperators; dividedBy(kwargs: DividedByArgs): ObservableWithMotionOperators; } @@ -53,7 +58,13 @@ export function withDividedBy>>(s * Divides the upstream value by the provided value and dispatches the * result. */ + dividedBy(value$: DividedByValue): ObservableWithMotionOperators; + dividedBy(kwargs: DividedByArgs): ObservableWithMotionOperators; dividedBy({ value$, ...reactiveMapOptions }: DividedByArgs): ObservableWithMotionOperators { + if (!isDefined(value$)) { + value$ = arguments[0]; + } + return this._mathOperator({ operation: (upstream, value) => upstream / value, value$, diff --git a/packages/core/src/operators/multipliedBy.ts b/packages/core/src/operators/multipliedBy.ts index df57c7a5..06eefbed 100644 --- a/packages/core/src/operators/multipliedBy.ts +++ b/packages/core/src/operators/multipliedBy.ts @@ -15,6 +15,7 @@ */ import { + isDefined, isPoint2D, } from '../typeGuards'; @@ -30,8 +31,9 @@ import { _ReactiveMapOptions, } from './foundation/_reactiveMap'; +export type MultipliedByValue = U | Observable; export type MultipliedByArgs = _ReactiveMapOptions & { - value$: U | Observable + value$: MultipliedByValue, }; export interface MotionMultipliable { @@ -42,7 +44,9 @@ export interface MotionMultipliable { // To work around this, we overload the method signature. When `T` is a // number, we explicitly return an `Observable`. Otherwise, we can // use the type variable `U`. + multipliedBy(value$: MultipliedByValue): ObservableWithMotionOperators; multipliedBy(kwargs: MultipliedByArgs): ObservableWithMotionOperators; + multipliedBy(value$: MultipliedByValue): ObservableWithMotionOperators; multipliedBy(kwargs: MultipliedByArgs): ObservableWithMotionOperators; } @@ -52,7 +56,13 @@ export function withMultipliedBy> * Multiplies the upstream value by the provided value and dispatches the * result. */ + multipliedBy(value$: MultipliedByValue): ObservableWithMotionOperators; + multipliedBy(kwargs: MultipliedByArgs): ObservableWithMotionOperators; multipliedBy({ value$, ...reactiveMapOptions }: MultipliedByArgs): ObservableWithMotionOperators { + if (!isDefined(value$)) { + value$ = arguments[0]; + } + return this._mathOperator({ operation: (upstream, value) => upstream * value, value$, diff --git a/packages/core/src/operators/subtractedBy.ts b/packages/core/src/operators/subtractedBy.ts index 6338e19f..24925760 100644 --- a/packages/core/src/operators/subtractedBy.ts +++ b/packages/core/src/operators/subtractedBy.ts @@ -15,6 +15,7 @@ */ import { + isDefined, isPoint2D, } from '../typeGuards'; @@ -30,8 +31,9 @@ import { _ReactiveMapOptions, } from './foundation/_reactiveMap'; +export type SubtractedByValue = U | Observable; export type SubtractedByArgs = _ReactiveMapOptions & { - value$: U | Observable + value$: SubtractedByValue, }; export interface MotionSubtractable { // Since number literals get their own types, `U extends T & number` will @@ -41,7 +43,9 @@ export interface MotionSubtractable { // To work around this, we overload the method signature. When `T` is a // number, we explicitly return an `Observable`. Otherwise, we can // use the type variable `U`. + subtractedBy(value$: SubtractedByValue): ObservableWithMotionOperators; subtractedBy(kwargs: SubtractedByArgs): ObservableWithMotionOperators; + subtractedBy(value$: SubtractedByValue): ObservableWithMotionOperators; subtractedBy(kwargs: SubtractedByArgs): ObservableWithMotionOperators; } @@ -51,7 +55,13 @@ export function withSubtractedBy> * Subtracts the provided value from the upstream value and dispatches the * result. */ + subtractedBy(value$: SubtractedByValue): ObservableWithMotionOperators; + subtractedBy(kwargs: SubtractedByArgs): ObservableWithMotionOperators; subtractedBy({ value$, ...reactiveMapOptions }: SubtractedByArgs): ObservableWithMotionOperators { + if (!isDefined(value$)) { + value$ = arguments[0]; + } + return this._mathOperator({ operation: (upstream, value) => upstream - value, value$,