arrmatura 4.2.2 → 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -25
- package/commons/auth/UserService.ts +107 -0
- package/commons/auth/index.ts +4 -0
- package/commons/auth/user.xml +66 -0
- package/commons/components/buttons.xml +23 -0
- package/commons/components/index.ts +13 -0
- package/commons/components/inputs.xml +33 -0
- package/commons/components/layouts.xml +77 -0
- package/commons/components/media.xml +21 -0
- package/commons/components/modal.xml +37 -0
- package/commons/components/navigation.xml +59 -0
- package/commons/components/popups.xml +12 -0
- package/commons/components/searchbar.xml +40 -0
- package/commons/components/table.xml +120 -0
- package/commons/components/toasts.xml +13 -0
- package/commons/components/viewport.xml +48 -0
- package/commons/forms/FieldItem.xml +20 -0
- package/commons/forms/FormController.ts +73 -0
- package/commons/forms/Item.ts +68 -0
- package/commons/forms/ItemCollection.ts +152 -0
- package/commons/forms/ItemController.ts +62 -0
- package/commons/forms/MetadataService.ts +45 -0
- package/commons/forms/MultiEnumField.xml +44 -0
- package/commons/forms/MultivalueController.ts +49 -0
- package/commons/forms/SmartareaField.xml +51 -0
- package/commons/forms/SuggestionController.ts +80 -0
- package/commons/forms/SuggestionField.xml +28 -0
- package/commons/forms/fields.xml +138 -0
- package/commons/forms/form.xml +37 -0
- package/commons/forms/index.ts +29 -0
- package/commons/index.ts +6 -0
- package/commons/pipes/index.ts +21 -0
- package/commons/pipes/showCounts.ts +16 -0
- package/commons/services/ErrorHandlingService.ts +11 -0
- package/commons/services/FirebaseService.ts +185 -0
- package/commons/services/GSheetsService.ts +92 -0
- package/commons/services/HeartbeatService.ts +35 -0
- package/commons/services/Invoke.ts +13 -0
- package/commons/services/JsonpService.ts +18 -0
- package/commons/services/Loader.ts +17 -0
- package/commons/services/LocalStorage.ts +46 -0
- package/commons/services/NavigationService.ts +55 -0
- package/commons/services/Service.ts +68 -0
- package/commons/services/ServiceWorker.ts +111 -0
- package/commons/services/ToastService.ts +49 -0
- package/commons/services/index.ts +14 -0
- package/core/Arrmatura.ts +79 -0
- package/core/CNode.ts +112 -0
- package/core/Component.ts +431 -0
- package/core/Platform.ts +3 -0
- package/core/cnodes/composition.ts +100 -0
- package/core/cnodes/conditionals.ts +51 -0
- package/core/cnodes/elementary.ts +45 -0
- package/core/cnodes/iterations.ts +94 -0
- package/core/cnodes/routing.ts +25 -0
- package/core/compileNode.ts +39 -0
- package/core/expression.ts +73 -0
- package/core/index.ts +2 -0
- package/core/register.ts +43 -0
- package/core/utils.ts +25 -0
- package/core-web/WebPlatform.ts +25 -0
- package/core-web/attributes.ts +197 -0
- package/core-web/elements.ts +64 -0
- package/core-web/index.ts +19 -0
- package/core-web/reflow.ts +30 -0
- package/core-web/type.ts +19 -0
- package/docs/index.js +191 -4077
- package/lib/arrayGroupBy.ts +19 -0
- package/lib/arraySortBy.ts +49 -0
- package/lib/arrayToObject.ts +18 -0
- package/lib/codus.ts +25 -0
- package/lib/consts.ts +3 -0
- package/lib/createListeners.ts +12 -0
- package/lib/curry.ts +8 -0
- package/lib/dateFormat.ts +69 -0
- package/lib/dateOf.ts +31 -0
- package/lib/dateParseISO8601.ts +27 -0
- package/lib/fx.ts +62 -0
- package/lib/guid.ts +7 -0
- package/lib/index.ts +19 -0
- package/lib/l10n.ts +40 -0
- package/lib/nextId.ts +9 -0
- package/lib/noop.ts +8 -0
- package/lib/obj.ts +62 -0
- package/lib/predicat.ts +26 -0
- package/lib/resources.ru.ts +20 -0
- package/lib/resources.ts +20 -0
- package/lib/scalar.ts +25 -0
- package/lib/str.ts +76 -0
- package/lib/urls.ts +91 -0
- package/lib/xml.ts +117 -0
- package/package.json +17 -8
- package/docs/index.js.map +0 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { arrayToObject } from 'arrayToObject';
|
|
2
|
+
import { urlAppendParams } from 'urls';
|
|
3
|
+
import { JsonpService } from './JsonpService';
|
|
4
|
+
import { LocalStorage } from './LocalStorage';
|
|
5
|
+
import { Service } from './Service';
|
|
6
|
+
|
|
7
|
+
export class GSheetsService extends Service {
|
|
8
|
+
url: string = '';
|
|
9
|
+
kind: string = 'sheet';
|
|
10
|
+
remote = new JsonpService();
|
|
11
|
+
persistence: 'local' | 'transient' = 'transient';
|
|
12
|
+
local?: LocalStorage;
|
|
13
|
+
|
|
14
|
+
init() {
|
|
15
|
+
this.local = new LocalStorage(this.persistence);
|
|
16
|
+
return this.syncAll();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
syncAll() {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
const { since, items: items0 = {} } = this.local?.get(this.kind) || { since: 0, items: {} };
|
|
22
|
+
return !this.url
|
|
23
|
+
? null
|
|
24
|
+
: {
|
|
25
|
+
isLoading: true,
|
|
26
|
+
'*': fetch(urlAppendParams(this.url, { since }), { redirect: 'follow' })
|
|
27
|
+
.then((res) => res.json())
|
|
28
|
+
.then(({ items: items1, error }) => {
|
|
29
|
+
if (error) throw error;
|
|
30
|
+
|
|
31
|
+
this.log('sync' + this.kind, now, items1);
|
|
32
|
+
const items = { ...items0, ...arrayToObject(items1) };
|
|
33
|
+
this.local?.set(this.kind, { since: now, items });
|
|
34
|
+
return { items, isLoading: false };
|
|
35
|
+
})
|
|
36
|
+
.catch((err) => {
|
|
37
|
+
this.toast({ mode: 'error', message: 'Ошибка: ' + JSON.stringify(err?.message || err || 'unknown') });
|
|
38
|
+
return { isLoading: false };
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getAll() {
|
|
44
|
+
const { items } = this.local?.get(this.kind) || {};
|
|
45
|
+
return items;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getItems() {
|
|
49
|
+
let items = this.getAll();
|
|
50
|
+
return Object.values(items || {});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async update(delta: Delta) {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const { since, items: items0 = [] } = this.local?.get(this.kind) || { since: 0, items: {} };
|
|
56
|
+
|
|
57
|
+
this.log('update', delta);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
busy: true,
|
|
61
|
+
'*': fetch(this.url, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
mode: 'cors',
|
|
64
|
+
redirect: 'follow',
|
|
65
|
+
body: JSON.stringify({ action: 'upsert', delta }),
|
|
66
|
+
})
|
|
67
|
+
.then((res) => res.json())
|
|
68
|
+
.then(({ error, ...item }) => {
|
|
69
|
+
if (error) throw error;
|
|
70
|
+
// items: items1 }
|
|
71
|
+
this.log('update sync' + this.kind, now, item.id);
|
|
72
|
+
const items = { ...items0, [item.id]: item };
|
|
73
|
+
this.toast('Сохранено успешно');
|
|
74
|
+
|
|
75
|
+
this.local?.set(this.kind, { since, items });
|
|
76
|
+
return { busy: false }; //items,
|
|
77
|
+
})
|
|
78
|
+
.catch((err) => {
|
|
79
|
+
this.toast({ mode: 'error', message: 'Ошибка: update ' + JSON.stringify(err?.message || err || 'unknown') });
|
|
80
|
+
return { busy: false };
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async onUpsert(data) {
|
|
86
|
+
return this.update(data);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async onDelete({ id }) {
|
|
90
|
+
return this.update({ [id]: { status: 'deleted' } });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class HeartbeatService {
|
|
2
|
+
interval: any;
|
|
3
|
+
watcher?: Procedure;
|
|
4
|
+
|
|
5
|
+
constructor(watcher?: Procedure) {
|
|
6
|
+
this.setWatcher(watcher);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
setWatcher(watcher?: Procedure) {
|
|
10
|
+
this.watcher = watcher;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
startWatch() {
|
|
14
|
+
if (!this.interval) {
|
|
15
|
+
this.interval = setInterval(() => {
|
|
16
|
+
this.watcher?.();
|
|
17
|
+
}, 1000);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
stopWatch() {
|
|
22
|
+
if (this.interval) {
|
|
23
|
+
clearInterval(this.interval);
|
|
24
|
+
this.interval = 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setActive(active: boolean) {
|
|
29
|
+
if (!active) {
|
|
30
|
+
this.stopWatch();
|
|
31
|
+
} else {
|
|
32
|
+
this.startWatch();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { urlAppendParams } from 'urls';
|
|
2
|
+
|
|
3
|
+
let count = 0;
|
|
4
|
+
|
|
5
|
+
export class JsonpService {
|
|
6
|
+
fetch(url: string) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const jsonp: string = 'jsonp_' + ++count;
|
|
9
|
+
const a = window.document.createElement('script');
|
|
10
|
+
a.async = true;
|
|
11
|
+
a.onerror = reject;
|
|
12
|
+
a.src = urlAppendParams(url, { jsonp });
|
|
13
|
+
window[jsonp] = resolve;
|
|
14
|
+
const m = window.document.getElementsByTagName('body')[0];
|
|
15
|
+
m?.parentNode?.insertBefore(a, m);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class Loader {
|
|
2
|
+
init() {
|
|
3
|
+
this.reload();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
setTrigger(val) {
|
|
7
|
+
this.trigger = val;
|
|
8
|
+
this.reload();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
reload() {
|
|
12
|
+
const { from, data, into } = this;
|
|
13
|
+
if (from && into) {
|
|
14
|
+
from({ data, callback: (error, result) => into({ error, ...result }) });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const parse = (s: string | null) => (s ? JSON.parse(s) : undefined);
|
|
2
|
+
|
|
3
|
+
class MemoryStore {
|
|
4
|
+
map = new Map<string, string>();
|
|
5
|
+
|
|
6
|
+
getItem(key: string) {
|
|
7
|
+
return this.map.get(key) || null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
setItem(key: string, value: string): void {
|
|
11
|
+
this.map.set(key, value);
|
|
12
|
+
}
|
|
13
|
+
removeItem(key: string): void {
|
|
14
|
+
this.map.delete(key);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class LocalStorage {
|
|
19
|
+
cache: Hash = {};
|
|
20
|
+
storage: MemoryStore | Storage;
|
|
21
|
+
|
|
22
|
+
constructor(mode?: 'local' | 'transient') {
|
|
23
|
+
this.storage = mode === 'local' ? window.localStorage : new MemoryStore();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get(key: string) {
|
|
27
|
+
return this.cache[key] || (this.cache[key] = parse(this.storage.getItem(key)));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
set(key: string, val: unknown) {
|
|
31
|
+
this.cache[key] = val;
|
|
32
|
+
if (val) {
|
|
33
|
+
this.storage.setItem(key, JSON.stringify(val));
|
|
34
|
+
} else {
|
|
35
|
+
this.storage.removeItem(key);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
assign(delta: Delta) {
|
|
40
|
+
Object.entries(delta).forEach(([key, val = null]) => this.set(key, val));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
transform(key: string, fn: Pipe) {
|
|
44
|
+
return this.set(key, fn(this.get(key)));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { urlParse, urlStringify } from 'urls';
|
|
2
|
+
import { Service } from './Service';
|
|
3
|
+
|
|
4
|
+
export class NavigationService extends Service {
|
|
5
|
+
prevHash: string = '';
|
|
6
|
+
host: any;
|
|
7
|
+
path: any;
|
|
8
|
+
params: any;
|
|
9
|
+
|
|
10
|
+
init() {
|
|
11
|
+
this.prevHash;
|
|
12
|
+
window.addEventListener('hashchange', () => this.hashchange());
|
|
13
|
+
setTimeout(() => this.hashchange(), 0);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
hashchange() {
|
|
17
|
+
const hash = window.location.hash.slice(1);
|
|
18
|
+
if (hash[0] === '/' && hash !== this.prevHash) {
|
|
19
|
+
this.emit('this.hash', { value: hash.slice(1) });
|
|
20
|
+
this.prevHash = hash;
|
|
21
|
+
} else if (!this.prevHash) {
|
|
22
|
+
this.emit('this.hash', { value: 'main' });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
done() {
|
|
27
|
+
window.removeEventListener('hashchange', this.hashchange);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setHash(d) {
|
|
31
|
+
const { host, path = ['*'], params } = urlParse('/' + d);
|
|
32
|
+
const state = {
|
|
33
|
+
host: (!host || host === '*' ? this.host : host) || 'main',
|
|
34
|
+
path: path[0] === '*' ? this.path : path,
|
|
35
|
+
params: params.reset ? { ...params, reset: null } : { ...this.params, ...params },
|
|
36
|
+
};
|
|
37
|
+
window.location.hash = this.prevHash = urlStringify(state).slice(1);
|
|
38
|
+
this.up(state);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getRoute() {
|
|
42
|
+
return {
|
|
43
|
+
host: this.host,
|
|
44
|
+
path: this.path,
|
|
45
|
+
params: this.params,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
onHash({ value }) {
|
|
50
|
+
this.setHash('/' + value);
|
|
51
|
+
}
|
|
52
|
+
onParams(params) {
|
|
53
|
+
this.setHash({ params: { ...params, data: null } });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Component } from '../../core/Component';
|
|
2
|
+
import { Toast } from './ToastService';
|
|
3
|
+
|
|
4
|
+
export class Service {
|
|
5
|
+
$: Component;
|
|
6
|
+
constructor(state, $: Component) {
|
|
7
|
+
this.state = state;
|
|
8
|
+
this.$ = $;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Framework
|
|
13
|
+
*/
|
|
14
|
+
lookupService(ref: string) {
|
|
15
|
+
return this.$.getByRef(ref);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
up(...args) {
|
|
19
|
+
return this.$.up(...args);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
defer(...args) {
|
|
23
|
+
return this.$.defer(...args);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
emit(...args) {
|
|
27
|
+
return this.$.emit(...args);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
res(...args) {
|
|
31
|
+
return this.$.res(...args);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
log(...args) {
|
|
35
|
+
console.log(this.$.ref + ': ', ...args);
|
|
36
|
+
return args[0];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Error handling
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
handleError({ message = '', code = '' }) {
|
|
44
|
+
// may overriden from props
|
|
45
|
+
const handler = this.lookupService('errorHandler');
|
|
46
|
+
if (handler) {
|
|
47
|
+
handler.handleError({ message, code, source: this.$ });
|
|
48
|
+
} else {
|
|
49
|
+
console.error(this.$.ref + ': ERROR: ', message, code);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
toast(message: string | Partial<Toast> = 'ok') {
|
|
54
|
+
// may overriden from props
|
|
55
|
+
this.emit('toasters.send', typeof message === 'string' ? { message } : message);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
safe(p, def) {
|
|
59
|
+
return p.catch((error) => {
|
|
60
|
+
this.handleError(error);
|
|
61
|
+
return def ? def(error) : { error };
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
onClearError() {
|
|
66
|
+
return { error: null };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Service } from './Service';
|
|
2
|
+
|
|
3
|
+
export class ServiceWorker extends Service {
|
|
4
|
+
get api() {
|
|
5
|
+
return navigator.serviceWorker;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
init() {
|
|
9
|
+
try {
|
|
10
|
+
Function.assert(this.api, 'Service Workers are not supported');
|
|
11
|
+
|
|
12
|
+
const { source = '/service-worker.js', scope = '/', push } = this;
|
|
13
|
+
|
|
14
|
+
this.api
|
|
15
|
+
.register(source, { scope })
|
|
16
|
+
.then((registration) => this.registered(registration))
|
|
17
|
+
.then(() => this.ready(() => this.log('Service Worker Ready')));
|
|
18
|
+
|
|
19
|
+
this.api.addEventListener('message', (ev) => this.onMessage(ev));
|
|
20
|
+
|
|
21
|
+
if (push) {
|
|
22
|
+
this.subscribe();
|
|
23
|
+
}
|
|
24
|
+
} catch (error) {
|
|
25
|
+
this.fallback(error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ensures that `fn` executed when api is ready
|
|
30
|
+
ready(fn) {
|
|
31
|
+
return this.api.ready.then(fn);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fallback(error) {
|
|
35
|
+
this.log(error);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// hook on registered
|
|
39
|
+
registered(registration) {
|
|
40
|
+
this.log('Service Worker Registered');
|
|
41
|
+
return registration;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Push
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
withPushManager(fn) {
|
|
48
|
+
return this.ready(({ pushManager }) => pushManager).then(fn);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
unsubscribe() {
|
|
52
|
+
this.withPushManager((pushManager) => pushManager.getSubscription())
|
|
53
|
+
.then((ss) => ss && ss.unsubscribe())
|
|
54
|
+
.then(() => this.saveSubscription())
|
|
55
|
+
.catch(function (e) {
|
|
56
|
+
console.log('Error thrown while unsubscribing from push messaging.', e);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
subscribe() {
|
|
61
|
+
function urlBase64ToUint8Array(base64String) {
|
|
62
|
+
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
|
|
63
|
+
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
|
|
64
|
+
|
|
65
|
+
const rawData = window.atob(base64);
|
|
66
|
+
return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
|
|
67
|
+
}
|
|
68
|
+
if (Notification.permission === 'granted') {
|
|
69
|
+
/* do our magic */
|
|
70
|
+
} else if (Notification.permission === 'blocked') {
|
|
71
|
+
/* the user has previously denied push. Can't reprompt. */
|
|
72
|
+
} else {
|
|
73
|
+
/* show a prompt to the user */
|
|
74
|
+
}
|
|
75
|
+
const applicationServerKey = urlBase64ToUint8Array(this.vapidPublicKey);
|
|
76
|
+
this.withPushManager((pushManager) =>
|
|
77
|
+
pushManager
|
|
78
|
+
.getSubscription()
|
|
79
|
+
// .then((ss) => ss && ss.unsubscribe())
|
|
80
|
+
.then(
|
|
81
|
+
(ss) =>
|
|
82
|
+
ss ||
|
|
83
|
+
pushManager.subscribe({
|
|
84
|
+
userVisibleOnly: true,
|
|
85
|
+
applicationServerKey,
|
|
86
|
+
})
|
|
87
|
+
)
|
|
88
|
+
.then((subscription) => this.saveSubscription(subscription.toJSON()))
|
|
89
|
+
.catch((err) => {
|
|
90
|
+
if (Notification.permission === 'denied') {
|
|
91
|
+
this.log('The user has blocked notifications.');
|
|
92
|
+
}
|
|
93
|
+
this.handleError(err);
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// to be overriden from props
|
|
99
|
+
saveSubscription(ss) {
|
|
100
|
+
this.subscription = ss;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Intercommunication between service worker.
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
// handles a message posted from Service worker.
|
|
108
|
+
onMessage(payload) {
|
|
109
|
+
this.log('onMessage', payload);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { HeartbeatService } from './HeartbeatService';
|
|
2
|
+
import { Service } from './Service';
|
|
3
|
+
|
|
4
|
+
export type Toast = {
|
|
5
|
+
message: string;
|
|
6
|
+
mode: 'error' | 'warning' | 'info' | '';
|
|
7
|
+
id: string;
|
|
8
|
+
ttl: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export class ToastService extends Service {
|
|
12
|
+
items: Toast[] = [];
|
|
13
|
+
|
|
14
|
+
heartbeat = new HeartbeatService(() => this.emit('this.tick'));
|
|
15
|
+
|
|
16
|
+
setItems(items: Toast[]) {
|
|
17
|
+
this.heartbeat.setActive(!!items.length);
|
|
18
|
+
this.items = items;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
onSend(data: Toast, { items }: this) {
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
const id = String(data.id || now);
|
|
24
|
+
const close = () => this.emit('this.close', { id });
|
|
25
|
+
const toast = {
|
|
26
|
+
...data,
|
|
27
|
+
mode: data?.message?.startsWith('error') ? 'error' : data.mode,
|
|
28
|
+
id,
|
|
29
|
+
close,
|
|
30
|
+
ttl: now + 3000,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
items: [toast, ...items.filter((e) => e.id !== id)],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onTick(_: unknown, { items }: this) {
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
|
|
41
|
+
return { items: items.filter((e: Toast) => e.ttl > now) };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
onClose({ id }: Datum, { items }: this) {
|
|
45
|
+
return {
|
|
46
|
+
items: items.filter((e) => e.id !== id),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { NavigationService } from './NavigationService';
|
|
2
|
+
export { ServiceWorker } from './ServiceWorker';
|
|
3
|
+
export { Service } from './Service';
|
|
4
|
+
export { ToastService } from './ToastService';
|
|
5
|
+
export { Loader } from './Loader';
|
|
6
|
+
export { Invoke } from './Invoke';
|
|
7
|
+
export { ErrorHandlingService } from './ErrorHandlingService';
|
|
8
|
+
export { FormController } from '../forms/FormController';
|
|
9
|
+
export * from './GSheetsService';
|
|
10
|
+
export * from '../forms/ItemCollection';
|
|
11
|
+
export * from '../forms/ItemController';
|
|
12
|
+
export * from './FirebaseService';
|
|
13
|
+
export * from '../forms/MetadataService';
|
|
14
|
+
export * from '../forms/MultivalueController';
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { CNode } from './CNode';
|
|
2
|
+
import { compileNodes } from './compileNode';
|
|
3
|
+
import { Component } from './Component';
|
|
4
|
+
import { xmlParse } from '../lib/xml';
|
|
5
|
+
import { Platform } from './Platform';
|
|
6
|
+
import { registerTypes } from './register';
|
|
7
|
+
|
|
8
|
+
export class Arrmatura extends Component<CArrmaturaNode> implements IArrmatura {
|
|
9
|
+
rootElement: any;
|
|
10
|
+
reflowId?: unknown;
|
|
11
|
+
|
|
12
|
+
get displayName(): string {
|
|
13
|
+
return 'Arrmatura';
|
|
14
|
+
}
|
|
15
|
+
get arrmatura(): IArrmatura {
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
requestReflow() {
|
|
20
|
+
if (this.reflowId) return;
|
|
21
|
+
this.reflowId = setTimeout(() => {
|
|
22
|
+
Platform.instance.reflow(this);
|
|
23
|
+
this.reflowId = undefined;
|
|
24
|
+
}, 10);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
res(key: string): any {
|
|
28
|
+
const [id, ...deep] = key.split('.');
|
|
29
|
+
const { resources = {} } = this.node;
|
|
30
|
+
const target = id ? resources?.[id] : resources;
|
|
31
|
+
if (!target || deep.length === 0) {
|
|
32
|
+
return target;
|
|
33
|
+
}
|
|
34
|
+
if (deep.length === 1) {
|
|
35
|
+
return target[deep[0]];
|
|
36
|
+
}
|
|
37
|
+
return deep.reduce((r, k) => (r ? r[k] : null), target);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pipe(key: string) {
|
|
41
|
+
const [id, ...deep] = key.split('.');
|
|
42
|
+
const target = this.node.functions?.[id];
|
|
43
|
+
if (!target || deep.length === 0) {
|
|
44
|
+
return target;
|
|
45
|
+
}
|
|
46
|
+
if (deep.length === 1) {
|
|
47
|
+
return (target as Hash)[deep[0]];
|
|
48
|
+
}
|
|
49
|
+
return deep.reduce((r, k) => (r ? (r as Hash)[k] : null), target);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class CArrmaturaNode extends CNode<Arrmatura> {
|
|
54
|
+
functions?: Hash;
|
|
55
|
+
resources: Hash<any> | undefined;
|
|
56
|
+
|
|
57
|
+
constructor(template: string, { types, functions, resources }: ArrmaturaOptions) {
|
|
58
|
+
super();
|
|
59
|
+
|
|
60
|
+
registerTypes(types);
|
|
61
|
+
|
|
62
|
+
this.functions = functions;
|
|
63
|
+
this.resources = resources;
|
|
64
|
+
|
|
65
|
+
this.content = compileNodes(xmlParse(template));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get ComponentConstructor() {
|
|
69
|
+
return Arrmatura;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
launch() {
|
|
73
|
+
const arrmatura = this.createComponent() as Arrmatura;
|
|
74
|
+
|
|
75
|
+
arrmatura.up({}, true);
|
|
76
|
+
|
|
77
|
+
console.log(arrmatura.toString());
|
|
78
|
+
}
|
|
79
|
+
}
|