@webtides/element-js 1.0.0 → 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.
Files changed (41) hide show
  1. package/CHANGELOG.md +140 -90
  2. package/README.md +3 -3
  3. package/docs/README.md +70 -89
  4. package/docs/concepts/classes.md +37 -66
  5. package/docs/concepts/hierarchy.md +29 -31
  6. package/docs/concepts/lifecycle.md +9 -23
  7. package/docs/concepts/reactivity.md +1 -3
  8. package/docs/features/computed-properties.md +13 -16
  9. package/docs/features/dependency-injection-context-protocol.md +75 -87
  10. package/docs/features/global-styles.md +1 -1
  11. package/docs/features/methods.md +14 -15
  12. package/docs/features/properties.md +21 -33
  13. package/docs/features/refs.md +23 -31
  14. package/docs/features/renderless.md +31 -37
  15. package/docs/features/store.md +63 -78
  16. package/docs/features/templates.md +4 -1
  17. package/docs/features/watch.md +28 -35
  18. package/docs/getting-started/upgrade-to-v1.md +16 -0
  19. package/docs/guide/guide.md +10 -22
  20. package/index.js +29 -2
  21. package/package.json +62 -62
  22. package/src/BaseElement.js +631 -631
  23. package/src/StyledElement.js +104 -104
  24. package/src/TemplateElement.js +53 -53
  25. package/src/dom-parts/AttributePart.js +116 -116
  26. package/src/dom-parts/ChildNodePart.js +288 -288
  27. package/src/dom-parts/NodePart.js +27 -27
  28. package/src/dom-parts/Part.js +6 -6
  29. package/src/dom-parts/TemplatePart.js +119 -119
  30. package/src/dom-parts/TemplateResult.js +270 -270
  31. package/src/dom-parts/directives.js +105 -48
  32. package/src/dom-parts/html.js +1 -1
  33. package/src/dom-parts/render.js +6 -6
  34. package/src/util/AttributeParser.js +47 -47
  35. package/src/util/DOMHelper.js +190 -190
  36. package/src/util/Directive.js +31 -31
  37. package/src/util/GlobalStylesStore.js +56 -53
  38. package/src/util/SerializeStateHelper.js +56 -56
  39. package/src/util/Store.js +174 -174
  40. package/src/util/defineElement.js +5 -5
  41. package/src/util/toString.js +5 -5
@@ -3,130 +3,118 @@
3
3
  _element-js_ provides a simple way of Dependency Injection following the idea of the Context [Protocol Proposal](https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md).
4
4
 
5
5
  #### Problem "Prop Drilling"
6
-
6
+
7
7
  If Data has to be passed down the DOM Tree to descending Child Elements it is usually done by assigning data to the descendants attributes:
8
8
 
9
9
  ```js
10
10
  export class PropDrilling extends TemplateElement {
11
- properties() {
12
- return {
13
- data: {some: "data", which: "bloats", the: "attributes", in: "DOM" }
14
- };
15
- }
16
-
17
- template() {
18
- return html`
19
- <!-- child element uses the same exacte data-set as the parent does -->
20
- <child-element redundant-data="${data}">
21
- <!-- grand child component uses parts of the parents or even the root elements dataset -->
22
- <grand-child-element in-data="${data.in}"></grand-child-element>
23
- </child-element>
24
- <!-- child component uses parts of the parents dataset -->
25
- <another-child-element some-data="${data.some}" the-data="${data.the}"></another-child-element>
26
- `;
27
- }
11
+ properties() {
12
+ return {
13
+ data: { some: 'data', which: 'bloats', the: 'attributes', in: 'DOM' }
14
+ };
15
+ }
16
+
17
+ template() {
18
+ return html`
19
+ <!-- child element uses the same exacte data-set as the parent does -->
20
+ <child-element redundant-data="${data}">
21
+ <!-- grand child component uses parts of the parents or even the root elements dataset -->
22
+ <grand-child-element in-data="${data.in}"></grand-child-element>
23
+ </child-element>
24
+ <!-- child component uses parts of the parents dataset -->
25
+ <another-child-element some-data="${data.some}" the-data="${data.the}"></another-child-element>
26
+ `;
27
+ }
28
28
  }
29
29
  ```
30
30
 
31
- While this is possible and totally fine for primitive values it can get confusing very fast with a deep nesting and complex data structures.
32
- It also bloats up the DOM Tree as redundant data ist being passed to multiple descending Elements being copied along the way.
31
+ While this is possible and totally fine for primitive values it can get confusing very fast with a deep nesting and complex data structures. It also bloats up the DOM Tree as redundant data ist being passed to multiple descending Elements being copied along the way.
32
+
33
+ #### Dependency Injection for the rescue
33
34
 
