@webtides/element-js 1.0.0-alpha.9 → 1.1.0
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/CHANGELOG.md +369 -0
- package/README.md +3 -3
- package/docs/README.md +70 -89
- package/docs/concepts/classes.md +37 -66
- package/docs/concepts/hierarchy.md +29 -31
- package/docs/concepts/lifecycle.md +9 -23
- package/docs/concepts/reactivity.md +1 -3
- package/docs/features/computed-properties.md +13 -16
- package/docs/features/dependency-injection-context-protocol.md +75 -87
- package/docs/features/global-styles.md +1 -1
- package/docs/features/methods.md +14 -15
- package/docs/features/properties.md +21 -33
- package/docs/features/refs.md +23 -31
- package/docs/features/renderless.md +31 -37
- package/docs/features/store.md +63 -78
- package/docs/features/templates.md +4 -1
- package/docs/features/watch.md +28 -35
- package/docs/getting-started/upgrade-to-v1.md +16 -0
- package/docs/guide/guide.md +10 -22
- package/index.js +29 -2
- package/package.json +62 -62
- package/src/BaseElement.js +637 -632
- package/src/StyledElement.js +104 -104
- package/src/TemplateElement.js +53 -58
- package/src/dom-parts/AttributePart.js +116 -116
- package/src/dom-parts/ChildNodePart.js +288 -288
- package/src/dom-parts/NodePart.js +27 -27
- package/src/dom-parts/Part.js +6 -6
- package/src/dom-parts/TemplatePart.js +119 -119
- package/src/dom-parts/TemplateResult.js +274 -267
- package/src/dom-parts/directives.js +105 -48
- package/src/dom-parts/html.js +2 -2
- package/src/dom-parts/render.js +6 -1
- package/src/util/AttributeParser.js +47 -47
- package/src/util/DOMHelper.js +190 -191
- package/src/util/Directive.js +31 -24
- package/src/util/GlobalStylesStore.js +56 -49
- package/src/util/SerializeStateHelper.js +56 -56
- package/src/util/Store.js +174 -174
- package/src/util/defineElement.js +5 -5
- package/src/util/toString.js +5 -5
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
## Attributes/Properties
|
|
2
2
|
|
|
3
|
-
With attributes and properties you can build out and define the public API of an element. _element-js_ elements can have
|
|
4
|
-
three different types of properties.
|
|
3
|
+
With attributes and properties you can build out and define the public API of an element. _element-js_ elements can have three different types of properties.
|
|
5
4
|
|
|
6
5
|
```javascript
|
|
7
6
|
export class MyElement extends BaseElement {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
7
|
+
_privatePropery = 'I am private (by convention)';
|
|
8
|
+
publicProperty = 'I am public';
|
|
9
|
+
|
|
10
|
+
properties() {
|
|
11
|
+
return {
|
|
12
|
+
reactivePublicProperty: 'I am public & reactive'
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
log() {
|
|
17
|
+
console.log(this._privatePropery);
|
|
18
|
+
console.log(this.publicProperty);
|
|
19
|
+
console.log(this.reactivePublicProperty);
|
|
20
|
+
}
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
defineElement('my-element', MyElement);
|
|
@@ -26,31 +25,20 @@ defineElement('my-element', MyElement);
|
|
|
26
25
|
|
|
27
26
|
#### Defining properties in JavaScript
|
|
28
27
|
|
|
29
|
-
Public properties for JavaScript classes where recently added by the JavaScript standard committee. For more information
|
|
30
|
-
see
|
|
31
|
-
the [Class fields documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields)
|
|
32
|
-
.
|
|
28
|
+
Public properties for JavaScript classes where recently added by the JavaScript standard committee. For more information see the [Class fields documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields) .
|
|
33
29
|
|
|
34
|
-
On top of these fields _element-js_ adds another form of properties - reactive properties. By overwriting
|
|
35
|
-
the `properties()` method on your element and returning an object of key/value pairs you can define a list of reactive
|
|
36
|
-
public properties. Behind the scenes _element-js_ will automatically generate getters and setters for each property and
|
|
37
|
-
trigger an update/render for the element when these properties change.
|
|
30
|
+
On top of these fields _element-js_ adds another form of properties - reactive properties. By overwriting the `properties()` method on your element and returning an object of key/value pairs you can define a list of reactive public properties. Behind the scenes _element-js_ will automatically generate getters and setters for each property and trigger an update/render for the element when these properties change.
|
|
38
31
|
|
|
39
32
|
All types of properties can be accessed via the `this` operator within the element class or the template function.
|
|
40
33
|
|
|
41
34
|
#### Using attributes/properties in HTML
|
|
42
35
|
|
|
43
|
-
_element-js_ will merge all attributes with the reactive properties map. Values set via attribute will always take
|
|
44
|
-
precedence over values defined in the properties map. Attributes will still be defined as properties even when they are
|
|
45
|
-
not set in the properties map.
|
|
36
|
+
_element-js_ will merge all attributes with the reactive properties map. Values set via attribute will always take precedence over values defined in the properties map. Attributes will still be defined as properties even when they are not set in the properties map.
|
|
46
37
|
|
|
47
|
-
HTML only has the concept of attributes and `string` values. _element-js_ will automatically convert attributes to their
|
|
48
|
-
correct types of `string`, `number`, `boolean`, `array` and `object`.
|
|
38
|
+
HTML only has the concept of attributes and `string` values. _element-js_ will automatically convert attributes to their correct types of `string`, `number`, `boolean`, `array` and `object`.
|
|
49
39
|
|
|
50
|
-
> In JavaScript you will typically use camelCase for declaring properties. HTML attributes however only can be
|
|
51
|
-
> lowercase. _element-js_ will therefore convert dash-case names to camelCase variables and vice versa.
|
|
40
|
+
> In JavaScript you will typically use camelCase for declaring properties. HTML attributes however only can be lowercase. _element-js_ will therefore convert dash-case names to camelCase variables and vice versa.
|
|
52
41
|
|
|
53
42
|
#### Property options
|
|
54
43
|
|
|
55
|
-
Reactive properties can be fine-tuned further by providing options via the constructor. See `propertyOptions`
|
|
56
|
-
in [Constructor options](../concepts/classes.md).
|
|
44
|
+
Reactive properties can be fine-tuned further by providing options via the constructor. See `propertyOptions` in [Constructor options](../concepts/classes.md).
|
package/docs/features/refs.md
CHANGED
|
@@ -1,47 +1,39 @@
|
|
|
1
|
-
|
|
2
1
|
### Refs
|
|
3
2
|
|
|
4
|
-
To avoid constant lookups of (child) elements from your elements _element-js_ will collect all elements with
|
|
5
|
-
a `[ref=“id”]` attribute and make them available as an object on the element itself via the special `this.$refs`
|
|
6
|
-
reference.
|
|
7
|
-
_element-js_ also provides referencing a list of Nodes by adding a dangling `[]` to the refs id `[ref=“entries[]”]`.
|
|
8
|
-
Be aware that list references will override singular references with the same name as they are considered to be more
|
|
9
|
-
explicit.
|
|
3
|
+
To avoid constant lookups of (child) elements from your elements _element-js_ will collect all elements with a `[ref=“id”]` attribute and make them available as an object on the element itself via the special `this.$refs` reference. _element-js_ also provides referencing a list of Nodes by adding a dangling `[]` to the refs id `[ref=“entries[]”]`. Be aware that list references will override singular references with the same name as they are considered to be more explicit.
|
|
10
4
|
|
|
11
5
|
```html
|
|
12
|
-
|
|
13
6
|
<places-search>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
<input type="text" ref="input" />
|
|
8
|
+
<places-list ref="list">
|
|
9
|
+
<list-entry ref="entries[]">A</list-entry>
|
|
10
|
+
<list-entry ref="entries[]">B</list-entry>
|
|
11
|
+
<list-entry ref="entries[]">C</list-entry>
|
|
12
|
+
</places-list>
|
|
20
13
|
</places-search>
|
|
21
14
|
```
|
|
22
15
|
|
|
23
16
|
```javascript
|
|
24
|
-
import {BaseElement, defineElement, html} from '@webtides/element-js';
|
|
17
|
+
import { BaseElement, defineElement, html } from '@webtides/element-js';
|
|
25
18
|
|
|
26
19
|
export class PlacesSearch extends BaseElement {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
20
|
+
events() {
|
|
21
|
+
return {
|
|
22
|
+
input: {
|
|
23
|
+
blur: (e) => {
|
|
24
|
+
// fetch places...
|
|
25
|
+
const places = [];
|
|
26
|
+
// update singular ref
|
|
27
|
+
this.$refs.list = places;
|
|
28
|
+
// do something with a list reference
|
|
29
|
+
this.$refs.entries.forEach((entry) => console.log(entry.innerText)); // A , B , C
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
41
34
|
}
|
|
42
35
|
|
|
43
36
|
defineElement('places-search', PlacesSearch);
|
|
44
37
|
```
|
|
45
38
|
|
|
46
|
-
> _element-js_ will smartly add and remove all refs on every update/render cycle to ensure that they will always be
|
|
47
|
-
> correct even though you might have changed the child DOM tree during render cycles.
|
|
39
|
+
> _element-js_ will smartly add and remove all refs on every update/render cycle to ensure that they will always be correct even though you might have changed the child DOM tree during render cycles.
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
|
|
2
1
|
### Not Rendering Elements / BaseElement
|
|
3
2
|
|
|
4
|
-
More often than not you won't actually need to render anything from your element but rather only trigger some changes on
|
|
5
|
-
other elements, fire events or fetch data and distribute it to related elements.
|
|
3
|
+
More often than not you won't actually need to render anything from your element but rather only trigger some changes on other elements, fire events or fetch data and distribute it to related elements.
|
|
6
4
|
|
|
7
|
-
A good example for this pattern is to use container elements for fetching data and delegating it to child elements that
|
|
8
|
-
only take attributes/properties and simply render them.
|
|
5
|
+
A good example for this pattern is to use container elements for fetching data and delegating it to child elements that only take attributes/properties and simply render them.
|
|
9
6
|
|
|
10
7
|
```html
|
|
11
8
|
|
|
@@ -16,54 +13,51 @@ only take attributes/properties and simply render them.
|
|
|
16
13
|
```
|
|
17
14
|
|
|
18
15
|
```javascript
|
|
19
|
-
import {BaseElement, defineElement} from '@webtides/element-js';
|
|
16
|
+
import { BaseElement, defineElement } from '@webtides/element-js';
|
|
20
17
|
|
|
21
18
|
export class PlacesSearch extends BaseElement {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
events() {
|
|
20
|
+
return {
|
|
21
|
+
input: {
|
|
22
|
+
blur: (e) => {
|
|
23
|
+
// fetch places...
|
|
24
|
+
const places = ['list of place objects…'];
|
|
25
|
+
this.$refs.list = places;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
defineElement('places-search', PlacesSearch);
|
|
36
33
|
```
|
|
37
34
|
|
|
38
|
-
Another good use case for renderless elements is when you need to change some classes or styles on child elements. It is
|
|
39
|
-
a bit like `jQuery` but much better because everything is declaratively composed of little elements instead of having a
|
|
40
|
-
big JavaScript file full of little mutations.
|
|
35
|
+
Another good use case for renderless elements is when you need to change some classes or styles on child elements. It is a bit like `jQuery` but much better because everything is declaratively composed of little elements instead of having a big JavaScript file full of little mutations.
|
|
41
36
|
|
|
42
37
|
```html
|
|
43
|
-
|
|
44
38
|
<dropdown-element>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
<button>Options</button>
|
|
40
|
+
<ul ref="“list”" class="“”hidden">
|
|
41
|
+
<li>Profile</li>
|
|
42
|
+
<li>Settings</li>
|
|
43
|
+
<li>Sign out</li>
|
|
44
|
+
</ul>
|
|
51
45
|
</dropdown-element>
|
|
52
46
|
```
|
|
53
47
|
|
|
54
48
|
```javascript
|
|
55
|
-
import {BaseElement, defineElement} from '@webtides/element-js';
|
|
49
|
+
import { BaseElement, defineElement } from '@webtides/element-js';
|
|
56
50
|
|
|
57
51
|
class DropdownElement extends BaseElement {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
52
|
+
events() {
|
|
53
|
+
return {
|
|
54
|
+
button: {
|
|
55
|
+
click: () => {
|
|
56
|
+
this.$refs.list.classList.remove('hidden');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
67
61
|
}
|
|
68
62
|
|
|
69
63
|
defineElement('dropdown-element', DropdownElement);
|
package/docs/features/store.md
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
#### Shared State / Reactive State / Store
|
|
2
2
|
|
|
3
|
-
When a certain property in the properties map is an instance of the provided class `Store` it is treated as an
|
|
4
|
-
external `Store`.
|
|
5
|
-
A `Store` can be created outside a components´ scope or in a dedicated Module to keep track of shared Application
|
|
6
|
-
State.
|
|
7
|
-
When a components´ property is an instance of a `Store` the instance will be added as an observer and
|
|
8
|
-
automatically updated when the store ist changed.
|
|
3
|
+
When a certain property in the properties map is an instance of the provided class `Store` it is treated as an external `Store`. A `Store` can be created outside a components´ scope or in a dedicated Module to keep track of shared Application State. When a components´ property is an instance of a `Store` the instance will be added as an observer and automatically updated when the store ist changed.
|
|
9
4
|
|
|
10
|
-
Instances of Store provide a way to share global state between as many components in an application as you like.
|
|
11
|
-
Shared State can be something very simple as (updated) Viewport Dimensions, Media Changes or complex fetched data from a REST Endpoint.
|
|
5
|
+
Instances of Store provide a way to share global state between as many components in an application as you like. Shared State can be something very simple as (updated) Viewport Dimensions, Media Changes or complex fetched data from a REST Endpoint.
|
|
12
6
|
|
|
13
7
|
Stores can also be initialized with a primitive value (Numbers, Booleans). Such stores will switch to a single Property Mode and provide direct access tio the property value via the stores valueOf / toString Function for direct access.
|
|
14
8
|
|
|
@@ -16,53 +10,49 @@ Stores can also be initialized with a primitive value (Numbers, Booleans). Such
|
|
|
16
10
|
|
|
17
11
|
```js
|
|
18
12
|
export const simpleStore = new Store({
|
|
19
|
-
|
|
20
|
-
})
|
|
13
|
+
value: 'simple'
|
|
14
|
+
});
|
|
21
15
|
|
|
22
16
|
class MoreComplexStore extends Store {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
17
|
+
properties() {
|
|
18
|
+
return {
|
|
19
|
+
storeCount: 1
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
get sum() {
|
|
23
|
+
return this.storeCount + this.argumentCount;
|
|
24
|
+
}
|
|
31
25
|
}
|
|
32
26
|
export const exampleStore = new MoreComplexStore({
|
|
33
|
-
|
|
34
|
-
})
|
|
35
|
-
|
|
27
|
+
argumentCount: 1
|
|
28
|
+
});
|
|
36
29
|
|
|
37
30
|
class MediaStore extends Store {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
31
|
+
constructor() {
|
|
32
|
+
super();
|
|
33
|
+
const mql = window.matchMedia('(max-width: 600px)');
|
|
34
|
+
mql.onchange = (e) => {
|
|
35
|
+
this.isMobile = e.matches;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
properties() {
|
|
39
|
+
return {
|
|
40
|
+
isMobile: true
|
|
41
|
+
};
|
|
42
|
+
}
|
|
50
43
|
}
|
|
51
|
-
export const mediaStore = new MediaStore()
|
|
44
|
+
export const mediaStore = new MediaStore();
|
|
52
45
|
|
|
53
|
-
export const scrollYStore = new Store(0)
|
|
46
|
+
export const scrollYStore = new Store(0);
|
|
54
47
|
window.addEventListener('scroll', () => {
|
|
55
|
-
|
|
48
|
+
scrollYStore.value = window.scrollY;
|
|
56
49
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
50
|
```
|
|
60
51
|
|
|
61
52
|
##### MyElement.js
|
|
62
53
|
|
|
63
|
-
```js
|
|
64
|
-
|
|
65
|
-
import { exampleStore, simpleStore, mediaStore } from './store.js';
|
|
54
|
+
```js
|
|
55
|
+
import { exampleStore, simpleStore, mediaStore } from './store.js';
|
|
66
56
|
|
|
67
57
|
export class MyElement extends TemplateElement {
|
|
68
58
|
properties() {
|
|
@@ -78,66 +68,61 @@ export class MyElement extends TemplateElement {
|
|
|
78
68
|
return `
|
|
79
69
|
<div>Simple Store: ${this.store.value} == "simple" </div>
|
|
80
70
|
<div>Complex Store: ${this.exampleStore.sum} == 2</div>
|
|
81
|
-
<div>Media: ${this.mediaStore.isMobile ?
|
|
71
|
+
<div>Media: ${this.mediaStore.isMobile ? 'MOBILE' : 'DESKTOP'}</div>
|
|
82
72
|
<div>Scoll Position: ${this.scrollYStore}</div>
|
|
83
|
-
|
|
73
|
+
`;
|
|
84
74
|
}
|
|
85
75
|
}
|
|
86
76
|
|
|
87
77
|
export class AnotherElement extends TemplateElement {
|
|
88
78
|
properties() {
|
|
89
79
|
return {
|
|
90
|
-
store: simpleStore
|
|
80
|
+
store: simpleStore
|
|
91
81
|
};
|
|
92
82
|
}
|
|
93
83
|
|
|
94
84
|
template() {
|
|
95
85
|
return `
|
|
96
86
|
<div>Simple Store: ${this.store.value} == the same as in MyElement's template.</div>
|
|
97
|
-
|
|
87
|
+
`;
|
|
98
88
|
}
|
|
99
89
|
}
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
|
|
90
|
+
```
|
|
103
91
|
|
|
104
92
|
##### storeception
|
|
105
93
|
|
|
106
94
|
Changes too nested stores will trigger updates and watcher in ParentStore and ultimately transfer to all elements referencing the parent
|
|
107
95
|
|
|
108
|
-
```js
|
|
109
|
-
|
|
96
|
+
```js
|
|
110
97
|
class NestedStore extends Store {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
98
|
+
properties() {
|
|
99
|
+
return {
|
|
100
|
+
nestedCount: 0
|
|
101
|
+
};
|
|
102
|
+
}
|
|
116
103
|
}
|
|
117
104
|
|
|
118
105
|
class ParentStore extends Store {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
requestUpdate() {
|
|
127
|
-
// will be triggered on count OR nestedStore.nestedCount changes
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
watch() {
|
|
131
|
-
return {
|
|
132
|
-
count: (newCount, oldCount) => {
|
|
133
|
-
// will be triggered on count changes
|
|
134
|
-
},
|
|
135
|
-
nestedStore: () => {
|
|
136
|
-
// will be triggered on nestedStore changes
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
}
|
|
106
|
+
properties() {
|
|
107
|
+
return {
|
|
108
|
+
count: 0,
|
|
109
|
+
nestedStore: new NestedStore()
|
|
110
|
+
};
|
|
111
|
+
}
|
|
141
112
|
|
|
142
|
-
|
|
113
|
+
requestUpdate() {
|
|
114
|
+
// will be triggered on count OR nestedStore.nestedCount changes
|
|
115
|
+
}
|
|
143
116
|
|
|
117
|
+
watch() {
|
|
118
|
+
return {
|
|
119
|
+
count: (newCount, oldCount) => {
|
|
120
|
+
// will be triggered on count changes
|
|
121
|
+
},
|
|
122
|
+
nestedStore: () => {
|
|
123
|
+
// will be triggered on nestedStore changes
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
@@ -135,7 +135,9 @@ Returns:
|
|
|
135
135
|
|
|
136
136
|
**spreadAttributes(attributes)**
|
|
137
137
|
|
|
138
|
-
Renders multiple attributes as key="value" pairs from a map of attributes.
|
|
138
|
+
Renders multiple attributes as key="value" pairs from a map of attributes.
|
|
139
|
+
Be aware that `spreadAttributes` only works with the given values and does not remember prior iterations.
|
|
140
|
+
Therefore, it will only add / remove values that are present in the given map.
|
|
139
141
|
|
|
140
142
|
Parameters:
|
|
141
143
|
|
|
@@ -152,6 +154,7 @@ const attrs = { id: 'myElement', ariaLabel: 'Description' };
|
|
|
152
154
|
const attributeString = spreadAttributes(attrs);
|
|
153
155
|
// When invoked, returns: "id='myElement' aria-label='Description'"
|
|
154
156
|
```
|
|
157
|
+
|
|
155
158
|
|
|
156
159
|
##### Writing custom directives
|
|
157
160
|
|
package/docs/features/watch.md
CHANGED
|
@@ -1,54 +1,47 @@
|
|
|
1
|
-
|
|
2
1
|
### Watch Changes
|
|
3
2
|
|
|
4
|
-
Alongside defining reactive properties in the `properties()` map, you can define a watcher for every property by
|
|
5
|
-
implementing a `watch()` map in the same manner.
|
|
3
|
+
Alongside defining reactive properties in the `properties()` map, you can define a watcher for every property by implementing a `watch()` map in the same manner.
|
|
6
4
|
|
|
7
5
|
```javascript
|
|
8
6
|
export class MyElement extends BaseElement {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
7
|
+
properties() {
|
|
8
|
+
return {
|
|
9
|
+
property: 'I am public & reactive'
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
watch() {
|
|
14
|
+
return {
|
|
15
|
+
property: (newValue, oldValue) => {
|
|
16
|
+
console.log('property changed', { newValue, oldValue });
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
22
20
|
}
|
|
23
21
|
```
|
|
24
22
|
|
|
25
|
-
Whenever a property is changed (either from within the element or from outside) it will trigger the callback defined in
|
|
26
|
-
the `watch()` map if present. The callback will be invoked with the old value and the new value as parameters.
|
|
23
|
+
Whenever a property is changed (either from within the element or from outside) it will trigger the callback defined in the `watch()` map if present. The callback will be invoked with the old value and the new value as parameters.
|
|
27
24
|
|
|
28
|
-
_element-js_ compares values by stringifying them rather than by reference and will only trigger update/render cycles if
|
|
29
|
-
the value actually changed.
|
|
25
|
+
_element-js_ compares values by stringifying them rather than by reference and will only trigger update/render cycles if the value actually changed.
|
|
30
26
|
|
|
31
|
-
> `array` or `object` data will not trigger changes if nested elements/keys are changed. Also methods like `push()`
|
|
32
|
-
> and `shift()` won't work.
|
|
27
|
+
> `array` or `object` data will not trigger changes if nested elements/keys are changed. Also methods like `push()` and `shift()` won't work.
|
|
33
28
|
|
|
34
|
-
Non-mutable operations like `map()` and `shift()` and the spread operator will however change the value and trigger an
|
|
35
|
-
update cycle.
|
|
29
|
+
Non-mutable operations like `map()` and `shift()` and the spread operator will however change the value and trigger an update cycle.
|
|
36
30
|
|
|
37
31
|
```javascript
|
|
38
32
|
export class MyElement extends BaseElement {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
33
|
+
properties() {
|
|
34
|
+
return {
|
|
35
|
+
items: ['one', 'two']
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
count() {
|
|
40
|
+
this.items = [...this.items, 'three'];
|
|
41
|
+
}
|
|
48
42
|
}
|
|
49
43
|
```
|
|
50
44
|
|
|
51
45
|
The spread operator works for objects and arrays.
|
|
52
46
|
|
|
53
|
-
If you want to make sure that an update will be triggered you can always request it manually by calling
|
|
54
|
-
the `requestUpdate()` method.
|
|
47
|
+
If you want to make sure that an update will be triggered you can always request it manually by calling the `requestUpdate()` method.
|
|
@@ -75,6 +75,22 @@ In both cases you must now change the imports to our new directives:
|
|
|
75
75
|
import { unsafeHTML } from '@webtides/element-js/src/dom-parts/directives';
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
### element-js@1.1+
|
|
79
|
+
|
|
80
|
+
since 1.1 its also possible to import directives directly from the modules index
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
import {
|
|
84
|
+
classMap,
|
|
85
|
+
styleMap,
|
|
86
|
+
when,
|
|
87
|
+
choose,
|
|
88
|
+
unsafeHTML,
|
|
89
|
+
spreadAttributes,
|
|
90
|
+
optionalAttribute
|
|
91
|
+
} from '@webtides/element-js';
|
|
92
|
+
```
|
|
93
|
+
|
|
78
94
|
## Removed childListUpdate option from constructor options
|
|
79
95
|
|
|
80
96
|
We removed the deprecated `BaseElement` constructor option `childListUpdate`. Please use the "mutationObserverOptions" dictionary instead.
|
package/docs/guide/guide.md
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
## Guides/Tooling
|
|
3
2
|
|
|
4
3
|
### Frontend Stack
|
|
@@ -13,10 +12,9 @@ This is an element style guide created and enforced for the purpose of standardi
|
|
|
13
12
|
|
|
14
13
|
#### File structure
|
|
15
14
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
- Implementation (.js) and styles (.css) of an element should live in the same directory.
|
|
15
|
+
- One element per file.
|
|
16
|
+
- One element per directory. Though it may make sense to group similar elements into the same directory, we've found it' s easier to consume and document elements when each one has its own directory.
|
|
17
|
+
- Implementation (.js) and styles (.css) of an element should live in the same directory.
|
|
20
18
|
|
|
21
19
|
Example:
|
|
22
20
|
|
|
@@ -39,16 +37,12 @@ Example:
|
|
|
39
37
|
|
|
40
38
|
##### Name
|
|
41
39
|
|
|
42
|
-
Elements are not actions, they are conceptually "things". It is better to use nouns, instead of verbs, such us: "
|
|
43
|
-
animation" instead of "animating". "input", "tab", "nav", "menu" are some examples.
|
|
40
|
+
Elements are not actions, they are conceptually "things". It is better to use nouns, instead of verbs, such us: " animation" instead of "animating". "input", "tab", "nav", "menu" are some examples.
|
|
44
41
|
|
|
45
42
|
##### -element postfix
|
|
46
43
|
|
|
47
|
-
The naming has a major role when you are creating a collection of elements intended to be used across different
|
|
48
|
-
|
|
49
|
-
is needed to prevent collisions.
|
|
50
|
-
Additionally, web components are required to contain a "-" dash within the tag name. When using the first section to
|
|
51
|
-
namespace your components - everything will look the same, and it will be hard to distinguish elements.
|
|
44
|
+
The naming has a major role when you are creating a collection of elements intended to be used across different projects. Web Components are not scoped because they are globally declared within the page, which means a "unique" name is needed to prevent collisions.
|
|
45
|
+
Additionally, web components are required to contain a "-" dash within the tag name. When using the first section to namespace your components - everything will look the same, and it will be hard to distinguish elements.
|
|
52
46
|
|
|
53
47
|
DO NOT do this:
|
|
54
48
|
|
|
@@ -72,8 +66,7 @@ card-element
|
|
|
72
66
|
|
|
73
67
|
##### Modifiers
|
|
74
68
|
|
|
75
|
-
When several elements are related and/or coupled, it is a good idea to share the name, and then add different modifiers,
|
|
76
|
-
for example:
|
|
69
|
+
When several elements are related and/or coupled, it is a good idea to share the name, and then add different modifiers, for example:
|
|
77
70
|
|
|
78
71
|
```
|
|
79
72
|
menu-element
|
|
@@ -91,13 +84,11 @@ card-content
|
|
|
91
84
|
The name of the ES6 class of the element should reflect the file name, and the html tag name.
|
|
92
85
|
|
|
93
86
|
```js
|
|
94
|
-
export class ButtonElement extends BaseElement {
|
|
95
|
-
}
|
|
87
|
+
export class ButtonElement extends BaseElement {}
|
|
96
88
|
|
|
97
89
|
customElements.define('button-element', ButtonElement);
|
|
98
90
|
|
|
99
|
-
export class MenuElement extends BaseElement {
|
|
100
|
-
}
|
|
91
|
+
export class MenuElement extends BaseElement {}
|
|
101
92
|
|
|
102
93
|
customElements.define('menu-element', MenuElement);
|
|
103
94
|
```
|
|
@@ -106,7 +97,4 @@ customElements.define('menu-element', MenuElement);
|
|
|
106
97
|
|
|
107
98
|
##### Newspaper Metaphor from The Robert C. Martin's _Clean Code_
|
|
108
99
|
|
|
109
|
-
> The source file should be organized like a newspaper article, with the highest level summary at the top, and more and
|
|
110
|
-
> more details further down. Functions called from the top function come directly below it, and so on down to the lowest
|
|
111
|
-
> level, and most detailed functions at the bottom. This is a good way to organize the source code, even though IDE:s make
|
|
112
|
-
> the location of functions less important, since it is so easy to navigate in and out of them.
|
|
100
|
+
> The source file should be organized like a newspaper article, with the highest level summary at the top, and more and more details further down. Functions called from the top function come directly below it, and so on down to the lowest level, and most detailed functions at the bottom. This is a good way to organize the source code, even though IDE:s make the location of functions less important, since it is so easy to navigate in and out of them.
|