import { LitElement } from 'lit'; import { A11yState } from './accessibility/a11y.state.enum'; import { A11yProperty } from './accessibility/a11y.property.enum'; import { Role } from './accessibility/role.enum'; export default class NileElement extends LitElement { protected BUBBLES: boolean = true; protected COMPOSED: boolean = true; protected CANCELABLE: boolean = false; /** Emits a custom event with more convenient defaults. */ emit(name: string, detail?: any, bubbles?: boolean , composed?: boolean, cancelable?: boolean) { const event = new CustomEvent( name, { bubbles: bubbles ?? this.BUBBLES, cancelable: cancelable ?? this.CANCELABLE, composed: composed ?? this.COMPOSED, detail } ); this.dispatchEvent(event); return event; } } export interface NileFormControl extends NileElement { // Form attributes name: string; value: unknown; disabled?: boolean; defaultValue?: unknown; defaultChecked?: boolean; form?: string; // Constraint validation attributes pattern?: string; min?: number | string | Date; max?: number | string | Date; step?: number | 'any'; required?: boolean; minlength?: number; maxlength?: number; // Form validation properties readonly validity: ValidityState; readonly validationMessage: string; // Form validation methods checkValidity: () => boolean; getForm: () => HTMLFormElement | null; reportValidity: () => boolean; setCustomValidity: (message: string) => void; } export interface A11yMetadataOptions { [Role.Role]?: string; [A11yState.AriaDisabled]?: boolean; [A11yState.AriaChecked]?: boolean; [A11yState.AriaSelected]?: boolean; [A11yState.AriaExpanded]?: boolean; [A11yState.AriaInvalid]?: boolean; [A11yState.AriaHidden]?: boolean; [A11yState.AriaPressed]?: boolean; [A11yState.AriaCurrent]?: string | boolean; [A11yState.AriaBusy]?: boolean; [A11yState.AriaRequired]?: boolean; [A11yState.AriaReadonly]?: boolean; [A11yState.AriaValueNow]?: string; [A11yState.AriaValueMin]?: string; [A11yState.AriaValueMax]?: string; [A11yState.AriaSort]?: string; [A11yProperty.AriaLabel]?: string; [A11yProperty.AriaLabelledby]?: string; [A11yProperty.AriaDescribedby]?: string; [A11yProperty.AriaErrormessage]?: string; [A11yProperty.AriaDetails]?: string; [A11yProperty.AriaControls]?: string; [A11yProperty.AriaOwns]?: string; [A11yProperty.AriaFlowto]?: string; [A11yProperty.AriaRoledescription]?: string; [A11yProperty.AriaModal]?: string; [A11yProperty.AriaOrientation]?: string; [A11yProperty.AriaKeyshortcuts]?: string; [A11yProperty.AriaValuenow]?: string; [A11yProperty.AriaValuemin]?: string; [A11yProperty.AriaValuemax]?: string; [A11yProperty.AriaValuetext]?: string; [A11yProperty.AriaLevel]?: string; [A11yProperty.AriaPosinset]?: string; [A11yProperty.AriaSetsize]?: string; [A11yProperty.AriaColcount]?: string; [A11yProperty.AriaColindex]?: string; [A11yProperty.AriaColspan]?: string; [A11yProperty.AriaRowcount]?: string; [A11yProperty.AriaRowindex]?: string; [A11yProperty.AriaRowspan]?: string; [A11yProperty.AriaAutocomplete]?: string; [A11yProperty.AriaPlaceholder]?: string; [A11yProperty.AriaHasPopup]?: string; [A11yProperty.Tabindex]?: string; [A11yProperty.AriaMultiselectable]?: string; } export function setupA11yMetadata(el: HTMLElement, options: A11yMetadataOptions = {}) { const role = options[Role.Role]; if (role && !el.hasAttribute(Role.Role)) { el.setAttribute(Role.Role, role); } const missingLabel = !el.hasAttribute(A11yProperty.AriaLabelledby) && !el.hasAttribute(A11yProperty.AriaLabel); if (missingLabel) { const fallbackLabel = options[A11yProperty.AriaLabel] ?? options[A11yProperty.AriaLabelledby] ?? (options[A11yProperty.AriaLabelledby] ? (el as any).value : null); if (fallbackLabel?.trim()) { el.setAttribute(A11yProperty.AriaLabel, fallbackLabel.trim()); } } const disabled = options[A11yState.AriaDisabled]; if (disabled === true) { el.setAttribute(A11yState.AriaDisabled, 'true'); } else if (disabled === false) { el.removeAttribute(A11yState.AriaDisabled); } for (const property of Object.values(A11yProperty)) { const value = (options as any)[property]; if (value !== undefined && !el.hasAttribute(property)) { el.setAttribute(property, String(value)); } } for (const state of Object.values(A11yState)) { if (state === A11yState.AriaDisabled) continue; const value = (options as any)[state]; if (value === undefined) continue; el.setAttribute(state, String(value)); } } function getLabelFromSlots(el: HTMLElement): string | null { const slot = el.querySelector('[slot="label"]'); return slot?.textContent?.trim() || null; } function getLabelFromText(el: HTMLElement): string | null { const text = el.textContent?.trim(); return text || null; }