feat(design-system): add reusable Input primitive
ControlValueAccessor-based, standalone, OnPush. Sizes sm/md/lg, states default/error/success. Colors/radius/spacing/transitions consumed via existing tenant CSS vars (--border-color, --primary-color, --error-color, --success-color, etc) with fallbacks - compatible with bootstrap JSON theming and Project Editor live preview.
This commit is contained in:
64
src/app/shared/ui/input/input.component.ts
Normal file
64
src/app/shared/ui/input/input.component.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { ChangeDetectionStrategy, Component, forwardRef, input } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
export type InputSize = 'sm' | 'md' | 'lg';
|
||||
export type InputState = 'default' | 'error' | 'success';
|
||||
|
||||
@Component({
|
||||
selector: 'app-input',
|
||||
standalone: true,
|
||||
templateUrl: './input.component.html',
|
||||
styleUrl: './input.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => InputComponent),
|
||||
multi: true
|
||||
}
|
||||
],
|
||||
host: {
|
||||
'[class.app-input-host--full-width]': 'fullWidth()'
|
||||
}
|
||||
})
|
||||
export class InputComponent implements ControlValueAccessor {
|
||||
readonly type = input<'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url'>('text');
|
||||
readonly size = input<InputSize>('md');
|
||||
readonly state = input<InputState>('default');
|
||||
readonly placeholder = input('');
|
||||
readonly disabled = input(false);
|
||||
readonly fullWidth = input(false);
|
||||
readonly ariaLabel = input<string | null>(null);
|
||||
|
||||
protected value: string | number = '';
|
||||
protected isDisabled = false;
|
||||
|
||||
private onChange: (value: string | number) => void = () => {};
|
||||
private onTouched: () => void = () => {};
|
||||
|
||||
writeValue(value: string | number): void {
|
||||
this.value = value ?? '';
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: string | number) => void): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this.isDisabled = isDisabled;
|
||||
}
|
||||
|
||||
protected handleInput(event: Event): void {
|
||||
const target = event.target as HTMLInputElement;
|
||||
this.value = target.value;
|
||||
this.onChange(this.value);
|
||||
}
|
||||
|
||||
protected handleBlur(): void {
|
||||
this.onTouched();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user