@webtides/element-js 0.6.2 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -14,6 +14,30 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
14
14
  <!-- ### Changed -->
15
15
  <!-- ### Removed -->
16
16
  <!-- ### Fixed -->
17
+
18
+ ## [0.7.1] - 2023-03-03
19
+
20
+ ### Added
21
+ * Elements are now able to watch Stores
22
+
23
+ ### Fixed
24
+ * Injection of falsy values
25
+
26
+ ## [0.7.0] - 2023-02-13
27
+
28
+ ### Added
29
+ * Dependency Injection via Context Protocol 💉
30
+ * Provide reactive Properties or entire Stores via: provideProperties() { return { name : value}}
31
+ * Inject / Request them via injectProperties() { return { name : 'defaultValue'}}
32
+
33
+ ### Changed
34
+ * docs structure
35
+
36
+ ### Fixed
37
+ * Cannot re-render slotted light dom when using the vanilla renderer https://github.com/webtides/element-js/issues/76
38
+ * adds Vanilla Renderer Tests
39
+
40
+
17
41
  ## [0.6.2] - 2022-11-18
18
42
 
19
43
  ### FIXED
package/dist/index.js CHANGED
@@ -33,6 +33,9 @@ function isNumber(value) {
33
33
  function isNaN(value) {
34
34
  return Number.isNaN(value);
35
35
  }
36
+ function deepEquals(valueA, valueB) {
37
+ return JSON.stringify(valueA) === JSON.stringify(valueB);
38
+ }
36
39
  function parseAttribute(value, options = {}) {
37
40
  if (options.parse === false || !isString(value)) {
38
41
  return value;
@@ -88,13 +91,16 @@ var Store = class {
88
91
  return this._state[key];
89
92
  },
90
93
  set: (newValue) => {
94
+ const oldState = structuredClone(this._state);
91
95
  const oldValue = this._state[key];
92
96
  this._state[key] = newValue;
93
- const watch = this.watch();
94
- if (watch.hasOwnProperty(key) && typeof watch[key] === "function") {
95
- watch[key](newValue, oldValue);
97
+ if (!deepEquals(newValue, oldValue)) {
98
+ const watch = this.watch();
99
+ if (watch.hasOwnProperty(key) && typeof watch[key] === "function") {
100
+ watch[key](newValue, oldValue);
101
+ }
102
+ this.requestUpdate(oldState);
96
103
  }
97
- this.requestUpdate();
98
104
  }
99
105
  });
100
106
  });
