leanweb 3.0.1 → 3.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.
- package/README.md +1 -693
- package/lib/lw-html-parser.js +5 -0
- package/package.json +1 -1
- package/templates/index.html +1 -0
- package/templates/lib/lw-element.js +12 -3
- package/templates/lib/lw-expr-parser.js +2 -1
- package/templates/page.html +1 -0
package/README.md
CHANGED
|
@@ -1,693 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Builds framework agnostic web components.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
- `npm install leanweb -g` as a global tool, or
|
|
8
|
-
- `npm install leanweb -D` in the project as a dev dependency.
|
|
9
|
-
|
|
10
|
-
If leanweb is installed as a dev dependency, you will need to run
|
|
11
|
-
`npx lw`, otherwise just run `lw` if it is installed as global tool.
|
|
12
|
-
|
|
13
|
-
I don't see any reason leanweb should be installed as `npm install leanweb`.
|
|
14
|
-
|
|
15
|
-
## Background
|
|
16
|
-
|
|
17
|
-
I like the idea in Angular that 3 files (html/js/scss) as a component are in
|
|
18
|
-
charge of a box, like a div, a rectangle area. But I don't like Angular in that
|
|
19
|
-
my code has to be depending on so many bloated dependencies to run. I created
|
|
20
|
-
leanweb as a set of tools to help create web components based web projects,
|
|
21
|
-
which:
|
|
22
|
-
|
|
23
|
-
- are based on native DOM and web components api
|
|
24
|
-
- are pure Javascript, no fancy framework
|
|
25
|
-
- are assistive, not restrictive
|
|
26
|
-
- are more standards, less proprietary
|
|
27
|
-
- are built to last
|
|
28
|
-
|
|
29
|
-
The principle is simply that 3 files (html/js/scss) as a web component will
|
|
30
|
-
control a box.
|
|
31
|
-
|
|
32
|
-
## Getting started
|
|
33
|
-
|
|
34
|
-
In this demo, I assume leanweb is installed as a global tool by running
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
npm i leanweb -g
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### `leanweb init` or `lw init`
|
|
41
|
-
|
|
42
|
-
Create a directory called `demo` for this demo project.
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
$ mkdir demo
|
|
46
|
-
$ cd demo
|
|
47
|
-
demo$ lw init
|
|
48
|
-
demo$
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Now a `src/` directory are created at the project root. `src/leanweb.json`
|
|
52
|
-
looks like:
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"name": "demo",
|
|
57
|
-
"version": "0.4.5",
|
|
58
|
-
"components": ["root"],
|
|
59
|
-
"resources": ["resources/"]
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
which suggests a root web component `demo-root` is created. In `src/` directory,
|
|
64
|
-
an `index.html`, an empty `demo.scss` and an empty `global-styles.scss` files
|
|
65
|
-
are created, in `global-styles.scss` we can add global styles. `demo-root` web
|
|
66
|
-
component directory is created at `src/components/root/`. There are 3 files in
|
|
67
|
-
this directory:
|
|
68
|
-
|
|
69
|
-
- root.html
|
|
70
|
-
- root.js
|
|
71
|
-
- root.scss
|
|
72
|
-
|
|
73
|
-
`root.html`
|
|
74
|
-
|
|
75
|
-
```html
|
|
76
|
-
<div>demo-root works!</div>
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
`root.js` defines your new web component `demo-root`, which is a web component
|
|
80
|
-
based on standard DOM api.
|
|
81
|
-
|
|
82
|
-
`root.js`
|
|
83
|
-
|
|
84
|
-
```javascript
|
|
85
|
-
import LWElement from "./../../lib/lw-element.js";
|
|
86
|
-
import ast from "./ast.js";
|
|
87
|
-
|
|
88
|
-
customElements.define(
|
|
89
|
-
"demo-root",
|
|
90
|
-
class extends LWElement {
|
|
91
|
-
// LWElement extends HTMLElement
|
|
92
|
-
constructor() {
|
|
93
|
-
super(ast);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
);
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
`root.scss` is empty, which is for you to add web component specific styles.
|
|
100
|
-
|
|
101
|
-
### `leanweb serve` or `lw serve`
|
|
102
|
-
|
|
103
|
-
Run `lw serve` and you should see a browser window open. Try make some
|
|
104
|
-
changes in the code, and save, the browser should refresh automatically to
|
|
105
|
-
reflect your changes.
|
|
106
|
-
<img src='https://leanweb.app/images/leanweb-serve.png' alt='lw serve' width='640'/>
|
|
107
|
-
|
|
108
|
-
### `leanweb generate` or `lw generate`
|
|
109
|
-
|
|
110
|
-
Let's create a `login` web component with `lw generate` or `lw g`.
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
demo$ lw g login
|
|
114
|
-
demo$
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Now the `leanweb.json` has one more entry in the component list:
|
|
118
|
-
|
|
119
|
-
```json
|
|
120
|
-
{
|
|
121
|
-
"name": "demo",
|
|
122
|
-
"version": "0.4.5",
|
|
123
|
-
"components": ["root", "login"],
|
|
124
|
-
"resources": ["resources/"]
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
`demo-login` is the newly generated web component. The web component name is
|
|
129
|
-
prefixed with project name `demo-`. Inside `src/components/`, a new web
|
|
130
|
-
component directory `login` is created containing 3 files:
|
|
131
|
-
|
|
132
|
-
- login.html
|
|
133
|
-
- login.js
|
|
134
|
-
- login.scss
|
|
135
|
-
|
|
136
|
-
Now let's make two changes, first open up `src/components/root/root.html`, and
|
|
137
|
-
add a new line `<demo-login></demo-login>`. The new `root.html` should look
|
|
138
|
-
like the following after the change:
|
|
139
|
-
|
|
140
|
-
```html
|
|
141
|
-
<div>demo-root works!</div>
|
|
142
|
-
<demo-login></demo-login>
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Then open up `src/components/login/login.scss`, and add the following style:
|
|
146
|
-
|
|
147
|
-
```scss
|
|
148
|
-
div {
|
|
149
|
-
color: red;
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
And you should see the changes in the browser. Please note the styles added to
|
|
154
|
-
the `login` component does not affect other components.
|
|
155
|
-
|
|
156
|
-
<img src='https://leanweb.app/images/leanweb-serve-1.png' alt='lw serve' width='640'/>
|
|
157
|
-
|
|
158
|
-
### `leanweb dist` or `lw dist`
|
|
159
|
-
|
|
160
|
-
Run `lw dist`, and a `dist` directory will be created with minified files
|
|
161
|
-
for production.
|
|
162
|
-
|
|
163
|
-
### `leanweb clean` or `lw clean`
|
|
164
|
-
|
|
165
|
-
`lw clean` will delete `build/` and `dist/` directories.
|
|
166
|
-
|
|
167
|
-
### `leanweb upgrade` or `lw u`
|
|
168
|
-
|
|
169
|
-
`lw upgrade` will upgrade `src/lib/` directory if there is a new version
|
|
170
|
-
available.
|
|
171
|
-
|
|
172
|
-
### `leanweb destroy` or `lw destroy`
|
|
173
|
-
|
|
174
|
-
`lw destroy project-name` will remove the `src/`, `build/` and `dist/`
|
|
175
|
-
directory. Please note the `src/` directory will be deleted by this command.
|
|
176
|
-
|
|
177
|
-
### `leanweb help` or `lw help`
|
|
178
|
-
|
|
179
|
-
`lw help command-name` will print help information for the command. For
|
|
180
|
-
example, `lw help dist` or `lw h di` will print help information for
|
|
181
|
-
`lean dist`.
|
|
182
|
-
|
|
183
|
-
### `leanweb version` or `lw version`
|
|
184
|
-
|
|
185
|
-
`lw version` will print version information.
|
|
186
|
-
|
|
187
|
-
## lw directives
|
|
188
|
-
|
|
189
|
-
### lw
|
|
190
|
-
|
|
191
|
-
Contents inside a tag with `lw` directive are considered expressions that will
|
|
192
|
-
be evaluated. In the example below, the `<span lw>name</span>` will be
|
|
193
|
-
evaluated as `<span>Leanweb</span>`, because the variable `name` is defined
|
|
194
|
-
in the web component js file with the value `Leanweb`.
|
|
195
|
-
|
|
196
|
-
```html
|
|
197
|
-
Hello <span lw>name</span>!
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
```javascript
|
|
201
|
-
// ...
|
|
202
|
-
name = "Leanweb";
|
|
203
|
-
// ...
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
```
|
|
207
|
-
Hello Leanweb!
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### lw-if
|
|
211
|
-
|
|
212
|
-
```html
|
|
213
|
-
<span lw-if='name==="Leanweb"'>Leanweb</span>
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
The `span` DOM node will be shown if `name==="Leanweb"` will evaluate true,
|
|
217
|
-
otherwise, it will not be shown.
|
|
218
|
-
|
|
219
|
-
### lw-for
|
|
220
|
-
|
|
221
|
-
The following example shows how `lw-for` directive helps to generate DOM nodes
|
|
222
|
-
for each `item` in the `items` array.
|
|
223
|
-
|
|
224
|
-
```html
|
|
225
|
-
<div lw lw-for="item, $index in items">$index+': '+item</div>
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
```javascript
|
|
229
|
-
// ...
|
|
230
|
-
items = ["one", "two", "three"];
|
|
231
|
-
// ...
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
```
|
|
235
|
-
0: one
|
|
236
|
-
1: two
|
|
237
|
-
2: three
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
#### access DOM from lw-for
|
|
241
|
-
|
|
242
|
-
You could access DOM nodes for each element in a `lw-for` loop by calling
|
|
243
|
-
`elem.getDom()` as long as `typeof elem` evaluates `object`.
|
|
244
|
-
|
|
245
|
-
### lw-model and lw-on:
|
|
246
|
-
|
|
247
|
-
```html
|
|
248
|
-
<input type="text" lw-model="name" />
|
|
249
|
-
<span lw>name</span>
|
|
250
|
-
<br />
|
|
251
|
-
<button lw-on:click="resetName()">Reset Name</button>
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
You could bind multiple events like `lw-on:click,change=handler($event, $node)`.
|
|
255
|
-
|
|
256
|
-
```javascript
|
|
257
|
-
// ...
|
|
258
|
-
resetName() {
|
|
259
|
-
this.name = 'Leanweb';
|
|
260
|
-
}
|
|
261
|
-
// ...
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
<img src='https://leanweb.app/images/lw-model.gif' alt='lw-model'/>
|
|
265
|
-
|
|
266
|
-
### lw-class:
|
|
267
|
-
|
|
268
|
-
```html
|
|
269
|
-
<div lw lw-for="item, $index in items" lw-class:active="isActive($index)">
|
|
270
|
-
item
|
|
271
|
-
</div>
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
```javascript
|
|
275
|
-
// ...
|
|
276
|
-
items = ['one', 'two', 'three'];
|
|
277
|
-
isActive(index) {
|
|
278
|
-
return index === 1;
|
|
279
|
-
}
|
|
280
|
-
// ...
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
```scss
|
|
284
|
-
.active {
|
|
285
|
-
color: red;
|
|
286
|
-
}
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
<img src='https://leanweb.app/images/lw-class.png' alt='lw-class' width='640'/>
|
|
290
|
-
|
|
291
|
-
### lw-bind:
|
|
292
|
-
|
|
293
|
-
```html
|
|
294
|
-
<img lw-bind:src="imgSrc" lw-bind:width="imageWidth" />
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
```javascript
|
|
298
|
-
// ...
|
|
299
|
-
imgSrc = "https://leanweb.app/images/az.gif";
|
|
300
|
-
imageWidth = 400;
|
|
301
|
-
// ...
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
<img src='https://leanweb.app/images/lw-bind.png' alt='lw-bind' width='640'/>
|
|
305
|
-
|
|
306
|
-
### lw-input:
|
|
307
|
-
|
|
308
|
-
`lw-input` is used to pass and share data from parent to children.
|
|
309
|
-
|
|
310
|
-
`demo-parent.html`
|
|
311
|
-
|
|
312
|
-
```html
|
|
313
|
-
<demo-child lw-input:parent="this" lw-input:userData="user"></demo-child>
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
`demo-parent.js`
|
|
317
|
-
|
|
318
|
-
```javascript
|
|
319
|
-
// ...
|
|
320
|
-
user = { firstname: "Qian", lastname: "Chen" };
|
|
321
|
-
// ...
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
The child is able to access the `parent` and `user` object passed in with
|
|
325
|
-
`lw-input:` directive from `inputReady()` method.
|
|
326
|
-
`demo-child.js`
|
|
327
|
-
|
|
328
|
-
```javascript
|
|
329
|
-
// ...
|
|
330
|
-
inputReady() {
|
|
331
|
-
console.log(this.parent);
|
|
332
|
-
console.log(this.userData);
|
|
333
|
-
}
|
|
334
|
-
// ...
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
## Form Binding
|
|
338
|
-
|
|
339
|
-
Here is a few examples how Leanweb helps web components work with form binding.
|
|
340
|
-
|
|
341
|
-
### Checkbox
|
|
342
|
-
|
|
343
|
-
#### Multiple chechboxes bound to an array
|
|
344
|
-
|
|
345
|
-
```javascript
|
|
346
|
-
// ...
|
|
347
|
-
items = ['one', 'two', 'three'];
|
|
348
|
-
toggleCheckboxes() {
|
|
349
|
-
if (this.checkedValues.length) {
|
|
350
|
-
this.checkedValues.length = 0;
|
|
351
|
-
} else {
|
|
352
|
-
this.checkedValues = [...this.items];
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
checkedValues = [];
|
|
356
|
-
// ...
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
```html
|
|
360
|
-
<button lw-on:click="toggleCheckboxes()">Toggle Checkboxes</button>
|
|
361
|
-
<div lw-for="item, $index in items">
|
|
362
|
-
<input type="checkbox" lw-bind:value="item" lw-model="checkedValues" />
|
|
363
|
-
<span lw>item</span>
|
|
364
|
-
</div>
|
|
365
|
-
<span lw>checkedValues</span>
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
<img src='https://leanweb.app/images/leanweb-form-binding-checkbox.gif' alt='Leanweb Form Binding Checkbox'/>
|
|
369
|
-
|
|
370
|
-
#### Single checkbox bound to a boolean value
|
|
371
|
-
|
|
372
|
-
```javascript
|
|
373
|
-
checked = false;
|
|
374
|
-
toggleCheckbox() {
|
|
375
|
-
this.checked = !this.checked;
|
|
376
|
-
}
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
```html
|
|
380
|
-
<button lw-on:click="toggleCheckbox()">Toggle Checkbox</button>
|
|
381
|
-
<div>
|
|
382
|
-
<input type="checkbox" lw-model="checked" />
|
|
383
|
-
<span lw>checked</span>
|
|
384
|
-
</div>
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
### Select
|
|
388
|
-
|
|
389
|
-
```javascript
|
|
390
|
-
// ...
|
|
391
|
-
items = ['one', 'two', 'three'];
|
|
392
|
-
selectTwo() {
|
|
393
|
-
this.selectedOption = 'two';
|
|
394
|
-
}
|
|
395
|
-
selectedOption;
|
|
396
|
-
// ...
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
```html
|
|
400
|
-
<button lw-on:click="selectTwo()">Select Two</button>
|
|
401
|
-
<div>
|
|
402
|
-
<select lw-model="selectedOption">
|
|
403
|
-
<option lw lw-for="item, $index in items">item</option>
|
|
404
|
-
</select>
|
|
405
|
-
</div>
|
|
406
|
-
<span lw> selectedOption </span>
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
<img src='https://leanweb.app/images/leanweb-form-binding-select.gif' alt='Leanweb Form Binding Select' />
|
|
410
|
-
|
|
411
|
-
### Multiple Select
|
|
412
|
-
|
|
413
|
-
```javascript
|
|
414
|
-
// ...
|
|
415
|
-
items = ['one', 'two', 'three'];
|
|
416
|
-
toggleAllOptions() {
|
|
417
|
-
if (this.selectedOptions.length) {
|
|
418
|
-
this.selectedOptions.length = 0;
|
|
419
|
-
} else {
|
|
420
|
-
this.selectedOptions = [...this.items];
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
selectedOptions = [];
|
|
424
|
-
// ...
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
```html
|
|
428
|
-
<button lw-on:click="toggleAllOptions()">Toggle All</button>
|
|
429
|
-
<div>
|
|
430
|
-
<select lw-model="selectedOptions" multiple>
|
|
431
|
-
<option lw lw-for="item, $index in items">item</option>
|
|
432
|
-
</select>
|
|
433
|
-
</div>
|
|
434
|
-
<span lw> selectedOptions </span>
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
<img src='https://leanweb.app/images/leanweb-form-binding-multiple-select.gif' alt='Leanweb Form Binding Multiple Select' />
|
|
438
|
-
|
|
439
|
-
### Radio Button
|
|
440
|
-
|
|
441
|
-
```javascript
|
|
442
|
-
// ...
|
|
443
|
-
items = ['one', 'two', 'three'];
|
|
444
|
-
chooseTwo() {
|
|
445
|
-
this.picked = 'two';
|
|
446
|
-
}
|
|
447
|
-
picked;
|
|
448
|
-
// ...
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
```html
|
|
452
|
-
<button lw-on:click="chooseTwo()">Choose Two</button>
|
|
453
|
-
<div lw-for="item, $index in items">
|
|
454
|
-
<input
|
|
455
|
-
type="radio"
|
|
456
|
-
name="pickOne"
|
|
457
|
-
lw-bind:value="item"
|
|
458
|
-
lw-model="picked"
|
|
459
|
-
/><span lw>item</span>
|
|
460
|
-
</div>
|
|
461
|
-
<span lw>picked</span>
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
<img src='https://leanweb.app/images/leanweb-form-binding-radio-button.gif' alt='Leanweb Form Binding Radio Button' />
|
|
465
|
-
|
|
466
|
-
### Range
|
|
467
|
-
|
|
468
|
-
```javascript
|
|
469
|
-
// ...
|
|
470
|
-
selectRange50() {
|
|
471
|
-
this.selectedRange = 50;
|
|
472
|
-
}
|
|
473
|
-
selectedRange = 10;
|
|
474
|
-
// ...
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
```html
|
|
478
|
-
<button lw-on:click="selectRange50()">Select Range 50</button> <br />
|
|
479
|
-
<input type="range" lw-model="selectedRange" />
|
|
480
|
-
<span lw>selectedRange</span>
|
|
481
|
-
```
|
|
482
|
-
|
|
483
|
-
<img src='https://leanweb.app/images/leanweb-form-binding-range.gif' alt='Leanweb Form Binding Range' />
|
|
484
|
-
|
|
485
|
-
## Component Communication
|
|
486
|
-
|
|
487
|
-
The following project demonstrates how Leanweb helps web components to talk to
|
|
488
|
-
each other.
|
|
489
|
-
|
|
490
|
-
<img src='https://leanweb.app/images/leanweb-pub-sub.gif' alt='Leanweb Component Communication'/>
|
|
491
|
-
|
|
492
|
-
`pub.js`
|
|
493
|
-
|
|
494
|
-
```javascript
|
|
495
|
-
// import LWElement from './../../lib/lw-element.js';
|
|
496
|
-
// import ast from './ast.js';
|
|
497
|
-
|
|
498
|
-
// customElements.define('demo-pub',
|
|
499
|
-
// class extends LWElement { // LWElement extends HTMLElement
|
|
500
|
-
// constructor() {
|
|
501
|
-
// super(ast);
|
|
502
|
-
|
|
503
|
-
setInterval(() => {
|
|
504
|
-
this.time = new Date(Date.now()).toLocaleString();
|
|
505
|
-
leanweb.eventBus.dispatchEvent("time", this.time);
|
|
506
|
-
this.update();
|
|
507
|
-
}, 1000);
|
|
508
|
-
|
|
509
|
-
// }
|
|
510
|
-
// }
|
|
511
|
-
// );
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
`pub.html`
|
|
515
|
-
|
|
516
|
-
```html
|
|
517
|
-
<div class="pub">
|
|
518
|
-
<span>Time Publisher</span>
|
|
519
|
-
<span lw>time</span>
|
|
520
|
-
</div>
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
`sub.js`
|
|
524
|
-
|
|
525
|
-
```javascript
|
|
526
|
-
// import LWElement from './../../lib/lw-element.js';
|
|
527
|
-
// import ast from './ast.js';
|
|
528
|
-
|
|
529
|
-
// customElements.define('demo-sub',
|
|
530
|
-
// class extends LWElement { // LWElement extends HTMLElement
|
|
531
|
-
// constructor() {
|
|
532
|
-
// super(ast);
|
|
533
|
-
// }
|
|
534
|
-
|
|
535
|
-
sub() {
|
|
536
|
-
this.listener = leanweb.eventBus.addEventListener('time', event => {
|
|
537
|
-
this.time = event.data;
|
|
538
|
-
this.update();
|
|
539
|
-
});
|
|
540
|
-
this.subscribed = true;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
unsub() {
|
|
544
|
-
leanweb.eventBus.removeEventListener(this.listener);
|
|
545
|
-
this.subscribed = false;
|
|
546
|
-
}
|
|
547
|
-
// }
|
|
548
|
-
// );
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
`sub.html`
|
|
552
|
-
|
|
553
|
-
```html
|
|
554
|
-
<div class="sub">
|
|
555
|
-
<span>Time Subscriber</span>
|
|
556
|
-
<span lw>time</span>
|
|
557
|
-
<div class="buttons">
|
|
558
|
-
<button lw-bind:disabled="subscribed" lw-on:click="sub()">
|
|
559
|
-
Subscribe Time
|
|
560
|
-
</button>
|
|
561
|
-
<button lw-bind:disabled="!subscribed" lw-on:click="unsub()">
|
|
562
|
-
UnSubscribe Time
|
|
563
|
-
</button>
|
|
564
|
-
</div>
|
|
565
|
-
</div>
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
Source code of this demo https://github.com/elgs/leanweb-pub-sub-demo.
|
|
569
|
-
|
|
570
|
-
## API
|
|
571
|
-
|
|
572
|
-
### globalThis.leanweb
|
|
573
|
-
|
|
574
|
-
`leanweb` is the only foot print on `globalThis` scope.
|
|
575
|
-
|
|
576
|
-
#### updateComponents(...tagNames)
|
|
577
|
-
|
|
578
|
-
`updateComponents` is used to update all component DOMs or DOMs of specific
|
|
579
|
-
component tag names. `updateComponents` takes any number of component tag
|
|
580
|
-
names as arguments. If no argument is provided, it will update all component
|
|
581
|
-
DOMs app wide.
|
|
582
|
-
|
|
583
|
-
#### eventBus
|
|
584
|
-
|
|
585
|
-
An instance of `LWEventBus` managed by `leanweb` to pass DOM update events.
|
|
586
|
-
|
|
587
|
-
#### urlHash
|
|
588
|
-
|
|
589
|
-
`urlHash` is a reference to `window.location.hash` which can be used for
|
|
590
|
-
routing.
|
|
591
|
-
|
|
592
|
-
#### urlHashPath
|
|
593
|
-
|
|
594
|
-
`urlHashPath` is used to set or get the `path` part in the urlHash. If the
|
|
595
|
-
`urlHash` is `#/login?a=b&a=b&c=d`, `urlHashPath` will be `#/login`.
|
|
596
|
-
|
|
597
|
-
#### urlHashParams
|
|
598
|
-
|
|
599
|
-
`urlHashParams` is used to set or get the `parameters` in the urlHash. If the
|
|
600
|
-
`urlHash` is `#/login?a=b&a=b&c=d`, `urlHashParams` will be
|
|
601
|
-
`{a: ['b', 'b'], c: 'd'}`.
|
|
602
|
-
|
|
603
|
-
### LWElement
|
|
604
|
-
|
|
605
|
-
`LWElement` extends `HTMLElement`, and Leanweb components extend `LWElement`.
|
|
606
|
-
So Leanweb components are just more specific versions of the standard
|
|
607
|
-
`HTMLElement`. `LWElement` helps to wire up the `lw` directives in the HTML and
|
|
608
|
-
provides some convenient methods to update the DOM.
|
|
609
|
-
|
|
610
|
-
#### update(rootNode = this.shadowRoot)
|
|
611
|
-
|
|
612
|
-
The `update` method provides a convenient way to update the DOM when the model
|
|
613
|
-
changes. You should feel free to use old way to update DOM. The `update` just
|
|
614
|
-
makes life a little easier. `update` takes `rootNode` as parameter, which
|
|
615
|
-
allows you to specify which DOM element to start with. The default value is
|
|
616
|
-
the current`shadowRoot`.
|
|
617
|
-
|
|
618
|
-
LWElement will call update in the following scenarios:
|
|
619
|
-
|
|
620
|
-
1. after all `lw` directives are initially bound to DOM;
|
|
621
|
-
2. after `lw-on:` event is fired;
|
|
622
|
-
3. after `lw-model` change is fired;
|
|
623
|
-
|
|
624
|
-
You may need to call the `update()` method manually in other events. For
|
|
625
|
-
example:
|
|
626
|
-
|
|
627
|
-
1. in your setTimeout/setInterval callbacks;
|
|
628
|
-
2. in `LWEventBus` callbacks;
|
|
629
|
-
3. in any network api callbacks;
|
|
630
|
-
|
|
631
|
-
#### domReady()
|
|
632
|
-
|
|
633
|
-
`domReady()` will be called after all initial DOM events are bound, and all
|
|
634
|
-
DOM interpolations are evaluated. This method is meant to be overridden and is a
|
|
635
|
-
great place to send events to the event bus.
|
|
636
|
-
|
|
637
|
-
#### inputReady()
|
|
638
|
-
|
|
639
|
-
`inputReady()` will be called after all input data from parent's `lw-input:`
|
|
640
|
-
are ready. In this method, children are able to access the passed in data
|
|
641
|
-
shared by parents.
|
|
642
|
-
|
|
643
|
-
#### turnedOn()
|
|
644
|
-
|
|
645
|
-
Called when the componnet `lw-if` is evaluated `true`.
|
|
646
|
-
|
|
647
|
-
#### turnedOff()
|
|
648
|
-
|
|
649
|
-
Called when the componnet `lw-if` is evaluated `false`.
|
|
650
|
-
|
|
651
|
-
#### urlHashChanged()
|
|
652
|
-
|
|
653
|
-
If `urlHashChanged()` is defined as a function, it will be called whenever the
|
|
654
|
-
urlHash changes. This could be useful to update the DOM in component routing.
|
|
655
|
-
|
|
656
|
-
### LWEventBus
|
|
657
|
-
|
|
658
|
-
`LWElement` comes with a global instance of `LWEventBus` that helps web
|
|
659
|
-
components to talk to each other by sending and receiving events and data. You
|
|
660
|
-
could use your own way for component communication. `LWEventBus` is just a
|
|
661
|
-
choice for you.
|
|
662
|
-
|
|
663
|
-
#### addEventListener(eventName, callback)
|
|
664
|
-
|
|
665
|
-
You can use `leanweb.eventBus` to get the global instance of event bus, and
|
|
666
|
-
use `leanweb.eventBus.addEventListener(eventName, callback)` to subscribe to
|
|
667
|
-
a type of event from the event bus. `addEventListener` takes two parameters.
|
|
668
|
-
The first `eventName` is the name of the event, and the second `callback` is a
|
|
669
|
-
function that will get called when a event is sent to the event bus. The
|
|
670
|
-
callback function that takes a parameter `event`, which contains `eventName`
|
|
671
|
-
and `data` fields. `addEventListener` returns the eventListener instance
|
|
672
|
-
being added, which could be passed in `removeEventListener` as parameter.
|
|
673
|
-
|
|
674
|
-
#### removeEventListener(listener)
|
|
675
|
-
|
|
676
|
-
`removeEventListener` removes the listener from the event bus, so it stops
|
|
677
|
-
being notified when a next event is fired.
|
|
678
|
-
|
|
679
|
-
#### dispatchEvent(eventName, data = null)
|
|
680
|
-
|
|
681
|
-
`dispatchEvent` is used to send an event to the event bus. It takes two
|
|
682
|
-
parameters. `eventName` is the name of the event, and `data` is the payload data
|
|
683
|
-
of the event.
|
|
684
|
-
|
|
685
|
-
### Post dist hook
|
|
686
|
-
|
|
687
|
-
If `post-dist` file is present in the project root directory, it will be called
|
|
688
|
-
after `lw dist` is done. This could be useful to copy `dist/\*` to somewhere
|
|
689
|
-
else.
|
|
690
|
-
|
|
691
|
-
## More examples and tutorials
|
|
692
|
-
|
|
693
|
-
https://leanweb.app
|
|
1
|
+
Please visit <a href="https://leanweb.app">leanweb.app</a>
|
package/lib/lw-html-parser.js
CHANGED
|
@@ -75,6 +75,11 @@ const walkNode = (node, interpolation) => {
|
|
|
75
75
|
const lwType = lw[0];
|
|
76
76
|
const lwValue = lw[1];
|
|
77
77
|
|
|
78
|
+
if (attr.name === 'lw-bind:class') {
|
|
79
|
+
const classAttr = node.attrs.find(a => a.name === 'class');
|
|
80
|
+
node.attrs.push({ name: 'lw-init-class', value: classAttr.value });
|
|
81
|
+
}
|
|
82
|
+
|
|
78
83
|
const ast = getAST(attr.value);
|
|
79
84
|
removeASTLocation(ast);
|
|
80
85
|
interpolation[key] = { ast, loc, lwType, lwValue };
|
package/package.json
CHANGED
package/templates/index.html
CHANGED
|
@@ -418,10 +418,19 @@ export default class LWElement extends HTMLElement {
|
|
|
418
418
|
const interpolation = this.ast[attrValue];
|
|
419
419
|
const parsed = parser.evaluate(interpolation.ast, context, interpolation.loc);
|
|
420
420
|
|
|
421
|
-
if (
|
|
422
|
-
bindNode.
|
|
421
|
+
if (interpolation.lwValue === 'class') {
|
|
422
|
+
const initClass = bindNode.getAttribute('lw-init-class');
|
|
423
|
+
if (!parsed[0]) {
|
|
424
|
+
bindNode.classList.remove(parsed[0]);
|
|
425
|
+
} else {
|
|
426
|
+
bindNode.classList = initClass + ' ' + parsed[0];
|
|
427
|
+
}
|
|
423
428
|
} else {
|
|
424
|
-
|
|
429
|
+
if (parsed[0] !== undefined && parsed[0] !== null) {
|
|
430
|
+
bindNode.setAttribute(interpolation.lwValue, parsed[0]);
|
|
431
|
+
} else {
|
|
432
|
+
bindNode.removeAttribute(interpolation.lwValue);
|
|
433
|
+
}
|
|
425
434
|
}
|
|
426
435
|
}
|
|
427
436
|
}
|
|
@@ -175,7 +175,8 @@ const immediateContext = (node, context) => {
|
|
|
175
175
|
if (context.length === 0) {
|
|
176
176
|
return null;
|
|
177
177
|
}
|
|
178
|
-
|
|
178
|
+
const qualifiedContext = context.filter(contextObj => !(('$event' in contextObj && '$node' in contextObj) || 'this' in contextObj));
|
|
179
|
+
return context.find(contextObj => node.name in contextObj) ?? qualifiedContext[0];
|
|
179
180
|
} else if (typeof context === 'object') {
|
|
180
181
|
return context;
|
|
181
182
|
}
|
package/templates/page.html
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
5
6
|
<title>${page.name} - ${project.name}</title>
|
|
6
7
|
<script type="module" src="${pathLevels}${project.name}.js"></script>
|
|
7
8
|
<link rel="stylesheet" href="${pathLevels}${project.name}.css">
|