Слоты и проекция контента

Haunted — это библиотека для создания веб-компонентов на базе функционального подхода и хуков, вдохновлённая React, но работающая на чистом Web Components API. Одним из ключевых механизмов для организации и переиспользования контента в компонентах являются слоты и проекция контента. Они позволяют вставлять динамический HTML внутрь компонента, обеспечивая гибкость в композиции интерфейсов.


Основы слотов

Слот — это специальный маркер внутри шаблона компонента, который указывает, куда будет вставлен внешний контент. В Haunted слоты работают так же, как в стандартных Web Components.

import { component, html, useState } from 'haunted';

function Card() {
  return html`
    <div class="card">
      <header>
        <slot name="header"></slot>
      </header>
      <main>
        <slot></slot>
      </main>
      <footer>
        <slot name="footer"></slot>
      </footer>
    </div>
  `;
}

customElements.define('my-card', component(Card));
  • <slot> без имени принимает неименованный контент.
  • <slot name="header"> и <slot name="footer"> принимают именованные фрагменты, передаваемые через атрибут slot.

Пример использования:

<my-card>
  <h1 slot="header">Заголовок карточки</h1>
  <p>Основной контент карточки</p>
  <small slot="footer">Подпись</small>
</my-card>

В этом примере:

  • Заголовок попадает в слот с именем header.
  • Параграф автоматически попадает в неименованный слот.
  • Подпись размещается в слоте footer.

Динамический контент через слоты

Haunted позволяет использовать состояния и хуки для динамического обновления содержимого слотов. Это особенно удобно для компонентов, где внешний контент может меняться в зависимости от данных.

function ToggleCard({ initialText }) {
  const [show, setShow] = useState(true);

  return html`
    <div class="toggle-card">
      <button @click=${() => setShow(!show)}>Переключить</button>
      <div>
        ${show ? html`<slot>${initialText}</slot>` : html`<slot>Контент скрыт</slot>`}
      </div>
    </div>
  `;
}

customElements.define('toggle-card', component(ToggleCard));

Здесь <slot> динамически меняет отображаемое содержимое на основе состояния show. Это демонстрирует, что слоты в Haunted интегрируются с реактивной моделью данных через хуки.


Проекция контента

Проекция контента (content projection) — это процесс передачи HTML или компонентов внутрь другого компонента через слоты. Haunted полностью наследует этот механизм из Web Components. Проекция позволяет компоненту оставаться универсальным и не зависеть от конкретного внутреннего контента.

Ключевые особенности:

  1. Именованные слоты: позволяют вставлять конкретные элементы в строго определённые части шаблона.
  2. Fallback контент: если слот не заполнен, используется содержимое внутри тега <slot>.

Пример fallback контента:

function Alert() {
  return html`
    <div class="alert">
      <slot>Внимание! Нет содержимого.</slot>
    </div>
  `;
}

customElements.define('my-alert', component(Alert));

Если пользователь не передаст содержимое в <my-alert>, отобразится сообщение по умолчанию.


Вложенные слоты и сложные проекции

Haunted позволяет создавать вложенные компоненты с слотами, что открывает возможности для построения сложных UI.

function Modal() {
  return html`
    <div class="modal">
      <div class="modal-header">
        <slot name="header">Заголовок по умолчанию</slot>
      </div>
      <div class="modal-body">
        <slot></slot>
      </div>
      <div class="modal-footer">
        <slot name="footer"></slot>
      </div>
    </div>
  `;
}

customElements.define('my-modal', component(Modal));

Использование:

<my-modal>
  <h2 slot="header">Диалог</h2>
  <p>Контент диалога</p>
  <button slot="footer">Закрыть</button>
</my-modal>

Такой подход позволяет строить гибкие и переиспользуемые компоненты с полностью настраиваемым содержимым.


Ограничения и особенности

  • Слоты не наследуют состояние компонента, в который проецируются. Внутри слота можно вставить только внешний контент или другой компонент, который управляется самостоятельно.
  • Если переданный элемент имеет событие, оно работает как обычно, но необходимо учитывать event bubbling, так как события могут всплывать через слот.
  • Для сложных шаблонов с множеством слотов рекомендуется использовать именованные слоты, чтобы избежать путаницы и обеспечить предсказуемое позиционирование контента.

Связь слотов с хуками Haunted

В Haunted слоты могут быть связаны с реактивными хуками для управления отображением:

function TabContent({ activeTab }) {
  return html`
    <div class="tabs">
      <slot name=${activeTab}></slot>
    </div>
  `;
}

customElements.define('tab-content', component(TabContent));

В этом примере через динамическое имя слота можно переключать отображаемое содержимое в зависимости от состояния activeTab. Это демонстрирует, что Haunted не ограничивается статической проекцией, а полностью интегрируется с реактивной моделью данных.


Слоты и проекция контента в Haunted — фундаментальные инструменты для построения модульных, переиспользуемых и реактивных компонентов. Они обеспечивают максимальную гибкость при структурировании интерфейсов и позволяют разделять логику компонента и его внешний контент без потери производительности.