('change', {
detail: { value },
bubbles: true,
composed: true,
});
this.dispatchEvent(changeEvent);
// Invoke callback if provided
if (this.onChange) {
this.onChange(changeEvent);
}
}
private renderLabel(ids: { inputId: string }) {
if (!this.label || this.noLabel) return '';
// Build position classes
const positionClasses: string[] = [];
if (isHorizontalLabel(this.labelPosition)) {
positionClasses.push('ag-form-control__label--horizontal');
positionClasses.push(`ag-form-control__label--${this.labelPosition}`);
} else if (this.labelPosition === 'bottom') {
positionClasses.push(`ag-form-control__label--${this.labelPosition}`);
}
return html`
`;
}
render() {
const ids = createFormControlIds('select');
const ariaDescribedBy = buildAriaDescribedBy({
helperId: ids.helperId,
errorId: ids.errorId,
hasHelper: !!this.helpText && !this.invalid,
hasError: !!this.invalid && !!this.errorMessage,
});
const isHorizontal = isHorizontalLabel(this.labelPosition);
const selectElement = html`
`;
const helperText = this.helpText && !this.invalid
? html`
${this.helpText}
`
: '';
const errorText = this.invalid && this.errorMessage
? html`
${this.errorMessage}
`
: '';
// For horizontal layout: [Label] [Select] structure with helper/error below
if (isHorizontal) {
return html`
${this.renderLabel(ids)}
${selectElement}
${helperText}
${errorText}
`;
}
// For bottom label: Select first, then helper/error, then label
if (this.labelPosition === 'bottom') {
return html`
${selectElement}
${helperText}
${errorText}
${this.renderLabel(ids)}
`;
}
// Top label (default): Label first, then select, then helper/error
return html`
${this.renderLabel(ids)}
${selectElement}
${helperText}
${errorText}
`;
}
}
// Register the custom element
if (!customElements.get('ag-select')) {
customElements.define('ag-select', Select);
}
declare global {
interface HTMLElementTagNameMap {
'ag-select': Select;
}
}