@egose/shadcn-theme-ng 0.5.3 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -12
- package/autocomplete/fesm2022/autocomplete.mjs +14 -4
- package/autocomplete/types/autocomplete.d.ts +4 -1
- package/checkbox/README.md +6 -2
- package/checkbox/fesm2022/checkbox.mjs +11 -7
- package/checkbox/types/checkbox.d.ts +6 -4
- package/combobox/fesm2022/combobox.mjs +13 -7
- package/combobox/types/combobox.d.ts +8 -2
- package/data-table/README.md +174 -0
- package/data-table/fesm2022/data-table.mjs +713 -0
- package/data-table/package.json +25 -0
- package/data-table/types/data-table.d.ts +495 -0
- package/date-picker/README.md +41 -9
- package/date-picker/fesm2022/date-picker.mjs +265 -59
- package/date-picker/types/date-picker.d.ts +58 -27
- package/dropdown-menu/README.md +2 -0
- package/form-autocomplete/README.md +4 -2
- package/form-autocomplete/fesm2022/form-autocomplete.mjs +19 -6
- package/form-autocomplete/types/form-autocomplete.d.ts +9 -3
- package/form-combobox/README.md +4 -2
- package/form-combobox/fesm2022/form-combobox.mjs +23 -6
- package/form-combobox/types/form-combobox.d.ts +10 -3
- package/form-input-otp/README.md +7 -5
- package/form-input-otp/fesm2022/form-input-otp.mjs +20 -8
- package/form-input-otp/types/form-input-otp.d.ts +9 -3
- package/form-searchable-multiselect/README.md +15 -1
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +15 -3
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +9 -1
- package/form-slider/README.md +3 -1
- package/form-slider/fesm2022/form-slider.mjs +19 -6
- package/form-slider/types/form-slider.d.ts +9 -3
- package/form-text-input/fesm2022/form-text-input.mjs +1 -1
- package/form-textarea/fesm2022/form-textarea.mjs +1 -1
- package/form-toggle/README.md +22 -18
- package/form-toggle/fesm2022/form-toggle.mjs +22 -9
- package/form-toggle/types/form-toggle.d.ts +6 -2
- package/input/README.md +7 -12
- package/input/fesm2022/input.mjs +7 -4
- package/input/types/input.d.ts +3 -1
- package/input-otp/README.md +33 -9
- package/input-otp/fesm2022/input-otp.mjs +127 -11
- package/input-otp/types/input-otp.d.ts +24 -4
- package/layout-simple/fesm2022/layout-simple.mjs +1 -1
- package/package.json +6 -1
- package/pagination/README.md +21 -12
- package/pagination/fesm2022/pagination.mjs +73 -65
- package/pagination/types/pagination.d.ts +21 -8
- package/phone-input/README.md +16 -0
- package/phone-input/fesm2022/phone-input.mjs +42 -4
- package/phone-input/types/phone-input.d.ts +5 -2
- package/searchable-multiselect/README.md +62 -40
- package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +59 -32
- package/searchable-multiselect/types/searchable-multiselect.d.ts +23 -6
- package/slider/fesm2022/slider.mjs +9 -5
- package/slider/types/slider.d.ts +6 -2
- package/stepper/README.md +39 -19
- package/stepper/fesm2022/stepper.mjs +64 -12
- package/stepper/types/stepper.d.ts +12 -1
- package/table/README.md +2 -0
- package/table/fesm2022/table.mjs +12 -8
- package/table/types/table.d.ts +7 -2
|
@@ -127,7 +127,7 @@ class EgFormTextInput {
|
|
|
127
127
|
$hintClass = computed(() => hlm("mt-0", this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
128
128
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
129
129
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
130
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormTextInput, isStandalone: true, selector: "eg-form-text-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, autoError: { classPropertyName: "autoError", publicName: "autoError", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxlength: { classPropertyName: "maxlength", publicName: "maxlength", isSignal: true, isRequired: false, transformFunction: null }, minlength: { classPropertyName: "minlength", publicName: "minlength", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, pattern: { classPropertyName: "pattern", publicName: "pattern", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, inputClass: { classPropertyName: "inputClass", publicName: "inputClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: "\n @let lbl = label();\n @let cnm = controlName();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <input\n hlmInput\n [attr.aria-label]=\"lbl\"\n [id]=\"effectiveId()\"\n [name]=\"cnm || name()\"\n [formControlName]=\"cnm\"\n [class]=\"$inputClass()\"\n [type]=\"type()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [attr.disabled]=\"effectiveDisabled() ? '' : null\"\n [ariaDescribedby]=\"describedBy()\"\n [max]=\"max()\"\n [min]=\"min()\"\n [maxlength]=\"maxlength()\"\n [minlength]=\"minlength()\"\n [pattern]=\"pattern()\"\n [autocomplete]=\"autocomplete()\"\n [autofocus]=\"autofocus()\"\n [required]=\"rqrd\"\n />\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: HlmFormField, selector: "hlm-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby"] }] });
|
|
130
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormTextInput, isStandalone: true, selector: "eg-form-text-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, autoError: { classPropertyName: "autoError", publicName: "autoError", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxlength: { classPropertyName: "maxlength", publicName: "maxlength", isSignal: true, isRequired: false, transformFunction: null }, minlength: { classPropertyName: "minlength", publicName: "minlength", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, pattern: { classPropertyName: "pattern", publicName: "pattern", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, inputClass: { classPropertyName: "inputClass", publicName: "inputClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: "\n @let lbl = label();\n @let cnm = controlName();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <input\n hlmInput\n [attr.aria-label]=\"lbl\"\n [id]=\"effectiveId()\"\n [name]=\"cnm || name()\"\n [formControlName]=\"cnm\"\n [class]=\"$inputClass()\"\n [type]=\"type()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [attr.disabled]=\"effectiveDisabled() ? '' : null\"\n [ariaDescribedby]=\"describedBy()\"\n [max]=\"max()\"\n [min]=\"min()\"\n [maxlength]=\"maxlength()\"\n [minlength]=\"minlength()\"\n [pattern]=\"pattern()\"\n [autocomplete]=\"autocomplete()\"\n [autofocus]=\"autofocus()\"\n [required]=\"rqrd\"\n />\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: HlmFormField, selector: "hlm-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby", "size"] }] });
|
|
131
131
|
}
|
|
132
132
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextInput, decorators: [{
|
|
133
133
|
type: Component,
|
|
@@ -120,7 +120,7 @@ class EgFormTextarea {
|
|
|
120
120
|
$hintClass = computed(() => hlm("mt-0", this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
121
121
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
122
122
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextarea, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
123
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormTextarea, isStandalone: true, selector: "eg-form-textarea", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, autoError: { classPropertyName: "autoError", publicName: "autoError", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxlength: { classPropertyName: "maxlength", publicName: "maxlength", isSignal: true, isRequired: false, transformFunction: null }, minlength: { classPropertyName: "minlength", publicName: "minlength", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, cols: { classPropertyName: "cols", publicName: "cols", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, textareaClass: { classPropertyName: "textareaClass", publicName: "textareaClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: "\n @let lbl = label();\n @let cnm = controlName();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <textarea\n hlmInput\n [attr.aria-label]=\"lbl\"\n [id]=\"effectiveId()\"\n [name]=\"cnm || name()\"\n [formControlName]=\"cnm\"\n [class]=\"$textareaClass()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [attr.disabled]=\"effectiveDisabled() ? '' : null\"\n [ariaDescribedby]=\"describedBy()\"\n [maxlength]=\"maxlength()\"\n [minlength]=\"minlength()\"\n [required]=\"rqrd\"\n [rows]=\"rows()\"\n [cols]=\"cols()\"\n ></textarea>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: HlmFormField, selector: "hlm-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby"] }] });
|
|
123
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormTextarea, isStandalone: true, selector: "eg-form-textarea", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, autoError: { classPropertyName: "autoError", publicName: "autoError", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxlength: { classPropertyName: "maxlength", publicName: "maxlength", isSignal: true, isRequired: false, transformFunction: null }, minlength: { classPropertyName: "minlength", publicName: "minlength", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, cols: { classPropertyName: "cols", publicName: "cols", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, textareaClass: { classPropertyName: "textareaClass", publicName: "textareaClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: "\n @let lbl = label();\n @let cnm = controlName();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <textarea\n hlmInput\n [attr.aria-label]=\"lbl\"\n [id]=\"effectiveId()\"\n [name]=\"cnm || name()\"\n [formControlName]=\"cnm\"\n [class]=\"$textareaClass()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [attr.disabled]=\"effectiveDisabled() ? '' : null\"\n [ariaDescribedby]=\"describedBy()\"\n [maxlength]=\"maxlength()\"\n [minlength]=\"minlength()\"\n [required]=\"rqrd\"\n [rows]=\"rows()\"\n [cols]=\"cols()\"\n ></textarea>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: HlmFormField, selector: "hlm-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby", "size"] }] });
|
|
124
124
|
}
|
|
125
125
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextarea, decorators: [{
|
|
126
126
|
type: Component,
|
package/form-toggle/README.md
CHANGED
|
@@ -77,23 +77,24 @@ Selector (from source): `eg-form-toggle` (standalone component, host `tw:w-full`
|
|
|
77
77
|
|
|
78
78
|
Bind `formControlName` on the element; there is no `controlName` input.
|
|
79
79
|
|
|
80
|
-
| Input
|
|
81
|
-
|
|
|
82
|
-
| `label`
|
|
83
|
-
| `error`
|
|
84
|
-
| `autoError`
|
|
85
|
-
| `hint`
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
80
|
+
| Input | Type | Default | Description |
|
|
81
|
+
| ------------------ | --------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
82
|
+
| `label` | `string \| undefined` | — | Label text beside the toggle (hidden when omitted). |
|
|
83
|
+
| `error` | `string \| undefined` | — | Error text shown when invalid. |
|
|
84
|
+
| `autoError` | `boolean` | `true` | Auto-resolve the message from the control's `ValidationErrors` when `error` is unset. Explicit `error` always wins; `error=""` counts as unset. Global wording via `provideEgFormErrorMessages`. |
|
|
85
|
+
| `hint` | `string \| undefined` | — | Hint text shown otherwise. |
|
|
86
|
+
| `aria-describedby` | `string \| null` | `null` | Consumer-owned description IDs merged with the displayed error or hint on the native button. |
|
|
87
|
+
| `controlId` | `string \| undefined` | — | Explicit id (first priority for `effectiveId`). |
|
|
88
|
+
| `id` | `string \| undefined` | — | Fallback id (second priority). |
|
|
89
|
+
| `disabled` | `boolean` | `false` | Locks interaction (form control stays enabled). |
|
|
90
|
+
| `required` | `boolean` | `false` | Shows a red `*` next to the label. |
|
|
91
|
+
| `variant` | `'default' \| 'outline'` | `'default'` | Toggle variant forwarded to `hlmToggle`. |
|
|
92
|
+
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Toggle size forwarded to `hlmToggle`. |
|
|
93
|
+
| `class` | `ClassValue` | `''` | Extra host classes (base `tw:w-full`). |
|
|
94
|
+
| `toggleClass` | `string` | `''` | Extra toggle-button classes. |
|
|
95
|
+
| `labelClass` | `string` | `''` | Extra label classes. |
|
|
96
|
+
| `errorClass` | `string` | `''` | Extra error classes (base `tw:mt-0`). |
|
|
97
|
+
| `hintClass` | `string` | `''` | Extra hint classes (base `tw:mt-0`). |
|
|
97
98
|
|
|
98
99
|
| Member | Description |
|
|
99
100
|
| -------------------- | ------------------------------------------------------------------------------ |
|
|
@@ -163,7 +164,10 @@ export class ConsentToggleComponent {
|
|
|
163
164
|
## Accessibility notes
|
|
164
165
|
|
|
165
166
|
- Label `for` ↔ button `id` wiring is automatic via `effectiveId` — always pass a `label`.
|
|
166
|
-
- The
|
|
167
|
+
- The actual toggle button exposes `aria-pressed` and references the **currently displayed error or hint** with `aria-describedby`. Touch/dirty-only changes and submission update the description; correction/reset restore the hint when present. Removing a hint removes its reference.
|
|
168
|
+
- Pass consumer-owned IDs with `[aria-describedby]="'notification-help notification-policy'"`. They precede the active wrapper message ID; whitespace is normalized and duplicate IDs are removed. Keep those external elements mounted. `${effectiveId}-error` and `${effectiveId}-hint` are reserved for wrapper messages; do not supply them as consumer IDs.
|
|
169
|
+
- The button exposes `aria-invalid="true"` while its validation error is displayed and removes the attribute when feedback clears. The native label association and pressed state remain independent of validation.
|
|
170
|
+
- The required `*` is visual; configure `Validators.requiredTrue` independently. Toggle buttons do not support `aria-required`.
|
|
167
171
|
|
|
168
172
|
## Theming / CSS variables
|
|
169
173
|
|
|
@@ -53,6 +53,8 @@ class EgFormToggle {
|
|
|
53
53
|
...(ngDevMode ? [{ debugName: "autoError" }] : /* istanbul ignore next */ []));
|
|
54
54
|
hint = input(undefined, /* @ts-ignore */
|
|
55
55
|
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
56
|
+
/** Consumer-owned description IDs, merged with the displayed error or hint on the button. */
|
|
57
|
+
ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
|
|
56
58
|
id = input(undefined, /* @ts-ignore */
|
|
57
59
|
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
58
60
|
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
@@ -78,12 +80,14 @@ class EgFormToggle {
|
|
|
78
80
|
...(ngDevMode ? [{ debugName: "_status" }] : /* istanbul ignore next */ []));
|
|
79
81
|
_submitted = signal(false, /* @ts-ignore */
|
|
80
82
|
...(ngDevMode ? [{ debugName: "_submitted" }] : /* istanbul ignore next */ []));
|
|
83
|
+
_interacted = signal(false, /* @ts-ignore */
|
|
84
|
+
...(ngDevMode ? [{ debugName: "_interacted" }] : /* istanbul ignore next */ []));
|
|
81
85
|
/**
|
|
82
86
|
* Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
|
|
83
87
|
* auto-resolved from the control's `ValidationErrors`. Plain method (not a
|
|
84
88
|
* computed): `ValidationErrors` is not a signal, so the control must be read
|
|
85
89
|
* fresh on every change-detection pass (`showError()` re-evaluates with it
|
|
86
|
-
* via the `_status`/`_submitted` signals).
|
|
90
|
+
* via the `_status`/`_submitted`/`_interacted` signals).
|
|
87
91
|
*/
|
|
88
92
|
resolvedError() {
|
|
89
93
|
const explicit = this.error();
|
|
@@ -96,20 +100,26 @@ class EgFormToggle {
|
|
|
96
100
|
showError = computed(() => {
|
|
97
101
|
this._status();
|
|
98
102
|
this._submitted();
|
|
103
|
+
this._interacted();
|
|
99
104
|
const control = this._controlDir?.control;
|
|
100
|
-
return
|
|
101
|
-
!!control?.errors &&
|
|
102
|
-
(control.touched || control.dirty || !!this._formGroupDirective?.submitted));
|
|
105
|
+
return !!this.resolvedError() && !!control?.errors && (this._interacted() || this._submitted());
|
|
103
106
|
}, /* @ts-ignore */
|
|
104
107
|
...(ngDevMode ? [{ debugName: "showError" }] : /* istanbul ignore next */ []));
|
|
105
108
|
showHint = computed(() => !this.showError() && !!this.hint(), /* @ts-ignore */
|
|
106
109
|
...(ngDevMode ? [{ debugName: "showHint" }] : /* istanbul ignore next */ []));
|
|
110
|
+
describedBy() {
|
|
111
|
+
const ids = [this.ariaDescribedBy(), this.showError() ? this.errorId() : this.showHint() ? this.hintId() : null];
|
|
112
|
+
return [...new Set(ids.filter(Boolean).join(' ').split(/\s+/).filter(Boolean))].join(' ') || null;
|
|
113
|
+
}
|
|
107
114
|
_onChange;
|
|
108
115
|
_onTouched;
|
|
109
116
|
ngDoCheck() {
|
|
110
117
|
const submitted = !!this._formGroupDirective?.submitted;
|
|
111
118
|
if (this._submitted() !== submitted)
|
|
112
119
|
this._submitted.set(submitted);
|
|
120
|
+
// Keep silent interaction updates (emitEvent: false) visible on the next check too.
|
|
121
|
+
const control = this._controlDir?.control;
|
|
122
|
+
this._interacted.set(!!control && (control.touched || control.dirty));
|
|
113
123
|
}
|
|
114
124
|
ngAfterViewInit() {
|
|
115
125
|
// Resolved here (not earlier): this component IS the value accessor for
|
|
@@ -119,8 +129,11 @@ class EgFormToggle {
|
|
|
119
129
|
this._controlDir = this._injector.get(FormControlName, null, { self: true, optional: true }) ?? null;
|
|
120
130
|
const control = this._controlDir?.control;
|
|
121
131
|
this._status.set(control?.status ?? null);
|
|
122
|
-
|
|
123
|
-
|
|
132
|
+
this._interacted.set(!!control && (control.touched || control.dirty));
|
|
133
|
+
// Touch/dirty/reset events must invalidate feedback even when status stays INVALID.
|
|
134
|
+
control?.events.pipe(takeUntilDestroyed(this._destroyRef)).subscribe(() => {
|
|
135
|
+
this._status.set(control.status);
|
|
136
|
+
this._interacted.set(control.touched || control.dirty);
|
|
124
137
|
});
|
|
125
138
|
}
|
|
126
139
|
_handleStateChange(state) {
|
|
@@ -167,7 +180,7 @@ class EgFormToggle {
|
|
|
167
180
|
$hintClass = computed(() => hlm("mt-0", this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
168
181
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
169
182
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormToggle, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
170
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormToggle, isStandalone: true, selector: "eg-form-toggle", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, autoError: { classPropertyName: "autoError", publicName: "autoError", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, toggleClass: { classPropertyName: "toggleClass", publicName: "toggleClass", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [EG_FORM_TOGGLE_VALUE_ACCESSOR, provideIcons({ lucideCheck })], ngImport: i0, template: "\n @let lbl = label();\n @let rqrd = required();\n\n <div>\n <div class=\"flex items-center gap-1\">\n <button\n hlmToggle\n type=\"button\"\n [id]=\"effectiveId()\"\n [state]=\"_state() ? 'on' : 'off'\"\n (stateChange)=\"_handleStateChange($event)\"\n [disabled]=\"_disabled()\"\n [variant]=\"variant()\"\n [size]=\"size()\"\n [class]=\"$toggleClass()\"\n >\n @if (_state()) {\n <ng-icon name=\"lucideCheck\" />\n }\n </button>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n </div>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n } @else if (showHint()) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hint() }}\n </hlm-hint>\n }\n </div>\n ", isInline: true, dependencies: [{ kind: "directive", type: HlmToggle, selector: "button[hlmToggle]", inputs: ["variant", "size"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
183
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormToggle, isStandalone: true, selector: "eg-form-toggle", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, autoError: { classPropertyName: "autoError", publicName: "autoError", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, toggleClass: { classPropertyName: "toggleClass", publicName: "toggleClass", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [EG_FORM_TOGGLE_VALUE_ACCESSOR, provideIcons({ lucideCheck })], ngImport: i0, template: "\n @let lbl = label();\n @let rqrd = required();\n\n <div>\n <div class=\"flex items-center gap-1\">\n <button\n hlmToggle\n type=\"button\"\n [id]=\"effectiveId()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"showError() ? 'true' : null\"\n [state]=\"_state() ? 'on' : 'off'\"\n (stateChange)=\"_handleStateChange($event)\"\n [disabled]=\"_disabled()\"\n [variant]=\"variant()\"\n [size]=\"size()\"\n [class]=\"$toggleClass()\"\n >\n @if (_state()) {\n <ng-icon name=\"lucideCheck\" />\n }\n </button>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n </div>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n } @else if (showHint()) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hint() }}\n </hlm-hint>\n }\n </div>\n ", isInline: true, dependencies: [{ kind: "directive", type: HlmToggle, selector: "button[hlmToggle]", inputs: ["variant", "size"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
171
184
|
}
|
|
172
185
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormToggle, decorators: [{
|
|
173
186
|
type: Component,
|
|
@@ -180,9 +193,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
180
193
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
181
194
|
imports: [HlmToggle, HlmError, HlmHint, HlmLabel, NgIcon],
|
|
182
195
|
providers: [EG_FORM_TOGGLE_VALUE_ACCESSOR, provideIcons({ lucideCheck })],
|
|
183
|
-
template: "\n @let lbl = label();\n @let rqrd = required();\n\n <div>\n <div class=\"flex items-center gap-1\">\n <button\n hlmToggle\n type=\"button\"\n [id]=\"effectiveId()\"\n [state]=\"_state() ? 'on' : 'off'\"\n (stateChange)=\"_handleStateChange($event)\"\n [disabled]=\"_disabled()\"\n [variant]=\"variant()\"\n [size]=\"size()\"\n [class]=\"$toggleClass()\"\n >\n @if (_state()) {\n <ng-icon name=\"lucideCheck\" />\n }\n </button>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n </div>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n } @else if (showHint()) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hint() }}\n </hlm-hint>\n }\n </div>\n ",
|
|
196
|
+
template: "\n @let lbl = label();\n @let rqrd = required();\n\n <div>\n <div class=\"flex items-center gap-1\">\n <button\n hlmToggle\n type=\"button\"\n [id]=\"effectiveId()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"showError() ? 'true' : null\"\n [state]=\"_state() ? 'on' : 'off'\"\n (stateChange)=\"_handleStateChange($event)\"\n [disabled]=\"_disabled()\"\n [variant]=\"variant()\"\n [size]=\"size()\"\n [class]=\"$toggleClass()\"\n >\n @if (_state()) {\n <ng-icon name=\"lucideCheck\" />\n }\n </button>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n </div>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n } @else if (showHint()) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hint() }}\n </hlm-hint>\n }\n </div>\n ",
|
|
184
197
|
}]
|
|
185
|
-
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], autoError: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoError", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], toggleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleClass", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
|
|
198
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], autoError: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoError", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], toggleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleClass", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
|
|
186
199
|
|
|
187
200
|
/*
|
|
188
201
|
* Public API Surface of form-toggle
|
|
@@ -36,6 +36,8 @@ declare class EgFormToggle implements ControlValueAccessor, AfterViewInit, DoChe
|
|
|
36
36
|
*/
|
|
37
37
|
autoError: _angular_core.InputSignal<boolean>;
|
|
38
38
|
hint: _angular_core.InputSignal<string | undefined>;
|
|
39
|
+
/** Consumer-owned description IDs, merged with the displayed error or hint on the button. */
|
|
40
|
+
ariaDescribedBy: _angular_core.InputSignal<string | null>;
|
|
39
41
|
id: _angular_core.InputSignal<string | undefined>;
|
|
40
42
|
disabled: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
|
|
41
43
|
required: _angular_core.InputSignal<boolean>;
|
|
@@ -49,16 +51,18 @@ declare class EgFormToggle implements ControlValueAccessor, AfterViewInit, DoChe
|
|
|
49
51
|
protected readonly _disabled: _angular_core.Signal<boolean>;
|
|
50
52
|
private readonly _status;
|
|
51
53
|
private readonly _submitted;
|
|
54
|
+
private readonly _interacted;
|
|
52
55
|
/**
|
|
53
56
|
* Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
|
|
54
57
|
* auto-resolved from the control's `ValidationErrors`. Plain method (not a
|
|
55
58
|
* computed): `ValidationErrors` is not a signal, so the control must be read
|
|
56
59
|
* fresh on every change-detection pass (`showError()` re-evaluates with it
|
|
57
|
-
* via the `_status`/`_submitted` signals).
|
|
60
|
+
* via the `_status`/`_submitted`/`_interacted` signals).
|
|
58
61
|
*/
|
|
59
62
|
protected resolvedError(): string | undefined;
|
|
60
63
|
protected readonly showError: _angular_core.Signal<boolean>;
|
|
61
64
|
protected readonly showHint: _angular_core.Signal<boolean>;
|
|
65
|
+
protected describedBy(): string | null;
|
|
62
66
|
protected _onChange?: ChangeFn<boolean>;
|
|
63
67
|
protected _onTouched?: TouchFn;
|
|
64
68
|
ngDoCheck(): void;
|
|
@@ -80,7 +84,7 @@ declare class EgFormToggle implements ControlValueAccessor, AfterViewInit, DoChe
|
|
|
80
84
|
$errorClass: _angular_core.Signal<string>;
|
|
81
85
|
$hintClass: _angular_core.Signal<string>;
|
|
82
86
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormToggle, never>;
|
|
83
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormToggle, "eg-form-toggle", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "autoError": { "alias": "autoError"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "toggleClass": { "alias": "toggleClass"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
87
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormToggle, "eg-form-toggle", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "autoError": { "alias": "autoError"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "ariaDescribedBy": { "alias": "aria-describedby"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "toggleClass": { "alias": "toggleClass"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
interface EgFormToggleConfig {
|
package/input/README.md
CHANGED
|
@@ -58,11 +58,12 @@ The directive matches `input[hlmInput]` usage in practice (selector `[hlmInput]`
|
|
|
58
58
|
|
|
59
59
|
### `[hlmInput]` — `HlmInput`
|
|
60
60
|
|
|
61
|
-
| Input | Type
|
|
62
|
-
| ----------------- |
|
|
63
|
-
| `ariaDescribedby` | `string \| null`
|
|
64
|
-
| `id` | (via `BrnInput`)
|
|
65
|
-
| `forceInvalid` | (via `BrnInput`)
|
|
61
|
+
| Input | Type | Default | Description |
|
|
62
|
+
| ----------------- | --------------------- | --------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
63
|
+
| `ariaDescribedby` | `string \| null` | `null` | Bound to `aria-describedby`. Combine with `<hlm-error>`/`<hlm-hint>` ids (the form wrappers do this automatically). |
|
|
64
|
+
| `id` | (via `BrnInput`) | — | Forwarded to the `BrnInput` host directive. |
|
|
65
|
+
| `forceInvalid` | (via `BrnInput`) | — | Forwarded to the `BrnInput` host directive; forces the `data-matches-spartan-invalid` error styles. |
|
|
66
|
+
| `size` | `sm \| default \| lg` | `default` | Height density: `h-8` / `h-9` / `h-10`. |
|
|
66
67
|
|
|
67
68
|
No outputs. Visual states are attribute-driven: `data-matches-spartan-invalid=true` switches the border/ring to destructive; `disabled` applies `pointer-events-none`, `cursor-not-allowed`, `opacity-50`. Base geometry: `h-9`, `rounded-md`, `text-base` (`md:text-sm`), full width.
|
|
68
69
|
|
|
@@ -180,13 +181,7 @@ import { HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
|
|
|
180
181
|
template: `
|
|
181
182
|
<form [formGroup]="form">
|
|
182
183
|
<label for="handle">Handle</label>
|
|
183
|
-
<input
|
|
184
|
-
hlmInput
|
|
185
|
-
id="handle"
|
|
186
|
-
formControlName="handle"
|
|
187
|
-
placeholder="@jane"
|
|
188
|
-
[ariaDescribedby]="describedBy()"
|
|
189
|
-
/>
|
|
184
|
+
<input hlmInput id="handle" formControlName="handle" placeholder="@jane" [ariaDescribedby]="describedBy()" />
|
|
190
185
|
@if (showError()) {
|
|
191
186
|
<hlm-error id="handle-error">Use 3+ lowercase letters.</hlm-error>
|
|
192
187
|
} @else {
|
package/input/fesm2022/input.mjs
CHANGED
|
@@ -4,16 +4,19 @@ import * as i2 from '@spartan-ng/brain/field';
|
|
|
4
4
|
import { BrnFieldControlDescribedBy } from '@spartan-ng/brain/field';
|
|
5
5
|
import * as i1 from '@spartan-ng/brain/input';
|
|
6
6
|
import { BrnInput } from '@spartan-ng/brain/input';
|
|
7
|
-
import { classes } from '@egose/shadcn-theme-ng/utils';
|
|
7
|
+
import { classes, hlm } from '@egose/shadcn-theme-ng/utils';
|
|
8
8
|
|
|
9
9
|
class HlmInput {
|
|
10
10
|
ariaDescribedby = input(null, /* @ts-ignore */
|
|
11
11
|
...(ngDevMode ? [{ debugName: "ariaDescribedby" }] : /* istanbul ignore next */ []));
|
|
12
|
+
/** Height density. Merged after the base height class, so it wins via `twMerge`. */
|
|
13
|
+
size = input('default', /* @ts-ignore */
|
|
14
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
12
15
|
constructor() {
|
|
13
|
-
classes(() => "dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 h-9 rounded-md border bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] file:h-7 file:text-sm file:font-medium focus-visible:ring-3 data-[matches-spartan-invalid=true]:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50");
|
|
16
|
+
classes(() => hlm("dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 h-9 rounded-md border bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] file:h-7 file:text-sm file:font-medium focus-visible:ring-3 data-[matches-spartan-invalid=true]:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", this.size() === 'sm' ? "h-8" : this.size() === 'lg' ? "h-10" : ''));
|
|
14
17
|
}
|
|
15
18
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
16
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: HlmInput, isStandalone: true, selector: "[hlmInput]", inputs: { ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "ariaDescribedby", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "input" }, properties: { "attr.aria-describedby": "ariaDescribedby()" } }, hostDirectives: [{ directive: i1.BrnInput, inputs: ["id", "id", "forceInvalid", "forceInvalid"] }, { directive: i2.BrnFieldControlDescribedBy }], ngImport: i0 });
|
|
19
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.3", type: HlmInput, isStandalone: true, selector: "[hlmInput]", inputs: { ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "ariaDescribedby", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "input" }, properties: { "attr.aria-describedby": "ariaDescribedby()" } }, hostDirectives: [{ directive: i1.BrnInput, inputs: ["id", "id", "forceInvalid", "forceInvalid"] }, { directive: i2.BrnFieldControlDescribedBy }], ngImport: i0 });
|
|
17
20
|
}
|
|
18
21
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmInput, decorators: [{
|
|
19
22
|
type: Directive,
|
|
@@ -25,7 +28,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
25
28
|
'[attr.aria-describedby]': 'ariaDescribedby()',
|
|
26
29
|
},
|
|
27
30
|
}]
|
|
28
|
-
}], ctorParameters: () => [], propDecorators: { ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedby", required: false }] }] } });
|
|
31
|
+
}], ctorParameters: () => [], propDecorators: { ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedby", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
29
32
|
|
|
30
33
|
/*
|
|
31
34
|
* Public API Surface of input
|
package/input/types/input.d.ts
CHANGED
|
@@ -4,9 +4,11 @@ import * as i2 from '@spartan-ng/brain/field';
|
|
|
4
4
|
|
|
5
5
|
declare class HlmInput {
|
|
6
6
|
readonly ariaDescribedby: i0.InputSignal<string | null>;
|
|
7
|
+
/** Height density. Merged after the base height class, so it wins via `twMerge`. */
|
|
8
|
+
readonly size: i0.InputSignal<"sm" | "default" | "lg">;
|
|
7
9
|
constructor();
|
|
8
10
|
static ɵfac: i0.ɵɵFactoryDeclaration<HlmInput, never>;
|
|
9
|
-
static ɵdir: i0.ɵɵDirectiveDeclaration<HlmInput, "[hlmInput]", never, { "ariaDescribedby": { "alias": "ariaDescribedby"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1.BrnInput; inputs: { "id": "id"; "forceInvalid": "forceInvalid"; }; outputs: {}; }, { directive: typeof i2.BrnFieldControlDescribedBy; inputs: {}; outputs: {}; }]>;
|
|
11
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<HlmInput, "[hlmInput]", never, { "ariaDescribedby": { "alias": "ariaDescribedby"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1.BrnInput; inputs: { "id": "id"; "forceInvalid": "forceInvalid"; }; outputs: {}; }, { directive: typeof i2.BrnFieldControlDescribedBy; inputs: {}; outputs: {}; }]>;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
declare const HlmInputImports: readonly [typeof HlmInput];
|
package/input-otp/README.md
CHANGED
|
@@ -61,7 +61,7 @@ import { HlmInputOtpModule } from '@egose/shadcn-theme-ng/input-otp';
|
|
|
61
61
|
export class MyModule {}
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
Exported from `src/public-api.ts`: `HlmInputOtp`, `HlmInputOtpGroup`, `HlmInputOtpSeparator`, `HlmInputOtpSlot`, `HlmInputOtpFakeCaret`, plus `HlmInputOtpImports` and `HlmInputOtpModule`.
|
|
64
|
+
Exported from `src/public-api.ts`: `HlmInputOtp`, `HlmInputOtpControl`, `HlmInputOtpGroup`, `HlmInputOtpSeparator`, `HlmInputOtpSlot`, `HlmInputOtpFakeCaret`, plus `HlmInputOtpImports` and `HlmInputOtpModule`.
|
|
65
65
|
|
|
66
66
|
## Anatomy / Structure
|
|
67
67
|
|
|
@@ -88,6 +88,32 @@ Brain inputs on `brn-input-otp` (from `@spartan-ng/brain/input-otp`): `length` (
|
|
|
88
88
|
|
|
89
89
|
## API reference
|
|
90
90
|
|
|
91
|
+
### `hlm-input-otp` — `HlmInputOtpControl`
|
|
92
|
+
|
|
93
|
+
Use this component for native-input accessibility forwarding. It extends `BrnInputOtp` with
|
|
94
|
+
a declarative input template and retains its inputs, outputs, CVA, paste and focus behavior.
|
|
95
|
+
`HlmInputOtpImports` / `HlmInputOtpModule` include it; a separate brain import is unnecessary.
|
|
96
|
+
Compose it with `HlmInputOtpSlot` (the raw brain slot's private injection token is not supported).
|
|
97
|
+
|
|
98
|
+
- `[aria-describedby]` (`string | null`, default `null`) reaches the native input and merges
|
|
99
|
+
with enclosing Spartan field description IDs, deduplicated. Bind only IDs of mounted elements.
|
|
100
|
+
- `required` (boolean-coerced, default `false`) exposes native `aria-required`. Add Angular
|
|
101
|
+
validators separately; native `aria-invalid` follows the bound form control.
|
|
102
|
+
- `inputId` connects a normal `<label for>` to the one focusable native input.
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<label for="verification">Verification code</label>
|
|
106
|
+
<hlm-input-otp [length]="4" inputId="verification" aria-describedby="verification-help" required>
|
|
107
|
+
<div hlmInputOtpGroup>
|
|
108
|
+
<hlm-input-otp-slot index="0" />
|
|
109
|
+
<hlm-input-otp-slot index="1" />
|
|
110
|
+
<hlm-input-otp-slot index="2" />
|
|
111
|
+
<hlm-input-otp-slot index="3" />
|
|
112
|
+
</div>
|
|
113
|
+
</hlm-input-otp>
|
|
114
|
+
<p id="verification-help">Enter the four-character code.</p>
|
|
115
|
+
```
|
|
116
|
+
|
|
91
117
|
### `brn-input-otp[hlmInputOtp]` — `HlmInputOtp`
|
|
92
118
|
|
|
93
119
|
Pure styling directive (`data-slot="input-otp"`, flex row, dims on `has-disabled`). No inputs/outputs — bind `length`, `value`, `disabled`, `inputId`, … on the `brn-input-otp` element itself.
|
|
@@ -102,7 +128,7 @@ Slot cluster wrapper (`data-slot="input-otp-group"`). No inputs/outputs. Use one
|
|
|
102
128
|
| ------- | -------------------------------------------------- | -------------------------------------------------------- |
|
|
103
129
|
| `index` | `number` (**required**, `numberAttribute` coerced) | Zero-based position of the character this slot displays. |
|
|
104
130
|
|
|
105
|
-
|
|
131
|
+
Under `brn-input-otp`, renders `<brn-input-otp-slot [index]>`; under `hlm-input-otp`, reads the adapter's public context and renders the same character/caret and state attributes declaratively. Active slots get the `ring` highlight.
|
|
106
132
|
|
|
107
133
|
### `hlm-input-otp-separator` — `HlmInputOtpSeparator`
|
|
108
134
|
|
|
@@ -267,17 +293,15 @@ export class LengthsExample {}
|
|
|
267
293
|
|
|
268
294
|
```ts
|
|
269
295
|
import { Component, signal } from '@angular/core';
|
|
270
|
-
import { BrnInputOtp } from '@spartan-ng/brain/input-otp';
|
|
271
296
|
import { HlmInputOtpImports } from '@egose/shadcn-theme-ng/input-otp';
|
|
272
297
|
import { HlmError } from '@egose/shadcn-theme-ng/form-field';
|
|
273
298
|
|
|
274
299
|
@Component({
|
|
275
300
|
standalone: true,
|
|
276
|
-
imports: [
|
|
301
|
+
imports: [HlmInputOtpImports, HlmError],
|
|
277
302
|
template: `
|
|
278
303
|
<label for="otp-verify">Verification code</label>
|
|
279
|
-
<
|
|
280
|
-
hlmInputOtp
|
|
304
|
+
<hlm-input-otp
|
|
281
305
|
[length]="6"
|
|
282
306
|
inputId="otp-verify"
|
|
283
307
|
inputMode="numeric"
|
|
@@ -286,7 +310,7 @@ import { HlmError } from '@egose/shadcn-theme-ng/form-field';
|
|
|
286
310
|
[value]="code()"
|
|
287
311
|
(valueChange)="code.set($event); error.set(null)"
|
|
288
312
|
(completed)="verify()"
|
|
289
|
-
aria-describedby="otp-error"
|
|
313
|
+
[aria-describedby]="error() ? 'otp-error' : null"
|
|
290
314
|
>
|
|
291
315
|
<div hlmInputOtpGroup>
|
|
292
316
|
<hlm-input-otp-slot index="0" />
|
|
@@ -299,7 +323,7 @@ import { HlmError } from '@egose/shadcn-theme-ng/form-field';
|
|
|
299
323
|
<hlm-input-otp-slot index="4" />
|
|
300
324
|
<hlm-input-otp-slot index="5" />
|
|
301
325
|
</div>
|
|
302
|
-
</
|
|
326
|
+
</hlm-input-otp>
|
|
303
327
|
@if (error()) {
|
|
304
328
|
<hlm-error id="otp-error">{{ error() }}</hlm-error>
|
|
305
329
|
}
|
|
@@ -366,7 +390,7 @@ export class PasteExample {
|
|
|
366
390
|
|
|
367
391
|
- Always render a visible `<label>` bound via `inputId` — the hidden native input is what AT focuses; without the label the purpose is unclear.
|
|
368
392
|
- `inputMode="numeric"` (digits) vs `text` (alphanumeric) selects the right mobile keyboard; `inputAutocomplete="one-time-code"` lets browsers/OS autofill SMS codes.
|
|
369
|
-
-
|
|
393
|
+
- Associate failures with text (`<hlm-error>` + `hlm-input-otp`'s `[aria-describedby]`), never by slot color alone. The legacy brain component has no native description input. `HlmError` is not a live region; browser DOM/focus tests do not establish screen-reader announcements.
|
|
370
394
|
- Keep `length` aligned with what the backend expects and validate `minLength(length)` on the form control so incomplete codes cannot submit.
|
|
371
395
|
|
|
372
396
|
## Theming / CSS variables
|