/**
* PXM Accordion Component
*
* A flexible, accessible accordion component that allows users to expand/collapse content sections.
* Bring your own animation library (GSAP, Anime.js, etc.) or use CSS transitions.
*
* Features:
* - Keyboard navigation
* - Arrow key navigation with wrapping (Up/Down arrows wrap to first/last item)
* - Dynamic content support (items can be added/removed after initialization)
* - Event-driven animation system (bring your own animation library)
* - Single or multiple items can be expanded simultaneously
*
* Keyboard Navigation:
* - `Enter` or `Space` - Toggle current item
* - `ArrowUp` - Focus previous item (wraps to last item if at first)
* - `ArrowDown` - Focus next item (wraps to first item if at last)
* - `Home` - Focus first item
* - `End` - Focus last item
*
* Basic Usage:
* ```html
*
*
*
* Section 1
* ▼
*
*
* Content goes here...
*
*
*
* ```
*
* Dynamic Content:
* ```javascript
* // Items can be added/removed dynamically
* const accordion = document.querySelector('pxm-accordion');
* const newItem = document.createElement('pxm-accordion-item');
* newItem.innerHTML = `
* New Section
* New content
* `;
* accordion.appendChild(newItem); // Automatically initialized
*
* // Listen for changes
* accordion.addEventListener('pxm:accordion:items-changed', (e) => {
* console.log(`Accordion now has ${e.detail.itemCount} items`);
* });
* ```
*
* With GSAP Animation (via events - recommended for CDN):
* ```javascript
* const accordion = document.querySelector('pxm-accordion');
*
* accordion.addEventListener('pxm:accordion:before-expand', (e) => {
* const { content, item, index } = e.detail;
* e.preventDefault(); // Take over the animation
*
* gsap.fromTo(content,
* { height: 0, opacity: 0 },
* { height: 'auto', opacity: 1, duration: 0.3, onComplete: () => {
* e.detail.complete(); // Signal animation complete
* }}
* );
* });
*
* accordion.addEventListener('pxm:accordion:before-collapse', (e) => {
* const { content, item, index } = e.detail;
* e.preventDefault(); // Take over the animation
*
* gsap.to(content, {
* height: 0,
* opacity: 0,
* duration: 0.3,
* onComplete: () => {
* e.detail.complete(); // Signal animation complete
* }
* });
* });
* ```
*
* With CSS Transitions (default):
* ```css
* pxm-accordion-content {
* transition: opacity 0.3s ease-in-out;
* }
* ```
*
* Consumer Styling Examples:
* ```css
* /* Style the component structure *\/
* pxm-accordion {
* /* Your layout styles *\/
* }
*
* pxm-accordion-item {
* /* Your item styles *\/
* }
*
* /* ✅ DO: Style based on data attributes (for CSS/JS targeting) /
* pxm-accordion-item[data-expanded="true"] pxm-accordion-content {
* /* Expanded state styling *\/
* display: block;
* opacity: 1;
* }
*
* pxm-accordion-item[data-state="active"] {
* /* Active state styling *\/
* background: #f0f0f0;
* }
*
* pxm-accordion-item[data-disabled="true"] {
* /* Disabled state styling *\/
* opacity: 0.5;
* }
*
* /* ❌ DON'T: Style based on ARIA attributes (bad practice) *\/
* pxm-accordion-item[aria-expanded="true"] {
* /* Don't use ARIA for styling *\/
* }
* ```
*
* With Tailwind CSS:
* ```html
*
*
*
*
* Section Title
* ▶
*
*
* Content here
*
*
*
* ```
*
* SSR / Hydration Support:
* ```css
* /* Recommended: Set initial styles in CSS to prevent hydration flash *\/
* pxm-accordion-item:not([data-expanded="true"]) pxm-accordion-content {
* display: none;
* opacity: 0;
* }
*
* pxm-accordion-item[data-expanded="true"] pxm-accordion-content {
* display: block;
* opacity: 1;
* }
*
* /* Optional: Hide content during hydration *\/
* pxm-accordion:not(:defined) pxm-accordion-content {
* display: none;
* }
* ```
*
* Accessibility:
* This component manages both ARIA attributes (for accessibility) and data attributes (for styling/JS).
* - ARIA attributes (aria-expanded) are automatically managed for screen readers
* - Data attributes (data-expanded, data-state) are provided for CSS styling and JavaScript hooks
* - Additional ARIA attributes, labels, and roles should be added by the consumer as needed
*
* Events:
* - `pxm:accordion:before-expand` - Cancelable. Fired before expansion starts.
* - `pxm:accordion:after-expand` - Fired after expansion completes.
* - `pxm:accordion:before-collapse` - Cancelable. Fired before collapse starts.
* - `pxm:accordion:after-collapse` - Fired after collapse completes.
* - `pxm:accordion:toggle` - Fired when an item is toggled.
* - `pxm:accordion:items-changed` - Fired when items are dynamically added/removed.
* - `pxm:accordion:state-sync` - Fired when internal state syncs with manually changed DOM attributes.
*
* State Synchronization:
* ```javascript
* // Manual data attribute changes are automatically synced
* const item = document.querySelector('pxm-accordion-item');
* item.setAttribute('data-expanded', 'true'); // Component state automatically updates
* item.setAttribute('data-state', 'active'); // Both data attributes should be set
*
* // Listen for sync events
* accordion.addEventListener('pxm:accordion:state-sync', (e) => {
* console.log(`Item ${e.detail.index} was ${e.detail.action}`);
* // Possible actions: 'activated-from-dom', 'deactivated-from-dom'
* });
* ```
*/
export interface AccordionEventDetail {
index: number;
item: HTMLElement;
content: HTMLElement;
trigger: HTMLElement;
complete: () => void;
}
export interface AccordionToggleEventDetail {
index: number;
item: HTMLElement;
isExpanding: boolean;
}
declare class PxmAccordion extends HTMLElement {
private config;
private state;
private _items?;
private animationPromises;
private mutationObserver?;
private attributeObserver?;
private itemStates;
private initializedItems;
private itemEventListeners;
private get items();
static get observedAttributes(): string[];
constructor();
connectedCallback(): void;
disconnectedCallback(): void;
attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
/**
* Set up MutationObserver to watch for dynamically added/removed items
*/
private observeChildChanges;
/**
* Set up MutationObserver to watch for data attribute changes on items
*/
private observeAttributeChanges;
/**
* Sync internal state when data attributes are manually changed
*/
private syncStateFromDOM;
/**
* Handle dynamic changes to accordion items
*/
private handleDynamicChanges;
/**
* Clean up event listeners for items that have been removed from the DOM
*/
private cleanupRemovedItems;
/**
* Save the current state of all items
*/
private saveItemStates;
/**
* Restore the state of items that still exist
*/
private restoreItemStates;
private defaultListeners;
private hasCustomAnimations;
private shouldSkipDefaultAnimations;
/**
* Set up default CSS-based animations if no hooks are provided and no event listeners exist
*/
private setupDefaultAnimations;
/**
* Helper to check if event listeners exist (for determining if we should use default animations)
*/
private hasEventListener;
/**
* Clear all event listener tracking attributes to reset to defaults
*/
private clearEventListenerTracking;
/**
* Override addEventListener to track when animation events are added
*/
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
/**
* Set up accordion items with event listeners
*/
private setupItems;
/**
* Focus the previous accordion item (wraps to last item if at first)
*/
private focusPreviousItem;
/**
* Focus the next accordion item (wraps to first item if at last)
*/
private focusNextItem;
/**
* Focus the first accordion item
*/
private focusFirstItem;
/**
* Focus the last accordion item
*/
private focusLastItem;
/**
* Update icon rotation for all items
*/
private updateIconRotations;
/**
* Update icon for a specific item
*/
private updateIcon;
/**
* Create animation promise that can be resolved externally
*/
private createAnimationPromise;
/**
* Expand an accordion item
*/
expandItem(index: number): Promise;
/**
* Collapse an accordion item
*/
collapseItem(index: number): Promise;
/**
* Toggle an accordion item's state
*/
toggleItem(index: number): Promise;
/**
* Remove default animation listeners (useful when using custom animation libraries)
*/
removeDefaultAnimations(): void;
/**
* Public API methods
*/
expandAll(): Promise;
collapseAll(): Promise;
getActiveItems(): number[];
isItemActive(index: number): boolean;
/**
* Clean up all event listeners for all items
*/
private cleanupAllEventListeners;
/**
* Clean up event listeners for a specific item
*/
private cleanupItemEventListeners;
/**
* Add an event listener to an item and track it for cleanup
*/
private addItemEventListener;
}
declare class PxmAccordionItem extends HTMLElement {
constructor();
}
declare class PxmAccordionTrigger extends HTMLElement {
constructor();
}
declare class PxmAccordionContent extends HTMLElement {
constructor();
}
export type { PxmAccordion };
export type { PxmAccordionItem };
export type { PxmAccordionTrigger };
export type { PxmAccordionContent };