Haunted — это библиотека для создания веб-компонентов на базе функций и реактивного подхода в JavaScript. Одной из ключевых возможностей является поддержка слотов, что позволяет строить гибкие и композиционно богатые компоненты.
Слот — это место внутри компонента, куда можно передавать
вложенный контент из родительского компонента. В
Haunted используется стандартный механизм Web
Components: <slot> и атрибуты
name.
import { html, component } from 'haunted';
function Card({ title }) {
return html`
<div class="card">
<h2>${title}</h2>
<div class="content">
<slot></slot>
</div>
</div>
`;
}
customElements.define('my-card', component(Card));
В примере выше Card создаёт слот
<slot></slot>. Любой контент, помещённый между
тегами <my-card>, попадёт внутрь блока
.content:
<my-card title="Пример">
<p>Это текст внутри слота.</p>
</my-card>
Если требуется несколько слотов, можно использовать именованные слоты:
function Layout() {
return html`
<header><slot name="header"></slot></header>
<main><slot></slot></main>
<footer><slot name="footer"></slot></footer>
`;
}
customElements.define('my-layout', component(Layout));
<my-layout>
<div slot="header">Заголовок страницы</div>
<p>Основное содержимое</p>
<div slot="footer">Подвал сайта</div>
</my-layout>
Важно: слоты позволяют сохранять структуру и стили родителя, не нарушая реактивность Haunted. Компонент реагирует на изменения переданного контента, если этот контент тоже построен с Haunted или Web Components.
Haunted напрямую не передаёт свойства родителя в слот, но можно использовать context или функции обратного вызова для взаимодействия с вложенным контентом. Это особенно полезно при создании интерактивных или динамических интерфейсов.
function ListItem({ onClick, text }) {
return html`<li @click=${onClick}>${text}</li>`;
}
function List({ items, onItemClick }) {
return html`
<ul>
${items.map(item => html`<${ListItem} text=${item} .onCl ick=${() => onItemClick(item)}></${ListItem}>`)}
</ul>
`;
}
customElements.define('my-list', component(List));
В этом примере каждый элемент списка получает своё событие
клика через свойство .onClick. Это демонстрирует
композицию: компонент List управляет данными и поведением
ListItem, передавая нужные функции.
Haunted поддерживает функциональную композицию, что позволяет строить сложные интерфейсы из маленьких, переиспользуемых компонентов. Основная идея — компоненты могут встраивать друг друга, передавать слоты, свойства и события.
function Modal({ open, onClose }) {
return html`
${open ? html`
<div class="overlay" @click=${onClose}>
<div class="modal" @click=${e => e.stopPropagation()}>
<slot></slot>
</div>
</div>` : ''}
`;
}
customElements.define('my-modal', component(Modal));
Использование:
<my-modal .open=${true} .onCl ose=${() => console.log('Закрыт')}>
<p>Содержимое модального окна</p>
</my-modal>
Композиция здесь реализуется через слот
<slot></slot> и управление состоянием родителя.
Вложенный контент остаётся реактивным, а модальное окно остаётся
полностью переиспользуемым.
Haunted строит интерфейс на реактивных свойствах и html
шаблонах. Важное свойство слотов: содержимое слота может быть
реактивным, если оно само использует Haunted шаблоны.
function Counter() {
const [count, setCount] = useState(0);
return html`
<div>
<button @click=${() => setCount(count - 1)}>-</button>
<span>Счёт: ${count}</span>
<button @click=${() => setCount(count + 1)}>+</button>
<slot></slot>
</div>
`;
}
customElements.define('my-counter', component(Counter));
Слот здесь может содержать любой реактивный компонент, который будет обновляться независимо, сохраняя синхронизацию с родителем.
Стили слотов могут быть контролируемыми через Shadow
DOM или глобальными. Haunted использует стандарт Shadow DOM,
поэтому стили из родителя не применяются к слоту напрямую, если не
использовать ::slotted():
::slotted(p) {
color: blue;
font-weight: bold;
}
Это позволяет точно управлять внешним видом вложенного контента, не нарушая инкапсуляцию компонента.
::slotted().Использование слотов в Haunted обеспечивает гибкую и модульную архитектуру, позволяя создавать высоко-переиспользуемые, динамические интерфейсы с минимальной связностью между компонентами.