jahilldev / component-elements

Create a custom element from any component with these tiny functions (2KB GZipped, ~1KB Brotli). Preact and React currently supported
MIT License
89 stars 7 forks source link

Slot support? #3

Closed Nicolab closed 2 years ago

Nicolab commented 2 years ago

Hello,

preactement supports the slots?

Example:

In the HTML (DOM):

<slot name="test">test1</slot>

In the component JSX:

<slot name="test">test2</slot>

Both are displayed.

jahilldev commented 2 years ago

Hi @Nicolab,

As of right now, <slot /> is not supported. If you could explain how you would like these to work, and what you're trying to solve I can look into supporting them.

Thanks!

Nicolab commented 2 years ago

Hi @jhukdev,

Thanks for your reply.

I use the custom element (Preactement) to render on the server side the raw HTML, then take over with the front. Example (ideally):

  <x-article articleId="1">
    <h3 class="title"><slot name="title">Article title</slot></h3>
    <p class="content"><slot name="content">Article content</slot></p>
  </x-article>

  <x-article articleId="2">
    <h3 class="title"><slot name="title">Article title 2</slot></h3>
    <p class="content"><slot name="content">Article content 2</slot></p>
  </x-article>

This could also have the form of <h3 class="title" slot="title">Article title 2</h3>, why not, it's shorter.

The article title and/or content can be edited in Article component (<x-article> tag). With a onClick, a form is displayed and after the submit, <slot name="title">{state.title}</slot> and/or <slot name="content">{state.content}</slot> should be rendered instead of the original content rendered by the server.

There are plenty of practical uses that would bring another dimension to deal between static content returned by the server and dynamic content generated by the front end (including REST requests, user interactions, ...).

preact-custom-element could do this but it renders 2 times the content of slots and props.children. This makes it impractical to handle static server-generated content via front-end dynamism.

There are some closed issues on preact-custom-element about this, but it is advised to go through the shadow DOM, which I want to avoid to not mess me with the CSS (especially with CSS frameworks that have no effect on the shadow DOM). It seems that this behavior will not be changed in preact-custom-element.

preactement works as desired, if there is slot support it would be perfect :rocket:

* I hope I am clear, my English is not great

jahilldev commented 2 years ago

Hi @Nicolab,

Thanks for taking the time to outline what you're looking for.

This sounds totally doable, I've noticed the code in preact-custom-element that handles <slot />'s, so based on your description above, I should be able to get a PR together from this.

When I get some time, I'll make a start on this. I'll ping you here with an update 👍

jahilldev commented 2 years ago

Hi @Nicolab,

Just so I'm clear, I see that preact-custom-element is assigning the value of the element with a slot attribute to the components props, e.g:

https://github.com/preactjs/preact-custom-element/blob/5790765ec0227ebb16add2f1469aef648e9f9f9e/src/index.js#L154

/**
  * Example HTML custom element
  */
<x-article>
  <span slot="testSlot">Hello</span>
</x-article>

/*[...]*/

/**
  * Example component defined as <x-article />
  */
function Article({ testSlot }) {
  console.log(testSlot); // <-- Will equal element value, e.g "<slot name="testSlot">Hello</slot>"

  return (
    <div>
      {testSlot}
    </div>
  );
}

Is this correct?

Nicolab commented 2 years ago

Excellent, thank you!

Will equal element value, e.g "<slot name="testSlot">Hello</slot>" .... Is this correct?

Yes, I'm pretty sure that this is the same behavior.

jahilldev commented 2 years ago

@Nicolab Hey!

By popular demand, a working PR with slot support: https://github.com/jhukdev/preactement/pull/5

This works in a few ways (to my preference, tell me if I'm wrong!). I've allowed the slot value to be either a string primitive if that's the only content type (saves having to parse VDom), or a VDom tree if there's some more complex HTML structure within.

I've outlined a couple of examples below.

Primitive String

/**
  * Example HTML custom element
  */
<x-article>
  <span slot="testSlot">Hello</span>
</x-article>

/**
  * Example component defined as <x-article />
  */
function Article({ testSlot }) {
  console.log(testSlot); // <-- Will equal string primitive "Hello"

  return (
    <div>
      {testSlot}
    </div>
  );
}

VDom Tree

/**
  * Example HTML custom element
  */
<x-article>
  <span slot="testSlot">
    <em class="text">Well, Hello!</em> 
  </span>
</x-article>

/**
  * Example component defined as <x-article />
  */
function Article({ testSlot }) {
  console.log(testSlot); // <-- Will equal VDom "<em class="text">Well, Hello!</em>"

  return (
    <div>
      {testSlot}
    </div>
  );
}

Let me know what you think, and whether this is something that would work. It's also worth noting that the <* slot="{key}" /> elements are removed from this.props.children to avoid duplicate content, or having to mitigate that.

Nicolab commented 2 years ago

Very nice. Yes, I prefer this way because it's more clean and flexible. We are able to get only the content Hello without element, it's handy. We can also get all elements if we want.

It's perfect, thanks! :+1:

jahilldev commented 2 years ago

@Nicolab Ok, merged and published: https://github.com/jahilldev/preactement/releases/tag/1.6.0

I've put together a demo here: https://github.com/jahilldev/preactement-htmlslots

Let me know how you get on 👍

jahilldev commented 2 years ago

@Nicolab I'm going to mark this as done, let me know if you have any issues.

Thanks for the suggestion!

Nicolab commented 2 years ago

Hey, sorry I didn't see your messages. I have seen the demo, this is perfect, thank you very much! Really :+1: I will integrate in my projects after the holidays :smiley:

Have a good end of year!