34
- #### Dependency Injection for the rescue
35
-
36
35
  To address the issues caused by "Prop Drilling" _element-js_ provides a simple Dependency Injection Implementation following the idea of the Context Protocol mentioned above.
37
36
 
38
37
  ##### Provide via provideProperties()
39
38
 
40
- An Element can control whatever is to be "provided" to requesting Elements by implementing the provideProperties function provided by the `BaseElement`.
39
+ An Element can control whatever is to be "provided" to requesting Elements by implementing the provideProperties function provided by the `BaseElement`.
41
40
 
42
41
  ```js
43
-
44
42
  export class AncestorElement extends TemplateElement {
45
-
46
- properties() {
47
- return {
48
- data: {some: "data", which: "bloats", the: "attributes", in: "DOM" },
49
- };
50
- }
51
- provideProperties() {
52
- return {
53
- data: this.data,
54
- in: this.data.in,
55
- static: 'string',
56
- };
57
- }
43
+ properties() {
44
+ return {
45
+ data: { some: 'data', which: 'bloats', the: 'attributes', in: 'DOM' }
46
+ };
47
+ }
48
+ provideProperties() {
49
+ return {
50
+ data: this.data,
51
+ in: this.data.in,
52
+ static: 'string'
53
+ };
54
+ }
58
55
  }
59
56
  ```
57
+
60
58
  #### Vanilla JS Provide via Event Listener / Context Protocol
61
59
 
62
60
  ```html
63
61
  <script>
64
- document.addEventListener('request-context', (event) => {
65
- // global value
66
- const vanillaValue = 'vanillaValue';
67
- for (const [key, value] of Object.entries(event.detail)) {
68
- if(key === 'vanillaContext') {
69
- if (typeof value === 'function') {
70
- // call function with context value
71
- value(vanillaValue);
72
- } else {
73
- // assign as a property to Requestee
74
- event.target[key] = vanillaValue;
75
- }
76
- }
77
- }
78
- };
62
+ document.addEventListener('request-context', (event) => {
63
+ // global value
64
+ const vanillaValue = 'vanillaValue';
65
+ for (const [key, value] of Object.entries(event.detail)) {
66
+ if(key === 'vanillaContext') {
67
+ if (typeof value === 'function') {
68
+ // call function with context value
69
+ value(vanillaValue);
70
+ } else {
71
+ // assign as a property to Requestee
72
+ event.target[key] = vanillaValue;
73
+ }
74
+ }
75
+ }
76
+ };
79
77
  </script>
80
78
  ```
81
79
 
82
-
83
-
84
80
  ##### Inject Properties via injectProperties()
85
81
 
86
- An Element can define injection requests by implementing the injectProperties function provided by the `BaseElement`.
87
- The initial values will also function as default values and will be replaced after the injection request is fulfilled by an Ancestor that provides a property with the requested name.
88
- Injected properties will automatically become reactive properties within the elements scope. DEVs do not have to care about where the Value is actually coming from.
89
- It will be just there at some Point after the Injection will trigger changes / updates just like regular [properties](./properties.md)..
82
+ An Element can define injection requests by implementing the injectProperties function provided by the `BaseElement`. The initial values will also function as default values and will be replaced after the injection request is fulfilled by an Ancestor that provides a property with the requested name. Injected properties will automatically become reactive properties within the elements scope. DEVs do not have to care about where the Value is actually coming from. It will be just there at some Point after the Injection will trigger changes / updates just like regular [properties](./properties.md)..
90
83
 
91
84
  ```js
92
85
  export class DescendingElement extends TemplateElement {
93
-
94
- injectProperties() {
95
- return {
96
- in: '',
97
- data: {},
98
- static: '',
99
- vanillaContext: '',
100
- };
101
- }
86
+ injectProperties() {
87
+ return {
88
+ in: '',
89
+ data: {},
90
+ static: '',
91
+ vanillaContext: ''
92
+ };
93
+ }
102
94
  }
103
-
104
95
  ```
105
96
 
106
97
  ##### Request Context Values via manual context Request
107
98
 
