@egose/shadcn-theme-ng 0.4.0 → 0.5.0
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 +1 -1
- package/form-autocomplete/fesm2022/form-autocomplete.mjs +7 -6
- package/form-autocomplete/types/form-autocomplete.d.ts +1 -0
- package/form-checkbox/README.md +3 -3
- package/form-checkbox/fesm2022/form-checkbox.mjs +65 -10
- package/form-checkbox/types/form-checkbox.d.ts +24 -4
- package/form-combobox/fesm2022/form-combobox.mjs +7 -6
- package/form-combobox/types/form-combobox.d.ts +1 -0
- package/form-date-picker/fesm2022/form-date-picker.mjs +7 -6
- package/form-date-picker/types/form-date-picker.d.ts +1 -0
- package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +7 -6
- package/form-date-picker-multi/types/form-date-picker-multi.d.ts +1 -0
- package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +7 -6
- package/form-date-range-picker/types/form-date-range-picker.d.ts +1 -0
- package/form-field/fesm2022/form-field.mjs +18 -1
- package/form-field/types/form-field.d.ts +48 -2
- package/form-input-otp/fesm2022/form-input-otp.mjs +6 -5
- package/form-input-otp/types/form-input-otp.d.ts +1 -0
- package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +7 -6
- package/form-month-year-picker/types/form-month-year-picker.d.ts +1 -0
- package/form-native-select/fesm2022/form-native-select.mjs +6 -5
- package/form-native-select/types/form-native-select.d.ts +1 -0
- package/form-phone-input/fesm2022/form-phone-input.mjs +6 -5
- package/form-phone-input/types/form-phone-input.d.ts +1 -0
- package/form-radio-group/fesm2022/form-radio-group.mjs +6 -5
- package/form-radio-group/types/form-radio-group.d.ts +1 -0
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +6 -5
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +1 -0
- package/form-select/fesm2022/form-select.mjs +6 -5
- package/form-select/types/form-select.d.ts +1 -0
- package/form-slider/fesm2022/form-slider.mjs +6 -5
- package/form-slider/types/form-slider.d.ts +1 -0
- package/form-switch/fesm2022/form-switch.mjs +6 -5
- package/form-switch/types/form-switch.d.ts +1 -0
- package/form-text-input/fesm2022/form-text-input.mjs +6 -5
- package/form-text-input/types/form-text-input.d.ts +1 -0
- package/form-textarea/fesm2022/form-textarea.mjs +6 -5
- package/form-textarea/types/form-textarea.d.ts +1 -0
- package/form-toggle/fesm2022/form-toggle.mjs +8 -5
- package/form-toggle/types/form-toggle.d.ts +1 -0
- package/form-toggle-group/fesm2022/form-toggle-group.mjs +6 -5
- package/form-toggle-group/types/form-toggle-group.d.ts +1 -0
- package/package.json +5 -1
- package/stepper/README.md +353 -0
- package/stepper/fesm2022/stepper.mjs +396 -0
- package/stepper/package.json +24 -0
- package/stepper/types/stepper.d.ts +116 -0
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, inject, input, computed, Component } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
|
|
5
|
-
import { injectEgFormErrorMessages, HlmFormIdGenerator, resolveEgFormError, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
|
|
5
|
+
import { injectEgFormErrorMessages, HlmFormIdGenerator, injectEgFormSharedConfig, resolveEgFormError, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
|
|
6
6
|
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
7
7
|
import { HlmInput } from '@egose/shadcn-theme-ng/input';
|
|
8
8
|
import { hlm } from '@egose/shadcn-theme-ng/utils';
|
|
@@ -20,6 +20,7 @@ class EgFormTextInput {
|
|
|
20
20
|
_errorMessages = injectEgFormErrorMessages();
|
|
21
21
|
generatedId = inject(HlmFormIdGenerator).generate('eg-form-text-input');
|
|
22
22
|
_config = injectEgFormTextInputConfig();
|
|
23
|
+
_shared = injectEgFormSharedConfig();
|
|
23
24
|
label = input(undefined, /* @ts-ignore */
|
|
24
25
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
25
26
|
controlId = input(undefined, /* @ts-ignore */
|
|
@@ -117,13 +118,13 @@ class EgFormTextInput {
|
|
|
117
118
|
// Computed class bindings (library base < global config < per-instance)
|
|
118
119
|
$userClass = computed(() => hlm("w-full", this.userClass()), /* @ts-ignore */
|
|
119
120
|
...(ngDevMode ? [{ debugName: "$userClass" }] : /* istanbul ignore next */ []));
|
|
120
|
-
$labelClass = computed(() => hlm("mb-1 gap-0", this._config.labelClass, this.labelClass()), /* @ts-ignore */
|
|
121
|
+
$labelClass = computed(() => hlm("mb-1 gap-0", this._shared.labelClass, this._config.labelClass, this.labelClass()), /* @ts-ignore */
|
|
121
122
|
...(ngDevMode ? [{ debugName: "$labelClass" }] : /* istanbul ignore next */ []));
|
|
122
|
-
$inputClass = computed(() => hlm("mb-1", this._config.inputClass, this.inputClass()), /* @ts-ignore */
|
|
123
|
+
$inputClass = computed(() => hlm("mb-1", this._shared.inputClass, this._config.inputClass, this.inputClass()), /* @ts-ignore */
|
|
123
124
|
...(ngDevMode ? [{ debugName: "$inputClass" }] : /* istanbul ignore next */ []));
|
|
124
|
-
$errorClass = computed(() => hlm("mt-0", this._config.errorClass, this.errorClass()), /* @ts-ignore */
|
|
125
|
+
$errorClass = computed(() => hlm("mt-0", this._shared.errorClass, this._config.errorClass, this.errorClass()), /* @ts-ignore */
|
|
125
126
|
...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
|
|
126
|
-
$hintClass = computed(() => hlm("mt-0", this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
127
|
+
$hintClass = computed(() => hlm("mt-0", this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
127
128
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
128
129
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
129
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"] }] });
|
|
@@ -7,6 +7,7 @@ declare class EgFormTextInput {
|
|
|
7
7
|
private readonly _errorMessages;
|
|
8
8
|
private readonly generatedId;
|
|
9
9
|
private readonly _config;
|
|
10
|
+
private readonly _shared;
|
|
10
11
|
label: _angular_core.InputSignal<string | undefined>;
|
|
11
12
|
controlId: _angular_core.InputSignal<string | undefined>;
|
|
12
13
|
controlName: _angular_core.InputSignal<string>;
|
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, inject, input, computed, Component } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
|
|
5
|
-
import { injectEgFormErrorMessages, HlmFormIdGenerator, resolveEgFormError, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
|
|
5
|
+
import { injectEgFormErrorMessages, HlmFormIdGenerator, injectEgFormSharedConfig, resolveEgFormError, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
|
|
6
6
|
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
7
7
|
import { HlmInput } from '@egose/shadcn-theme-ng/input';
|
|
8
8
|
import { hlm } from '@egose/shadcn-theme-ng/utils';
|
|
@@ -20,6 +20,7 @@ class EgFormTextarea {
|
|
|
20
20
|
_errorMessages = injectEgFormErrorMessages();
|
|
21
21
|
generatedId = inject(HlmFormIdGenerator).generate('eg-form-textarea');
|
|
22
22
|
_config = injectEgFormTextareaConfig();
|
|
23
|
+
_shared = injectEgFormSharedConfig();
|
|
23
24
|
// General props
|
|
24
25
|
label = input(undefined, /* @ts-ignore */
|
|
25
26
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
@@ -110,13 +111,13 @@ class EgFormTextarea {
|
|
|
110
111
|
// Computed class bindings (library base < global config < per-instance)
|
|
111
112
|
$userClass = computed(() => hlm("w-full", this.userClass()), /* @ts-ignore */
|
|
112
113
|
...(ngDevMode ? [{ debugName: "$userClass" }] : /* istanbul ignore next */ []));
|
|
113
|
-
$labelClass = computed(() => hlm("mb-1 gap-0", this._config.labelClass, this.labelClass()), /* @ts-ignore */
|
|
114
|
+
$labelClass = computed(() => hlm("mb-1 gap-0", this._shared.labelClass, this._config.labelClass, this.labelClass()), /* @ts-ignore */
|
|
114
115
|
...(ngDevMode ? [{ debugName: "$labelClass" }] : /* istanbul ignore next */ []));
|
|
115
|
-
$textareaClass = computed(() => hlm("mb-1", this._config.textareaClass, this.textareaClass()), /* @ts-ignore */
|
|
116
|
+
$textareaClass = computed(() => hlm("mb-1", this._shared.textareaClass, this._config.textareaClass, this.textareaClass()), /* @ts-ignore */
|
|
116
117
|
...(ngDevMode ? [{ debugName: "$textareaClass" }] : /* istanbul ignore next */ []));
|
|
117
|
-
$errorClass = computed(() => hlm("mt-0", this._config.errorClass, this.errorClass()), /* @ts-ignore */
|
|
118
|
+
$errorClass = computed(() => hlm("mt-0", this._shared.errorClass, this._config.errorClass, this.errorClass()), /* @ts-ignore */
|
|
118
119
|
...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
|
|
119
|
-
$hintClass = computed(() => hlm("mt-0", this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
120
|
+
$hintClass = computed(() => hlm("mt-0", this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
120
121
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
121
122
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextarea, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
122
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"] }] });
|
|
@@ -7,6 +7,7 @@ declare class EgFormTextarea {
|
|
|
7
7
|
private readonly _errorMessages;
|
|
8
8
|
private readonly generatedId;
|
|
9
9
|
private readonly _config;
|
|
10
|
+
private readonly _shared;
|
|
10
11
|
label: _angular_core.InputSignal<string | undefined>;
|
|
11
12
|
controlId: _angular_core.InputSignal<string | undefined>;
|
|
12
13
|
controlName: _angular_core.InputSignal<string>;
|
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, inject, forwardRef, Injector, DestroyRef, input, booleanAttribute, computed, signal, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
4
|
import { NG_VALUE_ACCESSOR, FormGroupDirective, FormControlName } from '@angular/forms';
|
|
5
|
-
import { HlmFormIdGenerator, injectEgFormErrorMessages, resolveEgFormError, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
|
|
5
|
+
import { HlmFormIdGenerator, injectEgFormSharedConfig, injectEgFormErrorMessages, resolveEgFormError, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
|
|
6
6
|
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
7
7
|
import { HlmToggle } from '@egose/shadcn-theme-ng/toggle';
|
|
8
8
|
import { hlm } from '@egose/shadcn-theme-ng/utils';
|
|
@@ -32,6 +32,7 @@ const EG_FORM_TOGGLE_VALUE_ACCESSOR = {
|
|
|
32
32
|
class EgFormToggle {
|
|
33
33
|
generatedId = inject(HlmFormIdGenerator).generate('eg-form-toggle');
|
|
34
34
|
_config = injectEgFormToggleConfig();
|
|
35
|
+
_shared = injectEgFormSharedConfig();
|
|
35
36
|
_injector = inject(Injector);
|
|
36
37
|
_destroyRef = inject(DestroyRef);
|
|
37
38
|
_formGroupDirective = inject(FormGroupDirective, { optional: true });
|
|
@@ -152,16 +153,18 @@ class EgFormToggle {
|
|
|
152
153
|
...(ngDevMode ? [{ debugName: "errorClass" }] : /* istanbul ignore next */ []));
|
|
153
154
|
hintClass = input('', /* @ts-ignore */
|
|
154
155
|
...(ngDevMode ? [{ debugName: "hintClass" }] : /* istanbul ignore next */ []));
|
|
155
|
-
// Computed classes (library base < global
|
|
156
|
+
// Computed classes (library base < shared global < component global < per-instance).
|
|
157
|
+
// Note: toggle keeps its own CVA/error-hint behavior; only the shared
|
|
158
|
+
// label/error/hint styling slots are picked up (toggleClass stays local).
|
|
156
159
|
$userClass = computed(() => hlm("w-full", this.userClass()), /* @ts-ignore */
|
|
157
160
|
...(ngDevMode ? [{ debugName: "$userClass" }] : /* istanbul ignore next */ []));
|
|
158
161
|
$toggleClass = computed(() => hlm(this._config.toggleClass, this.toggleClass()), /* @ts-ignore */
|
|
159
162
|
...(ngDevMode ? [{ debugName: "$toggleClass" }] : /* istanbul ignore next */ []));
|
|
160
|
-
$labelClass = computed(() => hlm(this._config.labelClass, this.labelClass()), /* @ts-ignore */
|
|
163
|
+
$labelClass = computed(() => hlm(this._shared.labelClass, this._config.labelClass, this.labelClass()), /* @ts-ignore */
|
|
161
164
|
...(ngDevMode ? [{ debugName: "$labelClass" }] : /* istanbul ignore next */ []));
|
|
162
|
-
$errorClass = computed(() => hlm("mt-0", this._config.errorClass, this.errorClass()), /* @ts-ignore */
|
|
165
|
+
$errorClass = computed(() => hlm("mt-0", this._shared.errorClass, this._config.errorClass, this.errorClass()), /* @ts-ignore */
|
|
163
166
|
...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
|
|
164
|
-
$hintClass = computed(() => hlm("mt-0", this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
167
|
+
$hintClass = computed(() => hlm("mt-0", this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
165
168
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
166
169
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormToggle, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
167
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 });
|
|
@@ -20,6 +20,7 @@ declare const EG_FORM_TOGGLE_VALUE_ACCESSOR: {
|
|
|
20
20
|
declare class EgFormToggle implements ControlValueAccessor, AfterViewInit, DoCheck {
|
|
21
21
|
private readonly generatedId;
|
|
22
22
|
private readonly _config;
|
|
23
|
+
private readonly _shared;
|
|
23
24
|
private readonly _injector;
|
|
24
25
|
private readonly _destroyRef;
|
|
25
26
|
private readonly _formGroupDirective;
|
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, inject, input, computed, Component } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
|
|
5
|
-
import { injectEgFormErrorMessages, HlmFormIdGenerator, resolveEgFormError, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
|
|
5
|
+
import { injectEgFormErrorMessages, HlmFormIdGenerator, injectEgFormSharedConfig, resolveEgFormError, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
|
|
6
6
|
import { EgFormField } from '@egose/shadcn-theme-ng/form-field-simple';
|
|
7
7
|
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
8
8
|
import { HlmToggleGroup, HlmToggleGroupItem } from '@egose/shadcn-theme-ng/toggle-group';
|
|
@@ -21,6 +21,7 @@ class EgFormToggleGroup {
|
|
|
21
21
|
_errorMessages = injectEgFormErrorMessages();
|
|
22
22
|
generatedId = inject(HlmFormIdGenerator).generate('eg-form-toggle-group');
|
|
23
23
|
_config = injectEgFormToggleGroupConfig();
|
|
24
|
+
_shared = injectEgFormSharedConfig();
|
|
24
25
|
label = input(undefined, /* @ts-ignore */
|
|
25
26
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
26
27
|
controlId = input(undefined, /* @ts-ignore */
|
|
@@ -108,13 +109,13 @@ class EgFormToggleGroup {
|
|
|
108
109
|
// Computed classes (library base < global config < per-instance)
|
|
109
110
|
$userClass = computed(() => hlm("w-full", this.userClass()), /* @ts-ignore */
|
|
110
111
|
...(ngDevMode ? [{ debugName: "$userClass" }] : /* istanbul ignore next */ []));
|
|
111
|
-
$labelClass = computed(() => hlm("mb-1", this._config.labelClass, this.labelClass()), /* @ts-ignore */
|
|
112
|
+
$labelClass = computed(() => hlm("mb-1", this._shared.labelClass, this._config.labelClass, this.labelClass()), /* @ts-ignore */
|
|
112
113
|
...(ngDevMode ? [{ debugName: "$labelClass" }] : /* istanbul ignore next */ []));
|
|
113
|
-
$groupClass = computed(() => hlm(this._config.groupClass, this.groupClass()), /* @ts-ignore */
|
|
114
|
+
$groupClass = computed(() => hlm(this._shared.groupClass, this._config.groupClass, this.groupClass()), /* @ts-ignore */
|
|
114
115
|
...(ngDevMode ? [{ debugName: "$groupClass" }] : /* istanbul ignore next */ []));
|
|
115
|
-
$errorClass = computed(() => hlm("mt-0", this._config.errorClass, this.errorClass()), /* @ts-ignore */
|
|
116
|
+
$errorClass = computed(() => hlm("mt-0", this._shared.errorClass, this._config.errorClass, this.errorClass()), /* @ts-ignore */
|
|
116
117
|
...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
|
|
117
|
-
$hintClass = computed(() => hlm("mt-0", this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
118
|
+
$hintClass = computed(() => hlm("mt-0", this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
|
|
118
119
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
119
120
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormToggleGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
120
121
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormToggleGroup, isStandalone: true, selector: "eg-form-toggle-group", 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 }, type: { classPropertyName: "type", publicName: "type", 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 }, 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 }, groupClass: { classPropertyName: "groupClass", publicName: "groupClass", 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 <eg-form-field>\n @if (lbl) {\n <span hlmLabel [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </span>\n }\n\n <hlm-toggle-group\n [formControlName]=\"cnm\"\n [type]=\"type()\"\n [disabled]=\"effectiveDisabled()\"\n [variant]=\"variant()\"\n [size]=\"size()\"\n [class]=\"$groupClass()\"\n >\n @for (option of options(); track option.value) {\n <button\n hlmToggleGroupItem\n type=\"button\"\n [value]=\"option.value\"\n [disabled]=\"option.disabled ?? false\"\n [attr.aria-describedby]=\"describedBy()\"\n >\n {{ option.label }}\n </button>\n }\n </hlm-toggle-group>\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 </eg-form-field>\n ", 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: "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: HlmToggleGroup, selector: "[hlmToggleGroup],hlm-toggle-group", inputs: ["variant", "size", "spacing", "orientation"] }, { kind: "directive", type: HlmToggleGroupItem, selector: "button[hlmToggleGroupItem]", inputs: ["variant", "size"] }, { kind: "component", type: EgFormField, selector: "eg-form-field", inputs: ["class"] }] });
|
|
@@ -12,6 +12,7 @@ declare class EgFormToggleGroup {
|
|
|
12
12
|
private readonly _errorMessages;
|
|
13
13
|
private readonly generatedId;
|
|
14
14
|
private readonly _config;
|
|
15
|
+
private readonly _shared;
|
|
15
16
|
label: _angular_core.InputSignal<string | undefined>;
|
|
16
17
|
controlId: _angular_core.InputSignal<string | undefined>;
|
|
17
18
|
controlName: _angular_core.InputSignal<string>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@egose/shadcn-theme-ng",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Angular shadcn UI theme",
|
|
5
5
|
"author": "Junmin Ahn",
|
|
6
6
|
"bundles": [
|
|
@@ -350,6 +350,10 @@
|
|
|
350
350
|
"types": "./spinner/types/spinner.d.ts",
|
|
351
351
|
"default": "./spinner/fesm2022/spinner.mjs"
|
|
352
352
|
},
|
|
353
|
+
"./stepper": {
|
|
354
|
+
"types": "./stepper/types/stepper.d.ts",
|
|
355
|
+
"default": "./stepper/fesm2022/stepper.mjs"
|
|
356
|
+
},
|
|
353
357
|
"./switch": {
|
|
354
358
|
"types": "./switch/types/switch.d.ts",
|
|
355
359
|
"default": "./switch/fesm2022/switch.mjs"
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# Stepper (`@egose/shadcn-theme-ng/stepper`)
|
|
2
|
+
|
|
3
|
+
Multi-step workflow navigation, ported from the spartan stepper blocks. This subpath ships seven pieces — `HlmStepper` (root), `HlmStep` (step), `HlmStepHeader` (clickable header with indicator), `HlmStepLabel` (rich label template), `HlmStepContent` (lazy body), `HlmStepperNext` / `HlmStepperPrevious` (navigation buttons) — plus `provideHlmStepperConfig` / `injectHlmStepperConfig` for global defaults. All are thin shadcn-styled components over Angular CDK's `@angular/cdk/stepper` primitives (`CdkStepper`, `CdkStep`, `CdkStepHeader`, `CdkStepLabel`).
|
|
4
|
+
|
|
5
|
+
> **Ships as:** `@egose/shadcn-theme-ng/stepper` and `@egose/shadcn-theme-ng-tw/stepper` (the `tw:`-prefixed Tailwind variant). See the [package README](../../README.md) for install steps, peer dependencies, Tailwind setup, and testing/release guidance. Do not publish this project directory independently.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Plain Tailwind (no prefix)
|
|
11
|
+
npm install @egose/shadcn-theme-ng
|
|
12
|
+
|
|
13
|
+
# Or the tw:-prefixed variant
|
|
14
|
+
npm install @egose/shadcn-theme-ng-tw
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`@angular/cdk` (stepper, portal) arrives via the package peer dependencies. See the [package README](../../README.md) for the full peer-dependency table.
|
|
18
|
+
|
|
19
|
+
## Imports
|
|
20
|
+
|
|
21
|
+
All public symbols are re-exported from `projects/stepper/src/public-api.ts`:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
HlmStep,
|
|
26
|
+
HlmStepContent,
|
|
27
|
+
HlmStepHeader,
|
|
28
|
+
HlmStepLabel,
|
|
29
|
+
HlmStepper,
|
|
30
|
+
HlmStepperImports,
|
|
31
|
+
HlmStepperModule,
|
|
32
|
+
HlmStepperNext,
|
|
33
|
+
HlmStepperPrevious,
|
|
34
|
+
injectHlmStepperConfig,
|
|
35
|
+
provideHlmStepperConfig,
|
|
36
|
+
} from '@egose/shadcn-theme-ng/stepper';
|
|
37
|
+
// tw variant: swap to '@egose/shadcn-theme-ng-tw/stepper'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Standalone-component usage (preferred):
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { Component } from '@angular/core';
|
|
44
|
+
import { HlmStepperImports } from '@egose/shadcn-theme-ng/stepper';
|
|
45
|
+
|
|
46
|
+
@Component({
|
|
47
|
+
selector: 'app-demo',
|
|
48
|
+
standalone: true,
|
|
49
|
+
imports: [...HlmStepperImports],
|
|
50
|
+
template: `<!-- stepper markup here -->`,
|
|
51
|
+
})
|
|
52
|
+
export class DemoComponent {}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
NgModule usage:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { NgModule } from '@angular/core';
|
|
59
|
+
import { HlmStepperModule } from '@egose/shadcn-theme-ng/stepper';
|
|
60
|
+
|
|
61
|
+
@NgModule({ imports: [HlmStepperModule] })
|
|
62
|
+
export class FeatureModule {}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| Symbol | Kind | Description |
|
|
66
|
+
| ------------------------ | ------------- | ------------------------------------------------------------------------ |
|
|
67
|
+
| `HlmStepper` | Component | Root: `<hlm-stepper>` — extends `CdkStepper`. |
|
|
68
|
+
| `HlmStep` | Component | Step: `<hlm-step label="…">` — extends `CdkStep`, adds `icon` input. |
|
|
69
|
+
| `HlmStepHeader` | Component | Header: `<hlm-step-header>` — extends `CdkStepHeader`, renders indicator. |
|
|
70
|
+
| `HlmStepLabel` | Directive | Rich label: `<ng-template hlmStepLabel>` — extends `CdkStepLabel`. |
|
|
71
|
+
| `HlmStepContent` | Directive | Lazy body: `<ng-template hlmStepContent>` — attached on first selection. |
|
|
72
|
+
| `HlmStepperNext` | Directive | Next button: `button[hlmStepperNext]` — extends `CdkStepperNext`. |
|
|
73
|
+
| `HlmStepperPrevious` | Directive | Back button: `button[hlmStepperPrevious]` — extends `CdkStepperPrevious`. |
|
|
74
|
+
| `HlmStepperImports` | `const` array | All seven, spread into `imports: [...]`. |
|
|
75
|
+
| `HlmStepperModule` | NgModule | Imports + re-exports all seven. |
|
|
76
|
+
| `provideHlmStepperConfig`| Function | Global defaults for animations + indicator mode. |
|
|
77
|
+
| `injectHlmStepperConfig` | Function | Reads the (optionally provided) global config. |
|
|
78
|
+
|
|
79
|
+
## Anatomy / Structure
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<hlm-stepper>
|
|
83
|
+
<hlm-step label="Step One">
|
|
84
|
+
<p>Content 1</p>
|
|
85
|
+
<button hlmBtn hlmStepperNext>Next</button>
|
|
86
|
+
</hlm-step>
|
|
87
|
+
|
|
88
|
+
<hlm-step label="Step Two">
|
|
89
|
+
<p>Content 2</p>
|
|
90
|
+
<button hlmBtn variant="outline" hlmStepperPrevious>Back</button>
|
|
91
|
+
<button hlmBtn hlmStepperNext>Next</button>
|
|
92
|
+
</hlm-step>
|
|
93
|
+
|
|
94
|
+
<hlm-step label="Step Three">
|
|
95
|
+
<p>Content 3</p>
|
|
96
|
+
<button hlmBtn variant="outline" hlmStepperPrevious>Back</button>
|
|
97
|
+
<button hlmBtn>Finish</button>
|
|
98
|
+
</hlm-step>
|
|
99
|
+
</hlm-stepper>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Rich label via template (takes precedence over the `label` string):
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<hlm-step [stepControl]="form">
|
|
106
|
+
<ng-template hlmStepLabel>Security</ng-template>
|
|
107
|
+
<!-- … -->
|
|
108
|
+
</hlm-step>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Lazy body (attached only when the step is first selected):
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<hlm-step label="Analytics">
|
|
115
|
+
<ng-template hlmStepContent>
|
|
116
|
+
<p>Heavy content…</p>
|
|
117
|
+
</ng-template>
|
|
118
|
+
</hlm-step>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## API reference
|
|
122
|
+
|
|
123
|
+
### `HlmStepper` — selector `hlm-stepper` (component, extends `CdkStepper`)
|
|
124
|
+
|
|
125
|
+
| Input | Type | Default | Description |
|
|
126
|
+
| -------------------- | ----------------------------- | ---------- | ------------------------------------------------------------------ |
|
|
127
|
+
| `orientation` | forwarded to `CdkStepper` | — | `'horizontal' \| 'vertical'`. |
|
|
128
|
+
| `linear` | forwarded to `CdkStepper` | — | Current step must be valid (via `stepControl`) before advancing. |
|
|
129
|
+
| `selectedIndex` | forwarded to `CdkStepper` | — | Controlled selected-step index. |
|
|
130
|
+
| `labelPosition` | `'end' \| 'bottom'` | `'end'` | Horizontal layout: label beside vs. below the indicator. |
|
|
131
|
+
| `headerPosition` | `'top' \| 'bottom'` | `'top'` | Horizontal layout: headers above vs. below the content panel. |
|
|
132
|
+
| `indicatorMode` | `'number' \| 'state' \| 'icon'` | from config (`'state'`) | Indicator rendering (see below). |
|
|
133
|
+
| `stepperAriaLabel` | `string \| null` | `'Progress'` | `aria-label` for the tablist (ignored when labelledby is set). |
|
|
134
|
+
| `stepperAriaLabelledby` | `string \| null` | `null` | `aria-labelledby` for the tablist. |
|
|
135
|
+
| `animationsEnabled` | `boolean` | from config (`true`) | Toggle step transitions per instance. |
|
|
136
|
+
| `animationDuration` | `number` (ms) | from config (`300`) | Transition duration per instance. |
|
|
137
|
+
|
|
138
|
+
`next()` is overridden to `markAllAsTouched()` + `updateValueAndValidity()` on the current `stepControl` first, so linear steppers surface validation errors before denying the transition.
|
|
139
|
+
|
|
140
|
+
### `HlmStep` — selector `hlm-step` (component, extends `CdkStep`)
|
|
141
|
+
|
|
142
|
+
| Input | Type | Default | Description |
|
|
143
|
+
| ------------- | ---------------- | ------- | -------------------------------------------------------- |
|
|
144
|
+
| `label` | forwarded to `CdkStep` | — | Step title (string, or rich template via `hlmStepLabel`). |
|
|
145
|
+
| `icon` | `string \| null` | `null` | Icon name rendered when `indicatorMode="icon"`. |
|
|
146
|
+
| `stepControl` | forwarded to `CdkStep` | — | Form group driving `linear` validation. |
|
|
147
|
+
| `optional` | forwarded to `CdkStep` | — | Shows an "Optional" caption in the header. |
|
|
148
|
+
| `errorMessage`| forwarded to `CdkStep` | — | Header error caption when the step state is `error`. |
|
|
149
|
+
| `hasError` | forwarded to `CdkStep` | — | Forces the error state (pair with `STEPPER_GLOBAL_OPTIONS` `showError`). |
|
|
150
|
+
|
|
151
|
+
### `HlmStepHeader` — selector `hlm-step-header` (component, extends `CdkStepHeader`)
|
|
152
|
+
|
|
153
|
+
Usually rendered by `HlmStepper` itself; use directly only for custom layouts.
|
|
154
|
+
|
|
155
|
+
| Input | Type | Default | Description |
|
|
156
|
+
| --------------- | ------------------------------------------- | ------- | -------------------------------------------- |
|
|
157
|
+
| `index` | `number` | `0` | Step index (drives the number indicator). |
|
|
158
|
+
| `state` | `StepState` | `'number'` | CDK step state (`number`, `edit`, `done`, `error`, …). |
|
|
159
|
+
| `label` | `HlmStepLabel \| string \| null` | `null` | Resolved label content. |
|
|
160
|
+
| `selected` / `reached` / `active` | `boolean` | `false` | Visual state flags. |
|
|
161
|
+
| `optional` / `disabled` | `boolean` | `false` | Optional caption / disabled styling. |
|
|
162
|
+
| `icon` | `string \| null` | `null` | Icon name for `indicatorMode="icon"`. |
|
|
163
|
+
| `indicatorMode` | `'number' \| 'state' \| 'icon'` | from config (`'state'`) | Indicator rendering. |
|
|
164
|
+
| `labelPosition` | `'end' \| 'bottom'` | `'end'` | Label beside vs. below the indicator. |
|
|
165
|
+
| `errorMessage` | `string` | `''` | Error caption shown when `state === 'error'`.|
|
|
166
|
+
|
|
167
|
+
Indicator modes: `number` always renders `1, 2, 3`; `state` (default) renders a check icon for selected/reached steps and an alert icon for errors; `icon` renders the per-step `icon` name (provide icons via `@ng-icons/core` `provideIcons` in your app).
|
|
168
|
+
|
|
169
|
+
### `HlmStepperNext` / `HlmStepperPrevious` — `button[hlmStepperNext]` / `button[hlmStepperPrevious]`
|
|
170
|
+
|
|
171
|
+
No inputs/outputs. Thin extensions of `CdkStepperNext` / `CdkStepperPrevious` (sets `type` binding + `touch-action: manipulation`). Pair with `hlmBtn` for styling.
|
|
172
|
+
|
|
173
|
+
### Global config
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { provideHlmStepperConfig } from '@egose/shadcn-theme-ng/stepper';
|
|
177
|
+
|
|
178
|
+
@Component({
|
|
179
|
+
// ...
|
|
180
|
+
providers: [provideHlmStepperConfig({ animationEnabled: true, animationDuration: 300, defaultIndicatorMode: 'state' })],
|
|
181
|
+
})
|
|
182
|
+
export class FeatureComponent {}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Per-instance `animationsEnabled`, `animationDuration`, and `indicatorMode` inputs override the global defaults.
|
|
186
|
+
|
|
187
|
+
## Examples
|
|
188
|
+
|
|
189
|
+
### 1. Basic horizontal stepper
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { Component } from '@angular/core';
|
|
193
|
+
import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
|
|
194
|
+
import { HlmStepperImports } from '@egose/shadcn-theme-ng/stepper';
|
|
195
|
+
|
|
196
|
+
@Component({
|
|
197
|
+
selector: 'demo-basic',
|
|
198
|
+
standalone: true,
|
|
199
|
+
imports: [...HlmStepperImports, ...HlmButtonImports],
|
|
200
|
+
template: `
|
|
201
|
+
<hlm-stepper>
|
|
202
|
+
<hlm-step label="Step One">
|
|
203
|
+
<p>Content 1</p>
|
|
204
|
+
<button hlmBtn hlmStepperNext>Next</button>
|
|
205
|
+
</hlm-step>
|
|
206
|
+
<hlm-step label="Step Two">
|
|
207
|
+
<p>Content 2</p>
|
|
208
|
+
<button hlmBtn variant="outline" hlmStepperPrevious>Back</button>
|
|
209
|
+
<button hlmBtn hlmStepperNext>Next</button>
|
|
210
|
+
</hlm-step>
|
|
211
|
+
<hlm-step label="Step Three">
|
|
212
|
+
<p>Content 3</p>
|
|
213
|
+
<button hlmBtn variant="outline" hlmStepperPrevious>Back</button>
|
|
214
|
+
<button hlmBtn>Finish</button>
|
|
215
|
+
</hlm-step>
|
|
216
|
+
</hlm-stepper>
|
|
217
|
+
`,
|
|
218
|
+
})
|
|
219
|
+
export class DemoBasic {}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 2. Vertical orientation
|
|
223
|
+
|
|
224
|
+
```html
|
|
225
|
+
<hlm-stepper orientation="vertical">
|
|
226
|
+
<hlm-step label="Campaign"><!-- … --></hlm-step>
|
|
227
|
+
<hlm-step label="Audience"><!-- … --></hlm-step>
|
|
228
|
+
<hlm-step label="Review"><!-- … --></hlm-step>
|
|
229
|
+
</hlm-stepper>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Use vertical when content is dense or horizontal space is limited.
|
|
233
|
+
|
|
234
|
+
### 3. Responsive orientation via `BreakpointObserver`
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import { BreakpointObserver } from '@angular/cdk/layout';
|
|
238
|
+
import { Component, computed, inject } from '@angular/core';
|
|
239
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
240
|
+
import { map } from 'rxjs/operators';
|
|
241
|
+
|
|
242
|
+
@Component({ /* … */ })
|
|
243
|
+
export class DemoResponsive {
|
|
244
|
+
private readonly _breakpoints = inject(BreakpointObserver);
|
|
245
|
+
private readonly _isSmall = toSignal(
|
|
246
|
+
this._breakpoints.observe('(max-width: 767.98px)').pipe(map((s) => s.matches)),
|
|
247
|
+
{ initialValue: false },
|
|
248
|
+
);
|
|
249
|
+
protected readonly orientation = computed(() => (this._isSmall() ? 'vertical' : 'horizontal'));
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
```html
|
|
254
|
+
<hlm-stepper [orientation]="orientation()"><!-- … --></hlm-stepper>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### 4. Linear stepper with validation
|
|
258
|
+
|
|
259
|
+
With `linear`, the current step must be valid before advancing — `HlmStepper.next()` marks the step form touched first:
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { Component, inject } from '@angular/core';
|
|
263
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
264
|
+
|
|
265
|
+
@Component({ /* …, imports: [ReactiveFormsModule, ...HlmStepperImports, ...HlmButtonImports] */ })
|
|
266
|
+
export class DemoLinear {
|
|
267
|
+
private readonly _fb = inject(FormBuilder);
|
|
268
|
+
protected readonly identity = this._fb.group({ name: ['', Validators.required] });
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
```html
|
|
273
|
+
<hlm-stepper [linear]="true">
|
|
274
|
+
<hlm-step [stepControl]="identity" label="Identity">
|
|
275
|
+
<form [formGroup]="identity">
|
|
276
|
+
<input hlmInput formControlName="name" placeholder="Required before continuing" />
|
|
277
|
+
<button hlmBtn hlmStepperNext>Next</button>
|
|
278
|
+
</form>
|
|
279
|
+
</hlm-step>
|
|
280
|
+
<hlm-step label="Review"><!-- … --></hlm-step>
|
|
281
|
+
</hlm-stepper>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### 5. Error states
|
|
285
|
+
|
|
286
|
+
Enable `showError` through `STEPPER_GLOBAL_OPTIONS` to surface a step's error message in its header:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
import { STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper';
|
|
290
|
+
|
|
291
|
+
@Component({
|
|
292
|
+
providers: [{ provide: STEPPER_GLOBAL_OPTIONS, useValue: { showError: true } }],
|
|
293
|
+
/* … */
|
|
294
|
+
})
|
|
295
|
+
export class DemoError {}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
```html
|
|
299
|
+
<hlm-step
|
|
300
|
+
[stepControl]="contact"
|
|
301
|
+
[hasError]="contact.invalid && contact.touched"
|
|
302
|
+
errorMessage="Enter a valid work email before continuing."
|
|
303
|
+
label="Contact"
|
|
304
|
+
>
|
|
305
|
+
<!-- … -->
|
|
306
|
+
</hlm-step>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 6. Layout controls
|
|
310
|
+
|
|
311
|
+
```html
|
|
312
|
+
<hlm-stepper labelPosition="bottom" headerPosition="bottom"><!-- … --></hlm-stepper>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
`labelPosition` moves horizontal labels below the indicator; `headerPosition="bottom"` moves the header row below the content panel.
|
|
316
|
+
|
|
317
|
+
### 7. Animation controls
|
|
318
|
+
|
|
319
|
+
```html
|
|
320
|
+
<hlm-stepper [animationsEnabled]="true" [animationDuration]="300"><!-- … --></hlm-stepper>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Or set global defaults with `provideHlmStepperConfig` and override per instance.
|
|
324
|
+
|
|
325
|
+
### 8. Indicator modes
|
|
326
|
+
|
|
327
|
+
```html
|
|
328
|
+
<hlm-stepper indicatorMode="number"><!-- always 1, 2, 3 --></hlm-stepper>
|
|
329
|
+
<hlm-stepper indicatorMode="state"><!-- default: check / alert icons --></hlm-stepper>
|
|
330
|
+
<hlm-stepper indicatorMode="icon">
|
|
331
|
+
<hlm-step label="Profile" icon="lucideUser"><!-- … --></hlm-step>
|
|
332
|
+
</hlm-stepper>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Per-step `icon` names resolve through `@ng-icons/core` — register them with `provideIcons` in your app.
|
|
336
|
+
|
|
337
|
+
## Accessibility notes
|
|
338
|
+
|
|
339
|
+
- Horizontal headers get `role="tab"` semantics inside a `role="tablist"` (labelled `Progress` by default — override with `stepperAriaLabel` / `stepperAriaLabelledby`); panels get `role="tabpanel"` wired via `aria-labelledby` / `aria-controls`.
|
|
340
|
+
- Vertical headers use `role="button"` with `aria-expanded` / `aria-current="step"`.
|
|
341
|
+
- Keyboard: `HlmStepper` inherits CDK arrow-key navigation (`_onKeydown`). Headers are focusable via roving `tabIndex`.
|
|
342
|
+
- Disabled steps (`linear` + not navigable) get `aria-disabled` and `data-disabled` styling — explain in the label why a step is unavailable.
|
|
343
|
+
- In `linear` mode, failed validation marks fields touched so screen readers announce the errors.
|
|
344
|
+
|
|
345
|
+
## Theming / CSS variables
|
|
346
|
+
|
|
347
|
+
Headers follow `buttonVariants({ size: 'icon-sm' })` (`default` when selected/reached, `outline` otherwise, `destructive` on error) with `data-disabled` opacity handling. Connectors use `bg-primary` once reached, `bg-border` otherwise. Follows your shadcn theme automatically; extend via `class` (merged through `classes()` on headers) or the layout/indicator inputs.
|
|
348
|
+
|
|
349
|
+
## Related subpaths
|
|
350
|
+
|
|
351
|
+
- `@egose/shadcn-theme-ng/button` — `buttonVariants` powers the step indicators; `hlmBtn` styles the nav buttons.
|
|
352
|
+
- `@egose/shadcn-theme-ng/input` / `@egose/shadcn-theme-ng/label` / `@egose/shadcn-theme-ng/field` — form controls inside linear steps.
|
|
353
|
+
- `@egose/shadcn-theme-ng/card` — rich content panels.
|