@skirbi/sugar 0.0.11 → 0.0.12

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/Changes CHANGED
@@ -1,9 +1,56 @@
1
1
  Revision history for @skirbi/sugar
2
2
 
3
+ 0.0.12 2026-04-13 00:51:51Z
4
+
5
+ * While checking other Semtic units, we tightened a few tests and exposed an
6
+ issue in how components were rendered and rerendered.
7
+
8
+ We added new test cases to separate what already worked from what did not,
9
+ and validated those cases against Livewire. That led to a small redesign of
10
+ the `connectedSugar` API.
11
+
12
+ The result is fairly clean: a consumer now defines two selectors:
13
+
14
+ * `renderGuardSelector` indicates whether the component is already fully
15
+ rendered.
16
+ * `morphTriggerSelector` indicates whether the component has been morphed back
17
+ to its authored state.
18
+
19
+ Based on these checks, `connectedCallbackSugar()` is called when needed, and
20
+ the component can normalize itself back to its canonical DOM.
21
+
22
+ The previously advised pattern for `connectedCallbackSugar()` is now
23
+ obsolete.
24
+
25
+ This
26
+
27
+ connectedCallbackSugar() {
28
+ this._compile();
29
+ this._observeChildListOnce(...);
30
+ }
31
+
32
+ Becomes:
33
+
34
+ class SugarParent extends withConnectedSugar(HTMLElementSugar) {
35
+ static tag = 'sugar-parent';
36
+
37
+ static renderGuardSelector = ':scope > div[data-sugar-parent]'
38
+ static morphTriggerSelector = ':scope > sugar-child';
39
+
40
+ static _tpl = this.tpl(`
41
+ <div data-sugar-parent></div>
42
+ `);
43
+
44
+ connectedCallbackSugar() {
45
+ /* Whatever you want to do here to (re-)render the component */
46
+ }
47
+ }
48
+
49
+
3
50
  0.0.11 2026-02-24 16:53:49Z
4
51
 
5
52
  * Fix comparing HTML nodes. Whitespace text nodes are a PITA, we deal with
6
- them and now you can just compare two nodes regardless how you wrote it.
53
+ <F2>them and now you can just compare two nodes regardless how you wrote it.
7
54
  Whitespace kinda matters in HTML I guess, until it doesn't. &nbsp;
8
55
 
9
56
  0.0.10 2026-02-24 03:09:34Z
package/lib/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // SPDX-License-Identifier: MIT
4
4
 
5
- const VERSION = "0.0.11";
5
+ const VERSION = "0.0.12";
6
6
 
7
7
  export { HTMLElementSugar } from './htmlelement.mjs';
8
8
  export { HTMLElementSugarInput } from './htmlelement-input.mjs';
@@ -1,6 +1,16 @@
1
1
  export const withConnectedSugar = (Base) =>
2
2
  class extends Base {
3
3
  static renderGuardSelector = '';
4
+ static morphTriggerSelector = '';
5
+
6
+ connectedCallback() {
7
+ this._syncObservedAttributesToConfig();
8
+ this._armMorphObserver();
9
+
10
+ if (this._shouldRenderOnConnect()) {
11
+ this.connectedCallbackSugar();
12
+ }
13
+ }
4
14
 
5
15
  _syncObservedAttributesToConfig() {
6
16
  for (const attr of this.constructor.observedAttributes ?? []) {
@@ -9,48 +19,43 @@ export const withConnectedSugar = (Base) =>
9
19
  }
10
20
  }
11
21
 
12
- _shouldRenderOnConnect() {
13
- const guard = this.constructor.renderGuardSelector;
14
- if (guard) return !this.querySelector(guard);
15
-
16
- if (this._rendered) return false;
17
- this._rendered = true;
18
- return true;
19
- }
20
-
21
- /**
22
- * Install a single MutationObserver that calls `fn()` when direct children
23
- * change and `when()` returns true.
24
- *
25
- * Intended for morphdom/livewire scenarios where the host stays the same
26
- * instance.
27
- *
28
- * @param {Function} when - predicate
29
- * @param {Function} fn - callback
30
- */
31
- _observeChildListOnce(when, fn) {
22
+ _armMorphObserver() {
32
23
  if (this._sugarMo) return;
33
24
 
34
25
  this._sugarMo = new MutationObserver(() => {
35
- if (when()) fn();
26
+ if (this._shouldRenderOnMorph()) {
27
+ this.connectedCallbackSugar();
28
+ }
36
29
  });
37
30
 
38
31
  this._sugarMo.observe(this, { childList: true });
39
32
  }
40
33
 
34
+ _shouldRenderOnMorph() {
35
+ return this._hasMorphTrigger() && !this._hasRenderedShape();
36
+ }
37
+
38
+ _hasMorphTrigger() {
39
+ const sel = this.constructor.morphTriggerSelector;
40
+ return !!(sel && this.querySelector(sel));
41
+ }
42
+
43
+ _shouldRenderOnConnect() {
44
+ const guard = this.constructor.renderGuardSelector;
45
+ if (guard) return !this._hasRenderedShape();
46
+
47
+ return true;
48
+ }
49
+
50
+ _hasRenderedShape() {
51
+ const sel = this.constructor.renderGuardSelector;
52
+ return !!(sel && this.querySelector(sel));
53
+ }
54
+
41
55
  disconnectedCallback() {
42
- // polite cleanup
43
56
  this._sugarMo?.disconnect();
44
57
  this._sugarMo = null;
45
58
  }
46
59
 
47
- connectedCallback() {
48
- this._syncObservedAttributesToConfig();
49
- if (!this._shouldRenderOnConnect()) return;
50
-
51
- if (typeof this.connectedCallbackSugar === 'function') {
52
- this.connectedCallbackSugar();
53
- }
54
- }
55
60
  };
56
61
 
package/package.json CHANGED
@@ -53,5 +53,5 @@
53
53
  },
54
54
  "sideEffects": false,
55
55
  "type": "module",
56
- "version": "0.0.11"
56
+ "version": "0.0.12"
57
57
  }