ng-primitives 0.92.0 → 0.94.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/a11y/index.d.ts +5 -1
- package/accordion/index.d.ts +30 -1
- package/autofill/index.d.ts +6 -0
- package/avatar/index.d.ts +17 -2
- package/breadcrumbs/index.d.ts +35 -7
- package/button/index.d.ts +24 -4
- package/checkbox/index.d.ts +17 -1
- package/fesm2022/ng-primitives-accordion.mjs +24 -0
- package/fesm2022/ng-primitives-accordion.mjs.map +1 -1
- package/fesm2022/ng-primitives-button.mjs +18 -4
- package/fesm2022/ng-primitives-button.mjs.map +1 -1
- package/fesm2022/ng-primitives-checkbox.mjs +25 -8
- package/fesm2022/ng-primitives-checkbox.mjs.map +1 -1
- package/fesm2022/ng-primitives-file-upload.mjs +39 -38
- package/fesm2022/ng-primitives-file-upload.mjs.map +1 -1
- package/fesm2022/ng-primitives-focus-trap.mjs +107 -138
- package/fesm2022/ng-primitives-focus-trap.mjs.map +1 -1
- package/fesm2022/ng-primitives-form-field.mjs +323 -365
- package/fesm2022/ng-primitives-form-field.mjs.map +1 -1
- package/fesm2022/ng-primitives-input.mjs +10 -2
- package/fesm2022/ng-primitives-input.mjs.map +1 -1
- package/fesm2022/ng-primitives-interactions.mjs +57 -58
- package/fesm2022/ng-primitives-interactions.mjs.map +1 -1
- package/fesm2022/ng-primitives-listbox.mjs +2 -2
- package/fesm2022/ng-primitives-listbox.mjs.map +1 -1
- package/fesm2022/ng-primitives-menu.mjs +313 -352
- package/fesm2022/ng-primitives-menu.mjs.map +1 -1
- package/fesm2022/ng-primitives-roving-focus.mjs +1 -1
- package/fesm2022/ng-primitives-roving-focus.mjs.map +1 -1
- package/fesm2022/ng-primitives-separator.mjs +15 -7
- package/fesm2022/ng-primitives-separator.mjs.map +1 -1
- package/fesm2022/ng-primitives-slider.mjs +26 -9
- package/fesm2022/ng-primitives-slider.mjs.map +1 -1
- package/fesm2022/ng-primitives-state.mjs +37 -8
- package/fesm2022/ng-primitives-state.mjs.map +1 -1
- package/fesm2022/ng-primitives-switch.mjs +16 -5
- package/fesm2022/ng-primitives-switch.mjs.map +1 -1
- package/fesm2022/ng-primitives-tabs.mjs +197 -189
- package/fesm2022/ng-primitives-tabs.mjs.map +1 -1
- package/fesm2022/ng-primitives-textarea.mjs +7 -0
- package/fesm2022/ng-primitives-textarea.mjs.map +1 -1
- package/fesm2022/ng-primitives-toggle-group.mjs +22 -5
- package/fesm2022/ng-primitives-toggle-group.mjs.map +1 -1
- package/fesm2022/ng-primitives-toggle.mjs +16 -5
- package/fesm2022/ng-primitives-toggle.mjs.map +1 -1
- package/fesm2022/ng-primitives-toolbar.mjs +2 -2
- package/fesm2022/ng-primitives-toolbar.mjs.map +1 -1
- package/fesm2022/ng-primitives-tooltip.mjs +4 -4
- package/fesm2022/ng-primitives-tooltip.mjs.map +1 -1
- package/fesm2022/ng-primitives-utils.mjs.map +1 -1
- package/file-upload/index.d.ts +30 -8
- package/focus-trap/index.d.ts +35 -75
- package/form-field/index.d.ts +328 -123
- package/input/index.d.ts +19 -0
- package/interactions/index.d.ts +16 -16
- package/menu/index.d.ts +247 -120
- package/package.json +1 -1
- package/roving-focus/index.d.ts +88 -12
- package/schematics/ng-generate/templates/tabs/tabs.__fileSuffix@dasherize__.ts.template +2 -2
- package/separator/index.d.ts +1 -0
- package/slider/index.d.ts +34 -6
- package/state/index.d.ts +31 -8
- package/switch/index.d.ts +20 -4
- package/tabs/index.d.ts +343 -82
- package/textarea/index.d.ts +13 -0
- package/toggle/index.d.ts +12 -3
- package/toggle-group/index.d.ts +22 -2
- package/toolbar/index.d.ts +7 -0
- package/utils/index.d.ts +1 -1
|
@@ -1,75 +1,253 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import { uniqueId, onBooleanChange, controlStatus
|
|
4
|
-
import { createStateToken, createStateProvider, createStateInjector, createState, attrBinding, dataBinding } from 'ng-primitives/state';
|
|
2
|
+
import { inject, Injector, signal, untracked, effect, input, Directive, computed, booleanAttribute, contentChild } from '@angular/core';
|
|
3
|
+
import { onChange, uniqueId, onBooleanChange, controlStatus } from 'ng-primitives/utils';
|
|
5
4
|
import { injectElementRef, explicitEffect } from 'ng-primitives/internal';
|
|
5
|
+
import { createPrimitive, dataBinding, onDestroy, attrBinding, listener } from 'ng-primitives/state';
|
|
6
6
|
import { NgControl } from '@angular/forms';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
8
|
+
const [NgpFormFieldStateToken, ngpFormField, injectFormFieldState, provideFormFieldState] = createPrimitive('NgpFormField', ({ ngControl }) => {
|
|
9
|
+
const element = injectElementRef();
|
|
10
|
+
const injector = inject(Injector);
|
|
11
|
+
// Store the form labels
|
|
12
|
+
const labels = signal([], ...(ngDevMode ? [{ debugName: "labels" }] : []));
|
|
13
|
+
// Store the form descriptions
|
|
14
|
+
const descriptions = signal([], ...(ngDevMode ? [{ debugName: "descriptions" }] : []));
|
|
15
|
+
// Store the id of the associated form control
|
|
16
|
+
const formControl = signal(null, ...(ngDevMode ? [{ debugName: "formControl" }] : []));
|
|
17
|
+
// Store the validation error messages
|
|
18
|
+
const errors = signal([], ...(ngDevMode ? [{ debugName: "errors" }] : []));
|
|
19
|
+
// Form control state signals
|
|
20
|
+
const pristine = signal(null, ...(ngDevMode ? [{ debugName: "pristine" }] : []));
|
|
21
|
+
const touched = signal(null, ...(ngDevMode ? [{ debugName: "touched" }] : []));
|
|
22
|
+
const dirty = signal(null, ...(ngDevMode ? [{ debugName: "dirty" }] : []));
|
|
23
|
+
const valid = signal(null, ...(ngDevMode ? [{ debugName: "valid" }] : []));
|
|
24
|
+
const invalid = signal(null, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
|
|
25
|
+
const pending = signal(null, ...(ngDevMode ? [{ debugName: "pending" }] : []));
|
|
26
|
+
const disabled = signal(null, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
27
|
+
// Store the current status subscription
|
|
28
|
+
let subscription;
|
|
29
|
+
// Host bindings
|
|
30
|
+
dataBinding(element, 'data-invalid', invalid);
|
|
31
|
+
dataBinding(element, 'data-valid', valid);
|
|
32
|
+
dataBinding(element, 'data-touched', touched);
|
|
33
|
+
dataBinding(element, 'data-pristine', pristine);
|
|
34
|
+
dataBinding(element, 'data-dirty', dirty);
|
|
35
|
+
dataBinding(element, 'data-pending', pending);
|
|
36
|
+
dataBinding(element, 'data-disabled', disabled);
|
|
37
|
+
function updateStatus() {
|
|
38
|
+
const control = ngControl();
|
|
39
|
+
if (!control) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Wrap in try-catch to handle signal-forms interop controls where
|
|
43
|
+
// the `field` input may not be available yet (throws NG0950).
|
|
44
|
+
// Reading the signal still establishes a dependency, so the effect
|
|
45
|
+
// will re-run when the input becomes available.
|
|
46
|
+
try {
|
|
47
|
+
const controlPristine = control.pristine;
|
|
48
|
+
const controlTouched = control.touched;
|
|
49
|
+
const controlDirty = control.dirty;
|
|
50
|
+
const controlValid = control.valid;
|
|
51
|
+
const controlInvalid = control.invalid;
|
|
52
|
+
const controlPending = control.pending;
|
|
53
|
+
const controlDisabled = control.disabled;
|
|
54
|
+
const controlErrors = control.errors;
|
|
55
|
+
untracked(() => {
|
|
56
|
+
pristine.set(controlPristine);
|
|
57
|
+
touched.set(controlTouched);
|
|
58
|
+
dirty.set(controlDirty);
|
|
59
|
+
valid.set(controlValid);
|
|
60
|
+
invalid.set(controlInvalid);
|
|
61
|
+
pending.set(controlPending);
|
|
62
|
+
disabled.set(controlDisabled);
|
|
63
|
+
errors.set(controlErrors ? Object.keys(controlErrors) : []);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// NG0950: Required input not available yet. The effect will re-run
|
|
68
|
+
// when the signal input becomes available.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function setupSubscriptions(control) {
|
|
72
|
+
// Unsubscribe from the previous subscriptions.
|
|
73
|
+
subscription?.unsubscribe();
|
|
74
|
+
if (!control) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// For signal-forms interop controls, use an effect to reactively track status.
|
|
78
|
+
// For classic controls, also use an effect but additionally subscribe to events.
|
|
79
|
+
effect(() => {
|
|
80
|
+
updateStatus();
|
|
81
|
+
}, { injector });
|
|
82
|
+
// Classic controls also have an events observable we can subscribe to.
|
|
83
|
+
const underlyingControl = control?.control;
|
|
84
|
+
if (underlyingControl?.events) {
|
|
85
|
+
subscription = underlyingControl.events.subscribe(() => updateStatus());
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Setup subscriptions when ngControl changes
|
|
89
|
+
onChange(ngControl, setupSubscriptions);
|
|
90
|
+
// Cleanup subscription on destroy
|
|
91
|
+
onDestroy(() => subscription?.unsubscribe());
|
|
92
|
+
// Methods
|
|
93
|
+
function setFormControl(id) {
|
|
94
|
+
formControl.set(id);
|
|
95
|
+
}
|
|
96
|
+
function addLabel(label) {
|
|
97
|
+
if (labels().includes(label)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
labels.update(currentLabels => [...currentLabels, label]);
|
|
101
|
+
}
|
|
102
|
+
function addDescription(description) {
|
|
103
|
+
if (descriptions().includes(description)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
descriptions.update(currentDescriptions => [...currentDescriptions, description]);
|
|
107
|
+
}
|
|
108
|
+
function removeFormControl() {
|
|
109
|
+
formControl.set(null);
|
|
110
|
+
}
|
|
111
|
+
function removeLabel(label) {
|
|
112
|
+
labels.update(currentLabels => currentLabels.filter(l => l !== label));
|
|
113
|
+
}
|
|
114
|
+
function removeDescription(description) {
|
|
115
|
+
descriptions.update(currentDescriptions => currentDescriptions.filter(d => d !== description));
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
labels,
|
|
119
|
+
descriptions,
|
|
120
|
+
formControl,
|
|
121
|
+
errors,
|
|
122
|
+
pristine,
|
|
123
|
+
touched,
|
|
124
|
+
dirty,
|
|
125
|
+
valid,
|
|
126
|
+
invalid,
|
|
127
|
+
pending,
|
|
128
|
+
disabled,
|
|
129
|
+
setFormControl,
|
|
130
|
+
addLabel,
|
|
131
|
+
addDescription,
|
|
132
|
+
removeFormControl,
|
|
133
|
+
removeLabel,
|
|
134
|
+
removeDescription,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const [NgpDescriptionStateToken, ngpDescription, injectDescriptionState, provideDescriptionState,] = createPrimitive('NgpDescription', ({ id = signal(uniqueId('ngp-description')) }) => {
|
|
139
|
+
const element = injectElementRef();
|
|
140
|
+
const formField = injectFormFieldState({ optional: true });
|
|
141
|
+
// Host bindings
|
|
142
|
+
attrBinding(element, 'id', id);
|
|
143
|
+
dataBinding(element, 'data-invalid', () => (formField()?.invalid() ? '' : null));
|
|
144
|
+
dataBinding(element, 'data-valid', () => (formField()?.valid() ? '' : null));
|
|
145
|
+
dataBinding(element, 'data-touched', () => (formField()?.touched() ? '' : null));
|
|
146
|
+
dataBinding(element, 'data-pristine', () => (formField()?.pristine() ? '' : null));
|
|
147
|
+
dataBinding(element, 'data-dirty', () => (formField()?.dirty() ? '' : null));
|
|
148
|
+
dataBinding(element, 'data-pending', () => (formField()?.pending() ? '' : null));
|
|
149
|
+
dataBinding(element, 'data-disabled', () => (formField()?.disabled() ? '' : null));
|
|
150
|
+
// Register with form field and cleanup on destroy
|
|
151
|
+
formField()?.addDescription(id());
|
|
152
|
+
onDestroy(() => formField()?.removeDescription(id()));
|
|
153
|
+
onChange(id, (newId, oldId) => {
|
|
154
|
+
if (oldId) {
|
|
155
|
+
formField()?.removeDescription(oldId);
|
|
156
|
+
}
|
|
157
|
+
formField()?.addDescription(newId);
|
|
158
|
+
});
|
|
159
|
+
return { id };
|
|
160
|
+
});
|
|
24
161
|
|
|
25
162
|
/**
|
|
26
163
|
* The `NgpDescription` directive is used to mark a description element within a form field. There may be multiple descriptions associated with a form control.
|
|
27
164
|
*/
|
|
28
165
|
class NgpDescription {
|
|
166
|
+
/**
|
|
167
|
+
* The description state.
|
|
168
|
+
*/
|
|
29
169
|
constructor() {
|
|
30
170
|
/**
|
|
31
171
|
* The id of the description. If not provided, a unique id will be generated.
|
|
32
172
|
*/
|
|
33
173
|
this.id = input(uniqueId('ngp-description'), ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
34
|
-
|
|
35
|
-
* Access the form field that the description is associated with.
|
|
36
|
-
*/
|
|
37
|
-
this.formField = injectFormFieldState({ optional: true });
|
|
38
|
-
effect(onCleanup => {
|
|
39
|
-
this.formField()?.addDescription(this.id());
|
|
40
|
-
onCleanup(() => this.formField()?.removeDescription(this.id()));
|
|
41
|
-
});
|
|
174
|
+
ngpDescription({ id: this.id });
|
|
42
175
|
}
|
|
43
176
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpDescription, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
44
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpDescription, isStandalone: true, selector: "[ngpDescription]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } },
|
|
177
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpDescription, isStandalone: true, selector: "[ngpDescription]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideDescriptionState()], exportAs: ["ngpDescription"], ngImport: i0 }); }
|
|
45
178
|
}
|
|
46
179
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpDescription, decorators: [{
|
|
47
180
|
type: Directive,
|
|
48
181
|
args: [{
|
|
49
182
|
selector: '[ngpDescription]',
|
|
50
183
|
exportAs: 'ngpDescription',
|
|
51
|
-
|
|
52
|
-
'[attr.id]': 'id()',
|
|
53
|
-
'[attr.data-invalid]': 'formField()?.invalid() ? "" : null',
|
|
54
|
-
'[attr.data-valid]': 'formField()?.valid() ? "" : null',
|
|
55
|
-
'[attr.data-touched]': 'formField()?.touched() ? "" : null',
|
|
56
|
-
'[attr.data-pristine]': 'formField()?.pristine() ? "" : null',
|
|
57
|
-
'[attr.data-dirty]': 'formField()?.dirty() ? "" : null',
|
|
58
|
-
'[attr.data-pending]': 'formField()?.pending() ? "" : null',
|
|
59
|
-
'[attr.data-disabled]': 'formField()?.disabled() ? "" : null',
|
|
60
|
-
},
|
|
184
|
+
providers: [provideDescriptionState()],
|
|
61
185
|
}]
|
|
62
186
|
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }] } });
|
|
63
187
|
|
|
188
|
+
const [NgpErrorStateToken, ngpError, injectErrorState, provideErrorState] = createPrimitive('NgpError', ({ id, validator = signal(null) }) => {
|
|
189
|
+
const element = injectElementRef();
|
|
190
|
+
const formField = injectFormFieldState({ optional: true });
|
|
191
|
+
// Determine if there is an error message
|
|
192
|
+
const hasError = computed(() => {
|
|
193
|
+
const errors = formField()?.errors() ?? [];
|
|
194
|
+
const validatorValue = validator();
|
|
195
|
+
return validatorValue ? errors?.includes(validatorValue) : errors?.length > 0;
|
|
196
|
+
}, ...(ngDevMode ? [{ debugName: "hasError" }] : []));
|
|
197
|
+
// Determine whether the validator associated with this error is failing
|
|
198
|
+
const state = computed(() => (hasError() ? 'fail' : 'pass'), ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
199
|
+
// Host bindings
|
|
200
|
+
attrBinding(element, 'id', id);
|
|
201
|
+
dataBinding(element, 'data-invalid', () => (formField()?.invalid() ? '' : null));
|
|
202
|
+
dataBinding(element, 'data-valid', () => (formField()?.valid() ? '' : null));
|
|
203
|
+
dataBinding(element, 'data-touched', () => (formField()?.touched() ? '' : null));
|
|
204
|
+
dataBinding(element, 'data-pristine', () => (formField()?.pristine() ? '' : null));
|
|
205
|
+
dataBinding(element, 'data-dirty', () => (formField()?.dirty() ? '' : null));
|
|
206
|
+
dataBinding(element, 'data-pending', () => (formField()?.pending() ? '' : null));
|
|
207
|
+
dataBinding(element, 'data-disabled', () => (formField()?.disabled() ? '' : null));
|
|
208
|
+
dataBinding(element, 'data-validator', state);
|
|
209
|
+
let currentId = id();
|
|
210
|
+
// Register/unregister with form field based on error state
|
|
211
|
+
function registerError() {
|
|
212
|
+
formField()?.addDescription(currentId);
|
|
213
|
+
}
|
|
214
|
+
function unregisterError() {
|
|
215
|
+
formField()?.removeDescription(currentId);
|
|
216
|
+
}
|
|
217
|
+
// Update error registration when hasError changes
|
|
218
|
+
onBooleanChange(hasError, registerError, unregisterError);
|
|
219
|
+
function updateIdRegistration(newId, oldId) {
|
|
220
|
+
if (oldId && hasError()) {
|
|
221
|
+
formField()?.removeDescription(oldId);
|
|
222
|
+
}
|
|
223
|
+
currentId = newId;
|
|
224
|
+
if (hasError()) {
|
|
225
|
+
formField()?.addDescription(newId);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Watch for id changes to update registration
|
|
229
|
+
explicitEffect([id], () => updateIdRegistration(id(), currentId));
|
|
230
|
+
// Cleanup on destroy
|
|
231
|
+
onDestroy(() => {
|
|
232
|
+
if (hasError()) {
|
|
233
|
+
formField()?.removeDescription(currentId);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
return {
|
|
237
|
+
id,
|
|
238
|
+
hasError,
|
|
239
|
+
state,
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
|
|
64
243
|
/**
|
|
65
244
|
* The `NgpError` directive is used to mark an error message element within a form field. There may be multiple error messages associated with a form control.
|
|
66
245
|
*/
|
|
67
246
|
class NgpError {
|
|
247
|
+
/**
|
|
248
|
+
* The error state.
|
|
249
|
+
*/
|
|
68
250
|
constructor() {
|
|
69
|
-
/**
|
|
70
|
-
* Access the form field that the description is associated with.
|
|
71
|
-
*/
|
|
72
|
-
this.formField = injectFormFieldState({ optional: true });
|
|
73
251
|
/**
|
|
74
252
|
* The id of the error message. If not provided, a unique id will be generated.
|
|
75
253
|
*/
|
|
@@ -80,67 +258,55 @@ class NgpError {
|
|
|
80
258
|
this.validator = input(null, ...(ngDevMode ? [{ debugName: "validator", alias: 'ngpErrorValidator' }] : [{
|
|
81
259
|
alias: 'ngpErrorValidator',
|
|
82
260
|
}]));
|
|
83
|
-
|
|
84
|
-
* Determine if there is an error message.
|
|
85
|
-
*/
|
|
86
|
-
this.hasError = computed(() => {
|
|
87
|
-
const errors = this.formField()?.errors() ?? [];
|
|
88
|
-
const validator = this.validator();
|
|
89
|
-
return validator ? errors?.includes(validator) : errors?.length > 0;
|
|
90
|
-
}, ...(ngDevMode ? [{ debugName: "hasError" }] : []));
|
|
91
|
-
/**
|
|
92
|
-
* Determine whether the validator associated with this error is failing.
|
|
93
|
-
*/
|
|
94
|
-
this.state = computed(() => (this.hasError() ? 'fail' : 'pass'), ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
95
|
-
// add or remove the error message when the error state changes
|
|
96
|
-
onBooleanChange(this.hasError, () => this.formField()?.addDescription(this.id()), () => this.formField()?.removeDescription(this.id()));
|
|
97
|
-
}
|
|
98
|
-
ngOnChanges(changes) {
|
|
99
|
-
if ('id' in changes) {
|
|
100
|
-
this.formField()?.removeDescription(changes['id'].previousValue);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
ngOnDestroy() {
|
|
104
|
-
this.formField()?.removeDescription(this.id());
|
|
261
|
+
ngpError({ id: this.id, validator: this.validator });
|
|
105
262
|
}
|
|
106
263
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpError, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
107
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpError, isStandalone: true, selector: "[ngpError]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, validator: { classPropertyName: "validator", publicName: "ngpErrorValidator", isSignal: true, isRequired: false, transformFunction: null } },
|
|
264
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpError, isStandalone: true, selector: "[ngpError]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, validator: { classPropertyName: "validator", publicName: "ngpErrorValidator", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideErrorState()], exportAs: ["ngpError"], ngImport: i0 }); }
|
|
108
265
|
}
|
|
109
266
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpError, decorators: [{
|
|
110
267
|
type: Directive,
|
|
111
268
|
args: [{
|
|
112
269
|
selector: '[ngpError]',
|
|
113
270
|
exportAs: 'ngpError',
|
|
114
|
-
|
|
115
|
-
'[attr.id]': 'id()',
|
|
116
|
-
'[attr.data-invalid]': 'formField()?.invalid() ? "" : null',
|
|
117
|
-
'[attr.data-valid]': 'formField()?.valid() ? "" : null',
|
|
118
|
-
'[attr.data-touched]': 'formField()?.touched() ? "" : null',
|
|
119
|
-
'[attr.data-pristine]': 'formField()?.pristine() ? "" : null',
|
|
120
|
-
'[attr.data-dirty]': 'formField()?.dirty() ? "" : null',
|
|
121
|
-
'[attr.data-pending]': 'formField()?.pending() ? "" : null',
|
|
122
|
-
'[attr.data-disabled]': 'formField()?.disabled() ? "" : null',
|
|
123
|
-
'[attr.data-validator]': 'state()',
|
|
124
|
-
},
|
|
271
|
+
providers: [provideErrorState()],
|
|
125
272
|
}]
|
|
126
273
|
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], validator: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpErrorValidator", required: false }] }] } });
|
|
127
274
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
275
|
+
function ngpFormControl({ id, disabled = signal(false), }) {
|
|
276
|
+
const elementRef = injectElementRef();
|
|
277
|
+
// Access the form field that the form control is associated with.
|
|
278
|
+
const formField = injectFormFieldState({ optional: true });
|
|
279
|
+
// Access the form control status.
|
|
280
|
+
const status = controlStatus();
|
|
281
|
+
// Determine the aria-labelledby attribute value.
|
|
282
|
+
const ariaLabelledBy = computed(() => {
|
|
283
|
+
const labels = formField()?.labels() ?? [];
|
|
284
|
+
return labels.length > 0 ? labels.join(' ') : null;
|
|
285
|
+
}, ...(ngDevMode ? [{ debugName: "ariaLabelledBy" }] : []));
|
|
286
|
+
// Determine the aria-describedby attribute value.
|
|
287
|
+
const ariaDescribedBy = computed(() => {
|
|
288
|
+
const descriptions = formField()?.descriptions() ?? [];
|
|
289
|
+
return descriptions.length > 0 ? descriptions.join(' ') : null;
|
|
290
|
+
}, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
|
|
291
|
+
const supportsDisabledAttribute = 'disabled' in elementRef.nativeElement;
|
|
292
|
+
// Host bindings
|
|
293
|
+
attrBinding(elementRef, 'disabled', () => (supportsDisabledAttribute && disabled() ? '' : null));
|
|
294
|
+
explicitEffect([id], ([id], onCleanup) => {
|
|
295
|
+
formField()?.setFormControl(id);
|
|
296
|
+
onCleanup(() => formField()?.removeFormControl());
|
|
297
|
+
});
|
|
298
|
+
attrBinding(elementRef, 'id', id);
|
|
299
|
+
attrBinding(elementRef, 'aria-labelledby', ariaLabelledBy);
|
|
300
|
+
attrBinding(elementRef, 'aria-describedby', ariaDescribedBy);
|
|
301
|
+
dataBinding(elementRef, 'data-invalid', () => status().invalid);
|
|
302
|
+
dataBinding(elementRef, 'data-valid', () => status().valid);
|
|
303
|
+
dataBinding(elementRef, 'data-touched', () => status().touched);
|
|
304
|
+
dataBinding(elementRef, 'data-pristine', () => status().pristine);
|
|
305
|
+
dataBinding(elementRef, 'data-dirty', () => status().dirty);
|
|
306
|
+
dataBinding(elementRef, 'data-pending', () => status().pending);
|
|
307
|
+
dataBinding(elementRef, 'data-disabled', () => disabled() || status().disabled);
|
|
308
|
+
return computed(() => ({ ...status(), disabled: status().disabled || disabled() }));
|
|
309
|
+
}
|
|
144
310
|
|
|
145
311
|
/**
|
|
146
312
|
* Typically this primitive would be not be used directly, but instead a more specific form control primitive would be used (e.g. `ngpInput`). All of our form control primitives use `ngpFormControl` internally so they will have the same accessibility features as described below.
|
|
@@ -162,243 +328,38 @@ class NgpFormControl {
|
|
|
162
328
|
transform: booleanAttribute,
|
|
163
329
|
}]));
|
|
164
330
|
/**
|
|
165
|
-
* The
|
|
331
|
+
* The status of the form control.
|
|
166
332
|
*/
|
|
167
|
-
this.
|
|
168
|
-
/**
|
|
169
|
-
* Whether the element supports the disabled attribute.
|
|
170
|
-
*/
|
|
171
|
-
this.supportsDisabledAttribute = 'disabled' in this.elementRef.nativeElement;
|
|
172
|
-
/**
|
|
173
|
-
* The state of the form control.
|
|
174
|
-
*/
|
|
175
|
-
this.state = formControlState(this);
|
|
176
|
-
// Sync the form control state with the control state.
|
|
177
|
-
this.status = ngpFormControl({ id: this.state.id, disabled: this.state.disabled });
|
|
333
|
+
this.status = ngpFormControl({ id: this.id, disabled: this.disabled });
|
|
178
334
|
}
|
|
179
335
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpFormControl, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
180
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpFormControl, isStandalone: true, selector: "[ngpFormControl]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpFormControlDisabled", isSignal: true, isRequired: false, transformFunction: null } },
|
|
336
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpFormControl, isStandalone: true, selector: "[ngpFormControl]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpFormControlDisabled", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["ngpFormControl"], ngImport: i0 }); }
|
|
181
337
|
}
|
|
182
338
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpFormControl, decorators: [{
|
|
183
339
|
type: Directive,
|
|
184
340
|
args: [{
|
|
185
341
|
selector: '[ngpFormControl]',
|
|
186
342
|
exportAs: 'ngpFormControl',
|
|
187
|
-
providers: [provideFormControlState()],
|
|
188
|
-
host: {
|
|
189
|
-
'[attr.disabled]': 'supportsDisabledAttribute && status().disabled ? "" : null',
|
|
190
|
-
},
|
|
191
343
|
}]
|
|
192
|
-
}],
|
|
193
|
-
function ngpFormControl({ id, disabled = signal(false), }) {
|
|
194
|
-
const element = injectElementRef();
|
|
195
|
-
// Access the form field that the form control is associated with.
|
|
196
|
-
const formField = injectFormFieldState({ optional: true });
|
|
197
|
-
// Access the form control status.
|
|
198
|
-
const status = controlStatus();
|
|
199
|
-
// Determine the aria-labelledby attribute value.
|
|
200
|
-
const ariaLabelledBy = computed(() => formField()?.labels().join(' '), ...(ngDevMode ? [{ debugName: "ariaLabelledBy" }] : []));
|
|
201
|
-
// Determine the aria-describedby attribute value.
|
|
202
|
-
const ariaDescribedBy = computed(() => formField()?.descriptions().join(' '), ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
|
|
203
|
-
explicitEffect([id], ([id], onCleanup) => {
|
|
204
|
-
formField()?.setFormControl(id);
|
|
205
|
-
onCleanup(() => formField()?.removeFormControl());
|
|
206
|
-
});
|
|
207
|
-
attrBinding(element, 'id', id);
|
|
208
|
-
attrBinding(element, 'aria-labelledby', ariaLabelledBy);
|
|
209
|
-
attrBinding(element, 'aria-describedby', ariaDescribedBy);
|
|
210
|
-
dataBinding(element, 'data-invalid', () => status().invalid);
|
|
211
|
-
dataBinding(element, 'data-valid', () => status().valid);
|
|
212
|
-
dataBinding(element, 'data-touched', () => status().touched);
|
|
213
|
-
dataBinding(element, 'data-pristine', () => status().pristine);
|
|
214
|
-
dataBinding(element, 'data-dirty', () => status().dirty);
|
|
215
|
-
dataBinding(element, 'data-pending', () => status().pending);
|
|
216
|
-
dataBinding(element, 'data-disabled', () => disabled() || status().disabled);
|
|
217
|
-
return computed(() => ({ ...status(), disabled: status().disabled || disabled() }));
|
|
218
|
-
}
|
|
344
|
+
}], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpFormControlDisabled", required: false }] }] } });
|
|
219
345
|
|
|
220
346
|
/**
|
|
221
347
|
* The `NgpFormField` directive is a container for form field elements. Any labels, form controls, or descriptions should be placed within this directive.
|
|
222
348
|
*/
|
|
223
349
|
class NgpFormField {
|
|
350
|
+
/**
|
|
351
|
+
* The form field state.
|
|
352
|
+
*/
|
|
224
353
|
constructor() {
|
|
225
|
-
/**
|
|
226
|
-
* Store the form label.
|
|
227
|
-
* @internal
|
|
228
|
-
*/
|
|
229
|
-
this.labels = signal([], ...(ngDevMode ? [{ debugName: "labels" }] : []));
|
|
230
|
-
/**
|
|
231
|
-
* Store the form descriptions.
|
|
232
|
-
* @internal
|
|
233
|
-
*/
|
|
234
|
-
this.descriptions = signal([], ...(ngDevMode ? [{ debugName: "descriptions" }] : []));
|
|
235
|
-
/**
|
|
236
|
-
* Store the id of the associated form control.
|
|
237
|
-
* @internal
|
|
238
|
-
*/
|
|
239
|
-
this.formControl = signal(null, ...(ngDevMode ? [{ debugName: "formControl" }] : []));
|
|
240
354
|
/**
|
|
241
355
|
* Find any NgControl within the form field.
|
|
242
356
|
* @internal
|
|
243
357
|
*/
|
|
244
358
|
this.ngControl = contentChild(NgControl, ...(ngDevMode ? [{ debugName: "ngControl" }] : []));
|
|
245
|
-
|
|
246
|
-
* Store the validation error messages.
|
|
247
|
-
* @internal
|
|
248
|
-
*/
|
|
249
|
-
this.errors = signal([], ...(ngDevMode ? [{ debugName: "errors" }] : []));
|
|
250
|
-
/**
|
|
251
|
-
* Whether the control is pristine.
|
|
252
|
-
* @internal
|
|
253
|
-
*/
|
|
254
|
-
this.pristine = signal(null, ...(ngDevMode ? [{ debugName: "pristine" }] : []));
|
|
255
|
-
/**
|
|
256
|
-
* Whether the control is touched.
|
|
257
|
-
* @internal
|
|
258
|
-
*/
|
|
259
|
-
this.touched = signal(null, ...(ngDevMode ? [{ debugName: "touched" }] : []));
|
|
260
|
-
/**
|
|
261
|
-
* Whether the control is dirty.
|
|
262
|
-
* @internal
|
|
263
|
-
*/
|
|
264
|
-
this.dirty = signal(null, ...(ngDevMode ? [{ debugName: "dirty" }] : []));
|
|
265
|
-
/**
|
|
266
|
-
* Whether the control is valid.
|
|
267
|
-
*/
|
|
268
|
-
this.valid = signal(null, ...(ngDevMode ? [{ debugName: "valid" }] : []));
|
|
269
|
-
/**
|
|
270
|
-
* Whether the control is invalid.
|
|
271
|
-
* @internal
|
|
272
|
-
*/
|
|
273
|
-
this.invalid = signal(null, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
|
|
274
|
-
/**
|
|
275
|
-
* Whether the control is pending.
|
|
276
|
-
* @internal
|
|
277
|
-
*/
|
|
278
|
-
this.pending = signal(null, ...(ngDevMode ? [{ debugName: "pending" }] : []));
|
|
279
|
-
/**
|
|
280
|
-
* Whether the control is disabled.
|
|
281
|
-
* @internal
|
|
282
|
-
*/
|
|
283
|
-
this.disabled = signal(null, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
284
|
-
/**
|
|
285
|
-
* Injector for creating effects outside the constructor.
|
|
286
|
-
*/
|
|
287
|
-
this.injector = inject(Injector);
|
|
288
|
-
/**
|
|
289
|
-
* The form field state.
|
|
290
|
-
*/
|
|
291
|
-
this.state = formFieldState(this);
|
|
292
|
-
// any time the ngControl changes, setup the subscriptions.
|
|
293
|
-
onChange(this.ngControl, this.setupSubscriptions.bind(this));
|
|
294
|
-
}
|
|
295
|
-
ngOnDestroy() {
|
|
296
|
-
this.subscription?.unsubscribe();
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Setup a listener for the form control status.
|
|
300
|
-
* @param control
|
|
301
|
-
*/
|
|
302
|
-
setupSubscriptions(control) {
|
|
303
|
-
// Unsubscribe from the previous subscriptions.
|
|
304
|
-
this.subscription?.unsubscribe();
|
|
305
|
-
if (!control) {
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
// For signal-forms interop controls, use an effect to reactively track status.
|
|
309
|
-
// For classic controls, also use an effect but additionally subscribe to events.
|
|
310
|
-
effect(() => {
|
|
311
|
-
this.updateStatus();
|
|
312
|
-
}, { injector: this.injector });
|
|
313
|
-
// Classic controls also have an events observable we can subscribe to.
|
|
314
|
-
const underlyingControl = control?.control;
|
|
315
|
-
if (underlyingControl?.events) {
|
|
316
|
-
this.subscription = underlyingControl.events.subscribe(() => this.updateStatus());
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
updateStatus() {
|
|
320
|
-
const control = this.ngControl();
|
|
321
|
-
if (!control) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
// Wrap in try-catch to handle signal-forms interop controls where
|
|
325
|
-
// the `field` input may not be available yet (throws NG0950).
|
|
326
|
-
// Reading the signal still establishes a dependency, so the effect
|
|
327
|
-
// will re-run when the input becomes available.
|
|
328
|
-
try {
|
|
329
|
-
const pristine = control.pristine;
|
|
330
|
-
const touched = control.touched;
|
|
331
|
-
const dirty = control.dirty;
|
|
332
|
-
const valid = control.valid;
|
|
333
|
-
const invalid = control.invalid;
|
|
334
|
-
const pending = control.pending;
|
|
335
|
-
const disabled = control.disabled;
|
|
336
|
-
const errors = control.errors;
|
|
337
|
-
untracked(() => {
|
|
338
|
-
this.pristine.set(pristine);
|
|
339
|
-
this.touched.set(touched);
|
|
340
|
-
this.dirty.set(dirty);
|
|
341
|
-
this.valid.set(valid);
|
|
342
|
-
this.invalid.set(invalid);
|
|
343
|
-
this.pending.set(pending);
|
|
344
|
-
this.disabled.set(disabled);
|
|
345
|
-
this.errors.set(errors ? Object.keys(errors) : []);
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
catch {
|
|
349
|
-
// NG0950: Required input not available yet. The effect will re-run
|
|
350
|
-
// when the signal input becomes available.
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Register the id of the associated form control.
|
|
355
|
-
* @param id
|
|
356
|
-
* @internal
|
|
357
|
-
*/
|
|
358
|
-
setFormControl(id) {
|
|
359
|
-
this.formControl.set(id);
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Register a label with the form field.
|
|
363
|
-
* @param label
|
|
364
|
-
* @internal
|
|
365
|
-
*/
|
|
366
|
-
addLabel(label) {
|
|
367
|
-
this.labels.update(labels => [...labels, label]);
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* Register a description with the form field.
|
|
371
|
-
* @param description
|
|
372
|
-
* @internal
|
|
373
|
-
*/
|
|
374
|
-
addDescription(description) {
|
|
375
|
-
this.descriptions.update(descriptions => [...descriptions, description]);
|
|
376
|
-
}
|
|
377
|
-
/**
|
|
378
|
-
* Remove the associated form control.
|
|
379
|
-
* @internal
|
|
380
|
-
*/
|
|
381
|
-
removeFormControl() {
|
|
382
|
-
this.formControl.set(null);
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Remove a label from the form field.
|
|
386
|
-
* @param label
|
|
387
|
-
* @internal
|
|
388
|
-
*/
|
|
389
|
-
removeLabel(label) {
|
|
390
|
-
this.labels.update(labels => labels.filter(l => l !== label));
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Remove a description from the form field.
|
|
394
|
-
* @param description
|
|
395
|
-
* @internal
|
|
396
|
-
*/
|
|
397
|
-
removeDescription(description) {
|
|
398
|
-
this.descriptions.update(descriptions => descriptions.filter(d => d !== description));
|
|
359
|
+
ngpFormField({ ngControl: this.ngControl });
|
|
399
360
|
}
|
|
400
361
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpFormField, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
401
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.9", type: NgpFormField, isStandalone: true, selector: "[ngpFormField]",
|
|
362
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.9", type: NgpFormField, isStandalone: true, selector: "[ngpFormField]", providers: [provideFormFieldState()], queries: [{ propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }], exportAs: ["ngpFormField"], ngImport: i0 }); }
|
|
402
363
|
}
|
|
403
364
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpFormField, decorators: [{
|
|
404
365
|
type: Directive,
|
|
@@ -406,59 +367,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
406
367
|
selector: '[ngpFormField]',
|
|
407
368
|
exportAs: 'ngpFormField',
|
|
408
369
|
providers: [provideFormFieldState()],
|
|
409
|
-
host: {
|
|
410
|
-
'[attr.data-invalid]': 'invalid() ? "" : null',
|
|
411
|
-
'[attr.data-valid]': 'valid() ? "" : null',
|
|
412
|
-
'[attr.data-touched]': 'touched() ? "" : null',
|
|
413
|
-
'[attr.data-pristine]': 'pristine() ? "" : null',
|
|
414
|
-
'[attr.data-dirty]': 'dirty() ? "" : null',
|
|
415
|
-
'[attr.data-pending]': 'pending() ? "" : null',
|
|
416
|
-
'[attr.data-disabled]': 'disabled() ? "" : null',
|
|
417
|
-
},
|
|
418
370
|
}]
|
|
419
371
|
}], ctorParameters: () => [], propDecorators: { ngControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }] } });
|
|
420
372
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
* Access the element that the label is associated with.
|
|
440
|
-
*/
|
|
441
|
-
this.elementRef = inject(ElementRef);
|
|
442
|
-
/**
|
|
443
|
-
* Determine if the label is an HTML label element.
|
|
444
|
-
*/
|
|
445
|
-
this.isLabel = this.elementRef.nativeElement instanceof HTMLLabelElement;
|
|
446
|
-
effect(onCleanup => {
|
|
447
|
-
this.formField()?.addLabel(this.id());
|
|
448
|
-
onCleanup(() => this.formField()?.removeLabel(this.id()));
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
onClick(event) {
|
|
373
|
+
const [NgpLabelStateToken, ngpLabel, injectLabelState, provideLabelState] = createPrimitive('NgpLabel', ({ id }) => {
|
|
374
|
+
const element = injectElementRef();
|
|
375
|
+
const formField = injectFormFieldState({ optional: true });
|
|
376
|
+
// Derive the for attribute value if the label is an HTML label element
|
|
377
|
+
const htmlFor = computed(() => formField()?.formControl() ?? null, ...(ngDevMode ? [{ debugName: "htmlFor" }] : []));
|
|
378
|
+
// Determine if the label is an HTML label element
|
|
379
|
+
const isLabel = element.nativeElement instanceof HTMLLabelElement;
|
|
380
|
+
// Host bindings
|
|
381
|
+
attrBinding(element, 'id', id);
|
|
382
|
+
attrBinding(element, 'for', htmlFor);
|
|
383
|
+
dataBinding(element, 'data-invalid', () => (formField()?.invalid() ? '' : null));
|
|
384
|
+
dataBinding(element, 'data-valid', () => (formField()?.valid() ? '' : null));
|
|
385
|
+
dataBinding(element, 'data-touched', () => (formField()?.touched() ? '' : null));
|
|
386
|
+
dataBinding(element, 'data-pristine', () => (formField()?.pristine() ? '' : null));
|
|
387
|
+
dataBinding(element, 'data-dirty', () => (formField()?.dirty() ? '' : null));
|
|
388
|
+
dataBinding(element, 'data-pending', () => (formField()?.pending() ? '' : null));
|
|
389
|
+
dataBinding(element, 'data-disabled', () => (formField()?.disabled() ? '' : null));
|
|
390
|
+
function onClick(event) {
|
|
452
391
|
// by default a label will perform a click on the associated form control, however
|
|
453
392
|
// this only works if the associated form control is an input element which may not always
|
|
454
393
|
// be the case, so we prevent the default behavior and handle the click event ourselves.
|
|
455
394
|
// This was inspired by the HeadlessUI approach:
|
|
456
395
|
// https://github.com/tailwindlabs/headlessui/blob/main/packages/%40headlessui-react/src/components/label/label.tsx#L58
|
|
457
|
-
if (
|
|
396
|
+
if (isLabel) {
|
|
458
397
|
event.preventDefault();
|
|
459
398
|
}
|
|
460
399
|
// to find the associated form control we can lookup via the known id
|
|
461
|
-
const targetId =
|
|
400
|
+
const targetId = htmlFor();
|
|
462
401
|
if (!targetId) {
|
|
463
402
|
return;
|
|
464
403
|
}
|
|
@@ -484,34 +423,53 @@ class NgpLabel {
|
|
|
484
423
|
// bound element is now focused.
|
|
485
424
|
target.focus({ preventScroll: true });
|
|
486
425
|
}
|
|
426
|
+
// Event listeners
|
|
427
|
+
listener(element, 'click', onClick);
|
|
428
|
+
// Register with form field and cleanup on destroy
|
|
429
|
+
formField()?.addLabel(id());
|
|
430
|
+
onDestroy(() => formField()?.removeLabel(id()));
|
|
431
|
+
// any time the id changes we need to update the registration with the form field
|
|
432
|
+
onChange(id, (newId, oldId) => {
|
|
433
|
+
if (oldId) {
|
|
434
|
+
formField()?.removeLabel(oldId);
|
|
435
|
+
}
|
|
436
|
+
formField()?.addLabel(newId);
|
|
437
|
+
});
|
|
438
|
+
return {
|
|
439
|
+
id,
|
|
440
|
+
htmlFor,
|
|
441
|
+
};
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* The `NgpLabel` directive is used to mark a label element within a form field. Preferably, there should use an HTML `<label>` element.
|
|
446
|
+
*/
|
|
447
|
+
class NgpLabel {
|
|
448
|
+
/**
|
|
449
|
+
* The label state.
|
|
450
|
+
*/
|
|
451
|
+
constructor() {
|
|
452
|
+
/**
|
|
453
|
+
* The id of the label. If not provided, a unique id will be generated.
|
|
454
|
+
*/
|
|
455
|
+
this.id = input(uniqueId('ngp-label'), ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
456
|
+
ngpLabel({ id: this.id });
|
|
457
|
+
}
|
|
487
458
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
488
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpLabel, isStandalone: true, selector: "[ngpLabel]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } },
|
|
459
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpLabel, isStandalone: true, selector: "[ngpLabel]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideLabelState()], exportAs: ["ngpLabel"], ngImport: i0 }); }
|
|
489
460
|
}
|
|
490
461
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpLabel, decorators: [{
|
|
491
462
|
type: Directive,
|
|
492
463
|
args: [{
|
|
493
464
|
selector: '[ngpLabel]',
|
|
494
465
|
exportAs: 'ngpLabel',
|
|
495
|
-
|
|
496
|
-
'[attr.id]': 'id()',
|
|
497
|
-
'[attr.for]': 'htmlFor()',
|
|
498
|
-
'[attr.data-invalid]': 'formField()?.invalid() ? "" : null',
|
|
499
|
-
'[attr.data-valid]': 'formField()?.valid() ? "" : null',
|
|
500
|
-
'[attr.data-touched]': 'formField()?.touched() ? "" : null',
|
|
501
|
-
'[attr.data-pristine]': 'formField()?.pristine() ? "" : null',
|
|
502
|
-
'[attr.data-dirty]': 'formField()?.dirty() ? "" : null',
|
|
503
|
-
'[attr.data-pending]': 'formField()?.pending() ? "" : null',
|
|
504
|
-
'[attr.data-disabled]': 'formField()?.disabled() ? "" : null',
|
|
505
|
-
},
|
|
466
|
+
providers: [provideLabelState()],
|
|
506
467
|
}]
|
|
507
|
-
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }]
|
|
508
|
-
type: HostListener,
|
|
509
|
-
args: ['click', ['$event']]
|
|
510
|
-
}] } });
|
|
468
|
+
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }] } });
|
|
511
469
|
|
|
512
470
|
/**
|
|
513
471
|
* Generated bundle index. Do not edit.
|
|
514
472
|
*/
|
|
515
473
|
|
|
516
|
-
export { NgpDescription, NgpError, NgpFormControl, NgpFormField, NgpLabel,
|
|
474
|
+
export { NgpDescription, NgpError, NgpFormControl, NgpFormField, NgpLabel, injectDescriptionState, injectErrorState, injectFormFieldState, injectLabelState, ngpDescription, ngpError, ngpFormControl, ngpFormField, ngpLabel, provideDescriptionState, provideErrorState, provideFormFieldState, provideLabelState };
|
|
517
475
|
//# sourceMappingURL=ng-primitives-form-field.mjs.map
|