Sebb

Sebb

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.

Showing Posts 1 to 10

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

@Sebb if you have a repo where you’re testing stuff I’m happy to help out. I’m benkimpel on github.

Sebb

Sebb OP

Wow that’s brutal.
And as far as I understand the problem now, this is the only way (without too much special-case-handling)

I don’t see how LV could ever handle arbitrary WC-libraries that do not follow at least these rules:

  • Do not generate elements in the light-DOM
  • Do not change the value of attributes that are not in a special namespace (and could thus be easily protected from LV)
Sebb

Sebb OP

I made the repo public.

pjode

pjode

Here’s the relevant issue in shoelace’s repo. Unfortunately I don’t think this is something that’s going to get addressed

Sebb

Sebb OP

That’s easily fixed by the onBeforeElUpdated callback which sets the defaults if not set.

cmo

cmo

I would’ve thought you’d need a phx-update="ignore“ on the shoelace components that use the light dom/change attributes. What’s the outcome when you use it?

Sebb

Sebb OP

Sure I could do that and it would be OK for several components.
But what about those that take children?
This would get messy quickly.

benkimpel

benkimpel

Yeah. That’s kind of what i was trying to get at with my element adapter proposal. With WCs that can do anything there has to be some translation layer between what lv is trying to do and how a WC can perform or ignore that operation and we can’t expect the WC author to do so. It’s most obvious in forms right now due to all of the element hardcoding. Aria, light dom mods, and simpler things could be handled in onBeforeElUpdated. It wouldn’t be pretty but it could work.

And sure, we could ignore updates but then the markup rendered from phoenix and passed into a WC is basically static (from phx side) and we have a new set of problems.

This is a tough one and it might just be that lv will only work with very simple WCs.

(I hadnt tested breadcrumbs, btw. Glad you tried that one.)

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? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 92995 915
New
caslu
I want to open this thread for you all to discuss and help those who really like Ash but are still hesitant to use it in a real project. ...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
GES233
I’m posting this in response to Jose’s recent tweet (Cr. link) : People are sleeping on Elixir for a coding harness: Hot-code swappi...
New
_mfierro
Hello, I wrote Stop My Hand, a Scattergories-like web application using Phoenix/LiveView as my learning project for Elixir (after readin...
New
marciol
It would be helpful to have a list of companies worldwide that hire engineers without prior experience in Elixir. Often, it can be quite ...
New
durvia
Anyone running long-lived stateful processes on BEAM? We’re building an AI agent runtime and would love to compare notes. We’re a small ...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews