arrmatura 5.0.4 → 5.2.0

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.
Files changed (81) hide show
  1. package/commons/auth/AuthService.ts +111 -0
  2. package/commons/auth/UserService.ts +28 -78
  3. package/commons/auth/index.ts +4 -3
  4. package/commons/auth/user.xml +18 -20
  5. package/commons/components/buttons.xml +6 -4
  6. package/commons/components/layouts.xml +8 -8
  7. package/commons/components/modal.xml +4 -4
  8. package/commons/components/navigation.xml +21 -7
  9. package/commons/components/searchbar.xml +36 -22
  10. package/commons/components/table.xml +25 -5
  11. package/commons/components/toasts.xml +10 -5
  12. package/commons/forms/FormController.ts +48 -34
  13. package/commons/forms/Item.ts +93 -29
  14. package/commons/forms/ItemCollectionController.ts +113 -0
  15. package/commons/forms/ItemController.ts +50 -21
  16. package/commons/forms/NewItemController.ts +46 -0
  17. package/commons/forms/SmartareaField.xml +3 -3
  18. package/commons/forms/SuggestionField.xml +3 -3
  19. package/commons/forms/fields.xml +28 -15
  20. package/commons/forms/form.xml +3 -3
  21. package/commons/forms/index.ts +4 -4
  22. package/commons/index.ts +2 -2
  23. package/commons/services/ApiClientService.ts +32 -0
  24. package/commons/services/GSheetsService.ts +92 -64
  25. package/commons/services/Invoke.ts +24 -8
  26. package/commons/services/JsonpService.ts +1 -1
  27. package/commons/services/MetadataService.ts +50 -0
  28. package/commons/services/NavigationService.ts +20 -1
  29. package/commons/services/Service.ts +22 -28
  30. package/commons/services/Storage.ts +55 -0
  31. package/commons/services/ToastService.ts +16 -4
  32. package/commons/services/index.ts +3 -4
  33. package/core/Arrmatura.ts +4 -9
  34. package/core/CNode.ts +1 -2
  35. package/core/Component.ts +29 -31
  36. package/core/cnodes/conditionals.ts +3 -2
  37. package/core/expression.ts +1 -1
  38. package/core/register.ts +1 -1
  39. package/core/utils.ts +1 -3
  40. package/core-web/attributes.ts +38 -10
  41. package/core-web/elements.ts +22 -4
  42. package/core-web/index.ts +14 -2
  43. package/core-web/reflow.ts +3 -1
  44. package/core-web/support.ts +27 -0
  45. package/docs/index.js +350 -66
  46. package/lib/{arrayGroupBy.ts → array/arrayGroupBy.ts} +1 -1
  47. package/lib/{arraySortBy.ts → array/arraySortBy.ts} +5 -5
  48. package/lib/{arrayToObject.ts → array/arrayToObject.ts} +0 -0
  49. package/lib/array/arrayish.ts +7 -0
  50. package/lib/array/index.ts +4 -0
  51. package/lib/crypto/index.ts +25 -0
  52. package/lib/{dateFormat.ts → date/index.ts} +61 -3
  53. package/lib/func/curry.ts +11 -0
  54. package/lib/{fx.ts → func/fx.ts} +32 -38
  55. package/lib/{noop.ts → func/identity.ts} +1 -3
  56. package/lib/{nextId.ts → func/nextId.ts} +0 -0
  57. package/lib/func/noop.ts +6 -0
  58. package/lib/geo/index.js +22 -0
  59. package/lib/index.ts +17 -19
  60. package/lib/{obj.ts → object/index.ts} +8 -3
  61. package/lib/{codus.ts → parsing/codus.ts} +1 -1
  62. package/lib/{predicat.ts → parsing/compiler.ts} +0 -0
  63. package/lib/{scalar.ts → parsing/scalarParse.ts} +0 -0
  64. package/lib/{urls.ts → parsing/urls.ts} +0 -0
  65. package/lib/{xml.ts → parsing/xml.ts} +3 -3
  66. package/lib/{l10n.ts → resources/l10n.ts} +2 -2
  67. package/lib/{resources.ru.ts → resources/resources.ru.ts} +0 -0
  68. package/lib/{resources.ts → resources/resources.ts} +0 -0
  69. package/lib/{str.ts → string/index.ts} +2 -20
  70. package/lib/{createListeners.ts → support/createListeners.ts} +0 -0
  71. package/package.json +7 -6
  72. package/commons/forms/ItemCollection.ts +0 -152
  73. package/commons/forms/MetadataService.ts +0 -45
  74. package/commons/pipes/index.ts +0 -21
  75. package/commons/pipes/showCounts.ts +0 -16
  76. package/commons/services/LocalStorage.ts +0 -46
  77. package/lib/consts.ts +0 -3
  78. package/lib/curry.ts +0 -8
  79. package/lib/dateOf.ts +0 -31
  80. package/lib/dateParseISO8601.ts +0 -27
  81. package/lib/guid.ts +0 -7
