Как перевести обычный CSS в веб-компонент и правильно применить дочерний селектор? - PullRequest
0 голосов
/ 10 марта 2020

У меня также есть код в Pen: https://codepen.io/veksi/pen/xxGpXWM?editors=1010, но он воспроизведен здесь, чтобы сохранить его для потомков.

У меня есть фрагмент кода, который работает при использовании CSS без используя веб-компоненты (или в данном случае - html, но это не имеет значения). Тем не менее, существует какой-то ментальный блок, так как я изо всех сил пытаюсь преобразовать «простую CSS» версию в версию, которая работает с веб-компонентами. Я думаю, что проблема с селекторами :host и ::slotted и их использованием, но меня удивляет, в чем именно заключается проблема. Может кто-нибудь помочь и указать путь с помощью правильного селектора?

Вот код:

image

import { LitElement, css, html } from 'https://unpkg.com/lit-element?module';


/* This seem to work almost while version 2 does not at all.
The problem in this version is alignment. The 'Search' button
is not on the right side and the search field occupying the
free space accordingly. For some reason flex-basis and
flex-grow are not matched by the CSS selector...

Basically trying to recreate .sidebar-test as given in the document CSS, but wrapped into an element.
*/
export class Sidebar1 extends LitElement {
  static get styles() {
    return css`    
    :host {
      overflow: hidden;
    }

    :host ::slotted(*) {
      display: flex;
      flex-wrap: wrap;
    }

    :host ::slotted(*) > ::slotted(*) {
      flex-grow: 1;
    }    
    `;
  }

  get sideStyle() {
    return html`
      <style>
        :host ::slotted(*) { margin: calc(${this.space} / 2 * -1); ${this.noStretch ? 'align-items: flex-start;' : ''} }

        :host ::slotted(*) > ::slotted(*) {  margin: calc(${this.space} / 2); ${this.sideWidth ? `flex-basis: ${this.sideWidth};` : ''} }

        :host ::slotted(*) > ${this.side !== 'left' ? `::slotted(*:first-child)` : `::slotted(*:last-child)`} {
            flex-basis: 0;
            flex-grow: 999;
            min-width: calc(${this.contentMin} - ${this.space});
          }
      </style>
    `;
  }

  static get properties() {
    return {
      side: { type: String },
      sideWidth: { type: String },
      contentMin: { type: String },
      space: { type: String },
      x: { type: String },
      noStretch: { type: Boolean, reflect: true, attribute: true }
    };
  }

  constructor() {
    super();

    this.side = "left";
    this.contentMin = "50%";

    if (this.children[0].children.length != 2) {
      console.error(`Should have exactly two children..`);
    }
  }

  render() {
    if (!this.contentMin.includes('%')) {
      console.warn('Minimum content should be in percentages to avoid overflows.');
    }

    return html`${this.sideStyle}<slot></slot>`;
  }
}

customElements.define('test-sidebar1', Sidebar1);


/* Compared to Sidebar1 there's a problem with layout. As if sideStyle wouldn't be applied correctly.*/
export class Sidebar2 extends LitElement {
  static get styles() {
    return css`
    :host {
      --sidebarSpace: 1rem;
      --sidebarContentMin: 50%;
      --sidebarWidth: 30ch;

      overflow: hidden;
    }

    :host > ::slotted(*) {
      display: flex;
      flex-wrap: wrap;

      margin: calc(var(--sidebarSpace) / 2 * -1);
    }

    :host([noStretch]) {
      :host > ::slotted(*) {
        align-items: flex-start;
      }
    }

    :host > ::slotted(*) > ::slotted(*) {
      margin: calc(var(--sidebarSpace) / 2);
      flex-grow: 1;
    }

    :host > ::slotted(*) > ::slotted(*:first-child) {
      flex-basis: 0;
      flex-grow: 999;
      min-width: calc(var(--sidebarContentMin) - var(--sidebarSpace));
    }
    `;
  }

  get sideStyle() {
    return html`
      <style>
         :host > ::slotted(*) > ::slotted(*) {
          ${this.sideWidth ? 'flex-basis: var(--sidebarWidth)' : ''}
        }

        :host > ::slotted(*) > ${this.side !== 'left' ? '::slotted(*:first-child)' : '::slotted(*:last-child)'} {
          flex-basis: 0;
          flex-grow: 999;
          min-width: calc(var(--sidebarContentMin) - var(--sidebarSpace));
        }
      </style>
    `;
  }

  static get properties() {
    return {
      side: { type: String },
      sideWidth: { type: String },
      contentMin: { type: String },
      space: { type: String },
      x: { type: String },
      noStretch: { type: Boolean, reflect: true, attribute: true }
    };
  }

  updated(changedProperties) {
    if (changedProperties.has("space")) {
      this.style.setProperty("--sidebarSpace", this.space);
    } else if (changedProperties.has("contentMin")) {
      this.style.setProperty("--sidebarContentMin", this.contentMin);
    } else if (changedProperties.has("sideWidth")) {
      this.style.setProperty("--sidebarWidth", this.sideWidth);
    }
  }

  constructor() {
    super();

    this.side = "left";
    this.contentMin = "50%";

    if (this.children[0].children.length != 2) {
      console.error(`Should have exactly two children..`);
    }
  }

  render() {
    if (!this.contentMin.includes('%')) {
      console.warn('Minimum content should be in percentages to avoid overflows.');
    }

    return html`${this.sideStyle}<slot></slot>`;
  }
}

customElements.define('test-sidebar2', Sidebar2);

Ответы [ 2 ]

0 голосов
/ 20 марта 2020

Если вы go до https://lit-element.polymer-project.org/guide/styles#style -the-components-children и немного прочитаете страницу, вы найдете следующее:

Обратите внимание, что только Прямые дочерние слоты могут быть стилизованы с помощью :: slotted ().

<my-element>
  <div>Stylable with ::slotted()</div>
</my-element>

<my-element>
  <div><p>Not stylable with ::slotted()</p></div>
</my-element>

, поэтому я не думаю, что селекторы типа :host > ::slotted(*) > ::slotted(*) будут работать.

0 голосов
/ 10 марта 2020

Кажется, проблема в том, что составные селекторы не поддерживаются в теневом DOM. Соответствующее обсуждение с некоторыми решениями, конкретно касающимися lit-html, можно провести по адресу https://github.com/Polymer/lit-element/issues/42.

...