Веб-компонент
Веб-компонент — это нативный кастомный HTML-элемент, не привязанный к конкретному фреймворку. После регистрации он одинаково работает в любом фреймворке или в обычном HTML — без обёрточных библиотек.
Обычный веб-компонент
Для демонстрации давайте рассмотрим простейший нативный веб-компонент, оборачивающий Vanilla Calendar Pro. Создайте файл с именем VanillaCalendarElement.ts и скопируйте в него следующий код:
import { Calendar, type Options } from 'vanilla-calendar-pro';
import 'vanilla-calendar-pro/styles/index.css';
class VanillaCalendarElement extends HTMLElement {
calendar?: Calendar;
connectedCallback() {
const options: Options = {
onClickDate(self) {
console.log(self.context.selectedDates);
},
};
this.calendar = new Calendar(this, options);
this.calendar.init();
}
disconnectedCallback() {
if (this.calendar) this.calendar.destroy();
}
}
customElements.define('vanilla-calendar-element', VanillaCalendarElement);Кастомный элемент рендерит календарь прямо в свой собственный (light) DOM — никакой дополнительной настройки не требуется, а disconnectedCallback вызывает calendar.destroy(), поэтому календарь корректно очищает за собой ресурсы при удалении кастомного элемента со страницы.
После регистрации используйте кастомный элемент где угодно в вашем HTML, в любом фреймворке или вообще без него:
<vanilla-calendar-element></vanilla-calendar-element>Веб-компонент с Shadow DOM
Если вам нужна полная изоляция стилей и DOM — например, чтобы встроить календарь в компонент дизайн-системы так, чтобы его CSS не «протекал» наружу и не конфликтовал со стилями хост-страницы — можно вместо этого подключить Shadow DOM. Vanilla Calendar Pro полностью поддерживает инициализацию внутри Shadow DOM: всплывающее окно добавляется в правильный корневой узел, клики и фокус отслеживаются относительно границы shadow-дерева, а слушатель системной темы работает отдельно для каждого экземпляра. Никакая специальная опция для этого не требуется.
import { Calendar, type Options } from 'vanilla-calendar-pro';
class VanillaCalendarElement extends HTMLElement {
calendar?: Calendar;
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
// the calendar's own CSS has to be loaded inside the shadow root too, since
// styles in the outer document don't cross the shadow boundary
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://cdn.jsdelivr.net/npm/vanilla-calendar-pro/styles/index.css';
shadow.appendChild(link);
const container = document.createElement('div');
shadow.appendChild(container);
const options: Options = {
onClickDate(self) {
console.log(self.context.selectedDates);
},
};
// pass the element directly rather than a string selector: a string selector is
// resolved with document.querySelector, which can't reach inside a Shadow DOM
this.calendar = new Calendar(container, options);
this.calendar.init();
}
disconnectedCallback() {
if (this.calendar) this.calendar.destroy();
}
}
customElements.define('vanilla-calendar-element', VanillaCalendarElement);Несколько важных моментов:
- Стили календаря подключаются через элемент
<link>, добавленный прямо внутрь shadow root, поскольку стили, объявленные во внешнем документе, не пересекают границу shadow-дерева. - Контейнер передаётся в
new Calendar(...)как элемент, а не как строковый селектор: строковый селектор резолвится черезdocument.querySelector, который не может заглянуть внутрь Shadow DOM.