arrmatura 6.2.1 → 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 +45 -0
- package/docs/glossary.md +19 -0
- package/docs/hello.md +46 -0
- package/docs/manual.md +294 -0
- package/index.ts +8 -7
- package/package.json +7 -5
- package/src/core/Component.ts +67 -0
- package/src/{ctx/Ctx.ts → core/Entitron.ts} +69 -87
- package/src/core/ManifestNode.ts +139 -0
- package/src/core/resolveExpression.ts +20 -0
- package/src/{core → registry}/composition.ts +17 -22
- package/src/{core → registry}/conditionals.ts +14 -19
- package/src/registry/elementary.ts +36 -0
- package/src/registry/fragment.ts +21 -0
- package/src/registry/index.ts +69 -0
- package/src/{core → registry}/iterations.ts +31 -30
- package/src/{core → registry}/root.ts +8 -8
- package/src/{core → registry}/routing.ts +7 -13
- package/src/{core → registry}/selection.ts +16 -16
- package/src/{core → registry}/slot.ts +9 -9
- package/src/utils/compileExpression.ts +16 -34
- package/src/utils/stringify.ts +3 -9
- package/types.ts +73 -0
- package/src/core/elementary.ts +0 -40
- package/src/core/fragment.ts +0 -21
- package/src/core/index.ts +0 -19
- package/src/ctx/CtxNode.ts +0 -159
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Arrmatura
|
|
2
|
+
|
|
3
|
+
## Definitive
|
|
4
|
+
|
|
5
|
+
`Arrmatura` is a programming framework strongly relied on declarative style and functional programming paradigm.
|
|
6
|
+
It comprises of
|
|
7
|
+
|
|
8
|
+
- declarative notation to define orbitrary components, its composition and data flow.
|
|
9
|
+
- set of public and private types, which outlines an implementation approach.
|
|
10
|
+
- a runtime engine, that enables launching applications of any kind.
|
|
11
|
+
- `IPlatform` implementation for web to build/launch client applications.
|
|
12
|
+
- Web UI kits with various components, services, forms, rich UI etc.
|
|
13
|
+
|
|
14
|
+
## Key features and benefits of the platform
|
|
15
|
+
|
|
16
|
+
The framework is designed to facilitate the creation of web applications
|
|
17
|
+
by allowing developers to define components and specify how they are composed and connected.
|
|
18
|
+
|
|
19
|
+
This component composition is achieved through a declarative syntax,
|
|
20
|
+
which describes how data flows between components and how it is propagated across the composition.
|
|
21
|
+
|
|
22
|
+
This streamlines the development process and makes it easier for developers
|
|
23
|
+
to create complex and dynamic applications that can respond to changes in data in real-time.
|
|
24
|
+
|
|
25
|
+
Overall, the framework aims to provide a flexible and intuitive way
|
|
26
|
+
to build web applications that are scalable, maintainable, and responsive.
|
|
27
|
+
|
|
28
|
+
## Documentation
|
|
29
|
+
|
|
30
|
+
- [Manual](docs/manual.md)
|
|
31
|
+
- [Glossary](docs/glossary.md)
|
|
32
|
+
|
|
33
|
+
## Getting started for Web
|
|
34
|
+
|
|
35
|
+
See [Hello, world](docs/HELLO.md) for starting example.
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
- [Emoji List](https://emojis-list.web.app/)
|
|
40
|
+
- [Countries List](https://countries-list.web.app/)
|
|
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/docs/glossary.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Glossary
|
|
2
|
+
|
|
3
|
+
| _Term_ | _Description_ |
|
|
4
|
+
| ----------------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
5
|
+
| `Program` | A formal prescription given to a runtime to process (to apply trasformation rules in some order) incoming data. |
|
|
6
|
+
| `Type` | A specification that defines the possible values that can be reffered by a variable. |
|
|
7
|
+
| `Variable` | A program entity, which may reffer some data value. |
|
|
8
|
+
| `Object-oriented programming` | A programming style that evolves around composing components, which interact with each others by handling streams of events. |
|
|
9
|
+
| `Component` | An individual runtime entity that has a lifecycle, state, and behavior. Components can interact with others and are treated as black boxes. |
|
|
10
|
+
| `Class` | A design-time specification for the state and behavior of a component. It provides guidance for the runtime to create and manage instances of the component. |
|
|
11
|
+
| `State` | A private data structure that is managed by a component. |
|
|
12
|
+
| `Property` | A public getter/setter pair that allows access to a state value by a specified key. |
|
|
13
|
+
| `Behavior` | A specification that defines how a component should change its state in response to external impacts. |
|
|
14
|
+
| `Interface` | A design-time specification of the methods and signatures that can be used to interact with a component. |
|
|
15
|
+
| `Composition` | The | parent-child and context-content relationships between components. It defines the scope and lifecycle of the components that they can interact with. |
|
|
16
|
+
| `Template` | A text written in a formal language that allows for the description of component composition and property binding in a declarative way, focused on the desired outcome rather than the implementation details. |
|
|
17
|
+
| `Property binding` | A formal sentence that expresses a dependency between the value of a target component's property and other properties. It ensures that the target property is updated whenever the dependency changes. |
|
|
18
|
+
| `Property propagation` | A one-way binding that passes property values from a container component to its contents. |
|
|
19
|
+
| `Hook` | A component-bound method that is called by the runtime at specific points in the component's lifecycle. |
|
package/docs/hello.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Hello, world
|
|
2
|
+
|
|
3
|
+
The code you have provided is a combination of XML and JavaScript that utilizes the arrmatura framework to create a web application.
|
|
4
|
+
|
|
5
|
+
## templates.xml
|
|
6
|
+
|
|
7
|
+
The templates.xml file defines two components: "Application" and "Main". The "Application" component is the root component of the application and contains a single instance of the "Main" component. The "Main" component is defined as a div element that displays a greeting, which consists of a static string ("Hello") and a dynamic value (name).
|
|
8
|
+
|
|
9
|
+
```xml
|
|
10
|
+
<component id="Application">
|
|
11
|
+
<Main name="world"/>
|
|
12
|
+
</component>
|
|
13
|
+
|
|
14
|
+
<component id="Main">
|
|
15
|
+
<div title="{#greeting}, {@name | upper}!"></div>
|
|
16
|
+
</component>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## index.ts
|
|
20
|
+
|
|
21
|
+
In the index.ts file, the arrmatura framework is imported and used to render the application.
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
import { arrmatura } from 'arrmatura';
|
|
25
|
+
import templates from './templates.xml';
|
|
26
|
+
|
|
27
|
+
arrmatura({
|
|
28
|
+
template: '<Application />'
|
|
29
|
+
types: [templates],
|
|
30
|
+
resources: {
|
|
31
|
+
greeting: 'Hello',
|
|
32
|
+
},
|
|
33
|
+
functions: {
|
|
34
|
+
upper: (x) => x.toUpperCase(),
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The arrmatura function is passed several arguments:
|
|
40
|
+
|
|
41
|
+
- template: Specifies the root component of the application, which is <Application />.
|
|
42
|
+
- types: An array of component templates, in this case, the contents of the templates.xml file.
|
|
43
|
+
- resources: An object that contains the static data used in the application. In this case, a single resource greeting with the value "Hello" is provided.
|
|
44
|
+
- functions: An object that contains functions that can be used in component templates. In this case, a single function upper is provided, which takes a string as an argument and returns the uppercase version of the string.
|
|
45
|
+
|
|
46
|
+
When the arrmatura function is executed, it creates an instance of the "Application" component and renders it to the page. The "Main" component is then rendered as a child of the "Application" component, and its title is set to the result of evaluating the expression "{#greeting}, {@name | upper}!". This expression uses a combination of static data (greeting), dynamic data (name), and a function (upper) to create the greeting that is displayed in the div element.
|
package/docs/manual.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#Templates
|
|
2
|
+
|
|
3
|
+
**Template** is a xml-based notation defining a components composition, events and data flow.
|
|
4
|
+
|
|
5
|
+
## Insight
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
|
|
9
|
+
<component id="NavTreeItem">
|
|
10
|
+
<a href="@id">
|
|
11
|
+
<span>{@name | slice:0:50 | capitalize}</span>
|
|
12
|
+
<span if="@label" class="label label-{@type}">{@label}</span>
|
|
13
|
+
</a>
|
|
14
|
+
</component>
|
|
15
|
+
|
|
16
|
+
<component id="NavTree">
|
|
17
|
+
<ul class="nav">
|
|
18
|
+
<li class="nav-item {@item.class}" each="item of @data">
|
|
19
|
+
<NavTreeItem {...@item}>
|
|
20
|
+
<NavTree if="@item.subs" data="@item.subs" />
|
|
21
|
+
</li>
|
|
22
|
+
</ul>
|
|
23
|
+
</component>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Composition Control flow.
|
|
27
|
+
|
|
28
|
+
### Conditionals.
|
|
29
|
+
|
|
30
|
+
With `if` attribute, an element(and its inner context) presents only if value of expression is truthy.
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<div ... if="@enabled">...</div>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### full `then-else` syntax
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<Fragment if="@enabled">
|
|
40
|
+
<Then><Case1 /></Then>
|
|
41
|
+
<Else><Case2 /></Else>
|
|
42
|
+
</Fragment>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Iterations.
|
|
46
|
+
|
|
47
|
+
`each` attribute multiplies component instances along items from given array.
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<ul>
|
|
51
|
+
<li each="item of @data">
|
|
52
|
+
<a href="/item/{@item.id}">{@item.position}. {@item.name}</span>
|
|
53
|
+
</li>
|
|
54
|
+
</ul>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> - items MUST HAVE unique `id` field
|
|
58
|
+
|
|
59
|
+
### Fragment.
|
|
60
|
+
|
|
61
|
+
`<Fragment>` is a transparent container and works just like a parens for multiple components.
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<Fragment if="@enabled"> <innerContent1 />...<innerContentN /> </Fragment>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Dynamic tags.
|
|
68
|
+
|
|
69
|
+
Used to calculate tag dunamically at runtime.
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<Dynamic as="{@type}Field" ...></Dynamic>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> use dot in type name to fallback to a basic implementation if a specific one not found `tag="Button.{@type}"`
|
|
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
|
+
```
|
|
86
|
+
|
|
87
|
+
## Properties.
|
|
88
|
+
|
|
89
|
+
Component properties can be assigned
|
|
90
|
+
|
|
91
|
+
### with scalar literals
|
|
92
|
+
|
|
93
|
+
`prop1="magicNumber"` puts a `magicNumber` literal into `prop1` property.
|
|
94
|
+
|
|
95
|
+
> - 'true', 'false' values are narrowed to boolean,
|
|
96
|
+
> - numbers has been narrowed to number type.
|
|
97
|
+
|
|
98
|
+
### with resources values
|
|
99
|
+
|
|
100
|
+
use `@@` prefix to refer any entity in the resource bundle.
|
|
101
|
+
|
|
102
|
+
`prop="@@resId"` puts result of `platform.getResource(resId)` into `prop`.
|
|
103
|
+
|
|
104
|
+
### with result of expression
|
|
105
|
+
|
|
106
|
+
`prop="@prop2"` puts value of `prop2` owner property
|
|
107
|
+
|
|
108
|
+
`prop="@data.key"` puts value of `prop2.key` owner property in depth.
|
|
109
|
+
|
|
110
|
+
`prop="@prop2#not"` the `#not` postfix narrow to boolean and inverts the value of `prop2`
|
|
111
|
+
|
|
112
|
+
`...="@data"` special `...` prop name spreads keys/values of `data` into properties of an entity.
|
|
113
|
+
|
|
114
|
+
### with result of chain of pipes
|
|
115
|
+
|
|
116
|
+
`prop="@some | pipeFn1 : 'strLiteral1' : 1 : true | pipeFn2 : @property2 | pipeFn2 : @@resourceId"` applies chain of pipes in left-to-right order.
|
|
117
|
+
|
|
118
|
+
> - Pipe functions can be chained. Result of the previous one passed as a first argument to the next one.
|
|
119
|
+
> - 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. Priority is still left-to-right here.
|
|
121
|
+
|
|
122
|
+
### `data-*` attributes
|
|
123
|
+
|
|
124
|
+
All `data-[key]` attributes will be collected into single `data` object property under its keys.
|
|
125
|
+
|
|
126
|
+
## Left arrow expression
|
|
127
|
+
|
|
128
|
+
`data="<-ref.prop"` makes a hot subscription to any property of orbitrary component in upper scopes.
|
|
129
|
+
|
|
130
|
+
> may use pipes to adapt received value `data="<-ref.prop | adjustFn"`.
|
|
131
|
+
|
|
132
|
+
## Right arrow expression
|
|
133
|
+
|
|
134
|
+
```html
|
|
135
|
+
<button ... action="-> ref.key1" data-key="val" data="@data" />
|
|
136
|
+
|
|
137
|
+
<button ... action="-> ref.key1 = @value|pipe" />
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Right arrow creates a function, that
|
|
141
|
+
|
|
142
|
+
- `action="-> ref.key1"` invokes `upperScopes[ref].onKey1(data)` action handler with an `data` object as parameter.
|
|
143
|
+
|
|
144
|
+
- `action="-> ref.!prop1"` updates container state for given key `upperScopes[ref].up({ prop1: data })`.
|
|
145
|
+
|
|
146
|
+
> `action="-> ref.key1 = * | prepare"` pipes will be applied on `data`-object before it passed to the action handler .
|
|
147
|
+
|
|
148
|
+
> `click="-> @opened"` if `ref` is omitted, then a target will be a scope component `scope.up({opened:data})`
|
|
149
|
+
|
|
150
|
+
> `click="-> ..."` will spread data to state of a scope component `scope.up(data)`
|
|
151
|
+
|
|
152
|
+
#### Right arrows with inline payload
|
|
153
|
+
|
|
154
|
+
Often, it is shorter to pass payload inline instead of using `data` property.
|
|
155
|
+
|
|
156
|
+
- `click="->" data="@data | assignKeyValue:key:@value"` updates a scope properties with `data` object.
|
|
157
|
+
- `click="-> prop" data="@data"` updates a given scope property of owner with `data` object.
|
|
158
|
+
- `click="-> prop=literalValue"` updates a given scope property of owner with literal.
|
|
159
|
+
|
|
160
|
+
## Slots
|
|
161
|
+
|
|
162
|
+
Slots are placeholders to inject an inner content of component usage tag.
|
|
163
|
+
|
|
164
|
+
```html
|
|
165
|
+
<Comp>
|
|
166
|
+
<!-- inner content of component usage -->
|
|
167
|
+
<InnerContent />
|
|
168
|
+
</Comp>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```html
|
|
172
|
+
<component id="Comp">
|
|
173
|
+
<div class="container">
|
|
174
|
+
<!-- Inner content will replace <Slot/> -->
|
|
175
|
+
<Slot>
|
|
176
|
+
</div>
|
|
177
|
+
</component>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Multi-part extra content.
|
|
181
|
+
|
|
182
|
+
Inner content could be multiple-part and thus, distributed separately inside component template.
|
|
183
|
+
|
|
184
|
+
```html
|
|
185
|
+
<Comp>
|
|
186
|
+
<Comp:key1><Extra1 /></Comp:key1>
|
|
187
|
+
<Comp:key2><Extra2 /></Comp:key2>
|
|
188
|
+
<DefaultSlotContent />
|
|
189
|
+
</Comp>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<div class="component template">
|
|
194
|
+
|
|
195
|
+
<!-- <Extra1/> will be placed here-->
|
|
196
|
+
<Slot key="key1">
|
|
197
|
+
|
|
198
|
+
<!-- special `slot(key)` conditional expression may be used to check if non-empty slot content passed. -->
|
|
199
|
+
<div class="comp" if="slot(key2)">
|
|
200
|
+
<!-- <Extra2/> will be placed here -->
|
|
201
|
+
<Slot key="key2">
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<!-- <DefaultContent/> will be placed here -->
|
|
205
|
+
<Slot>
|
|
206
|
+
</div>
|
|
207
|
+
```
|
|
208
|
+
|
|
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
|
+
# Custom components
|
|
218
|
+
|
|
219
|
+
There is a [Component] class that could be used an base ancestor for custom components.
|
|
220
|
+
|
|
221
|
+
It allows
|
|
222
|
+
|
|
223
|
+
- to define life-cycle hooks;
|
|
224
|
+
- to add getters/setter for its properties;
|
|
225
|
+
- to define action handlers;
|
|
226
|
+
- to use context methods like `up()`, `emit()`, `defer()`.
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
class MyService extends Component {
|
|
230
|
+
|
|
231
|
+
constructor(initials: Hash, ctx: ICtx) {
|
|
232
|
+
Object.asign(this, initials);
|
|
233
|
+
this.ctx = ctx;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// life-cycle hook called once on component is inited
|
|
237
|
+
init() {
|
|
238
|
+
this.cancel = api.listen(this)
|
|
239
|
+
// ...or using $.defer()
|
|
240
|
+
this.ctx.defer(api.listen2(this));
|
|
241
|
+
|
|
242
|
+
// will update component state with returned result
|
|
243
|
+
return {
|
|
244
|
+
prop1:'value',
|
|
245
|
+
// can be promise as well
|
|
246
|
+
prop2: Promise.resolve(2)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// life-cycle hook called once on component is done
|
|
251
|
+
done (){
|
|
252
|
+
this.cancel();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// property getter
|
|
256
|
+
getSrc(){
|
|
257
|
+
return this.url.toString()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// property setter
|
|
261
|
+
setSrc(value){
|
|
262
|
+
this.url = URL.parse(value)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// getter can return promise
|
|
266
|
+
async getData() {
|
|
267
|
+
return this.fetchData()
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// action handler. To be invoked with '-> ref.someAction' notation
|
|
271
|
+
onSomeAction(data, T:This) {
|
|
272
|
+
if (asyncMode) {
|
|
273
|
+
return promise.then(() => delta)
|
|
274
|
+
}
|
|
275
|
+
// delta object to update component state
|
|
276
|
+
return {
|
|
277
|
+
// instant value for 'prop'
|
|
278
|
+
prop: data.value,
|
|
279
|
+
// async evaluation for 'prop'. 'Promise' postfix is optional.
|
|
280
|
+
propPromise: T.fetchProp(),
|
|
281
|
+
// async spread
|
|
282
|
+
'*': Promise.resolve({
|
|
283
|
+
prop1: 'val1'
|
|
284
|
+
prop2: 'val2'
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
toast (message) {
|
|
290
|
+
// emit action event
|
|
291
|
+
this.emit('toasters.send', { message });
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
```
|
package/index.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { CRootNode } from "./src/
|
|
1
|
+
import { IEntitron, IPlatform } from "arrmatura/types";
|
|
2
|
+
import { CRootNode } from "./src/registry/root";
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
export { CRootNode } from "./src/
|
|
4
|
+
export * from "./src/registry";
|
|
5
|
+
export { CRootNode } from "./src/registry/root";
|
|
6
|
+
export * from "./src/core/Component";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Launches the runtime with given top-level template on the specified platform.
|
|
9
10
|
*
|
|
10
11
|
* @param {IPlatform} platform - The platform on which to launch the template.
|
|
11
12
|
* @param {string} template - The template to launch with.
|
|
12
|
-
* @return {
|
|
13
|
+
* @return {IEntitron} The root context object.
|
|
13
14
|
*/
|
|
14
|
-
export const launch = (platform: IPlatform, template: string):
|
|
15
|
-
const root = new CRootNode(template).
|
|
15
|
+
export const launch = (platform: IPlatform, template: string): IEntitron => {
|
|
16
|
+
const root = new CRootNode(template).createEntitron(platform);
|
|
16
17
|
|
|
17
18
|
root.up({}, true);
|
|
18
19
|
|
package/package.json
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arrmatura",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.1",
|
|
4
4
|
"description": "Arrmatura runtime engine",
|
|
5
5
|
"author": "alitskevich@gmail.com",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"main": "index.ts",
|
|
8
|
+
"main": "./index.ts",
|
|
9
9
|
"files": [
|
|
10
|
-
"src
|
|
10
|
+
"src",
|
|
11
|
+
"types.ts",
|
|
12
|
+
"docs/**/*"
|
|
11
13
|
],
|
|
12
14
|
"dependencies": {
|
|
13
|
-
"arrmatura
|
|
14
|
-
"ultimus": "2.1.
|
|
15
|
+
"arrmatura": "6.3.1",
|
|
16
|
+
"ultimus": "2.1.8"
|
|
15
17
|
},
|
|
16
18
|
"scripts": {
|
|
17
19
|
"pnpm:publish": "pnpm publish --no-git-checks"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Data, Delta, Hash, LogEntry } from "ultimus/types";
|
|
2
|
+
import type { IComponent, IEntitron } from "../../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base Ancestor for custom components.
|
|
6
|
+
*/
|
|
7
|
+
export abstract class Component implements IComponent {
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
|
|
10
|
+
constructor(_: Hash, readonly ctx: IEntitron) {
|
|
11
|
+
//no-op
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get platform() {
|
|
15
|
+
return this.ctx.platform;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get isDone() {
|
|
19
|
+
return this.ctx.isDone;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// hook on done
|
|
23
|
+
done(_: IEntitron): void {
|
|
24
|
+
//no-op
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// hook on init
|
|
28
|
+
// returned value will be used to update state
|
|
29
|
+
init(_: IEntitron): Delta | null | undefined | unknown {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// update its state
|
|
34
|
+
up(d: Delta) {
|
|
35
|
+
return this.ctx.up(d);
|
|
36
|
+
}
|
|
37
|
+
touch() {
|
|
38
|
+
return this.ctx.touch();
|
|
39
|
+
}
|
|
40
|
+
// emit action event to another component by key
|
|
41
|
+
emit(key: string, data: Delta) {
|
|
42
|
+
return this.ctx.emit(key, data);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// access to resource entity by key
|
|
46
|
+
res(key: string): Data | Data[] {
|
|
47
|
+
return this.ctx.res(key);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// register callback to be called on done
|
|
51
|
+
defer(fn: () => void) {
|
|
52
|
+
this.ctx.defer(fn);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
log(val: unknown, ...args: unknown[]) {
|
|
56
|
+
this.ctx.log(val, ...args);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
logError(val: unknown, ...args: unknown[]) {
|
|
60
|
+
this.ctx.logError(val, ...args);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
toast(message: string | Partial<LogEntry> = "ok") {
|
|
64
|
+
// may overriden from props
|
|
65
|
+
void this.emit("toasters.send", typeof message === "string" ? { message } : message);
|
|
66
|
+
}
|
|
67
|
+
}
|