readme.md
198 lines
| 4.9 KiB
| text/x-minidsrc
|
MarkdownLexer
|
|
r4 | # @implab/djx | ||
|
|
r41 | ## SYNOPSIS | ||
| ```tsx | ||||
| import { djbase, djclass, bind, prototype, AbstractConstructor } from "@implab/djx/declare"; | ||||
| import { DjxWidgetBase } from "@implab/djx/tsx/DjxWidgetBase"; | ||||
| import { createElement } from "@implab/djx/tsx"; | ||||
| interface MyWidgetAttrs { | ||||
| title: string; | ||||
| counter: number; | ||||
| } | ||||
| interface MyWidgetEvents { | ||||
| "count-inc": Event; | ||||
| "count-dec": Event; | ||||
| } | ||||
| @djclass | ||||
| export class MyWidget extends djbase( | ||||
| DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>> | ||||
| ) { | ||||
| @bind({ node: "titleNode", type: "innerHTML" }) | ||||
| title = ""; | ||||
| @prototype() | ||||
| counter = 0; | ||||
| render() { | ||||
| const Frame = (props: any) => <div>{props.children}</div>; | ||||
| return <div | ||||
| className="myWidget" | ||||
| tabIndex={3} | ||||
| style={ alignContent: "center", border: "1px solid" } | ||||
| > | ||||
| <h1 data-dojo-attach-point="titleNode"></h1> | ||||
| <Frame> | ||||
| <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span> | ||||
| <span class="down-button" onclick={() => this._onDecClick()}>[-]</span> | ||||
| </Frame> | ||||
| </div>; | ||||
| } | ||||
| _onIncClick(e: MouseEvent) { | ||||
| this.emit("count-inc", { bubbles: false }); | ||||
| } | ||||
| _onDecClick() { | ||||
| this.emit("count-dec", { bubbles: false }); | ||||
| } | ||||
| } | ||||
| ``` | ||||
| ## DESCRIPTION | ||||
|
|
r53 | This package provides you with the tools to glue your good-fellow dojo with modern | ||
| techniques of building the webapp. The core concept is to built around widgets and | ||||
| using .tsx to write it. Here are some features: | ||||
|
|
r41 | |||
| * `djbase()`, `@djaclass` - traits to declare your classes with `dojo/_base/declare` | ||||
| * `@implab/djx/tsx` - traits to build the rendering of your widgets with tsx | ||||
| * `DjxWidgetBase` - abstract class which supports tsx markup and | ||||
| `data-dojo-attach-*` attributes. | ||||
|
|
r53 | * `@bind(...)` - annotations provide an easy way of using standard dojo widget | ||
|
|
r41 | attribute bindings. | ||
| ### djbase, @djclass | ||||
|
|
r53 | These two traits provide convenient way of using `dojo/_base/declare` in Typescript | ||
|
|
r41 | for declaring your classes. | ||
| `djbase(...constructors)` - this method accepts a list of constructors in its | ||||
| parameters and returns the **fake** base type which then can be used to derive | ||||
| your own class. This allows you to provide the Typescript with the correct | ||||
|
|
r53 | information about the base type and even use `super`!. The only caveat of | ||
|
|
r41 | this approach is that you **MUST** decorate your class with `@djclass` annotation. | ||
| Consider the following example: | ||||
| ```ts | ||||
| import { djbase, djclass } from "@implab/djx/declare"; | ||||
| import { FooMixin } from "./FooMixin"; | ||||
| import { BarMixin } from "./BarMixin"; | ||||
| import { BoxMixin } from "./BoxMixin"; | ||||
| @djclass | ||||
| export class Baz extends djbase(FooMixin, BarMixin, BoxMixin) { | ||||
| writeHello(out: string[]) { | ||||
| out.push("-> Baz"); | ||||
| super.writeHello(out); | ||||
| out.push("<- Baz"); | ||||
| } | ||||
| } | ||||
| ``` | ||||
| All mixins are declared like the one below: | ||||
| ```ts | ||||
| import { djclass, djbase } from "@implab/djx/declare"; | ||||
| interface Super { | ||||
| writeHello(out: string[]): void; | ||||
| } | ||||
| @djclass | ||||
| export class BarMixin extends djbase<Super>() { | ||||
| writeHello(out: string[]) { | ||||
| out.push("-> Bar"); | ||||
| super.writeHello(out); | ||||
| out.push("<- Bar"); | ||||
| } | ||||
| } | ||||
| ``` | ||||
| finally create an instance and call the `writeHello` method | ||||
| ```ts | ||||
| const baz = new Baz(); | ||||
| const data: string[] = []; | ||||
| baz.writeHello(data); | ||||
| console.log(data.join("\n")); | ||||
| ``` | ||||
| you will get the following output: | ||||
| ```text | ||||
| -> Baz | ||||
| -> Box | ||||
| -> Bar | ||||
| -> Foo | ||||
| <- Foo | ||||
| <- Bar | ||||
| <- Box | ||||
| <- Baz | ||||
| ``` | ||||
|
|
r53 | Let's take a closer look at the `Baz` declaration it uses `djbase` to derive | ||
|
|
r41 | from three mixins and the class is decorated with `@djclass` to accomplish the | ||
| declaration and make a real constructor. | ||||
|
|
r53 | To allow access to the next sibling method (in terms of multiple inheritance) | ||
|
|
r41 | Dojo provides `this.inherited(arguments)` method but this approach leads to the | ||
| problem with 'strict' mode of ES5 and eliminates the type information about a | ||||
| calling method. This library solves the problem calling inherited/next method by | ||||
| utilizing `super` keyword. Under the hood there are proxy methods generated in | ||||
| the prototype of the declared class which make calls to `this.inherited(...)` | ||||
| method. This technique is compatible with 'strict' mode. | ||||
|
|
r53 | Mixins are declared similar, they also may have the base types although | ||
|
|
r41 | the most common case is declaring the mixin without any base classes. To allow | ||
|
|
r53 | the mixin to access the next method declare the interface with | ||
|
|
r41 | desired methods and use the special form of `djbase<Super>()` without arguments. | ||
| ### DjxWidgetBase<Attrs, Events> | ||||
| TODO | ||||
| ### Markup (.tsx) | ||||
| Add to your `tsconfig.json` the following options | ||||
| ```json | ||||
| { | ||||
| "compilerOptions": { | ||||
| "types": ["@implab/djx"], | ||||
| "experimentalDecorators": true, | ||||
| "jsxFactory": "createElement", | ||||
| "jsx": "react", | ||||
| } | ||||
| } | ||||
| ``` | ||||
| Import `createElement` into your `.tsx` file | ||||
| ```ts | ||||
| import { createElement } from "@implab/djx/tsx"; | ||||
| ``` | ||||
| You are ready to go! | ||||
| TODO | ||||
