arrmatura 5.2.0 → 6.3.1
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 +29 -38
- package/docs/glossary.md +19 -0
- package/docs/hello.md +46 -0
- package/docs/manual.md +294 -0
- package/index.ts +21 -0
- package/package.json +16 -44
- package/src/core/Component.ts +67 -0
- package/src/core/Entitron.ts +406 -0
- package/src/core/ManifestNode.ts +139 -0
- package/src/core/resolveExpression.ts +20 -0
- package/src/registry/composition.ts +66 -0
- package/src/registry/conditionals.ts +75 -0
- package/src/registry/elementary.ts +36 -0
- package/src/registry/fragment.ts +21 -0
- package/src/registry/index.ts +69 -0
- package/src/registry/iterations.ts +122 -0
- package/src/registry/root.ts +46 -0
- package/src/registry/routing.ts +31 -0
- package/src/registry/selection.ts +57 -0
- package/src/registry/slot.ts +36 -0
- package/src/utils/compileExpression.ts +108 -0
- package/src/utils/stringify.ts +33 -0
- package/types.ts +73 -0
- package/LICENSE +0 -21
- package/commons/auth/AuthService.ts +0 -111
- package/commons/auth/UserService.ts +0 -57
- package/commons/auth/index.ts +0 -5
- package/commons/auth/user.xml +0 -64
- package/commons/components/buttons.xml +0 -25
- package/commons/components/index.ts +0 -13
- package/commons/components/inputs.xml +0 -33
- package/commons/components/layouts.xml +0 -77
- package/commons/components/media.xml +0 -21
- package/commons/components/modal.xml +0 -37
- package/commons/components/navigation.xml +0 -73
- package/commons/components/popups.xml +0 -12
- package/commons/components/searchbar.xml +0 -54
- package/commons/components/table.xml +0 -140
- package/commons/components/toasts.xml +0 -18
- package/commons/components/viewport.xml +0 -48
- package/commons/forms/FieldItem.xml +0 -20
- package/commons/forms/FormController.ts +0 -87
- package/commons/forms/Item.ts +0 -132
- package/commons/forms/ItemCollectionController.ts +0 -113
- package/commons/forms/ItemController.ts +0 -91
- package/commons/forms/MultiEnumField.xml +0 -44
- package/commons/forms/MultivalueController.ts +0 -49
- package/commons/forms/NewItemController.ts +0 -46
- package/commons/forms/SmartareaField.xml +0 -51
- package/commons/forms/SuggestionController.ts +0 -80
- package/commons/forms/SuggestionField.xml +0 -28
- package/commons/forms/fields.xml +0 -151
- package/commons/forms/form.xml +0 -37
- package/commons/forms/index.ts +0 -29
- package/commons/index.ts +0 -6
- package/commons/services/ApiClientService.ts +0 -32
- package/commons/services/ErrorHandlingService.ts +0 -11
- package/commons/services/FirebaseService.ts +0 -185
- package/commons/services/GSheetsService.ts +0 -120
- package/commons/services/HeartbeatService.ts +0 -35
- package/commons/services/Invoke.ts +0 -29
- package/commons/services/JsonpService.ts +0 -18
- package/commons/services/Loader.ts +0 -17
- package/commons/services/MetadataService.ts +0 -50
- package/commons/services/NavigationService.ts +0 -74
- package/commons/services/Service.ts +0 -62
- package/commons/services/ServiceWorker.ts +0 -111
- package/commons/services/Storage.ts +0 -55
- package/commons/services/ToastService.ts +0 -61
- package/commons/services/index.ts +0 -13
- package/core/Arrmatura.ts +0 -79
- package/core/CNode.ts +0 -111
- package/core/Component.ts +0 -424
- package/core/cnodes/composition.ts +0 -100
- package/core/cnodes/conditionals.ts +0 -52
- package/core/cnodes/elementary.ts +0 -44
- package/core/cnodes/iterations.ts +0 -92
- package/core/cnodes/routing.ts +0 -25
- package/core/compileNode.ts +0 -39
- package/core/expression.ts +0 -73
- package/core/index.ts +0 -1
- package/core/register.ts +0 -43
- package/core/utils.ts +0 -23
- package/core-web/WebPlatform.ts +0 -25
- package/core-web/attributes.ts +0 -218
- package/core-web/elements.ts +0 -81
- package/core-web/index.ts +0 -28
- package/core-web/reflow.ts +0 -30
- package/core-web/support.ts +0 -27
- package/core-web/type.ts +0 -18
- package/docs/index.js +0 -485
- package/lib/array/arrayGroupBy.ts +0 -19
- package/lib/array/arraySortBy.ts +0 -49
- package/lib/array/arrayToObject.ts +0 -18
- package/lib/array/arrayish.ts +0 -7
- package/lib/array/index.ts +0 -4
- package/lib/crypto/index.ts +0 -25
- package/lib/date/index.ts +0 -127
- package/lib/func/curry.ts +0 -11
- package/lib/func/fx.ts +0 -56
- package/lib/func/identity.ts +0 -6
- package/lib/func/nextId.ts +0 -9
- package/lib/func/noop.ts +0 -6
- package/lib/geo/index.js +0 -22
- package/lib/index.ts +0 -17
- package/lib/object/index.ts +0 -69
- package/lib/parsing/codus.ts +0 -25
- package/lib/parsing/compiler.ts +0 -26
- package/lib/parsing/scalarParse.ts +0 -25
- package/lib/parsing/urls.ts +0 -91
- package/lib/parsing/xml.ts +0 -117
- package/lib/resources/l10n.ts +0 -40
- package/lib/resources/resources.ru.ts +0 -20
- package/lib/resources/resources.ts +0 -20
- package/lib/string/index.ts +0 -58
- package/lib/support/createListeners.ts +0 -12
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
+
import { Entitron } from "../core/Entitron";
|
|
3
|
+
import type { Hash, XmlNode } from "ultimus/types";
|
|
4
|
+
import { IPlatform } from "arrmatura/types";
|
|
5
|
+
|
|
6
|
+
class NativeElementEntitron extends Entitron<CElementNode> {
|
|
7
|
+
get displayName(): string {
|
|
8
|
+
return this.manifest.tag;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
createComponent(initials: Hash) {
|
|
12
|
+
return this.platform.createComponent({ tag: this.manifest.tag, native: true }, initials, this);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class CElementNode extends ManifestNode {
|
|
17
|
+
#nodes: XmlNode[] | undefined;
|
|
18
|
+
|
|
19
|
+
constructor({ tag, attrs, nodes, text }: XmlNode) {
|
|
20
|
+
super();
|
|
21
|
+
this.tag = tag;
|
|
22
|
+
this.compileAttributes(attrs);
|
|
23
|
+
if (text) {
|
|
24
|
+
this.compileAttribute("#text", text);
|
|
25
|
+
}
|
|
26
|
+
this.#nodes = nodes;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getSubNodes(platform: IPlatform) {
|
|
30
|
+
return this.#nodes ? platform.getCompiledNodes(this.#nodes) : undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get EntitronConstructor() {
|
|
34
|
+
return NativeElementEntitron;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
+
import { Entitron } from "../core/Entitron";
|
|
3
|
+
import type { XmlNode } from "ultimus/types";
|
|
4
|
+
import type { IPlatform } from "arrmatura/types";
|
|
5
|
+
|
|
6
|
+
export class CFragmentNode extends ManifestNode {
|
|
7
|
+
#nodes: XmlNode[] | undefined;
|
|
8
|
+
|
|
9
|
+
constructor({ nodes }: XmlNode) {
|
|
10
|
+
super();
|
|
11
|
+
this.#nodes = nodes;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getSubNodes(platform: IPlatform) {
|
|
15
|
+
return this.#nodes ? platform.getCompiledNodes(this.#nodes) : undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get EntitronConstructor() {
|
|
19
|
+
return Entitron;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { CForNode } from "./iterations";
|
|
2
|
+
import { CCompositeNode } from "./composition";
|
|
3
|
+
import { CIfNode } from "./conditionals";
|
|
4
|
+
import { CElementNode } from "./elementary";
|
|
5
|
+
import { CDynamicTagNode } from "./routing";
|
|
6
|
+
import { CSlotNode } from "./slot";
|
|
7
|
+
import { CFragmentNode } from "./fragment";
|
|
8
|
+
import { CSelectorNode } from "./selection";
|
|
9
|
+
|
|
10
|
+
import { Uid, XmlNode } from "ultimus";
|
|
11
|
+
import { IManifestNode } from "../../types";
|
|
12
|
+
|
|
13
|
+
export const Registry = {
|
|
14
|
+
CForNode,
|
|
15
|
+
CIfNode,
|
|
16
|
+
CDynamicTagNode,
|
|
17
|
+
CSlotNode,
|
|
18
|
+
CFragmentNode,
|
|
19
|
+
CCompositeNode,
|
|
20
|
+
CSelectorNode,
|
|
21
|
+
CElementNode,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const cloneNode = (x: XmlNode, eliminateAttr: string): XmlNode => {
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
26
|
+
const { [eliminateAttr]: _, ...attrs } = x.attrs ?? {};
|
|
27
|
+
return {
|
|
28
|
+
text: x.text,
|
|
29
|
+
tag: x.tag,
|
|
30
|
+
id: x.id,
|
|
31
|
+
attrs,
|
|
32
|
+
nodes: x.nodes?.map((n) => n),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const __compileManifestNode = (x: XmlNode): IManifestNode => {
|
|
37
|
+
const { tag = 'div', attrs } = x;
|
|
38
|
+
if (attrs?.["each"]) {
|
|
39
|
+
return new Registry.CForNode(cloneNode(x, "each"), String(attrs["each"]).trim().split(" "));
|
|
40
|
+
}
|
|
41
|
+
if (attrs?.["if"]) {
|
|
42
|
+
return new Registry.CIfNode(cloneNode(x, "if"), String(attrs["if"]));
|
|
43
|
+
}
|
|
44
|
+
if (tag === "div" || tag[0]?.match(/[A-Z0-9]/) == null) {
|
|
45
|
+
return new Registry.CElementNode(x);
|
|
46
|
+
}
|
|
47
|
+
if (tag === "Dynamic") {
|
|
48
|
+
return new Registry.CDynamicTagNode(cloneNode(x, "as"), String(attrs?.as ?? "Error.Dynamic"));
|
|
49
|
+
}
|
|
50
|
+
if (tag === "Slot") {
|
|
51
|
+
return new Registry.CSlotNode(x);
|
|
52
|
+
}
|
|
53
|
+
if (tag === "Selector") {
|
|
54
|
+
return new Registry.CSelectorNode(x);
|
|
55
|
+
}
|
|
56
|
+
if (tag === "Fragment" || tag === "Then" || tag === "Else") {
|
|
57
|
+
return new Registry.CFragmentNode(x);
|
|
58
|
+
}
|
|
59
|
+
return new Registry.CCompositeNode(x);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const CTXNODES = new Map();
|
|
63
|
+
|
|
64
|
+
export const compileManifestNode = (x: XmlNode): IManifestNode => {
|
|
65
|
+
return CTXNODES.has(x) ? CTXNODES.get(x) : CTXNODES.set(x, __compileManifestNode(x)).get(x);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const compileManifestNodes = (x: XmlNode[]): Map<Uid, IManifestNode> =>
|
|
69
|
+
new Map<Uid, IManifestNode>(x?.map(compileManifestNode).map((c) => [c.uid, c]) ?? []);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
+
import { Entitron } from "../core/Entitron";
|
|
3
|
+
import type { Datum, Delta, Hash, Uid, XmlNode } from "ultimus/types";
|
|
4
|
+
import type { EContent, IManifestNode, IPlatform } from "arrmatura/types";
|
|
5
|
+
import { compilePlaceholder } from "../utils/compileExpression";
|
|
6
|
+
|
|
7
|
+
const narrowData = (data: any) => {
|
|
8
|
+
if (!data) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(data)) return data;
|
|
12
|
+
if (typeof data[Symbol.iterator] === 'function') return [...data];
|
|
13
|
+
if (typeof data === "string") return data.split(',').map(id => ({ id, name: id }));
|
|
14
|
+
if (typeof data === "object") return Object.values(data);
|
|
15
|
+
return [{ id: String(data), name: String(data) }];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class Iterative extends Entitron<CForNode> {
|
|
19
|
+
pkHash: Hash<Datum> = {};
|
|
20
|
+
|
|
21
|
+
get displayName(): string {
|
|
22
|
+
return "🔹each";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get contentManifests() {
|
|
26
|
+
const each: any = narrowData(this.component.each);
|
|
27
|
+
const itemNode = this.manifest.getItemCtxNode(this.platform);
|
|
28
|
+
const nodes: Map<Uid, IManifestNode> = new Map();
|
|
29
|
+
this.pkHash = {};
|
|
30
|
+
if (each?.length) {
|
|
31
|
+
if (!each.forEach) {
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
33
|
+
throw new Error(`[each] Items has no forEach() ${each.toString()}`);
|
|
34
|
+
}
|
|
35
|
+
each.forEach((datum: Datum, index: number) => {
|
|
36
|
+
let id = datum.id;
|
|
37
|
+
if (id == null) {
|
|
38
|
+
this.logError("ERROR: empty item id: ", datum);
|
|
39
|
+
id = String(index);
|
|
40
|
+
}
|
|
41
|
+
const pk = String(id);
|
|
42
|
+
if (this.pkHash[pk]) {
|
|
43
|
+
this.logError(`ERROR: duplicate item id : ${pk} (skipped)`, datum);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this.pkHash[pk] = datum;
|
|
47
|
+
const inode = itemNode
|
|
48
|
+
.cloneWithDatum(datum, this.platform)
|
|
49
|
+
.addPropertyResolver(() => this.pkHash[pk], itemNode.itemName);
|
|
50
|
+
nodes.set(inode.uid, inode);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return nodes;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class CForNode extends ManifestNode {
|
|
58
|
+
#itemNode?: CItemNode;
|
|
59
|
+
#itemName: string;
|
|
60
|
+
#xmlNode: XmlNode;
|
|
61
|
+
constructor(xmlNode: XmlNode, [itemName, prep, expr = prep]: string[]) {
|
|
62
|
+
super();
|
|
63
|
+
this.#xmlNode = xmlNode;
|
|
64
|
+
this.#itemName = itemName;
|
|
65
|
+
if (expr[0] === "<" && expr[1] === "-") {
|
|
66
|
+
this.addConnector(expr.slice(2), "each");
|
|
67
|
+
} else {
|
|
68
|
+
this.addPropertyResolver(compilePlaceholder(expr), "each");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getItemCtxNode(platform: IPlatform) {
|
|
73
|
+
return (
|
|
74
|
+
this.#itemNode ?? (this.#itemNode = new CItemNode(this.#itemName, platform.getCompiledNodes([this.#xmlNode])))
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get EntitronConstructor() {
|
|
79
|
+
return Iterative;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class IterativeItem extends Entitron<CItemNode> {
|
|
84
|
+
get recontentScope(): Entitron {
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get displayName(): string {
|
|
89
|
+
return "🔹ui:for:item";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
emit(key: string, data: Delta) {
|
|
93
|
+
return this.scope.emit(key, data);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
get(propId: string) {
|
|
97
|
+
const iName: string = this.manifest.itemName;
|
|
98
|
+
const value = propId.startsWith(`${iName}.`) || propId === iName ? super.get(propId) : this.scope.get(propId);
|
|
99
|
+
// this.log(propId, value);
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class CItemNode extends ManifestNode {
|
|
105
|
+
itemName: string;
|
|
106
|
+
constructor(itemName: string, content?: EContent) {
|
|
107
|
+
super();
|
|
108
|
+
this.itemName = itemName;
|
|
109
|
+
this.$content = content;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
cloneWithDatum(datum: Datum, platform: IPlatform) {
|
|
113
|
+
const uid = `${String(this.uid)}#${datum.id}`;
|
|
114
|
+
return Object.assign(new CItemNode(this.itemName, this.getSubNodes(platform)), {
|
|
115
|
+
uid,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get EntitronConstructor() {
|
|
120
|
+
return IterativeItem;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
+
import { Entitron } from "../core/Entitron";
|
|
3
|
+
import type { Hash, XmlNode } from "ultimus/types";
|
|
4
|
+
import type { EContent, IPlatform } from "arrmatura/types";
|
|
5
|
+
import { xmlParse } from "ultimus";
|
|
6
|
+
import { CCompositeNode } from "./composition";
|
|
7
|
+
|
|
8
|
+
class RootCtx extends Entitron<CCompositeNode> {
|
|
9
|
+
get displayName(): string {
|
|
10
|
+
return `ROOT`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get recontentScope(): Entitron {
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
slotContent(_slotId?: string): EContent | undefined {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
createComponent(initials: Hash) {
|
|
22
|
+
return this.platform.createComponent({}, initials, this);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class CRootNode extends ManifestNode {
|
|
27
|
+
slotContent: Hash<EContent>;
|
|
28
|
+
nodes?: XmlNode["nodes"];
|
|
29
|
+
|
|
30
|
+
constructor(private template: string) {
|
|
31
|
+
super();
|
|
32
|
+
this.slotContent = {};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getSubNodes(platform: IPlatform) {
|
|
36
|
+
return platform.getCompiledNodes(xmlParse(this.template));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getSlotContent(_platform: IPlatform, _slotId?: string) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get EntitronConstructor() {
|
|
44
|
+
return RootCtx;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
+
import { Entitron } from "../core/Entitron";
|
|
3
|
+
import type { XmlNode } from "ultimus/types";
|
|
4
|
+
|
|
5
|
+
class DynamicTag extends Entitron<CDynamicTagNode> {
|
|
6
|
+
get displayName(): string {
|
|
7
|
+
return "dynamics";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get contentManifests() {
|
|
11
|
+
const tag = this.component.tag;
|
|
12
|
+
const uid = `${this.uid}:${String(tag ?? "")}`;
|
|
13
|
+
return new Map([
|
|
14
|
+
[uid, Object.assign(this.platform.getCompiledNodes({ ...this.manifest.xml, tag } as XmlNode), { uid })],
|
|
15
|
+
]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class CDynamicTagNode extends ManifestNode {
|
|
20
|
+
xml: XmlNode & { tag: string };
|
|
21
|
+
|
|
22
|
+
constructor(xml: XmlNode, expr: string) {
|
|
23
|
+
super();
|
|
24
|
+
this.compileAttribute("tag", expr);
|
|
25
|
+
this.xml = xml;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get EntitronConstructor() {
|
|
29
|
+
return DynamicTag;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
+
import { Entitron } from "../core/Entitron";
|
|
3
|
+
import type { Hash, XmlNode } from "ultimus/types";
|
|
4
|
+
import type { EContent, IPlatform } from "arrmatura/types";
|
|
5
|
+
|
|
6
|
+
export class Selector extends Entitron<CSelectorNode> {
|
|
7
|
+
get displayName(): string {
|
|
8
|
+
return `💠Selector`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Gets the content of the function.
|
|
13
|
+
*
|
|
14
|
+
* @return {any} The content of the function.
|
|
15
|
+
*/
|
|
16
|
+
get contentManifests() {
|
|
17
|
+
const cases = this.manifest.getCases(this.platform);
|
|
18
|
+
const matchedCase = cases?.[this.component.on as string];
|
|
19
|
+
if (!matchedCase) {
|
|
20
|
+
this.log("unmatched case", this.component.on, cases);
|
|
21
|
+
}
|
|
22
|
+
return matchedCase ?? cases?.["default"] ?? Object.values(cases)[0];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class CSelectorNode extends ManifestNode {
|
|
27
|
+
cases: Hash<EContent> | null = null;
|
|
28
|
+
key = "";
|
|
29
|
+
nodes?: XmlNode["nodes"];
|
|
30
|
+
|
|
31
|
+
constructor({ tag, attrs, nodes }: XmlNode) {
|
|
32
|
+
super();
|
|
33
|
+
this.tag = tag;
|
|
34
|
+
this.nodes = nodes;
|
|
35
|
+
this.compileAttributes(attrs);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Retrieves the cases nodes for a component.
|
|
40
|
+
*
|
|
41
|
+
* @param {IPlatform} platform - The platform to get cases for.
|
|
42
|
+
* @return {Hash<EContent>} - The cases for the platform.
|
|
43
|
+
*/
|
|
44
|
+
getCases(platform: IPlatform): Hash<EContent> {
|
|
45
|
+
return (
|
|
46
|
+
this.cases ??
|
|
47
|
+
(this.cases = (this.nodes ?? []).reduce<Hash<EContent>>((groups, node) => {
|
|
48
|
+
groups[node.attrs?.when as string] = platform.getCompiledNodes(node.nodes ?? []);
|
|
49
|
+
return groups;
|
|
50
|
+
}, {}))
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get EntitronConstructor() {
|
|
55
|
+
return Selector;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
+
import { Entitron } from "../core/Entitron";
|
|
3
|
+
import type { XmlNode } from "ultimus/types";
|
|
4
|
+
import type { IEntitron } from "arrmatura/types";
|
|
5
|
+
import { Composite } from "./composition";
|
|
6
|
+
|
|
7
|
+
class Slot extends Entitron {
|
|
8
|
+
get displayName(): string {
|
|
9
|
+
return "🔸";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get compositeScope(): Composite {
|
|
13
|
+
return this.scope as Composite;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get contentManifests() {
|
|
17
|
+
return this.compositeScope.slotContent((this.manifest as CSlotNode).key);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get recontentScope(): IEntitron {
|
|
21
|
+
return this.compositeScope.scope;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class CSlotNode extends ManifestNode {
|
|
26
|
+
key?: string;
|
|
27
|
+
|
|
28
|
+
constructor(x: XmlNode) {
|
|
29
|
+
super();
|
|
30
|
+
this.key = x.attrs?.key as string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get EntitronConstructor() {
|
|
34
|
+
return Slot;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { scalarParse } from "ultimus";
|
|
2
|
+
import type { ExpressionText, Fn, StringHash } from "ultimus/types";
|
|
3
|
+
import type { IEntitron } from "arrmatura/types";
|
|
4
|
+
|
|
5
|
+
type PipeFn = (c: IEntitron, argValue: unknown) => unknown;
|
|
6
|
+
|
|
7
|
+
const OP_SHORTCUTS: StringHash = {
|
|
8
|
+
"&&": "|and:",
|
|
9
|
+
"==": "|equals:",
|
|
10
|
+
"!=": "|notEquals:",
|
|
11
|
+
"||": "|or:",
|
|
12
|
+
"<": "|less:",
|
|
13
|
+
"[": "|dot:",
|
|
14
|
+
"]": "",
|
|
15
|
+
">": "|greater:",
|
|
16
|
+
"??": "|coalesce:",
|
|
17
|
+
"?": "|then:",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const withNoPipes = (_: unknown, v: unknown) => v;
|
|
21
|
+
|
|
22
|
+
export function compileConstant(a: string) {
|
|
23
|
+
const vv = scalarParse(a[0] === "'" ? a.slice(1, a.length - 1) : a);
|
|
24
|
+
return () => vv;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const compilePropGet = (key: string) => {
|
|
28
|
+
return (c: IEntitron) => c.getFromScope(key);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const compileArgument = (a: string) => {
|
|
32
|
+
if (a[0] !== "@") return compileConstant(a);
|
|
33
|
+
|
|
34
|
+
if (a.endsWith("#not")) {
|
|
35
|
+
const getter = compilePropGet(a.slice(1, a.length - 4));
|
|
36
|
+
return (c: IEntitron) => !getter(c);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return compilePropGet(a.slice(1));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const splitExpression = (v: ExpressionText) =>
|
|
43
|
+
v
|
|
44
|
+
.replace(/(&&|!=|==|>|<|\[|\]|\|\||\?\??)/g, (_, s) => `${OP_SHORTCUTS[s]}`)
|
|
45
|
+
.split("|")
|
|
46
|
+
.map((s) => s.trim());
|
|
47
|
+
|
|
48
|
+
function withPipes(pipes: string[]) {
|
|
49
|
+
if (!pipes.length) return withNoPipes;
|
|
50
|
+
|
|
51
|
+
const compiledPipes = pipes.map((key) => {
|
|
52
|
+
const [pipeId, ...args] = key.split(":").map((s) => s.trim());
|
|
53
|
+
return {
|
|
54
|
+
pipeId,
|
|
55
|
+
args: args.map((a) => compileArgument(a)),
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return (c: IEntitron, initialValue: unknown) =>
|
|
60
|
+
compiledPipes.reduce((value, { pipeId, args }) => {
|
|
61
|
+
try {
|
|
62
|
+
const fn = c.platform.getFunction(pipeId);
|
|
63
|
+
if (typeof fn !== "function") {
|
|
64
|
+
throw new Error(`${fn != null ? "must be a function" : "not found"}`);
|
|
65
|
+
}
|
|
66
|
+
const args0 = [value, ...args.map((fn) => fn(c))];
|
|
67
|
+
return fn.apply(c, args0);
|
|
68
|
+
} catch (ex) {
|
|
69
|
+
c.logError(`ERROR: Function ${pipeId || "<empty>"}`, ex);
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
}, initialValue);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function withHardValue(hardValue: ExpressionText, pipec: PipeFn): PipeFn {
|
|
76
|
+
if (!hardValue || hardValue === '*') return pipec;
|
|
77
|
+
const iniFn = hardValue[0] === "@" ? compilePlaceholder(hardValue) : compileConstant(hardValue);
|
|
78
|
+
return (c: IEntitron) => pipec(c, iniFn(c));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function compilePipeExpression(v: ExpressionText) {
|
|
82
|
+
const [_key, ...pipes] = splitExpression(v);
|
|
83
|
+
const [key, hardValue] = _key.split("=").map((s) => s.trim());
|
|
84
|
+
return { key, pipec: withHardValue(hardValue, withPipes(pipes)) };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function compilePlaceholder(expr: string): Fn {
|
|
88
|
+
const [key, ...pipesExpressions] = splitExpression(expr);
|
|
89
|
+
const pipec = withPipes(pipesExpressions);
|
|
90
|
+
|
|
91
|
+
let fn: any = null;
|
|
92
|
+
|
|
93
|
+
if (key[0] === "'") {
|
|
94
|
+
fn = compileConstant(key);
|
|
95
|
+
} else {
|
|
96
|
+
if (key[0] !== "@") {
|
|
97
|
+
console.warn(`Invalid placeholder: missed @-prefix: "${expr}"`);
|
|
98
|
+
}
|
|
99
|
+
fn = compileArgument(key[0] === "@" ? key : `@${key}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (c) => pipec(c as IEntitron, fn(c));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function compileJsExpression(x: string) {
|
|
106
|
+
return new Function("c", x.replaceAll(/[^'a-z]([a-z][a-z0-9-\.]+)/gi, 'c.prop("$1")'));
|
|
107
|
+
}
|
|
108
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Hash } from "ultimus";
|
|
2
|
+
const attributesToString = (v?: Hash): string => {
|
|
3
|
+
if (typeof v === "object") {
|
|
4
|
+
if (Array.isArray(v)) {
|
|
5
|
+
return !v.length ? "[]" : `[${attributesToString(v[0]) ?? ""} x${String(v.length)}]`;
|
|
6
|
+
}
|
|
7
|
+
return `{${Object.keys(v).toString()}}`;
|
|
8
|
+
}
|
|
9
|
+
return v ?? "";
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const stringifyState = (attrs?: object | Hash) => {
|
|
13
|
+
if (!attrs) {
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const r: string[] = [];
|
|
18
|
+
Object.entries(attrs).forEach(([k, v]) => {
|
|
19
|
+
if (v && k[0] !== "$") {
|
|
20
|
+
r.push(` ${k}="${attributesToString(v)}"`);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return r.length ? r.join("") : "";
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const stringify = (T: any, tab = ""): string => {
|
|
28
|
+
const { uid, displayName: tag = uid, component: impl, children } = T;
|
|
29
|
+
|
|
30
|
+
const ssubs: string = children?.size ? [...children.values()].map((c) => stringify(c, ` ${tab}`)).join("\n") : "";
|
|
31
|
+
|
|
32
|
+
return `${tab}<${tag}${stringifyState(impl)}${!ssubs ? "/>" : `>\n${ssubs}\n${tab}</${tag}>`}`;
|
|
33
|
+
};
|
package/types.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Uid, Delta, Data, LogEntry, Hash, XmlNode } from "ultimus/types";
|
|
2
|
+
|
|
3
|
+
export type IComponent = {
|
|
4
|
+
$element?: unknown; // native implementation
|
|
5
|
+
done?: (c: IEntitron) => void;
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
7
|
+
init?: (c: IEntitron) => Delta | null | undefined | unknown | void;
|
|
8
|
+
stateChanged?: (changes: Map<string, unknown>) => void;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type IEntitron = {
|
|
13
|
+
isDone: any;
|
|
14
|
+
readonly uid: Uid;
|
|
15
|
+
parent?: IEntitron;
|
|
16
|
+
children?: Map<Uid, IEntitron>;
|
|
17
|
+
scope?: IEntitron;
|
|
18
|
+
component: IComponent;
|
|
19
|
+
root: IEntitron;
|
|
20
|
+
platform: IPlatform;
|
|
21
|
+
|
|
22
|
+
up: (d: Delta | null | void | unknown, force?: boolean) => void;
|
|
23
|
+
touch: () => void;
|
|
24
|
+
emit: (key: string, arg1: Delta) => void;
|
|
25
|
+
res: (key: string | string[]) => Data;
|
|
26
|
+
log: (val: unknown, ...arg1: unknown[]) => void;
|
|
27
|
+
logError: (error: unknown, ...arg1: unknown[]) => void;
|
|
28
|
+
defer: (fn: () => void) => void;
|
|
29
|
+
get: (propId: string) => unknown;
|
|
30
|
+
|
|
31
|
+
done: () => void;
|
|
32
|
+
|
|
33
|
+
addReference: (refId: string, r: IEntitron) => void;
|
|
34
|
+
getByRef: (refId: string) => IEntitron | undefined;
|
|
35
|
+
getFromScope: (key: string) => unknown;
|
|
36
|
+
settleAsChild: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type IManifestNode = {
|
|
40
|
+
connectors?: Map<string, (c: IEntitron, x: unknown) => unknown>;
|
|
41
|
+
refId?: string;
|
|
42
|
+
readonly uid: Uid;
|
|
43
|
+
getSubNodes(platform: IPlatform): Map<Uid, IManifestNode> | undefined;
|
|
44
|
+
resolveProps: (c: IEntitron) => Delta | Promise<unknown> | undefined;
|
|
45
|
+
resolveInitialProps: (c: IEntitron) => Hash;
|
|
46
|
+
|
|
47
|
+
createEntitron: (platform: IPlatform, parent?: IEntitron, scope?: IEntitron) => IEntitron;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type IPlatform = {
|
|
51
|
+
log(arg0: LogEntry): void;
|
|
52
|
+
getCompiledNodes(tag: string): Map<Uid, IManifestNode>;
|
|
53
|
+
getCompiledNodes(tag: XmlNode): IManifestNode;
|
|
54
|
+
getCompiledNodes(tag: XmlNode[]): Map<Uid, IManifestNode>;
|
|
55
|
+
getResource(key: string | string[]): Data;
|
|
56
|
+
getFunction(id: string): (...args: unknown[]) => unknown;
|
|
57
|
+
redraw(source: IEntitron, root: IEntitron): void;
|
|
58
|
+
createComponent(def: IComponentDescriptor | string, initials: Hash, ctx: IEntitron): IComponent | Hash;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type ComponentConstructor = new (initials: Hash, c: IEntitron) => IComponent;
|
|
62
|
+
|
|
63
|
+
export type IComponentDescriptor = {
|
|
64
|
+
tag?: string;
|
|
65
|
+
template?: string;
|
|
66
|
+
Ctor?: ComponentConstructor;
|
|
67
|
+
unknown?: boolean;
|
|
68
|
+
native?: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type EclecticTypeInfo = string | IComponentDescriptor | ComponentConstructor;
|
|
72
|
+
|
|
73
|
+
export type EContent = Map<Uid, IManifestNode>;
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2017 Alex Litskevich
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|