@rhds/elements
Version:
Red Hat Design System Elements
336 lines • 15.2 kB
JavaScript
var _RhAccordion_instances, _a, _RhAccordion_expandedIndexSet, _RhAccordion_expanded, _RhAccordion_expandedIndex, _RhAccordion_logger, _RhAccordion_mo, _RhAccordion_makeContext, _RhAccordion_panelForHeader, _RhAccordion_expand, _RhAccordion_collapse, _RhAccordion_onChange, _RhAccordion_allHeaders, _RhAccordion_allPanels, _RhAccordion_getIndex;
var RhAccordion_1;
import { __classPrivateFieldGet, __classPrivateFieldSet, __decorate } from "tslib";
import { LitElement, html } from 'lit';
import { classMap } from 'lit/directives/class-map.js';
import { customElement } from 'lit/decorators/custom-element.js';
import { property } from 'lit/decorators/property.js';
import { observes } from '@patternfly/pfe-core/decorators/observes.js';
import { provide } from '@lit/context';
import { colorPalettes } from '@rhds/elements/lib/color-palettes.js';
import { themable } from '@rhds/elements/lib/themable.js';
import { NumberListConverter, ComposedEvent } from '@patternfly/pfe-core';
import { Logger } from '@patternfly/pfe-core/controllers/logger.js';
import { RhAccordionHeader, AccordionHeaderChangeEvent } from './rh-accordion-header.js';
import { RhAccordionPanel } from './rh-accordion-panel.js';
import { context } from './context.js';
export * from './rh-accordion-header.js';
export * from './rh-accordion-panel.js';
import { css } from "lit";
const styles = css `:host{display:block;container:accordion/inline-size}#container{color:var(--rh-color-text-primary);--_accordion-background:light-dark(var(--rh-color-surface-lightest,#fff),var(--rh-color-surface-darkest,#151515))}#container.palette{--_accordion-background:var(--rh-color-surface)}#container.expanded{box-shadow:var(--rh-box-shadow-sm,0 2px 4px 0 #15151533)}#container.large{--_header-font-size:var(--rh-font-size-body-text-md,1rem);--_panel-font-size:var(--rh-font-size-body-text-md,1rem)} accordion (min-width: 768px){#container.large{--_header-font-size:var(--rh-font-size-body-text-lg,1.125rem)}}::slotted(rh-accordion-header:first-child){display:block;border-block:1px solid var(--rh-color-border-subtle)}::slotted(rh-accordion-header:not(:first-child)){display:block;border-block-end:1px solid var(--rh-color-border-subtle)}::slotted(rh-accordion-header:is([expanded])){display:block;border-block-end:0}::slotted(rh-accordion-panel:is([expanded])){display:block;border-block-end:1px solid var(--rh-color-border-subtle);box-shadow:var(--rh-box-shadow-sm,0 2px 4px 0 #15151533)}`;
export class AccordionExpandEvent extends ComposedEvent {
constructor(toggle, panel) {
super('expand');
this.toggle = toggle;
this.panel = panel;
}
}
export class AccordionCollapseEvent extends ComposedEvent {
constructor(toggle, panel) {
super('collapse');
this.toggle = toggle;
this.panel = panel;
}
}
/**
* Organizes content into expandable panels for scanning and selective
* disclosure. Must contain paired `rh-accordion-header` and
* `rh-accordion-panel` children. Should have two or more pairs; for a
* single section use `rh-disclosure`. Headers use ARIA `role="heading"`
* with `aria-expanded`/`aria-controls` for screen readers. Supports
* keyboard navigation: Tab to move focus, Enter or Space to toggle.
*
* @summary Organizes content into expandable sections users can open or close
*
* @alias accordion
*
* @fires {AccordionExpandEvent} expand - Fires when a panel expands.
* Event detail: `toggle` (RhAccordionHeader), `panel` (RhAccordionPanel).
* @fires {AccordionCollapseEvent} collapse - Fires when a panel collapses.
* Event detail: `toggle` (RhAccordionHeader), `panel` (RhAccordionPanel).
*/
let RhAccordion = RhAccordion_1 = _a = class RhAccordion extends LitElement {
constructor() {
super(...arguments);
_RhAccordion_instances.add(this);
/**
* Switches the accordion to large size, increasing font size and padding.
* Avoid on viewports below 576px; the accordion automatically falls back
* to small size on mobile breakpoints. Use `large` for page-level content
* sections where the accordion is the primary content structure.
*/
this.large = false;
_RhAccordion_expandedIndexSet.set(this, new Set());
_RhAccordion_expanded.set(this, false);
_RhAccordion_expandedIndex.set(this, []);
_RhAccordion_logger.set(this, new Logger(this));
_RhAccordion_mo.set(this, new MutationObserver(() => this.updateAccessibility()));
this.ctx = __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_makeContext).call(this);
}
static isAccordion(target) {
return target instanceof RhAccordion_1;
}
static isHeader(target) {
return target instanceof RhAccordionHeader;
}
static isPanel(target) {
return target instanceof RhAccordionPanel;
}
static isAccordionChangeEvent(event) {
return event instanceof AccordionHeaderChangeEvent;
}
/**
* Comma-separated 0-based indexes of initially expanded panels.
* Defaults to none (all collapsed). Example: `expanded-index="0,2"`
* expands the first and third panels.
*/
get expandedIndex() {
return __classPrivateFieldGet(this, _RhAccordion_expandedIndex, "f");
}
set expandedIndex(value) {
__classPrivateFieldSet(this, _RhAccordion_expandedIndex, value, "f");
}
/** All headers for this accordion */
get headers() {
return __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_allHeaders).call(this);
}
/** All panels for this accordion */
get panels() {
return __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_allPanels).call(this);
}
connectedCallback() {
super.connectedCallback();
this.addEventListener('change', __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_onChange));
__classPrivateFieldGet(this, _RhAccordion_mo, "f").observe(this, { childList: true });
this.updateAccessibility();
}
render() {
const { large } = this;
const expanded = __classPrivateFieldGet(this, _RhAccordion_expanded, "f");
return html `
<div id="container"
class="${classMap({ large, expanded })}"><!--
summary: Alternating rh-accordion-header and rh-accordion-panel pairs
description: |
Must contain paired rh-accordion-header and rh-accordion-panel elements
in alternating order. Each header Must be immediately followed by its
corresponding panel. Should contain at least two pairs.
Headers provide aria-controls linking to their panel for screen readers.
--><slot></slot></div>
`;
}
async getUpdateComplete() {
const c = await super.getUpdateComplete();
const results = await Promise.all([
...this.headers.map(x => x.updateComplete),
...this.panels.map(x => x.updateComplete),
]);
return c && results.every(Boolean);
}
updateExpanded() {
if (__classPrivateFieldGet(this, _RhAccordion_expandedIndex, "f").length === 0) {
return;
}
// close all first
this.collapseAll();
__classPrivateFieldGet(this, _RhAccordion_expandedIndex, "f").forEach(headerIndex => {
if (!this.headers[headerIndex]) {
return;
}
__classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_expand).call(this, headerIndex);
});
}
contextChanged() {
this.ctx = __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_makeContext).call(this);
}
/**
* Initialize the accordion by connecting headers and panels
* with aria controls and labels; set up the default disclosure
* state if not set by the author; and check the URL for default
* open
*/
updateAccessibility() {
const { headers } = this;
// For each header in the accordion, attach the aria connections
for (const header of headers) {
const panel = __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_panelForHeader).call(this, header);
if (panel) {
header.setAttribute('aria-controls', panel.id);
panel.setAttribute('aria-labelledby', header.id);
}
}
}
/**
* Accepts a 0-based index value (integer) for the set of accordion items to expand or collapse.
* @param index header index to toggle
*/
async toggle(index) {
const { headers } = this;
const header = headers[index];
if (!header.expanded) {
await __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_expand).call(this, index);
}
else {
await __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_collapse).call(this, index);
}
}
/**
* Accepts a 0-based index value (integer) for the set of accordion items to expand.
* Accepts an optional parent accordion to search for headers and panels.
* @param index header index to toggle
* @param parentAccordion target accordion to expand in
*/
async expand(index, parentAccordion) {
const allHeaders = __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_allHeaders).call(this, parentAccordion);
const header = allHeaders[index];
if (!header) {
return;
}
const panel = __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_panelForHeader).call(this, header);
if (!panel) {
return;
}
// If the header and panel exist, open both
__classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_expand).call(this, index);
header.focus();
this.dispatchEvent(new AccordionExpandEvent(header, panel));
await this.updateComplete;
}
/**
* Expands all accordion items.
*/
async expandAll() {
this.headers.forEach((_, i) => __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_expand).call(this, i));
await this.updateComplete;
}
/**
* Accepts a 0-based index value (integer) for the set of accordion items to collapse.
* @param index header index to collapse
*/
async collapse(index) {
const header = this.headers.at(index);
const panel = this.panels.at(index);
if (!header || !panel) {
return;
}
__classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_collapse).call(this, index);
this.dispatchEvent(new AccordionCollapseEvent(header, panel));
await this.updateComplete;
}
/**
* Collapses all accordion items.
*/
async collapseAll() {
this.headers.forEach((_, i) => __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_collapse).call(this, i));
await this.updateComplete;
}
};
_RhAccordion_expandedIndexSet = new WeakMap();
_RhAccordion_expanded = new WeakMap();
_RhAccordion_expandedIndex = new WeakMap();
_RhAccordion_logger = new WeakMap();
_RhAccordion_mo = new WeakMap();
_RhAccordion_instances = new WeakSet();
_RhAccordion_makeContext = function _RhAccordion_makeContext() {
const { accents = 'inline', large } = this;
return { accents, large };
};
_RhAccordion_panelForHeader = function _RhAccordion_panelForHeader(header) {
const next = header.nextElementSibling;
if (!RhAccordion_1.isPanel(next)) {
return next?.querySelector('rh-accordion-panel');
}
else {
return next;
}
};
_RhAccordion_expand = function _RhAccordion_expand(index) {
// If this index is not already listed in the expandedSets array, add it
if (__classPrivateFieldGet(this, _RhAccordion_expandedIndexSet, "f").has(index)) {
return;
}
__classPrivateFieldGet(this, _RhAccordion_expandedIndexSet, "f").add(index);
const header = this.headers[index];
const panel = this.panels[index];
if (header && panel) {
header.expanded = true;
panel.expanded = true;
}
};
_RhAccordion_collapse = function _RhAccordion_collapse(index) {
if (!__classPrivateFieldGet(this, _RhAccordion_expandedIndexSet, "f").has(index)) {
return;
}
const header = this.headers[index];
const panel = this.panels[index];
if (header && panel) {
header.expanded = false;
panel.expanded = false;
}
__classPrivateFieldGet(this, _RhAccordion_expandedIndexSet, "f").delete(index);
};
_RhAccordion_onChange = function _RhAccordion_onChange(event) {
if (this.contains(event.target)) {
event.stopPropagation();
}
const index = __classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_getIndex).call(this, event.target);
if (event.expanded) {
__classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_expand).call(this, index);
}
else {
__classPrivateFieldGet(this, _RhAccordion_instances, "m", _RhAccordion_collapse).call(this, index);
}
};
_RhAccordion_allHeaders = function _RhAccordion_allHeaders(accordion = this) {
return Array.from(accordion.children ?? []).filter((x) => x instanceof RhAccordionHeader);
};
_RhAccordion_allPanels = function _RhAccordion_allPanels(accordion = this) {
return Array.from(accordion.children).filter((x => RhAccordion_1.isPanel(x)));
};
_RhAccordion_getIndex = function _RhAccordion_getIndex(el) {
if (RhAccordion_1.isHeader(el)) {
return this.headers.findIndex(header => header.id === el.id);
}
if (RhAccordion_1.isPanel(el)) {
return this.panels.findIndex(panel => panel.id === el.id);
}
__classPrivateFieldGet(this, _RhAccordion_logger, "f").warn('The #getIndex method expects to receive a header or panel element.');
return -1;
};
RhAccordion.styles = [styles];
__decorate([
property({ attribute: true, reflect: true })
], RhAccordion.prototype, "accents", void 0);
__decorate([
property({ reflect: true, type: Boolean })
], RhAccordion.prototype, "large", void 0);
__decorate([
property({ reflect: true, attribute: 'color-palette' })
], RhAccordion.prototype, "colorPalette", void 0);
__decorate([
property({
attribute: 'expanded-index',
converter: NumberListConverter,
hasChanged(value, old) {
return JSON.stringify(old) !== JSON.stringify(value);
},
})
], RhAccordion.prototype, "expandedIndex", null);
__decorate([
provide({ context })
], RhAccordion.prototype, "ctx", void 0);
__decorate([
observes('expandedIndex')
], RhAccordion.prototype, "updateExpanded", null);
__decorate([
observes('accents'),
observes('large'),
observes('expandedIndex')
], RhAccordion.prototype, "contextChanged", null);
RhAccordion = RhAccordion_1 = __decorate([
customElement('rh-accordion'),
colorPalettes,
themable
], RhAccordion);
export { RhAccordion };
//# sourceMappingURL=rh-accordion.js.map