@@ -117,8 +123,26 @@ var Store = class {
117
123
  unsubscribe(observer) {
118
124
  this._observer.delete(observer);
119
125
  }
120
- requestUpdate() {
121
- this._observer.forEach((observer) => typeof observer.requestUpdate === "function" ? observer.requestUpdate() : observer());
126
+ requestUpdate(oldState) {
127
+ this._observer.forEach(async (observer) => {
128
+ var _a, _b;
129
+ if (observer instanceof BaseElement) {
130
+ await observer.requestUpdate();
131
+ if (Object.keys((_a = observer.watch()) != null ? _a : {}).length > 0) {
132
+ const properties = observer.properties();
133
+ Object.keys((_b = observer.watch()) != null ? _b : {}).forEach((key) => {
134
+ if (properties[key] === this) {
135
+ observer.callPropertyWatcher(key, this._state, oldState);
136
+ observer.notifyPropertyChange(key, this._state);
137
+ }
138
+ });
139
+ }
140
+ } else if (typeof observer === "function") {
141
+ observer();
142
+ } else {
143
+ console.error("Store: observer type is not supported.", observer);
144
+ }
145
+ });
122
146
  }
123
147
  };
124
148
 
@@ -170,6 +194,7 @@ var BaseElement = class extends HTMLElement {
170
194
  this.defineAttributesAsProperties();
171
195
  this.defineProperties();
172
196
  this.defineComputedProperties();
197
+ this.definePropertyInjection();
173
198
  this.defineObserver();
174
199
  if (this.hasAttribute("defer-update") || this._options.deferUpdate) {
175
200
  this.registerEventsAndRefs();
@@ -203,6 +228,9 @@ var BaseElement = class extends HTMLElement {
203
228
  }
204
229
  disconnectedCallback() {
205
230
  this.removeEvents();
231
+ if (Object.keys(this.provideProperties()).length > 0) {
232
+ this.removeEventListener("request-context", this.onRequestContext);
233
+ }
206
234
  if (this._mutationObserver)
207
235
  this._mutationObserver.disconnect();
208
236
  Object.values(this.properties()).forEach((property) => {
@@ -257,7 +285,7 @@ var BaseElement = class extends HTMLElement {
257
285
  return {};
258
286
  }
259
287
  defineProperties() {
260
- Object.keys(this.properties()).forEach((prop) => {
288
+ Object.keys({...this.injectProperties(), ...this.properties()}).forEach((prop) => {
261
289
  const value = this[prop] || this.properties()[prop];
262
290
  this.defineProperty(prop, value);
263
291
  });
@@ -287,17 +315,12 @@ var BaseElement = class extends HTMLElement {
287
315
  const newValueString = JSON.stringify(newValue);
288
316
  if (JSON.stringify(oldValue) !== newValueString) {
289
317
  this._state[property] = newValue;
318
+ if (newValue instanceof Store) {
319
+ newValue.subscribe(this);
320
+ }
290
321
  if (reflectAttribute || ((_a2 = this._options.propertyOptions[property]) == null ? void 0 : _a2.reflect)) {
291
322
  this.reflectProperty({property, newValue, newValueString});
292
323
  }
293
- const informWatchedPropertiesAndDispatchChangeEvent = () => {
294
- if (property in this.watch()) {
295
- this.watch()[property](newValue, oldValue);
296
- }
297
- if (property in this._options["propertyOptions"] && this._options["propertyOptions"][property]["notify"] === true) {
298
- this.dispatch(`${camelToDash(property)}-changed`, newValue, true);
299
- }
300
- };
301
324
  if (this._options.autoUpdate) {
302
325
  this.requestUpdate({
303
326
  notify: true,
@@ -306,16 +329,29 @@ var BaseElement = class extends HTMLElement {
306
329
  newValueString,
307
330
  oldValue
308
331
  }).finally(() => {
309
- informWatchedPropertiesAndDispatchChangeEvent();
332
+ this.callPropertyWatcher(property, newValue, oldValue);
333
+ this.notifyPropertyChange(property, newValue);
310
334
  });
311
335
  } else {
312
- informWatchedPropertiesAndDispatchChangeEvent();
336
+ this.callPropertyWatcher(property, newValue, oldValue);
337
+ this.notifyPropertyChange(property, newValue);
313
338
  }
314
339
  }
315
340
  return this;
316
341
  }
317
342
  });
318
343
  }
344
+ callPropertyWatcher(property, newValue, oldValue) {
345
+ const watch = this.watch();
346
+ if (property in watch) {
347
+ watch[property](newValue, oldValue);
348
+ }
349
+ }
350
+ notifyPropertyChange(property, newValue) {
351
+ if (property in this._options["propertyOptions"] && this._options["propertyOptions"][property]["notify"] === true) {
352
+ this.dispatch(`${camelToDash(property)}-changed`, newValue, true);
353
+ }
354
+ }
319
355
  reflectProperty(options) {
320
356
  var _a;
321
357
  const {property, newValue} = options;
@@ -330,6 +366,39 @@ var BaseElement = class extends HTMLElement {
330
366
  this.setAttribute(camelToDash(property), attributeValue);
331
367
  }
332
368
  }
369
+ injectProperties() {
370
+ return {};
371
+ }
372
+ provideProperties() {
373
+ return {};
374
+ }
375
+ definePropertyInjection() {
376
+ Object.entries(this.injectProperties()).forEach(([key, value]) => {
377
+ this.requestContext(key, value);
378
+ });
379
+ if (Object.keys(this.provideProperties()).length > 0) {
380
+ this.addEventListener("request-context", this.onRequestContext);
381
+ }
382
+ }
383
+ requestContext(propertyName, valueOrCallback) {
384
+ this.dispatch("request-context", {[propertyName]: valueOrCallback}, true);
385
+ }
386
+ onRequestContext(event) {
387
+ var _a;
388
+ const properties = this.properties();
389
+ const provideProperties = this.provideProperties();
390
+ Object.entries((_a = event.detail) != null ? _a : {}).forEach(([key, valueOrCallback]) => {
391
+ if (key in provideProperties) {
392
+ const providedValue = provideProperties[key];
393
+ event.stopPropagation();
394
+ if (typeof valueOrCallback === "function") {
395
+ valueOrCallback(providedValue);
396
+ } else {
397
+ event.target[key] = providedValue;
398
+ }
399
+ }
400
+ });
401
+ }
333
402
  hooks() {
334
403
  return {};
335
404
  }