@synerity/ui
Version:
Synerity UI Component Library
477 lines (473 loc) • 1.55 MB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, forwardRef, Output, Input, Component, ChangeDetectionStrategy, ViewChild, HostListener, ViewChildren, ElementRef } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule, NgFor, NgIf, NgTemplateOutlet } from '@angular/common';
import * as i2 from '@angular/forms';
import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
import { fromEvent, interval } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
class Button {
variant = 'primary';
size = 'md';
disabled = false;
loading = false;
type = 'button';
fullWidth = false;
icon = null;
iconPosition = 'left';
click = new EventEmitter();
// ControlValueAccessor implementation
_value = false;
onChange = (value) => { };
onTouched = () => { };
get value() {
return this._value;
}
set value(val) {
this._value = val;
this.onChange(val);
}
onButtonClick(event) {
if (this.disabled || this.loading) {
event.preventDefault();
event.stopPropagation();
return;
}
// Emit the click event for external bindings
this.click.emit(event);
// Toggle value for button group usage
if (this.type === 'button') {
this.value = !this.value;
}
}
writeValue(value) {
this._value = value;
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
get buttonClasses() {
const baseClasses = 'sui-button';
const sizeClass = `sui-button-${this.size}`;
const variantClass = `sui-button-${this.variant}`;
const widthClass = this.fullWidth ? 'sui-button-full-width' : '';
const loadingClass = this.loading ? 'sui-button-loading' : '';
return `${baseClasses} ${sizeClass} ${variantClass} ${widthClass} ${loadingClass}`.trim();
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.3", type: Button, isStandalone: true, selector: "sui-button", inputs: { variant: "variant", size: "size", disabled: "disabled", loading: "loading", type: "type", fullWidth: "fullWidth", icon: "icon", iconPosition: "iconPosition" }, outputs: { click: "click" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Button),
multi: true
}
], ngImport: i0, template: "<button \n [type]=\"type\"\n [disabled]=\"disabled || loading\"\n [class]=\"buttonClasses\"\n (click)=\"onButtonClick($event); $event.stopPropagation()\"\n [attr.aria-disabled]=\"disabled || loading\"\n [attr.aria-busy]=\"loading\"\n role=\"button\"\n tabindex=\"0\">\n \n <!-- Loading Spinner -->\n <svg *ngIf=\"loading\" \n class=\"sui-button-spinner\" \n xmlns=\"http://www.w3.org/2000/svg\" \n fill=\"none\" \n viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n\n <!-- Left Icon -->\n <ng-container *ngIf=\"icon && iconPosition === 'left' && !loading\">\n <i class=\"sui-button-icon-left\" [ngClass]=\"icon\"></i>\n </ng-container>\n\n <!-- Button Content -->\n <ng-content></ng-content>\n\n <!-- Right Icon -->\n <ng-container *ngIf=\"icon && iconPosition === 'right' && !loading\">\n <i class=\"sui-button-icon-right\" [ngClass]=\"icon\"></i>\n </ng-container>\n</button>\n", styles: [".sui-button{display:inline-flex;align-items:center;justify-content:center;font-weight:500;transition:all .2s ease-in-out;border-radius:.375rem;border:1px solid transparent;cursor:pointer;text-decoration:none;outline:none;position:relative;overflow:hidden}.sui-button:focus{outline:2px solid transparent;outline-offset:2px}.sui-button:disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.sui-button-sm{padding:.375rem .75rem;font-size:.875rem;line-height:1.25rem}.sui-button-md{padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem}.sui-button-lg{padding:.75rem 1.5rem;font-size:1rem;line-height:1.5rem}.sui-button-primary{background-color:#3b82f6;color:#fff;border-color:#3b82f6}.sui-button-primary:hover:not(:disabled){background-color:#2563eb;border-color:#2563eb}.sui-button-primary:focus{box-shadow:0 0 0 3px #3b82f680}.sui-button-secondary{background-color:#6b7280;color:#fff;border-color:#6b7280}.sui-button-secondary:hover:not(:disabled){background-color:#4b5563;border-color:#4b5563}.sui-button-outline{background-color:transparent;color:#374151;border-color:#d1d5db}.sui-button-outline:hover:not(:disabled){background-color:#f9fafb;border-color:#9ca3af}.sui-button-ghost{background-color:transparent;color:#374151;border-color:transparent}.sui-button-ghost:hover:not(:disabled){background-color:#f3f4f6}.sui-button-danger{background-color:#dc2626;color:#fff;border-color:#dc2626}.sui-button-danger:hover:not(:disabled){background-color:#b91c1c;border-color:#b91c1c}.sui-button-full-width{width:100%}.sui-button-loading{position:relative;color:transparent}.sui-button-loading:after{content:\"\";position:absolute;width:1rem;height:1rem;top:50%;left:50%;margin-left:-.5rem;margin-top:-.5rem;border:2px solid transparent;border-top-color:currentColor;border-radius:50%;animation:sui-button-spin 1s linear infinite}@keyframes sui-button-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: Button, decorators: [{
type: Component,
args: [{ selector: 'sui-button', standalone: true, imports: [CommonModule], providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Button),
multi: true
}
], template: "<button \n [type]=\"type\"\n [disabled]=\"disabled || loading\"\n [class]=\"buttonClasses\"\n (click)=\"onButtonClick($event); $event.stopPropagation()\"\n [attr.aria-disabled]=\"disabled || loading\"\n [attr.aria-busy]=\"loading\"\n role=\"button\"\n tabindex=\"0\">\n \n <!-- Loading Spinner -->\n <svg *ngIf=\"loading\" \n class=\"sui-button-spinner\" \n xmlns=\"http://www.w3.org/2000/svg\" \n fill=\"none\" \n viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n\n <!-- Left Icon -->\n <ng-container *ngIf=\"icon && iconPosition === 'left' && !loading\">\n <i class=\"sui-button-icon-left\" [ngClass]=\"icon\"></i>\n </ng-container>\n\n <!-- Button Content -->\n <ng-content></ng-content>\n\n <!-- Right Icon -->\n <ng-container *ngIf=\"icon && iconPosition === 'right' && !loading\">\n <i class=\"sui-button-icon-right\" [ngClass]=\"icon\"></i>\n </ng-container>\n</button>\n", styles: [".sui-button{display:inline-flex;align-items:center;justify-content:center;font-weight:500;transition:all .2s ease-in-out;border-radius:.375rem;border:1px solid transparent;cursor:pointer;text-decoration:none;outline:none;position:relative;overflow:hidden}.sui-button:focus{outline:2px solid transparent;outline-offset:2px}.sui-button:disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.sui-button-sm{padding:.375rem .75rem;font-size:.875rem;line-height:1.25rem}.sui-button-md{padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem}.sui-button-lg{padding:.75rem 1.5rem;font-size:1rem;line-height:1.5rem}.sui-button-primary{background-color:#3b82f6;color:#fff;border-color:#3b82f6}.sui-button-primary:hover:not(:disabled){background-color:#2563eb;border-color:#2563eb}.sui-button-primary:focus{box-shadow:0 0 0 3px #3b82f680}.sui-button-secondary{background-color:#6b7280;color:#fff;border-color:#6b7280}.sui-button-secondary:hover:not(:disabled){background-color:#4b5563;border-color:#4b5563}.sui-button-outline{background-color:transparent;color:#374151;border-color:#d1d5db}.sui-button-outline:hover:not(:disabled){background-color:#f9fafb;border-color:#9ca3af}.sui-button-ghost{background-color:transparent;color:#374151;border-color:transparent}.sui-button-ghost:hover:not(:disabled){background-color:#f3f4f6}.sui-button-danger{background-color:#dc2626;color:#fff;border-color:#dc2626}.sui-button-danger:hover:not(:disabled){background-color:#b91c1c;border-color:#b91c1c}.sui-button-full-width{width:100%}.sui-button-loading{position:relative;color:transparent}.sui-button-loading:after{content:\"\";position:absolute;width:1rem;height:1rem;top:50%;left:50%;margin-left:-.5rem;margin-top:-.5rem;border:2px solid transparent;border-top-color:currentColor;border-radius:50%;animation:sui-button-spin 1s linear infinite}@keyframes sui-button-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
}], propDecorators: { variant: [{
type: Input
}], size: [{
type: Input
}], disabled: [{
type: Input
}], loading: [{
type: Input
}], type: [{
type: Input
}], fullWidth: [{
type: Input
}], icon: [{
type: Input
}], iconPosition: [{
type: Input
}], click: [{
type: Output
}] } });
class InputText {
placeholder = '';
disabled = false;
readonly = false;
required = false;
size = 'md';
variant = 'default';
validationState = 'none';
errorMessage = '';
helperText = '';
label = '';
type = 'text';
maxlength = null;
minlength = null;
pattern = null;
autocomplete = null;
autofocus = false;
spellcheck = true;
prefix = null;
suffix = null;
clearable = false;
showPasswordToggle = false;
blur = new EventEmitter();
focus = new EventEmitter();
input = new EventEmitter();
clear = new EventEmitter();
// ControlValueAccessor implementation
_value = '';
onChange = (value) => { };
onTouched = () => { };
// Internal state
_showPassword = false;
_focused = false;
get value() {
return this._value;
}
set value(val) {
this._value = val;
this.onChange(val);
}
get showPassword() {
return this._showPassword;
}
get focused() {
return this._focused;
}
get inputType() {
if (this.type === 'password' && this.showPasswordToggle) {
return this._showPassword ? 'text' : 'password';
}
return this.type;
}
ngOnInit() {
// Component initialization
}
ngOnDestroy() {
// Cleanup
}
onInput(event) {
const target = event.target;
this.value = target.value;
this.input.emit(event);
}
onBlur(event) {
this._focused = false;
this.onTouched();
this.blur.emit(event);
}
onFocus(event) {
this._focused = true;
this.focus.emit(event);
}
onClear() {
this.value = '';
this.clear.emit();
}
togglePasswordVisibility() {
this._showPassword = !this._showPassword;
}
writeValue(value) {
this._value = value || '';
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
get inputClasses() {
const baseClasses = 'sui-input-text';
const sizeClass = `sui-input-text-${this.size}`;
const variantClass = `sui-input-text-${this.variant}`;
const stateClass = this.validationState !== 'none' ? `sui-input-text-${this.validationState}` : '';
const disabledClass = this.disabled ? 'sui-input-text-disabled' : '';
const readonlyClass = this.readonly ? 'sui-input-text-readonly' : '';
return `${baseClasses} ${sizeClass} ${variantClass} ${stateClass} ${disabledClass} ${readonlyClass}`.trim();
}
get containerClasses() {
return 'relative';
}
get hasValue() {
return !!(this.value && this.value.length > 0);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: InputText, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.3", type: InputText, isStandalone: true, selector: "sui-input-text", inputs: { placeholder: "placeholder", disabled: "disabled", readonly: "readonly", required: "required", size: "size", variant: "variant", validationState: "validationState", errorMessage: "errorMessage", helperText: "helperText", label: "label", type: "type", maxlength: "maxlength", minlength: "minlength", pattern: "pattern", autocomplete: "autocomplete", autofocus: "autofocus", spellcheck: "spellcheck", prefix: "prefix", suffix: "suffix", clearable: "clearable", showPasswordToggle: "showPasswordToggle" }, outputs: { blur: "blur", focus: "focus", input: "input", clear: "clear" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputText),
multi: true
}
], ngImport: i0, template: "<div class=\"sui-input-text-container\">\n <!-- Label -->\n <label *ngIf=\"label\" \n [for]=\"label\"\n class=\"sui-input-text-label\">\n {{ label }}\n <span *ngIf=\"required\" class=\"sui-input-text-required\">*</span>\n </label>\n\n <!-- Input Container -->\n <div class=\"sui-input-text-wrapper\">\n <!-- Prefix -->\n <div *ngIf=\"prefix\" \n class=\"sui-input-text-prefix\">\n <span>{{ prefix }}</span>\n </div>\n\n <!-- Input Field -->\n <input\n [type]=\"inputType\"\n [value]=\"value\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [attr.maxlength]=\"maxlength\"\n [attr.minlength]=\"minlength\"\n [attr.pattern]=\"pattern\"\n [attr.autocomplete]=\"autocomplete\"\n [attr.autofocus]=\"autofocus\"\n [attr.spellcheck]=\"spellcheck\"\n [class]=\"inputClasses\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur($event)\"\n (focus)=\"onFocus($event)\"\n [attr.aria-invalid]=\"validationState === 'invalid'\"\n [attr.aria-describedby]=\"(errorMessage || helperText) ? (label + '-description') : null\"\n role=\"textbox\"\n tabindex=\"0\">\n\n <!-- Suffix / Clear Button / Password Toggle -->\n <div class=\"sui-input-text-suffix\">\n <!-- Clear Button -->\n <button *ngIf=\"clearable && hasValue && !disabled && !readonly\"\n type=\"button\"\n (click)=\"onClear()\"\n class=\"sui-input-text-clear\"\n aria-label=\"Clear input\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n </svg>\n </button>\n\n <!-- Password Toggle -->\n <button *ngIf=\"type === 'password' && showPasswordToggle && !disabled && !readonly\"\n type=\"button\"\n (click)=\"togglePasswordVisibility()\"\n class=\"sui-input-text-password-toggle\"\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\n <svg *ngIf=\"!showPassword\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\"></path>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z\"></path>\n </svg>\n <svg *ngIf=\"showPassword\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21\"></path>\n </svg>\n </button>\n\n <!-- Suffix -->\n <span *ngIf=\"suffix\" class=\"sui-input-text-suffix-text\">{{ suffix }}</span>\n </div>\n </div>\n\n <!-- Helper Text / Error Message -->\n <div *ngIf=\"errorMessage || helperText\" \n [id]=\"label + '-description'\"\n class=\"sui-input-text-helper\"\n [class.sui-input-text-error]=\"validationState === 'invalid'\">\n {{ errorMessage || helperText }}\n </div>\n</div>\n", styles: [".sui-input-text-container{width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif}.sui-input-text-label{display:inline-block;font-size:.875rem;font-weight:600;color:var(--sui-gray-700, #374151);margin-bottom:.5rem;letter-spacing:.01em;transition:color .2s ease}.sui-input-text-label .sui-input-text-required{color:var(--sui-danger-500, #ef4444);margin-left:.125rem;font-weight:700}.sui-input-text-wrapper{position:relative;display:flex;align-items:center;width:100%}.sui-input-text{width:100%;border:2px solid var(--sui-gray-300, #d1d5db);border-radius:.5rem;padding:.625rem .875rem;font-size:.9375rem;font-weight:400;line-height:1.5;color:var(--sui-gray-900, #111827);background-color:var(--sui-white, #ffffff);transition:all .2s cubic-bezier(.4,0,.2,1);outline:none;box-shadow:0 1px 2px #00000008}.sui-input-text::placeholder{color:var(--sui-gray-400, #9ca3af);opacity:1;transition:opacity .2s ease}.sui-input-text:hover:not(:disabled):not(:read-only){border-color:var(--sui-gray-400, #9ca3af);box-shadow:0 1px 3px #00000014}.sui-input-text:focus{border-color:var(--sui-primary-500, #3b82f6);box-shadow:0 0 0 4px #3b82f61f,0 1px 3px #0000001a;background-color:var(--sui-white, #ffffff)}.sui-input-text:focus::placeholder{opacity:.6}.sui-input-text:disabled{background-color:var(--sui-gray-50, #f9fafb);border-color:var(--sui-gray-200, #e5e7eb);color:var(--sui-gray-500, #6b7280);cursor:not-allowed;box-shadow:none;opacity:.7}.sui-input-text:read-only{background-color:var(--sui-gray-50, #f9fafb);border-color:var(--sui-gray-200, #e5e7eb);cursor:default;box-shadow:none}.sui-input-text-sm{padding:.5rem .75rem;font-size:.8125rem;border-radius:.375rem;line-height:1.4}.sui-input-text-md{padding:.625rem .875rem;font-size:.9375rem;border-radius:.5rem;line-height:1.5}.sui-input-text-lg{padding:.875rem 1.125rem;font-size:1rem;border-radius:.625rem;line-height:1.6;font-weight:500}.sui-input-text-filled{background-color:var(--sui-gray-100, #f3f4f6);border-color:transparent;box-shadow:inset 0 1px 2px #0000000d}.sui-input-text-filled:hover:not(:disabled):not(:read-only){background-color:var(--sui-gray-200, #e5e7eb);border-color:transparent}.sui-input-text-filled:focus{background-color:var(--sui-white, #ffffff);border-color:var(--sui-primary-500, #3b82f6);box-shadow:0 0 0 4px #3b82f61f,inset 0 1px 2px #0000000d}.sui-input-text-outlined{background-color:transparent;border-width:2px;border-color:var(--sui-gray-300, #d1d5db)}.sui-input-text-outlined:focus{background-color:var(--sui-white, #ffffff)}.sui-input-text-valid{border-color:var(--sui-success-500, #10b981);background-color:#10b98105}.sui-input-text-valid:focus{border-color:var(--sui-success-600, #059669);box-shadow:0 0 0 4px #10b9811f,0 1px 3px #0000001a}.sui-input-text-valid:hover:not(:disabled):not(:read-only){border-color:var(--sui-success-600, #059669)}.sui-input-text-invalid{border-color:var(--sui-danger-500, #ef4444);background-color:#ef444405}.sui-input-text-invalid:focus{border-color:var(--sui-danger-600, #dc2626);box-shadow:0 0 0 4px #ef44441f,0 1px 3px #0000001a}.sui-input-text-invalid:hover:not(:disabled):not(:read-only){border-color:var(--sui-danger-600, #dc2626)}.sui-input-text-prefix,.sui-input-text-suffix{position:absolute;display:flex;align-items:center;gap:.375rem;pointer-events:none;z-index:1}.sui-input-text-prefix{left:.875rem;font-size:.9375rem;font-weight:500;color:var(--sui-gray-500, #6b7280)}.sui-input-text-prefix+.sui-input-text{padding-left:2.5rem}.sui-input-text-suffix{right:.875rem;pointer-events:auto}.sui-input-text-suffix-text{font-size:.9375rem;font-weight:500;color:var(--sui-gray-500, #6b7280)}.sui-input-text-clear,.sui-input-text-password-toggle{display:flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;background:transparent;border:none;border-radius:.375rem;color:var(--sui-gray-500, #6b7280);cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);pointer-events:auto}.sui-input-text-clear svg,.sui-input-text-password-toggle svg{width:1rem;height:1rem;stroke-width:2.5}.sui-input-text-clear:hover,.sui-input-text-password-toggle:hover{color:var(--sui-gray-700, #374151);background-color:var(--sui-gray-100, #f3f4f6);transform:scale(1.05)}.sui-input-text-clear:active,.sui-input-text-password-toggle:active{transform:scale(.95);background-color:var(--sui-gray-200, #e5e7eb)}.sui-input-text-clear:focus-visible,.sui-input-text-password-toggle:focus-visible{outline:2px solid var(--sui-primary-500, #3b82f6);outline-offset:2px}.sui-input-text-wrapper:has(.sui-input-text-clear) .sui-input-text,.sui-input-text-wrapper:has(.sui-input-text-password-toggle) .sui-input-text{padding-right:2.75rem}.sui-input-text-wrapper:has(.sui-input-text-suffix-text) .sui-input-text{padding-right:3rem}.sui-input-text-helper{display:block;margin-top:.375rem;font-size:.8125rem;line-height:1.4;color:var(--sui-gray-600, #4b5563);font-weight:400;transition:color .2s ease}.sui-input-text-helper.sui-input-text-error{color:var(--sui-danger-600, #dc2626);font-weight:500}.sui-input-text-disabled .sui-input-text-label{color:var(--sui-gray-500, #6b7280);opacity:.7}.sui-input-text-readonly .sui-input-text-label{color:var(--sui-gray-600, #4b5563)}.sui-input-text-wrapper:focus-within .sui-input-text-prefix,.sui-input-text-wrapper:focus-within .sui-input-text-suffix-text{color:var(--sui-primary-600, #2563eb)}@keyframes shake{0%,to{transform:translate(0)}25%{transform:translate(-4px)}75%{transform:translate(4px)}}.sui-input-text-invalid:focus{animation:shake .3s ease-in-out}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: InputText, decorators: [{
type: Component,
args: [{ selector: 'sui-input-text', standalone: true, imports: [CommonModule], providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputText),
multi: true
}
], template: "<div class=\"sui-input-text-container\">\n <!-- Label -->\n <label *ngIf=\"label\" \n [for]=\"label\"\n class=\"sui-input-text-label\">\n {{ label }}\n <span *ngIf=\"required\" class=\"sui-input-text-required\">*</span>\n </label>\n\n <!-- Input Container -->\n <div class=\"sui-input-text-wrapper\">\n <!-- Prefix -->\n <div *ngIf=\"prefix\" \n class=\"sui-input-text-prefix\">\n <span>{{ prefix }}</span>\n </div>\n\n <!-- Input Field -->\n <input\n [type]=\"inputType\"\n [value]=\"value\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [attr.maxlength]=\"maxlength\"\n [attr.minlength]=\"minlength\"\n [attr.pattern]=\"pattern\"\n [attr.autocomplete]=\"autocomplete\"\n [attr.autofocus]=\"autofocus\"\n [attr.spellcheck]=\"spellcheck\"\n [class]=\"inputClasses\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur($event)\"\n (focus)=\"onFocus($event)\"\n [attr.aria-invalid]=\"validationState === 'invalid'\"\n [attr.aria-describedby]=\"(errorMessage || helperText) ? (label + '-description') : null\"\n role=\"textbox\"\n tabindex=\"0\">\n\n <!-- Suffix / Clear Button / Password Toggle -->\n <div class=\"sui-input-text-suffix\">\n <!-- Clear Button -->\n <button *ngIf=\"clearable && hasValue && !disabled && !readonly\"\n type=\"button\"\n (click)=\"onClear()\"\n class=\"sui-input-text-clear\"\n aria-label=\"Clear input\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"></path>\n </svg>\n </button>\n\n <!-- Password Toggle -->\n <button *ngIf=\"type === 'password' && showPasswordToggle && !disabled && !readonly\"\n type=\"button\"\n (click)=\"togglePasswordVisibility()\"\n class=\"sui-input-text-password-toggle\"\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\n <svg *ngIf=\"!showPassword\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\"></path>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z\"></path>\n </svg>\n <svg *ngIf=\"showPassword\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21\"></path>\n </svg>\n </button>\n\n <!-- Suffix -->\n <span *ngIf=\"suffix\" class=\"sui-input-text-suffix-text\">{{ suffix }}</span>\n </div>\n </div>\n\n <!-- Helper Text / Error Message -->\n <div *ngIf=\"errorMessage || helperText\" \n [id]=\"label + '-description'\"\n class=\"sui-input-text-helper\"\n [class.sui-input-text-error]=\"validationState === 'invalid'\">\n {{ errorMessage || helperText }}\n </div>\n</div>\n", styles: [".sui-input-text-container{width:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif}.sui-input-text-label{display:inline-block;font-size:.875rem;font-weight:600;color:var(--sui-gray-700, #374151);margin-bottom:.5rem;letter-spacing:.01em;transition:color .2s ease}.sui-input-text-label .sui-input-text-required{color:var(--sui-danger-500, #ef4444);margin-left:.125rem;font-weight:700}.sui-input-text-wrapper{position:relative;display:flex;align-items:center;width:100%}.sui-input-text{width:100%;border:2px solid var(--sui-gray-300, #d1d5db);border-radius:.5rem;padding:.625rem .875rem;font-size:.9375rem;font-weight:400;line-height:1.5;color:var(--sui-gray-900, #111827);background-color:var(--sui-white, #ffffff);transition:all .2s cubic-bezier(.4,0,.2,1);outline:none;box-shadow:0 1px 2px #00000008}.sui-input-text::placeholder{color:var(--sui-gray-400, #9ca3af);opacity:1;transition:opacity .2s ease}.sui-input-text:hover:not(:disabled):not(:read-only){border-color:var(--sui-gray-400, #9ca3af);box-shadow:0 1px 3px #00000014}.sui-input-text:focus{border-color:var(--sui-primary-500, #3b82f6);box-shadow:0 0 0 4px #3b82f61f,0 1px 3px #0000001a;background-color:var(--sui-white, #ffffff)}.sui-input-text:focus::placeholder{opacity:.6}.sui-input-text:disabled{background-color:var(--sui-gray-50, #f9fafb);border-color:var(--sui-gray-200, #e5e7eb);color:var(--sui-gray-500, #6b7280);cursor:not-allowed;box-shadow:none;opacity:.7}.sui-input-text:read-only{background-color:var(--sui-gray-50, #f9fafb);border-color:var(--sui-gray-200, #e5e7eb);cursor:default;box-shadow:none}.sui-input-text-sm{padding:.5rem .75rem;font-size:.8125rem;border-radius:.375rem;line-height:1.4}.sui-input-text-md{padding:.625rem .875rem;font-size:.9375rem;border-radius:.5rem;line-height:1.5}.sui-input-text-lg{padding:.875rem 1.125rem;font-size:1rem;border-radius:.625rem;line-height:1.6;font-weight:500}.sui-input-text-filled{background-color:var(--sui-gray-100, #f3f4f6);border-color:transparent;box-shadow:inset 0 1px 2px #0000000d}.sui-input-text-filled:hover:not(:disabled):not(:read-only){background-color:var(--sui-gray-200, #e5e7eb);border-color:transparent}.sui-input-text-filled:focus{background-color:var(--sui-white, #ffffff);border-color:var(--sui-primary-500, #3b82f6);box-shadow:0 0 0 4px #3b82f61f,inset 0 1px 2px #0000000d}.sui-input-text-outlined{background-color:transparent;border-width:2px;border-color:var(--sui-gray-300, #d1d5db)}.sui-input-text-outlined:focus{background-color:var(--sui-white, #ffffff)}.sui-input-text-valid{border-color:var(--sui-success-500, #10b981);background-color:#10b98105}.sui-input-text-valid:focus{border-color:var(--sui-success-600, #059669);box-shadow:0 0 0 4px #10b9811f,0 1px 3px #0000001a}.sui-input-text-valid:hover:not(:disabled):not(:read-only){border-color:var(--sui-success-600, #059669)}.sui-input-text-invalid{border-color:var(--sui-danger-500, #ef4444);background-color:#ef444405}.sui-input-text-invalid:focus{border-color:var(--sui-danger-600, #dc2626);box-shadow:0 0 0 4px #ef44441f,0 1px 3px #0000001a}.sui-input-text-invalid:hover:not(:disabled):not(:read-only){border-color:var(--sui-danger-600, #dc2626)}.sui-input-text-prefix,.sui-input-text-suffix{position:absolute;display:flex;align-items:center;gap:.375rem;pointer-events:none;z-index:1}.sui-input-text-prefix{left:.875rem;font-size:.9375rem;font-weight:500;color:var(--sui-gray-500, #6b7280)}.sui-input-text-prefix+.sui-input-text{padding-left:2.5rem}.sui-input-text-suffix{right:.875rem;pointer-events:auto}.sui-input-text-suffix-text{font-size:.9375rem;font-weight:500;color:var(--sui-gray-500, #6b7280)}.sui-input-text-clear,.sui-input-text-password-toggle{display:flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;background:transparent;border:none;border-radius:.375rem;color:var(--sui-gray-500, #6b7280);cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);pointer-events:auto}.sui-input-text-clear svg,.sui-input-text-password-toggle svg{width:1rem;height:1rem;stroke-width:2.5}.sui-input-text-clear:hover,.sui-input-text-password-toggle:hover{color:var(--sui-gray-700, #374151);background-color:var(--sui-gray-100, #f3f4f6);transform:scale(1.05)}.sui-input-text-clear:active,.sui-input-text-password-toggle:active{transform:scale(.95);background-color:var(--sui-gray-200, #e5e7eb)}.sui-input-text-clear:focus-visible,.sui-input-text-password-toggle:focus-visible{outline:2px solid var(--sui-primary-500, #3b82f6);outline-offset:2px}.sui-input-text-wrapper:has(.sui-input-text-clear) .sui-input-text,.sui-input-text-wrapper:has(.sui-input-text-password-toggle) .sui-input-text{padding-right:2.75rem}.sui-input-text-wrapper:has(.sui-input-text-suffix-text) .sui-input-text{padding-right:3rem}.sui-input-text-helper{display:block;margin-top:.375rem;font-size:.8125rem;line-height:1.4;color:var(--sui-gray-600, #4b5563);font-weight:400;transition:color .2s ease}.sui-input-text-helper.sui-input-text-error{color:var(--sui-danger-600, #dc2626);font-weight:500}.sui-input-text-disabled .sui-input-text-label{color:var(--sui-gray-500, #6b7280);opacity:.7}.sui-input-text-readonly .sui-input-text-label{color:var(--sui-gray-600, #4b5563)}.sui-input-text-wrapper:focus-within .sui-input-text-prefix,.sui-input-text-wrapper:focus-within .sui-input-text-suffix-text{color:var(--sui-primary-600, #2563eb)}@keyframes shake{0%,to{transform:translate(0)}25%{transform:translate(-4px)}75%{transform:translate(4px)}}.sui-input-text-invalid:focus{animation:shake .3s ease-in-out}\n"] }]
}], propDecorators: { placeholder: [{
type: Input
}], disabled: [{
type: Input
}], readonly: [{
type: Input
}], required: [{
type: Input
}], size: [{
type: Input
}], variant: [{
type: Input
}], validationState: [{
type: Input
}], errorMessage: [{
type: Input
}], helperText: [{
type: Input
}], label: [{
type: Input
}], type: [{
type: Input
}], maxlength: [{
type: Input
}], minlength: [{
type: Input
}], pattern: [{
type: Input
}], autocomplete: [{
type: Input
}], autofocus: [{
type: Input
}], spellcheck: [{
type: Input
}], prefix: [{
type: Input
}], suffix: [{
type: Input
}], clearable: [{
type: Input
}], showPasswordToggle: [{
type: Input
}], blur: [{
type: Output
}], focus: [{
type: Output
}], input: [{
type: Output
}], clear: [{
type: Output
}] } });
class Card {
variant = 'default';
size = 'md';
clickable = false;
disabled = false;
header = '';
subheader = '';
showHeader = true;
showFooter = false;
loading = false;
hoverable = false;
borderless = false;
click = new EventEmitter();
headerClick = new EventEmitter();
onCardClick(event) {
if (this.disabled || this.loading || !this.clickable) {
return;
}
this.click.emit(event);
}
onHeaderClick(event) {
if (this.disabled || this.loading) {
return;
}
this.headerClick.emit(event);
}
get cardClasses() {
const baseClasses = 'sui-card';
const sizeClass = `sui-card-${this.size}`;
const variantClass = `sui-card-${this.variant}`;
const interactiveClass = this.clickable && !this.disabled ? 'sui-card-clickable' : '';
const hoverClass = this.hoverable && !this.disabled ? 'sui-card-hoverable' : '';
const disabledClass = this.disabled ? 'sui-card-disabled' : '';
const borderlessClass = this.borderless ? 'sui-card-borderless' : '';
return `${baseClasses} ${sizeClass} ${variantClass} ${interactiveClass} ${hoverClass} ${disabledClass} ${borderlessClass}`.trim();
}
get headerClasses() {
const baseClasses = 'font-semibold text-gray-900';
const sizeClasses = {
sm: 'text-base',
md: 'text-lg',
lg: 'text-xl'
};
const clickableClasses = this.clickable ? 'cursor-pointer hover:text-primary-600' : '';
return `${baseClasses} ${sizeClasses[this.size]} ${clickableClasses}`.trim();
}
get subheaderClasses() {
return 'text-sm text-gray-600 mt-1';
}
get contentClasses() {
const sizeClasses = {
sm: 'mt-3',
md: 'mt-4',
lg: 'mt-6'
};
return `${sizeClasses[this.size]}`;
}
get footerClasses() {
const sizeClasses = {
sm: 'mt-3 pt-3',
md: 'mt-4 pt-4',
lg: 'mt-6 pt-6'
};
return `border-t border-gray-200 ${sizeClasses[this.size]}`;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: Card, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.3", type: Card, isStandalone: true, selector: "sui-card", inputs: { variant: "variant", size: "size", clickable: "clickable", disabled: "disabled", header: "header", subheader: "subheader", showHeader: "showHeader", showFooter: "showFooter", loading: "loading", hoverable: "hoverable", borderless: "borderless" }, outputs: { click: "click", headerClick: "headerClick" }, ngImport: i0, template: "<div [class]=\"cardClasses\" \n (click)=\"onCardClick($event)\"\n [attr.role]=\"clickable ? 'button' : null\"\n [attr.tabindex]=\"clickable && !disabled ? '0' : null\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-busy]=\"loading\">\n\n <!-- Loading Overlay -->\n <div *ngIf=\"loading\" class=\"sui-card-loading-overlay\">\n <svg class=\"sui-card-loading-spinner\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </div>\n\n <!-- Card Header -->\n <div *ngIf=\"showHeader && (header || subheader)\" \n (click)=\"onHeaderClick($event)\"\n class=\"mb-0\">\n <h3 *ngIf=\"header\" [class]=\"headerClasses\">\n {{ header }}\n </h3>\n <p *ngIf=\"subheader\" [class]=\"subheaderClasses\">\n {{ subheader }}\n </p>\n </div>\n\n <!-- Card Content -->\n <div [class]=\"contentClasses\">\n <ng-content></ng-content>\n </div>\n\n <!-- Card Footer -->\n <div *ngIf=\"showFooter\" [class]=\"footerClasses\">\n <ng-content select=\"[slot=footer]\"></ng-content>\n </div>\n</div>\n", styles: [".sui-card{position:relative;display:block;background-color:var(--sui-gray-50, #ffffff);border:1px solid var(--sui-gray-200, #e5e7eb);border-radius:.5rem;padding:1rem;transition:all .2s ease-in-out}.sui-card-default{background-color:var(--sui-gray-50, #ffffff);border:1px solid var(--sui-gray-200, #e5e7eb)}.sui-card-outlined{background-color:transparent;border:2px solid var(--sui-primary-200, #bfdbfe)}.sui-card-elevated{background-color:var(--sui-gray-50, #ffffff);border:1px solid var(--sui-gray-200, #e5e7eb);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.sui-card-filled{background-color:var(--sui-primary-50, #eff6ff);border:1px solid var(--sui-primary-200, #bfdbfe)}.sui-card-sm{padding:.75rem}.sui-card-md{padding:1rem}.sui-card-lg{padding:1.5rem}.sui-card-clickable{cursor:pointer}.sui-card-clickable:hover,.sui-card-hoverable:hover{border-color:var(--sui-primary-300, #93c5fd);box-shadow:0 2px 4px -1px #0000001a}.sui-card-disabled{opacity:.6;cursor:not-allowed}.sui-card-borderless{border:none}.sui-card-loading-overlay{position:absolute;inset:0;background-color:#ffffffbf;display:flex;align-items:center;justify-content:center;border-radius:.5rem;z-index:10}.sui-card-loading-spinner{animation:spin 1s linear infinite;width:1.5rem;height:1.5rem;color:var(--sui-primary-600, #2563eb)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: Card, decorators: [{
type: Component,
args: [{ selector: 'sui-card', standalone: true, imports: [CommonModule], template: "<div [class]=\"cardClasses\" \n (click)=\"onCardClick($event)\"\n [attr.role]=\"clickable ? 'button' : null\"\n [attr.tabindex]=\"clickable && !disabled ? '0' : null\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-busy]=\"loading\">\n\n <!-- Loading Overlay -->\n <div *ngIf=\"loading\" class=\"sui-card-loading-overlay\">\n <svg class=\"sui-card-loading-spinner\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n </div>\n\n <!-- Card Header -->\n <div *ngIf=\"showHeader && (header || subheader)\" \n (click)=\"onHeaderClick($event)\"\n class=\"mb-0\">\n <h3 *ngIf=\"header\" [class]=\"headerClasses\">\n {{ header }}\n </h3>\n <p *ngIf=\"subheader\" [class]=\"subheaderClasses\">\n {{ subheader }}\n </p>\n </div>\n\n <!-- Card Content -->\n <div [class]=\"contentClasses\">\n <ng-content></ng-content>\n </div>\n\n <!-- Card Footer -->\n <div *ngIf=\"showFooter\" [class]=\"footerClasses\">\n <ng-content select=\"[slot=footer]\"></ng-content>\n </div>\n</div>\n", styles: [".sui-card{position:relative;display:block;background-color:var(--sui-gray-50, #ffffff);border:1px solid var(--sui-gray-200, #e5e7eb);border-radius:.5rem;padding:1rem;transition:all .2s ease-in-out}.sui-card-default{background-color:var(--sui-gray-50, #ffffff);border:1px solid var(--sui-gray-200, #e5e7eb)}.sui-card-outlined{background-color:transparent;border:2px solid var(--sui-primary-200, #bfdbfe)}.sui-card-elevated{background-color:var(--sui-gray-50, #ffffff);border:1px solid var(--sui-gray-200, #e5e7eb);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.sui-card-filled{background-color:var(--sui-primary-50, #eff6ff);border:1px solid var(--sui-primary-200, #bfdbfe)}.sui-card-sm{padding:.75rem}.sui-card-md{padding:1rem}.sui-card-lg{padding:1.5rem}.sui-card-clickable{cursor:pointer}.sui-card-clickable:hover,.sui-card-hoverable:hover{border-color:var(--sui-primary-300, #93c5fd);box-shadow:0 2px 4px -1px #0000001a}.sui-card-disabled{opacity:.6;cursor:not-allowed}.sui-card-borderless{border:none}.sui-card-loading-overlay{position:absolute;inset:0;background-color:#ffffffbf;display:flex;align-items:center;justify-content:center;border-radius:.5rem;z-index:10}.sui-card-loading-spinner{animation:spin 1s linear infinite;width:1.5rem;height:1.5rem;color:var(--sui-primary-600, #2563eb)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
}], propDecorators: { variant: [{
type: Input
}], size: [{
type: Input
}], clickable: [{
type: Input
}], disabled: [{
type: Input
}], header: [{
type: Input
}], subheader: [{
type: Input
}], showHeader: [{
type: Input
}], showFooter: [{
type: Input
}], loading: [{
type: Input
}], hoverable: [{
type: Input
}], borderless: [{
type: Input
}], click: [{
type: Output
}], headerClick: [{
type: Output
}] } });
class AutoComplete {
disabled = false;
name = '';
inputId = '';
placeholder = '';
minLength = 1;
options = [];
change = new EventEmitter();
value = '';
term = '';
open = false;
activeIndex = -1;
onChange = () => { };
onTouched = () => { };
writeValue(obj) {
this.value = obj ?? '';
this.term = this.displayValue(this.value);
}
registerOnChange(fn) { this.onChange = fn; }
registerOnTouched(fn) { this.onTouched = fn; }
setDisabledState(isDisabled) { this.disabled = isDisabled; }
onInput(event) {
const input = event.target;
this.term = input.value ?? '';
this.open = this.term.length >= this.minLength && this.filtered.length > 0;
this.activeIndex = this.filtered.length ? 0 : -1;
}
onFocus() {
this.open = this.term.length >= this.minLength && this.filtered.length > 0;
}
onBlur() {
this.onTouched();
// Add a small delay to allow click events to be processed before closing
setTimeout(() => {
this.open = false;
}, 150);
}
onKeyDown(event) {
if (!this.open)
return;
if (event.key === 'ArrowDown') {
event.preventDefault();
this.activeIndex = Math.min(this.activeIndex + 1, this.filtered.length - 1);
}
else if (event.key === 'ArrowUp') {
event.preventDefault();
this.activeIndex = Math.max(this.activeIndex - 1, 0);
}
else if (event.key === 'Enter') {
event.preventDefault();
if (this.activeIndex >= 0)
this.select(this.filtered[this.activeIndex]);
}
else if (event.key === 'Escape') {
this.open = false;
}
}
select(item) {
if (this.disabled)
return;
const val = this.resolveValue(item);
this.value = val;
this.term = this.displayValue(val);
this.onChange(this.value);
this.change.emit(this.value);
this.open = false;
}
clear() {
if (this.disabled)
return;
this.value = '';
this.term = '';
this.onChange(this.value);
this.change.emit(this.value);
}
get filtered() {
const t = (this.term || '').toLowerCase();
if (!t)
return [];
return this.options.filter(opt => this.displayItem(opt).toLowerCase().includes(t));
}
displayItem(item) {
return typeof item === 'string' ? item : item.label ?? '';
}
resolveValue(item) {
return typeof item === 'string' ? item : item.value;
}
displayValue(val) {
if (typeof val === 'string')
return val;
const match = this.options.find(o => typeof o !== 'string' && o.value === val);
return match?.label ?? '';
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AutoComplete, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.3", type: AutoComplete, isStandalone: true, selector: "sui-auto-complete", inputs: { disabled: "disabled", name: "name", inputId: "inputId", placeholder: "placeholder", minLength: "minLength", options: "options" }, outputs: { change: "change" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AutoComplete),
multi: true
}
], ngImport: i0, template: "<div class=\"sui-auto-complete\">\n\t<input\n\t\t[type]=\"'text'\"\n\t\t[id]=\"inputId\"\n\t\t[name]=\"name\"\n\t\t[disabled]=\"disabled\"\n\t\t[placeholder]=\"placeholder\"\n\t\t[value]=\"term\"\n\t\t(input)=\"onInput($event)\"\n\t\t(focus)=\"onFocus()\"\n\t\t(blur)=\"onBlur()\"\n\t\t(keydown)=\"onKeyDown($event)\"\n\t/>\n\t<button *ngIf=\"term && !disabled\" type=\"button\" (click)=\"clear()\" class=\"clear-button\">\u00D7</button>\n\t<ul *ngIf=\"open\" class=\"dropdown\">\n\t\t<li\n\t\t\t*ngFor=\"let item of filtered; let i = index\"\n\t\t\t(click)=\"select(item)\"\n\t\t\t(mousedown)=\"$event.preventDefault()\"\n\t\t\t[class.active]=\"i === activeIndex\"\n\t\t\tclass=\"dropdown-item\"\n\t\t>\n\t\t\t{{ displayItem(item) }}\n\t\t</li>\n\t</ul>\n</div>\n", styles: [".sui-auto-complete{position:relative;display:block;width:100%}.sui-auto-complete input{width:100%;padding:var(--sui-input-padding-y, .5rem) var(--sui-input-padding-x, .75rem);border:var(--sui-input-border-width, 1px) solid var(--sui-input-border-color, #d1d5db);border-radius:var(--sui-input-border-radius, .375rem);background-color:var(--sui-input-bg, #ffffff);color:var(--sui-input-text-color, #111827);font-size:var(--sui-input-font-size, .875rem);line-height:var(--sui-input-line-height, 1.25rem);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.sui-auto-complete input:focus{outline:none;border-color:var(--sui-input-focus-border-color, #3b82f6);box-shadow:0 0 0 3px var(--sui-input-focus-ring-color, rgba(59, 130, 246, .1))}.sui-auto-complete input:disabled{background-color:var(--sui-input-disabled-bg, #f9fafb);color:var(--sui-input-disabled-text-color, #9ca3af);cursor:not-allowed}.sui-auto-complete input::placeholder{color:var(--sui-input-placeholder-color, #9ca3af)}.sui-auto-complete .clear-button{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--sui-input-clear-button-color, #9ca3af);font-size:1.25rem;cursor:pointer;padding:.25rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out}.sui-auto-complete .clear-button:hover{color:var(--sui-input-clear-button-hover-color, #6b7280);background-color:var(--sui-input-clear-button-hover-bg, #f3f4f6)}.sui-auto-complete .clear-button:focus{outline:none;color:var(--s