mol_view_tree2_lib 1.0.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/.nojekyll ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,608 @@
1
+ # $mol_view
2
+
3
+ The base class for all visual components. It provides the infrastructure for reactive lazy rendering, handling exceptions. By default it finds or creates a `div` without child node changing and additional attributes, fields and event handler creation. You can customize it by inheritance or properties overriding at instantiating.
4
+
5
+ ## Properties
6
+
7
+ **`dom_name()' : string`**
8
+
9
+ Returns name of the DOM-element creating for component, if the element with appropriate id is not presented at DOM yet.
10
+
11
+ **`dom_name_space() = 'http://www.w3.org/1999/xhtml'`**
12
+
13
+ Returns namespaceURI for the DOM element.
14
+
15
+ **`sub() : Array< $mol_view | Node | string | number | boolean > = null `**
16
+
17
+ Returns list of child components/elements/primitives. If the list have not been set (by default), then the content of the DOM-element would not be changed in way, it's helpful for manual operating with DOM.
18
+
19
+ **`context( next? : $ ) : $`**
20
+ Some rendering context. Parent node injects context to all rendered child components.
21
+
22
+ **`minimal_height()` = 0**
23
+
24
+ Returns minimum possible height of the component. It's set by hand with constant or some expression.This property is used for lazy rendering.
25
+
26
+ **`dom_node() : Element`**
27
+
28
+ Returns DOM-element, to which the component is bounded to. At first the method tries to find the element by its id at DOM and only if it would have not been found - the method would create and remember a new one.
29
+
30
+ **`dom_tree() : Element`**
31
+
32
+ Same as `dom_node`, but its guarantee, that the content, attributes and properties of the DOM-element should be in actual state.
33
+
34
+ **`attr() : { [ key : string ] : string | number | boolean }`**
35
+
36
+ Returns the dictionary of the DOM-attributes, which values would be set while rendering. Passing `null` or `false` as the value to the attribute would lead to removing the attribute.
37
+ Passing `true` is an equivalent to passing its name as value. `undefined` is just ignored.
38
+
39
+ **`field() : { [ key : string ] : any }`**
40
+
41
+ Returns dictionary of fields, which is necessary to set to the DOM-element after rendering.
42
+
43
+ **`style() : { [ key : string ] : string | number }`**
44
+
45
+ Returns dictionary of styles. Numbers will be converted to string with "px" suffix.
46
+
47
+ **`event() : { [ key : string ] : ( event : Event )=> void }`**
48
+
49
+ Returns dictionary of event handlers. The event handlers are bind to the DOM-element one time, when the value is set to `dom_node` property. This handlers are synchronous and can be cancelled by ```preventDefault()```.
50
+
51
+ **`focused( next? : boolean ) : boolean`**
52
+
53
+ Determines, whether the component is focused or not at this time. If any inserted component would be focused, then its parent component would be focused also.
54
+
55
+ **`plugins() : Array< $mol_view > = null`**
56
+
57
+ Array of plugins. Plugin is a component which can be supplemented with the logic of the current components.
58
+
59
+ For example, list component with keyboard navigation (used `$mol_nav` plugin):
60
+
61
+ ```
62
+ <= Options $mol_list
63
+ plugins /
64
+ <= Nav $mol_nav
65
+ keys_y <= options /
66
+ rows <= options /
67
+ ```
68
+
69
+ ## *.view.tree
70
+
71
+ *view.tree* - is a declarative language of describing components, based on [tree format](https://github.com/nin-jin/tree.d). One file can have multiple component definitions, but better to put every component in a separate file, except in very trivial cases.
72
+ To create a new component in `view.tree` file you must inherit it from any existing one or `$mol_view`.
73
+ Name of the component should begin with `$` and be unique globally accordance with principles presented on [MAM](https://github.com/eigenmethod/mam). For example, let's declare the component `$my_button` extended from `$mol_view`:
74
+
75
+ ```tree
76
+ $my_button $mol_view
77
+ ```
78
+
79
+ It translates to (every *.view.tree code would be translated to *.view.tree.ts):
80
+
81
+ ```typescript
82
+ namespace $ { export class $my_button extends $mol_view {} }
83
+ ```
84
+
85
+ When inheriting, it is possible to declare additional properties or overload existing ones (but the property type must match). For example, lets overload a `uri` property with `"https://example.org"` string, and `sub` - with array of one string `"Click me!"`, besides, lets declare a new property `target` with `"_top"` value by default (default value is necessary when declaring a new property):
86
+
87
+ ```tree
88
+ $my_example $mol_link
89
+ uri \https://example.org
90
+ sub /
91
+ \Click me!
92
+ target \_top
93
+ ```
94
+
95
+ ```typescript
96
+ namespace $ { export class $my_example extends $mol_link {
97
+
98
+ uri() { return "https://example.org" }
99
+
100
+ sub() { return [ "Click me!" ] }
101
+
102
+ target() { return "_top" }
103
+ } }
104
+ ```
105
+
106
+ Note: For better readability, a single child node in a tree file is often written on a single line. You can expand all nodes in the previous example:
107
+
108
+ ```tree
109
+ $my_example
110
+ $mol_link
111
+ uri
112
+ \https://example.org
113
+ sub
114
+ /
115
+ \Click me!
116
+ target
117
+ \_top
118
+ ```
119
+
120
+ Node where name starts with `$` - name of component.
121
+ Child nodes beginning with node `/` - list. You can set type of list, e.g `/number`, `/$mol_view` for better type checking.
122
+ Text after `\` - raw data which can contain entirely any data until the end of the line.
123
+ Node `@` marks string for extraction to separate `*.locale=en.json` file and used for i18n translation, e.g `@ \Values example`.
124
+ Numbers, booleans values and `null` is being wrote as it is, without any prefixes:
125
+ Nodes after `-` are ignored, you can use them for commenting and temporary disable subtree.
126
+
127
+ ```tree
128
+ $my_values $mol_view
129
+ title @ \Values example
130
+ sub /
131
+ 0
132
+ 1.1
133
+ true
134
+ false
135
+ null
136
+ \I can contain any character! \("o")/
137
+ - I
138
+ am
139
+ remark...
140
+ ```
141
+
142
+ ```typescript
143
+ namespace $ { export class $my_values extends $mol_view {
144
+
145
+ title() {
146
+ return this.$.$mol_locale.text( '$my_values_title' )
147
+ }
148
+
149
+ sub() {
150
+ return [ 0 , 1.1 , true , false , <any> null , "I can contain any character! \\(\"o\")/" ]
151
+ }
152
+
153
+ } }
154
+ ````
155
+
156
+ Dictionary (correspondence keys to their values) could be declared through a node `*` (you can use `^` to inherit pairs from superclass). For example, set DOM-element's attribute values:
157
+
158
+ ```tree
159
+ $my_number $mol_view
160
+ dom_name \input
161
+ attr *
162
+ ^
163
+ type \number
164
+ - attribute values must be a strings
165
+ min \0
166
+ max \20
167
+ ```
168
+
169
+ ```typescript
170
+ namespace $ { export class $my_number extends $mol_view {
171
+
172
+ dom_name() { return "input" }
173
+
174
+ attr() {
175
+ return { ...super.attr() ,
176
+ "type" : "number" ,
177
+ "min" : "0" ,
178
+ "max" : "20" ,
179
+ }
180
+ }
181
+ } }
182
+ ```
183
+
184
+ To set a value for a DOM element's fields:
185
+
186
+ ```tree
187
+ $my_scroll $mol_view
188
+ field *
189
+ ^
190
+ scrollTop 0
191
+ ```
192
+
193
+ ```typescript
194
+ namespace $ { export class $my_scroll extends $mol_view {
195
+
196
+ field() {
197
+ return { ...super.field() ,
198
+ "scrollTop" : 0 ,
199
+ }
200
+ }
201
+ } }
202
+ ```
203
+
204
+ To set styles:
205
+
206
+ ```tree
207
+ $my_rotate $mol_view
208
+ style *
209
+ ^
210
+ transform \rotate( 180deg )
211
+ ```
212
+
213
+ ```typescript
214
+ namespace $ { export class $my_rotate extends $mol_view {
215
+
216
+ style() {
217
+ return { ...super.style() ,
218
+ "transform" : "rotate( 180deg )" ,
219
+ }
220
+ }
221
+ } }
222
+ ```
223
+
224
+ As a value, we could cast not only constants, but also the contents of other properties through `<=` one-way binding. For example, let's declare two text properties `hint` and `text` and then use them for the `field` dictionary and `sub` list:
225
+
226
+ ```tree
227
+ $my_hint $mol_view
228
+ hint \Default hint
229
+ text \Default text
230
+ field *
231
+ ^
232
+ title <= hint -
233
+ sub /
234
+ <= text -
235
+ ```
236
+
237
+ ```typescript
238
+ namespace $ { export class $my_hint extends $mol_view {
239
+
240
+ hint() { return "Default hint" }
241
+
242
+ text() { return "Default text" }
243
+
244
+ field() {
245
+ return { ...super.field() ,
246
+ "title" : this.hint() ,
247
+ }
248
+ }
249
+
250
+ sub() {
251
+ return [ this.text() ]
252
+ }
253
+ } }
254
+ ```
255
+
256
+ It's often convenient to combine declaring a property and using it. The following example is exactly the same as the previous one:
257
+
258
+ ```tree
259
+ $my_hint $mol_view
260
+ field *
261
+ ^
262
+ title <= hint \Default hint
263
+ sub /
264
+ <= text \Default text
265
+ ```
266
+
267
+ Reactions on DOM-events are required for two-way binding. For example, lets point out, that objects of `click` event is necessary to put in `remove` property, which we declare right here and set it a default value `null`:
268
+
269
+ ```tree
270
+ $my_remover $mol_view
271
+ event *
272
+ ^
273
+ click? <=> remove? null
274
+ sub /
275
+ \Remove
276
+ ```
277
+
278
+ ```typescript
279
+ namespace $ { export class $my_remover extends $mol_view {
280
+
281
+ @ $mol_mem
282
+ remove( next? : any ) {
283
+ return ( next !== undefinded ) ? next : null as any
284
+ }
285
+
286
+ event() {
287
+ return { ...super.event() ,
288
+ "click" : ( next? : any )=> this.remove( next ) ,
289
+ }
290
+ }
291
+
292
+ sub() {
293
+ return [ "Remove" ]
294
+ }
295
+ } }
296
+ ```
297
+
298
+ You can declare an instance of another class as a value directly. The following example declares a `List` property with the value of instance `$mol_list_demo_tree` and then places it in a list of `sub` child components:
299
+
300
+ ```tree
301
+ $my_app $mol_view
302
+ List $mol_list_demo_tree
303
+ sub /
304
+ <= List -
305
+ ```
306
+
307
+ ```typescript
308
+ namespace $ { export class $my_app extends $mol_view {
309
+
310
+ @ $mol_mem
311
+ List() {
312
+ const obj = new $mol_list_demo_tree
313
+ return obj
314
+ }
315
+
316
+ sub() {
317
+ return [ this.List() ]
318
+ }
319
+
320
+ } }
321
+ ```
322
+
323
+ Properties of a nested component can be overloaded, below overloaded `title` and `content` properties of `$mol_label` component:
324
+
325
+ ```tree
326
+ $my_name $mol_view
327
+ sub /
328
+ <= Info $mol_label
329
+ title \Name
330
+ content \Jin
331
+ ```
332
+
333
+ ```typescript
334
+ namespace $ { export class $my_name extends $mol_view {
335
+
336
+ @ $mol_mem
337
+ Info() {
338
+ const obj = new $mol_label
339
+ obj.title = () => "Name"
340
+ obj.content = () => "Jin"
341
+ return obj
342
+ }
343
+
344
+ sub() {
345
+ return [ this.Info() ]
346
+ }
347
+ } }
348
+ ```
349
+
350
+ Properties of parent and child components can be linked. In the following example, we declare a reactive `name` property and tell the `Input` child component to use the `name` property as its own `value` property, we also tell the `Output` child component that we want the `name` property to output inside that.
351
+ The `Input` and `Output` components are linked through the `name` parent property, and changing the value in the `Input` will also update the `Output`:
352
+
353
+ ```tree
354
+ $my_greeter $mol_view
355
+ sub /
356
+ <= Input $mol_string
357
+ hint \Name
358
+ value? <=> name? \
359
+ <= Output $mol_view
360
+ sub /
361
+ <= name? \
362
+ ```
363
+
364
+ ```typescript
365
+ namespace $ { export class $my_greeter extends $mol_view {
366
+
367
+ @ $mol_mem
368
+ name( next? : any ) {
369
+ return ( next !== undefined ) ? next : ""
370
+ }
371
+
372
+ @ $mol_mem
373
+ Input() {
374
+ const obj = new $mol_string
375
+ obj.hint = () => "Name"
376
+ obj.value = ( next? : any ) => this.name( next )
377
+ return obj
378
+ }
379
+
380
+ @ $mol_mem
381
+ Output() {
382
+ const obj = new $mol_view
383
+ obj.sub = () => [ this.name() ]
384
+ return obj
385
+ }
386
+
387
+ sub() {
388
+ return [ this.Input() , this.Output() ]
389
+ }
390
+
391
+ } }
392
+ ```
393
+
394
+ `=>` - Right-side binding. It declares alias for property of subcomponent in declared component.
395
+
396
+ ```
397
+ $my_app $mol_scroll
398
+ sub /
399
+ <= Page $mol_page
400
+ Title => Page_title -
401
+ head /
402
+ <= Back $mol_button_minor
403
+ title \Back
404
+ <= Page_title -
405
+ ```
406
+
407
+ ```typescript
408
+ namespace $ {
409
+ export class $my_app extends $mol_scroll {
410
+
411
+ // sub / <= Page
412
+ sub() {
413
+ return [
414
+ this.Page()
415
+ ] as readonly any[]
416
+ }
417
+
418
+ // Back $mol_button_minor title \Back
419
+ @ $mol_mem
420
+ Back() {
421
+ const obj = new this.$.$mol_button_minor()
422
+
423
+ obj.title = () => "Back"
424
+
425
+ return obj
426
+ }
427
+
428
+ // Page_title
429
+ Page_title() {
430
+ return this.Page().Title()
431
+ }
432
+
433
+ // Page $mol_page
434
+ // Title => Page_title
435
+ // head /
436
+ // <= Back
437
+ // <= Page_title
438
+ @ $mol_mem
439
+ Page() {
440
+ const obj = new this.$.$mol_page()
441
+
442
+ obj.head = () => [
443
+ this.Back(),
444
+ this.Page_title()
445
+ ] as readonly any[]
446
+
447
+ return obj
448
+ }
449
+ }
450
+ }
451
+ ```
452
+
453
+ There are certain properties that return different values depending on the key. A typical example of is a list of strings. Each row is a separate component, accessed by a unique key. The list of such properties has a `*` after the name:
454
+
455
+ ```tree
456
+ $my_tasks $mol_list
457
+ sub <= task_rows /
458
+ Task_row* $mol_view
459
+ sub /
460
+ <= task_title* <= task_title_default \
461
+ ```
462
+
463
+ ```typescript
464
+ namespace $ {
465
+ export class $my_tasks extends $mol_list {
466
+
467
+ // sub <= task_rows
468
+ sub() { return this.task_rows() }
469
+
470
+ // Task_row* $mol_view sub / <= task_title*
471
+ @ $mol_mem_key
472
+ Task_row(id: any) {
473
+ const obj = new this.$.$mol_view()
474
+
475
+ obj.sub = () => [
476
+ this.task_title(id)
477
+ ] as readonly any[]
478
+
479
+ return obj
480
+ }
481
+
482
+ // task_rows /
483
+ task_rows() { return [] as readonly any[] }
484
+
485
+ // task_title_default \
486
+ task_title_default() { return "" }
487
+
488
+ // task_title* <= task_title_default
489
+ task_title(id: any) { return this.task_title_default() }
490
+ }
491
+ }
492
+ ```
493
+
494
+ In above example we declared the property `Task_row`, which takes on input some ID-key and returns an unique instance of `$mol_view` for every key.
495
+ `Task_row` has overloaded property `sub` that outputs appropriate `task_title` for every `Task_row` (`task_title` returns content of property `task_title_default`), which is equal to empty string initially.
496
+ Further, by overloading any of these properties, we can change any aspect of the component's behavior. You can override `task_rows` in a subclass to generate rows of your choice. For example":
497
+
498
+ ```
499
+ task_rows() {
500
+ const rows = [] as $mol_view[]
501
+ for( let i = 0 ; i < 10 ; ++ i ) rows.push( this.Task_row( i ) )
502
+ return rows
503
+ }
504
+
505
+ task_title(id: any) {
506
+ return `Title - ${id}`
507
+ }
508
+ ```
509
+
510
+ Note: There is old way howto to use property with id. Instead of `*` you can write `!id`. E.g. instead of `task_title*` you can use `task_title!key`. You might find such usage in old examples, tutorials or old projects.
511
+
512
+
513
+ ### All special chars
514
+
515
+ - `-` - remarks, ignored by code generation
516
+ - `$` - component name prefix, e.g `$mol_button`
517
+ - `/` - array, optionally you can set type of array, e.g `sub /number`
518
+ - `*` - dictionary (string keys, any values)
519
+ - `^` - return value of the same property from super class
520
+ - `\` - raw string, e.g. `message \Hello`
521
+ - `@` - localized string, e.g. `message @ \Hello world`
522
+ - `<=` - provides read-only property from owner to sub-componen
523
+ - `=>` - provides read-only property from sub-componen to owner
524
+ - `<=>` - fully replace sub component property by owner's one
525
+ - property + `*` or `!` - property takes ID as first argument, e.g. `Task_row* $mol_view`
526
+ - property + `?` - property can be changed by providing an additional optional argument, e.g. `value <=> name? \`
527
+
528
+ ## view.ts
529
+
530
+ In addition to declarative description of component, next to it could be created a file of the same name with `view.ts` extension, where a behavior could be described. Using a special construction, it could be inherited from realization obtained of `view.tree` and it would be overloaded automatically by heir:
531
+
532
+ For example we have following description into `./my/hello/hello.view.tree`:
533
+
534
+ ```tree
535
+ $my_hello $mol_view
536
+ sub /
537
+ <= Input $mol_string
538
+ hint \Name
539
+ value? <=> name? \
540
+ <= message \
541
+ ```
542
+
543
+ Here we have declared 2 properties: `name` to get the value from `Input` and `message` to output the value (we will override this property below).
544
+ It will be translated into following file `./my/hello/-view.tree/hello.view.tree.ts`:
545
+
546
+ ```typescript
547
+ namespace $ {
548
+ export class $my_hello extends $mol_view {
549
+
550
+ // sub /
551
+ // <= Input
552
+ // <= message
553
+ sub() {
554
+ return [
555
+ this.Input(),
556
+ this.message()
557
+ ] as readonly any[]
558
+ }
559
+
560
+ // name? \
561
+ @ $mol_mem
562
+ name(val?: any) {
563
+ if ( val !== undefined ) return val as never
564
+ return ""
565
+ }
566
+
567
+ // Input $mol_string
568
+ // hint \Name
569
+ // value? <=> name?
570
+ @ $mol_mem
571
+ Input() {
572
+ const obj = new this.$.$mol_string()
573
+
574
+ obj.hint = () => "Name"
575
+ obj.value = (val?: any) => this.name(val)
576
+
577
+ return obj
578
+ }
579
+
580
+ // message \
581
+ message() { return "" }
582
+ }
583
+ }
584
+ ```
585
+
586
+ Now let's override the `message` method, which will use the `name` property in `./my/hello/hello.view.ts` behaivour file. `message` will depend on the `name` property entered by the user:
587
+
588
+ ```typescript
589
+ namespace $.$$ {
590
+ export class $my_hello extends $.$my_hello {
591
+
592
+ message() {
593
+ const name = this.name()
594
+ return name && `Hello, ${name}!`
595
+ }
596
+
597
+ }
598
+ }
599
+ ```
600
+
601
+ ## IDE Support
602
+
603
+ * [Language Service for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=valikov.tree-language-service)
604
+
605
+ ## Articles
606
+
607
+ * [View formats](https://github.com/eigenmethod/mol/wiki/View-formats) - Comparison of component description formats (tree, xml, json)
608
+ * [React'ивные Panel'и](https://habrahabr.ru/post/314752/) - JSX vs view.tree
package/node.audit.js ADDED
@@ -0,0 +1,2 @@
1
+ console.info( `%cplace: $mol_build
2
+ message: Audit passed`, 'color:forestgreen; font-weight:bolder' )