@alivecss/aliveui 1.0.2 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +591 -457
- package/dist/index.js +591 -457
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +591 -457
- package/dist/index.mjs.map +1 -1
- package/dist/runtime.js +61 -0
- package/dist/runtime.js.map +1 -1
- package/dist/runtime.mjs +61 -0
- package/dist/runtime.mjs.map +1 -1
- package/dist/vite.js +593 -457
- package/dist/vite.js.map +1 -1
- package/dist/vite.mjs +593 -457
- package/dist/vite.mjs.map +1 -1
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -211,12 +211,73 @@ function activateTab(container, panelId) {
|
|
|
211
211
|
activePanel?.classList.add("is-active");
|
|
212
212
|
activePanel?.setAttribute("aria-hidden", "false");
|
|
213
213
|
}
|
|
214
|
+
function wireScroll(el, root) {
|
|
215
|
+
const obs = new IntersectionObserver(
|
|
216
|
+
(entries) => {
|
|
217
|
+
entries.forEach((entry) => {
|
|
218
|
+
if (entry.isIntersecting) {
|
|
219
|
+
entry.target.classList.add("is-visible");
|
|
220
|
+
obs.unobserve(entry.target);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
{ threshold: 0.1, rootMargin: "0px 0px -40px 0px" }
|
|
225
|
+
);
|
|
226
|
+
obs.observe(el);
|
|
227
|
+
registerCleanup(root, () => obs.disconnect());
|
|
228
|
+
}
|
|
229
|
+
function wireStagger(container) {
|
|
230
|
+
const children = [...container.children];
|
|
231
|
+
children.forEach((child, i) => {
|
|
232
|
+
child.style.setProperty("--alive-index", String(i));
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
function wireTilt(el, root) {
|
|
236
|
+
const htmlEl = el;
|
|
237
|
+
const STRENGTH = 8;
|
|
238
|
+
const moveCleanup = addListener(htmlEl, "mousemove", (e) => {
|
|
239
|
+
const rect = htmlEl.getBoundingClientRect();
|
|
240
|
+
const x = (e.clientX - rect.left) / rect.width - 0.5;
|
|
241
|
+
const y = (e.clientY - rect.top) / rect.height - 0.5;
|
|
242
|
+
htmlEl.style.transform = `perspective(800px) rotateY(${x * STRENGTH}deg) rotateX(${-y * STRENGTH}deg) scale(1.01)`;
|
|
243
|
+
htmlEl.style.transition = "transform 0.1s ease-out";
|
|
244
|
+
});
|
|
245
|
+
const leaveCleanup = addListener(htmlEl, "mouseleave", () => {
|
|
246
|
+
htmlEl.style.transform = "";
|
|
247
|
+
htmlEl.style.transition = "transform 0.4s cubic-bezier(0.16, 1, 0.3, 1)";
|
|
248
|
+
});
|
|
249
|
+
registerCleanup(root, moveCleanup);
|
|
250
|
+
registerCleanup(root, leaveCleanup);
|
|
251
|
+
}
|
|
252
|
+
function wireMagnetic(el, root) {
|
|
253
|
+
const htmlEl = el;
|
|
254
|
+
const PULL = 0.35;
|
|
255
|
+
const moveCleanup = addListener(htmlEl, "mousemove", (e) => {
|
|
256
|
+
const rect = htmlEl.getBoundingClientRect();
|
|
257
|
+
const cx = rect.left + rect.width / 2;
|
|
258
|
+
const cy = rect.top + rect.height / 2;
|
|
259
|
+
const dx = (e.clientX - cx) * PULL;
|
|
260
|
+
const dy = (e.clientY - cy) * PULL;
|
|
261
|
+
htmlEl.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
262
|
+
htmlEl.style.transition = "transform 0.2s cubic-bezier(0.16, 1, 0.3, 1)";
|
|
263
|
+
});
|
|
264
|
+
const leaveCleanup = addListener(htmlEl, "mouseleave", () => {
|
|
265
|
+
htmlEl.style.transform = "";
|
|
266
|
+
htmlEl.style.transition = "transform 0.5s cubic-bezier(0.16, 1, 0.3, 1)";
|
|
267
|
+
});
|
|
268
|
+
registerCleanup(root, moveCleanup);
|
|
269
|
+
registerCleanup(root, leaveCleanup);
|
|
270
|
+
}
|
|
214
271
|
function init(root = document.documentElement) {
|
|
215
272
|
root.querySelectorAll("[data-alive-accordion]").forEach((el) => wireAccordion(el, root));
|
|
216
273
|
root.querySelectorAll("[data-alive-modal]").forEach((el) => wireModal(el, root));
|
|
217
274
|
root.querySelectorAll("[data-alive-drawer]").forEach((el) => wireDrawer(el, root));
|
|
218
275
|
root.querySelectorAll("[data-alive-dropdown]").forEach((el) => wireDropdown(el, root));
|
|
219
276
|
root.querySelectorAll("[data-alive-tabs]").forEach((el) => wireTabs(el, root));
|
|
277
|
+
root.querySelectorAll("[data-alive-scroll]").forEach((el) => wireScroll(el, root));
|
|
278
|
+
root.querySelectorAll("[data-alive-stagger]").forEach((el) => wireStagger(el));
|
|
279
|
+
root.querySelectorAll("[data-alive-tilt]").forEach((el) => wireTilt(el, root));
|
|
280
|
+
root.querySelectorAll("[data-alive-magnetic]").forEach((el) => wireMagnetic(el, root));
|
|
220
281
|
}
|
|
221
282
|
function destroy(root = document.documentElement) {
|
|
222
283
|
const cleanups = cleanupRegistry.get(root) ?? [];
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * AliveUI Runtime\n *\n * Provides data-attribute-driven interactivity for accordion, modal, drawer,\n * dropdown, and tabs components — zero dependencies, ~2 KB.\n *\n * Usage:\n * import { init } from '@alivecss/aliveui/runtime'\n * init() // wire everything in document\n * init(myRootElement) // wire a subtree\n */\n\ntype Cleanup = () => void\n\nconst cleanupRegistry = new WeakMap<Element, Cleanup[]>()\n\nfunction registerCleanup(root: Element, fn: Cleanup): void {\n const existing = cleanupRegistry.get(root) ?? []\n existing.push(fn)\n cleanupRegistry.set(root, existing)\n}\n\nfunction addListener<K extends keyof HTMLElementEventMap>(\n el: EventTarget,\n type: K,\n handler: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n): Cleanup {\n el.addEventListener(type, handler as EventListener, options)\n return () => el.removeEventListener(type, handler as EventListener, options)\n}\n\n// ── Accordion ─────────────────────────────────────────────────────────────────\n\nfunction wireAccordion(container: Element, root: Element): void {\n const triggers = container.querySelectorAll<HTMLElement>('[data-alive-trigger]')\n triggers.forEach(trigger => {\n const item = trigger.closest('[data-alive-accordion-item]') ?? trigger.parentElement\n if (!item) return\n\n const cleanup = addListener(trigger, 'click', () => {\n const isOpen = item.classList.contains('is-open')\n // Close all items if not multi-open\n if (!container.hasAttribute('data-alive-multi')) {\n container.querySelectorAll('[data-alive-accordion-item]').forEach(i => {\n i.classList.remove('is-open')\n i.querySelector('[data-alive-trigger]')?.setAttribute('aria-expanded', 'false')\n })\n }\n if (!isOpen) {\n item.classList.add('is-open')\n trigger.setAttribute('aria-expanded', 'true')\n }\n })\n registerCleanup(root, cleanup)\n })\n}\n\n// ── Modal ─────────────────────────────────────────────────────────────────────\n\nfunction wireModal(modal: Element, root: Element): void {\n const id = modal.getAttribute('data-alive-modal')\n if (!id) return\n\n // Open triggers\n const openers = document.querySelectorAll<HTMLElement>(`[data-alive-open=\"${id}\"]`)\n openers.forEach(opener => {\n const cleanup = addListener(opener, 'click', () => openModal(id))\n registerCleanup(root, cleanup)\n })\n\n // Close triggers inside the modal\n modal.querySelectorAll<HTMLElement>('[data-alive-close]').forEach(closer => {\n const cleanup = addListener(closer, 'click', () => closeModal(id))\n registerCleanup(root, cleanup)\n })\n\n // Backdrop click to close (click on the modal wrapper, not the content)\n const cleanup = addListener(modal, 'click', (e) => {\n if (e.target === modal) closeModal(id)\n })\n registerCleanup(root, cleanup)\n\n // ESC key\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && modal.classList.contains('is-open')) closeModal(id)\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function openModal(id: string): void {\n const modal = document.querySelector<HTMLElement>(`[data-alive-modal=\"${id}\"]`)\n if (!modal) return\n modal.classList.add('is-open')\n modal.setAttribute('aria-hidden', 'false')\n document.body.style.overflow = 'hidden'\n // Focus first focusable element\n const focusable = modal.querySelector<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n )\n focusable?.focus()\n}\n\nexport function closeModal(id: string): void {\n const modal = document.querySelector<HTMLElement>(`[data-alive-modal=\"${id}\"]`)\n if (!modal) return\n modal.classList.remove('is-open')\n modal.setAttribute('aria-hidden', 'true')\n document.body.style.overflow = ''\n}\n\n// ── Drawer ────────────────────────────────────────────────────────────────────\n\nfunction wireDrawer(drawer: Element, root: Element): void {\n const id = drawer.getAttribute('data-alive-drawer')\n if (!id) return\n\n const openers = document.querySelectorAll<HTMLElement>(`[data-alive-open=\"${id}\"]`)\n openers.forEach(opener => {\n const cleanup = addListener(opener, 'click', () => openDrawer(id))\n registerCleanup(root, cleanup)\n })\n\n drawer.querySelectorAll<HTMLElement>('[data-alive-close]').forEach(closer => {\n const cleanup = addListener(closer, 'click', () => closeDrawer(id))\n registerCleanup(root, cleanup)\n })\n\n const backdropCleanup = addListener(drawer, 'click', (e) => {\n if (e.target === drawer) closeDrawer(id)\n })\n registerCleanup(root, backdropCleanup)\n\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && drawer.classList.contains('is-open')) closeDrawer(id)\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function openDrawer(id: string): void {\n const drawer = document.querySelector<HTMLElement>(`[data-alive-drawer=\"${id}\"]`)\n if (!drawer) return\n drawer.classList.add('is-open')\n drawer.setAttribute('aria-hidden', 'false')\n document.body.style.overflow = 'hidden'\n}\n\nexport function closeDrawer(id: string): void {\n const drawer = document.querySelector<HTMLElement>(`[data-alive-drawer=\"${id}\"]`)\n if (!drawer) return\n drawer.classList.remove('is-open')\n drawer.setAttribute('aria-hidden', 'true')\n document.body.style.overflow = ''\n}\n\n// ── Dropdown ──────────────────────────────────────────────────────────────────\n\nfunction wireDropdown(container: Element, root: Element): void {\n const trigger = container.querySelector<HTMLElement>('[data-alive-trigger]')\n const menu = container.querySelector<HTMLElement>('[data-alive-dropdown-menu]')\n if (!trigger || !menu) return\n\n const toggleCleanup = addListener(trigger, 'click', (e) => {\n e.stopPropagation()\n toggleDropdown(container)\n })\n registerCleanup(root, toggleCleanup)\n\n // Close on outside click\n const outsideCleanup = addListener(document, 'click', (e) => {\n if (!container.contains(e.target as Node)) {\n menu.classList.remove('is-open')\n trigger.setAttribute('aria-expanded', 'false')\n }\n })\n registerCleanup(root, outsideCleanup)\n\n // Close on ESC\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && menu.classList.contains('is-open')) {\n menu.classList.remove('is-open')\n trigger.setAttribute('aria-expanded', 'false')\n trigger.focus()\n }\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function toggleDropdown(container: Element): void {\n const trigger = container.querySelector<HTMLElement>('[data-alive-trigger]')\n const menu = container.querySelector<HTMLElement>('[data-alive-dropdown-menu]')\n if (!menu) return\n const isOpen = menu.classList.toggle('is-open')\n trigger?.setAttribute('aria-expanded', String(isOpen))\n if (isOpen) {\n // Focus first item\n menu.querySelector<HTMLElement>('[role=\"menuitem\"], button, a')?.focus()\n }\n}\n\n// ── Tabs ──────────────────────────────────────────────────────────────────────\n\nfunction wireTabs(container: Element, root: Element): void {\n const tabs = container.querySelectorAll<HTMLElement>('[data-alive-tab]')\n tabs.forEach(tab => {\n const cleanup = addListener(tab, 'click', () => {\n const panelId = tab.getAttribute('data-alive-tab')\n if (!panelId) return\n activateTab(container, panelId)\n })\n registerCleanup(root, cleanup)\n\n // Keyboard navigation\n const keyCleanup = addListener(tab, 'keydown', (e) => {\n const allTabs = [...container.querySelectorAll<HTMLElement>('[data-alive-tab]')]\n const idx = allTabs.indexOf(tab)\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n e.preventDefault()\n allTabs[(idx + 1) % allTabs.length]?.focus()\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n e.preventDefault()\n allTabs[(idx - 1 + allTabs.length) % allTabs.length]?.focus()\n }\n })\n registerCleanup(root, keyCleanup)\n })\n}\n\nfunction activateTab(container: Element, panelId: string): void {\n // Deactivate all tabs + panels within this tabs container\n container.querySelectorAll('[data-alive-tab]').forEach(t => {\n t.classList.remove('is-active')\n t.setAttribute('aria-selected', 'false')\n t.setAttribute('tabindex', '-1')\n })\n\n // Find panels — look in the container, then in the whole document\n const searchRoot = container.closest('[data-alive-tabs-panels]') ?? document\n searchRoot.querySelectorAll('[data-alive-panel]').forEach(p => {\n p.classList.remove('is-active')\n p.setAttribute('aria-hidden', 'true')\n })\n\n // Activate the selected tab\n const activeTab = container.querySelector<HTMLElement>(`[data-alive-tab=\"${panelId}\"]`)\n activeTab?.classList.add('is-active')\n activeTab?.setAttribute('aria-selected', 'true')\n activeTab?.setAttribute('tabindex', '0')\n\n // Activate the matching panel\n const activePanel = searchRoot.querySelector<HTMLElement>(`[data-alive-panel=\"${panelId}\"]`)\n activePanel?.classList.add('is-active')\n activePanel?.setAttribute('aria-hidden', 'false')\n}\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Wire all data-alive-* components within `root` (defaults to document.documentElement).\n * Safe to call multiple times — existing listeners are tracked and replaced on `destroy()`.\n */\nexport function init(root: Element = document.documentElement): void {\n // Accordion\n root.querySelectorAll('[data-alive-accordion]').forEach(el => wireAccordion(el, root))\n\n // Modal\n root.querySelectorAll('[data-alive-modal]').forEach(el => wireModal(el, root))\n\n // Drawer\n root.querySelectorAll('[data-alive-drawer]').forEach(el => wireDrawer(el, root))\n\n // Dropdown\n root.querySelectorAll('[data-alive-dropdown]').forEach(el => wireDropdown(el, root))\n\n // Tabs\n root.querySelectorAll('[data-alive-tabs]').forEach(el => wireTabs(el, root))\n}\n\n/**\n * Remove all event listeners registered by `init(root)`.\n */\nexport function destroy(root: Element = document.documentElement): void {\n const cleanups = cleanupRegistry.get(root) ?? []\n cleanups.forEach(fn => fn())\n cleanupRegistry.delete(root)\n}\n"],"mappings":"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","names":["cleanup"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * AliveUI Runtime\n *\n * Provides data-attribute-driven interactivity for accordion, modal, drawer,\n * dropdown, and tabs components — zero dependencies, ~2 KB.\n *\n * Usage:\n * import { init } from '@alivecss/aliveui/runtime'\n * init() // wire everything in document\n * init(myRootElement) // wire a subtree\n */\n\ntype Cleanup = () => void\n\nconst cleanupRegistry = new WeakMap<Element, Cleanup[]>()\n\nfunction registerCleanup(root: Element, fn: Cleanup): void {\n const existing = cleanupRegistry.get(root) ?? []\n existing.push(fn)\n cleanupRegistry.set(root, existing)\n}\n\nfunction addListener<K extends keyof HTMLElementEventMap>(\n el: EventTarget,\n type: K,\n handler: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n): Cleanup {\n el.addEventListener(type, handler as EventListener, options)\n return () => el.removeEventListener(type, handler as EventListener, options)\n}\n\n// ── Accordion ─────────────────────────────────────────────────────────────────\n\nfunction wireAccordion(container: Element, root: Element): void {\n const triggers = container.querySelectorAll<HTMLElement>('[data-alive-trigger]')\n triggers.forEach(trigger => {\n const item = trigger.closest('[data-alive-accordion-item]') ?? trigger.parentElement\n if (!item) return\n\n const cleanup = addListener(trigger, 'click', () => {\n const isOpen = item.classList.contains('is-open')\n // Close all items if not multi-open\n if (!container.hasAttribute('data-alive-multi')) {\n container.querySelectorAll('[data-alive-accordion-item]').forEach(i => {\n i.classList.remove('is-open')\n i.querySelector('[data-alive-trigger]')?.setAttribute('aria-expanded', 'false')\n })\n }\n if (!isOpen) {\n item.classList.add('is-open')\n trigger.setAttribute('aria-expanded', 'true')\n }\n })\n registerCleanup(root, cleanup)\n })\n}\n\n// ── Modal ─────────────────────────────────────────────────────────────────────\n\nfunction wireModal(modal: Element, root: Element): void {\n const id = modal.getAttribute('data-alive-modal')\n if (!id) return\n\n // Open triggers\n const openers = document.querySelectorAll<HTMLElement>(`[data-alive-open=\"${id}\"]`)\n openers.forEach(opener => {\n const cleanup = addListener(opener, 'click', () => openModal(id))\n registerCleanup(root, cleanup)\n })\n\n // Close triggers inside the modal\n modal.querySelectorAll<HTMLElement>('[data-alive-close]').forEach(closer => {\n const cleanup = addListener(closer, 'click', () => closeModal(id))\n registerCleanup(root, cleanup)\n })\n\n // Backdrop click to close (click on the modal wrapper, not the content)\n const cleanup = addListener(modal, 'click', (e) => {\n if (e.target === modal) closeModal(id)\n })\n registerCleanup(root, cleanup)\n\n // ESC key\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && modal.classList.contains('is-open')) closeModal(id)\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function openModal(id: string): void {\n const modal = document.querySelector<HTMLElement>(`[data-alive-modal=\"${id}\"]`)\n if (!modal) return\n modal.classList.add('is-open')\n modal.setAttribute('aria-hidden', 'false')\n document.body.style.overflow = 'hidden'\n // Focus first focusable element\n const focusable = modal.querySelector<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n )\n focusable?.focus()\n}\n\nexport function closeModal(id: string): void {\n const modal = document.querySelector<HTMLElement>(`[data-alive-modal=\"${id}\"]`)\n if (!modal) return\n modal.classList.remove('is-open')\n modal.setAttribute('aria-hidden', 'true')\n document.body.style.overflow = ''\n}\n\n// ── Drawer ────────────────────────────────────────────────────────────────────\n\nfunction wireDrawer(drawer: Element, root: Element): void {\n const id = drawer.getAttribute('data-alive-drawer')\n if (!id) return\n\n const openers = document.querySelectorAll<HTMLElement>(`[data-alive-open=\"${id}\"]`)\n openers.forEach(opener => {\n const cleanup = addListener(opener, 'click', () => openDrawer(id))\n registerCleanup(root, cleanup)\n })\n\n drawer.querySelectorAll<HTMLElement>('[data-alive-close]').forEach(closer => {\n const cleanup = addListener(closer, 'click', () => closeDrawer(id))\n registerCleanup(root, cleanup)\n })\n\n const backdropCleanup = addListener(drawer, 'click', (e) => {\n if (e.target === drawer) closeDrawer(id)\n })\n registerCleanup(root, backdropCleanup)\n\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && drawer.classList.contains('is-open')) closeDrawer(id)\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function openDrawer(id: string): void {\n const drawer = document.querySelector<HTMLElement>(`[data-alive-drawer=\"${id}\"]`)\n if (!drawer) return\n drawer.classList.add('is-open')\n drawer.setAttribute('aria-hidden', 'false')\n document.body.style.overflow = 'hidden'\n}\n\nexport function closeDrawer(id: string): void {\n const drawer = document.querySelector<HTMLElement>(`[data-alive-drawer=\"${id}\"]`)\n if (!drawer) return\n drawer.classList.remove('is-open')\n drawer.setAttribute('aria-hidden', 'true')\n document.body.style.overflow = ''\n}\n\n// ── Dropdown ──────────────────────────────────────────────────────────────────\n\nfunction wireDropdown(container: Element, root: Element): void {\n const trigger = container.querySelector<HTMLElement>('[data-alive-trigger]')\n const menu = container.querySelector<HTMLElement>('[data-alive-dropdown-menu]')\n if (!trigger || !menu) return\n\n const toggleCleanup = addListener(trigger, 'click', (e) => {\n e.stopPropagation()\n toggleDropdown(container)\n })\n registerCleanup(root, toggleCleanup)\n\n // Close on outside click\n const outsideCleanup = addListener(document, 'click', (e) => {\n if (!container.contains(e.target as Node)) {\n menu.classList.remove('is-open')\n trigger.setAttribute('aria-expanded', 'false')\n }\n })\n registerCleanup(root, outsideCleanup)\n\n // Close on ESC\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && menu.classList.contains('is-open')) {\n menu.classList.remove('is-open')\n trigger.setAttribute('aria-expanded', 'false')\n trigger.focus()\n }\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function toggleDropdown(container: Element): void {\n const trigger = container.querySelector<HTMLElement>('[data-alive-trigger]')\n const menu = container.querySelector<HTMLElement>('[data-alive-dropdown-menu]')\n if (!menu) return\n const isOpen = menu.classList.toggle('is-open')\n trigger?.setAttribute('aria-expanded', String(isOpen))\n if (isOpen) {\n // Focus first item\n menu.querySelector<HTMLElement>('[role=\"menuitem\"], button, a')?.focus()\n }\n}\n\n// ── Tabs ──────────────────────────────────────────────────────────────────────\n\nfunction wireTabs(container: Element, root: Element): void {\n const tabs = container.querySelectorAll<HTMLElement>('[data-alive-tab]')\n tabs.forEach(tab => {\n const cleanup = addListener(tab, 'click', () => {\n const panelId = tab.getAttribute('data-alive-tab')\n if (!panelId) return\n activateTab(container, panelId)\n })\n registerCleanup(root, cleanup)\n\n // Keyboard navigation\n const keyCleanup = addListener(tab, 'keydown', (e) => {\n const allTabs = [...container.querySelectorAll<HTMLElement>('[data-alive-tab]')]\n const idx = allTabs.indexOf(tab)\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n e.preventDefault()\n allTabs[(idx + 1) % allTabs.length]?.focus()\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n e.preventDefault()\n allTabs[(idx - 1 + allTabs.length) % allTabs.length]?.focus()\n }\n })\n registerCleanup(root, keyCleanup)\n })\n}\n\nfunction activateTab(container: Element, panelId: string): void {\n // Deactivate all tabs + panels within this tabs container\n container.querySelectorAll('[data-alive-tab]').forEach(t => {\n t.classList.remove('is-active')\n t.setAttribute('aria-selected', 'false')\n t.setAttribute('tabindex', '-1')\n })\n\n // Find panels — look in the container, then in the whole document\n const searchRoot = container.closest('[data-alive-tabs-panels]') ?? document\n searchRoot.querySelectorAll('[data-alive-panel]').forEach(p => {\n p.classList.remove('is-active')\n p.setAttribute('aria-hidden', 'true')\n })\n\n // Activate the selected tab\n const activeTab = container.querySelector<HTMLElement>(`[data-alive-tab=\"${panelId}\"]`)\n activeTab?.classList.add('is-active')\n activeTab?.setAttribute('aria-selected', 'true')\n activeTab?.setAttribute('tabindex', '0')\n\n // Activate the matching panel\n const activePanel = searchRoot.querySelector<HTMLElement>(`[data-alive-panel=\"${panelId}\"]`)\n activePanel?.classList.add('is-active')\n activePanel?.setAttribute('aria-hidden', 'false')\n}\n\n// ── Scroll Reveal ──────────────────────────────────────────────────────────────\n\nfunction wireScroll(el: Element, root: Element): void {\n const obs = new IntersectionObserver(\n (entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n entry.target.classList.add('is-visible')\n obs.unobserve(entry.target)\n }\n })\n },\n { threshold: 0.1, rootMargin: '0px 0px -40px 0px' },\n )\n obs.observe(el)\n registerCleanup(root, () => obs.disconnect())\n}\n\n// ── Auto-Stagger ───────────────────────────────────────────────────────────────\n\nfunction wireStagger(container: Element): void {\n const children = [...container.children] as HTMLElement[]\n children.forEach((child, i) => {\n child.style.setProperty('--alive-index', String(i))\n })\n}\n\n// ── 3D Tilt ────────────────────────────────────────────────────────────────────\n\nfunction wireTilt(el: Element, root: Element): void {\n const htmlEl = el as HTMLElement\n const STRENGTH = 8\n\n const moveCleanup = addListener(htmlEl, 'mousemove', (e) => {\n const rect = htmlEl.getBoundingClientRect()\n const x = (e.clientX - rect.left) / rect.width - 0.5\n const y = (e.clientY - rect.top) / rect.height - 0.5\n htmlEl.style.transform = `perspective(800px) rotateY(${x * STRENGTH}deg) rotateX(${-y * STRENGTH}deg) scale(1.01)`\n htmlEl.style.transition = 'transform 0.1s ease-out'\n })\n\n const leaveCleanup = addListener(htmlEl, 'mouseleave', () => {\n htmlEl.style.transform = ''\n htmlEl.style.transition = 'transform 0.4s cubic-bezier(0.16, 1, 0.3, 1)'\n })\n\n registerCleanup(root, moveCleanup)\n registerCleanup(root, leaveCleanup)\n}\n\n// ── Magnetic Follow ────────────────────────────────────────────────────────────\n\nfunction wireMagnetic(el: Element, root: Element): void {\n const htmlEl = el as HTMLElement\n const PULL = 0.35\n\n const moveCleanup = addListener(htmlEl, 'mousemove', (e) => {\n const rect = htmlEl.getBoundingClientRect()\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n const dx = (e.clientX - cx) * PULL\n const dy = (e.clientY - cy) * PULL\n htmlEl.style.transform = `translate(${dx}px, ${dy}px)`\n htmlEl.style.transition = 'transform 0.2s cubic-bezier(0.16, 1, 0.3, 1)'\n })\n\n const leaveCleanup = addListener(htmlEl, 'mouseleave', () => {\n htmlEl.style.transform = ''\n htmlEl.style.transition = 'transform 0.5s cubic-bezier(0.16, 1, 0.3, 1)'\n })\n\n registerCleanup(root, moveCleanup)\n registerCleanup(root, leaveCleanup)\n}\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Wire all data-alive-* components within `root` (defaults to document.documentElement).\n * Safe to call multiple times — existing listeners are tracked and replaced on `destroy()`.\n */\nexport function init(root: Element = document.documentElement): void {\n // Accordion\n root.querySelectorAll('[data-alive-accordion]').forEach(el => wireAccordion(el, root))\n\n // Modal\n root.querySelectorAll('[data-alive-modal]').forEach(el => wireModal(el, root))\n\n // Drawer\n root.querySelectorAll('[data-alive-drawer]').forEach(el => wireDrawer(el, root))\n\n // Dropdown\n root.querySelectorAll('[data-alive-dropdown]').forEach(el => wireDropdown(el, root))\n\n // Tabs\n root.querySelectorAll('[data-alive-tabs]').forEach(el => wireTabs(el, root))\n\n // Scroll reveal\n root.querySelectorAll('[data-alive-scroll]').forEach(el => wireScroll(el, root))\n\n // Auto-stagger children\n root.querySelectorAll('[data-alive-stagger]').forEach(el => wireStagger(el))\n\n // 3D tilt\n root.querySelectorAll('[data-alive-tilt]').forEach(el => wireTilt(el, root))\n\n // Magnetic follow\n root.querySelectorAll('[data-alive-magnetic]').forEach(el => wireMagnetic(el, root))\n}\n\n/**\n * Remove all event listeners registered by `init(root)`.\n */\nexport function destroy(root: Element = document.documentElement): void {\n const cleanups = cleanupRegistry.get(root) ?? []\n cleanups.forEach(fn => fn())\n cleanupRegistry.delete(root)\n}\n"],"mappings":"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","names":["cleanup"]}
|
package/dist/runtime.mjs
CHANGED
|
@@ -181,12 +181,73 @@ function activateTab(container, panelId) {
|
|
|
181
181
|
activePanel?.classList.add("is-active");
|
|
182
182
|
activePanel?.setAttribute("aria-hidden", "false");
|
|
183
183
|
}
|
|
184
|
+
function wireScroll(el, root) {
|
|
185
|
+
const obs = new IntersectionObserver(
|
|
186
|
+
(entries) => {
|
|
187
|
+
entries.forEach((entry) => {
|
|
188
|
+
if (entry.isIntersecting) {
|
|
189
|
+
entry.target.classList.add("is-visible");
|
|
190
|
+
obs.unobserve(entry.target);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
{ threshold: 0.1, rootMargin: "0px 0px -40px 0px" }
|
|
195
|
+
);
|
|
196
|
+
obs.observe(el);
|
|
197
|
+
registerCleanup(root, () => obs.disconnect());
|
|
198
|
+
}
|
|
199
|
+
function wireStagger(container) {
|
|
200
|
+
const children = [...container.children];
|
|
201
|
+
children.forEach((child, i) => {
|
|
202
|
+
child.style.setProperty("--alive-index", String(i));
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function wireTilt(el, root) {
|
|
206
|
+
const htmlEl = el;
|
|
207
|
+
const STRENGTH = 8;
|
|
208
|
+
const moveCleanup = addListener(htmlEl, "mousemove", (e) => {
|
|
209
|
+
const rect = htmlEl.getBoundingClientRect();
|
|
210
|
+
const x = (e.clientX - rect.left) / rect.width - 0.5;
|
|
211
|
+
const y = (e.clientY - rect.top) / rect.height - 0.5;
|
|
212
|
+
htmlEl.style.transform = `perspective(800px) rotateY(${x * STRENGTH}deg) rotateX(${-y * STRENGTH}deg) scale(1.01)`;
|
|
213
|
+
htmlEl.style.transition = "transform 0.1s ease-out";
|
|
214
|
+
});
|
|
215
|
+
const leaveCleanup = addListener(htmlEl, "mouseleave", () => {
|
|
216
|
+
htmlEl.style.transform = "";
|
|
217
|
+
htmlEl.style.transition = "transform 0.4s cubic-bezier(0.16, 1, 0.3, 1)";
|
|
218
|
+
});
|
|
219
|
+
registerCleanup(root, moveCleanup);
|
|
220
|
+
registerCleanup(root, leaveCleanup);
|
|
221
|
+
}
|
|
222
|
+
function wireMagnetic(el, root) {
|
|
223
|
+
const htmlEl = el;
|
|
224
|
+
const PULL = 0.35;
|
|
225
|
+
const moveCleanup = addListener(htmlEl, "mousemove", (e) => {
|
|
226
|
+
const rect = htmlEl.getBoundingClientRect();
|
|
227
|
+
const cx = rect.left + rect.width / 2;
|
|
228
|
+
const cy = rect.top + rect.height / 2;
|
|
229
|
+
const dx = (e.clientX - cx) * PULL;
|
|
230
|
+
const dy = (e.clientY - cy) * PULL;
|
|
231
|
+
htmlEl.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
232
|
+
htmlEl.style.transition = "transform 0.2s cubic-bezier(0.16, 1, 0.3, 1)";
|
|
233
|
+
});
|
|
234
|
+
const leaveCleanup = addListener(htmlEl, "mouseleave", () => {
|
|
235
|
+
htmlEl.style.transform = "";
|
|
236
|
+
htmlEl.style.transition = "transform 0.5s cubic-bezier(0.16, 1, 0.3, 1)";
|
|
237
|
+
});
|
|
238
|
+
registerCleanup(root, moveCleanup);
|
|
239
|
+
registerCleanup(root, leaveCleanup);
|
|
240
|
+
}
|
|
184
241
|
function init(root = document.documentElement) {
|
|
185
242
|
root.querySelectorAll("[data-alive-accordion]").forEach((el) => wireAccordion(el, root));
|
|
186
243
|
root.querySelectorAll("[data-alive-modal]").forEach((el) => wireModal(el, root));
|
|
187
244
|
root.querySelectorAll("[data-alive-drawer]").forEach((el) => wireDrawer(el, root));
|
|
188
245
|
root.querySelectorAll("[data-alive-dropdown]").forEach((el) => wireDropdown(el, root));
|
|
189
246
|
root.querySelectorAll("[data-alive-tabs]").forEach((el) => wireTabs(el, root));
|
|
247
|
+
root.querySelectorAll("[data-alive-scroll]").forEach((el) => wireScroll(el, root));
|
|
248
|
+
root.querySelectorAll("[data-alive-stagger]").forEach((el) => wireStagger(el));
|
|
249
|
+
root.querySelectorAll("[data-alive-tilt]").forEach((el) => wireTilt(el, root));
|
|
250
|
+
root.querySelectorAll("[data-alive-magnetic]").forEach((el) => wireMagnetic(el, root));
|
|
190
251
|
}
|
|
191
252
|
function destroy(root = document.documentElement) {
|
|
192
253
|
const cleanups = cleanupRegistry.get(root) ?? [];
|
package/dist/runtime.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * AliveUI Runtime\n *\n * Provides data-attribute-driven interactivity for accordion, modal, drawer,\n * dropdown, and tabs components — zero dependencies, ~2 KB.\n *\n * Usage:\n * import { init } from '@alivecss/aliveui/runtime'\n * init() // wire everything in document\n * init(myRootElement) // wire a subtree\n */\n\ntype Cleanup = () => void\n\nconst cleanupRegistry = new WeakMap<Element, Cleanup[]>()\n\nfunction registerCleanup(root: Element, fn: Cleanup): void {\n const existing = cleanupRegistry.get(root) ?? []\n existing.push(fn)\n cleanupRegistry.set(root, existing)\n}\n\nfunction addListener<K extends keyof HTMLElementEventMap>(\n el: EventTarget,\n type: K,\n handler: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n): Cleanup {\n el.addEventListener(type, handler as EventListener, options)\n return () => el.removeEventListener(type, handler as EventListener, options)\n}\n\n// ── Accordion ─────────────────────────────────────────────────────────────────\n\nfunction wireAccordion(container: Element, root: Element): void {\n const triggers = container.querySelectorAll<HTMLElement>('[data-alive-trigger]')\n triggers.forEach(trigger => {\n const item = trigger.closest('[data-alive-accordion-item]') ?? trigger.parentElement\n if (!item) return\n\n const cleanup = addListener(trigger, 'click', () => {\n const isOpen = item.classList.contains('is-open')\n // Close all items if not multi-open\n if (!container.hasAttribute('data-alive-multi')) {\n container.querySelectorAll('[data-alive-accordion-item]').forEach(i => {\n i.classList.remove('is-open')\n i.querySelector('[data-alive-trigger]')?.setAttribute('aria-expanded', 'false')\n })\n }\n if (!isOpen) {\n item.classList.add('is-open')\n trigger.setAttribute('aria-expanded', 'true')\n }\n })\n registerCleanup(root, cleanup)\n })\n}\n\n// ── Modal ─────────────────────────────────────────────────────────────────────\n\nfunction wireModal(modal: Element, root: Element): void {\n const id = modal.getAttribute('data-alive-modal')\n if (!id) return\n\n // Open triggers\n const openers = document.querySelectorAll<HTMLElement>(`[data-alive-open=\"${id}\"]`)\n openers.forEach(opener => {\n const cleanup = addListener(opener, 'click', () => openModal(id))\n registerCleanup(root, cleanup)\n })\n\n // Close triggers inside the modal\n modal.querySelectorAll<HTMLElement>('[data-alive-close]').forEach(closer => {\n const cleanup = addListener(closer, 'click', () => closeModal(id))\n registerCleanup(root, cleanup)\n })\n\n // Backdrop click to close (click on the modal wrapper, not the content)\n const cleanup = addListener(modal, 'click', (e) => {\n if (e.target === modal) closeModal(id)\n })\n registerCleanup(root, cleanup)\n\n // ESC key\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && modal.classList.contains('is-open')) closeModal(id)\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function openModal(id: string): void {\n const modal = document.querySelector<HTMLElement>(`[data-alive-modal=\"${id}\"]`)\n if (!modal) return\n modal.classList.add('is-open')\n modal.setAttribute('aria-hidden', 'false')\n document.body.style.overflow = 'hidden'\n // Focus first focusable element\n const focusable = modal.querySelector<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n )\n focusable?.focus()\n}\n\nexport function closeModal(id: string): void {\n const modal = document.querySelector<HTMLElement>(`[data-alive-modal=\"${id}\"]`)\n if (!modal) return\n modal.classList.remove('is-open')\n modal.setAttribute('aria-hidden', 'true')\n document.body.style.overflow = ''\n}\n\n// ── Drawer ────────────────────────────────────────────────────────────────────\n\nfunction wireDrawer(drawer: Element, root: Element): void {\n const id = drawer.getAttribute('data-alive-drawer')\n if (!id) return\n\n const openers = document.querySelectorAll<HTMLElement>(`[data-alive-open=\"${id}\"]`)\n openers.forEach(opener => {\n const cleanup = addListener(opener, 'click', () => openDrawer(id))\n registerCleanup(root, cleanup)\n })\n\n drawer.querySelectorAll<HTMLElement>('[data-alive-close]').forEach(closer => {\n const cleanup = addListener(closer, 'click', () => closeDrawer(id))\n registerCleanup(root, cleanup)\n })\n\n const backdropCleanup = addListener(drawer, 'click', (e) => {\n if (e.target === drawer) closeDrawer(id)\n })\n registerCleanup(root, backdropCleanup)\n\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && drawer.classList.contains('is-open')) closeDrawer(id)\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function openDrawer(id: string): void {\n const drawer = document.querySelector<HTMLElement>(`[data-alive-drawer=\"${id}\"]`)\n if (!drawer) return\n drawer.classList.add('is-open')\n drawer.setAttribute('aria-hidden', 'false')\n document.body.style.overflow = 'hidden'\n}\n\nexport function closeDrawer(id: string): void {\n const drawer = document.querySelector<HTMLElement>(`[data-alive-drawer=\"${id}\"]`)\n if (!drawer) return\n drawer.classList.remove('is-open')\n drawer.setAttribute('aria-hidden', 'true')\n document.body.style.overflow = ''\n}\n\n// ── Dropdown ──────────────────────────────────────────────────────────────────\n\nfunction wireDropdown(container: Element, root: Element): void {\n const trigger = container.querySelector<HTMLElement>('[data-alive-trigger]')\n const menu = container.querySelector<HTMLElement>('[data-alive-dropdown-menu]')\n if (!trigger || !menu) return\n\n const toggleCleanup = addListener(trigger, 'click', (e) => {\n e.stopPropagation()\n toggleDropdown(container)\n })\n registerCleanup(root, toggleCleanup)\n\n // Close on outside click\n const outsideCleanup = addListener(document, 'click', (e) => {\n if (!container.contains(e.target as Node)) {\n menu.classList.remove('is-open')\n trigger.setAttribute('aria-expanded', 'false')\n }\n })\n registerCleanup(root, outsideCleanup)\n\n // Close on ESC\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && menu.classList.contains('is-open')) {\n menu.classList.remove('is-open')\n trigger.setAttribute('aria-expanded', 'false')\n trigger.focus()\n }\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function toggleDropdown(container: Element): void {\n const trigger = container.querySelector<HTMLElement>('[data-alive-trigger]')\n const menu = container.querySelector<HTMLElement>('[data-alive-dropdown-menu]')\n if (!menu) return\n const isOpen = menu.classList.toggle('is-open')\n trigger?.setAttribute('aria-expanded', String(isOpen))\n if (isOpen) {\n // Focus first item\n menu.querySelector<HTMLElement>('[role=\"menuitem\"], button, a')?.focus()\n }\n}\n\n// ── Tabs ──────────────────────────────────────────────────────────────────────\n\nfunction wireTabs(container: Element, root: Element): void {\n const tabs = container.querySelectorAll<HTMLElement>('[data-alive-tab]')\n tabs.forEach(tab => {\n const cleanup = addListener(tab, 'click', () => {\n const panelId = tab.getAttribute('data-alive-tab')\n if (!panelId) return\n activateTab(container, panelId)\n })\n registerCleanup(root, cleanup)\n\n // Keyboard navigation\n const keyCleanup = addListener(tab, 'keydown', (e) => {\n const allTabs = [...container.querySelectorAll<HTMLElement>('[data-alive-tab]')]\n const idx = allTabs.indexOf(tab)\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n e.preventDefault()\n allTabs[(idx + 1) % allTabs.length]?.focus()\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n e.preventDefault()\n allTabs[(idx - 1 + allTabs.length) % allTabs.length]?.focus()\n }\n })\n registerCleanup(root, keyCleanup)\n })\n}\n\nfunction activateTab(container: Element, panelId: string): void {\n // Deactivate all tabs + panels within this tabs container\n container.querySelectorAll('[data-alive-tab]').forEach(t => {\n t.classList.remove('is-active')\n t.setAttribute('aria-selected', 'false')\n t.setAttribute('tabindex', '-1')\n })\n\n // Find panels — look in the container, then in the whole document\n const searchRoot = container.closest('[data-alive-tabs-panels]') ?? document\n searchRoot.querySelectorAll('[data-alive-panel]').forEach(p => {\n p.classList.remove('is-active')\n p.setAttribute('aria-hidden', 'true')\n })\n\n // Activate the selected tab\n const activeTab = container.querySelector<HTMLElement>(`[data-alive-tab=\"${panelId}\"]`)\n activeTab?.classList.add('is-active')\n activeTab?.setAttribute('aria-selected', 'true')\n activeTab?.setAttribute('tabindex', '0')\n\n // Activate the matching panel\n const activePanel = searchRoot.querySelector<HTMLElement>(`[data-alive-panel=\"${panelId}\"]`)\n activePanel?.classList.add('is-active')\n activePanel?.setAttribute('aria-hidden', 'false')\n}\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Wire all data-alive-* components within `root` (defaults to document.documentElement).\n * Safe to call multiple times — existing listeners are tracked and replaced on `destroy()`.\n */\nexport function init(root: Element = document.documentElement): void {\n // Accordion\n root.querySelectorAll('[data-alive-accordion]').forEach(el => wireAccordion(el, root))\n\n // Modal\n root.querySelectorAll('[data-alive-modal]').forEach(el => wireModal(el, root))\n\n // Drawer\n root.querySelectorAll('[data-alive-drawer]').forEach(el => wireDrawer(el, root))\n\n // Dropdown\n root.querySelectorAll('[data-alive-dropdown]').forEach(el => wireDropdown(el, root))\n\n // Tabs\n root.querySelectorAll('[data-alive-tabs]').forEach(el => wireTabs(el, root))\n}\n\n/**\n * Remove all event listeners registered by `init(root)`.\n */\nexport function destroy(root: Element = document.documentElement): void {\n const cleanups = cleanupRegistry.get(root) ?? []\n cleanups.forEach(fn => fn())\n cleanupRegistry.delete(root)\n}\n"],"mappings":"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","names":["cleanup"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * AliveUI Runtime\n *\n * Provides data-attribute-driven interactivity for accordion, modal, drawer,\n * dropdown, and tabs components — zero dependencies, ~2 KB.\n *\n * Usage:\n * import { init } from '@alivecss/aliveui/runtime'\n * init() // wire everything in document\n * init(myRootElement) // wire a subtree\n */\n\ntype Cleanup = () => void\n\nconst cleanupRegistry = new WeakMap<Element, Cleanup[]>()\n\nfunction registerCleanup(root: Element, fn: Cleanup): void {\n const existing = cleanupRegistry.get(root) ?? []\n existing.push(fn)\n cleanupRegistry.set(root, existing)\n}\n\nfunction addListener<K extends keyof HTMLElementEventMap>(\n el: EventTarget,\n type: K,\n handler: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n): Cleanup {\n el.addEventListener(type, handler as EventListener, options)\n return () => el.removeEventListener(type, handler as EventListener, options)\n}\n\n// ── Accordion ─────────────────────────────────────────────────────────────────\n\nfunction wireAccordion(container: Element, root: Element): void {\n const triggers = container.querySelectorAll<HTMLElement>('[data-alive-trigger]')\n triggers.forEach(trigger => {\n const item = trigger.closest('[data-alive-accordion-item]') ?? trigger.parentElement\n if (!item) return\n\n const cleanup = addListener(trigger, 'click', () => {\n const isOpen = item.classList.contains('is-open')\n // Close all items if not multi-open\n if (!container.hasAttribute('data-alive-multi')) {\n container.querySelectorAll('[data-alive-accordion-item]').forEach(i => {\n i.classList.remove('is-open')\n i.querySelector('[data-alive-trigger]')?.setAttribute('aria-expanded', 'false')\n })\n }\n if (!isOpen) {\n item.classList.add('is-open')\n trigger.setAttribute('aria-expanded', 'true')\n }\n })\n registerCleanup(root, cleanup)\n })\n}\n\n// ── Modal ─────────────────────────────────────────────────────────────────────\n\nfunction wireModal(modal: Element, root: Element): void {\n const id = modal.getAttribute('data-alive-modal')\n if (!id) return\n\n // Open triggers\n const openers = document.querySelectorAll<HTMLElement>(`[data-alive-open=\"${id}\"]`)\n openers.forEach(opener => {\n const cleanup = addListener(opener, 'click', () => openModal(id))\n registerCleanup(root, cleanup)\n })\n\n // Close triggers inside the modal\n modal.querySelectorAll<HTMLElement>('[data-alive-close]').forEach(closer => {\n const cleanup = addListener(closer, 'click', () => closeModal(id))\n registerCleanup(root, cleanup)\n })\n\n // Backdrop click to close (click on the modal wrapper, not the content)\n const cleanup = addListener(modal, 'click', (e) => {\n if (e.target === modal) closeModal(id)\n })\n registerCleanup(root, cleanup)\n\n // ESC key\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && modal.classList.contains('is-open')) closeModal(id)\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function openModal(id: string): void {\n const modal = document.querySelector<HTMLElement>(`[data-alive-modal=\"${id}\"]`)\n if (!modal) return\n modal.classList.add('is-open')\n modal.setAttribute('aria-hidden', 'false')\n document.body.style.overflow = 'hidden'\n // Focus first focusable element\n const focusable = modal.querySelector<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n )\n focusable?.focus()\n}\n\nexport function closeModal(id: string): void {\n const modal = document.querySelector<HTMLElement>(`[data-alive-modal=\"${id}\"]`)\n if (!modal) return\n modal.classList.remove('is-open')\n modal.setAttribute('aria-hidden', 'true')\n document.body.style.overflow = ''\n}\n\n// ── Drawer ────────────────────────────────────────────────────────────────────\n\nfunction wireDrawer(drawer: Element, root: Element): void {\n const id = drawer.getAttribute('data-alive-drawer')\n if (!id) return\n\n const openers = document.querySelectorAll<HTMLElement>(`[data-alive-open=\"${id}\"]`)\n openers.forEach(opener => {\n const cleanup = addListener(opener, 'click', () => openDrawer(id))\n registerCleanup(root, cleanup)\n })\n\n drawer.querySelectorAll<HTMLElement>('[data-alive-close]').forEach(closer => {\n const cleanup = addListener(closer, 'click', () => closeDrawer(id))\n registerCleanup(root, cleanup)\n })\n\n const backdropCleanup = addListener(drawer, 'click', (e) => {\n if (e.target === drawer) closeDrawer(id)\n })\n registerCleanup(root, backdropCleanup)\n\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && drawer.classList.contains('is-open')) closeDrawer(id)\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function openDrawer(id: string): void {\n const drawer = document.querySelector<HTMLElement>(`[data-alive-drawer=\"${id}\"]`)\n if (!drawer) return\n drawer.classList.add('is-open')\n drawer.setAttribute('aria-hidden', 'false')\n document.body.style.overflow = 'hidden'\n}\n\nexport function closeDrawer(id: string): void {\n const drawer = document.querySelector<HTMLElement>(`[data-alive-drawer=\"${id}\"]`)\n if (!drawer) return\n drawer.classList.remove('is-open')\n drawer.setAttribute('aria-hidden', 'true')\n document.body.style.overflow = ''\n}\n\n// ── Dropdown ──────────────────────────────────────────────────────────────────\n\nfunction wireDropdown(container: Element, root: Element): void {\n const trigger = container.querySelector<HTMLElement>('[data-alive-trigger]')\n const menu = container.querySelector<HTMLElement>('[data-alive-dropdown-menu]')\n if (!trigger || !menu) return\n\n const toggleCleanup = addListener(trigger, 'click', (e) => {\n e.stopPropagation()\n toggleDropdown(container)\n })\n registerCleanup(root, toggleCleanup)\n\n // Close on outside click\n const outsideCleanup = addListener(document, 'click', (e) => {\n if (!container.contains(e.target as Node)) {\n menu.classList.remove('is-open')\n trigger.setAttribute('aria-expanded', 'false')\n }\n })\n registerCleanup(root, outsideCleanup)\n\n // Close on ESC\n const escCleanup = addListener(document, 'keydown', (e) => {\n if (e.key === 'Escape' && menu.classList.contains('is-open')) {\n menu.classList.remove('is-open')\n trigger.setAttribute('aria-expanded', 'false')\n trigger.focus()\n }\n })\n registerCleanup(root, escCleanup)\n}\n\nexport function toggleDropdown(container: Element): void {\n const trigger = container.querySelector<HTMLElement>('[data-alive-trigger]')\n const menu = container.querySelector<HTMLElement>('[data-alive-dropdown-menu]')\n if (!menu) return\n const isOpen = menu.classList.toggle('is-open')\n trigger?.setAttribute('aria-expanded', String(isOpen))\n if (isOpen) {\n // Focus first item\n menu.querySelector<HTMLElement>('[role=\"menuitem\"], button, a')?.focus()\n }\n}\n\n// ── Tabs ──────────────────────────────────────────────────────────────────────\n\nfunction wireTabs(container: Element, root: Element): void {\n const tabs = container.querySelectorAll<HTMLElement>('[data-alive-tab]')\n tabs.forEach(tab => {\n const cleanup = addListener(tab, 'click', () => {\n const panelId = tab.getAttribute('data-alive-tab')\n if (!panelId) return\n activateTab(container, panelId)\n })\n registerCleanup(root, cleanup)\n\n // Keyboard navigation\n const keyCleanup = addListener(tab, 'keydown', (e) => {\n const allTabs = [...container.querySelectorAll<HTMLElement>('[data-alive-tab]')]\n const idx = allTabs.indexOf(tab)\n if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {\n e.preventDefault()\n allTabs[(idx + 1) % allTabs.length]?.focus()\n } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {\n e.preventDefault()\n allTabs[(idx - 1 + allTabs.length) % allTabs.length]?.focus()\n }\n })\n registerCleanup(root, keyCleanup)\n })\n}\n\nfunction activateTab(container: Element, panelId: string): void {\n // Deactivate all tabs + panels within this tabs container\n container.querySelectorAll('[data-alive-tab]').forEach(t => {\n t.classList.remove('is-active')\n t.setAttribute('aria-selected', 'false')\n t.setAttribute('tabindex', '-1')\n })\n\n // Find panels — look in the container, then in the whole document\n const searchRoot = container.closest('[data-alive-tabs-panels]') ?? document\n searchRoot.querySelectorAll('[data-alive-panel]').forEach(p => {\n p.classList.remove('is-active')\n p.setAttribute('aria-hidden', 'true')\n })\n\n // Activate the selected tab\n const activeTab = container.querySelector<HTMLElement>(`[data-alive-tab=\"${panelId}\"]`)\n activeTab?.classList.add('is-active')\n activeTab?.setAttribute('aria-selected', 'true')\n activeTab?.setAttribute('tabindex', '0')\n\n // Activate the matching panel\n const activePanel = searchRoot.querySelector<HTMLElement>(`[data-alive-panel=\"${panelId}\"]`)\n activePanel?.classList.add('is-active')\n activePanel?.setAttribute('aria-hidden', 'false')\n}\n\n// ── Scroll Reveal ──────────────────────────────────────────────────────────────\n\nfunction wireScroll(el: Element, root: Element): void {\n const obs = new IntersectionObserver(\n (entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n entry.target.classList.add('is-visible')\n obs.unobserve(entry.target)\n }\n })\n },\n { threshold: 0.1, rootMargin: '0px 0px -40px 0px' },\n )\n obs.observe(el)\n registerCleanup(root, () => obs.disconnect())\n}\n\n// ── Auto-Stagger ───────────────────────────────────────────────────────────────\n\nfunction wireStagger(container: Element): void {\n const children = [...container.children] as HTMLElement[]\n children.forEach((child, i) => {\n child.style.setProperty('--alive-index', String(i))\n })\n}\n\n// ── 3D Tilt ────────────────────────────────────────────────────────────────────\n\nfunction wireTilt(el: Element, root: Element): void {\n const htmlEl = el as HTMLElement\n const STRENGTH = 8\n\n const moveCleanup = addListener(htmlEl, 'mousemove', (e) => {\n const rect = htmlEl.getBoundingClientRect()\n const x = (e.clientX - rect.left) / rect.width - 0.5\n const y = (e.clientY - rect.top) / rect.height - 0.5\n htmlEl.style.transform = `perspective(800px) rotateY(${x * STRENGTH}deg) rotateX(${-y * STRENGTH}deg) scale(1.01)`\n htmlEl.style.transition = 'transform 0.1s ease-out'\n })\n\n const leaveCleanup = addListener(htmlEl, 'mouseleave', () => {\n htmlEl.style.transform = ''\n htmlEl.style.transition = 'transform 0.4s cubic-bezier(0.16, 1, 0.3, 1)'\n })\n\n registerCleanup(root, moveCleanup)\n registerCleanup(root, leaveCleanup)\n}\n\n// ── Magnetic Follow ────────────────────────────────────────────────────────────\n\nfunction wireMagnetic(el: Element, root: Element): void {\n const htmlEl = el as HTMLElement\n const PULL = 0.35\n\n const moveCleanup = addListener(htmlEl, 'mousemove', (e) => {\n const rect = htmlEl.getBoundingClientRect()\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n const dx = (e.clientX - cx) * PULL\n const dy = (e.clientY - cy) * PULL\n htmlEl.style.transform = `translate(${dx}px, ${dy}px)`\n htmlEl.style.transition = 'transform 0.2s cubic-bezier(0.16, 1, 0.3, 1)'\n })\n\n const leaveCleanup = addListener(htmlEl, 'mouseleave', () => {\n htmlEl.style.transform = ''\n htmlEl.style.transition = 'transform 0.5s cubic-bezier(0.16, 1, 0.3, 1)'\n })\n\n registerCleanup(root, moveCleanup)\n registerCleanup(root, leaveCleanup)\n}\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\n/**\n * Wire all data-alive-* components within `root` (defaults to document.documentElement).\n * Safe to call multiple times — existing listeners are tracked and replaced on `destroy()`.\n */\nexport function init(root: Element = document.documentElement): void {\n // Accordion\n root.querySelectorAll('[data-alive-accordion]').forEach(el => wireAccordion(el, root))\n\n // Modal\n root.querySelectorAll('[data-alive-modal]').forEach(el => wireModal(el, root))\n\n // Drawer\n root.querySelectorAll('[data-alive-drawer]').forEach(el => wireDrawer(el, root))\n\n // Dropdown\n root.querySelectorAll('[data-alive-dropdown]').forEach(el => wireDropdown(el, root))\n\n // Tabs\n root.querySelectorAll('[data-alive-tabs]').forEach(el => wireTabs(el, root))\n\n // Scroll reveal\n root.querySelectorAll('[data-alive-scroll]').forEach(el => wireScroll(el, root))\n\n // Auto-stagger children\n root.querySelectorAll('[data-alive-stagger]').forEach(el => wireStagger(el))\n\n // 3D tilt\n root.querySelectorAll('[data-alive-tilt]').forEach(el => wireTilt(el, root))\n\n // Magnetic follow\n root.querySelectorAll('[data-alive-magnetic]').forEach(el => wireMagnetic(el, root))\n}\n\n/**\n * Remove all event listeners registered by `init(root)`.\n */\nexport function destroy(root: Element = document.documentElement): void {\n const cleanups = cleanupRegistry.get(root) ?? []\n cleanups.forEach(fn => fn())\n cleanupRegistry.delete(root)\n}\n"],"mappings":";AAcA,IAAM,kBAAkB,oBAAI,QAA4B;AAExD,SAAS,gBAAgB,MAAe,IAAmB;AACzD,QAAM,WAAW,gBAAgB,IAAI,IAAI,KAAK,CAAC;AAC/C,WAAS,KAAK,EAAE;AAChB,kBAAgB,IAAI,MAAM,QAAQ;AACpC;AAEA,SAAS,YACP,IACA,MACA,SACA,SACS;AACT,KAAG,iBAAiB,MAAM,SAA0B,OAAO;AAC3D,SAAO,MAAM,GAAG,oBAAoB,MAAM,SAA0B,OAAO;AAC7E;AAIA,SAAS,cAAc,WAAoB,MAAqB;AAC9D,QAAM,WAAW,UAAU,iBAA8B,sBAAsB;AAC/E,WAAS,QAAQ,aAAW;AAC1B,UAAM,OAAO,QAAQ,QAAQ,6BAA6B,KAAK,QAAQ;AACvE,QAAI,CAAC,KAAM;AAEX,UAAM,UAAU,YAAY,SAAS,SAAS,MAAM;AAClD,YAAM,SAAS,KAAK,UAAU,SAAS,SAAS;AAEhD,UAAI,CAAC,UAAU,aAAa,kBAAkB,GAAG;AAC/C,kBAAU,iBAAiB,6BAA6B,EAAE,QAAQ,OAAK;AACrE,YAAE,UAAU,OAAO,SAAS;AAC5B,YAAE,cAAc,sBAAsB,GAAG,aAAa,iBAAiB,OAAO;AAAA,QAChF,CAAC;AAAA,MACH;AACA,UAAI,CAAC,QAAQ;AACX,aAAK,UAAU,IAAI,SAAS;AAC5B,gBAAQ,aAAa,iBAAiB,MAAM;AAAA,MAC9C;AAAA,IACF,CAAC;AACD,oBAAgB,MAAM,OAAO;AAAA,EAC/B,CAAC;AACH;AAIA,SAAS,UAAU,OAAgB,MAAqB;AACtD,QAAM,KAAK,MAAM,aAAa,kBAAkB;AAChD,MAAI,CAAC,GAAI;AAGT,QAAM,UAAU,SAAS,iBAA8B,qBAAqB,EAAE,IAAI;AAClF,UAAQ,QAAQ,YAAU;AACxB,UAAMA,WAAU,YAAY,QAAQ,SAAS,MAAM,UAAU,EAAE,CAAC;AAChE,oBAAgB,MAAMA,QAAO;AAAA,EAC/B,CAAC;AAGD,QAAM,iBAA8B,oBAAoB,EAAE,QAAQ,YAAU;AAC1E,UAAMA,WAAU,YAAY,QAAQ,SAAS,MAAM,WAAW,EAAE,CAAC;AACjE,oBAAgB,MAAMA,QAAO;AAAA,EAC/B,CAAC;AAGD,QAAM,UAAU,YAAY,OAAO,SAAS,CAAC,MAAM;AACjD,QAAI,EAAE,WAAW,MAAO,YAAW,EAAE;AAAA,EACvC,CAAC;AACD,kBAAgB,MAAM,OAAO;AAG7B,QAAM,aAAa,YAAY,UAAU,WAAW,CAAC,MAAM;AACzD,QAAI,EAAE,QAAQ,YAAY,MAAM,UAAU,SAAS,SAAS,EAAG,YAAW,EAAE;AAAA,EAC9E,CAAC;AACD,kBAAgB,MAAM,UAAU;AAClC;AAEO,SAAS,UAAU,IAAkB;AAC1C,QAAM,QAAQ,SAAS,cAA2B,sBAAsB,EAAE,IAAI;AAC9E,MAAI,CAAC,MAAO;AACZ,QAAM,UAAU,IAAI,SAAS;AAC7B,QAAM,aAAa,eAAe,OAAO;AACzC,WAAS,KAAK,MAAM,WAAW;AAE/B,QAAM,YAAY,MAAM;AAAA,IACtB;AAAA,EACF;AACA,aAAW,MAAM;AACnB;AAEO,SAAS,WAAW,IAAkB;AAC3C,QAAM,QAAQ,SAAS,cAA2B,sBAAsB,EAAE,IAAI;AAC9E,MAAI,CAAC,MAAO;AACZ,QAAM,UAAU,OAAO,SAAS;AAChC,QAAM,aAAa,eAAe,MAAM;AACxC,WAAS,KAAK,MAAM,WAAW;AACjC;AAIA,SAAS,WAAW,QAAiB,MAAqB;AACxD,QAAM,KAAK,OAAO,aAAa,mBAAmB;AAClD,MAAI,CAAC,GAAI;AAET,QAAM,UAAU,SAAS,iBAA8B,qBAAqB,EAAE,IAAI;AAClF,UAAQ,QAAQ,YAAU;AACxB,UAAM,UAAU,YAAY,QAAQ,SAAS,MAAM,WAAW,EAAE,CAAC;AACjE,oBAAgB,MAAM,OAAO;AAAA,EAC/B,CAAC;AAED,SAAO,iBAA8B,oBAAoB,EAAE,QAAQ,YAAU;AAC3E,UAAM,UAAU,YAAY,QAAQ,SAAS,MAAM,YAAY,EAAE,CAAC;AAClE,oBAAgB,MAAM,OAAO;AAAA,EAC/B,CAAC;AAED,QAAM,kBAAkB,YAAY,QAAQ,SAAS,CAAC,MAAM;AAC1D,QAAI,EAAE,WAAW,OAAQ,aAAY,EAAE;AAAA,EACzC,CAAC;AACD,kBAAgB,MAAM,eAAe;AAErC,QAAM,aAAa,YAAY,UAAU,WAAW,CAAC,MAAM;AACzD,QAAI,EAAE,QAAQ,YAAY,OAAO,UAAU,SAAS,SAAS,EAAG,aAAY,EAAE;AAAA,EAChF,CAAC;AACD,kBAAgB,MAAM,UAAU;AAClC;AAEO,SAAS,WAAW,IAAkB;AAC3C,QAAM,SAAS,SAAS,cAA2B,uBAAuB,EAAE,IAAI;AAChF,MAAI,CAAC,OAAQ;AACb,SAAO,UAAU,IAAI,SAAS;AAC9B,SAAO,aAAa,eAAe,OAAO;AAC1C,WAAS,KAAK,MAAM,WAAW;AACjC;AAEO,SAAS,YAAY,IAAkB;AAC5C,QAAM,SAAS,SAAS,cAA2B,uBAAuB,EAAE,IAAI;AAChF,MAAI,CAAC,OAAQ;AACb,SAAO,UAAU,OAAO,SAAS;AACjC,SAAO,aAAa,eAAe,MAAM;AACzC,WAAS,KAAK,MAAM,WAAW;AACjC;AAIA,SAAS,aAAa,WAAoB,MAAqB;AAC7D,QAAM,UAAU,UAAU,cAA2B,sBAAsB;AAC3E,QAAM,OAAO,UAAU,cAA2B,4BAA4B;AAC9E,MAAI,CAAC,WAAW,CAAC,KAAM;AAEvB,QAAM,gBAAgB,YAAY,SAAS,SAAS,CAAC,MAAM;AACzD,MAAE,gBAAgB;AAClB,mBAAe,SAAS;AAAA,EAC1B,CAAC;AACD,kBAAgB,MAAM,aAAa;AAGnC,QAAM,iBAAiB,YAAY,UAAU,SAAS,CAAC,MAAM;AAC3D,QAAI,CAAC,UAAU,SAAS,EAAE,MAAc,GAAG;AACzC,WAAK,UAAU,OAAO,SAAS;AAC/B,cAAQ,aAAa,iBAAiB,OAAO;AAAA,IAC/C;AAAA,EACF,CAAC;AACD,kBAAgB,MAAM,cAAc;AAGpC,QAAM,aAAa,YAAY,UAAU,WAAW,CAAC,MAAM;AACzD,QAAI,EAAE,QAAQ,YAAY,KAAK,UAAU,SAAS,SAAS,GAAG;AAC5D,WAAK,UAAU,OAAO,SAAS;AAC/B,cAAQ,aAAa,iBAAiB,OAAO;AAC7C,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF,CAAC;AACD,kBAAgB,MAAM,UAAU;AAClC;AAEO,SAAS,eAAe,WAA0B;AACvD,QAAM,UAAU,UAAU,cAA2B,sBAAsB;AAC3E,QAAM,OAAO,UAAU,cAA2B,4BAA4B;AAC9E,MAAI,CAAC,KAAM;AACX,QAAM,SAAS,KAAK,UAAU,OAAO,SAAS;AAC9C,WAAS,aAAa,iBAAiB,OAAO,MAAM,CAAC;AACrD,MAAI,QAAQ;AAEV,SAAK,cAA2B,8BAA8B,GAAG,MAAM;AAAA,EACzE;AACF;AAIA,SAAS,SAAS,WAAoB,MAAqB;AACzD,QAAM,OAAO,UAAU,iBAA8B,kBAAkB;AACvE,OAAK,QAAQ,SAAO;AAClB,UAAM,UAAU,YAAY,KAAK,SAAS,MAAM;AAC9C,YAAM,UAAU,IAAI,aAAa,gBAAgB;AACjD,UAAI,CAAC,QAAS;AACd,kBAAY,WAAW,OAAO;AAAA,IAChC,CAAC;AACD,oBAAgB,MAAM,OAAO;AAG7B,UAAM,aAAa,YAAY,KAAK,WAAW,CAAC,MAAM;AACpD,YAAM,UAAU,CAAC,GAAG,UAAU,iBAA8B,kBAAkB,CAAC;AAC/E,YAAM,MAAM,QAAQ,QAAQ,GAAG;AAC/B,UAAI,EAAE,QAAQ,gBAAgB,EAAE,QAAQ,aAAa;AACnD,UAAE,eAAe;AACjB,iBAAS,MAAM,KAAK,QAAQ,MAAM,GAAG,MAAM;AAAA,MAC7C,WAAW,EAAE,QAAQ,eAAe,EAAE,QAAQ,WAAW;AACvD,UAAE,eAAe;AACjB,iBAAS,MAAM,IAAI,QAAQ,UAAU,QAAQ,MAAM,GAAG,MAAM;AAAA,MAC9D;AAAA,IACF,CAAC;AACD,oBAAgB,MAAM,UAAU;AAAA,EAClC,CAAC;AACH;AAEA,SAAS,YAAY,WAAoB,SAAuB;AAE9D,YAAU,iBAAiB,kBAAkB,EAAE,QAAQ,OAAK;AAC1D,MAAE,UAAU,OAAO,WAAW;AAC9B,MAAE,aAAa,iBAAiB,OAAO;AACvC,MAAE,aAAa,YAAY,IAAI;AAAA,EACjC,CAAC;AAGD,QAAM,aAAa,UAAU,QAAQ,0BAA0B,KAAK;AACpE,aAAW,iBAAiB,oBAAoB,EAAE,QAAQ,OAAK;AAC7D,MAAE,UAAU,OAAO,WAAW;AAC9B,MAAE,aAAa,eAAe,MAAM;AAAA,EACtC,CAAC;AAGD,QAAM,YAAY,UAAU,cAA2B,oBAAoB,OAAO,IAAI;AACtF,aAAW,UAAU,IAAI,WAAW;AACpC,aAAW,aAAa,iBAAiB,MAAM;AAC/C,aAAW,aAAa,YAAY,GAAG;AAGvC,QAAM,cAAc,WAAW,cAA2B,sBAAsB,OAAO,IAAI;AAC3F,eAAa,UAAU,IAAI,WAAW;AACtC,eAAa,aAAa,eAAe,OAAO;AAClD;AAIA,SAAS,WAAW,IAAa,MAAqB;AACpD,QAAM,MAAM,IAAI;AAAA,IACd,CAAC,YAAY;AACX,cAAQ,QAAQ,WAAS;AACvB,YAAI,MAAM,gBAAgB;AACxB,gBAAM,OAAO,UAAU,IAAI,YAAY;AACvC,cAAI,UAAU,MAAM,MAAM;AAAA,QAC5B;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,EAAE,WAAW,KAAK,YAAY,oBAAoB;AAAA,EACpD;AACA,MAAI,QAAQ,EAAE;AACd,kBAAgB,MAAM,MAAM,IAAI,WAAW,CAAC;AAC9C;AAIA,SAAS,YAAY,WAA0B;AAC7C,QAAM,WAAW,CAAC,GAAG,UAAU,QAAQ;AACvC,WAAS,QAAQ,CAAC,OAAO,MAAM;AAC7B,UAAM,MAAM,YAAY,iBAAiB,OAAO,CAAC,CAAC;AAAA,EACpD,CAAC;AACH;AAIA,SAAS,SAAS,IAAa,MAAqB;AAClD,QAAM,SAAS;AACf,QAAM,WAAW;AAEjB,QAAM,cAAc,YAAY,QAAQ,aAAa,CAAC,MAAM;AAC1D,UAAM,OAAO,OAAO,sBAAsB;AAC1C,UAAM,KAAK,EAAE,UAAU,KAAK,QAAQ,KAAK,QAAQ;AACjD,UAAM,KAAK,EAAE,UAAU,KAAK,OAAO,KAAK,SAAS;AACjD,WAAO,MAAM,YAAY,8BAA8B,IAAI,QAAQ,gBAAgB,CAAC,IAAI,QAAQ;AAChG,WAAO,MAAM,aAAa;AAAA,EAC5B,CAAC;AAED,QAAM,eAAe,YAAY,QAAQ,cAAc,MAAM;AAC3D,WAAO,MAAM,YAAY;AACzB,WAAO,MAAM,aAAa;AAAA,EAC5B,CAAC;AAED,kBAAgB,MAAM,WAAW;AACjC,kBAAgB,MAAM,YAAY;AACpC;AAIA,SAAS,aAAa,IAAa,MAAqB;AACtD,QAAM,SAAS;AACf,QAAM,OAAO;AAEb,QAAM,cAAc,YAAY,QAAQ,aAAa,CAAC,MAAM;AAC1D,UAAM,OAAO,OAAO,sBAAsB;AAC1C,UAAM,KAAK,KAAK,OAAO,KAAK,QAAQ;AACpC,UAAM,KAAK,KAAK,MAAM,KAAK,SAAS;AACpC,UAAM,MAAM,EAAE,UAAU,MAAM;AAC9B,UAAM,MAAM,EAAE,UAAU,MAAM;AAC9B,WAAO,MAAM,YAAY,aAAa,EAAE,OAAO,EAAE;AACjD,WAAO,MAAM,aAAa;AAAA,EAC5B,CAAC;AAED,QAAM,eAAe,YAAY,QAAQ,cAAc,MAAM;AAC3D,WAAO,MAAM,YAAY;AACzB,WAAO,MAAM,aAAa;AAAA,EAC5B,CAAC;AAED,kBAAgB,MAAM,WAAW;AACjC,kBAAgB,MAAM,YAAY;AACpC;AAQO,SAAS,KAAK,OAAgB,SAAS,iBAAuB;AAEnE,OAAK,iBAAiB,wBAAwB,EAAE,QAAQ,QAAM,cAAc,IAAI,IAAI,CAAC;AAGrF,OAAK,iBAAiB,oBAAoB,EAAE,QAAQ,QAAM,UAAU,IAAI,IAAI,CAAC;AAG7E,OAAK,iBAAiB,qBAAqB,EAAE,QAAQ,QAAM,WAAW,IAAI,IAAI,CAAC;AAG/E,OAAK,iBAAiB,uBAAuB,EAAE,QAAQ,QAAM,aAAa,IAAI,IAAI,CAAC;AAGnF,OAAK,iBAAiB,mBAAmB,EAAE,QAAQ,QAAM,SAAS,IAAI,IAAI,CAAC;AAG3E,OAAK,iBAAiB,qBAAqB,EAAE,QAAQ,QAAM,WAAW,IAAI,IAAI,CAAC;AAG/E,OAAK,iBAAiB,sBAAsB,EAAE,QAAQ,QAAM,YAAY,EAAE,CAAC;AAG3E,OAAK,iBAAiB,mBAAmB,EAAE,QAAQ,QAAM,SAAS,IAAI,IAAI,CAAC;AAG3E,OAAK,iBAAiB,uBAAuB,EAAE,QAAQ,QAAM,aAAa,IAAI,IAAI,CAAC;AACrF;AAKO,SAAS,QAAQ,OAAgB,SAAS,iBAAuB;AACtE,QAAM,WAAW,gBAAgB,IAAI,IAAI,KAAK,CAAC;AAC/C,WAAS,QAAQ,QAAM,GAAG,CAAC;AAC3B,kBAAgB,OAAO,IAAI;AAC7B;","names":["cleanup"]}
|