Add definition for deku (#12557)

* Add definition for deku

* Add deku/tsconfig.json

* Replace Action to any

* Fix test

* Fix exporting

* Rename deku.d.ts -> index.d.ts

* Remove unnecessary reference

* Change to more specific type

* Add private type: Dispatch
* dispatch: Function -> dispatch: Dispatch
* Remove duplicated overloading
* More specific type for createThunkElement

* Fix version

* Change interface Actions -> class Actions

* interface -> class
* Change return type of deku.diff.Actions.case
* Update test

* Change parameter type of deku.dom.update

* dispatch: Function -> dispatch: Dispatch
This commit is contained in:
Sho Fuji 2016-11-10 22:47:55 +09:00 committed by Andy
parent 1b5a083cdf
commit c4afd27a44
3 changed files with 363 additions and 0 deletions

208
deku/deku-tests.ts Normal file
View File

@ -0,0 +1,208 @@
// Example from deku/examples/basic
(function (){
const {h, createApp} = deku
function view(state = { count: 0 }, dispatch: Function){
return (
h('div', {}, [
h('div', {}, 'Counter: ' + state.count),
h('button', {onClick: increment(dispatch)}, 'Increment'),
h('button', {onClick: decrement(dispatch)}, 'Decrement')
])
)
}
function increment(dispatch: Function){
return () => dispatch({
type: 'INCREMENT'
})
}
function decrement(dispatch: Function){
return () => dispatch({
type: 'DECREMENT'
})
}
let render = createApp(document.body)
function main(state: any){
let vnode = view(state, (action: any) => main({ count: 0 }))
render(vnode)
}
main({ count: 0 })
})();
// Example from deku/docs/api/create-app
(function (){
const {createApp, element} = deku
const App = ({ props = { size: 'medium' } }) => {
return element('div', { class: `size-${ props.size }` })
}
const render = createApp(document.body)
render(element(App, { size: 'small' }))
render(element(App, { size: 'large' }))
})();
// Example from deku/docs/api/string
(function (){
const { h } = deku
const html = deku.string.render(h('div', {}, [
h('header'),
h('sidebar'),
h('app'),
]))
})();
// Example from deku/docs/api/element
(function (){
const { element } = deku
// Native elements
element('div', { class: 'greeting' }, [
element('span', {}, ['Hello'])
])
// Components
let App = {
render: ({ props = { name: '' } }) => element('div', {}, `Hello ${ props.name }!`)
}
element(App, { name: 'Tom' })
})();
// deku.createApp
(function (){
const { createApp, element } = deku
let render: Function = createApp(document.body)
render(element('div'))
render = createApp(document.body, (action: any) => {
render(element('div'))
})
render(element('div'))
})();
// deku.dom
(function (){
const { dom, element } = deku
let el: HTMLElement = dom.create(element('div'), '0.0', ()=>{}, {})
const update: (DOMElement: HTMLElement, action: any) => HTMLElement = dom.update(()=>{}, {})
el = update(el, {})
})();
// deku.string
(function (){
const { element } = deku
let html: string = deku.string.render(element('div'))
html = deku.string.render(element('div'), {})
})();
// deku.element
(function (){
const { element } = deku
let v: deku.VirtualElement = element('div')
v = element('div', {})
v = element('div', {}, [])
v = element('div', {}, ['foo', 0, 'bar'])
v = element('div', {}, 'foo')
v = element('div', {}, 0)
v = element('div', {}, 'foo', 'bar')
let Component = {
render({}){
return element('div')
}
}
v = element(Component)
v = element(Component, {})
v = element(Component, {}, [])
})();
// deku.diff
(function (){
const { diff, element } = deku
const { Actions } = diff
let diffs: any[] = diff.diffNode(element('div'), element('span'))
let actions: deku.diff.Actions[] = [
Actions.setAttribute('class', 'foo', 'bar'),
Actions.removeAttribute('foo', {}),
Actions.insertChild({}, 0, '0.0'),
Actions.removeChild(0),
Actions.updateChild(0, []),
Actions.updateChildren([]),
Actions.insertBefore(0),
Actions.replaceNode({}, {}, '0.0'),
Actions.removeNode({}),
Actions.sameNode(),
Actions.updateThunk({}, {}, '0.0')
]
actions.forEach(action => {
Actions.case({
setAttribute: (name: string, value: any, previousValue: any) => {
},
_: () => {
}
}, action)
})
})();
// deku.vnode
(function (){
const { vnode, element } = deku
let v: deku.VirtualElement = vnode.create('div')
v = vnode.createTextElement('foo')
const Component = {
render({}){
return element('div')
}
}
v = vnode.createThunkElement(Component.render, '', Component, [], {})
v = vnode.createEmptyElement()
let b: boolean = vnode.isThunk(v)
b = vnode.isText(v)
b = vnode.isEmpty(v)
b = vnode.isSameThunk(v, v)
let path: string = vnode.createPath('0', '1', '2', '3')
path = vnode.createPath(0, 1, 2, 3)
})();

136
deku/index.d.ts vendored Normal file
View File

@ -0,0 +1,136 @@
// Type definitions for deku v2.0
// Project: https://github.com/anthonyshort/deku
// Definitions by: Sho Fuji <https://github.com/pocka/>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
export = deku;
export as namespace deku;
declare namespace deku {
interface VirtualElement {
type: string;
}
/**
* Create a DOM renderer using a container element.
* Everything will be rendered inside of that container.
* Returns a function that accepts new state that can replace what is currently rendered.
*/
function createApp(el: HTMLElement, dispatch?: Dispatch): Render;
namespace dom {
/**
* Create a real DOM element from a virtual element, recursively looping down.
* When it finds custom elements it will render them, cache them, and keep going,
* so they are treated like any other native element.
*/
function create<C>(vnode: VirtualElement, path: string, dispatch: Dispatch, context: C): HTMLElement;
/**
* Modify a DOM element given an array of actions.
*/
function update<C, A>(dispatch: Dispatch, context: C): (DOMElement: HTMLElement, action: A) => HTMLElement;
}
namespace string {
/**
* Render a virtual element to a string. You can pass in an option state context object that will be given to all components.
*/
function render(vnode: VirtualElement): string;
function render<C>(vnode: VirtualElement, context: C): string;
}
/**
* This function lets us create virtual nodes using a simple syntax.
* It is compatible with JSX transforms so you can use JSX to write nodes that will compile to this function.
*/
function element(type: string): VirtualElement;
function element<A>(type: string, attributes: A, ...children: any[]): VirtualElement;
function element(type: Thunk): VirtualElement;
function element<A>(type: Thunk, attributes: A, ...children: any[]): VirtualElement;
var h: typeof element;
namespace diff {
/**
* Compare two virtual nodes and return an array of changes to turn the left into the right.
*/
function diffNode(prevNode: VirtualElement, nextNode: VirtualElement): any[];
class Actions {
private _keys: string[];
private _name: string;
static setAttribute(a: string, b: any, c: any): Actions;
static removeAttribute(a: string, b: any): Actions;
static insertChild(a: any, b: number, c: string): Actions;
static removeChild(a: number): Actions;
static updateChild(a: number, b: any[]): Actions;
static updateChildren(a: any[]): Actions;
static insertBefore(a: number): Actions;
static replaceNode(a: any, b: any, c: string): Actions;
static removeNode(a: any): Actions;
static sameNode(): Actions;
static updateThunk(a: any, b: any, c: string): Actions;
static case(pat: any, action: Actions): any;
}
}
namespace vnode {
var create: typeof element;
/**
* Text nodes are stored as objects to keep things simple
*/
function createTextElement(text: string): VirtualElement;
/**
* Lazily-rendered virtual nodes
*/
function createThunkElement<P, T, O>(fn: (model: Model) => VirtualElement, key: string, props: P, children: T[], options: O): VirtualElement;
function createEmptyElement(): VirtualElement;
function isThunk(vnode: VirtualElement): boolean;
function isText(vnode: VirtualElement): boolean;
function isEmpty(vnode: VirtualElement): boolean;
function isSameThunk(prevNode: VirtualElement, nextNode: VirtualElement): boolean;
// function isValidAttribute<A>(value: A): boolean;
/**
* Create a node path, eg. (23,5,2,4) => '23.5.2.4'
*/
function createPath(...paths: (number|string)[]): string;
}
}
interface Model {
props?: any,
children?: any[],
path?: string,
dispatch?: Dispatch,
context?: any
}
interface Component {
render: (model: Model) => deku.VirtualElement;
onCreate?: (model: Model) => any;
onUpdate?: (model: Model) => any;
onRemove?: (model: Model) => any;
}
/**
* Thunk object passed to `element`
*/
type Thunk = Component | ((model: Model) => deku.VirtualElement);
type Render = (vnode: deku.VirtualElement, context?: any) => void;
type Dispatch = (action: any) => any;

19
deku/tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"noImplicitAny": true,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"deku-tests.ts"
]
}