diff --git a/mobservable-react/mobservable-react-tests.ts b/mobservable-react/mobservable-react-tests.ts new file mode 100644 index 000000000..7bda00cf4 --- /dev/null +++ b/mobservable-react/mobservable-react-tests.ts @@ -0,0 +1,58 @@ +/// +/// + +import {reactiveComponent} from 'mobservable-react'; + +{ + let c1 = reactiveComponent(React.createClass({ + getDefaultProps() { + return { + test: "hi" + }; + }, + + render: function() { + return React.createElement("div"); + } + })); + + let c1Factory = React.createFactory(c1); + React.render(c1Factory({ + test: "hello" + }), null); +} + +@reactiveComponent +class TestComponent extends React.Component<{ test: string },{}> { + render() { + return React.createElement("div"); + } +} + +{ + let c2Factory = React.createFactory(TestComponent); + React.render(c2Factory({ + test: "hello" + }) , null); +} + +{ + var c3 = reactiveComponent((props: { test: string }) => React.createElement("div")); + var c3Factory = React.createFactory(c3); //without JSX + React.render(c3Factory({ + test: "hello" + }), null); +} + + +class TestComponent2 extends React.Component<{ test: string },{}> { + render() { + return React.createElement("div"); + } +} +{ + let c4Factory = React.createFactory(reactiveComponent(TestComponent2)); + React.render(c4Factory({ + test: "hello" + }) , null); +} \ No newline at end of file diff --git a/mobservable-react/mobservable-react-tests.ts.tscparams b/mobservable-react/mobservable-react-tests.ts.tscparams new file mode 100644 index 000000000..3f1358357 --- /dev/null +++ b/mobservable-react/mobservable-react-tests.ts.tscparams @@ -0,0 +1 @@ +--noImplicitAny --module commonjs --target es5 --experimentalDecorators \ No newline at end of file diff --git a/mobservable-react/mobservable-react.d.ts b/mobservable-react/mobservable-react.d.ts new file mode 100644 index 000000000..34ecd78f8 --- /dev/null +++ b/mobservable-react/mobservable-react.d.ts @@ -0,0 +1,16 @@ +// Type definitions for mobservable v0.1.8 +// Project: https://github.com/mweststrate/mobservable-react +// Definitions by: Michel Weststrate +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// + +declare module "mobservable-react" { + /** + * Turns a React component or stateless render function into a reactive component. + */ + export function reactiveComponent

(clazz: React.ClassicComponentClass

): React.ClassicComponentClass

; + export function reactiveComponent

(clazz: React.ComponentClass

): React.ComponentClass

; + export function reactiveComponent

(clazz: React.ComponentClass

): void; // for decorator + export function reactiveComponent

(renderFunction: (props: P) => React.ReactElement): React.ClassicComponentClass

