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.
Files changed (93) hide show
  1. package/README.md +30 -25
  2. package/commons/auth/UserService.ts +107 -0
  3. package/commons/auth/index.ts +4 -0
  4. package/commons/auth/user.xml +66 -0
  5. package/commons/components/buttons.xml +23 -0
  6. package/commons/components/index.ts +13 -0
  7. package/commons/components/inputs.xml +33 -0
  8. package/commons/components/layouts.xml +77 -0
  9. package/commons/components/media.xml +21 -0
  10. package/commons/components/modal.xml +37 -0
  11. package/commons/components/navigation.xml +59 -0
  12. package/commons/components/popups.xml +12 -0
  13. package/commons/components/searchbar.xml +40 -0
  14. package/commons/components/table.xml +120 -0
  15. package/commons/components/toasts.xml +13 -0
  16. package/commons/components/viewport.xml +48 -0
  17. package/commons/forms/FieldItem.xml +20 -0
  18. package/commons/forms/FormController.ts +73 -0
  19. package/commons/forms/Item.ts +68 -0
  20. package/commons/forms/ItemCollection.ts +152 -0
  21. package/commons/forms/ItemController.ts +62 -0
  22. package/commons/forms/MetadataService.ts +45 -0
  23. package/commons/forms/MultiEnumField.xml +44 -0
  24. package/commons/forms/MultivalueController.ts +49 -0
  25. package/commons/forms/SmartareaField.xml +51 -0
  26. package/commons/forms/SuggestionController.ts +80 -0
  27. package/commons/forms/SuggestionField.xml +28 -0
  28. package/commons/forms/fields.xml +138 -0
  29. package/commons/forms/form.xml +37 -0
  30. package/commons/forms/index.ts +29 -0
  31. package/commons/index.ts +6 -0
  32. package/commons/pipes/index.ts +21 -0
  33. package/commons/pipes/showCounts.ts +16 -0
  34. package/commons/services/ErrorHandlingService.ts +11 -0
  35. package/commons/services/FirebaseService.ts +185 -0
  36. package/commons/services/GSheetsService.ts +92 -0
  37. package/commons/services/HeartbeatService.ts +35 -0
  38. package/commons/services/Invoke.ts +13 -0
  39. package/commons/services/JsonpService.ts +18 -0
  40. package/commons/services/Loader.ts +17 -0
  41. package/commons/services/LocalStorage.ts +46 -0
  42. package/commons/services/NavigationService.ts +55 -0
  43. package/commons/services/Service.ts +68 -0
  44. package/commons/services/ServiceWorker.ts +111 -0
  45. package/commons/services/ToastService.ts +49 -0
  46. package/commons/services/index.ts +14 -0
  47. package/core/Arrmatura.ts +79 -0
  48. package/core/CNode.ts +112 -0
  49. package/core/Component.ts +431 -0
  50. package/core/Platform.ts +3 -0
  51. package/core/cnodes/composition.ts +100 -0
  52. package/core/cnodes/conditionals.ts +51 -0
  53. package/core/cnodes/elementary.ts +45 -0
  54. package/core/cnodes/iterations.ts +94 -0
  55. package/core/cnodes/routing.ts +25 -0
  56. package/core/compileNode.ts +39 -0
  57. package/core/expression.ts +73 -0
  58. package/core/index.ts +2 -0
  59. package/core/register.ts +43 -0
  60. package/core/utils.ts +25 -0
  61. package/core-web/WebPlatform.ts +25 -0
  62. package/core-web/attributes.ts +197 -0
  63. package/core-web/elements.ts +64 -0
  64. package/core-web/index.ts +19 -0
  65. package/core-web/reflow.ts +30 -0
  66. package/core-web/type.ts +19 -0
  67. package/docs/index.js +191 -4077
  68. package/lib/arrayGroupBy.ts +19 -0
  69. package/lib/arraySortBy.ts +49 -0
  70. package/lib/arrayToObject.ts +18 -0
  71. package/lib/codus.ts +25 -0
  72. package/lib/consts.ts +3 -0
  73. package/lib/createListeners.ts +12 -0
  74. package/lib/curry.ts +8 -0
  75. package/lib/dateFormat.ts +69 -0
  76. package/lib/dateOf.ts +31 -0
  77. package/lib/dateParseISO8601.ts +27 -0
  78. package/lib/fx.ts +62 -0
  79. package/lib/guid.ts +7 -0
  80. package/lib/index.ts +19 -0
  81. package/lib/l10n.ts +40 -0
  82. package/lib/nextId.ts +9 -0
  83. package/lib/noop.ts +8 -0
  84. package/lib/obj.ts +62 -0
  85. package/lib/predicat.ts +26 -0
  86. package/lib/resources.ru.ts +20 -0
  87. package/lib/resources.ts +20 -0
  88. package/lib/scalar.ts +25 -0
  89. package/lib/str.ts +76 -0
  90. package/lib/urls.ts +91 -0
  91. package/lib/xml.ts +117 -0
  92. package/package.json +17 -8
  93. package/docs/index.js.map +0 -1
