/**
* @file ds-row.js
* @summary A custom Web Component for a Flexbox container for horizontal layouts.
* @description
* The `ds-row` component provides a flexible container for arranging items in a row.
* It leverages CSS Flexbox properties, exposing them as attributes for easy configuration.
*
* - The content inside `<ds-row>...</ds-row>` is rendered via the default slot.
* - The row is a flex container (`display: flex; flex-direction: row`).
* - Supports ARIA attributes: `aria-label`, `aria-describedby` for accessibility.
* - No role is set by default; set ARIA attributes as needed for context.
* - The component is styled via Shadow DOM; no `part` attribute is exposed.
* - No custom events are fired.
*
* @element ds-row
*
* @slot - Renders child elements inside the row container.
*
* @attr {string} justify-content - Aligns content along the main axis. Accepts CSS `justify-content` values (e.g., `flex-start`, `center`, `space-between`).
* @attr {string} align-items - Aligns content along the cross axis. Accepts CSS `align-items` values (e.g., `stretch`, `center`, `flex-end`).
* @attr {string} gap - Sets the spacing between flex items (e.g., "16px", "1rem").
* @attr {boolean} wrap - If present, sets `flex-wrap: wrap;` allowing items to wrap onto multiple lines.
* @attr {string} aria-label - Accessible label for the row container.
* @attr {string} aria-describedby - Reference to element(s) describing the row.
*
* @note The row is a flex container. No role is set by default; use ARIA attributes for accessibility context as needed.
* @note No custom events or part attributes are exposed.
*
* @example
* <!-- A basic row with default alignment and spacing -->
* <ds-row>
* <div>Item 1</div>
* <div>Item 2</div>
* </ds-row>
*
* @example
* <!-- A row with items centered and a specific gap -->
* <ds-row justify-content="center" align-items="center" gap="20px">
* <div>Centered Item A</div>
* <div>Centered Item B</div>
* </ds-row>
*
* @example
* <!-- A wrapping row with space between items -->
* <ds-row justify-content="space-between" wrap>
* <div>Long Item 1</div>
* <div>Item 2</div>
* <div>Another Item 3</div>
* <div>Short Item 4</div>
* </ds-row>
*/
import BaseComponent from './base-component.js';
class DsRow extends BaseComponent {
constructor() {
// ARIA config for ds-row (none required, but allow aria-label/aria-describedby)
const ariaConfig = {
staticAriaAttributes: {},
dynamicAriaAttributes: [
'aria-label',
'aria-describedby'
],
requiredAriaAttributes: [],
referenceAttributes: ['aria-describedby'],
};
const template = document.createElement('template');
template.innerHTML = `
<style>
@import url('/src/styles/styles.css');
:host {
display: block; /* Custom elements are inline by default */
}
.row-container {
display: flex;
flex-direction: row;
}
</style>
<div class="row-container">
<slot></slot>
</div>
`;
super({
template: template.innerHTML,
targetSelector: '.row-container',
ariaConfig,
events: [],
observedAttributes: ['justify-content', 'align-items', 'gap', 'wrap']
});
this.rowContainer = this.shadowRoot.querySelector('.row-container');
}
static get observedAttributes() {
return ['justify-content', 'align-items', 'gap', 'wrap', 'aria-label', 'aria-describedby'];
}
attributeChangedCallback(name, oldValue, newValue) {
super.attributeChangedCallback(name, oldValue, newValue);
if (oldValue === newValue) return;
switch (name) {
case 'justify-content':
this.rowContainer.style.justifyContent = newValue || '';
break;
case 'align-items':
this.rowContainer.style.alignItems = newValue || '';
break;
case 'gap':
this.rowContainer.style.gap = newValue || '';
break;
case 'wrap':
if (this.hasAttribute('wrap')) {
this.rowContainer.style.flexWrap = 'wrap';
} else {
this.rowContainer.style.flexWrap = 'nowrap';
}
break;
}
}
// ARIA property accessors
get ariaLabel() {
const value = this.rowContainer.getAttribute('aria-label');
return value === null ? null : value;
}
set ariaLabel(val) {
if (val === null || val === undefined) {
this.rowContainer.removeAttribute('aria-label');
} else {
this.rowContainer.setAttribute('aria-label', val);
}
}
get ariaDescribedBy() {
const value = this.rowContainer.getAttribute('aria-describedby');
return value === null ? null : value;
}
set ariaDescribedBy(val) {
if (val === null || val === undefined) {
this.rowContainer.removeAttribute('aria-describedby');
} else {
this.rowContainer.setAttribute('aria-describedby', val);
}
}
// Optionally override validateARIA if needed
}
// Register the custom element
if (!customElements.get('ds-row')) {
customElements.define('ds-row', DsRow);
}
// Export for use in other modules
export default DsRow;