arrmatura 6.3.1 → 6.3.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 +6 -10
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/docs/manual.md +84 -66
- package/index.ts +5 -4
- package/package.json +10 -4
- package/src/core/{Entitron.ts → Arrmatron.ts} +147 -136
- package/src/core/Component.ts +44 -21
- package/src/core/ManifestNode.ts +36 -25
- package/src/core/resolveExpression.ts +9 -4
- package/src/registry/composition.ts +9 -8
- package/src/registry/conditionals.ts +10 -8
- package/src/registry/connector.ts +47 -0
- package/src/registry/elementary.ts +7 -6
- package/src/registry/fragment.ts +7 -6
- package/src/registry/index.ts +21 -25
- package/src/registry/iterations.ts +30 -18
- package/src/registry/root.ts +8 -6
- package/src/registry/routing.ts +6 -5
- package/src/registry/selection.ts +11 -9
- package/src/registry/slot.ts +8 -7
- package/src/utils/FingerprintMashine.ts +128 -0
- package/src/utils/applyStateChangedToImpl.ts +18 -0
- package/src/utils/compileExpression.ts +136 -29
- package/src/utils/objectFingerprint.spec.ts +70 -0
- package/types.ts +38 -29
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Definitive
|
|
4
4
|
|
|
5
|
-
`Arrmatura` is a
|
|
5
|
+
`Arrmatura` is a framework which inherits declarative approach and functional programming.
|
|
6
|
+
|
|
6
7
|
It comprises of
|
|
7
8
|
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- Web UI kits with various components, services, forms, rich UI etc.
|
|
9
|
+
- extended HTML notation to define components, its composition, behaviour and data flow.
|
|
10
|
+
- type system and runtime enabling development of applications of any kind.
|
|
11
|
+
- `IPlatform` implementation for web client applications.
|
|
12
|
+
- Web UI kits with plenty of components, services, forms, editors etc.
|
|
13
13
|
|
|
14
14
|
## Key features and benefits of the platform
|
|
15
15
|
|
|
@@ -39,7 +39,3 @@ See [Hello, world](docs/HELLO.md) for starting example.
|
|
|
39
39
|
- [Emoji List](https://emojis-list.web.app/)
|
|
40
40
|
- [Countries List](https://countries-list.web.app/)
|
|
41
41
|
- [Game Solver](https://dlitskevich.github.io/solver/) ([source](https://github.com/dlitskevich/solver/tree/master/app))
|
|
42
|
-
|
|
43
|
-
## Limitations and considerations
|
|
44
|
-
|
|
45
|
-
TBD
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{CRootNode as m}from"./src/registry/root";export*from"./src/registry";import{CRootNode as c}from"./src/registry/root";export*from"./src/core/Component";var n=(o,t)=>{let r=new m(t).createArrmatron(o);return r.up({},!0),r};export{c as CRootNode,n as launch};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../index.ts"],
|
|
4
|
+
"sourcesContent": ["import { IArrmatron, IPlatform } from \"arrmatura/types\";\n\nimport { CRootNode } from \"./src/registry/root\";\n\nexport * from \"./src/registry\";\nexport { CRootNode } from \"./src/registry/root\";\nexport * from \"./src/core/Component\";\n\n/**\n * Launches the runtime with given top-level template on the specified platform.\n *\n * @param {IPlatform} platform - The platform on which to launch the template.\n * @param {string} template - The template to launch with.\n * @return {IArrmatron} The root context object.\n */\nexport const launch = (platform: IPlatform, template: string): IArrmatron => {\n const root = new CRootNode(template).createArrmatron(platform);\n\n root.up({}, true);\n\n return root;\n};\n"],
|
|
5
|
+
"mappings": "AAEA,OAAS,aAAAA,MAAiB,sBAE1B,WAAc,iBACd,OAAS,aAAAA,MAAiB,sBAC1B,WAAc,uBASP,IAAMC,EAAS,CAACC,EAAqBC,IAAiC,CAC3E,IAAMC,EAAO,IAAIJ,EAAUG,CAAQ,EAAE,gBAAgBD,CAAQ,EAE7D,OAAAE,EAAK,GAAG,CAAC,EAAG,EAAI,EAETA,CACT",
|
|
6
|
+
"names": ["CRootNode", "launch", "platform", "template", "root"]
|
|
7
|
+
}
|
package/docs/manual.md
CHANGED
|
@@ -7,17 +7,17 @@
|
|
|
7
7
|
```html
|
|
8
8
|
|
|
9
9
|
<component id="NavTreeItem">
|
|
10
|
-
<a href="
|
|
11
|
-
<span>{
|
|
12
|
-
<span
|
|
10
|
+
<a href="id">
|
|
11
|
+
<span>{name | slice:0:50 | capitalize}</span>
|
|
12
|
+
<span If="label" class="label label-{type}">{label}</span>
|
|
13
13
|
</a>
|
|
14
14
|
</component>
|
|
15
15
|
|
|
16
16
|
<component id="NavTree">
|
|
17
17
|
<ul class="nav">
|
|
18
|
-
<li class="nav-item {
|
|
19
|
-
<NavTreeItem {
|
|
20
|
-
<NavTree
|
|
18
|
+
<li class="nav-item {item.class}" Each="item of data">
|
|
19
|
+
<NavTreeItem (...)="{item}">
|
|
20
|
+
<NavTree If="item.subs" data="{item.subs}" />
|
|
21
21
|
</li>
|
|
22
22
|
</ul>
|
|
23
23
|
</component>
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
With `if` attribute, an element(and its inner context) presents only if value of expression is truthy.
|
|
31
31
|
|
|
32
32
|
```html
|
|
33
|
-
<div ...
|
|
33
|
+
<div ... If="enabled">...</div>
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
#### full `then-else` syntax
|
|
37
37
|
|
|
38
38
|
```html
|
|
39
|
-
<Fragment
|
|
39
|
+
<Fragment If="enabled">
|
|
40
40
|
<Then><Case1 /></Then>
|
|
41
41
|
<Else><Case2 /></Else>
|
|
42
42
|
</Fragment>
|
|
@@ -48,8 +48,8 @@ With `if` attribute, an element(and its inner context) presents only if value of
|
|
|
48
48
|
|
|
49
49
|
```html
|
|
50
50
|
<ul>
|
|
51
|
-
<li
|
|
52
|
-
<a href="/item/{
|
|
51
|
+
<li Each="item of data">
|
|
52
|
+
<a href="/item/{item.id}">{item.position}. {item.name}</span>
|
|
53
53
|
</li>
|
|
54
54
|
</ul>
|
|
55
55
|
```
|
|
@@ -61,7 +61,7 @@ With `if` attribute, an element(and its inner context) presents only if value of
|
|
|
61
61
|
`<Fragment>` is a transparent container and works just like a parens for multiple components.
|
|
62
62
|
|
|
63
63
|
```html
|
|
64
|
-
<Fragment
|
|
64
|
+
<Fragment If="enabled"> <innerContent1 />...<innerContentN /> </Fragment>
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
## Dynamic tags.
|
|
@@ -69,93 +69,108 @@ With `if` attribute, an element(and its inner context) presents only if value of
|
|
|
69
69
|
Used to calculate tag dunamically at runtime.
|
|
70
70
|
|
|
71
71
|
```html
|
|
72
|
-
<Dynamic
|
|
72
|
+
<Dynamic As="Field.{type}" ...></Dynamic>
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
>
|
|
76
|
-
|
|
77
|
-
### References.
|
|
78
|
-
|
|
79
|
-
Add `ref` attribute to any component to get refer it in `arrows` expressions.
|
|
80
|
-
|
|
81
|
-
```html
|
|
82
|
-
<UserService ref="user" />
|
|
83
|
-
...
|
|
84
|
-
<UserAvatar data="<- user.profile" onSave="->user.update" />
|
|
85
|
-
```
|
|
75
|
+
> it will fallback to a `Field` if a specific type not found.
|
|
86
76
|
|
|
87
77
|
## Properties.
|
|
88
78
|
|
|
89
|
-
Component
|
|
79
|
+
Component state can be mutated different ways listed below.
|
|
80
|
+
State updates lead to redraw UI and notify subscribers(left arrows)
|
|
81
|
+
Updating data is compared with existing state values for 'sameness' (same set of same values).
|
|
90
82
|
|
|
91
83
|
### with scalar literals
|
|
92
84
|
|
|
93
|
-
`prop1="
|
|
85
|
+
`prop1="'somestring'"` puts a literal into `prop1` property.
|
|
94
86
|
|
|
95
87
|
> - 'true', 'false' values are narrowed to boolean,
|
|
96
88
|
> - numbers has been narrowed to number type.
|
|
97
89
|
|
|
98
90
|
### with resources values
|
|
99
91
|
|
|
100
|
-
|
|
92
|
+
`R`-prefix used to refer any value in the resource bundle.
|
|
101
93
|
|
|
102
|
-
`prop="
|
|
94
|
+
`prop="R.resId"` assigns `resId` resource value to `prop`.
|
|
103
95
|
|
|
104
96
|
### with result of expression
|
|
105
97
|
|
|
106
|
-
`prop="
|
|
98
|
+
`prop="{prop2}"` assigns value of `prop2` property of the current scope.
|
|
107
99
|
|
|
108
|
-
`prop="
|
|
100
|
+
`prop="{data.key}"` assigns value of `data.key`.
|
|
109
101
|
|
|
110
|
-
`prop="
|
|
102
|
+
`prop="{!prop2}"` assigns inverted value of `prop2`
|
|
111
103
|
|
|
112
|
-
|
|
104
|
+
`(...)="{data}"` special `(...)` notation used to spread keys/values of `data` into properties of a target entity.
|
|
113
105
|
|
|
114
106
|
### with result of chain of pipes
|
|
115
107
|
|
|
116
|
-
`prop="
|
|
108
|
+
`prop="{some | pipeFn1 : 'strLiteral1' : 1 : true | pipeFn2 : property2 | pipeFn2 : R.resourceId}` applies chain of pipes.
|
|
117
109
|
|
|
118
110
|
> - Pipe functions can be chained. Result of the previous one passed as a first argument to the next one.
|
|
119
111
|
> - Optional colon-separated arguments can be passed to a pipe function as second, third arguments.
|
|
120
|
-
> - Use shortcuts like `==, && , ??, ?, >, <, []` for `equals, and, or, then, less, greater, dot` functions respectively.
|
|
112
|
+
> - Use shortcuts like `==, && , ??, ?, >, <, []` for `equals, and, or, then, less, greater, dot` functions respectively.
|
|
113
|
+
> - There is no operation priority, they applied in left-to-right order.
|
|
121
114
|
|
|
122
115
|
### `data-*` attributes
|
|
123
116
|
|
|
124
117
|
All `data-[key]` attributes will be collected into single `data` object property under its keys.
|
|
125
118
|
|
|
126
|
-
|
|
119
|
+
### References.
|
|
127
120
|
|
|
128
|
-
`
|
|
121
|
+
Add `Ref` attribute to any component to get refer it in `arrows` expressions.
|
|
129
122
|
|
|
130
|
-
|
|
123
|
+
```html
|
|
124
|
+
<UserService Ref="user" /> <UserAvatar data="<- user.profile" onSave="-> user.update" />
|
|
125
|
+
```
|
|
131
126
|
|
|
132
|
-
##
|
|
127
|
+
## Left arrow expression
|
|
133
128
|
|
|
134
|
-
|
|
135
|
-
<button ... action="-> ref.key1" data-key="val" data="@data" />
|
|
129
|
+
`data="<- ref.prop"` makes a hot subscription to any property of orbitrary component in the current or upper scopes.
|
|
136
130
|
|
|
137
|
-
|
|
138
|
-
|
|
131
|
+
> use pipes to adapt received value `data="<- ref.prop | adjustFn"`.
|
|
132
|
+
|
|
133
|
+
## Right arrow expression
|
|
139
134
|
|
|
140
135
|
Right arrow creates a function, that
|
|
141
136
|
|
|
142
|
-
- `action="-> ref.
|
|
137
|
+
- `action="data-> ref.submit(data)"` invokes `scope.refs[ref].submit(data)` action with an `data` object as parameter.
|
|
138
|
+
|
|
139
|
+
- `action="-> ref.submit(prop1)"` invokes `scope.refs[ref].submit(data)` action with a value of property `prop1` as parameter.
|
|
143
140
|
|
|
144
|
-
- `action="-> ref
|
|
141
|
+
- `action="-> ref.prop1"` updates container state for given key `scope.refs[ref].up({ prop1: data })`.
|
|
145
142
|
|
|
146
|
-
> `action="-> ref.
|
|
143
|
+
> `action="data-> ref.action(data | prepare)` pipes will be applied on `data`-object before it passed to the action.
|
|
147
144
|
|
|
148
|
-
> `click="->
|
|
145
|
+
> `click="-> opened"` if `ref` is omitted, then a target will be a scope component `scope.up({opened:data})`
|
|
149
146
|
|
|
150
|
-
> `click="->
|
|
147
|
+
> `click="-> *"` will spread data to state of a scope component `scope.up(data)`
|
|
151
148
|
|
|
152
149
|
#### Right arrows with inline payload
|
|
153
150
|
|
|
154
151
|
Often, it is shorter to pass payload inline instead of using `data` property.
|
|
155
152
|
|
|
156
|
-
- `click="->
|
|
157
|
-
- `click="-> prop" data="
|
|
158
|
-
- `click="-> prop=literalValue"` updates a given scope property of owner with literal.
|
|
153
|
+
- `click="data-> * = data | assignKeyValue:'key':value"` updates a scope properties with `data` object.
|
|
154
|
+
- `click="-> prop" data="data"` updates a given scope property of owner with `data` object.
|
|
155
|
+
- `click="-> prop='literalValue'"` updates a given scope property of owner with literal.
|
|
156
|
+
|
|
157
|
+
## Connector
|
|
158
|
+
|
|
159
|
+
Special build-in `Connector` component serves for async data binding
|
|
160
|
+
It acts with timeout(0 by default) and debounces if any:
|
|
161
|
+
|
|
162
|
+
```html
|
|
163
|
+
<!-- `data` works as a `trigger`, if none specified. -->
|
|
164
|
+
<Connector data="<- todo.shownItems|adjust" change="-> someData" />
|
|
165
|
+
|
|
166
|
+
<!-- `change` is invokes with `data` on `trigger` changed. -->
|
|
167
|
+
<Connector
|
|
168
|
+
data="someData"
|
|
169
|
+
trigger="<- todo.shownItemsCount"
|
|
170
|
+
timeout="100"
|
|
171
|
+
change="data-> discriminant=data|calculate"
|
|
172
|
+
/>
|
|
173
|
+
```
|
|
159
174
|
|
|
160
175
|
## Slots
|
|
161
176
|
|
|
@@ -193,12 +208,12 @@ Inner content could be multiple-part and thus, distributed separately inside com
|
|
|
193
208
|
<div class="component template">
|
|
194
209
|
|
|
195
210
|
<!-- <Extra1/> will be placed here-->
|
|
196
|
-
<Slot
|
|
211
|
+
<Slot Key="key1">
|
|
197
212
|
|
|
198
213
|
<!-- special `slot(key)` conditional expression may be used to check if non-empty slot content passed. -->
|
|
199
|
-
<div class="comp"
|
|
214
|
+
<div class="comp" If="slot(key2)">
|
|
200
215
|
<!-- <Extra2/> will be placed here -->
|
|
201
|
-
<Slot
|
|
216
|
+
<Slot Key="key2">
|
|
202
217
|
</div>
|
|
203
218
|
|
|
204
219
|
<!-- <DefaultContent/> will be placed here -->
|
|
@@ -206,24 +221,18 @@ Inner content could be multiple-part and thus, distributed separately inside com
|
|
|
206
221
|
</div>
|
|
207
222
|
```
|
|
208
223
|
|
|
209
|
-
### DOM support.
|
|
210
|
-
|
|
211
|
-
You can provide life-cycle hooks for DOM element:
|
|
212
|
-
|
|
213
|
-
```html
|
|
214
|
-
<div attached="initBehavior" detached="finalizeBehavior"></div>
|
|
215
|
-
```
|
|
216
|
-
|
|
217
224
|
# Custom components
|
|
218
225
|
|
|
219
226
|
There is a [Component] class that could be used an base ancestor for custom components.
|
|
220
227
|
|
|
221
|
-
|
|
228
|
+
While designing custom components you can
|
|
222
229
|
|
|
223
230
|
- to define life-cycle hooks;
|
|
224
231
|
- to add getters/setter for its properties;
|
|
225
|
-
- to define
|
|
226
|
-
- to
|
|
232
|
+
- to define `__getProperty(propName)` to return value of any property by its name
|
|
233
|
+
- to define `__stateChanged(changes)` to intercept appluing state changes
|
|
234
|
+
- to define action handlers like `doSomethig(data: any): object`, which may return a delta object for updating a component state;
|
|
235
|
+
- to use context methods like `up(delta)`, `emit('ref-target', data)`, `defer(fn)`.
|
|
227
236
|
|
|
228
237
|
```typescript
|
|
229
238
|
class MyService extends Component {
|
|
@@ -252,6 +261,15 @@ class MyService extends Component {
|
|
|
252
261
|
this.cancel();
|
|
253
262
|
}
|
|
254
263
|
|
|
264
|
+
// to intercept getting of any property
|
|
265
|
+
__getProperty(key: string) {
|
|
266
|
+
this.log('getProperty', key)
|
|
267
|
+
return this[key]
|
|
268
|
+
|
|
269
|
+
// to intercept state changes
|
|
270
|
+
__stateChanged(changes: Map) {
|
|
271
|
+
}
|
|
272
|
+
|
|
255
273
|
// property getter
|
|
256
274
|
getSrc(){
|
|
257
275
|
return this.url.toString()
|
|
@@ -268,7 +286,7 @@ class MyService extends Component {
|
|
|
268
286
|
}
|
|
269
287
|
|
|
270
288
|
// action handler. To be invoked with '-> ref.someAction' notation
|
|
271
|
-
onSomeAction(data
|
|
289
|
+
onSomeAction(data: any) {
|
|
272
290
|
if (asyncMode) {
|
|
273
291
|
return promise.then(() => delta)
|
|
274
292
|
}
|
|
@@ -279,7 +297,7 @@ class MyService extends Component {
|
|
|
279
297
|
// async evaluation for 'prop'. 'Promise' postfix is optional.
|
|
280
298
|
propPromise: T.fetchProp(),
|
|
281
299
|
// async spread
|
|
282
|
-
'
|
|
300
|
+
'...': Promise.resolve({
|
|
283
301
|
prop1: 'val1'
|
|
284
302
|
prop2: 'val2'
|
|
285
303
|
})
|
|
@@ -288,7 +306,7 @@ class MyService extends Component {
|
|
|
288
306
|
|
|
289
307
|
toast (message) {
|
|
290
308
|
// emit action event
|
|
291
|
-
this.emit('toasters.
|
|
309
|
+
this.emit('toasters.onSend(data)', { message });
|
|
292
310
|
};
|
|
293
311
|
}
|
|
294
312
|
```
|
package/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IArrmatron, IPlatform } from "arrmatura/types";
|
|
2
|
+
|
|
2
3
|
import { CRootNode } from "./src/registry/root";
|
|
3
4
|
|
|
4
5
|
export * from "./src/registry";
|
|
@@ -10,10 +11,10 @@ export * from "./src/core/Component";
|
|
|
10
11
|
*
|
|
11
12
|
* @param {IPlatform} platform - The platform on which to launch the template.
|
|
12
13
|
* @param {string} template - The template to launch with.
|
|
13
|
-
* @return {
|
|
14
|
+
* @return {IArrmatron} The root context object.
|
|
14
15
|
*/
|
|
15
|
-
export const launch = (platform: IPlatform, template: string):
|
|
16
|
-
const root = new CRootNode(template).
|
|
16
|
+
export const launch = (platform: IPlatform, template: string): IArrmatron => {
|
|
17
|
+
const root = new CRootNode(template).createArrmatron(platform);
|
|
17
18
|
|
|
18
19
|
root.up({}, true);
|
|
19
20
|
|
package/package.json
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arrmatura",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.2",
|
|
4
4
|
"description": "Arrmatura runtime engine",
|
|
5
5
|
"author": "alitskevich@gmail.com",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"
|
|
8
|
+
"exports": {
|
|
9
|
+
"import": "./index.ts",
|
|
10
|
+
"require": "./dist/index.js"
|
|
11
|
+
},
|
|
9
12
|
"files": [
|
|
10
13
|
"src",
|
|
14
|
+
"dist",
|
|
15
|
+
"index.ts",
|
|
11
16
|
"types.ts",
|
|
12
17
|
"docs/**/*"
|
|
13
18
|
],
|
|
14
19
|
"dependencies": {
|
|
15
|
-
"arrmatura": "6.3.
|
|
16
|
-
"ultimus": "2.1.
|
|
20
|
+
"arrmatura": "6.3.2",
|
|
21
|
+
"ultimus": "2.1.10"
|
|
17
22
|
},
|
|
18
23
|
"scripts": {
|
|
24
|
+
"esbuild": "esbuild index.ts --bundle --outdir=./dist --platform=browser --format=esm --sourcemap --target=esnext --external:* --minify",
|
|
19
25
|
"pnpm:publish": "pnpm publish --no-git-checks"
|
|
20
26
|
}
|
|
21
27
|
}
|