; +} \ No newline at end of file diff --git a/mobservable/mobservable-tests.ts b/mobservable/mobservable-tests.ts new file mode 100644 index 000000000..2966128fe --- /dev/null +++ b/mobservable/mobservable-tests.ts @@ -0,0 +1,57 @@ +/// +import mobservable = require('mobservable'); +import {observable} from "mobservable"; + +var v = mobservable(3); +v.observe(() => {}); + +var a = mobservable([1,2,3]); + +class Order { + @observable price:number = 3; + @observable amount:number = 2; + @observable orders:string[] = []; + + @observable get total() { + return this.amount * this.price * (1 + this.orders.length); + } +} + +export function testObservable() { + var a = mobservable(3); + var b = mobservable(() => a() * 2); +} + +export function testAnnotations() { + var order1totals:number[] = []; + var order1 = new Order(); + var order2 = new Order(); + + var disposer = mobservable.observe(() => { + order1totals.push(order1.total) + }); + + order2.price = 4; + order1.amount = 1; + + order2.orders.push('bla'); + + order1.orders.splice(0,0,'boe', 'hoi'); + + disposer(); + order1.orders.pop(); +}; + +export function testTyping() { + var ar:Mobservable.IObservableArray = mobservable.makeReactive([1,2]); + ar.observe((d:Mobservable.IArrayChange|Mobservable.IArraySplice) => { + console.log(d.type); + }); + + var ar2:Mobservable.IObservableArray = mobservable([1,2]); + ar2.observe((d:Mobservable.IArrayChange|Mobservable.IArraySplice) => { + console.log(d.type); + }); + + var x:Mobservable.IObservableValue = mobservable(3); +} \ No newline at end of file diff --git a/mobservable/mobservable-tests.ts.tscparams b/mobservable/mobservable-tests.ts.tscparams new file mode 100644 index 000000000..77f208cc0 --- /dev/null +++ b/mobservable/mobservable-tests.ts.tscparams @@ -0,0 +1 @@ +--noImplicitAny --module commonjs --target es5 --experimentalDecorators diff --git a/mobservable/mobservable.d.ts b/mobservable/mobservable.d.ts new file mode 100644 index 000000000..d98c34454 --- /dev/null +++ b/mobservable/mobservable.d.ts @@ -0,0 +1,189 @@ +// Type definitions for mobservable v0.6.10 +// Project: https://mweststrate.github.io/mobservable +// Definitions by: Michel Weststrate +// Definitions: https://github.com/borisyankov/DefinitelyTyped + + +interface _IMobservableStatic { + /** + * Turns an object, array or function into a reactive structure. + * @param value the value which should become observable. + */ + makeReactive: IMakeReactive; + + /** + * Extends an object with reactive capabilities. + * @param target the object to which reactive properties should be added + * @param properties the properties that should be added and made reactive + * @returns targer + */ + extendReactive(target: Object, properties: Object):Object; + + /** + * Returns true if the provided value is reactive. + * @param value object, function or array + * @param propertyName if propertyName is specified, checkes whether value.propertyName is reactive. + */ + isReactive(value: any, propertyName?:string): boolean; + + /** + * Can be used in combination with makeReactive / extendReactive. + * Enforces that a reference to 'value' is stored as property, + * but that 'value' itself is not turned into something reactive. + * Future assignments to the same property will inherit this behavior. + * @param value initial value of the reactive property that is being defined. + */ + asReference(value: any):{value:T}; + + /** + * ES6 / Typescript decorator which can to make class properties and getter functions reactive. + */ + observable(target: Object, key: string):any; // decorator / annotation + + /** + * Creates a reactive view and keeps it alive, so that the view is always + * updated if one of the dependencies changes, even when the view is not further used by something else. + * @param func The reactive view + * @param scope (optional) + * @returns disposer function, which can be used to stop the view from being updated in the future. + */ + observe(func: Mobservable.Lambda, scope?: any): Mobservable.Lambda; + + /** + * Deprecated, use mobservable.observe instead. + */ + sideEffect(func: Mobservable.Lambda, scope?: any): Mobservable.Lambda; + + /** + * Similar to 'observer', observes the given predicate until it returns true. + * Once it returns true, the 'effect' function is invoked an the observation is cancelled. + * @param predicate + * @param effect + * @param scope (optional) + * @returns disposer function to prematurely end the observer. + */ + observeUntil(predicate: ()=>boolean, effect: Mobservable.Lambda, scope?: any): Mobservable.Lambda; + + /** + * During a transaction no views are updated until the end of the transaction. + * The transaction will be run synchronously nonetheless. + * @param action a function that updates some reactive state + * @returns any value that was returned by the 'action' parameter. + */ + transaction(action: ()=>T): T; + + /** + * Converts a reactive structure into a non-reactive structure. + * Basically a deep-clone. + */ + toJSON(value: T): T; + + /** + * Sets the reporting level Defaults to 1. Use 0 for production or 2 for increased verbosity. + */ + logLevel: number; // 0 = production, 1 = development, 2 = debugging + + extras: { + getDependencyTree(thing:any, property?:string): Mobservable.IDependencyTree; + + getObserverTree(thing:any, property?:string): Mobservable.IObserverTree; + + trackTransitions(extensive?:boolean, onReport?:(lines:Mobservable.ITransitionEvent) => void) : Mobservable.Lambda; + } +} + +interface IMakeReactive { + (value: T[], opts?: Mobservable.IMakeReactiveOptions): Mobservable.IObservableArray; + (value: ()=>T, opts?: Mobservable.IMakeReactiveOptions): Mobservable.IObservableValue; + (value: T, opts?: Mobservable.IMakeReactiveOptions): Mobservable.IObservableValue; + (value: Object, opts?: Mobservable.IMakeReactiveOptions): T; +} + +interface IMobservableStatic extends _IMobservableStatic, IMakeReactive { +} + +declare module Mobservable { + interface IMakeReactiveOptions { + as?: string /* "auto" | "reference" | TODO: see #8 "structure" */ + scope?: Object, + context?: Object, + recurse?: boolean; + name?: string; + // protected: boolean TODO: see #9 + } + + export interface IContextInfoStruct { + object: Object; + name: string; + } + + export type IContextInfo = IContextInfoStruct | string; + + interface Lambda { + (): void; + name?: string; + } + + interface IObservable { + observe(callback: (...args: any[])=>void, fireImmediately?: boolean): Lambda; + } + + interface IObservableValue extends IObservable { + (): T; + (value: T):void; + observe(callback: (newValue: T, oldValue: T)=>void, fireImmediately?: boolean): Lambda; + } + + interface IObservableArray extends IObservable, Array { + spliceWithArray(index: number, deleteCount?: number, newItems?: T[]): T[]; + observe(listener: (changeData: IArrayChange|IArraySplice)=>void, fireImmediately?: boolean): Lambda; + clear(): T[]; + replace(newItems: T[]): T[]; + find(predicate: (item: T,index: number,array: IObservableArray)=>boolean,thisArg?: any,fromIndex?: number): T; + remove(value: T): boolean; + } + + interface IArrayChange { + type: string; // Always: 'update' + object: IObservableArray; + index: number; + oldValue: T; + } + + interface IArraySplice { + type: string; // Always: 'splice' + object: IObservableArray; + index: number; + removed: T[]; + addedCount: number; + } + + interface IDependencyTree { + id: number; + name: string; + context: any; + dependencies?: IDependencyTree[]; + } + + interface IObserverTree { + id: number; + name: string; + context: any; + observers?: IObserverTree[]; + listeners?: number; // amount of functions manually attached using an .observe method + } + + interface ITransitionEvent { + id: number; + name: string; + context: Object; + state: string; + changed: boolean; + newValue: string; + } +} + +declare module "mobservable" { + var m : IMobservableStatic; + export = m; +} \ No newline at end of file