@@ -3,15 +3,24 @@ import { Service } from './Service';
3
3
 
4
4
  export type Toast = {
5
5
  message: string;
6
+ link?: string;
6
7
  mode: 'error' | 'warning' | 'info' | '';
7
8
  id: string;
8
9
  ttl: number;
10
+ timeout?: number;
11
+ counter?: number;
9
12
  };
10
13
 
14
+ export class ToastSender extends Service {
15
+ onData(data: Delta) {
16
+ this.emit('toaster.send', data);
17
+ }
18
+ }
19
+
11
20
  export class ToastService extends Service {
12
21
  items: Toast[] = [];
13
22
 
14
- heartbeat = new HeartbeatService(() => this.emit('this.tick'));
23
+ heartbeat = new HeartbeatService(() => this.emit('this.tick', {}));
15
24
 
16
25
  setItems(items: Toast[]) {
17
26
  this.heartbeat.setActive(!!items.length);
@@ -27,11 +36,14 @@ export class ToastService extends Service {
27
36
  mode: data?.message?.startsWith('error') ? 'error' : data.mode,
28
37
  id,
29
38
  close,
30
- ttl: now + 3000,
39
+ ttl: now + (data.timeout || 6000),
31
40
  };
32
-
41
+ const prev = items.find((e) => e.id === id);
42
+ if (prev) {
43
+ toast.counter = 1 + (prev.counter || 1);
44
+ }
33
45
  return {
34
- items: [toast, ...items.filter((e) => e.id !== id)],
46
+ items: [toast, ...items.filter((e) => e !== prev)],
35
47
  };
36
48
  }
37
49
 
@@ -6,9 +6,8 @@ export { Loader } from './Loader';
6
6
  export { Invoke } from './Invoke';
7
7
  export { ErrorHandlingService } from './ErrorHandlingService';
8
8
  export { FormController } from '../forms/FormController';
9
+ export * from './MetadataService';
9
10
  export * from './GSheetsService';
10
- export * from '../forms/ItemCollection';
11
- export * from '../forms/ItemController';
11
+ export * from './ApiClientService';
12
12
  export * from './FirebaseService';
13
- export * from '../forms/MetadataService';
14
- export * from '../forms/MultivalueController';
13
+ export * from './MetadataService';
package/core/Arrmatura.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CNode } from './CNode';
2
2
  import { compileNodes } from './compileNode';
3
3
  import { Component } from './Component';
4
- import { xmlParse } from '../lib/xml';
4
+ import { xmlParse } from '../lib/parsing/xml';
5
5
  import { registerTypes } from './register';
6
6
 
