@egose/shadcn-theme-ng-tw 0.6.0 → 0.6.2
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 +119 -1
- 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 +15 -9
- package/checkbox/types/checkbox.d.ts +6 -4
- package/combobox/fesm2022/combobox.mjs +22 -6
- package/combobox/types/combobox.d.ts +8 -2
- package/data-table/README.md +11 -1
- package/data-table/fesm2022/data-table.mjs +76 -26
- package/data-table/types/data-table.d.ts +25 -6
- 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/form-autocomplete/README.md +4 -2
- package/form-autocomplete/fesm2022/form-autocomplete.mjs +33 -8
- package/form-autocomplete/types/form-autocomplete.d.ts +9 -3
- package/form-combobox/README.md +4 -2
- package/form-combobox/fesm2022/form-combobox.mjs +65 -14
- 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 +28 -14
- 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 +23 -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 +21 -6
- package/form-slider/types/form-slider.d.ts +9 -3
- package/form-toggle/README.md +22 -18
- package/form-toggle/fesm2022/form-toggle.mjs +25 -8
- package/form-toggle/types/form-toggle.d.ts +6 -2
- 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/package.json +1 -1
- package/pagination/README.md +21 -12
- package/pagination/fesm2022/pagination.mjs +83 -75
- package/pagination/types/pagination.d.ts +21 -8
- package/phone-input/README.md +16 -0
- package/phone-input/fesm2022/phone-input.mjs +41 -3
- package/phone-input/types/phone-input.d.ts +5 -2
- package/searchable-multiselect/README.md +62 -40
- package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +97 -36
- package/searchable-multiselect/types/searchable-multiselect.d.ts +23 -6
- package/slider/fesm2022/slider.mjs +15 -5
- package/slider/types/slider.d.ts +6 -2
- package/stepper/README.md +39 -19
- package/stepper/fesm2022/stepper.mjs +71 -17
- package/stepper/types/stepper.d.ts +12 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, viewChild, input, computed, Component } from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, viewChild, ChangeDetectorRef, DestroyRef, input, computed, Component } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
|
|
5
5
|
import { injectEgFormErrorMessages, HlmFormIdGenerator, injectEgFormSharedConfig, resolveEgFormError, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
|
|
6
6
|
import { HlmLabel } from '@egose/shadcn-theme-ng-tw/label';
|
|
7
7
|
import { HlmCombobox, HlmComboboxChip, HlmComboboxChipInput, HlmComboboxChips, HlmComboboxContent, HlmComboboxEmpty, HlmComboboxInput, HlmComboboxItem, HlmComboboxList, HlmComboboxMultiple, HlmComboboxPortal, HlmComboboxTrigger } from '@egose/shadcn-theme-ng-tw/combobox';
|
|
8
8
|
import { BrnComboboxMultiple, BrnCombobox } from '@spartan-ng/brain/combobox';
|
|
9
|
+
import { BrnFieldControlDescribedBy } from '@spartan-ng/brain/field';
|
|
9
10
|
import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
|
|
11
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
10
12
|
|
|
11
13
|
const EgFormComboboxConfigToken = new InjectionToken('EgFormComboboxConfig');
|
|
12
14
|
function provideEgFormComboboxConfig(config) {
|
|
@@ -26,6 +28,14 @@ class EgFormCombobox {
|
|
|
26
28
|
...(ngDevMode ? [{ debugName: "_picker" }] : /* istanbul ignore next */ []));
|
|
27
29
|
_single = viewChild((BrnCombobox), /* @ts-ignore */
|
|
28
30
|
...(ngDevMode ? [{ debugName: "_single" }] : /* istanbul ignore next */ []));
|
|
31
|
+
_cdr = inject(ChangeDetectorRef);
|
|
32
|
+
_destroyRef = inject(DestroyRef);
|
|
33
|
+
ngOnInit() {
|
|
34
|
+
// Submit/reset may change message visibility without changing the field's value or validity.
|
|
35
|
+
this.formGroupDirective.form.events
|
|
36
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
37
|
+
.subscribe(() => this._cdr.markForCheck());
|
|
38
|
+
}
|
|
29
39
|
label = input(undefined, /* @ts-ignore */
|
|
30
40
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
31
41
|
controlId = input(undefined, /* @ts-ignore */
|
|
@@ -43,6 +53,8 @@ class EgFormCombobox {
|
|
|
43
53
|
...(ngDevMode ? [{ debugName: "autoError" }] : /* istanbul ignore next */ []));
|
|
44
54
|
hint = input(undefined, /* @ts-ignore */
|
|
45
55
|
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
56
|
+
/** Consumer-owned description IDs, merged with the current error and hint IDs in both modes. */
|
|
57
|
+
ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
|
|
46
58
|
id = input(undefined, /* @ts-ignore */
|
|
47
59
|
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
48
60
|
placeholder = input('Pick…', /* @ts-ignore */
|
|
@@ -64,6 +76,8 @@ class EgFormCombobox {
|
|
|
64
76
|
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
65
77
|
effectiveId = computed(() => this.controlId() || this.id() || this.generatedId, /* @ts-ignore */
|
|
66
78
|
...(ngDevMode ? [{ debugName: "effectiveId" }] : /* istanbul ignore next */ []));
|
|
79
|
+
labelId = computed(() => `${this.effectiveId()}-label`, /* @ts-ignore */
|
|
80
|
+
...(ngDevMode ? [{ debugName: "labelId" }] : /* istanbul ignore next */ []));
|
|
67
81
|
errorId = computed(() => `${this.effectiveId()}-error`, /* @ts-ignore */
|
|
68
82
|
...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
|
|
69
83
|
hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
|
|
@@ -108,6 +122,10 @@ class EgFormCombobox {
|
|
|
108
122
|
effectiveDisabled() {
|
|
109
123
|
return this.disabled() || !!this.formGroupDirective.form.get(this.controlName())?.disabled;
|
|
110
124
|
}
|
|
125
|
+
describedBy() {
|
|
126
|
+
const ids = [this.ariaDescribedBy(), this.showError() ? this.errorId() : this.hint() ? this.hintId() : null];
|
|
127
|
+
return [...new Set(ids.filter(Boolean).join(' ').split(/\s+/).filter(Boolean))].join(' ') || null;
|
|
128
|
+
}
|
|
111
129
|
// Styling
|
|
112
130
|
userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
|
|
113
131
|
labelClass = input('', /* @ts-ignore */
|
|
@@ -121,8 +139,6 @@ class EgFormCombobox {
|
|
|
121
139
|
hintClass = input('', /* @ts-ignore */
|
|
122
140
|
...(ngDevMode ? [{ debugName: "hintClass" }] : /* istanbul ignore next */ []));
|
|
123
141
|
// Computed classes (library base < global config < per-instance).
|
|
124
|
-
// NOTE: the chip search input exposes no aria-describedby, so error/hint
|
|
125
|
-
// ids render without an input-level describedby link.
|
|
126
142
|
$userClass = computed(() => hlm('tw:w-full', this.userClass()), /* @ts-ignore */
|
|
127
143
|
...(ngDevMode ? [{ debugName: "$userClass" }] : /* istanbul ignore next */ []));
|
|
128
144
|
$labelClass = computed(() => hlm('tw:mb-1', this._shared.labelClass, this._config.labelClass, this.labelClass()), /* @ts-ignore */
|
|
@@ -136,7 +152,7 @@ class EgFormCombobox {
|
|
|
136
152
|
$hintClass = computed(() => hlm('tw:mt-0', this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
137
153
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
138
154
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormCombobox, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
139
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormCombobox, isStandalone: true, selector: "eg-form-combobox", 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 }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", 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 }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", 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 }, controlClass: { classPropertyName: "controlClass", publicName: "controlClass", isSignal: true, isRequired: false, transformFunction: null }, chipsClass: { classPropertyName: "chipsClass", publicName: "chipsClass", 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 }], viewQueries: [{ propertyName: "_picker", first: true, predicate: (BrnComboboxMultiple), descendants: true, isSignal: true }, { propertyName: "_single", first: true, predicate: (BrnCombobox), descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
155
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormCombobox, isStandalone: true, selector: "eg-form-combobox", 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 }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", 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 }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", 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 }, controlClass: { classPropertyName: "controlClass", publicName: "controlClass", isSignal: true, isRequired: false, transformFunction: null }, chipsClass: { classPropertyName: "chipsClass", publicName: "chipsClass", 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 }], viewQueries: [{ propertyName: "_picker", first: true, predicate: (BrnComboboxMultiple), descendants: true, isSignal: true }, { propertyName: "_single", first: true, predicate: (BrnCombobox), descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
140
156
|
@let lbl = label();
|
|
141
157
|
@let cnm = controlName();
|
|
142
158
|
@let hnt = hint();
|
|
@@ -144,7 +160,7 @@ class EgFormCombobox {
|
|
|
144
160
|
|
|
145
161
|
<hlm-form-field>
|
|
146
162
|
@if (lbl) {
|
|
147
|
-
<label hlmLabel [for]="effectiveId()" [class]="$labelClass()"
|
|
163
|
+
<label hlmLabel [id]="labelId()" [for]="effectiveId()" [class]="$labelClass()"
|
|
148
164
|
>{{ lbl }}
|
|
149
165
|
@if (rqrd) {
|
|
150
166
|
<span class="tw:text-red-500">*</span>
|
|
@@ -154,11 +170,21 @@ class EgFormCombobox {
|
|
|
154
170
|
|
|
155
171
|
@if (mode() === 'single') {
|
|
156
172
|
<hlm-combobox [formControlName]="cnm" [disabled]="effectiveDisabled()">
|
|
157
|
-
<hlm-combobox-trigger
|
|
173
|
+
<hlm-combobox-trigger
|
|
174
|
+
[buttonId]="effectiveId()"
|
|
175
|
+
[aria-describedby]="describedBy()"
|
|
176
|
+
[required]="rqrd"
|
|
177
|
+
[class]="$controlClass()"
|
|
178
|
+
>
|
|
158
179
|
{{ singleDisplay() ?? placeholder() }}
|
|
159
180
|
</hlm-combobox-trigger>
|
|
160
181
|
<hlm-combobox-content *hlmComboboxPortal>
|
|
161
|
-
<hlm-combobox-input
|
|
182
|
+
<hlm-combobox-input
|
|
183
|
+
[inputId]="effectiveId() + '-search'"
|
|
184
|
+
[aria-labelledby]="lbl ? labelId() : null"
|
|
185
|
+
[aria-describedby]="describedBy()"
|
|
186
|
+
[placeholder]="placeholder()"
|
|
187
|
+
/>
|
|
162
188
|
<div hlmComboboxList>
|
|
163
189
|
@for (option of options(); track option) {
|
|
164
190
|
<hlm-combobox-item [value]="option">{{ option }}</hlm-combobox-item>
|
|
@@ -173,7 +199,14 @@ class EgFormCombobox {
|
|
|
173
199
|
@for (v of selectedValues(); track v) {
|
|
174
200
|
<hlm-combobox-chip [value]="v">{{ v }}</hlm-combobox-chip>
|
|
175
201
|
}
|
|
176
|
-
<input
|
|
202
|
+
<input
|
|
203
|
+
hlmComboboxChipInput
|
|
204
|
+
[id]="effectiveId()"
|
|
205
|
+
[placeholder]="placeholder()"
|
|
206
|
+
brnFieldControlDescribedBy
|
|
207
|
+
[aria-describedby]="describedBy()"
|
|
208
|
+
[attr.aria-required]="rqrd || null"
|
|
209
|
+
/>
|
|
177
210
|
</hlm-combobox-chips>
|
|
178
211
|
<hlm-combobox-content *hlmComboboxPortal>
|
|
179
212
|
<div hlmComboboxList>
|
|
@@ -198,7 +231,7 @@ class EgFormCombobox {
|
|
|
198
231
|
</hlm-hint>
|
|
199
232
|
}
|
|
200
233
|
</hlm-form-field>
|
|
201
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { 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: HlmCombobox, selector: "[hlmCombobox],hlm-combobox" }, { kind: "component", type: HlmComboboxChip, selector: "hlm-combobox-chip", inputs: ["showRemove"] }, { kind: "directive", type: HlmComboboxChipInput, selector: "input[hlmComboboxChipInput]" }, { kind: "directive", type: HlmComboboxChips, selector: "[hlmComboboxChips],hlm-combobox-chips", inputs: ["forceInvalid"] }, { kind: "directive", type: HlmComboboxContent, selector: "[hlmComboboxContent],hlm-combobox-content" }, { kind: "directive", type: HlmComboboxEmpty, selector: "[hlmComboboxEmpty],hlm-combobox-empty" }, { kind: "component", type: HlmComboboxInput, selector: "hlm-combobox-input", inputs: ["inputId", "placeholder", "showTrigger", "showClear", "forceInvalid", "aria-invalid"] }, { kind: "component", type: HlmComboboxItem, selector: "hlm-combobox-item" }, { kind: "directive", type: HlmComboboxList, selector: "[hlmComboboxList]" }, { kind: "directive", type: HlmComboboxMultiple, selector: "[hlmComboboxMultiple],hlm-combobox-multiple" }, { kind: "directive", type: HlmComboboxPortal, selector: "[hlmComboboxPortal]" }, { kind: "component", type: HlmComboboxTrigger, selector: "hlm-combobox-trigger", inputs: ["class", "buttonId", "variant", "forceInvalid"] }] });
|
|
234
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { 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: HlmCombobox, selector: "[hlmCombobox],hlm-combobox" }, { kind: "component", type: HlmComboboxChip, selector: "hlm-combobox-chip", inputs: ["showRemove"] }, { kind: "directive", type: HlmComboboxChipInput, selector: "input[hlmComboboxChipInput]" }, { kind: "directive", type: HlmComboboxChips, selector: "[hlmComboboxChips],hlm-combobox-chips", inputs: ["forceInvalid"] }, { kind: "directive", type: HlmComboboxContent, selector: "[hlmComboboxContent],hlm-combobox-content" }, { kind: "directive", type: HlmComboboxEmpty, selector: "[hlmComboboxEmpty],hlm-combobox-empty" }, { kind: "component", type: HlmComboboxInput, selector: "hlm-combobox-input", inputs: ["inputId", "placeholder", "aria-describedby", "aria-labelledby", "showTrigger", "showClear", "forceInvalid", "aria-invalid"] }, { kind: "component", type: HlmComboboxItem, selector: "hlm-combobox-item" }, { kind: "directive", type: HlmComboboxList, selector: "[hlmComboboxList]" }, { kind: "directive", type: HlmComboboxMultiple, selector: "[hlmComboboxMultiple],hlm-combobox-multiple" }, { kind: "directive", type: HlmComboboxPortal, selector: "[hlmComboboxPortal]" }, { kind: "component", type: HlmComboboxTrigger, selector: "hlm-combobox-trigger", inputs: ["class", "buttonId", "aria-describedby", "required", "variant", "forceInvalid"] }, { kind: "directive", type: BrnFieldControlDescribedBy, selector: "[brnFieldControlDescribedBy]", inputs: ["aria-describedby"] }] });
|
|
202
235
|
}
|
|
203
236
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormCombobox, decorators: [{
|
|
204
237
|
type: Component,
|
|
@@ -226,6 +259,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
226
259
|
HlmComboboxMultiple,
|
|
227
260
|
HlmComboboxPortal,
|
|
228
261
|
HlmComboboxTrigger,
|
|
262
|
+
BrnFieldControlDescribedBy,
|
|
229
263
|
],
|
|
230
264
|
providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
|
|
231
265
|
template: `
|
|
@@ -236,7 +270,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
236
270
|
|
|
237
271
|
<hlm-form-field>
|
|
238
272
|
@if (lbl) {
|
|
239
|
-
<label hlmLabel [for]="effectiveId()" [class]="$labelClass()"
|
|
273
|
+
<label hlmLabel [id]="labelId()" [for]="effectiveId()" [class]="$labelClass()"
|
|
240
274
|
>{{ lbl }}
|
|
241
275
|
@if (rqrd) {
|
|
242
276
|
<span class="tw:text-red-500">*</span>
|
|
@@ -246,11 +280,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
246
280
|
|
|
247
281
|
@if (mode() === 'single') {
|
|
248
282
|
<hlm-combobox [formControlName]="cnm" [disabled]="effectiveDisabled()">
|
|
249
|
-
<hlm-combobox-trigger
|
|
283
|
+
<hlm-combobox-trigger
|
|
284
|
+
[buttonId]="effectiveId()"
|
|
285
|
+
[aria-describedby]="describedBy()"
|
|
286
|
+
[required]="rqrd"
|
|
287
|
+
[class]="$controlClass()"
|
|
288
|
+
>
|
|
250
289
|
{{ singleDisplay() ?? placeholder() }}
|
|
251
290
|
</hlm-combobox-trigger>
|
|
252
291
|
<hlm-combobox-content *hlmComboboxPortal>
|
|
253
|
-
<hlm-combobox-input
|
|
292
|
+
<hlm-combobox-input
|
|
293
|
+
[inputId]="effectiveId() + '-search'"
|
|
294
|
+
[aria-labelledby]="lbl ? labelId() : null"
|
|
295
|
+
[aria-describedby]="describedBy()"
|
|
296
|
+
[placeholder]="placeholder()"
|
|
297
|
+
/>
|
|
254
298
|
<div hlmComboboxList>
|
|
255
299
|
@for (option of options(); track option) {
|
|
256
300
|
<hlm-combobox-item [value]="option">{{ option }}</hlm-combobox-item>
|
|
@@ -265,7 +309,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
265
309
|
@for (v of selectedValues(); track v) {
|
|
266
310
|
<hlm-combobox-chip [value]="v">{{ v }}</hlm-combobox-chip>
|
|
267
311
|
}
|
|
268
|
-
<input
|
|
312
|
+
<input
|
|
313
|
+
hlmComboboxChipInput
|
|
314
|
+
[id]="effectiveId()"
|
|
315
|
+
[placeholder]="placeholder()"
|
|
316
|
+
brnFieldControlDescribedBy
|
|
317
|
+
[aria-describedby]="describedBy()"
|
|
318
|
+
[attr.aria-required]="rqrd || null"
|
|
319
|
+
/>
|
|
269
320
|
</hlm-combobox-chips>
|
|
270
321
|
<hlm-combobox-content *hlmComboboxPortal>
|
|
271
322
|
<div hlmComboboxList>
|
|
@@ -292,7 +343,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
292
343
|
</hlm-form-field>
|
|
293
344
|
`,
|
|
294
345
|
}]
|
|
295
|
-
}], propDecorators: { _picker: [{ type: i0.ViewChild, args: [i0.forwardRef(() => BrnComboboxMultiple), { isSignal: true }] }], _single: [{ type: i0.ViewChild, args: [i0.forwardRef(() => BrnCombobox), { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", 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 }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], controlClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlClass", required: false }] }], chipsClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "chipsClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
|
|
346
|
+
}], propDecorators: { _picker: [{ type: i0.ViewChild, args: [i0.forwardRef(() => BrnComboboxMultiple), { isSignal: true }] }], _single: [{ type: i0.ViewChild, args: [i0.forwardRef(() => BrnCombobox), { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", 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 }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], controlClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlClass", required: false }] }], chipsClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "chipsClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
|
|
296
347
|
|
|
297
348
|
/*
|
|
298
349
|
* Public API Surface of form-combobox
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as _angular_core from '@angular/core';
|
|
2
|
-
import { ValueProvider } from '@angular/core';
|
|
2
|
+
import { OnInit, ValueProvider } from '@angular/core';
|
|
3
3
|
import { ClassValue } from 'clsx';
|
|
4
4
|
|
|
5
|
-
declare class EgFormCombobox {
|
|
5
|
+
declare class EgFormCombobox implements OnInit {
|
|
6
6
|
private readonly formGroupDirective;
|
|
7
7
|
private readonly _errorMessages;
|
|
8
8
|
private readonly generatedId;
|
|
@@ -10,6 +10,9 @@ declare class EgFormCombobox {
|
|
|
10
10
|
private readonly _shared;
|
|
11
11
|
private readonly _picker;
|
|
12
12
|
private readonly _single;
|
|
13
|
+
private readonly _cdr;
|
|
14
|
+
private readonly _destroyRef;
|
|
15
|
+
ngOnInit(): void;
|
|
13
16
|
label: _angular_core.InputSignal<string | undefined>;
|
|
14
17
|
controlId: _angular_core.InputSignal<string | undefined>;
|
|
15
18
|
controlName: _angular_core.InputSignal<string>;
|
|
@@ -21,6 +24,8 @@ declare class EgFormCombobox {
|
|
|
21
24
|
*/
|
|
22
25
|
autoError: _angular_core.InputSignal<boolean>;
|
|
23
26
|
hint: _angular_core.InputSignal<string | undefined>;
|
|
27
|
+
/** Consumer-owned description IDs, merged with the current error and hint IDs in both modes. */
|
|
28
|
+
ariaDescribedBy: _angular_core.InputSignal<string | null>;
|
|
24
29
|
id: _angular_core.InputSignal<string | undefined>;
|
|
25
30
|
placeholder: _angular_core.InputSignal<string>;
|
|
26
31
|
emptyText: _angular_core.InputSignal<string>;
|
|
@@ -34,6 +39,7 @@ declare class EgFormCombobox {
|
|
|
34
39
|
mode: _angular_core.InputSignal<"single" | "multiple">;
|
|
35
40
|
options: _angular_core.InputSignal<string[]>;
|
|
36
41
|
readonly effectiveId: _angular_core.Signal<string>;
|
|
42
|
+
readonly labelId: _angular_core.Signal<string>;
|
|
37
43
|
readonly errorId: _angular_core.Signal<string>;
|
|
38
44
|
readonly hintId: _angular_core.Signal<string>;
|
|
39
45
|
/**
|
|
@@ -59,6 +65,7 @@ declare class EgFormCombobox {
|
|
|
59
65
|
* wrapper-toggle + `control.disable()` cannot clobber CVA state.
|
|
60
66
|
*/
|
|
61
67
|
effectiveDisabled(): boolean;
|
|
68
|
+
protected describedBy(): string | null;
|
|
62
69
|
userClass: _angular_core.InputSignal<ClassValue>;
|
|
63
70
|
labelClass: _angular_core.InputSignal<string>;
|
|
64
71
|
controlClass: _angular_core.InputSignal<string>;
|
|
@@ -72,7 +79,7 @@ declare class EgFormCombobox {
|
|
|
72
79
|
$errorClass: _angular_core.Signal<string>;
|
|
73
80
|
$hintClass: _angular_core.Signal<string>;
|
|
74
81
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormCombobox, never>;
|
|
75
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormCombobox, "eg-form-combobox", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "controlName": { "alias": "controlName"; "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; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "controlClass": { "alias": "controlClass"; "required": false; "isSignal": true; }; "chipsClass": { "alias": "chipsClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
82
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormCombobox, "eg-form-combobox", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "controlName": { "alias": "controlName"; "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; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "controlClass": { "alias": "controlClass"; "required": false; "isSignal": true; }; "chipsClass": { "alias": "chipsClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
76
83
|
}
|
|
77
84
|
|
|
78
85
|
interface EgFormComboboxConfig {
|
package/form-input-otp/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Form Input OTP (`@egose/shadcn-theme-ng/form-input-otp`)
|
|
2
2
|
|
|
3
|
-
A ready-made reactive-form one-time-code field: label + `
|
|
3
|
+
A ready-made reactive-form one-time-code field: label + `hlm-input-otp` slots + validation error/hint display in one tag. The model is a plain `string`.
|
|
4
4
|
|
|
5
|
-
The Angular implementation is a standalone wrapper: it renders `HlmFormField` / `HlmError` / `HlmHint` from `@egose/shadcn-theme-ng/form-field`, `HlmLabel`, and `
|
|
5
|
+
The Angular implementation is a standalone wrapper: it renders `HlmFormField` / `HlmError` / `HlmHint` from `@egose/shadcn-theme-ng/form-field`, `HlmLabel`, and `HlmInputOtpControl` + `HlmInputOtp*` parts from `@egose/shadcn-theme-ng/input-otp`. The adapter inherits `BrnInputOtp` behavior. The OTP input is bound with `[formControlName]="controlName()"` and renders one `hlm-input-otp-slot` per `length` unit in a single group. It must live inside a `FormGroupDirective` (`[formGroup]` parent); ids come from `HlmFormIdGenerator` unless overridden.
|
|
6
6
|
|
|
7
7
|
> **Ships as:** `@egose/shadcn-theme-ng/form-input-otp` and `@egose/shadcn-theme-ng-tw/form-input-otp`
|
|
8
8
|
> (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
|
|
@@ -53,7 +53,7 @@ NgModule-based consumer: add `EgFormInputOtp` (and `ReactiveFormsModule`) to the
|
|
|
53
53
|
`imports` — it is a standalone component, not a module.
|
|
54
54
|
|
|
55
55
|
`ControlValueAccessor` behavior: `eg-form-input-otp` itself is not a `ControlValueAccessor`; the
|
|
56
|
-
inner `
|
|
56
|
+
inner `hlm-input-otp` is (bound via `formControlName`), so `formControlName`/`formGroup` handling,
|
|
57
57
|
`Validators`, and `disabled` state from the control all flow through the parent form.
|
|
58
58
|
|
|
59
59
|
## Anatomy / Structure
|
|
@@ -169,8 +169,10 @@ export class PinComponent {
|
|
|
169
169
|
## Accessibility notes
|
|
170
170
|
|
|
171
171
|
- Label `for` ↔ OTP `inputId` wiring is automatic via `effectiveId` — always pass a `label`.
|
|
172
|
-
- `
|
|
173
|
-
-
|
|
172
|
+
- The wrapper uses `HlmInputOtpControl` (`hlm-input-otp`), a declarative adapter retaining `BrnInputOtp`'s CVA/editing behavior. Its native input references the displayed error (invalid + touched/dirty/submitted), otherwise the hint; correction/reset restores the hint.
|
|
173
|
+
- Optional `[aria-describedby]` (`string | null`, default `null`) adds consumer-owned IDs before the current message ID, normalized and deduplicated. Keep external elements mounted and reserve `<effectiveId>-error` / `-hint` for the wrapper. Bind the input rather than `[attr.aria-describedby]` on the wrapper host.
|
|
174
|
+
- `required` supplies the visual `*` and native `aria-required`; configure `Validators.required` (plus `minLength`) independently. Native `aria-invalid` follows the form control.
|
|
175
|
+
- `HlmError` is not a live region. Rendered DOM/focus regressions are automated coverage, not screen-reader testing.
|
|
174
176
|
|
|
175
177
|
## Theming / CSS variables
|
|
176
178
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, input, numberAttribute, computed, Component } from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, ChangeDetectorRef, DestroyRef, input, numberAttribute, computed, Component } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
|
|
5
5
|
import { injectEgFormErrorMessages, HlmFormIdGenerator, injectEgFormSharedConfig, resolveEgFormError, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
|
|
6
6
|
import { HlmLabel } from '@egose/shadcn-theme-ng-tw/label';
|
|
7
|
-
import { BrnInputOtp } from '@spartan-ng/brain/input-otp';
|
|
8
7
|
import * as i2 from '@egose/shadcn-theme-ng-tw/input-otp';
|
|
9
8
|
import { HlmInputOtpImports } from '@egose/shadcn-theme-ng-tw/input-otp';
|
|
10
9
|
import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
|
|
10
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
11
11
|
|
|
12
12
|
const EgFormInputOtpConfigToken = new InjectionToken('EgFormInputOtpConfig');
|
|
13
13
|
function provideEgFormInputOtpConfig(config) {
|
|
@@ -23,6 +23,14 @@ class EgFormInputOtp {
|
|
|
23
23
|
generatedId = inject(HlmFormIdGenerator).generate('eg-form-input-otp');
|
|
24
24
|
_config = injectEgFormInputOtpConfig();
|
|
25
25
|
_shared = injectEgFormSharedConfig();
|
|
26
|
+
_cdr = inject(ChangeDetectorRef);
|
|
27
|
+
_destroyRef = inject(DestroyRef);
|
|
28
|
+
ngOnInit() {
|
|
29
|
+
// Submit/reset may change message visibility without changing the field's value or validity.
|
|
30
|
+
this.formGroupDirective.form.events
|
|
31
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
32
|
+
.subscribe(() => this._cdr.markForCheck());
|
|
33
|
+
}
|
|
26
34
|
label = input(undefined, /* @ts-ignore */
|
|
27
35
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
28
36
|
controlId = input(undefined, /* @ts-ignore */
|
|
@@ -40,6 +48,8 @@ class EgFormInputOtp {
|
|
|
40
48
|
...(ngDevMode ? [{ debugName: "autoError" }] : /* istanbul ignore next */ []));
|
|
41
49
|
hint = input(undefined, /* @ts-ignore */
|
|
42
50
|
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
51
|
+
/** Consumer-owned description IDs, merged with the current error and hint IDs. */
|
|
52
|
+
ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
|
|
43
53
|
id = input(undefined, /* @ts-ignore */
|
|
44
54
|
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
45
55
|
length = input(6, { ...(ngDevMode ? { debugName: "length" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
@@ -89,8 +99,10 @@ class EgFormInputOtp {
|
|
|
89
99
|
effectiveDisabled() {
|
|
90
100
|
return this.disabled() || !!this.formGroupDirective.form.get(this.controlName())?.disabled;
|
|
91
101
|
}
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
describedBy() {
|
|
103
|
+
const ids = [this.ariaDescribedBy(), this.showError() ? this.errorId() : this.hint() ? this.hintId() : null];
|
|
104
|
+
return [...new Set(ids.filter(Boolean).join(' ').split(/\s+/).filter(Boolean))].join(' ') || null;
|
|
105
|
+
}
|
|
94
106
|
// Styling
|
|
95
107
|
userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
|
|
96
108
|
labelClass = input('', /* @ts-ignore */
|
|
@@ -113,7 +125,7 @@ class EgFormInputOtp {
|
|
|
113
125
|
$hintClass = computed(() => hlm('tw:mt-0', this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
114
126
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
115
127
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormInputOtp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
116
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormInputOtp, isStandalone: true, selector: "eg-form-input-otp", 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 }, length: { classPropertyName: "length", publicName: "length", 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 }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, otpClass: { classPropertyName: "otpClass", publicName: "otpClass", 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: `
|
|
128
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormInputOtp, isStandalone: true, selector: "eg-form-input-otp", 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 }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, length: { classPropertyName: "length", publicName: "length", 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 }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, otpClass: { classPropertyName: "otpClass", publicName: "otpClass", 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: `
|
|
117
129
|
@let lbl = label();
|
|
118
130
|
@let cnm = controlName();
|
|
119
131
|
@let hnt = hint();
|
|
@@ -129,11 +141,12 @@ class EgFormInputOtp {
|
|
|
129
141
|
</label>
|
|
130
142
|
}
|
|
131
143
|
|
|
132
|
-
<
|
|
133
|
-
hlmInputOtp
|
|
144
|
+
<hlm-input-otp
|
|
134
145
|
[length]="length()"
|
|
135
146
|
[formControlName]="cnm"
|
|
136
147
|
[inputId]="effectiveId()"
|
|
148
|
+
[aria-describedby]="describedBy()"
|
|
149
|
+
[required]="rqrd"
|
|
137
150
|
[disabled]="effectiveDisabled()"
|
|
138
151
|
[class]="$otpClass()"
|
|
139
152
|
>
|
|
@@ -142,7 +155,7 @@ class EgFormInputOtp {
|
|
|
142
155
|
<hlm-input-otp-slot [index]="i" />
|
|
143
156
|
}
|
|
144
157
|
</div>
|
|
145
|
-
</
|
|
158
|
+
</hlm-input-otp>
|
|
146
159
|
|
|
147
160
|
@if (showError()) {
|
|
148
161
|
<hlm-error [id]="errorId()" [class]="$errorClass()">
|
|
@@ -156,7 +169,7 @@ class EgFormInputOtp {
|
|
|
156
169
|
</hlm-hint>
|
|
157
170
|
}
|
|
158
171
|
</hlm-form-field>
|
|
159
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { 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: "component", type:
|
|
172
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { 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.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: "component", type: i2.HlmInputOtpControl, selector: "hlm-input-otp", inputs: ["aria-describedby", "required"] }, { kind: "directive", type: i2.HlmInputOtpGroup, selector: "[hlmInputOtpGroup],hlm-input-otp-group" }, { kind: "component", type: i2.HlmInputOtpSlot, selector: "hlm-input-otp-slot", inputs: ["index"] }] });
|
|
160
173
|
}
|
|
161
174
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormInputOtp, decorators: [{
|
|
162
175
|
type: Component,
|
|
@@ -166,7 +179,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
166
179
|
host: {
|
|
167
180
|
'[class]': '$userClass()',
|
|
168
181
|
},
|
|
169
|
-
imports: [ReactiveFormsModule, HlmFormField, HlmError, HlmHint, HlmLabel,
|
|
182
|
+
imports: [ReactiveFormsModule, HlmFormField, HlmError, HlmHint, HlmLabel, HlmInputOtpImports],
|
|
170
183
|
providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
|
|
171
184
|
template: `
|
|
172
185
|
@let lbl = label();
|
|
@@ -184,11 +197,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
184
197
|
</label>
|
|
185
198
|
}
|
|
186
199
|
|
|
187
|
-
<
|
|
188
|
-
hlmInputOtp
|
|
200
|
+
<hlm-input-otp
|
|
189
201
|
[length]="length()"
|
|
190
202
|
[formControlName]="cnm"
|
|
191
203
|
[inputId]="effectiveId()"
|
|
204
|
+
[aria-describedby]="describedBy()"
|
|
205
|
+
[required]="rqrd"
|
|
192
206
|
[disabled]="effectiveDisabled()"
|
|
193
207
|
[class]="$otpClass()"
|
|
194
208
|
>
|
|
@@ -197,7 +211,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
197
211
|
<hlm-input-otp-slot [index]="i" />
|
|
198
212
|
}
|
|
199
213
|
</div>
|
|
200
|
-
</
|
|
214
|
+
</hlm-input-otp>
|
|
201
215
|
|
|
202
216
|
@if (showError()) {
|
|
203
217
|
<hlm-error [id]="errorId()" [class]="$errorClass()">
|
|
@@ -213,7 +227,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
213
227
|
</hlm-form-field>
|
|
214
228
|
`,
|
|
215
229
|
}]
|
|
216
|
-
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", 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 }] }], length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], otpClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "otpClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
|
|
230
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", 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 }] }], length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], otpClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "otpClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
|
|
217
231
|
|
|
218
232
|
/*
|
|
219
233
|
* Public API Surface of form-input-otp
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import * as _angular_core from '@angular/core';
|
|
2
|
-
import { ValueProvider } from '@angular/core';
|
|
2
|
+
import { OnInit, ValueProvider } from '@angular/core';
|
|
3
3
|
import { ClassValue } from 'clsx';
|
|
4
4
|
import { NumberInput } from '@angular/cdk/coercion';
|
|
5
5
|
|
|
6
|
-
declare class EgFormInputOtp {
|
|
6
|
+
declare class EgFormInputOtp implements OnInit {
|
|
7
7
|
private readonly formGroupDirective;
|
|
8
8
|
private readonly _errorMessages;
|
|
9
9
|
private readonly generatedId;
|
|
10
10
|
private readonly _config;
|
|
11
11
|
private readonly _shared;
|
|
12
|
+
private readonly _cdr;
|
|
13
|
+
private readonly _destroyRef;
|
|
14
|
+
ngOnInit(): void;
|
|
12
15
|
label: _angular_core.InputSignal<string | undefined>;
|
|
13
16
|
controlId: _angular_core.InputSignal<string | undefined>;
|
|
14
17
|
controlName: _angular_core.InputSignal<string>;
|
|
@@ -20,6 +23,8 @@ declare class EgFormInputOtp {
|
|
|
20
23
|
*/
|
|
21
24
|
autoError: _angular_core.InputSignal<boolean>;
|
|
22
25
|
hint: _angular_core.InputSignal<string | undefined>;
|
|
26
|
+
/** Consumer-owned description IDs, merged with the current error and hint IDs. */
|
|
27
|
+
ariaDescribedBy: _angular_core.InputSignal<string | null>;
|
|
23
28
|
id: _angular_core.InputSignal<string | undefined>;
|
|
24
29
|
length: _angular_core.InputSignalWithTransform<number, NumberInput>;
|
|
25
30
|
disabled: _angular_core.InputSignal<boolean>;
|
|
@@ -47,6 +52,7 @@ declare class EgFormInputOtp {
|
|
|
47
52
|
* wrapper-toggle + `control.disable()` cannot clobber CVA state.
|
|
48
53
|
*/
|
|
49
54
|
effectiveDisabled(): boolean;
|
|
55
|
+
protected describedBy(): string | null;
|
|
50
56
|
userClass: _angular_core.InputSignal<ClassValue>;
|
|
51
57
|
labelClass: _angular_core.InputSignal<string>;
|
|
52
58
|
otpClass: _angular_core.InputSignal<string>;
|
|
@@ -58,7 +64,7 @@ declare class EgFormInputOtp {
|
|
|
58
64
|
$errorClass: _angular_core.Signal<string>;
|
|
59
65
|
$hintClass: _angular_core.Signal<string>;
|
|
60
66
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormInputOtp, never>;
|
|
61
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormInputOtp, "eg-form-input-otp", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "controlName": { "alias": "controlName"; "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; }; "length": { "alias": "length"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "otpClass": { "alias": "otpClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
67
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormInputOtp, "eg-form-input-otp", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "controlName": { "alias": "controlName"; "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; }; "length": { "alias": "length"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "otpClass": { "alias": "otpClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
interface EgFormInputOtpConfig {
|
|
@@ -100,6 +100,19 @@ No outputs. Readonly computeds/methods (usable via template ref): `effectiveId()
|
|
|
100
100
|
|
|
101
101
|
`SelectOption` is `export interface SelectOption { label: string; value: string }` from `@egose/shadcn-theme-ng/searchable-multiselect`.
|
|
102
102
|
|
|
103
|
+
### Local search and form values
|
|
104
|
+
|
|
105
|
+
Additional copy inputs:
|
|
106
|
+
|
|
107
|
+
- `searchLabel: string` (default `'Search options'`): visible native search label.
|
|
108
|
+
- `searchPlaceholder: string` (default `'Type to filter…'`): hint inside the search input.
|
|
109
|
+
- `emptyMessage: string` (default `'No matching options'`): polite status message for no matches or no supplied options.
|
|
110
|
+
- `removeLabel: (option: SelectOption) => string` (default ``option => `Remove ${option.label}` ``): pure accessible chip-remove name formatter; unresolved labels are raw IDs.
|
|
111
|
+
|
|
112
|
+
Pass the full options list. The inner control trims the query and matches label substrings case-insensitively in source order. Filtering retains hidden selections and their labels, including unresolved IDs written before asynchronous options arrive. Searching/clearing the query never changes or touches the form value. The query persists across closing/reopening and form resets; clearing its text restores all options. Wrapper and reactive-form disabled states disable the search even if the popover is already open.
|
|
113
|
+
|
|
114
|
+
The search has an associated visible label, no-match feedback uses a polite live region, and each chip remove button has an accessible name. All four copy inputs above are forwarded unchanged. For example, on the wrapper in the complete reactive-form example below, add `searchLabel="Find tags" searchPlaceholder="Type a tag name…" emptyMessage="No tags match"`. Import `SelectOption` from the multiselect subpath to type a localized `[removeLabel]` callback. See the [multiselect usage](../searchable-multiselect/README.md#local-search) for keyboard and async ownership details. This is local label search, with no remote request or virtualization behavior.
|
|
115
|
+
|
|
103
116
|
## Examples
|
|
104
117
|
|
|
105
118
|
### 1. Basic reactive form
|
|
@@ -237,7 +250,8 @@ import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searcha
|
|
|
237
250
|
controlId="framework-picker"
|
|
238
251
|
label="Frameworks"
|
|
239
252
|
placeholder="Search frameworks…"
|
|
240
|
-
|
|
253
|
+
searchLabel="Find frameworks"
|
|
254
|
+
hint="Type to filter, then Tab to a checkbox and Space to select"
|
|
241
255
|
[options]="frameworks"
|
|
242
256
|
class="tw:max-w-md"
|
|
243
257
|
labelClass="tw:font-semibold"
|