##// END OF EJS Templates
corrected i18n, css modules to support requirejs optimizer...
cin -
r53:deb0ed6fb680 v1.0.7 default
parent child
Show More
@@ -0,0 +1,8
1 import { bundle } from "../i18n";
2
3 export default bundle({
4 greeting: (name: string) => `Hello, ${name}!`,
5 goodbye: (name: string) => `Bye, ${name}!`
6 }, {
7 ru: () => import("./ru/foo")
8 });
@@ -0,0 +1,6
1 import foo from "../foo";
2
3 export default foo.define({
4 greeting: (name: string) => `ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ${name}`,
5 goodbye: (name: string) => `Пока, ${name}`
6 });
1 NO CONTENT: new file 100644
@@ -1,198 +1,198
1 1 # @implab/djx
2 2
3 3 ## SYNOPSIS
4 4
5 5 ```tsx
6 6 import { djbase, djclass, bind, prototype, AbstractConstructor } from "@implab/djx/declare";
7 7
8 8 import { DjxWidgetBase } from "@implab/djx/tsx/DjxWidgetBase";
9 9 import { createElement } from "@implab/djx/tsx";
10 10
11 11 interface MyWidgetAttrs {
12 12 title: string;
13 13
14 14 counter: number;
15 15 }
16 16
17 17 interface MyWidgetEvents {
18 18 "count-inc": Event;
19 19
20 20 "count-dec": Event;
21 21 }
22 22
23 23
24 24 @djclass
25 25 export class MyWidget extends djbase(
26 26 DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>
27 27 ) {
28 28
29 29 @bind({ node: "titleNode", type: "innerHTML" })
30 30 title = "";
31 31
32 32 @prototype()
33 33 counter = 0;
34 34
35 35 render() {
36 36 const Frame = (props: any) => <div>{props.children}</div>;
37 37 return <div
38 38 className="myWidget"
39 39 tabIndex={3}
40 40 style={ alignContent: "center", border: "1px solid" }
41 41 >
42 42 <h1 data-dojo-attach-point="titleNode"></h1>
43 43 <Frame>
44 44 <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span>
45 45 <span class="down-button" onclick={() => this._onDecClick()}>[-]</span>
46 46 </Frame>
47 47 </div>;
48 48 }
49 49
50 50 _onIncClick(e: MouseEvent) {
51 51 this.emit("count-inc", { bubbles: false });
52 52 }
53 53
54 54 _onDecClick() {
55 55 this.emit("count-dec", { bubbles: false });
56 56 }
57 57 }
58 58
59 59 ```
60 60
61 61 ## DESCRIPTION
62 62
63 This package provides you with tools to glue your good-fellow dojo with modern
64 techniques of building the webapp. The core concept is built around widgets and
65 using .tsx to write it. Here some features:
63 This package provides you with the tools to glue your good-fellow dojo with modern
64 techniques of building the webapp. The core concept is to built around widgets and
65 using .tsx to write it. Here are some features:
66 66
67 67 * `djbase()`, `@djaclass` - traits to declare your classes with `dojo/_base/declare`
68 68 * `@implab/djx/tsx` - traits to build the rendering of your widgets with tsx
69 69 * `DjxWidgetBase` - abstract class which supports tsx markup and
70 70 `data-dojo-attach-*` attributes.
71 * `@bind(...)` - annotations provides an easy way of using standard dojo widget
71 * `@bind(...)` - annotations provide an easy way of using standard dojo widget
72 72 attribute bindings.
73 73
74 74 ### djbase, @djclass
75 75
76 These two traits provides convenient way of using `dojo/_base/declare` in Typescript
76 These two traits provide convenient way of using `dojo/_base/declare` in Typescript
77 77 for declaring your classes.
78 78
79 79 `djbase(...constructors)` - this method accepts a list of constructors in its
80 80 parameters and returns the **fake** base type which then can be used to derive
81 81 your own class. This allows you to provide the Typescript with the correct
82 information about the base type and even use `super`!. The only one caveat of
82 information about the base type and even use `super`!. The only caveat of
83 83 this approach is that you **MUST** decorate your class with `@djclass` annotation.
84 84
85 85 Consider the following example:
86 86
87 87 ```ts
88 88 import { djbase, djclass } from "@implab/djx/declare";
89 89 import { FooMixin } from "./FooMixin";
90 90 import { BarMixin } from "./BarMixin";
91 91 import { BoxMixin } from "./BoxMixin";
92 92
93 93 @djclass
94 94 export class Baz extends djbase(FooMixin, BarMixin, BoxMixin) {
95 95 writeHello(out: string[]) {
96 96 out.push("-> Baz");
97 97
98 98 super.writeHello(out);
99 99
100 100 out.push("<- Baz");
101 101 }
102 102 }
103 103
104 104 ```
105 105
106 106 All mixins are declared like the one below:
107 107
108 108 ```ts
109 109 import { djclass, djbase } from "@implab/djx/declare";
110 110
111 111 interface Super {
112 112 writeHello(out: string[]): void;
113 113
114 114 }
115 115
116 116 @djclass
117 117 export class BarMixin extends djbase<Super>() {
118 118 writeHello(out: string[]) {
119 119 out.push("-> Bar");
120 120
121 121 super.writeHello(out);
122 122
123 123 out.push("<- Bar");
124 124 }
125 125 }
126 126 ```
127 127
128 128 finally create an instance and call the `writeHello` method
129 129
130 130 ```ts
131 131 const baz = new Baz();
132 132
133 133 const data: string[] = [];
134 134 baz.writeHello(data);
135 135
136 136 console.log(data.join("\n"));
137 137
138 138 ```
139 139
140 140 you will get the following output:
141 141
142 142 ```text
143 143 -> Baz
144 144 -> Box
145 145 -> Bar
146 146 -> Foo
147 147 <- Foo
148 148 <- Bar
149 149 <- Box
150 150 <- Baz
151 151 ```
152 152
153 Let's take a closer look to the `Baz` declaration it uses `djbase` to derive
153 Let's take a closer look at the `Baz` declaration it uses `djbase` to derive
154 154 from three mixins and the class is decorated with `@djclass` to accomplish the
155 155 declaration and make a real constructor.
156 156
157 To allow an access to the next sibling method (in terms of multiple inheritance)
157 To allow access to the next sibling method (in terms of multiple inheritance)
158 158 Dojo provides `this.inherited(arguments)` method but this approach leads to the
159 159 problem with 'strict' mode of ES5 and eliminates the type information about a
160 160 calling method. This library solves the problem calling inherited/next method by
161 161 utilizing `super` keyword. Under the hood there are proxy methods generated in
162 162 the prototype of the declared class which make calls to `this.inherited(...)`
163 163 method. This technique is compatible with 'strict' mode.
164 164
165 Mixins are declared the similar, they are also may have the base types although
165 Mixins are declared similar, they also may have the base types although
166 166 the most common case is declaring the mixin without any base classes. To allow
167 the mixin to access the next method you should declare the interface with
167 the mixin to access the next method declare the interface with
168 168 desired methods and use the special form of `djbase<Super>()` without arguments.
169 169
170 170 ### DjxWidgetBase<Attrs, Events>
171 171
172 172 TODO
173 173
174 174 ### Markup (.tsx)
175 175
176 176 Add to your `tsconfig.json` the following options
177 177
178 178 ```json
179 179 {
180 180 "compilerOptions": {
181 181 "types": ["@implab/djx"],
182 182 "experimentalDecorators": true,
183 183 "jsxFactory": "createElement",
184 184 "jsx": "react",
185 185 }
186 186 }
187 187
188 188 ```
189 189
190 190 Import `createElement` into your `.tsx` file
191 191
192 192 ```ts
193 193 import { createElement } from "@implab/djx/tsx";
194 194 ```
195 195
196 196 You are ready to go!
197 197
198 198 TODO
@@ -1,81 +1,82
1 1 import { MapOf } from "@implab/core-amd/interfaces";
2 2 import { isPromise, mixin } from "@implab/core-amd/safe";
3 3 import { locale as sysLocale } from "dojo/_base/kernel";
4 4 import { id as mid } from "module";
5 5 import { TraceSource } from "@implab/core-amd/log/TraceSource";
6 6
7 7 const trace = TraceSource.get(mid);
8 8
9 9 type PromiseOrValue<T> = PromiseLike<T> | T;
10 10 type ResolveCallback<T> = () => PromiseOrValue<T>;
11 11
12 12 trace.debug("Current sysLocale: {0}", sysLocale);
13 13
14 14 function when<T, T2>(value: PromiseOrValue<T>, cb: (v: T) => PromiseOrValue<T2>): PromiseOrValue<T2> {
15 15 return isPromise(value) ?
16 16 value.then(cb) :
17 17 cb(value);
18 18 }
19 19
20 20 function isCallback<T>(v: ResolveCallback<T> | PromiseOrValue<T>): v is ResolveCallback<T> {
21 21 return typeof v === "function";
22 22 }
23 23
24 function defaultResolver(module: string) {
25 return import(module).then(x => x && x.default ? x.default : x);
26 }
27
24 28 function chainObjects<T extends object>(o1: T, o2: T) {
25 29 if (!o1)
26 30 return o2;
27 31 if (!o2)
28 32 return o1;
29 33
30 34 return mixin(Object.create(o1) as T, o2);
31 35 }
32 36
33 37 export class NlsBundle<T extends object> {
34 _locales: MapOf<ResolveCallback<T> | PromiseOrValue<T>>;
35 default: T;
38 private _locales: MapOf<ResolveCallback<T> | PromiseOrValue<T>>;
36 39
37 _cache: MapOf<PromiseOrValue<T>>;
40 private _default: T;
41
42 private _cache: MapOf<PromiseOrValue<T>>;
38 43
39 44 constructor(defNls: T, locales?: MapOf<any>) {
40 this.default = defNls;
45 this._default = defNls;
41 46 this._locales = locales || {};
42 47 this._cache = {};
43 48 }
44 49
45 getLocale(locale: string) {
46 const _loc = locale || sysLocale;
47
48 const locales = new Array<string>();
50 getLocale(locale?: string) {
51 const _loc = locale ?? sysLocale;
49 52
50 _loc.split("-").reduce((a, x) => {
51 a.push(x);
52 locales.unshift(a.join("-"));
53 return a;
54 }, new Array<string>());
55
53 // en-US => ["en", "en-US"]
54 const locales = _loc.split(/-|_/).map((x, i, a) => a.slice(0, i + 1).join("-"));
56 55 return this._resolveLocale(locales);
57 56 }
58 57
59 58 _resolveLocale(locales: string[]): PromiseOrValue<T> {
60 59 if (!locales.length)
61 return this.default;
60 return this._default;
62 61
63 const locale = locales.shift();
64
62 const locale = locales.pop();
65 63 if (!locale)
66 return this._resolveLocale(locales);
64 throw new Error("The locale can't be empty");
67 65
68 66 if (this._cache[locale])
69 67 return this._cache[locale];
70 68
71 let data = this._locales[locale];
72 if (isCallback(data))
73 data = data();
74
69 const data = this._loadPackage(this._locales[locale]);
75 70 const parent = this._resolveLocale(locales);
76 71
77 72 return this._cache[locale] = when(data, x => {
78 73 return when(parent, y => this._cache[locale] = chainObjects(y, x));
79 74 });
80 75 }
76
77 _loadPackage(localeData: any) {
78 if (isCallback(localeData))
79 return when(localeData(), data => data && "default" in data ? data.default : data);
80 return localeData;
81 81 }
82 }
@@ -1,18 +1,18
1 1 import inject from "./dom-inject";
2 2
3 3 interface OnLoad {
4 4 (result?: any): void;
5 5 error(err: any): void;
6 6 }
7 7
8 8 const plugin = {
9 9 load: (id: string, require: Require, cb: OnLoad, config: { isBuild?: boolean }) => {
10 if (config.isBuild) {
10 if (config && config.isBuild) {
11 11 cb();
12 12 } else {
13 13 const url = require.toUrl(id);
14 14 inject.injectStylesheet(url).then(() => cb({ url }), e => cb.error(e));
15 15 }
16 16 }
17 17 };
18 18 export = plugin;
@@ -1,38 +1,38
1 1 import { MapOf } from "@implab/core-amd/interfaces";
2 2 import { NlsBundle } from "./NlsBundle";
3 3 import { isPromise } from "@implab/core-amd/safe";
4 4
5 5 export interface OnLoad {
6 6 (result?: any): void;
7 7 error(err: any): void;
8 8 }
9 9
10 10 export function bundle<T extends object>(nls: T, locales?: MapOf<any>) {
11 11 const nlsBundle = new NlsBundle(nls, locales);
12 12
13 13 const fn = (locale?: string) => {
14 const result = locale ? nlsBundle.getLocale(locale) : nlsBundle.default;
14 const result = nlsBundle.getLocale(locale);
15 15
16 16 if (isPromise(result))
17 17 throw new Error(`The bundle '${locale}' isn't loaded`);
18 18 else
19 19 return result;
20 20 };
21 21
22 22 fn.define = (pack: Partial<T>) => pack;
23 fn.default = nlsBundle.default;
24 23 fn.load = async (id: string, require: Require, cb: OnLoad, config: any) => {
25 24 if (config && config.isBuild) {
26 25 cb();
27 26 } else {
28 27 try {
29 28 await nlsBundle.getLocale(id);
30 29 cb();
31 30 } catch (e) {
31 if(cb.error)
32 32 cb.error(e);
33 33 }
34 34 }
35 35 };
36 36
37 37 return fn;
38 38 }
General Comments 0
You need to be logged in to leave comments. Login now