Motivated by the success @benkimpel had with shoelace (see: https://elixirforum.com/t/improve-support-for-web-components-in-forms-with-element-adapters/61175) and the thread by @adw632 (see: Adobe Spectrum 2 web components with LiveView) I tried it myself.
I had some success, but there are also some problems I do not know how to solve and if they are solvable.
Solved? Problem 1 - LV removes attributes set by shoelace
If you do not set all relevant attributes on an element, shoelace sets defaults, LV removes them. This can be easily solved by either wrapping all shoelace elements into function components (see Ben’s thread) or just:
SL_DEFAULTS = {
"SL-BUTTON": { "variant": "default", "size": "medium" },
"SL-AVATAR": { "shape": "circle" },
"SL-BADGE": { "variant": "neutral" },
"SL-ICON": { "library": "default" },
...
}
let liveSocket = new LiveSocket("/live", Socket, {
params: { _csrf_token: csrfToken },
dom: {
onBeforeElUpdated(_from, to) {
const sl_defaults_for_current = SL_DEFAULTS[to.tagName];
if (sl_defaults_for_current) {
Object.entries(sl_defaults_for_current).forEach(([key, value]) => {
if (!to.hasAttribute(key)) {
to.setAttribute(key, value);
}
});
}
}
}
})
this seems to work fine.
Solved? Problem 2 - LV removes state like in @open
Multiple components store their state in an @open
, eg <sl-details>
<sl-details summary="Toggle Me">
Lorem ipsum ...
</sl-details>
So when you
- render → closed (
@open
not set) - toggle → opens (
@open
set) - rerender → closes (
@open
removed by LV)
This can be fixed by sth like
window.addEventListener("sl-after-show", (evt) => {
liveSocket.execJS(evt.target, '[["set_attr", {"attr": ["open", true]}]]');
})
window.addEventListener("sl-after-hide", (evt) => {
liveSocket.execJS(evt.target, '[["remove_attr", {"attr": "open"}]]');
})
Or sth more sophisticated (see Ben’s thread)
Seems to work fine.
Problem 3 - LV removes elements that shoelace places into the light-DOM
This happens with <sl-breadcrumb>
, code:
<sl-breadcrumb>
<sl-icon name="arrow-right" slot="separator" aria-hidden="true"></sl-icon>
<sl-breadcrumb-item>
<sl-icon slot="prefix" name="house"></sl-icon>First
</sl-breadcrumb-item>
<sl-breadcrumb-item>Second</sl-breadcrumb-item>
<sl-breadcrumb-item>Third</sl-breadcrumb-item>
</sl-breadcrumb>
This is how the breadcrumbs look like after first render (correct):
Note the arrow-icon that shoelace dynamically put into the separator slot of the item:
after a rerender it looks like:
Note the missing arrow-icon:
Problem 4 - LV removes generated classes
happens with <button-group>
, code:
<sl-button-group label="Alignment">
<sl-button size="small">Left</sl-button>
<sl-button size="small">Center</sl-button>
<sl-button size="small">Right</sl-button>
</sl-button-group>
first render (correct):
note the classes:
after rerender:
classes missing:
Problem 5 - ARIA
Seems like shoelace puts some important aria attributes which LV removes, didn’t look into that.
Problem 6 - Forms
This does absolutely not work right now, see Ben’s thread.