108
- If you want more control over what happens with an injected value after the Injection you can do a manual context Request by using the builtin requestContext function of the BaseElement.
99
+ If you want more control over what happens with an injected value after the Injection you can do a manual context Request by using the builtin requestContext function of the BaseElement.
100
+
109
101
  ```js
110
102
  export class ManualRequestElement extends TemplateElement {
111
- properties() {
112
- return {
113
- reversedArray: [],
114
- };
115
- }
116
- connected() {
117
- this.requestContext('regularArray', (injectedArray) => {
118
- // do whatever you like with the value
119
- const processedArray = injectedArray.reverse();
120
- // OPTIONAL assign to a property to trigger an update and render the reversed values´
121
- this.reversedArray = processedArray;
122
- });
123
- }
103
+ properties() {
104
+ return {
105
+ reversedArray: []
106
+ };
107
+ }
108
+ connected() {
109
+ this.requestContext('regularArray', (injectedArray) => {
110
+ // do whatever you like with the value
111
+ const processedArray = injectedArray.reverse();
112
+ // OPTIONAL assign to a property to trigger an update and render the reversed values´
113
+ this.reversedArray = processedArray;
114
+ });
115
+ }
124
116
  }
125
117
  ```
126
118
 
127
- Still looking for more Information ?
119
+ Still looking for more Information ?
128
120
  You might take a look at the [Test Implementation](../../test/unit/context-protocol.test.html) which should cover all cases
129
-
130
-
131
-
132
-
@@ -117,7 +117,7 @@ export class ShadowElement extends TemplateElement {
117
117
 
118
118
  By default, _element-js_ will only apply all style elements from the global document that are present in the initial HTML. It will not observe the document for any changes regarding style elements.
119
119
 
120
- > Observing global styles is done via a MutationObserver on the whole document. This might impose performance issues.
120
+ > Observing global styles is done via a MutationObserver on the whole document. This might impose performance issues.
121
121
 
122
122
  To enable global style observation you can add the following (inline, sync) script block to yor HTML.
123
123
 
@@ -1,33 +1,32 @@
1
-
2
1
  ### Methods
3
2
 
4
3
  Besides public properties you can also shape the API for your element with public methods.
5
4
 
6
5
  ```javascript
7
- import {BaseElement, defineElement} from '@webtides/element-js';
6
+ import { BaseElement, defineElement } from '@webtides/element-js';
8
7
 
9
8
  export class ModalElement extends BaseElement {
10
- open() {
11
- // do the things to actually open the modal…
12
- }
9
+ open() {
10
+ // do the things to actually open the modal…
11
+ }
13
12
  }
14
13
 
15
14
  defineElement('modal-element', ModalElement);
16
15
  ```
17
16
 
18
17
  ```javascript
19
- import {BaseElement, defineElement} from '@webtides/element-js';
18
+ import { BaseElement, defineElement } from '@webtides/element-js';
20
19
 
21
20
  export class OtherElement extends BaseElement {
22
- events() {
23
- return {
24
- this: {
25
- click: () => {
26
- this.$refs.modal.open();
27
- }
28
- }
29
- };
30
- }
21
+ events() {
22
+ return {
23
+ this: {
24
+ click: () => {
25
+ this.$refs.modal.open();
26
+ }
27
+ }
28
+ };
29
+ }
31
30
  }
32
31
 
33
32
  defineElement('other-element', OtherElement);
@@ -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
- _privatePropery = 'I am private (by convention)';
9
- publicProperty = 'I am public';
10
-
11
- properties() {
12
- return {
13
- reactivePublicProperty: 'I am public & reactive'
14
- };
15
- }
16
-
17
- log() {
18
- console.log(this._privatePropery);
19
- console.log(this.publicProperty);
20
- console.log(this.reactivePublicProperty);
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).
@@ -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
- <input type="text" ref="input"/>
15
- <places-list ref="list">
16
- <list-entry ref="entries[]">A</list-entry>
17
- <list-entry ref="entries[]">B</list-entry>
18
- <list-entry ref="entries[]">C</list-entry>
19
- </places-list>
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
- events() {
28
- return {
29
- input: {
30
- blur: (e) => {
31
- // fetch places...
32
- const places = [];
33
- // update singular ref
34
- this.$refs.list = places;
35
- // do something with a list reference
36
- this.$refs.entries.forEach(entry => console.log(entry.innerText)); // A , B , C
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
- events() {
23
- return {
24
- input: {
25
- blur: (e) => {
26
- // fetch places...
27
- const places = ['list of place objects…'];
28
- this.$refs.list = places;
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
- <button>Options</button>
46
- <ul ref="“list”" class="“”hidden">
47
- <li>Profile</li>
48
- <li>Settings</li>
49
- <li>Sign out</li>
50
- </ul>
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
- events() {
59
- return {
60
- button: {
61
- click: () => {
62
- this.$refs.list.classList.remove('hidden');
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);