Skip to content

Commit

Permalink
[added] Operable interface
Browse files Browse the repository at this point in the history
Summary:
This allows types that don't implement the same constructor as Observable to be extended with operators.

Closes #194

Reviewers: O2 Material Motion, O3 Material JavaScript platform reviewers, #material_motion, featherless

Reviewed By: O2 Material Motion, #material_motion, featherless

Tags: #material_motion

Differential Revision: http://codereview.cc/D3128
  • Loading branch information
appsforartists committed May 2, 2017
1 parent 60cd82c commit 52c2e42
Show file tree
Hide file tree
Showing 8 changed files with 28 additions and 22 deletions.
16 changes: 6 additions & 10 deletions packages/core/src/observables/MotionObservable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
NextChannel,
NextOperation,
Observable,
Operable,
Subscription,
} from '../types';

Expand All @@ -32,22 +33,15 @@ import {
withMotionOperators,
} from '../operators';

// Mixins and generics don't work together yet:
//
// https://github.com/Microsoft/TypeScript/issues/13807
//
// In the mean time, we can work around this by passing `any` where `T` ought to
// be and mixing our observable together before extending it.
export type ShouldBeT = any;
export const MixedTogetherObservable: Constructor<ObservableWithMotionOperators<ShouldBeT>> = withMotionOperators<ShouldBeT, Constructor<Observable<ShouldBeT>>>(IndefiniteObservable);
export type MotionObservable<T> = Constructor<ObservableWithMotionOperators<T>>;

