Semantic-Org / Semantic-UI

Semantic is a UI component framework based around useful principles from natural language.
http://www.semantic-ui.com
MIT License
51.1k stars 4.95k forks source link

Discuss Web Components integration #4963

Open tbranyen opened 7 years ago

tbranyen commented 7 years ago

I understand that as an early adopter using technology like Web Components, I cannot expect the existing tooling I'm accustomed to, to "just work". Although in the case of Semantic UI, I'm worried that its current design decisions do not align with the future of isolation and custom elements in the web via Web Components.

Example: You are building a data grid and want to separate the table from the row via components. So you have the following two components:

Once rendered the DOM Tree looks something like:

~ TableComponent ~
  -> Shadow Root
    -> slot (table)
  -> table
      -> thead
      -> tbody
        -> ~ TableRowComponent (Subclasses TR) ~
          -> Shadow Root
            -> td
            -> td

The problem may not be immediately obvious, but as styles do not cross the shadow boundary and the way that Semantic UI forfeits explicit class names for element names, means that it's pretty much impossible to get this to work:

/* .ui.celled.table is styled perfectly, but there's no way to cascade into the td */
.ui.celled.table tr td, .ui.celled.table tr th {
    border-left: 1px solid rgba(34,36,38,.1);
}

How can we get these styles into the Shadow Root?

Does the project/community have thoughts on this fundamental design problem?

tbranyen commented 7 years ago

Changed the title to be less confrontational, hope we can get some good discussion in here.

PhilLehmann commented 7 years ago

While I generally understand your concern, why would one create a separate TableRowComponent? Or is this just a bad example?

tbranyen commented 7 years ago

@philrykoff Generally web applications are structured using components and with Web Components you are encouraged to encapsulate the shared content within a Shadow Root. This example is absolutely real world, you cannot inject a <div> while maintaining the structural integrity of a table. Therefore the only way you can Web-Componentize the row-level of a table is to subclass the <tr>. I'm trying to use Semantic UI to create development tools in Chrome, and am blocked currently being unable to style the <td> element's in a satisfactory manner (or at all).

When structuring tables you want this:

<table>
  <tr is="some-custom-row"></tr>
</table>

You cannot do this:

<table>
  <div class="some-custom-row">
    <some-custom-row></some-custom-row>
  </div>
</table>
tbranyen commented 7 years ago

Looks like is didn't make it to the v1 specification which is very frustrating to learn. So the above code doesn't work. The best you can do right now is:

<table>
  <some-custom-row>
    <td>
    <td>
  </some-custom-row>
</table>

<style>
some-custom-row {
  display: table-row;
}
</style>

This displays correctly and will be styled nicely by Semantic UI. Issues will occur once you decide to make <some-custom-row> re-usable and stateful using Web Components.

I'm going to investigate generating Web Components for Semantic UI. Would be nice to have a UI Toolkit for diffHTML (the tool I'm working on) and this way I could simply extend from SemanticUITableRow like this:

import { html } from 'diffhtml';
import { WebComponent, PropTypes } from 'diffhtml-components';
import { SemanticUITableRow } from 'diffhtml-semantic-ui';

class DevtoolsTransactionRow extends SemanticUITableRow {
  static propTypes = {
    index: PropTypes.number,
    transaction: PropTypes.shape({
      patches: PropTypes.shape({
        TREE_OPS: PropTypes.array,
      }),
    }),
  }

  render() {
    const { index, transaction } = this.props;
    const { patches } = transaction;
    const { TREE_OPS} = patches;
    const stats = { insert: 0, replace: 0, remove: 0 };

    TREE_OPS.forEach(patchset => {
      if (patchset.INSERT_BEFORE) {
        stats.insert += patchset.INSERT_BEFORE.length;
      }

      if (patchset.REPLACE_CHILD) {
        stats.replace += patchset.REPLACE_CHILD.length;
      }

      if (patchset.REMOVE_CHILD) {
        stats.remove += patchset.REMOVE_CHILD.length;
      }

    });

    return html`
      <td>${String(index + 1)}</td>
      <td class="center aligned"><strong>${stats.insert}</strong></td>
      <td class="center aligned"><strong>${stats.replace}</strong></td>
      <td class="center aligned"><strong>${stats.remove}</strong></td>
    `;
  }
}

customElements.define('devtools-transaction-row', DevtoolsTransactionRow);

This would be automatically styled, next pass could be maybe making them stateful w/ the JS.

tbranyen commented 7 years ago

I took a look into the official React bindings and it appears those components use flat class names. Since I'm working on diffHTML/React integration, I'm going to see if this prior work can solve my issue inside the Shadow DOM.

oleersoy commented 7 years ago

CSS Variables? (Not really, they won't substitute full blocks, could help with theming)

What if you use something like this:

--ComponentName[-descendant|--modifier][-onState]-(cssProperty|variableName)

That should allow you to substitute the pieces of the full block that you want to be configurable. I'm planning on prototyping this out further under the superfly-css organization.

oleersoy commented 7 years ago

Here's an example: SUIT-CSS Button Component

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 30 days if no further activity occurs. Thank you for your contributions.

brodycj commented 6 years ago

Please don't close this. Web Components would be a nice way to specify the Semantic-UI behavior in a standard way that would work in frameworks such as React, Vue, Angular, etc.

brodycj commented 6 years ago

@jlukic https://github.com/Semantic-Org/Semantic-UI/issues/6109#issuecomment-369702192:

@brodybits Pretty good overview in this article (1).

... with link to an excellent response (2) added to the end.

Other related, recommended resources: [3], [4], [5]/[6]

I would favor using something simple such as X-Tag ([7], [8], [9]). Svelte [10] and StencilJS [11] are also designed to support web components but with what I think is some unnecessary level of complexity in API (StencilJS only) and generated code (I would not personally consider Svelte to be 100% disappearing). Surplus [12] also supports web components [13] without need for a virtual DOM.

[1] https://dmitriid.com/blog/2017/03/the-broken-promise-of-web-components/ [2] https://robdodson.me/regarding-the-broken-promise-of-web-components/ [3] https://medium.com/dev-channel/the-case-for-custom-elements-part-1-65d807b4b439 [4] https://medium.com/dev-channel/custom-elements-that-work-anywhere-898e1dd2bc48 [5] https://custom-elements-everywhere.com/ [6] https://github.com/webcomponents/custom-elements-everywhere [7] https://github.com/x-tag/x-tag [8] http://x-tag.github.io/ [9] https://www.sitepoint.com/building-custom-web-components-with-x-tag/ [10] https://svelte.technology/ [11] https://stenciljs.com/ [12] https://github.com/adamhaile/surplus [13] https://custom-elements-everywhere.com/#surplus

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 30 days if no further activity occurs. Thank you for your contributions.

brodycj commented 6 years ago

I would still consider this one to be relevant.

Antarian commented 4 years ago

14 months from last commit is almost equal to dead. There is some resurrection in React version, but that one does not seems to use web components also. I was deciding between multiple UI frameworks to use. Found Stencil web components, which are also used in Ionic 4 (for web, iOS, Android with React, Angular, Vue, plain javascript), as a way to go.