# dom.js — декларативное создание DOM с реактивностью на сигналах Библиотека предоставляет текучий (fluent) API для создания DOM-элементов и реактивного управления ими через собственную систему сигналов. ## Установка ```html ``` ## Быстрый старт ```javascript // Создать кнопку с реактивным текстом const count = dom.signal(0); const button = dom.button('counter') .cls('btn btn-primary') .text(count) .on('click', () => count(count() + 1)) .done(); document.body.appendChild(button); ``` ## API ### Сигналы Сигнал — реактивный контейнер для значения. При изменении значения все подписчики автоматически уведомляются. ```javascript // Создание сигнала const name = dom.signal('John'); // Чтение значения console.log(name()); // 'John' console.log(name.value); // 'John' // Изменение значения name('Jane'); name.value = 'Jane'; // Подписка на изменения const unsubscribe = name.on((newVal, oldVal) => { console.log(`${oldVal} → ${newVal}`); }); // Отписка unsubscribe(); ``` #### Производные сигналы `.map()` Создаёт новый сигнал, значение которого автоматически обновляется при изменении родительского: ```javascript const count = dom.signal(1); const doubled = count.map(x => x * 2); console.log(doubled()); // 2 count(5); console.log(doubled()); // 10 ``` #### Управление памятью `.destroy()` Уничтожает сигнал, отписывая его от родителя и очищая всех слушателей: ```javascript doubled.destroy(); ``` #### Отладка `.chain()` и `.listenerCount()` ```javascript console.log(doubled.chain()); // 'signal_a1b2c → signal_a1b2c_map_1' console.log(count.listenerCount()); // количество подписчиков ``` ### Создание элементов Любой HTML-тег доступен как метод `dom`: ```javascript dom.div() //
dom.div('app') //
dom.section() //
dom.span() // ``` CamelCase автоматически преобразуется в kebab-case: ```javascript dom.viewBox() // ``` ### Инструкции После создания элемента конфигурация задаётся цепочкой инструкций. Все инструкции возвращают прокси элемента для продолжения цепочки. | Инструкция | Описание | Пример | |---|---|---| | `.id(id)` | Установить id | `.id('main')` | | `.cls(classes)` | Добавить CSS-классы | `.cls('btn primary')` | | `.atr(name, value)` | Установить атрибут | `.atr('href', '/home')` | | `.atrs({...})` | Установить несколько атрибутов | `.atrs({ href: '/', title: 'Home' })` | | `.css(styles)` | Добавить инлайн-стили | `.css('color: red;')` | | `.pcss(selector, styles)` | Scoped-стили | `.pcss(':hover', 'color: blue;')` | | `.mcss(query, styles)` | Media-запросы | `.mcss('(max-width: 768px)', 'font-size: 14px;')` | | `.text(content)` | Установить текстовое содержимое | `.text('Hello')` | | `.child(element)` | Добавить дочерний элемент | `.child(dom.span().done())` | | `.children([...])` | Добавить массив дочерних | `.children([el1, el2])` | **Важно:** внутри `.atrs()`, `.children()` и других методов, где значения могут быть сигналами, нужно передавать их явно: ```javascript // Передача сигнала в атрибуты const href = dom.signal('/home'); dom.a().atr('href', href).done(); ``` #### Scoped-стили: `pcss` Создаёт стили, привязанные только к этому элементу, с уникальным классом: ```javascript dom.button() .pcss(':hover', 'background: blue; color: white;') .pcss('::after', 'content: "→";') .done(); ``` Сгенерирует `