7
7
  export class Arrmatura extends Component<CArrmaturaNode> implements IArrmatura {
@@ -11,9 +11,6 @@ export class Arrmatura extends Component<CArrmaturaNode> implements IArrmatura {
11
11
  get displayName(): string {
12
12
  return 'Arrmatura';
13
13
  }
14
- get arrmatura(): IArrmatura {
15
- return this;
16
- }
17
14
 
18
15
  get platform(): IPlatform {
19
16
  return this.node.platform;
@@ -74,11 +71,9 @@ export class CArrmaturaNode extends CNode<Arrmatura> {
74
71
  return Arrmatura;
75
72
  }
76
73
 
77
- launch() {
78
- const arrmatura = this.createComponent() as Arrmatura;
79
-
80
- arrmatura.up({}, true);
74
+ launch(config = {}) {
75
+ const arrmatura = this.createComponent();
81
76
 
82
- console.log(arrmatura.toString());
77
+ arrmatura.up(config, true);
83
78
  }
84
79
  }
package/core/CNode.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { nextId } from 'nextId';
2
- import { identity } from 'noop';
1
+ import { nextId, identity } from '../lib';
3
2
  import { Component } from './Component';
4
3
  import { compileExpression, compilePipeExpression } from './expression';
5
4
 
package/core/Component.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable no-console */
2
2
  import { applyValue, methodName, stringifyState } from './utils';
3
- import { scalarParse } from 'scalar';
4
- import { createListeners } from 'createListeners';
3
+ import { scalarParse } from '../lib';
4
+ import { createListeners } from 'support/createListeners';
5
5
 
6
6
  /**
7
7
  * Runtime Component class.
@@ -13,6 +13,7 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
13
13
 
14
14
  readonly $scope: IScope;
15
15
  readonly #parent?: IComponent;
16
+ readonly arrmatura: IArrmatura;
16
17
  #isInited: boolean = false;
17
18
  #isDone: boolean = false;
18
19
  #listeners?: Listeners<void>;
@@ -24,6 +25,7 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
24
25
  constructor(readonly _node: INode, _parent?: IComponent, scope?: IScope) {
25
26
  this.$scope = scope || this;
26
27
  this.#parent = _parent;
28
+ this.arrmatura = _parent?.arrmatura || (this as unknown as IArrmatura);
27
29
  this.$impl = this.createImplementation(this.getInitialState());
28
30
 
29
31
  if (this.refId) {
@@ -31,10 +33,6 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
31
33
  }
32
34
  }
33
35
 
34
- get parent(): IComponent {
35
- return this.#parent || this;
36
- }
37
-
38
36
  get node(): T {
39
37
  return this._node as T;
40
38
  }
@@ -43,10 +41,6 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
43
41
  return '🔹';
44
42
  }
45
43
 
46
- get arrmatura(): IArrmatura {
47
- return this.parent.arrmatura;
48
- }
49
-
50
44
  get uid() {
51
45
  return this.node.uid;
52
46
  }
@@ -61,20 +55,19 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
61
55
  return this.node.resolveInitialProps(this);
62
56
  }
63
57
 
64
- up(Δ: Delta | Promise<any> = {}, force: boolean = false): void {
65
- if (this.#isDone) {
66
- return;
58
+ up(Δ: Delta | Promise<any>, force: boolean = false): null | Promise<any> {
59
+ if (!Δ || this.#isDone) {
60
+ return null;
67
61
  }
68
62
  if (Δ.then && Δ.catch) {
69
- this.upAsync(Δ as Promise<any>);
70
- return;
63
+ return this.upAsync(Δ as Promise<any>);
71
64
  }
72
-
65
+ let prom: Promise<any>[] | null = null;
73
66
  const changes = new Map<string, unknown>();
74
67
  Object.entries(Δ).forEach(([k, v]) => {
75
- const pv = v as Promise<any>;
76
- if (v && pv.then && pv.catch) {
77
- this.upAsync(pv, k);
68
+ if (v && v.then && v.catch) {
69
+ const pv = v as Promise<any>;
70
+ (prom || (prom = [])).push(this.upAsync(pv, k));
78
71
  } else if (k && typeof v !== 'undefined' && v !== this.$impl[k]) {
79
72
  changes.set(k, v);
80
73
  }
@@ -87,16 +80,19 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
87
80
  this.notify();
88
81
  }
89
82
  }
83
+ if (prom) {
84
+ return Promise.all(prom).then((arr: object[]) => arr.reduce((a, b) => ({ ...a, ...b }), {}));
85
+ }
86
+ return null;
90
87
  }
91
88
 
92
- upAsync(promise: Promise<any>, key?: string) {
89
+ upAsync(promise: Promise<any>, key?: string): Promise<any> {
93
90
  const racer = this.raceCondition('set:' + key);
94
- const up = (r: Delta) => racer(() => this.up(r));
95
- if (key && key !== '*') {
91
+ if (key && key[0] !== '*') {
96
92
  const akey = key.replace('Promise', '');
97
- promise.then((val) => up({ [akey]: val }));
93
+ promise.then((val) => racer(() => this.up({ [akey]: val })));
98
94
  } else {
99
- promise.then(up);
95
+ promise.then((r: Delta) => racer(() => this.up(r)));
100
96
  }
101
97
  return promise;
102
98
  }
@@ -180,11 +176,10 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
180
176
 
181
177
  // --- Right Arrow.
182
178
 
183
- emit(key: string, data: Delta): void {
179
+ emit(key: string, data: Delta): null | Promise<any> {
184
180
  if (!key || !key.includes('.')) {
185
- this.up(key ? { [key]: data } : data);
186
181
  this.log('update:' + key, data, this.$impl);
187
- return;
182
+ return this.up(key ? { [key]: data } : data);
188
183
  }
189
184
 
190
185
  const [refId, target] = key.split('.');
@@ -206,11 +201,13 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
206
201
 
207
202
  this.log(refId + ':' + propId, result, data, impl);
208
203
  if (result) {
209
- ref.up(result);
204
+ const ret = ref.up(result);
205
+ return ret;
210
206
  }
211
207
  } catch (ex) {
212
208
  this.logError('emit ' + key + ':', ex);
213
209
  }
210
+ return null;
214
211
  }
215
212
 
216
213
  // --- Life-cycle hooks.
@@ -386,7 +383,7 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
386
383
  return (fn: Procedure) => {
387
384
  if (counter === COUNTERS.get(key)) {
388
385
  counter = 0;
389
- fn();
386
+ return fn();
390
387
  }
391
388
  };
392
389
  }
@@ -397,8 +394,9 @@ export class Component<T extends INode = INode> implements IComponent, IScope {
397
394
  }
398
395
  }
399
396
 
400
- log(...args: unknown[]) {
401
- console.log('' + this.displayName + ':' + this.uid, ...args);
397
+ log(val: any, ...args: unknown[]) {
398
+ console.log('' + this.displayName + ':' + this.uid, val, ...args);
399
+ return val;
402
400
  }
403
401
 
404
402
  logError(...args: unknown[]) {
@@ -6,7 +6,7 @@ import { Component } from '../Component';
6
6
 
7
7
  class IfComponent extends Component<CIfNode> {
8
8
  get content() {
9
- return this.$impl.condition ? this.node.then : this.node.else;
9
+ return !!this.$impl.condition ? this.node.then : this.node.else;
10
10
  }
11
11
  }
12
12
 
@@ -45,7 +45,8 @@ export class CIfNode extends CNode<IfComponent> {
45
45
  // DO NOT place inside iterations.
46
46
  this.addPropertyResolver((c: IComponent) => (c.$scope as CompositeComponent).slotContent?.(slotId), 'condition');
47
47
  } else {
48
- this.addPropertyResolver(compileExpression(expr[0] === '{' ? expr : `{${expr}}`), 'condition');
48
+ const resolver = compileExpression(expr[0] === '{' ? expr : `{${expr}}`);
49
+ this.addPropertyResolver((c) => !!resolver(c), 'condition');
49
50
  }
50
51
  }
51
52
  }
@@ -1,4 +1,4 @@
1
- import { scalarParse } from 'scalar';
1
+ import { scalarParse } from '../lib';
2
2
 
3
3
  const RE_SINGLE_PLACEHOLDER = /^\{([^}]+)\}$/;
4
4
 
package/core/register.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { compileNodes } from './compileNode';
2
- import { xmlParse } from 'xml';
2
+ import { xmlParse } from '../lib';
3
3
  import { fnName } from './utils';
4
4
 
5
5
  export class ComponentDefinition {
package/core/utils.ts CHANGED
@@ -1,6 +1,4 @@
1
- import { nextId } from 'nextId';
2
- import { identity } from 'noop';
3
- import { attributesToString } from 'xml';
1
+ import { nextId, identity, attributesToString } from '../lib';
4
2
 
5
3
  export const applyValue = (value: any, fn = identity) => (value?.then ? value.then(fn) : fn(value));
6
4
 
@@ -1,4 +1,4 @@
1
- import { DomNode, IElement } from './type';
1
+ import type { DomNode, IElement } from './type';
2
2
 
3
3
  const camelize = (key: string, sep = '_', jn = ' ') =>
4
4
  ('' + key)
@@ -6,13 +6,14 @@ const camelize = (key: string, sep = '_', jn = ' ') =>
6
6
  .map((s, i) => (i ? s[0].toUpperCase() + s.slice(1) : s))
7
7
  .join(jn);
8
8
 
9
- const setListener = ($: IElement, key: string, e: DomNode, v: unknown) => {
9
+ const setListener = ($: IElement, key: string, e: DomNode, fn: unknown) => {
10
10
  const fnKey = key.split(':')[0];
11
- if (!v) {
11
+ if (!fn) {
12
12
  setAttribute($, key, null);
13
13
  } else {
14
- setAttribute($, key, (ev: Event) => {
15
- $[fnKey]({ ...e.$dataset }, ev);
14
+ setAttribute($, key, (domEvent: Event) => {
15
+ // indirect reference. could be changed along the time.
16
+ $[fnKey]({ ...e.$dataset }, domEvent);
16
17
  return false;
17
18
  });
18
19
  }
@@ -74,11 +75,38 @@ export const ATTR_SETTERS: Hash<($: IElement, e: DomNode, v: unknown) => void> =
74
75
  }
75
76
  },
76
77
 
77
- click: ($, e, v) => setListener($, 'click:click', e, v),
78
+ click: ($, e, v) => setListener($, 'click', e, v),
79
+ click2: ($, e, v) => setListener($, 'click2:click', e, v),
78
80
  ['bubble-click']: ($, e, v) => setListener($, 'bubble-click:click', e, v),
79
- blur: ($, e, v) => setListener($, 'blur:blur', e, v),
80
- dblclick: ($, e, v) => setListener($, 'dblclick:dblclick', e, v),
81
- scroll: ($, e, v) => setListener($, 'scroll:scroll', e, v),
81
+ blur: ($, e, v) => setListener($, 'blur', e, v),
82
+ focus: ($, e, v) => setListener($, 'focus', e, v),
83
+ dblclick: ($, e, v) => setListener($, 'dblclick', e, v),
84
+ scroll: ($, e, v) =>
85
+ setAttribute(
86
+ $,
87
+ 'scroll',
88
+ !v
89
+ ? null
90
+ : (ev) => {
91
+ $.scroll({ value: e.scrollTop }, ev);
92
+ }
93
+ ),
94
+ scrolledDown: ($, element, v) =>
95
+ setAttribute(
96
+ $,
97
+ 'scrolledDown:scroll',
98
+ !v
99
+ ? null
100
+ : (ev) => {
101
+ const gap = $.scrolledDownGap || 10;
102
+ const offset = element.clientHeight + element.scrollTop;
103
+ const isDown = Math.abs(element.scrollHeight - offset) < gap;
104
+ if (isDown && offset > ($._maxScrolledDownOffset || 0)) {
105
+ $._maxScrolledDownOffset = offset;
106
+ $.scrolledDown({ offset }, ev);
107
+ }
108
+ }
109
+ ),
82
110
 
83
111
  touchstart: function ($, e, v) {
84
112
  const h = !v
@@ -166,7 +194,7 @@ export const ATTR_SETTERS: Hash<($: IElement, e: DomNode, v: unknown) => void> =
166
194
  change: function ($, e, v) {
167
195
  setAttribute(
168
196
  $,
169
- 'change:change',
197
+ 'change',
170
198
  !v
171
199
  ? null
172
200
  : (ev: Event) => {
@@ -1,11 +1,12 @@
1
+ import { decodeXmlEntities } from '../lib';
1
2
  import { ATTR_SETTERS, setAttribute } from './attributes';
2
- import { DomNode, IElement } from './type';
3
+ import type { DomNode, IElement } from './type';
3
4
 
4
5
  export class DomElement implements IComponentImplementation, IElement {
5
6
  $: IComponent;
6
7
  $element: DomNode;
7
8
  $listeners: any;
8
-
9
+ $changes: Map<string, any> = new Map();
9
10
  init?: () => any;
10
11
  [key: string]: unknown;
11
12
 
@@ -20,10 +21,21 @@ export class DomElement implements IComponentImplementation, IElement {
20
21
  }
21
22
 
22
23
  stateChanged(changes: Map<string, any>) {
24
+ const attrs = this;
25
+ changes.forEach((value, key) => {
26
+ if (key[0] === '$') return;
27
+ if (value !== attrs[key]) {
28
+ this.$changes?.set(key, value);
29
+ }
30
+ });
31
+ }
32
+
33
+ applyStateChanges() {
23
34
  const attrs = this;
24
35
  const e = this.$element;
36
+ if (!this.$changes.size) return;
25
37
  // this.$.log('changes', changes)
26
- changes.forEach((value, key) => {
38
+ this.$changes.forEach((value, key) => {
27
39
  if (key[0] === '$') return;
28
40
  if (value !== attrs[key]) {
29
41
  const setter = ATTR_SETTERS[key];
@@ -40,11 +52,13 @@ export class DomElement implements IComponentImplementation, IElement {
40
52
  }
41
53
  attrs[key] = value;
42
54
  });
55
+ this.$changes.clear();
43
56
  }
44
57
  }
45
58
 
46
59
  export class DomTextElement implements IComponentImplementation {
47
60
  $element: Text;
61
+ $string: string | null = null;
48
62
  [key: string]: unknown;
49
63
 
50
64
  constructor(attrs: Hash) {
@@ -57,7 +71,11 @@ export class DomTextElement implements IComponentImplementation {
57
71
 
58
72
  stateChanged(changes: Map<string, any>) {
59
73
  if (changes.has('text')) {
60
- this.$element.textContent = changes.get('text');
74
+ this.$string = decodeXmlEntities(changes.get('text'));
61
75
  }
62
76
  }
77
+
78
+ applyStateChanges() {
79
+ this.$element.textContent = this.$string;
80
+ }
63
81
  }
package/core-web/index.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  import { CArrmaturaNode } from '../core';
2
2
  import { WebPlatform } from './WebPlatform';
3
+ import * as lib from '../lib';
4
+
5
+ import commons from '../commons';
6
+
7
+ import support from './support';
3
8
 
4
9
  export type ArrmaturaOptionsForWeb = ArrmaturaOptions & {
5
10
  rootElement?: HTMLElement;
@@ -7,10 +12,17 @@ export type ArrmaturaOptionsForWeb = ArrmaturaOptions & {
7
12
 
8
13
  export function arrmatura(
9
14
  template: string,
10
- { rootElement, pipes, functions = pipes, types, resources }: ArrmaturaOptionsForWeb & { pipes: Hash<Pipe> }
15
+ { rootElement, pipes, functions = pipes, types = [], resources }: ArrmaturaOptionsForWeb & { pipes: Hash<Pipe> }
11
16
  ) {
12
- const node = new CArrmaturaNode(template, { functions, types, resources }, new WebPlatform(rootElement));
17
+ const options: ArrmaturaOptions = {
18
+ functions: { ...lib, ...support(arrmatura), ...functions },
19
+ types: [...commons, ...types],
20
+ resources,
21
+ };
22
+ const node = new CArrmaturaNode(template, options, new WebPlatform(rootElement));
13
23
  node.launch();
14
24
  }
15
25
 
26
+ arrmatura.lib = lib;
27
+
16
28
  Object.defineProperty(window, 'arrmatura', { value: arrmatura });
@@ -1,4 +1,5 @@
1
- import { DomNode } from './type';
1
+ import { DomElement } from './elements';
2
+ import type { DomNode } from './type';
2
3
 
3
4
  function appendElt(e: DomNode, p: DomNode | HTMLElement, cursor?: DomNode): DomNode {
4
5
  const before = cursor ? cursor.nextSibling : p.firstChild;
@@ -18,6 +19,7 @@ export function reflow($: IComponent, parent: DomNode | HTMLElement, cursor?: Do
18
19
  const e = p.$impl.$element as DomNode;
19
20
  if (e) {
20
21
  reflow(p, e);
22
+ (p.$impl as DomElement).applyStateChanges();
21
23
  cursor = appendElt(e, parent, cursor);
22
24
  } else {
23
25
  cursor = reflow(p, parent, cursor);
@@ -0,0 +1,27 @@
1
+ const showCounts = (counts: Obj, nicNema = '⭕', neZnojdzeno = '⭕', loading = '…') => {
2
+ if (!counts) {
3
+ return loading;
4
+ }
5
+ const { total, actual } = counts;
6
+ if (!total) {
7
+ return nicNema;
8
+ }
9
+ if (!actual) {
10
+ return neZnojdzeno;
11
+ }
12
+ if (actual === total) {
13
+ return total;
14
+ }
15
+ return actual + ' / ' + total;
16
+ };
17
+
18
+ export default (arrmatura: (m: string, opts: unknown) => void) => ({
19
+ showCounts,
20
+
21
+ sandbox: (markup: string) =>
22
+ function () {
23
+ try {
24
+ arrmatura(`<div>${markup}</div>`, { rootElement: this.$element });
25
+ } catch (err) {}
26
+ },
27
+ });