@egose/shadcn-theme-ng-tw 0.0.28 → 0.2.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 +130 -13
- package/accordion/README.md +376 -34
- package/accordion/fesm2022/accordion.mjs +20 -20
- package/alert/README.md +343 -34
- package/alert/fesm2022/alert.mjs +18 -18
- package/alert-dialog/README.md +471 -5
- package/alert-dialog/fesm2022/alert-dialog.mjs +48 -48
- package/alert-dialog/types/alert-dialog.d.ts +1 -1
- package/aspect-ratio/README.md +272 -5
- package/aspect-ratio/fesm2022/aspect-ratio.mjs +9 -9
- package/autocomplete/README.md +473 -34
- package/autocomplete/fesm2022/autocomplete.mjs +54 -54
- package/autocomplete/types/autocomplete.d.ts +1 -1
- package/avatar/README.md +357 -5
- package/avatar/fesm2022/avatar.mjs +32 -32
- package/badge/README.md +289 -34
- package/badge/fesm2022/badge.mjs +4 -4
- package/basic-alert/README.md +324 -34
- package/basic-alert/fesm2022/basic-alert.mjs +4 -4
- package/basic-alert/types/basic-alert.d.ts +1 -1
- package/breadcrumb/README.md +406 -5
- package/breadcrumb/fesm2022/breadcrumb.mjs +34 -34
- package/button/README.md +453 -34
- package/button/fesm2022/button.mjs +96 -118
- package/button/types/button.d.ts +7 -10
- package/button-group/README.md +318 -5
- package/button-group/fesm2022/button-group.mjs +20 -20
- package/calendar/README.md +328 -34
- package/calendar/fesm2022/calendar.mjs +154 -154
- package/card/README.md +331 -5
- package/card/fesm2022/card.mjs +33 -33
- package/carousel/README.md +333 -5
- package/carousel/fesm2022/carousel.mjs +47 -44
- package/carousel/types/carousel.d.ts +1 -1
- package/checkbox/README.md +291 -34
- package/checkbox/fesm2022/checkbox.mjs +22 -20
- package/checkbox/types/checkbox.d.ts +5 -4
- package/collapsible/README.md +332 -5
- package/collapsible/fesm2022/collapsible.mjs +15 -15
- package/combobox/README.md +507 -5
- package/combobox/fesm2022/combobox.mjs +95 -92
- package/combobox/types/combobox.d.ts +1 -1
- package/command/README.md +435 -5
- package/command/fesm2022/command.mjs +56 -56
- package/confirmation-dialog/README.md +272 -34
- package/confirmation-dialog/fesm2022/confirmation-dialog.mjs +13 -12
- package/confirmation-dialog/types/confirmation-dialog.d.ts +9 -5
- package/context-menu/README.md +366 -5
- package/context-menu/fesm2022/context-menu.mjs +9 -9
- package/date-picker/README.md +436 -34
- package/date-picker/fesm2022/date-picker.mjs +79 -68
- package/date-picker/types/date-picker.d.ts +8 -4
- package/dialog/README.md +419 -34
- package/dialog/fesm2022/dialog.mjs +48 -48
- package/drawer/README.md +395 -5
- package/drawer/fesm2022/drawer.mjs +45 -45
- package/dropdown-menu/README.md +417 -5
- package/dropdown-menu/fesm2022/dropdown-menu.mjs +85 -73
- package/dropdown-menu/types/dropdown-menu.d.ts +4 -0
- package/empty/README.md +329 -5
- package/empty/fesm2022/empty.mjs +31 -31
- package/field/README.md +385 -5
- package/field/fesm2022/field.mjs +59 -59
- package/field/types/field.d.ts +1 -1
- package/form-checkbox/README.md +283 -34
- package/form-checkbox/fesm2022/form-checkbox.mjs +37 -19
- package/form-checkbox/types/form-checkbox.d.ts +7 -1
- package/form-date-picker/README.md +293 -34
- package/form-date-picker/fesm2022/form-date-picker.mjs +59 -24
- package/form-date-picker/types/form-date-picker.d.ts +7 -1
- package/form-field/README.md +327 -34
- package/form-field/fesm2022/form-field.mjs +30 -16
- package/form-field/types/form-field.d.ts +28 -20
- package/form-field-simple/README.md +311 -34
- package/form-field-simple/fesm2022/form-field-simple.mjs +4 -4
- package/form-searchable-multiselect/README.md +332 -34
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +41 -21
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +8 -2
- package/form-select/README.md +321 -34
- package/form-select/fesm2022/form-select.mjs +62 -29
- package/form-select/types/form-select.d.ts +7 -1
- package/form-text-input/README.md +342 -34
- package/form-text-input/fesm2022/form-text-input.mjs +43 -55
- package/form-text-input/types/form-text-input.d.ts +9 -12
- package/form-textarea/README.md +318 -34
- package/form-textarea/fesm2022/form-textarea.mjs +42 -23
- package/form-textarea/types/form-textarea.d.ts +8 -1
- package/hover-card/README.md +256 -5
- package/hover-card/fesm2022/hover-card.mjs +19 -19
- package/icon/README.md +210 -34
- package/icon/fesm2022/icon.mjs +8 -8
- package/input/README.md +240 -34
- package/input/fesm2022/input.mjs +17 -12
- package/input/types/input.d.ts +2 -1
- package/input-group/README.md +335 -5
- package/input-group/fesm2022/input-group.mjs +37 -37
- package/input-group/types/input-group.d.ts +3 -3
- package/input-otp/README.md +375 -5
- package/input-otp/fesm2022/input-otp.mjs +27 -27
- package/item/README.md +385 -5
- package/item/fesm2022/item.mjs +54 -54
- package/kbd/README.md +291 -5
- package/kbd/fesm2022/kbd.mjs +13 -13
- package/label/README.md +243 -34
- package/label/fesm2022/label.mjs +9 -9
- package/label/types/label.d.ts +1 -1
- package/layout-simple/README.md +174 -44
- package/layout-simple/fesm2022/layout-simple.mjs +917 -398
- package/layout-simple/types/layout-simple.d.ts +180 -129
- package/menu/README.md +418 -0
- package/menu/fesm2022/menu.mjs +461 -0
- package/menu/package.json +24 -0
- package/menu/types/menu.d.ts +137 -0
- package/menubar/README.md +343 -5
- package/menubar/fesm2022/menubar.mjs +13 -13
- package/native-select/README.md +323 -5
- package/native-select/fesm2022/native-select.mjs +19 -19
- package/navigation-menu/README.md +369 -5
- package/navigation-menu/fesm2022/navigation-menu.mjs +37 -37
- package/package.json +183 -154
- package/pagination/README.md +388 -5
- package/pagination/fesm2022/pagination.mjs +63 -63
- package/popover/README.md +302 -34
- package/popover/fesm2022/popover.mjs +30 -30
- package/progress/README.md +311 -5
- package/progress/fesm2022/progress.mjs +13 -13
- package/radio-group/README.md +335 -34
- package/radio-group/fesm2022/radio-group.mjs +26 -19
- package/resizable/README.md +269 -5
- package/resizable/fesm2022/resizable.mjs +19 -19
- package/scroll-area/README.md +233 -5
- package/scroll-area/fesm2022/scroll-area.mjs +9 -9
- package/searchable-multiselect/README.md +294 -34
- package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +63 -16
- package/searchable-multiselect/types/searchable-multiselect.d.ts +8 -2
- package/select/README.md +408 -34
- package/select/fesm2022/select.mjs +80 -72
- package/select/types/select.d.ts +3 -1
- package/separator/README.md +193 -34
- package/separator/fesm2022/separator.mjs +9 -9
- package/separator/types/separator.d.ts +1 -1
- package/sheet/README.md +282 -34
- package/sheet/fesm2022/sheet.mjs +30 -30
- package/sidebar/README.md +457 -5
- package/sidebar/fesm2022/sidebar.mjs +136 -134
- package/sidebar/types/sidebar.d.ts +3 -3
- package/skeleton/README.md +217 -5
- package/skeleton/fesm2022/skeleton.mjs +9 -9
- package/slider/README.md +273 -5
- package/slider/fesm2022/slider.mjs +29 -25
- package/sonner/README.md +317 -34
- package/sonner/fesm2022/sonner.mjs +9 -9
- package/spinner/README.md +255 -34
- package/spinner/fesm2022/spinner.mjs +8 -8
- package/switch/README.md +281 -34
- package/switch/fesm2022/switch.mjs +12 -14
- package/table/README.md +423 -5
- package/table/fesm2022/table.mjs +41 -41
- package/tabs/README.md +382 -34
- package/tabs/fesm2022/tabs.mjs +48 -38
- package/textarea/README.md +282 -5
- package/textarea/fesm2022/textarea.mjs +9 -9
- package/toggle/README.md +270 -5
- package/toggle/fesm2022/toggle.mjs +14 -14
- package/toggle-group/README.md +340 -5
- package/toggle-group/fesm2022/toggle-group.mjs +13 -13
- package/toggle-group/types/toggle-group.d.ts +13 -7
- package/tooltip/README.md +240 -34
- package/tooltip/fesm2022/tooltip.mjs +14 -14
- package/typography/README.md +271 -5
- package/typography/fesm2022/typography.mjs +53 -53
- package/typography/types/typography.d.ts +12 -12
- package/utils/README.md +274 -34
- package/utils/fesm2022/utils.mjs +68 -44
- package/accordion/fesm2022/accordion.mjs.map +0 -1
- package/alert/fesm2022/alert.mjs.map +0 -1
- package/alert-dialog/fesm2022/alert-dialog.mjs.map +0 -1
- package/aspect-ratio/fesm2022/aspect-ratio.mjs.map +0 -1
- package/autocomplete/fesm2022/autocomplete.mjs.map +0 -1
- package/avatar/fesm2022/avatar.mjs.map +0 -1
- package/badge/fesm2022/badge.mjs.map +0 -1
- package/basic-alert/fesm2022/basic-alert.mjs.map +0 -1
- package/breadcrumb/fesm2022/breadcrumb.mjs.map +0 -1
- package/button/fesm2022/button.mjs.map +0 -1
- package/button-group/fesm2022/button-group.mjs.map +0 -1
- package/calendar/fesm2022/calendar.mjs.map +0 -1
- package/card/fesm2022/card.mjs.map +0 -1
- package/carousel/fesm2022/carousel.mjs.map +0 -1
- package/checkbox/fesm2022/checkbox.mjs.map +0 -1
- package/collapsible/fesm2022/collapsible.mjs.map +0 -1
- package/combobox/fesm2022/combobox.mjs.map +0 -1
- package/command/fesm2022/command.mjs.map +0 -1
- package/confirmation-dialog/fesm2022/confirmation-dialog.mjs.map +0 -1
- package/context-menu/fesm2022/context-menu.mjs.map +0 -1
- package/date-picker/fesm2022/date-picker.mjs.map +0 -1
- package/dialog/fesm2022/dialog.mjs.map +0 -1
- package/drawer/fesm2022/drawer.mjs.map +0 -1
- package/dropdown-menu/fesm2022/dropdown-menu.mjs.map +0 -1
- package/empty/fesm2022/empty.mjs.map +0 -1
- package/field/fesm2022/field.mjs.map +0 -1
- package/form-checkbox/fesm2022/form-checkbox.mjs.map +0 -1
- package/form-date-picker/fesm2022/form-date-picker.mjs.map +0 -1
- package/form-field/fesm2022/form-field.mjs.map +0 -1
- package/form-field-simple/fesm2022/form-field-simple.mjs.map +0 -1
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs.map +0 -1
- package/form-select/fesm2022/form-select.mjs.map +0 -1
- package/form-text-input/fesm2022/form-text-input.mjs.map +0 -1
- package/form-textarea/fesm2022/form-textarea.mjs.map +0 -1
- package/hover-card/fesm2022/hover-card.mjs.map +0 -1
- package/icon/fesm2022/icon.mjs.map +0 -1
- package/input/fesm2022/input.mjs.map +0 -1
- package/input-group/fesm2022/input-group.mjs.map +0 -1
- package/input-otp/fesm2022/input-otp.mjs.map +0 -1
- package/item/fesm2022/item.mjs.map +0 -1
- package/kbd/fesm2022/kbd.mjs.map +0 -1
- package/label/fesm2022/label.mjs.map +0 -1
- package/layout-simple/fesm2022/layout-simple.mjs.map +0 -1
- package/menubar/fesm2022/menubar.mjs.map +0 -1
- package/native-select/fesm2022/native-select.mjs.map +0 -1
- package/navigation-menu/fesm2022/navigation-menu.mjs.map +0 -1
- package/pagination/fesm2022/pagination.mjs.map +0 -1
- package/popover/fesm2022/popover.mjs.map +0 -1
- package/progress/fesm2022/progress.mjs.map +0 -1
- package/radio-group/fesm2022/radio-group.mjs.map +0 -1
- package/resizable/fesm2022/resizable.mjs.map +0 -1
- package/scroll-area/fesm2022/scroll-area.mjs.map +0 -1
- package/searchable-multiselect/fesm2022/searchable-multiselect.mjs.map +0 -1
- package/select/fesm2022/select.mjs.map +0 -1
- package/separator/fesm2022/separator.mjs.map +0 -1
- package/sheet/fesm2022/sheet.mjs.map +0 -1
- package/sidebar/fesm2022/sidebar.mjs.map +0 -1
- package/skeleton/fesm2022/skeleton.mjs.map +0 -1
- package/slider/fesm2022/slider.mjs.map +0 -1
- package/sonner/fesm2022/sonner.mjs.map +0 -1
- package/spinner/fesm2022/spinner.mjs.map +0 -1
- package/switch/fesm2022/switch.mjs.map +0 -1
- package/table/fesm2022/table.mjs.map +0 -1
- package/tabs/fesm2022/tabs.mjs.map +0 -1
- package/textarea/fesm2022/textarea.mjs.map +0 -1
- package/toggle/fesm2022/toggle.mjs.map +0 -1
- package/toggle-group/fesm2022/toggle-group.mjs.map +0 -1
- package/tooltip/fesm2022/tooltip.mjs.map +0 -1
- package/typography/fesm2022/typography.mjs.map +0 -1
- package/utils/fesm2022/utils.mjs.map +0 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, computed, Component } from '@angular/core';
|
|
2
|
+
import { inject, input, computed, Component } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
|
-
import {
|
|
5
|
-
import { HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
|
|
4
|
+
import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
|
|
5
|
+
import { HlmFormIdGenerator, 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 { HlmDatePicker, HlmDatePickerInput } from '@egose/shadcn-theme-ng-tw/date-picker';
|
|
8
8
|
import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
|
|
9
9
|
|
|
10
|
-
let nextId = 0;
|
|
11
10
|
class EgFormDatePicker {
|
|
11
|
+
formGroupDirective = inject(FormGroupDirective);
|
|
12
|
+
generatedId = inject(HlmFormIdGenerator).generate('eg-form-date-picker');
|
|
12
13
|
// Inputs
|
|
13
14
|
label = input(undefined, /* @ts-ignore */
|
|
14
15
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
@@ -21,7 +22,7 @@ class EgFormDatePicker {
|
|
|
21
22
|
hint = input(undefined, /* @ts-ignore */
|
|
22
23
|
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
23
24
|
// HTML attributes
|
|
24
|
-
id = input(
|
|
25
|
+
id = input(undefined, /* @ts-ignore */
|
|
25
26
|
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
26
27
|
name = input(undefined, /* @ts-ignore */
|
|
27
28
|
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
@@ -39,6 +40,20 @@ class EgFormDatePicker {
|
|
|
39
40
|
...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
|
|
40
41
|
autoCloseOnSelect = input(true, /* @ts-ignore */
|
|
41
42
|
...(ngDevMode ? [{ debugName: "autoCloseOnSelect" }] : /* istanbul ignore next */ []));
|
|
43
|
+
effectiveId = computed(() => this.controlId() || this.id() || this.generatedId, /* @ts-ignore */
|
|
44
|
+
...(ngDevMode ? [{ debugName: "effectiveId" }] : /* istanbul ignore next */ []));
|
|
45
|
+
errorId = computed(() => `${this.effectiveId()}-error`, /* @ts-ignore */
|
|
46
|
+
...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
|
|
47
|
+
hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
|
|
48
|
+
...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
|
|
49
|
+
describedBy() {
|
|
50
|
+
const control = this.formGroupDirective.form.get(this.controlName());
|
|
51
|
+
return this.error() && control?.invalid && (control.dirty || control.touched)
|
|
52
|
+
? this.errorId()
|
|
53
|
+
: this.hint()
|
|
54
|
+
? this.hintId()
|
|
55
|
+
: null;
|
|
56
|
+
}
|
|
42
57
|
// Styling
|
|
43
58
|
userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
|
|
44
59
|
labelClass = input('', /* @ts-ignore */
|
|
@@ -60,10 +75,9 @@ class EgFormDatePicker {
|
|
|
60
75
|
...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
|
|
61
76
|
$hintClass = computed(() => hlm('tw:mt-0', this.hintClass()), /* @ts-ignore */
|
|
62
77
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
63
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
64
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
78
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormDatePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
79
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormDatePicker, isStandalone: true, selector: "eg-form-date-picker", 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 }, 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 }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, autoCloseOnSelect: { classPropertyName: "autoCloseOnSelect", publicName: "autoCloseOnSelect", 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 }, pickerClass: { classPropertyName: "pickerClass", publicName: "pickerClass", 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: { classAttribute: "tw:w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
|
|
65
80
|
@let lbl = label();
|
|
66
|
-
@let cid = controlId();
|
|
67
81
|
@let cnm = controlName();
|
|
68
82
|
@let err = error();
|
|
69
83
|
@let hnt = hint();
|
|
@@ -71,33 +85,44 @@ class EgFormDatePicker {
|
|
|
71
85
|
|
|
72
86
|
<hlm-form-field>
|
|
73
87
|
@if (lbl) {
|
|
74
|
-
<
|
|
88
|
+
<label hlmLabel [for]="effectiveId()" [class]="$labelClass()"
|
|
75
89
|
>{{ lbl }}
|
|
76
90
|
@if (rqrd) {
|
|
77
91
|
<span class="tw:text-red-500">*</span>
|
|
78
92
|
}
|
|
79
|
-
</
|
|
93
|
+
</label>
|
|
80
94
|
}
|
|
81
95
|
|
|
82
|
-
<hlm-date-picker
|
|
83
|
-
|
|
96
|
+
<hlm-date-picker
|
|
97
|
+
[min]="min()"
|
|
98
|
+
[max]="max()"
|
|
99
|
+
[wrapperDisabled]="disabled()"
|
|
100
|
+
[formControlName]="cnm"
|
|
101
|
+
[class]="$pickerClass()"
|
|
102
|
+
>
|
|
103
|
+
<hlm-date-picker-input
|
|
104
|
+
[inputId]="effectiveId()"
|
|
105
|
+
[ariaLabel]="lbl"
|
|
106
|
+
[ariaDescribedby]="describedBy()"
|
|
107
|
+
[placeholder]="placeholder()"
|
|
108
|
+
/>
|
|
84
109
|
</hlm-date-picker>
|
|
85
110
|
|
|
86
111
|
@if (err) {
|
|
87
|
-
<hlm-error [class]="$errorClass()">
|
|
112
|
+
<hlm-error [id]="errorId()" [class]="$errorClass()">
|
|
88
113
|
{{ err }}
|
|
89
114
|
</hlm-error>
|
|
90
115
|
}
|
|
91
116
|
|
|
92
117
|
@if (hnt) {
|
|
93
|
-
<hlm-hint [class]="$hintClass()">
|
|
118
|
+
<hlm-hint [id]="hintId()" [class]="$hintClass()">
|
|
94
119
|
{{ hnt }}
|
|
95
120
|
</hlm-hint>
|
|
96
121
|
}
|
|
97
122
|
</hlm-form-field>
|
|
98
|
-
`, 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: HlmDatePicker, selector: "hlm-date-picker", inputs: ["captionLayout", "min", "max", "disabled", "date", "defaultFocusedDate", "autoCloseOnSelect", "formatDate", "transformDate"], outputs: ["dateChange"] }, { kind: "component", type: HlmDatePickerInput, selector: "hlm-date-picker-input", inputs: ["parseDate", "formatInputDate"] }] });
|
|
123
|
+
`, 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: HlmDatePicker, selector: "hlm-date-picker", inputs: ["captionLayout", "min", "max", "disabled", "wrapperDisabled", "date", "defaultFocusedDate", "autoCloseOnSelect", "formatDate", "transformDate"], outputs: ["dateChange"] }, { kind: "component", type: HlmDatePickerInput, selector: "hlm-date-picker-input", inputs: ["ariaLabel", "ariaDescribedby", "parseDate", "formatInputDate"] }] });
|
|
99
124
|
}
|
|
100
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
125
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormDatePicker, decorators: [{
|
|
101
126
|
type: Component,
|
|
102
127
|
args: [{
|
|
103
128
|
selector: 'eg-form-date-picker',
|
|
@@ -109,7 +134,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
109
134
|
providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
|
|
110
135
|
template: `
|
|
111
136
|
@let lbl = label();
|
|
112
|
-
@let cid = controlId();
|
|
113
137
|
@let cnm = controlName();
|
|
114
138
|
@let err = error();
|
|
115
139
|
@let hnt = hint();
|
|
@@ -117,26 +141,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
117
141
|
|
|
118
142
|
<hlm-form-field>
|
|
119
143
|
@if (lbl) {
|
|
120
|
-
<
|
|
144
|
+
<label hlmLabel [for]="effectiveId()" [class]="$labelClass()"
|
|
121
145
|
>{{ lbl }}
|
|
122
146
|
@if (rqrd) {
|
|
123
147
|
<span class="tw:text-red-500">*</span>
|
|
124
148
|
}
|
|
125
|
-
</
|
|
149
|
+
</label>
|
|
126
150
|
}
|
|
127
151
|
|
|
128
|
-
<hlm-date-picker
|
|
129
|
-
|
|
152
|
+
<hlm-date-picker
|
|
153
|
+
[min]="min()"
|
|
154
|
+
[max]="max()"
|
|
155
|
+
[wrapperDisabled]="disabled()"
|
|
156
|
+
[formControlName]="cnm"
|
|
157
|
+
[class]="$pickerClass()"
|
|
158
|
+
>
|
|
159
|
+
<hlm-date-picker-input
|
|
160
|
+
[inputId]="effectiveId()"
|
|
161
|
+
[ariaLabel]="lbl"
|
|
162
|
+
[ariaDescribedby]="describedBy()"
|
|
163
|
+
[placeholder]="placeholder()"
|
|
164
|
+
/>
|
|
130
165
|
</hlm-date-picker>
|
|
131
166
|
|
|
132
167
|
@if (err) {
|
|
133
|
-
<hlm-error [class]="$errorClass()">
|
|
168
|
+
<hlm-error [id]="errorId()" [class]="$errorClass()">
|
|
134
169
|
{{ err }}
|
|
135
170
|
</hlm-error>
|
|
136
171
|
}
|
|
137
172
|
|
|
138
173
|
@if (hnt) {
|
|
139
|
-
<hlm-hint [class]="$hintClass()">
|
|
174
|
+
<hlm-hint [id]="hintId()" [class]="$hintClass()">
|
|
140
175
|
{{ hnt }}
|
|
141
176
|
</hlm-hint>
|
|
142
177
|
}
|
|
@@ -154,4 +189,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
154
189
|
*/
|
|
155
190
|
|
|
156
191
|
export { EgFormDatePicker };
|
|
157
|
-
|
|
192
|
+
|
|
@@ -2,12 +2,14 @@ import * as _angular_core from '@angular/core';
|
|
|
2
2
|
import { ClassValue } from 'clsx';
|
|
3
3
|
|
|
4
4
|
declare class EgFormDatePicker {
|
|
5
|
+
private readonly formGroupDirective;
|
|
6
|
+
private readonly generatedId;
|
|
5
7
|
label: _angular_core.InputSignal<string | undefined>;
|
|
6
8
|
controlId: _angular_core.InputSignal<string | undefined>;
|
|
7
9
|
controlName: _angular_core.InputSignal<string>;
|
|
8
10
|
error: _angular_core.InputSignal<string | undefined>;
|
|
9
11
|
hint: _angular_core.InputSignal<string | undefined>;
|
|
10
|
-
id: _angular_core.InputSignal<string>;
|
|
12
|
+
id: _angular_core.InputSignal<string | undefined>;
|
|
11
13
|
name: _angular_core.InputSignal<string | undefined>;
|
|
12
14
|
placeholder: _angular_core.InputSignal<string>;
|
|
13
15
|
readonly: _angular_core.InputSignal<boolean>;
|
|
@@ -16,6 +18,10 @@ declare class EgFormDatePicker {
|
|
|
16
18
|
min: _angular_core.InputSignal<string | Date | null>;
|
|
17
19
|
max: _angular_core.InputSignal<string | Date | null>;
|
|
18
20
|
autoCloseOnSelect: _angular_core.InputSignal<boolean>;
|
|
21
|
+
readonly effectiveId: _angular_core.Signal<string>;
|
|
22
|
+
readonly errorId: _angular_core.Signal<string>;
|
|
23
|
+
readonly hintId: _angular_core.Signal<string>;
|
|
24
|
+
describedBy(): string | null;
|
|
19
25
|
userClass: _angular_core.InputSignal<ClassValue>;
|
|
20
26
|
labelClass: _angular_core.InputSignal<string>;
|
|
21
27
|
pickerClass: _angular_core.InputSignal<string>;
|
package/form-field/README.md
CHANGED
|
@@ -1,64 +1,357 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Form Field (`@egose/shadcn-theme-ng/form-field`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The classic labelled-field wrapper: `hlm-form-field` groups a brain field control with a label, shows `hlm-hint` by default, and swaps to `hlm-error` automatically when the control reports validation errors. Equivalent to shadcn/ui `FormField` + `FormMessage` composition.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The Angular implementation is a small composition layer over
|
|
6
|
+
[`BrnField` from `@spartan-ng/brain/field`](https://www.spartan-ng.com/): `HlmFormField` hosts
|
|
7
|
+
`BrnField`, reads the projected `BrnFieldControl` (`contentChild`) and `HlmError` children
|
|
8
|
+
(`contentChildren`), and projects either `<ng-content select="hlm-error">` or
|
|
9
|
+
`<ng-content select="hlm-hint">`. `HlmError` / `HlmHint` are pure styling directives;
|
|
10
|
+
`HlmFormIdGenerator` mints unique ids. Must live inside a `FormGroupDirective` (`[formGroup]`
|
|
11
|
+
parent) so `formControlName` and `ControlContainer` resolve.
|
|
6
12
|
|
|
7
|
-
|
|
13
|
+
> **Ships as:** `@egose/shadcn-theme-ng/form-field` and `@egose/shadcn-theme-ng-tw/form-field`
|
|
14
|
+
> (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
|
|
15
|
+
> emitted Tailwind class strings differ. See the [package README](../../README.md) for install
|
|
16
|
+
> steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
8
19
|
|
|
9
20
|
```bash
|
|
10
|
-
|
|
21
|
+
# Plain Tailwind (no prefix)
|
|
22
|
+
npm install @egose/shadcn-theme-ng
|
|
23
|
+
|
|
24
|
+
# tw:-prefixed Tailwind variant
|
|
25
|
+
npm install @egose/shadcn-theme-ng-tw
|
|
11
26
|
```
|
|
12
27
|
|
|
13
|
-
|
|
28
|
+
Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
|
|
29
|
+
[package README](../../README.md#peer-dependencies). This subpath has no extra runtime component
|
|
30
|
+
dependencies beyond `@egose/shadcn-theme-ng/utils` and `@spartan-ng/brain/field`.
|
|
14
31
|
|
|
15
|
-
|
|
16
|
-
|
|
32
|
+
## Imports
|
|
33
|
+
|
|
34
|
+
All symbols are exported from the subpath root (`projects/form-field/src/public-api.ts`):
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import {
|
|
38
|
+
HlmFormField,
|
|
39
|
+
HlmError,
|
|
40
|
+
HlmHint,
|
|
41
|
+
HlmFormIdGenerator,
|
|
42
|
+
HlmFormFieldImports,
|
|
43
|
+
HlmFormFieldModule,
|
|
44
|
+
} from '@egose/shadcn-theme-ng/form-field';
|
|
45
|
+
// tw variant: replace with '@egose/shadcn-theme-ng-tw/form-field'
|
|
17
46
|
```
|
|
18
47
|
|
|
19
|
-
|
|
48
|
+
Standalone component — spread the `*Imports` array:
|
|
20
49
|
|
|
21
|
-
|
|
50
|
+
```ts
|
|
51
|
+
import { Component } from '@angular/core';
|
|
52
|
+
import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
|
|
22
53
|
|
|
23
|
-
|
|
24
|
-
|
|
54
|
+
@Component({
|
|
55
|
+
selector: 'app-demo',
|
|
56
|
+
standalone: true,
|
|
57
|
+
imports: [...HlmFormFieldImports],
|
|
58
|
+
template: `...`,
|
|
59
|
+
})
|
|
60
|
+
export class DemoComponent {}
|
|
25
61
|
```
|
|
26
62
|
|
|
27
|
-
|
|
63
|
+
NgModule-based consumer — import the module:
|
|
28
64
|
|
|
29
|
-
|
|
65
|
+
```ts
|
|
66
|
+
import { NgModule } from '@angular/core';
|
|
67
|
+
import { HlmFormFieldModule } from '@egose/shadcn-theme-ng/form-field';
|
|
30
68
|
|
|
31
|
-
|
|
69
|
+
@NgModule({ imports: [HlmFormFieldModule] })
|
|
70
|
+
export class DemoModule {}
|
|
71
|
+
```
|
|
32
72
|
|
|
33
|
-
|
|
73
|
+
`ControlValueAccessor` behavior: `hlm-form-field` is not itself a form control — the projected
|
|
74
|
+
control (any `BrnFieldControl`: `hlm-input`, `hlm-checkbox`, `hlm-date-picker`, …) carries the
|
|
75
|
+
`formControlName` binding. The wrapper only observes its `errors()` signal to switch hint/error.
|
|
34
76
|
|
|
35
|
-
|
|
36
|
-
cd dist/form-field
|
|
37
|
-
```
|
|
77
|
+
## Anatomy / Structure
|
|
38
78
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
79
|
+
```html
|
|
80
|
+
<form [formGroup]="form">
|
|
81
|
+
<hlm-form-field>
|
|
82
|
+
<label hlmLabel for="email">Email</label>
|
|
83
|
+
<input hlmInput id="email" formControlName="email" placeholder="you@example.com" />
|
|
84
|
+
<hlm-hint>We'll never share your email.</hlm-hint>
|
|
85
|
+
<hlm-error>Email is required.</hlm-error>
|
|
86
|
+
</hlm-form-field>
|
|
87
|
+
</form>
|
|
88
|
+
```
|
|
43
89
|
|
|
44
|
-
|
|
90
|
+
Real selectors (from source):
|
|
45
91
|
|
|
46
|
-
|
|
92
|
+
| Class | Selector | Kind |
|
|
93
|
+
| -------------------- | ------------------------------- | ---------------------------- |
|
|
94
|
+
| `HlmFormField` | `hlm-form-field` | Component (hosts `BrnField`) |
|
|
95
|
+
| `HlmError` | `hlm-error` | Directive |
|
|
96
|
+
| `HlmHint` | `hlm-hint` | Directive |
|
|
97
|
+
| `HlmFormIdGenerator` | (service, `providedIn: 'root'`) | Injectable |
|
|
47
98
|
|
|
48
|
-
|
|
49
|
-
|
|
99
|
+
## API reference
|
|
100
|
+
|
|
101
|
+
### `HlmFormField` — `hlm-form-field`
|
|
102
|
+
|
|
103
|
+
| Input | Type | Default | Description |
|
|
104
|
+
| ------- | ------------ | ------- | -------------------------------------------------- |
|
|
105
|
+
| `class` | `ClassValue` | `''` | Extra classes merged over `tw:block tw:space-y-2`. |
|
|
106
|
+
|
|
107
|
+
| Member | Description |
|
|
108
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
109
|
+
| `control` | `contentChild(BrnFieldControl)` — the projected field control; **throws `hlm-form-field must contain a BrnFieldControl` if absent.** |
|
|
110
|
+
| `errorChildren` | `contentChildren(HlmError)` — drives the hint/error switch. |
|
|
111
|
+
| `_hasDisplayedMessage` | `'error'` when an `hlm-error` child exists and `control.errors()` is non-empty, else `'hint'` (protected, governs projection). |
|
|
112
|
+
|
|
113
|
+
Template projects default content always, then either `hlm-error` (error case) or `hlm-hint`.
|
|
114
|
+
|
|
115
|
+
### `HlmError` — `hlm-error` / `HlmHint` — `hlm-hint`
|
|
116
|
+
|
|
117
|
+
Style-only directives (`text-destructive` / `text-muted-foreground`, block, `text-sm`).
|
|
118
|
+
|
|
119
|
+
| Input | Type | Default | Description |
|
|
120
|
+
| ------- | ------------ | ------- | -------------- |
|
|
121
|
+
| `class` | `ClassValue` | `''` | Extra classes. |
|
|
122
|
+
|
|
123
|
+
### `HlmFormIdGenerator`
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
@Injectable({ providedIn: 'root' })
|
|
127
|
+
export class HlmFormIdGenerator {
|
|
128
|
+
generate(prefix: string): string; // `${prefix}-${appId}-${nextId++}`
|
|
129
|
+
}
|
|
50
130
|
```
|
|
51
131
|
|
|
52
|
-
|
|
132
|
+
Inject and call `generate('my-field')` for stable label/control ids (used by the `eg-form-*` wrappers).
|
|
53
133
|
|
|
54
|
-
|
|
134
|
+
## Examples
|
|
55
135
|
|
|
56
|
-
|
|
57
|
-
|
|
136
|
+
### 1. Text input with hint → error swap
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { Component, inject } from '@angular/core';
|
|
140
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
141
|
+
import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
|
|
142
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
143
|
+
import { HlmInput } from '@egose/shadcn-theme-ng/input';
|
|
144
|
+
|
|
145
|
+
@Component({
|
|
146
|
+
selector: 'app-text-field',
|
|
147
|
+
standalone: true,
|
|
148
|
+
imports: [ReactiveFormsModule, HlmLabel, HlmInput, ...HlmFormFieldImports],
|
|
149
|
+
template: `
|
|
150
|
+
<form [formGroup]="form" (ngSubmit)="save()">
|
|
151
|
+
<hlm-form-field>
|
|
152
|
+
<label hlmLabel for="username">Username</label>
|
|
153
|
+
<input hlmInput id="username" formControlName="username" placeholder="ada" />
|
|
154
|
+
<hlm-hint>Letters and numbers only.</hlm-hint>
|
|
155
|
+
<hlm-error>Username is required.</hlm-error>
|
|
156
|
+
</hlm-form-field>
|
|
157
|
+
<button type="submit" [disabled]="form.invalid">Save</button>
|
|
158
|
+
</form>
|
|
159
|
+
`,
|
|
160
|
+
})
|
|
161
|
+
export class TextFieldComponent {
|
|
162
|
+
private readonly fb = inject(FormBuilder);
|
|
163
|
+
readonly form = this.fb.group({ username: ['', Validators.required] });
|
|
164
|
+
save() {
|
|
165
|
+
console.log(this.form.value);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 2. Textarea with generated ids
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { Component, inject } from '@angular/core';
|
|
174
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
175
|
+
import { HlmFormFieldImports, HlmFormIdGenerator } from '@egose/shadcn-theme-ng/form-field';
|
|
176
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
177
|
+
import { HlmTextarea } from '@egose/shadcn-theme-ng/textarea';
|
|
178
|
+
|
|
179
|
+
@Component({
|
|
180
|
+
selector: 'app-bio-field',
|
|
181
|
+
standalone: true,
|
|
182
|
+
imports: [ReactiveFormsModule, HlmLabel, HlmTextarea, ...HlmFormFieldImports],
|
|
183
|
+
template: `
|
|
184
|
+
<form [formGroup]="form">
|
|
185
|
+
<hlm-form-field>
|
|
186
|
+
<label hlmLabel [for]="bioId">Bio</label>
|
|
187
|
+
<textarea hlmTextarea [id]="bioId" formControlName="bio" rows="3"></textarea>
|
|
188
|
+
<hlm-hint>Tell us about yourself in 160 characters.</hlm-hint>
|
|
189
|
+
<hlm-error>Bio is required.</hlm-error>
|
|
190
|
+
</hlm-form-field>
|
|
191
|
+
</form>
|
|
192
|
+
`,
|
|
193
|
+
})
|
|
194
|
+
export class BioFieldComponent {
|
|
195
|
+
private readonly fb = inject(FormBuilder);
|
|
196
|
+
private readonly ids = inject(HlmFormIdGenerator);
|
|
197
|
+
readonly bioId = this.ids.generate('bio');
|
|
198
|
+
readonly form = this.fb.group({ bio: ['', Validators.required] });
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 3. Select field
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { Component, inject } from '@angular/core';
|
|
206
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
207
|
+
import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
|
|
208
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
209
|
+
import { HlmSelectImports } from '@egose/shadcn-theme-ng/select';
|
|
210
|
+
|
|
211
|
+
@Component({
|
|
212
|
+
selector: 'app-role-field',
|
|
213
|
+
standalone: true,
|
|
214
|
+
imports: [ReactiveFormsModule, HlmLabel, ...HlmFormFieldImports, ...HlmSelectImports],
|
|
215
|
+
template: `
|
|
216
|
+
<form [formGroup]="form">
|
|
217
|
+
<hlm-form-field>
|
|
218
|
+
<label hlmLabel for="role">Role</label>
|
|
219
|
+
<hlm-select formControlName="role" id="role" placeholder="Select a role">
|
|
220
|
+
<hlm-select-content>
|
|
221
|
+
<hlm-select-item value="admin">Admin</hlm-select-item>
|
|
222
|
+
<hlm-select-item value="editor">Editor</hlm-select-item>
|
|
223
|
+
</hlm-select-content>
|
|
224
|
+
</hlm-select>
|
|
225
|
+
<hlm-hint>Controls what they can access.</hlm-hint>
|
|
226
|
+
<hlm-error>Pick a role.</hlm-error>
|
|
227
|
+
</hlm-form-field>
|
|
228
|
+
</form>
|
|
229
|
+
`,
|
|
230
|
+
})
|
|
231
|
+
export class RoleFieldComponent {
|
|
232
|
+
private readonly fb = inject(FormBuilder);
|
|
233
|
+
readonly form = this.fb.group({ role: ['', Validators.required] });
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 4. Date picker field
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { Component, inject } from '@angular/core';
|
|
241
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
242
|
+
import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
|
|
243
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
244
|
+
import { HlmDatePickerImports } from '@egose/shadcn-theme-ng/date-picker';
|
|
245
|
+
|
|
246
|
+
@Component({
|
|
247
|
+
selector: 'app-date-field',
|
|
248
|
+
standalone: true,
|
|
249
|
+
imports: [ReactiveFormsModule, HlmLabel, ...HlmFormFieldImports, ...HlmDatePickerImports],
|
|
250
|
+
template: `
|
|
251
|
+
<form [formGroup]="form">
|
|
252
|
+
<hlm-form-field>
|
|
253
|
+
<label hlmLabel for="start">Start date</label>
|
|
254
|
+
<hlm-date-picker formControlName="start">
|
|
255
|
+
<hlm-date-picker-input inputId="start" placeholder="Pick a date" />
|
|
256
|
+
</hlm-date-picker>
|
|
257
|
+
<hlm-hint>First day of the subscription.</hlm-hint>
|
|
258
|
+
<hlm-error>Start date is required.</hlm-error>
|
|
259
|
+
</hlm-form-field>
|
|
260
|
+
</form>
|
|
261
|
+
`,
|
|
262
|
+
})
|
|
263
|
+
export class DateFieldComponent {
|
|
264
|
+
private readonly fb = inject(FormBuilder);
|
|
265
|
+
readonly form = this.fb.group({ start: [null as Date | null, Validators.required] });
|
|
266
|
+
}
|
|
58
267
|
```
|
|
59
268
|
|
|
60
|
-
|
|
269
|
+
### 5. Multiple errors (first-match display)
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
import { Component, inject } from '@angular/core';
|
|
273
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
274
|
+
import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
|
|
275
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
276
|
+
import { HlmInput } from '@egose/shadcn-theme-ng/input';
|
|
277
|
+
|
|
278
|
+
@Component({
|
|
279
|
+
selector: 'app-multi-error',
|
|
280
|
+
standalone: true,
|
|
281
|
+
imports: [ReactiveFormsModule, HlmLabel, HlmInput, ...HlmFormFieldImports],
|
|
282
|
+
template: `
|
|
283
|
+
<form [formGroup]="form" (ngSubmit)="submit()">
|
|
284
|
+
<hlm-form-field>
|
|
285
|
+
<label hlmLabel for="email">Email</label>
|
|
286
|
+
<input hlmInput id="email" type="email" formControlName="email" />
|
|
287
|
+
<hlm-hint>We'll send the receipt here.</hlm-hint>
|
|
288
|
+
<!-- All hlm-error children project together while any error exists. -->
|
|
289
|
+
<hlm-error>Email is required.</hlm-error>
|
|
290
|
+
<hlm-error>Enter a valid email address.</hlm-error>
|
|
291
|
+
</hlm-form-field>
|
|
292
|
+
<button type="submit" (click)="form.markAllAsTouched()">Submit</button>
|
|
293
|
+
</form>
|
|
294
|
+
`,
|
|
295
|
+
})
|
|
296
|
+
export class MultiErrorComponent {
|
|
297
|
+
private readonly fb = inject(FormBuilder);
|
|
298
|
+
readonly form = this.fb.group({ email: ['', [Validators.required, Validators.email]] });
|
|
299
|
+
submit() {
|
|
300
|
+
this.form.markAllAsTouched();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### 6. Checkbox row + submit-gated validation
|
|
306
|
+
|
|
307
|
+
```ts
|
|
308
|
+
import { Component, inject } from '@angular/core';
|
|
309
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
310
|
+
import { HlmFormFieldImports } from '@egose/shadcn-theme-ng/form-field';
|
|
311
|
+
import { HlmCheckbox } from '@egose/shadcn-theme-ng/checkbox';
|
|
312
|
+
import { HlmLabel } from '@egose/shadcn-theme-ng/label';
|
|
313
|
+
|
|
314
|
+
@Component({
|
|
315
|
+
selector: 'app-check-field',
|
|
316
|
+
standalone: true,
|
|
317
|
+
imports: [ReactiveFormsModule, HlmCheckbox, HlmLabel, ...HlmFormFieldImports],
|
|
318
|
+
template: `
|
|
319
|
+
<form [formGroup]="form" (ngSubmit)="submit()">
|
|
320
|
+
<hlm-form-field class="tw:space-y-1">
|
|
321
|
+
<div class="tw:flex tw:items-center tw:gap-2">
|
|
322
|
+
<hlm-checkbox id="consent" formControlName="consent" />
|
|
323
|
+
<label hlmLabel for="consent">I agree to the terms</label>
|
|
324
|
+
</div>
|
|
325
|
+
<hlm-error>You must agree before continuing.</hlm-error>
|
|
326
|
+
</hlm-form-field>
|
|
327
|
+
<button type="submit">Continue</button>
|
|
328
|
+
</form>
|
|
329
|
+
`,
|
|
330
|
+
})
|
|
331
|
+
export class CheckFieldComponent {
|
|
332
|
+
private readonly fb = inject(FormBuilder);
|
|
333
|
+
readonly form = this.fb.group({ consent: [false, Validators.requiredTrue] });
|
|
334
|
+
submit() {
|
|
335
|
+
this.form.markAllAsTouched();
|
|
336
|
+
if (this.form.valid) console.log('ok');
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Accessibility notes
|
|
342
|
+
|
|
343
|
+
- The wrapper itself adds no label — always include a `<label hlmLabel>` (or `aria-label` on the control) so the field has an accessible name.
|
|
344
|
+
- Hint and error are mutually exclusive in the DOM (only one projects at a time), keeping `aria-describedby` targets unambiguous.
|
|
345
|
+
- For custom controls, implement `BrnFieldControl` so `errors()` and described-by wiring flow; otherwise the wrapper throws at runtime.
|
|
346
|
+
- Mark forms for submit-gated validation with `markAllAsTouched()` so errors appear on submit, not just on blur.
|
|
347
|
+
|
|
348
|
+
## Theming / CSS variables
|
|
349
|
+
|
|
350
|
+
No component-specific CSS variables. The wrapper is `tw:block tw:space-y-2`; errors are `text-destructive`, hints `text-muted-foreground`. Extend via `class` on each element.
|
|
61
351
|
|
|
62
|
-
##
|
|
352
|
+
## Related subpaths
|
|
63
353
|
|
|
64
|
-
|
|
354
|
+
- `@egose/shadcn-theme-ng/form-field-simple` — lighter `eg-form-field` alternative with touched/dirty-gated errors.
|
|
355
|
+
- `@egose/shadcn-theme-ng/field` — full shadcn Field layout system (groups, legends, separators).
|
|
356
|
+
- `@egose/shadcn-theme-ng/label` — `HlmLabel` used for field labels.
|
|
357
|
+
- `@egose/shadcn-theme-ng/form-checkbox`, `.../form-date-picker`, `.../form-text-input` — ready-made wrappers built on these primitives.
|