@@ -0,0 +1,45 @@
1
+ import { Platform } from '../Platform';
2
+ import { CNode } from '../CNode';
3
+ import { compileNodes } from '../compileNode';
4
+ import { Component } from '../Component';
5
+
6
+ export class ElementComponent extends Component<CElementNode> {
7
+ get displayName(): string {
8
+ return this.node.tag;
9
+ }
10
+
11
+ createImplementation(initials: Hash): IComponentImplementation {
12
+ return Platform.instance.createElement(this.node.tag, initials, this);
13
+ }
14
+ }
15
+
16
+ export class TextElementComponent extends Component<CTextElementNode> {
17
+ createImplementation(initials: Hash) {
18
+ return Platform.instance.createTextElement(initials, this);
19
+ }
20
+ }
21
+
22
+ export class CElementNode extends CNode<ElementComponent> {
23
+ constructor(x: XmlNode) {
24
+ super();
25
+ this.tag = x.tag;
26
+ this.content = compileNodes(x.nodes);
27
+ this.compileAttributes(x.attrs);
28
+ }
29
+
30
+ get ComponentConstructor() {
31
+ return ElementComponent;
32
+ }
33
+ }
34
+
35
+ export class CTextElementNode extends CNode<TextElementComponent> {
36
+ constructor(x: XmlNode) {
37
+ super();
38
+ this.tag = x.tag;
39
+ this.compileAttributes(x.attrs);
40
+ }
41
+
42
+ get ComponentConstructor() {
43
+ return TextElementComponent;
44
+ }
45
+ }
@@ -0,0 +1,94 @@
1
+ import { CNode } from '../CNode';
2
+ import { compileNodes } from '../compileNode';
3
+ import { Component } from '../Component';
4
+
5
+ class ForComponent extends Component<CForNode> {
6
+ pkHash: Hash<Datum> = {};
7
+
8
+ get displayName(): string {
9
+ return '🔹';
10
+ }
11
+
12
+ get content() {
13
+ const {
14
+ state: { each },
15
+ } = this;
16
+ const itemNode = this.node.itemNode;
17
+ const nodes: ComponentContent = new Map();
18
+ this.pkHash = {};
19
+ if (each?.length) {
20
+ if (!each.forEach) {
21
+ throw new Error('[ui:for] Items has no forEach() ' + each);
22
+ }
23
+ each.forEach((datum: Datum) => {
24
+ const id = datum.id;
25
+ if (id == null) {
26
+ console.error('ERROR: empty item id: ', datum);
27
+ return;
28
+ }
29
+ const pk = String(id);
30
+ if (this.pkHash[pk]) {
31
+ console.error('ERROR: duplicate item id: ' + pk, datum);
32
+ return;
33
+ }
34
+ this.pkHash[pk] = datum;
35
+ const inode = itemNode.cloneWithDatum(datum).addPropertyResolver(() => this.pkHash[pk], itemNode.itemName);
36
+ nodes.set(inode.uid, inode);
37
+ });
38
+ }
39
+ return nodes;
40
+ }
41
+ }
42
+
43
+ export class CForNode extends CNode<ForComponent> {
44
+ itemNode: CItemNode;
45
+ constructor(x: XmlNode, [itemName, prep, expr = prep]: string[]) {
46
+ super();
47
+ this.itemNode = new CItemNode(itemName, compileNodes([x]));
48
+ this.compileAttribute('each', '{:<'.includes(expr[0]) ? expr : `{${expr}}`);
49
+ }
50
+
51
+ get ComponentConstructor() {
52
+ return ForComponent;
53
+ }
54
+ }
55
+
56
+ class ItemComponent extends Component<CItemNode> {
57
+ get displayName(): string {
58
+ return '🔹';
59
+ }
60
+ get recontentScope(): Component {
61
+ return this;
62
+ }
63
+
64
+ emit(key: string, data: Delta): void {
65
+ return this.scope.emit(key, data);
66
+ }
67
+
68
+ get(propId: string) {
69
+ const iName: string = this.node.itemName;
70
+ const value = propId.startsWith(iName + '.') || propId === iName ? super.get(propId) : this.scope.get(propId);
71
+ // this.log(propId, value);
72
+ return value;
73
+ }
74
+ }
75
+
76
+ export class CItemNode extends CNode<ItemComponent> {
77
+ itemName: string;
78
+ constructor(itemName: string, content?: ComponentContent) {
79
+ super();
80
+ this.itemName = itemName;
81
+ this.content = content;
82
+ }
83
+
84
+ cloneWithDatum(datum: Datum) {
85
+ const uid = this.uid + '#' + datum.id;
86
+ return Object.assign(new CItemNode(this.itemName, this.content), {
87
+ uid,
88
+ });
89
+ }
90
+
91
+ get ComponentConstructor() {
92
+ return ItemComponent;
93
+ }
94
+ }
@@ -0,0 +1,25 @@
1
+ import { CNode } from '../CNode';
2
+ import { compileNode } from '../compileNode';
3
+ import { Component } from '../Component';
4
+
5
+ export class DynamicTagComponent extends Component<CDynamicTagNode> {
6
+ get content(): ComponentContent | undefined {
7
+ const { tag } = this.state;
8
+ const uid = this.node.uid + ':' + tag;
9
+ return new Map([[uid, Object.assign(compileNode(Object.assign(this.node.xml, { tag })), { uid })]]);
10
+ }
11
+ }
12
+
13
+ export class CDynamicTagNode extends CNode<DynamicTagComponent> {
14
+ xml: XmlNode & { tag: string };
15
+
16
+ constructor(xml: XmlNode, expr: string) {
17
+ super();
18
+ this.compileAttribute('tag', expr);
19
+ this.xml = xml;
20
+ }
21
+
22
+ get ComponentConstructor() {
23
+ return DynamicTagComponent;
24
+ }
25
+ }
@@ -0,0 +1,39 @@
1
+ import { CCompositeNode, CFragmentNode, CSlotNode } from './cnodes/composition';
2
+ import { CDynamicTagNode } from './cnodes/routing';
3
+ import { CElementNode, CTextElementNode } from './cnodes/elementary';
4
+ import { CForNode } from './cnodes/iterations';
5
+ import { CIfNode } from './cnodes/conditionals';
6
+
7
+ export const compileNode = (x: XmlNode): INode => {
8
+ const { tag, attrs } = x;
9
+ const each = attrs?.['ui:for'];
10
+ if (each) {
11
+ delete attrs['ui:for'];
12
+ return new CForNode(x, String(each).trim().split(' '));
13
+ }
14
+ const when = attrs?.['ui:if'];
15
+ if (when) {
16
+ delete attrs['ui:if'];
17
+ return new CIfNode(x, String(when));
18
+ }
19
+ if (tag === 'ui:tag') {
20
+ const expr = attrs?.['tag'] || tag;
21
+ delete attrs?.['tag'];
22
+ return new CDynamicTagNode(x, String(expr));
23
+ }
24
+ if (tag === 'ui:slot') {
25
+ return new CSlotNode(x);
26
+ }
27
+ if (tag === 'ui:fragment') {
28
+ return new CFragmentNode(x);
29
+ }
30
+ if (tag === '#text') {
31
+ return new CTextElementNode(x);
32
+ }
33
+ if (tag[0].match(/[A-Z]/)) return new CCompositeNode(x);
34
+
35
+ return new CElementNode(x);
36
+ };
37
+
38
+ export const compileNodes = (x?: XmlNode[] | null): ComponentContent =>
39
+ new Map<Uid, INode>(x?.map(compileNode).map((c) => [c.uid, c]) || []);
@@ -0,0 +1,73 @@
1
+ import { scalarParse } from 'scalar';
2
+
3
+ const RE_SINGLE_PLACEHOLDER = /^\{([^}]+)\}$/;
4
+
5
+ const RE_PLACEHOLDER = /\{([^}]+)\}/g;
6
+
7
+ const withNoPipes = (_: unknown, v: unknown) => v;
8
+
9
+ const splitExpression = (v: Expression) => {
10
+ return v.split('|').map((s) => s.trim());
11
+ };
12
+
13
+ export function withPipes(pipes: string[]) {
14
+ return !pipes.length
15
+ ? withNoPipes
16
+ : (c: IComponent, initialValue: unknown) => pipes.reduce((r, pk) => c.applyPipe(pk, r), initialValue);
17
+ }
18
+
19
+ export function compilePipeExpression(v: Expression) {
20
+ const [key, ...pipes] = splitExpression(v);
21
+ return { key, pipec: withPipes(pipes) };
22
+ }
23
+
24
+ function stringInterpolation(v: string, fnx: Array<(c: IComponent) => unknown> = []) {
25
+ const pattern = v.trim().replace(RE_PLACEHOLDER, (_: string, expr: string) => {
26
+ fnx.push(placeholder(expr));
27
+ return '{{' + (fnx.length - 1) + '}}';
28
+ });
29
+ return !fnx.length
30
+ ? (_: IComponent) => pattern
31
+ : (c: IComponent) =>
32
+ pattern
33
+ .replace(/\{\{(\d+)\}\}/g, (_: string, idx: string) => {
34
+ const r = fnx[Number(idx)](c);
35
+ return !r && r !== 0 ? '' : String(r);
36
+ })
37
+ .replace(/\s+/g, ' ');
38
+ }
39
+
40
+ function resourceExpression(expr: string) {
41
+ const { key, pipec } = compilePipeExpression(expr);
42
+ const intrpltn = stringInterpolation(key);
43
+ return !pipec.length ? (c: IComponent) => c.res(intrpltn(c)) : (c: IComponent) => pipec(c, c.res(intrpltn(c)));
44
+ }
45
+
46
+ function placeholder(expr: string) {
47
+ const { key, pipec } = compilePipeExpression(expr);
48
+ const fn =
49
+ key[0] === ':'
50
+ ? (
51
+ (k) => (c: IComponent) =>
52
+ c.res(k)
53
+ )(key.slice(1))
54
+ : (c: IComponent) => c.getFromScope(key);
55
+ return !pipec.length ? fn : (c: IComponent) => pipec(c, fn(c));
56
+ }
57
+
58
+ export function compileExpression(v: unknown) {
59
+ if (typeof v !== 'string') {
60
+ return () => v;
61
+ }
62
+ if (v[0] === ':') {
63
+ return resourceExpression(v.slice(1).trim());
64
+ }
65
+ if (!v.includes('{')) {
66
+ const vv = scalarParse(v);
67
+ return () => vv;
68
+ }
69
+ if (v.match(RE_SINGLE_PLACEHOLDER)) {
70
+ return placeholder(v.slice(1, -1).trim());
71
+ }
72
+ return stringInterpolation(v);
73
+ }
package/core/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './Arrmatura';
2
+ export * from './Platform';
@@ -0,0 +1,43 @@
1
+ import { compileNodes } from './compileNode';
2
+ import { xmlParse } from 'xml';
3
+ import { fnName } from './utils';
4
+
5
+ export class ComponentDefinition {
6
+ tag: string;
7
+ template?: string;
8
+ Ctor: any;
9
+ $templateContent?: ComponentContent;
10
+
11
+ constructor({ tag, template, Ctor }: ComponentDefinitionInfo) {
12
+ this.tag = tag;
13
+ this.template = template;
14
+ this.Ctor = Ctor || function () {};
15
+ }
16
+
17
+ get content() {
18
+ return this.$templateContent || (this.$templateContent = compileNodes(xmlParse(this.template)));
19
+ }
20
+ }
21
+
22
+ export const REGISTRY = new Map<string, ComponentDefinition>();
23
+
24
+ const notFound = (tag: string): ComponentDefinition =>
25
+ new ComponentDefinition({ tag, template: '<b>??' + tag + '??</b>' });
26
+
27
+ const setInfo = (ctr: ComponentDefinitionInfo) => REGISTRY.set(ctr.tag, new ComponentDefinition(ctr));
28
+
29
+ const registerType = (ctr: string | ComponentDefinitionInfo | Constructor) => {
30
+ if (typeof ctr === 'string') {
31
+ ctr.replace(/<component\s+id="(.+)">([\s\S]*?)<\/component>/gm, (_, tag, template) => {
32
+ setInfo({ tag, template: template.trim() });
33
+ return '';
34
+ });
35
+ } else {
36
+ setInfo(typeof ctr === 'function' ? { Ctor: ctr, tag: ctr.name || fnName(ctr) } : ctr);
37
+ }
38
+ };
39
+
40
+ export const registerTypes = (types?: Array<string | ComponentDefinitionInfo | Constructor>) =>
41
+ types?.forEach(registerType);
42
+
43
+ export const getByTag = (tag: string) => REGISTRY.get(tag) || notFound(tag);
package/core/utils.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { nextId } from 'nextId';
2
+ import { identity } from 'noop';
3
+ import { attributesToString } from 'xml';
4
+
5
+ export const applyValue = (value: any, fn = identity) => (value?.then ? value.then(fn) : fn(value));
6
+
7
+ export const fnName = (ctor: object) => (/^function\s+([\w$]+)\s*\(/.exec(ctor.toString()) || [])[1] || nextId('C');
8
+
9
+ export const methodName = (x: unknown, pre = '') => {
10
+ const s = `${x || ''}`;
11
+ return pre + s[0].toUpperCase() + s.slice(1);
12
+ };
13
+
14
+ export const stringifyState = (attrs?: object | Hash) => {
15
+ if (!attrs) {
16
+ return '';
17
+ }
18
+ const r: string[] = [];
19
+ Object.entries(attrs).forEach(([k, v]) => {
20
+ if (v) {
21
+ r.push(' ' + k + '="' + attributesToString(v) + '"');
22
+ }
23
+ });
24
+ return r.length ? r.join('') : '';
25
+ };
@@ -0,0 +1,25 @@
1
+ import { DomElement, DomTextElement } from './elements';
2
+ import { reflow } from './reflow';
3
+
4
+ export class WebPlatform implements IPlatform {
5
+ rootElement: HTMLElement;
6
+
7
+ constructor(root: HTMLElement | string = 'root') {
8
+ if (typeof root === 'string') {
9
+ root = document.getElementById(root) || window.document.body;
10
+ }
11
+ this.rootElement = root || window.document.body;
12
+ }
13
+
14
+ createTextElement(initials: Hash, _: IComponent): IComponentImplementation {
15
+ return new DomTextElement(initials);
16
+ }
17
+
18
+ createElement(tag: string, initials: Hash, owner: IComponent): IComponentImplementation {
19
+ return new DomElement(tag, initials, owner);
20
+ }
21
+
22
+ reflow(root: IComponent) {
23
+ reflow(root, this.rootElement);
24
+ }
25
+ }
@@ -0,0 +1,197 @@
1
+ import { DomNode, IElement } from './type';
2
+
3
+ const camelize = (key: string, sep = '_', jn = ' ') =>
4
+ ('' + key)
5
+ .split(sep)
6
+ .map((s, i) => (i ? s[0].toUpperCase() + s.slice(1) : s))
7
+ .join(jn);
8
+
9
+ const setListener = ($: IElement, key: string, e: DomNode, v: unknown) => {
10
+ const fnKey = key.split(':')[0];
11
+ if (!v) {
12
+ setAttribute($, key, null);
13
+ } else {
14
+ setAttribute($, key, (ev: Event) => {
15
+ $.$attributes[fnKey]({ ...e.$dataset }, ev);
16
+ return false;
17
+ });
18
+ }
19
+ };
20
+ export function setAttribute($: IElement, key: string, value: null | string | Function) {
21
+ if (value != null) {
22
+ if (typeof value === 'function') {
23
+ const fnValue = (...args: unknown[]) => {
24
+ if (!$.isDone) {
25
+ value(...args);
26
+ }
27
+ };
28
+ if (!$.listeners) {
29
+ $.listeners = new Map();
30
+ }
31
+ if (!$.listeners.has(key)) {
32
+ const [akey, ekey = akey] = key.split(':');
33
+ $.element.addEventListener(ekey, fnValue, false);
34
+ $.listeners.set(key, fnValue);
35
+ }
36
+ } else {
37
+ $.element.setAttribute(key, value);
38
+ }
39
+ } else {
40
+ if ($.listeners?.has(key)) {
41
+ const [akey, ekey = akey] = key.split(':');
42
+ $.element.removeEventListener(ekey, $.listeners.get(key));
43
+ $.listeners.delete(key);
44
+ } else {
45
+ $.element.removeAttribute(key);
46
+ }
47
+ }
48
+ }
49
+
50
+ // DOM
51
+ export const ATTR_SETTERS: Hash<($: IElement, e: DomNode, v: unknown) => void> = {
52
+ disabled: (_, e, v) => (e.disabled = v ? true : null),
53
+ class: (_, e, v) => {
54
+ e.className = String(v || '');
55
+ },
56
+ selected: (_, e, v) => (e.selected = v ? true : null),
57
+ value: (_, e, v) => (e.value = v == null ? '' : v),
58
+ checked: (_, e, v) => (e.checked = !!v),
59
+
60
+ init: ($, e, v) => {
61
+ const fn = v as Function;
62
+ $.init = () => fn(e, $);
63
+ },
64
+ data: function (_, e, u) {
65
+ const v = u as Hash;
66
+ if (v && e.$dataset) {
67
+ Object.keys(e.$dataset).forEach((k) => {
68
+ if (v[k] == null) {
69
+ e.dataset[camelize(k, '-', '')] = null;
70
+ }
71
+ });
72
+ e.$dataset = { ...e.$dataset, ...v };
73
+ } else {
74
+ e.$dataset = v ? { ...v } : e.$dataset || {};
75
+ }
76
+ if (v) {
77
+ Object.keys(v).forEach((k) => {
78
+ e.dataset[camelize(k, '-', '')] = v[k];
79
+ });
80
+ }
81
+ },
82
+
83
+ click: ($, e, v) => setListener($, 'click:click', e, v),
84
+ ['bubble-click']: ($, e, v) => setListener($, 'bubble-click:click', e, v),
85
+ blur: ($, e, v) => setListener($, 'blur:blur', e, v),
86
+ dblclick: ($, e, v) => setListener($, 'dblclick:dblclick', e, v),
87
+ scroll: ($, e, v) => setListener($, 'scroll:scroll', e, v),
88
+
89
+ touchstart: function ($, e, v) {
90
+ const h = !v
91
+ ? null
92
+ : (ev: TouchEvent & { pageX: number; pageY: number }) => {
93
+ $.$attributes.touchstart(
94
+ {
95
+ ...e.$dataset,
96
+ x: ev.pageX || ev.changedTouches[0].screenX,
97
+ y: ev.pageY || ev.changedTouches[0].screenY,
98
+ },
99
+ ev
100
+ );
101
+ return false;
102
+ };
103
+ setAttribute($, 'touchstart:touchstart', h);
104
+ setAttribute($, 'touchstart:mousedown', h);
105
+ },
106
+ touch: function ($, e, v) {
107
+ const data = { ...e.$dataset };
108
+ const hs = !v
109
+ ? null
110
+ : (ev: TouchEvent & { pageX: number; pageY: number }) => {
111
+ data.active = true;
112
+ data.x = ev.pageX || ev.changedTouches[0].screenX;
113
+ data.y = ev.pageY || ev.changedTouches[0].screenY;
114
+ return false;
115
+ };
116
+ setAttribute($, 'touch:touchstart', hs);
117
+ setAttribute($, 'touch:mousedown', hs);
118
+ const h = !v
119
+ ? () => null
120
+ : (stop: boolean = false) =>
121
+ (ev: TouchEvent & { pageX: number; pageY: number }) => {
122
+ if (data.active) {
123
+ data.active = !stop;
124
+
125
+ data.xx = ev.pageX || ev.changedTouches[0].screenX;
126
+ data.yy = ev.pageY || ev.changedTouches[0].screenY;
127
+ data.dx = data.xx - data.x;
128
+ data.dy = data.yy - data.y;
129
+ $.$attributes.touch(data, ev);
130
+ }
131
+
132
+ return false;
133
+ };
134
+ setAttribute($, 'touch:touchcancel', h(true));
135
+ setAttribute($, 'touch:touchend', h(true));
136
+ setAttribute($, 'touch:mouseup', h(true));
137
+ setAttribute($, 'touch:touchmove', h(false));
138
+ setAttribute($, 'touch:mousemove', h(false));
139
+ },
140
+ keypress: function ($, e, v) {
141
+ setAttribute(
142
+ $,
143
+ 'keypress:keyup',
144
+ !v
145
+ ? null
146
+ : (ev: KeyboardEvent) => {
147
+ if (ev.keyCode !== 13 && ev.keyCode !== 27) {
148
+ const fn = $.$attributes.keypress;
149
+ fn && fn({ value: e.value, ...e.$dataset }, ev);
150
+ setTimeout(() => e.focus(), 0);
151
+ }
152
+ return false;
153
+ }
154
+ );
155
+ },
156
+ enter: function ($, e, v) {
157
+ setAttribute(
158
+ $,
159
+ 'enter:keyup',
160
+ !v
161
+ ? null
162
+ : (ev: KeyboardEvent) => {
163
+ if (ev.keyCode === 13) {
164
+ $.$attributes.enter({ value: e.value, ...e.$dataset }, ev);
165
+ }
166
+ if (ev.keyCode === 13 || ev.keyCode === 27) {
167
+ e.blur();
168
+ }
169
+ return false;
170
+ }
171
+ );
172
+ },
173
+ change: function ($, e, v) {
174
+ setAttribute(
175
+ $,
176
+ 'change:change',
177
+ !v
178
+ ? null
179
+ : (ev: Event) => {
180
+ $.$attributes.change({ value: e.value, ...e.$dataset }, ev);
181
+ return false;
182
+ }
183
+ );
184
+ },
185
+ toggle: function ($, e, v) {
186
+ setAttribute(
187
+ $,
188
+ 'toggle:change',
189
+ !v
190
+ ? null
191
+ : (ev: Event) => {
192
+ $.$attributes.toggle({ value: e.checked, ...e.$dataset }, ev);
193
+ return false;
194
+ }
195
+ );
196
+ },
197
+ };
@@ -0,0 +1,64 @@
1
+ import { ATTR_SETTERS, setAttribute } from './attributes';
2
+ import { DomNode, IElement } from './type';
3
+
4
+ export class DomElement implements IComponentImplementation, IElement {
5
+ $: IComponent;
6
+ $attributes?: Hash;
7
+ element: DomNode;
8
+ [key: string]: unknown;
9
+ isDone: boolean = false;
10
+ listeners: any;
11
+ init?: () => any;
12
+
13
+ constructor(tag: string, _: Hash, $: IComponent) {
14
+ this.$ = $;
15
+ this.element = document.createElement(tag) as DomNode;
16
+ this.element.impl = this;
17
+ }
18
+
19
+ done() {
20
+ this.element?.parentElement?.removeChild(this.element);
21
+ this.$attributes = undefined;
22
+ }
23
+
24
+ stateChanged(changes: Map<string, any>) {
25
+ const attrs: Hash = this.$attributes || (this.$attributes = {});
26
+ const e = this.element;
27
+ // this.$.log('changes', changes)
28
+ changes.forEach((value, key) => {
29
+ if (value !== attrs[key]) {
30
+ const setter = ATTR_SETTERS[key];
31
+ if (setter) {
32
+ setter(this, e, value);
33
+ } else if (key.slice(0, 5) === 'data-') {
34
+ ATTR_SETTERS.data(this, e, {
35
+ ...e.$dataset,
36
+ [key.slice(5)]: value,
37
+ });
38
+ } else {
39
+ setAttribute(this, key, value);
40
+ }
41
+ }
42
+ attrs[key] = value;
43
+ });
44
+ }
45
+ }
46
+
47
+ export class DomTextElement implements IComponentImplementation {
48
+ element: Text;
49
+ [key: string]: unknown;
50
+
51
+ constructor(attrs: Hash) {
52
+ this.element = document.createTextNode(String(attrs.text || ''));
53
+ }
54
+
55
+ done() {
56
+ this.element.parentElement?.removeChild(this.element);
57
+ }
58
+
59
+ stateChanged(changes: Map<string, any>) {
60
+ if (changes.has('text')) {
61
+ this.element.textContent = changes.get('text');
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,19 @@
1
+ import { CArrmaturaNode, Platform } from '../core';
2
+ import { WebPlatform } from './WebPlatform';
3
+
4
+ export type ArrmaturaOptionsForWeb = ArrmaturaOptions & {
5
+ rootElement?: HTMLElement;
6
+ };
7
+
8
+ export function arrmatura(
9
+ template: string,
10
+ { rootElement, pipes, functions = pipes, types, resources }: ArrmaturaOptionsForWeb
11
+ ) {
12
+ Platform.instance = new WebPlatform(rootElement);
13
+
14
+ const node = new CArrmaturaNode(template, { functions, types, resources });
15
+
16
+ node.launch();
17
+ }
18
+
19
+ Object.defineProperty(window, 'arrmatura', { value: arrmatura });
@@ -0,0 +1,30 @@
1
+ import { DomNode } from './type';
2
+
3
+ function appendElt(e: DomNode, p: DomNode | HTMLElement, cursor?: DomNode): DomNode {
4
+ const before = cursor ? cursor.nextSibling : p.firstChild;
5
+ if (!before) {
6
+ if (p !== e.parentElement) {
7
+ p.appendChild(e);
8
+ }
9
+ } else if (e !== before) {
10
+ p.insertBefore(e, before);
11
+ }
12
+ return e;
13
+ }
14
+
15
+ export function reflow($: IComponent, parent: DomNode | HTMLElement, cursor?: DomNode) {
16
+ if ($.children) {
17
+ for (let p of $.children.values()) {
18
+ const e = p.impl.element as DomNode;
19
+ if (e) {
20
+ if (e) {
21
+ reflow(p, e);
22
+ cursor = appendElt(e, parent, cursor);
23
+ }
24
+ } else {
25
+ cursor = reflow(p, parent, cursor);
26
+ }
27
+ }
28
+ }
29
+ return cursor;
30
+ }