@skirbi/sugar 0.1.2 → 0.1.4
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 +41 -0
- package/lib/index.mjs +1 -1
- package/lib/livewire.mjs +42 -0
- package/package.json +2 -1
package/Changes
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
Revision history for @skirbi/sugar
|
|
2
2
|
|
|
3
|
+
0.1.4 2026-06-06 16:41:57Z
|
|
4
|
+
|
|
5
|
+
* Semtic already had this, but sugar in itself also has side-effects.
|
|
6
|
+
In the future we'll probably note which things have side-effects, but for
|
|
7
|
+
now we treat ourselves as fully side-effect-ish.
|
|
8
|
+
|
|
9
|
+
0.1.3 2026-06-05 23:23:43Z
|
|
10
|
+
|
|
11
|
+
* Add Livewire integration module
|
|
12
|
+
|
|
13
|
+
Add @skirbi/sugar/livewire.mjs. An optional side-effect module that
|
|
14
|
+
fixes structural mismatches between Livewire's morphing algorithm and
|
|
15
|
+
Sugar web components.
|
|
16
|
+
|
|
17
|
+
Problem? Yes.
|
|
18
|
+
Livewire sends new HTML in its authored (dehydrated) state. Custom
|
|
19
|
+
elements in that HTML have not yet run connectedCallback, so their
|
|
20
|
+
DOM structure differs from the current rendered (hydrated) DOM.
|
|
21
|
+
Alpine's morph algorithm compares these two structurally different
|
|
22
|
+
trees, sees mismatches, and calls swapElements — replacing entire
|
|
23
|
+
subtrees, disconnecting all custom element descendants, triggering
|
|
24
|
+
cascading connectedCallback re-renders, and causing focus loss on
|
|
25
|
+
every Livewire update.
|
|
26
|
+
|
|
27
|
+
Fix, days:
|
|
28
|
+
Before Livewire's morph runs, parse the new HTML into a hidden
|
|
29
|
+
staging element and wait two microtasks for custom elements to
|
|
30
|
+
hydrate. Alpine then morphs two fully hydrated trees — apples to
|
|
31
|
+
apples — no structural mismatches, no swaps, no cascade, no focus
|
|
32
|
+
loss.
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
import '@skirbi/sugar/livewire';
|
|
36
|
+
|
|
37
|
+
Only import when using Sugar/Semtic/Skirbi components inside a Livewire
|
|
38
|
+
application. The guard on typeof document makes it safe in SSR/Node
|
|
39
|
+
environments.
|
|
40
|
+
|
|
41
|
+
This fix is a candidate for upstreaming to Livewire once it has been
|
|
42
|
+
validated.
|
|
43
|
+
|
|
3
44
|
0.1.2 2026-06-05 13:39:51Z
|
|
4
45
|
|
|
5
46
|
* Add replaceChildrenSugarSafe to allow repaints without focus loss
|
package/lib/index.mjs
CHANGED
package/lib/livewire.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Wesley Schwengle <wesleys@opperschaap.net>
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @skirbi/sugar — Livewire integration
|
|
7
|
+
*
|
|
8
|
+
* Hydrates custom elements in Livewire's new HTML before morphing starts.
|
|
9
|
+
* This ensures Alpine morphs hydrated vs hydrated trees — apples to apples —
|
|
10
|
+
* preventing structural mismatches that cause swapElements cascades,
|
|
11
|
+
* disconnected/reconnected custom elements, and focus loss on every update.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* import '@skirbi/sugar/livewire';
|
|
15
|
+
*
|
|
16
|
+
* Only import this when using Sugar components inside a Livewire application.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
if (typeof document !== 'undefined') {
|
|
20
|
+
document.addEventListener('livewire:init', () => {
|
|
21
|
+
window.Livewire.hook('component.init', ({ component }) => {
|
|
22
|
+
const original = component.processEffects.bind(component);
|
|
23
|
+
|
|
24
|
+
component.processEffects = function(effects) {
|
|
25
|
+
if (!effects.html) return original(effects);
|
|
26
|
+
if (effects._sugarHydrated) return original(effects);
|
|
27
|
+
|
|
28
|
+
const staging = document.createElement('div');
|
|
29
|
+
staging.style.display = 'none';
|
|
30
|
+
document.body.appendChild(staging);
|
|
31
|
+
staging.innerHTML = effects.html;
|
|
32
|
+
|
|
33
|
+
Promise.resolve().then(() => Promise.resolve().then(() => {
|
|
34
|
+
effects.html = staging.innerHTML;
|
|
35
|
+
effects._sugarHydrated = true;
|
|
36
|
+
staging.remove();
|
|
37
|
+
original(effects);
|
|
38
|
+
}));
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"./htmlelement": "./lib/htmlelement.mjs",
|
|
25
25
|
"./htmlelement-input": "./lib/htmlelement-input.mjs",
|
|
26
26
|
"./htmlelement-select": "./lib/htmlelement-select.mjs",
|
|
27
|
+
"./livewire": "./lib/livewire.mjs",
|
|
27
28
|
"./testing": "./lib/testing.mjs",
|
|
28
29
|
"./with-connected-sugar": "./lib/with-connected-sugar.mjs"
|
|
29
30
|
},
|
|
@@ -52,5 +53,5 @@
|
|
|
52
53
|
},
|
|
53
54
|
"sideEffects": false,
|
|
54
55
|
"type": "module",
|
|
55
|
-
"version": "0.1.
|
|
56
|
+
"version": "0.1.4"
|
|
56
57
|
}
|