Sebb

Sebb

Let's make shoelace work with liveview

Motivated by the success @benkimpel had with shoelace (see: Improve Support for Web Components in Forms with "Element Adapters") 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

  1. render → closed (@open not set)
  2. toggle → opens (@open set)
  3. 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):
image

Note the arrow-icon that shoelace dynamically put into the separator slot of the item:

after a rerender it looks like:
image

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):
image

note the classes:

after rerender:

classes missing:
image

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.

Most Liked

Sebb

Sebb

sad story.

Thank’s for all the help so far, did put me on the right track.
We’ll see what comes of it.

benkimpel

benkimpel

This is great. Let’s see if we can fix these.

When we were testing it out we found that adding an ID to some of the Shoelace slots helped with patching. It triggers different behavior in morphdom since the id is the nodeKey so rather than patching as children it patches directly.

There’s a more drastic option as well…

// WARNING: Lazy example. There's probably much more to it.
onBeforeElUpdated: (from, to) => {
  // Effectively make sure Shoelace always wins the attr merge by assigning from
  // back over it.
  // 
  // This could be done better with a "Managed Attr" list where instead of this
  // all or nothing approach below one could say: 
  // "Ok, for attr ABC of element XYZ we never patch it."
  // or even handle some specifically...
  // "For class of element XYZ we can merge the classes, but never remove"
  //
  // BUT I expect this would result in unnecessary renders all the time?
  //
  // This is why I think there are ultimately low-level changes involved if we want
  // to support arbitrary WCs
  //
  if (from.tagName.startsWith("SL-")) {
    [...to.attributes, ...from.attributes].forEach((attr) => {
      to.setAttribute(attr.name, attr.value); 
    });
},
benkimpel

benkimpel

For example, this fixes the button group…

// onBeforeElUpdated

// Protect sl- classes
if (from.tagName.startsWith("SL-")) {
  from.classList.forEach((cls) => {
    if (cls.startsWith("sl-")) {
      to.classList.add(cls);
    }
  });
}

But how fragile is all of this? idk

Where Next?

Popular in Discussions Top

andre1sk
A big advantage to Elixir is all the distributed goodness but for many applications running on multiple nodes having integrated Etcd, Zoo...
New
mikl
I wanted to capitalize a string, and tried using String.capitalize(). That generally works well, until you try to capitalize a word like...
New
cvkmohan
The upcoming Phoenix 1.6 release looks very interesting. Became a habit to watch the commits - and - what they are bringing in. phx.gen...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
thojanssens1
It would be nice to be able to define a redirect from one route to another from the router.ex file. E.g.: redirect "/", UserController, ...
New
AlexMcConnell
The reason that Rails is as popular as it is is because it’s very easy for relatively inexperienced developers to get a lot of work done....
588 19568 166
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31142 143
New
fireproofsocks
I’ve been working on an Elixir project that has required a lot of scripting. I usually reach for Elixir because I like it more (and in th...
New
rower687
Hi all, I’ve been reading a lot about the “let it crash” term and how supervising processes and the whole messaging passing make an elixi...
New
paulanthonywilson
I like Umbrella projects and pretty much always use them for personal Elixir stuff, especially Nerves things. But I don’t think this is ...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31142 143
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 47930 226
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 126479 1222
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement