readme.md
198 lines
| 4.9 KiB
| text/x-minidsrc
|
MarkdownLexer
/ djx / readme.md
cin
|
r65 | # @implab/djx | ||
## 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 | ||||
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: | ||||
* `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. | ||||
* `@bind(...)` - annotations provide an easy way of using standard dojo widget | ||||
attribute bindings. | ||||
### djbase, @djclass | ||||
These two traits provide convenient way of using `dojo/_base/declare` in Typescript | ||||
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 | ||||
information about the base type and even use `super`!. The only caveat of | ||||
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 | ||||
``` | ||||
Let's take a closer look at the `Baz` declaration it uses `djbase` to derive | ||||
from three mixins and the class is decorated with `@djclass` to accomplish the | ||||
declaration and make a real constructor. | ||||
To allow access to the next sibling method (in terms of multiple inheritance) | ||||
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. | ||||
Mixins are declared similar, they also may have the base types although | ||||
the most common case is declaring the mixin without any base classes. To allow | ||||
the mixin to access the next method declare the interface with | ||||
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 | ||||