/**
* `MotionObservable` is an extension of `IndefiniteObservable` that includes
* a series of purely-declarative operators that are useful for building
* animated interactions. Those operators are specified in the
* [Starmap](https://material-motion.github.io/material-motion/starmap/specifications/operators/)
*/
export class MotionObservable<T> extends MixedTogetherObservable {
export class OperableObservable<T> extends IndefiniteObservable<T> implements Operable<T> {
/**
* Creates a new `MotionObservable` that dispatches whatever values it
* receives from the provided stream.
Expand All @@ -64,6 +58,8 @@ export class MotionObservable<T> extends MixedTogetherObservable {
}
);
}
}

_observableConstructor = MotionObservable;
}
export const MotionObservable = withMotionOperators(OperableObservable);
export default MotionObservable;
5 changes: 3 additions & 2 deletions packages/core/src/operators/delayBy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,21 @@ import {
Observable,
ObservableWithMotionOperators,
Observer,
Operable,
} from '../types';

export interface MotionDelayable<T> {
delayBy(time: number): ObservableWithMotionOperators<T>;
}

export function withDelayBy<T, S extends Constructor<Observable<T>>>(superclass: S): S & Constructor<MotionDelayable<T>> {
export function withDelayBy<T, S extends Constructor<Observable<T> & Operable<T>>>(superclass: S): S & Constructor<MotionDelayable<T>> {
return class extends superclass implements MotionDelayable<T> {
/**
* Buffers upstream values for the specified number of milliseconds, then
* dispatches them.
*/
delayBy(time: number): ObservableWithMotionOperators<T> {
const constructor = this.constructor as Constructor<ObservableWithMotionOperators<T>>;
const constructor = this._observableConstructor as Constructor<ObservableWithMotionOperators<T>>;

return new constructor(
(observer: Observer<T>) => {
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/operators/foundation/_nextOperator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import {
Observable,
ObservableWithMotionOperators,
Observer,
Operable,
} from '../../types';

export interface MotionNextOperable<T> extends Observable<T> {
_nextOperator<U>(operation: NextOperation<T, U>): ObservableWithMotionOperators<U>;
}

export function withNextOperator<T, S extends Constructor<Observable<T>>>(superclass: S): S & Constructor<MotionNextOperable<T>> {
export function withNextOperator<T, S extends Constructor<Observable<T> & Operable<T>>>(superclass: S): S & Constructor<MotionNextOperable<T>> {
return class extends superclass implements MotionNextOperable<T> {
/**
* `_nextOperator` is sugar for creating an operator that reads and writes
Expand All @@ -38,7 +39,7 @@ export function withNextOperator<T, S extends Constructor<Observable<T>>>(superc
* the result to the observer's `next` channel.
*/
_nextOperator<U>(operation: NextOperation<T, U>): ObservableWithMotionOperators<U> {
const constructor = this.constructor as Constructor<ObservableWithMotionOperators<T>>;
const constructor = this._observableConstructor as Constructor<ObservableWithMotionOperators<T>>;

return new constructor(
(observer: Observer<U>) => {
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/operators/foundation/_remember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ import {
Observable,
ObservableWithMotionOperators,
Observer,
Operable,
Subscription,
} from '../../types';

export interface MotionMemorable<T> extends Observable<T> {
_remember(): ObservableWithMotionOperators<T>;
}

export function withRemember<T, S extends Constructor<Observable<T>>>(superclass: S): S & Constructor<MotionMemorable<T>> {
export function withRemember<T, S extends Constructor<Observable<T> & Operable<T>>>(superclass: S): S & Constructor<MotionMemorable<T>> {
return class extends superclass implements MotionMemorable<T> {
/**
* Remembers the most recently dispatched value and synchronously
Expand All @@ -44,7 +45,7 @@ export function withRemember<T, S extends Constructor<Observable<T>>>(superclass
let lastValue: T;
let hasStarted = false;

const constructor = this.constructor as Constructor<ObservableWithMotionOperators<T>>;
const constructor = this._observableConstructor as Constructor<ObservableWithMotionOperators<T>>;

return new constructor(
(observer: Observer<T>) => {
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/operators/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
Observable,
ObservableWithMotionOperators,
Observer,
Operable,
} from '../types';

import {
Expand All @@ -29,14 +30,14 @@ export interface MotionMergeable<T> extends Observable<T> {
merge(...otherStreams: Array<Observable<any>>): ObservableWithMotionOperators<any>;
}

export function withMerge<T, S extends Constructor<Observable<T>>>(superclass: S): S & Constructor<MotionMergeable<T>> {
export function withMerge<T, S extends Constructor<Observable<T> & Operable<T>>>(superclass: S): S & Constructor<MotionMergeable<T>> {
return class extends superclass implements MotionMergeable<T> {
/**
* Dispatches values as it receives them, both from upstream and from any
* streams provided as arguments.
*/
merge(...otherStreams: Array<Observable<any>>): ObservableWithMotionOperators<any> {
const constructor = this.constructor as Constructor<ObservableWithMotionOperators<T>>;
const constructor = this._observableConstructor as Constructor<ObservableWithMotionOperators<T>>;

return new constructor(
(observer: Observer<any>) => {
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/operators/startWith.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ import {
ObservableWithFoundationalMotionOperators,
ObservableWithMotionOperators,
Observer,
Operable,
} from '../types';

export interface MotionSeedable<T> {
startWith(initialValue: T): ObservableWithMotionOperators<T>;
}

export function withStartWith<T, S extends Constructor<ObservableWithFoundationalMotionOperators<T>>>(superclass: S): S & Constructor<MotionSeedable<T>> {
export function withStartWith<T, S extends Constructor<ObservableWithFoundationalMotionOperators<T> & Operable<T>>>(superclass: S): S & Constructor<MotionSeedable<T>> {
return class extends superclass implements MotionSeedable<T> {
/**
* Dispatches `initialValue` and passes through all subsequent values.
Expand All @@ -34,7 +35,7 @@ export function withStartWith<T, S extends Constructor<ObservableWithFoundationa
* receive the most recent value.
*/
startWith(initialValue: T): ObservableWithMotionOperators<T> {
const constructor = this.constructor as Constructor<ObservableWithMotionOperators<T>>;
const constructor = this._observableConstructor as Constructor<ObservableWithMotionOperators<T>>;

return new constructor(
(observer: Observer<T>) => {
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/operators/velocity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
Observable,
ObservableWithMotionOperators,
Observer,
Operable,
Point2D,
Timestamped,
} from '../types';
Expand Down Expand Up @@ -54,7 +55,7 @@ const pluckValue = createPlucker('value') as any as NumericPlucker;
const pluckX = createPlucker('value.x') as any as NumericPlucker;
const pluckY = createPlucker('value.y') as any as NumericPlucker;

export function withVelocity<T, S extends Constructor<Observable<T> & MotionTimestampable<T> & MotionWindowable<T>>>(superclass: S): S & Constructor<MotionVelocityMeasurable<T>> {
export function withVelocity<T, S extends Constructor<Observable<T> & MotionTimestampable<T> & MotionWindowable<T> & Operable<T>>>(superclass: S): S & Constructor<MotionVelocityMeasurable<T>> {
return class extends superclass implements MotionVelocityMeasurable<T> {
/**
* Computes the velocity of an incoming stream and dispatches the result.
Expand All @@ -71,7 +72,7 @@ export function withVelocity<T, S extends Constructor<Observable<T> & MotionTime
* is only calculated when it will be used.
*/
velocity(pulse: MotionTimestampable<T> & Observable<any> = this): ObservableWithMotionOperators<T> {
const constructor = this.constructor as Constructor<ObservableWithMotionOperators<T>>;
const constructor = this._observableConstructor as Constructor<ObservableWithMotionOperators<T>>;

return new constructor(
(observer: Observer<T>) => {
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ export interface ScopedWritable<T> {
write: Write<T>;
}

export interface Operable<T> {
_observableConstructor: Constructor<Observable<T>>;
}

export interface PropertyObservable<T> extends Observable<T>, ScopedReadable<T>, ScopedWritable<T> {}

export interface MotionElement {
Expand Down

0 comments on commit 52c2e42

Please sign in to comment.