##// END OF EJS Templates
fixed NlsBundle locale package loading...
cin -
r112:2ccfaae984e9 v1.4.4 default
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,58 +1,60
1 1 import { MapOf, PromiseOrValue } from "@implab/core-amd/interfaces";
2 2 import { argumentNotEmptyString, isPromise, mixin } from "@implab/core-amd/safe";
3 3
4 4 export type LocaleProvider<T> = () => PromiseOrValue<T | { default: T }>;
5 5
6 6 function when<T, T2>(value: PromiseOrValue<T>, cb: (v: T) => PromiseOrValue<T2>): PromiseOrValue<T2> {
7 7 return isPromise(value) ?
8 8 value.then(cb) :
9 9 cb(value);
10 10 }
11 11
12 const chainObjects = <T extends object, T2 extends object>(o1: T, o2: T2) =>
13 mixin(Object.create(o1) as T, o2);
12 const loadPackage = <T extends object>(localeData: LocaleProvider<Partial<T>> | undefined) =>
13 localeData ?
14 when(localeData(), data => data && "default" in data ? data.default : data) :
15 undefined;
16
17 const chainObjects = <T extends object>(o1: T, o2: Partial<T> | undefined): T =>
18 o2 ? mixin(Object.create(o1) as T, o2) : o1;
19
14 20 export class NlsBundle<T extends object> {
15 21 private readonly _locales: MapOf<LocaleProvider<Partial<T>>>;
16 22
17 23 private readonly _default: T;
18 24
19 25 private _cache: MapOf<PromiseOrValue<T>>;
20 26
21 27 constructor(defNls: T, locales?: MapOf<LocaleProvider<Partial<T>>>) {
22 28 this._default = defNls;
23 29 this._locales = locales || {};
24 30 this._cache = {};
25 31 }
26 32
27 33 getLocale(locale: string) {
28 34 argumentNotEmptyString(locale, "locale");
29 35 const _loc = locale;
30 36
31 37 // en-US => ["en", "en-US"]
32 38 const locales = _loc.split(/-|_/).map((x, i, a) => a.slice(0, i + 1).join("-"));
33 39 return this._resolveLocale(locales);
34 40 }
35 41
36 42 _resolveLocale(locales: string[]): PromiseOrValue<T> {
37 43 if (!locales.length)
38 44 return this._default;
39 45
40 46 const locale = locales.pop();
41 47 if (!locale)
42 48 throw new Error("The locale can't be empty");
43 49
44 50 if (this._cache[locale])
45 51 return this._cache[locale];
46 52
47 const data = this._loadPackage(this._locales[locale]);
53 const data = loadPackage(this._locales[locale]);
48 54 const parent = this._resolveLocale(locales);
49 55
50 56 return this._cache[locale] = when(data, x => {
51 57 return when(parent, y => this._cache[locale] = chainObjects(y, x));
52 58 });
53 59 }
54
55 _loadPackage(localeData: LocaleProvider<Partial<T>>) {
56 return when(localeData(), data => data && "default" in data ? data.default : data);
57 }
58 60 }
@@ -1,7 +1,11
1 import { FunctionRendition } from "./FunctionRendition";
2 import { getItemDom } from "./render";
3
1 4 /** Special functional component used to create a document fragment */
2 export function DjxFragment({ children }: { children?: Node | Node[] }) {
3 const fragment = document.createDocumentFragment();
4 if (children)
5 (children instanceof Array ? children : [children]).forEach(child => fragment.appendChild(child));
6 return fragment;
7 } No newline at end of file
5 export const DjxFragment = ({ children }: { children?: unknown | unknown[] }) =>
6 new FunctionRendition(() => {
7 const fragment = document.createDocumentFragment();
8 if (children)
9 (children instanceof Array ? children : [children]).map(getItemDom).forEach(child => fragment.appendChild(child));
10 return fragment;
11 }); No newline at end of file
@@ -1,131 +1,135
1 1 import { djbase, djclass } from "../declare";
2 2 import _WidgetBase = require("dijit/_WidgetBase");
3 3 import _AttachMixin = require("dijit/_AttachMixin");
4 import { Rendition, isNode, isElementNode } from "./traits";
4 import { isNode, isElementNode } from "./traits";
5 5 import registry = require("dijit/registry");
6 6 import on = require("dojo/on");
7 7 import { Scope } from "./Scope";
8 8 import { render } from "./render";
9 import { isNull } from "@implab/core-amd/safe";
9 10
10 11 // type Handle = dojo.Handle;
11 12
12 13 export interface EventArgs {
13 14 bubbles?: boolean;
14 15
15 16 cancelable?: boolean;
16 17
17 18 composed?: boolean;
18 19 }
19 20
20 21 // eslint-disable-next-line @typescript-eslint/no-unused-vars
21 22 export interface DjxWidgetBase<Attrs = object, Events extends { [name in keyof Events]: Event } = object> extends
22 23 _WidgetBase<Events> {
23 24
24 25 /** This property is declared only for type inference to work, it is never assigned
25 26 * and should not be used.
26 27 */
27 28 readonly _eventMap: Events & GlobalEventHandlersEventMap;
28 29
29 30 /** The list of pairs of event and method names. When the widget is created all methods from
30 31 * this list will be connected to corresponding events.
31 32 *
32 33 * This property is maintained in the prototype
33 34 */
34 35 _eventHandlers: Array<{
35 36 eventName: string,
36 37 handlerMethod: string;
37 38 }>;
38 39 }
39 40
40 41 type _super = {
41 42 startup(): void;
42 43
43 44 destroy(preserveDom?: boolean): void;
44 45 };
45 46
46 47 @djclass
47 48 // eslint-disable-next-line @typescript-eslint/no-unused-vars
48 49 export abstract class DjxWidgetBase<Attrs = object, Events = object> extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) {
49 50 private readonly _scope = new Scope();
50 51
51 52 buildRendering() {
52 53 const node = render(this.render(), this._scope);
53 54 if (!isElementNode(node))
54 55 throw new Error("The render method must return a single DOM element");
55 56 this.domNode = node as HTMLElement;
56 57
57 58 super.buildRendering();
58 59
59 60 // now we should get assigned data-dojo-attach-points
60 61 // place the contents of the original srcNode to the containerNode
61 62 const src = this.srcNodeRef;
62 63 const dest = this.containerNode;
63 64
64 65 // the donNode is constructed now we need to connect event handlers
65 66 this._connectEventHandlers();
66 67
67 68 if (src && dest) {
68 69 while (src.firstChild)
69 70 dest.appendChild(src.firstChild);
70 71 }
71 72 }
72 73
73 abstract render(): Rendition;
74 abstract render(): JSX.Element;
74 75
75 76 private _connectEventHandlers() {
76 77 if (this._eventHandlers)
77 78 this._eventHandlers.forEach(({ eventName, handlerMethod }) => {
78 79 const handler = this[handlerMethod as keyof this];
79 80 if (typeof handler === "function")
80 81 on(this.domNode, eventName, handler.bind(this) as (...args: unknown[]) => unknown);
81 82 });
82 83 }
83 84
84 85 _processTemplateNode<T extends (Element | Node | _WidgetBase)>(
85 86 baseNode: T,
86 getAttrFunc: (baseNode: T, attr: string) => string,
87 getAttrFunc: (baseNode: T, attr: string) => string | undefined,
87 88 // tslint:disable-next-line: ban-types
88 89 attachFunc: (node: T, type: string, func?: (...args: unknown[]) => unknown) => dojo.Handle
89 90 ): boolean {
90 91 if (isNode(baseNode)) {
91 92 const w = registry.byNode(baseNode);
92 93 if (w) {
93 94 // from dijit/_WidgetsInTemplateMixin
94 95 this._processTemplateNode(w,
95 (n, p) => String(n.get(p as keyof typeof n)), // callback to get a property of a widget
96 (n, p) => {
97 const v = n.get(p as keyof typeof n);
98 return isNull(v) ? undefined : String(v);
99 }, // callback to get a property of a widget
96 100 (widget, type, callback) => {
97 101 if (!callback)
98 102 throw new Error("The callback must be specified");
99 103
100 104 // callback to do data-dojo-attach-event to a widget
101 105 if (type in widget) {
102 106 // back-compat, remove for 2.0
103 107 return widget.connect(widget, type, callback as EventListener);
104 108 } else {
105 109 // 1.x may never hit this branch, but it's the default for 2.0
106 110 return widget.on(type as keyof GlobalEventHandlersEventMap, callback);
107 111 }
108 112
109 113 });
110 114 // don't process widgets internals
111 115 return false;
112 116 }
113 117 }
114 118 // eslint-disable-next-line @typescript-eslint/ban-types
115 return super._processTemplateNode(baseNode, getAttrFunc, attachFunc as (node: T, type: string, func?: Function) => dojo.Handle);
119 return super._processTemplateNode(baseNode, getAttrFunc as (baseNode: T, attr: string) => string, attachFunc as (node: T, type: string, func?: Function) => dojo.Handle);
116 120 }
117 121
118 122 /** Starts current widget and all its supporting widgets (placed outside
119 123 * `containerNode`) and child widgets (placed inside `containerNode`)
120 124 */
121 125 startup() {
122 126 // startup supporting widgets
123 127 registry.findWidgets(this.domNode, this.containerNode).forEach(w => w.startup());
124 128 super.startup();
125 129 }
126 130
127 131 destroy(preserveDom?: boolean) {
128 132 this._scope.destroy();
129 133 super.destroy(preserveDom);
130 134 }
131 135 }
@@ -1,70 +1,70
1 1 import { isPlainObject, DojoNodePosition, Rendition, isDocumentFragmentNode, placeAt, collectNodes, isMounted, startupWidgets } from "./traits";
2 2
3 3 export abstract class RenditionBase<TNode extends Node> implements Rendition<TNode> {
4 4 private _attrs = {};
5 5
6 6 private _children: unknown[] = [];
7 7
8 8 private _created = false;
9 9
10 10 visitNext(v: unknown) {
11 11 if (this._created)
12 12 throw new Error("The Element is already created");
13 13
14 14 if (v === null || v === undefined || typeof v === "boolean")
15 15 // skip null, undefined, booleans ( this will work: {value && <span>{value}</span>} )
16 16 return;
17 17
18 18 if (isPlainObject(v)) {
19 19 this._attrs = {... this._attrs, ...v};
20 20 } else if (v instanceof Array) {
21 21 v.forEach(x => this.visitNext(x));
22 22 } else {
23 23 this._children.push(v);
24 24 }
25 25 }
26 26
27 27 ensureCreated() {
28 28 if (!this._created) {
29 29 this._create(this._attrs, this._children);
30 30 this._children = [];
31 31 this._attrs = {};
32 32 this._created = true;
33 33 }
34 34 }
35 35
36 36 /** Is rendition was instantiated to the DOM node */
37 37 isCreated() {
38 38 return this._created;
39 39 }
40 40
41 41 /** Creates DOM node if not created. No additional actions are taken. */
42 42 getDomNode() {
43 43 this.ensureCreated();
44 44 return this._getDomNode();
45 45 }
46 46
47 47 /** Creates DOM node if not created, places it to the specified position
48 48 * and calls startup() method for all widgets contained by this node.
49 49 *
50 50 * @param {string | Node} refNode The reference node where the created
51 51 * DOM should be placed.
52 52 * @param {DojoNodePosition} position Optional parameter, specifies the
53 53 * position relative to refNode. Default is "last" (i.e. last child).
54 54 */
55 55 placeAt(refNode: string | Node, position: DojoNodePosition = "last") {
56 56 const domNode = this.getDomNode();
57 57
58 58 const startupPending = isDocumentFragmentNode(domNode) ? collectNodes(domNode.childNodes) : [domNode];
59 59
60 60 placeAt(domNode, refNode, position);
61 61
62 if (isMounted(startupPending[0]))
62 if (startupPending.length && isMounted(startupPending[0]))
63 63 startupPending.forEach(n => startupWidgets(n));
64 64
65 65 }
66 66
67 67 protected abstract _create(attrs: object, children: unknown[]): void;
68 68
69 69 protected abstract _getDomNode(): TNode;
70 70 }
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now