mi-element 0.6.7 → 0.7.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/README.md +11 -9
- package/dist/context.js +6 -0
- package/dist/element.js +92 -109
- package/dist/escape.js +12 -5
- package/dist/index.js +5 -5
- package/dist/refs.js +1 -9
- package/dist/styling.js +12 -5
- package/docs/context.md +43 -27
- package/docs/controller.md +4 -0
- package/docs/element.md +47 -60
- package/docs/reactivity.md +2 -2
- package/docs/signal.md +13 -6
- package/docs/store.md +1 -0
- package/docs/styling.md +17 -5
- package/package.json +16 -20
- package/src/context.js +8 -0
- package/src/element.js +203 -197
- package/src/escape.js +17 -13
- package/src/index.js +4 -14
- package/src/refs.js +0 -24
- package/src/styling.js +19 -9
- package/types/context.d.ts +2 -0
- package/types/element.d.ts +53 -34
- package/types/escape.d.ts +1 -1
- package/types/index.d.ts +4 -8
- package/types/refs.d.ts +0 -11
- package/types/styling.d.ts +1 -3
- package/dist/index.min.js +0 -2
- package/dist/index.min.js.map +0 -1
- package/src/min.js +0 -17
- package/types/min.d.ts +0 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ setting objects or functions either through `el.setAttribute(name, value)` or
|
|
|
24
24
|
properties `el[name] = value`.
|
|
25
25
|
|
|
26
26
|
Furthermore all observed attributes have a reactive behavior through the use of
|
|
27
|
-
signals and effects (loosely) following the
|
|
27
|
+
signals and effects (loosely) following the
|
|
28
28
|
[TC39 JavaScript Signals standard proposal][].
|
|
29
29
|
|
|
30
30
|
# Usage
|
|
@@ -38,7 +38,8 @@ npm i mi-element
|
|
|
38
38
|
```js
|
|
39
39
|
/** @file ./mi-counter.js */
|
|
40
40
|
|
|
41
|
-
import { MiElement, define,
|
|
41
|
+
import { MiElement, define, Signal } from 'mi-element'
|
|
42
|
+
const { effect, createSignal } = Signal
|
|
42
43
|
|
|
43
44
|
// define your Component
|
|
44
45
|
class MiCounter extends MiElement {
|
|
@@ -46,25 +47,27 @@ class MiCounter extends MiElement {
|
|
|
46
47
|
<style>
|
|
47
48
|
:host { font-size: 1.25rem; }
|
|
48
49
|
</style>
|
|
49
|
-
<div
|
|
50
|
-
<button
|
|
50
|
+
<div aria-label="Counter value">0</div>
|
|
51
|
+
<button aria-label="Increment counter"> + </button>
|
|
51
52
|
`
|
|
52
53
|
|
|
53
|
-
static get
|
|
54
|
+
static get properties() {
|
|
54
55
|
// declare reactive attribute(s)
|
|
55
|
-
return { count: 0 }
|
|
56
|
+
return { count: { type: Number, initial: 0 } }
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
static createSignal = createSignal
|
|
60
|
+
|
|
58
61
|
// called by connectedCallback()
|
|
59
62
|
render() {
|
|
60
63
|
// gather refs from template (here by id)
|
|
61
|
-
this.refs =
|
|
64
|
+
this.refs = this.refsBySelector({ increment: 'button', div: 'div' })
|
|
62
65
|
// apply event listeners
|
|
63
66
|
this.refs.increment.addEventListener('click', () => {
|
|
64
67
|
// change observed and reactive attribute...
|
|
65
68
|
this.count++
|
|
66
69
|
})
|
|
67
|
-
|
|
70
|
+
effect(() => {
|
|
68
71
|
// ...triggers update on every change of `this.count`
|
|
69
72
|
this.refs.div.textContent = this.count
|
|
70
73
|
})
|
|
@@ -114,7 +117,6 @@ MIT licensed
|
|
|
114
117
|
[Web Components]: https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks
|
|
115
118
|
[TC39 JavaScript Signals standard proposal]: https://github.com/tc39/proposal-signals
|
|
116
119
|
[krausest/js-framework-benchmark]: https://github.com/krausest/js-framework-benchmark
|
|
117
|
-
|
|
118
120
|
[npm-badge]: https://badgen.net/npm/v/mi-element
|
|
119
121
|
[npm]: https://www.npmjs.com/package/mi-element
|
|
120
122
|
[types-badge]: https://badgen.net/npm/types/mi-element
|
package/dist/context.js
CHANGED
|
@@ -17,6 +17,12 @@ class ContextProvider {
|
|
|
17
17
|
get() {
|
|
18
18
|
return this.state.get();
|
|
19
19
|
}
|
|
20
|
+
set value(newValue) {
|
|
21
|
+
this.set(newValue);
|
|
22
|
+
}
|
|
23
|
+
get value() {
|
|
24
|
+
return this.get();
|
|
25
|
+
}
|
|
20
26
|
onContextRequest=ev => {
|
|
21
27
|
if (ev.context !== this.context) return;
|
|
22
28
|
let unsubscribe;
|
package/dist/element.js
CHANGED
|
@@ -1,86 +1,80 @@
|
|
|
1
|
-
import { camelToKebabCase } from './case.js';
|
|
1
|
+
import { kebabToCamelCase, camelToKebabCase } from './case.js';
|
|
2
|
+
|
|
3
|
+
import { addGlobalStyles } from './styling.js';
|
|
4
|
+
|
|
5
|
+
import { refsBySelector } from './refs.js';
|
|
2
6
|
|
|
3
7
|
import { createSignal } from 'mi-signal';
|
|
4
8
|
|
|
9
|
+
const nameMap = {
|
|
10
|
+
class: 'className',
|
|
11
|
+
for: 'htmlFor'
|
|
12
|
+
};
|
|
13
|
+
|
|
5
14
|
class MiElement extends HTMLElement {
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
#types=new Map;
|
|
15
|
+
_props={};
|
|
16
|
+
#changedProps={};
|
|
9
17
|
#disposers=new Set;
|
|
10
18
|
#controllers=new Set;
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
static template;
|
|
17
|
-
static get attributes() {
|
|
18
|
-
return {};
|
|
19
|
+
#updateRequested=!1;
|
|
20
|
+
static get shadowRootInit() {
|
|
21
|
+
return {
|
|
22
|
+
mode: 'open'
|
|
23
|
+
};
|
|
19
24
|
}
|
|
20
|
-
static
|
|
21
|
-
|
|
25
|
+
static template;
|
|
26
|
+
static get properties() {}
|
|
27
|
+
static observedAttributes=[];
|
|
28
|
+
static styles='';
|
|
29
|
+
static get useGlobalStyles() {
|
|
30
|
+
return !1;
|
|
22
31
|
}
|
|
32
|
+
static createSignal=createSignal;
|
|
23
33
|
constructor() {
|
|
24
|
-
super()
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
for (const [name, value] of Object.entries(attributes)) {
|
|
41
|
-
const initial = initialValueType(value);
|
|
42
|
-
this.#types.set(name, initial.type), this.#attrLc.set(name.toLowerCase(), name),
|
|
43
|
-
this.#attrLc.set(camelToKebabCase(name), name), this.#observe(name, initial.value);
|
|
34
|
+
super();
|
|
35
|
+
const {createSignal: createSignal, properties: properties} = this.constructor;
|
|
36
|
+
for (const [name, {initial: initial}] of Object.entries(properties)) {
|
|
37
|
+
const descriptor = Object.getOwnPropertyDescriptor(this.constructor.prototype, name);
|
|
38
|
+
createSignal && (this._props[name] = createSignal()), Object.defineProperty(this, name, {
|
|
39
|
+
get() {
|
|
40
|
+
return descriptor?.get ? descriptor.get.call(this) : createSignal ? this._props[name].value : this._props[name];
|
|
41
|
+
},
|
|
42
|
+
set(value) {
|
|
43
|
+
const oldValue = this[name];
|
|
44
|
+
descriptor?.set ? descriptor.set.call(this, value) : createSignal ? this._props[name].value = value : this._props[name] = value,
|
|
45
|
+
oldValue !== this[name] && this.requestUpdate({
|
|
46
|
+
[name]: value
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}), this[name] = initial;
|
|
44
50
|
}
|
|
45
51
|
}
|
|
46
|
-
#observedProperties(properties = {}) {
|
|
47
|
-
for (const [name, value] of Object.entries(properties)) this.#attrLc.has(name) || name in this.#attr || this.#observe(name, value);
|
|
48
|
-
}
|
|
49
|
-
#getName(name) {
|
|
50
|
-
return this.#attrLc.get(name) || name;
|
|
51
|
-
}
|
|
52
|
-
#getType(name) {
|
|
53
|
-
return this.#types.get(name);
|
|
54
|
-
}
|
|
55
52
|
connectedCallback() {
|
|
56
53
|
this.#controllers.forEach(controller => controller.hostConnected?.());
|
|
57
|
-
const {
|
|
58
|
-
this.renderRoot =
|
|
59
|
-
this.addTemplate(template), this.
|
|
54
|
+
const {shadowRootInit: shadowRootInit, useGlobalStyles: useGlobalStyles, template: template} = this.constructor;
|
|
55
|
+
this.renderRoot = shadowRootInit ? this.shadowRoot ?? this.attachShadow(shadowRootInit) : this,
|
|
56
|
+
this.addTemplate(template), useGlobalStyles && addGlobalStyles(this.renderRoot),
|
|
57
|
+
this.render(), this.requestUpdate();
|
|
60
58
|
}
|
|
61
59
|
disconnectedCallback() {
|
|
62
60
|
this.#disposers.forEach(remover => remover()), this.#controllers.forEach(controller => controller.hostDisconnected?.());
|
|
63
61
|
}
|
|
64
|
-
attributeChangedCallback(name,
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
!this.#dedupe && this.isConnected && (this.#dedupe = !0, requestAnimationFrame(() => {
|
|
81
|
-
this.#dedupe = !1;
|
|
82
|
-
const _changedAttributes = this.#changedAttr;
|
|
83
|
-
this.#changedAttr = {}, this.shouldUpdate(_changedAttributes) && this.update(_changedAttributes);
|
|
62
|
+
attributeChangedCallback(name, _oldValue, newValue) {
|
|
63
|
+
const camelName = nameMap[name] ?? kebabToCamelCase(name), properties = this.constructor?.properties, {type: type} = properties?.[camelName] ?? {}, coercedValue = convertType(newValue, type);
|
|
64
|
+
if (name.startsWith('data-')) {
|
|
65
|
+
const datasetName = kebabToCamelCase(name.substring(5));
|
|
66
|
+
datasetName && (this.dataset[datasetName] = coercedValue);
|
|
67
|
+
}
|
|
68
|
+
this[camelName] = coercedValue;
|
|
69
|
+
}
|
|
70
|
+
requestUpdate(changedProps) {
|
|
71
|
+
this.#changedProps = {
|
|
72
|
+
...this.#changedProps,
|
|
73
|
+
...changedProps
|
|
74
|
+
}, !this.#updateRequested && this.renderRoot && (this.#updateRequested = !0, window.requestAnimationFrame(() => {
|
|
75
|
+
this.#updateRequested = !1;
|
|
76
|
+
const changedProps = this.#changedProps;
|
|
77
|
+
this.#changedProps = {}, this.update(changedProps);
|
|
84
78
|
}));
|
|
85
79
|
}
|
|
86
80
|
addTemplate(template) {
|
|
@@ -90,7 +84,7 @@ class MiElement extends HTMLElement {
|
|
|
90
84
|
}
|
|
91
85
|
}
|
|
92
86
|
render() {}
|
|
93
|
-
update(
|
|
87
|
+
update(_changedProps) {}
|
|
94
88
|
on(eventName, listener, node = this) {
|
|
95
89
|
node.addEventListener(eventName, listener), this.#disposers.add(() => node.removeEventListener(eventName, listener));
|
|
96
90
|
}
|
|
@@ -111,53 +105,42 @@ class MiElement extends HTMLElement {
|
|
|
111
105
|
removeController(controller) {
|
|
112
106
|
this.#controllers.delete(controller);
|
|
113
107
|
}
|
|
108
|
+
refsBySelector(selectors) {
|
|
109
|
+
return refsBySelector(this.renderRoot, selectors);
|
|
110
|
+
}
|
|
114
111
|
}
|
|
115
112
|
|
|
116
|
-
const define = (
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
const define = (tagName, elementClass, options) => {
|
|
114
|
+
if (customElements.get(tagName)) return;
|
|
115
|
+
const {usedCssPrefix: usedCssPrefix = "", cssPrefix: cssPrefix = "", styles: styles} = options || {};
|
|
116
|
+
if (elementClass.properties) {
|
|
117
|
+
const observedAttrs = [];
|
|
118
|
+
for (const [name, {attribute: attribute = !0}] of Object.entries(elementClass.properties)) attribute && observedAttrs.push(camelToKebabCase(name));
|
|
119
|
+
Object.defineProperty(elementClass, 'observedAttributes', {
|
|
120
|
+
get: () => observedAttrs
|
|
121
|
+
});
|
|
122
|
+
} else if (elementClass.observedAttributes) {
|
|
123
|
+
const properties = elementClass.observedAttributes.reduce((acc, attr) => (acc[kebabToCamelCase(attr)] = {},
|
|
124
|
+
acc), {});
|
|
125
|
+
Object.defineProperty(elementClass, 'properties', {
|
|
126
|
+
get: () => properties
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
elementClass.styles && (elementClass.styles = styles || (usedCssPrefix === cssPrefix ? elementClass.styles : elementClass.styles.replaceAll(`--${usedCssPrefix}-`, cssPrefix))),
|
|
130
|
+
renderTemplate(elementClass), window.customElements.define(tagName, elementClass);
|
|
119
131
|
}, renderTemplate = element => {
|
|
120
132
|
if (element.template instanceof HTMLTemplateElement) return;
|
|
121
133
|
const el = document.createElement('template');
|
|
122
134
|
el.innerHTML = element.template, element.template = el;
|
|
123
|
-
},
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
value: void 0,
|
|
134
|
-
type: 'Number'
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
case String:
|
|
138
|
-
return {
|
|
139
|
-
value: void 0,
|
|
140
|
-
type: 'String'
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
default:
|
|
144
|
-
return {
|
|
145
|
-
value: value,
|
|
146
|
-
type: toString.call(value).slice(8, -1)
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
}, convertType = (any, type) => {
|
|
150
|
-
switch (type) {
|
|
151
|
-
case 'Number':
|
|
152
|
-
return (any => {
|
|
153
|
-
const n = Number(any);
|
|
154
|
-
return isNaN(n) ? any : n;
|
|
155
|
-
})(any);
|
|
156
|
-
|
|
157
|
-
case 'Boolean':
|
|
158
|
-
return 'false' !== any && ('' === any || !!any);
|
|
159
|
-
}
|
|
160
|
-
return any;
|
|
161
|
-
};
|
|
135
|
+
}, toJson = any => {
|
|
136
|
+
try {
|
|
137
|
+
return JSON.parse(any);
|
|
138
|
+
} catch {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}, convertType = (value, type) => type === Boolean ? null !== value : type === Number ? (any => {
|
|
142
|
+
const n = Number(any);
|
|
143
|
+
return isNaN(n) ? 0 : n;
|
|
144
|
+
})(value) : type === Array ? toJson(value) ?? value.split(',').map(v => v.trim()) : type === Object ? toJson(value) : value;
|
|
162
145
|
|
|
163
146
|
export { MiElement, convertType, define };
|
package/dist/escape.js
CHANGED
|
@@ -3,11 +3,18 @@ class UnsafeHtml extends String {}
|
|
|
3
3
|
const unsafeHtml = str => new UnsafeHtml(str), escMap = {
|
|
4
4
|
'&': '&',
|
|
5
5
|
'<': '<',
|
|
6
|
-
'>': '>'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
'>': '>'
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
let esc = string => string.replace(/&/g, '&').replace(/[&<>]/g, tag => escMap[tag]);
|
|
10
|
+
|
|
11
|
+
'undefined' != typeof document && (esc = string => {
|
|
12
|
+
const div = document.createElement('div');
|
|
13
|
+
return div.textContent = string, div.innerHTML;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const escHtml = string => string instanceof UnsafeHtml ? string : unsafeHtml(esc('' + string)), html = (strings, ...values) => unsafeHtml(String.raw({
|
|
10
17
|
raw: strings
|
|
11
18
|
}, ...values.map(val => Array.isArray(val) ? val.map(escHtml).join('') : escHtml(val))));
|
|
12
19
|
|
|
13
|
-
export {
|
|
20
|
+
export { escHtml, html, unsafeHtml };
|
package/dist/index.js
CHANGED
|
@@ -2,12 +2,12 @@ export { ContextConsumer, ContextProvider, ContextRequestEvent } from './context
|
|
|
2
2
|
|
|
3
3
|
export { MiElement, convertType, define } from './element.js';
|
|
4
4
|
|
|
5
|
-
export {
|
|
5
|
+
export { escHtml, html, unsafeHtml } from './escape.js';
|
|
6
6
|
|
|
7
|
-
export {
|
|
8
|
-
|
|
9
|
-
export { Computed, default as Signal, State, createSignal, effect } from 'mi-signal';
|
|
7
|
+
export { refsBySelector } from './refs.js';
|
|
10
8
|
|
|
11
9
|
export { Store } from './store.js';
|
|
12
10
|
|
|
13
|
-
export { addGlobalStyles,
|
|
11
|
+
export { addGlobalStyles, classNames, css, styleMap } from './styling.js';
|
|
12
|
+
|
|
13
|
+
export { default as Signal } from 'mi-signal';
|
package/dist/refs.js
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import { kebabToCamelCase } from './case.js';
|
|
2
|
-
|
|
3
|
-
function refsById(container) {
|
|
4
|
-
const nodes = container.querySelectorAll?.('[id]') || [], found = {};
|
|
5
|
-
for (const node of nodes) found[kebabToCamelCase(node.getAttribute('id') || node.nodeName.toLowerCase())] = node;
|
|
6
|
-
return found;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
1
|
function refsBySelector(container, selectors) {
|
|
10
2
|
const found = {};
|
|
11
3
|
for (const [name, selector] of Object.entries(selectors)) found[name] = container.querySelector?.(selector);
|
|
12
4
|
return found;
|
|
13
5
|
}
|
|
14
6
|
|
|
15
|
-
export {
|
|
7
|
+
export { refsBySelector };
|
package/dist/styling.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { camelToKebabCase } from './case.js';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const classNames = (...args) => {
|
|
4
|
+
const classList = [];
|
|
5
|
+
return args.forEach(arg => {
|
|
6
|
+
arg && ('string' == typeof arg ? classList.push(arg) : Array.isArray(arg) ? classList.push(classNames(...arg)) : 'object' == typeof arg && Object.entries(arg).forEach(([key, value]) => {
|
|
7
|
+
value && classList.push(key);
|
|
8
|
+
}));
|
|
9
|
+
}), classList.join(' ');
|
|
7
10
|
}, styleMap = (map, options) => {
|
|
8
11
|
const {unit: unit = "px"} = options || {}, acc = [];
|
|
9
12
|
for (const [name, value] of Object.entries(map ?? {})) {
|
|
@@ -23,4 +26,8 @@ function addGlobalStyles(renderRoot) {
|
|
|
23
26
|
})), globalSheets));
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
const css = (strings, ...values) => String.raw({
|
|
30
|
+
raw: strings
|
|
31
|
+
}, ...values);
|
|
32
|
+
|
|
33
|
+
export { addGlobalStyles, classNames, css, styleMap };
|
package/docs/context.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- !toc (minlevel=2) -->
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
- [ContextProvider](#contextprovider)
|
|
6
|
+
- [ContextConsumer](#contextconsumer)
|
|
7
|
+
- [Connecting consumers to providers](#connecting-consumers-to-providers)
|
|
8
8
|
|
|
9
9
|
<!-- toc! -->
|
|
10
10
|
|
|
@@ -17,29 +17,34 @@ Implements the [Context Protocol][].
|
|
|
17
17
|
## ContextProvider
|
|
18
18
|
|
|
19
19
|
```js
|
|
20
|
-
import {
|
|
21
|
-
define,
|
|
22
|
-
MiElement,
|
|
23
|
-
ContextProvider,
|
|
24
|
-
ContextConsumer,
|
|
25
|
-
} from 'mi-element'
|
|
20
|
+
import { define, MiElement, ContextProvider, ContextConsumer } from 'mi-element'
|
|
26
21
|
|
|
27
22
|
define(
|
|
28
23
|
'mi-context-provider',
|
|
29
24
|
class extends MiElement {
|
|
30
|
-
static get
|
|
25
|
+
static get properties () {
|
|
31
26
|
return {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
value: 0
|
|
27
|
+
context: {},
|
|
28
|
+
value: { type: Number }
|
|
35
29
|
}
|
|
36
30
|
}
|
|
37
31
|
|
|
32
|
+
constructor() {
|
|
33
|
+
super()
|
|
34
|
+
// define the context and set initial value
|
|
35
|
+
// default context shall be unique amongst other context providers
|
|
36
|
+
this.context = 'my-context-provider'
|
|
37
|
+
this.value = 0
|
|
38
|
+
}
|
|
39
|
+
|
|
38
40
|
render() {
|
|
39
|
-
this.value = this.initialValue
|
|
40
41
|
this.renderRoot.innerHTML = '<slot></slot>'
|
|
41
42
|
this.provider = new ContextProvider(
|
|
42
|
-
this,
|
|
43
|
+
this,
|
|
44
|
+
this.context,
|
|
45
|
+
this._providerValue()
|
|
46
|
+
)
|
|
47
|
+
this.update()
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
update() {
|
|
@@ -48,14 +53,16 @@ define(
|
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
increment() {
|
|
51
|
-
// value is observed value, requestUpdate() is called on
|
|
56
|
+
// value is observed value, requestUpdate() is called on every change
|
|
52
57
|
this.value++
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
_providerValue() {
|
|
56
61
|
// create a new object on every change and add all shared values and methods
|
|
57
62
|
return {
|
|
58
|
-
value: this.value,
|
|
63
|
+
value: this.value,
|
|
64
|
+
increment: () => this.increment()
|
|
65
|
+
}
|
|
59
66
|
}
|
|
60
67
|
}
|
|
61
68
|
)
|
|
@@ -64,7 +71,7 @@ define(
|
|
|
64
71
|
Works also with HTMLElement. In this case you must provide the necessary wiring.
|
|
65
72
|
|
|
66
73
|
```js
|
|
67
|
-
|
|
74
|
+
customElement.define('html-context-provider', extends class HTMLElement {
|
|
68
75
|
connectedCallback() {
|
|
69
76
|
this.provider = new ContextProvider(this, this.context, this)
|
|
70
77
|
this.provider.hostConnected()
|
|
@@ -85,11 +92,10 @@ customeElement.define('html-context-provider', extends class HTMLElement {
|
|
|
85
92
|
define(
|
|
86
93
|
'mi-context-consumer',
|
|
87
94
|
class extends MiElement {
|
|
88
|
-
static get
|
|
95
|
+
static get properties() {
|
|
89
96
|
return {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
subscribe: true
|
|
97
|
+
context: {},
|
|
98
|
+
subscribe: { type: Boolean }
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -97,14 +103,24 @@ define(
|
|
|
97
103
|
<button id>Increment</button>
|
|
98
104
|
<span id>0</span>`
|
|
99
105
|
|
|
106
|
+
constructor() {
|
|
107
|
+
super()
|
|
108
|
+
// define the context and set initial value
|
|
109
|
+
// default context shall be unique amongst other context providers
|
|
110
|
+
// must match the context-providers context!
|
|
111
|
+
this.context = 'my-context-provider'
|
|
112
|
+
this.subscribe = false
|
|
113
|
+
}
|
|
114
|
+
|
|
100
115
|
render() {
|
|
101
116
|
this.consumer = new ContextConsumer(this, this.context, {
|
|
102
|
-
subscribe:
|
|
117
|
+
subscribe: this.subscribe
|
|
103
118
|
})
|
|
104
119
|
this.refs = refsById(this.renderRoot)
|
|
105
120
|
this.refs.button.addEventListener('click', () => {
|
|
106
121
|
this.consumer.value.increment()
|
|
107
122
|
})
|
|
123
|
+
this.update()
|
|
108
124
|
}
|
|
109
125
|
|
|
110
126
|
update() {
|
|
@@ -120,14 +136,14 @@ define(
|
|
|
120
136
|
<mi-context-provider context="outer">
|
|
121
137
|
<mi-context-provider value="3">
|
|
122
138
|
<div>
|
|
123
|
-
<!-- does not subscribe to any changes
|
|
124
|
-
<mi-context-consumer
|
|
139
|
+
<!-- does not subscribe to any changes -->
|
|
140
|
+
<mi-context-consumer>
|
|
125
141
|
<!-- 3 -->
|
|
126
142
|
</mi-context-consumer>
|
|
127
143
|
</div>
|
|
128
144
|
<div>
|
|
129
|
-
<!-- connects to outer context provider -->
|
|
130
|
-
<mi-context-consumer context="outer">
|
|
145
|
+
<!-- connects to outer context provider and subscribes to changes -->
|
|
146
|
+
<mi-context-consumer context="outer" subscribe>
|
|
131
147
|
<!-- 0 -->
|
|
132
148
|
</mi-context-consumer>
|
|
133
149
|
</div>
|