@radix-ng/primitives 1.0.9 → 1.1.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 +26 -5
- package/fesm2022/radix-ng-primitives-autocomplete.mjs +35 -19
- package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-checkbox.mjs +55 -7
- package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-combobox.mjs +46 -21
- package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-composite.mjs +221 -36
- package/fesm2022/radix-ng-primitives-composite.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-core.mjs +189 -26
- package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-date-field.mjs +29 -13
- package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-field.mjs +127 -23
- package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs +9 -0
- package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-focus-scope.mjs +241 -95
- package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-form.mjs +35 -4
- package/fesm2022/radix-ng-primitives-form.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-input.mjs +87 -15
- package/fesm2022/radix-ng-primitives-input.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menu.mjs +131 -16
- package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs +17 -3
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popper.mjs +223 -29
- package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-portal.mjs +16 -5
- package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-scroll-area.mjs +6 -3
- package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-select.mjs +49 -17
- package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-signal-forms.mjs +27 -11
- package/fesm2022/radix-ng-primitives-signal-forms.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-time-field.mjs +24 -10
- package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
- package/package.json +8 -7
- package/types/radix-ng-primitives-autocomplete.d.ts +7 -2
- package/types/radix-ng-primitives-checkbox.d.ts +10 -1
- package/types/radix-ng-primitives-combobox.d.ts +29 -2
- package/types/radix-ng-primitives-composite.d.ts +48 -2
- package/types/radix-ng-primitives-core.d.ts +165 -104
- package/types/radix-ng-primitives-date-field.d.ts +21 -8
- package/types/radix-ng-primitives-field.d.ts +55 -8
- package/types/radix-ng-primitives-floating-focus-manager.d.ts +7 -0
- package/types/radix-ng-primitives-focus-scope.d.ts +61 -44
- package/types/radix-ng-primitives-form.d.ts +9 -0
- package/types/radix-ng-primitives-input.d.ts +10 -2
- package/types/radix-ng-primitives-menu.d.ts +59 -7
- package/types/radix-ng-primitives-navigation-menu.d.ts +2 -2
- package/types/radix-ng-primitives-popover.d.ts +2 -2
- package/types/radix-ng-primitives-popper.d.ts +186 -48
- package/types/radix-ng-primitives-portal.d.ts +18 -8
- package/types/radix-ng-primitives-preview-card.d.ts +2 -2
- package/types/radix-ng-primitives-select.d.ts +17 -3
- package/types/radix-ng-primitives-signal-forms.d.ts +22 -8
- package/types/radix-ng-primitives-time-field.d.ts +21 -10
- package/types/radix-ng-primitives-tooltip.d.ts +2 -2
- package/fesm2022/radix-ng-primitives-focus-guards.mjs +0 -53
- package/fesm2022/radix-ng-primitives-focus-guards.mjs.map +0 -1
- package/focus-guards/README.md +0 -1
- package/types/radix-ng-primitives-focus-guards.d.ts +0 -15
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { forwardRef, input, booleanAttribute, output, linkedSignal, untracked, Directive, inject, isDevMode, HOST_TAG_NAME, ElementRef, InjectionToken,
|
|
3
|
-
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
2
|
+
import { forwardRef, input, booleanAttribute, output, linkedSignal, untracked, Directive, inject, Injector, DestroyRef, signal, computed, isDevMode, HOST_TAG_NAME, ElementRef, InjectionToken, APP_ID, Injectable, DOCUMENT, PLATFORM_ID, model, effect, afterNextRender, assertInInjectionContext } from '@angular/core';
|
|
3
|
+
import { NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
|
|
4
4
|
import { getLocalTimeZone, CalendarDateTime, ZonedDateTime, getDayOfWeek, DateFormatter, createCalendar, toCalendar, CalendarDate, Time, startOfMonth, endOfMonth, today } from '@internationalized/date';
|
|
5
5
|
import { isPlatformBrowser, DOCUMENT as DOCUMENT$1 } from '@angular/common';
|
|
6
6
|
|
|
@@ -113,6 +113,94 @@ function injectControlValueAccessor() {
|
|
|
113
113
|
return inject(RdxControlValueAccessor);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
function toValidationErrors(errors) {
|
|
117
|
+
if (!errors) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
return Object.entries(errors).map(([kind, details]) => {
|
|
121
|
+
const message = typeof details === 'string'
|
|
122
|
+
? details
|
|
123
|
+
: details && typeof details === 'object' && typeof details['message'] === 'string'
|
|
124
|
+
? details['message']
|
|
125
|
+
: undefined;
|
|
126
|
+
return message === undefined ? { kind } : { kind, message };
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Lazily connects to the same-host `NgControl` without creating a circular dependency while Angular
|
|
131
|
+
* is resolving its `ControlValueAccessor`. The unified `AbstractControl.events` stream mirrors
|
|
132
|
+
* programmatic value, status, validation, and interaction-state changes as well as user-driven ones.
|
|
133
|
+
*
|
|
134
|
+
* Must be called in an injection context.
|
|
135
|
+
*
|
|
136
|
+
* @ignore
|
|
137
|
+
*/
|
|
138
|
+
function injectNgControlState() {
|
|
139
|
+
const injector = inject(Injector);
|
|
140
|
+
const destroyRef = inject(DestroyRef);
|
|
141
|
+
const connected = signal(false, { debugName: 'RdxNgControlState.connected' });
|
|
142
|
+
const name = signal(undefined, { debugName: 'RdxNgControlState.name' });
|
|
143
|
+
const value = signal(undefined, { debugName: 'RdxNgControlState.value' });
|
|
144
|
+
const valid = signal(false, { debugName: 'RdxNgControlState.valid' });
|
|
145
|
+
const invalid = signal(false, { debugName: 'RdxNgControlState.invalid' });
|
|
146
|
+
const pending = signal(false, { debugName: 'RdxNgControlState.pending' });
|
|
147
|
+
const disabled = signal(false, { debugName: 'RdxNgControlState.disabled' });
|
|
148
|
+
const rawErrors = signal(null, { debugName: 'RdxNgControlState.rawErrors' });
|
|
149
|
+
const errors = computed(() => toValidationErrors(rawErrors()), {
|
|
150
|
+
debugName: 'RdxNgControlState.errors'
|
|
151
|
+
});
|
|
152
|
+
const dirty = signal(false, { debugName: 'RdxNgControlState.dirty' });
|
|
153
|
+
const touched = signal(false, { debugName: 'RdxNgControlState.touched' });
|
|
154
|
+
let destroyed = false;
|
|
155
|
+
let unsubscribe;
|
|
156
|
+
const connect = () => {
|
|
157
|
+
if (destroyed) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const ngControl = injector.get(NgControl, null, { self: true, optional: true });
|
|
161
|
+
const control = ngControl?.control;
|
|
162
|
+
// Signal Forms' `FormField` is also discoverable through Angular's forms DI integration, but
|
|
163
|
+
// it is not an `AbstractControl` and owns state through signals instead of `control.events`.
|
|
164
|
+
if (!control || typeof control.events?.subscribe !== 'function') {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const sync = () => {
|
|
168
|
+
name.set(ngControl.name == null ? undefined : String(ngControl.name));
|
|
169
|
+
value.set(control.value);
|
|
170
|
+
valid.set(control.valid);
|
|
171
|
+
invalid.set(control.invalid);
|
|
172
|
+
pending.set(control.pending);
|
|
173
|
+
disabled.set(control.disabled);
|
|
174
|
+
rawErrors.set(control.errors);
|
|
175
|
+
dirty.set(control.dirty);
|
|
176
|
+
touched.set(control.touched);
|
|
177
|
+
};
|
|
178
|
+
sync();
|
|
179
|
+
const subscription = control.events.subscribe(sync);
|
|
180
|
+
unsubscribe = () => subscription.unsubscribe();
|
|
181
|
+
connected.set(true);
|
|
182
|
+
};
|
|
183
|
+
// `NgControl` resolves its value accessor from this same element. Defer the lookup until every
|
|
184
|
+
// directive on the host has finished constructing to avoid a DI cycle.
|
|
185
|
+
queueMicrotask(connect);
|
|
186
|
+
destroyRef.onDestroy(() => {
|
|
187
|
+
destroyed = true;
|
|
188
|
+
unsubscribe?.();
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
connected: connected.asReadonly(),
|
|
192
|
+
name: name.asReadonly(),
|
|
193
|
+
value: value.asReadonly(),
|
|
194
|
+
valid: valid.asReadonly(),
|
|
195
|
+
invalid: invalid.asReadonly(),
|
|
196
|
+
pending: pending.asReadonly(),
|
|
197
|
+
disabled: disabled.asReadonly(),
|
|
198
|
+
errors,
|
|
199
|
+
dirty: dirty.asReadonly(),
|
|
200
|
+
touched: touched.asReadonly()
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
116
204
|
/**
|
|
117
205
|
* The `clamp` function restricts a number within a specified range by returning the value itself if it
|
|
118
206
|
* falls within the range, or the closest boundary value if it exceeds the range.
|
|
@@ -1405,7 +1493,7 @@ function useDateField(props) {
|
|
|
1405
1493
|
return prevValue;
|
|
1406
1494
|
const str = prevValue.toString();
|
|
1407
1495
|
if (str.length === 1) {
|
|
1408
|
-
props.modelValue.set(
|
|
1496
|
+
props.modelValue.set(null);
|
|
1409
1497
|
return null;
|
|
1410
1498
|
}
|
|
1411
1499
|
return Number.parseInt(str.slice(0, -1));
|
|
@@ -2008,22 +2096,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
2008
2096
|
* of the specified class. This is especially useful when you want multiple
|
|
2009
2097
|
* tokens (or interfaces) to resolve to the same directive/component instance.
|
|
2010
2098
|
*
|
|
2011
|
-
*
|
|
2012
|
-
*
|
|
2013
|
-
*
|
|
2099
|
+
* `Type<T>` ties the class to the token's contract, so a class that does not
|
|
2100
|
+
* satisfy `T` is rejected at compile time instead of failing at runtime.
|
|
2101
|
+
*
|
|
2102
|
+
* @template T - The type associated with the provider token.
|
|
2103
|
+
* @param token - The token (InjectionToken, class, or abstract class) to provide.
|
|
2104
|
+
* @param type - The class whose existing instance resolves this token; must satisfy `T`.
|
|
2014
2105
|
* @returns A Provider configuration object for Angular's DI system.
|
|
2015
2106
|
*
|
|
2016
2107
|
* @example
|
|
2017
2108
|
*
|
|
2018
2109
|
* @Directive({
|
|
2019
2110
|
* providers: [
|
|
2020
|
-
*
|
|
2111
|
+
* provideExistingToken(RdxFooToken, RdxFooDirective),
|
|
2021
2112
|
* provideValueAccessor(RdxFooDirective)
|
|
2022
2113
|
* ]
|
|
2023
2114
|
* })
|
|
2024
2115
|
* export class RdxFooDirective {}
|
|
2025
2116
|
*/
|
|
2026
|
-
function
|
|
2117
|
+
function provideExistingToken(token, type) {
|
|
2027
2118
|
return {
|
|
2028
2119
|
provide: token,
|
|
2029
2120
|
useExisting: forwardRef(() => type)
|
|
@@ -2032,8 +2123,8 @@ function provideToken(token, type) {
|
|
|
2032
2123
|
|
|
2033
2124
|
/**
|
|
2034
2125
|
* Builds the shared form-UI state and its mutators from a control's input signals, removing the
|
|
2035
|
-
* per-control copy-paste of the
|
|
2036
|
-
*
|
|
2126
|
+
* per-control copy-paste of the validation/interaction computeds and the `markAsTouched`/`markDirty`
|
|
2127
|
+
* logic.
|
|
2037
2128
|
*
|
|
2038
2129
|
* **Why the inputs stay on the control (not in here):** Angular's compiler only discovers
|
|
2039
2130
|
* `input()`/`model()` declared as field initializers, and Signal Forms binds form-written state
|
|
@@ -2046,22 +2137,58 @@ function provideToken(token, type) {
|
|
|
2046
2137
|
function createFormUiState(options) {
|
|
2047
2138
|
const dirtyValue = signal(false, /* @ts-ignore */
|
|
2048
2139
|
...(ngDevMode ? [{ debugName: "dirtyValue" }] : /* istanbul ignore next */ []));
|
|
2140
|
+
const errorsState = computed(() => {
|
|
2141
|
+
const ownErrors = options.errors() ?? [];
|
|
2142
|
+
const ngControlErrors = options.ngControlState?.connected() ? options.ngControlState.errors() : [];
|
|
2143
|
+
return ngControlErrors.length > 0 ? [...ownErrors, ...ngControlErrors] : ownErrors;
|
|
2144
|
+
}, /* @ts-ignore */
|
|
2145
|
+
...(ngDevMode ? [{ debugName: "errorsState" }] : /* istanbul ignore next */ []));
|
|
2146
|
+
const invalidState = computed(() => options.invalid() ||
|
|
2147
|
+
errorsState().length > 0 ||
|
|
2148
|
+
Boolean(options.ngControlState?.connected() && options.ngControlState.invalid()), /* @ts-ignore */
|
|
2149
|
+
...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
|
|
2150
|
+
const pendingState = computed(() => (options.pending?.() ?? false) ||
|
|
2151
|
+
Boolean(options.ngControlState?.connected() && options.ngControlState.pending()), /* @ts-ignore */
|
|
2152
|
+
...(ngDevMode ? [{ debugName: "pendingState" }] : /* istanbul ignore next */ []));
|
|
2153
|
+
const validState = computed(() => {
|
|
2154
|
+
if (pendingState()) {
|
|
2155
|
+
return null;
|
|
2156
|
+
}
|
|
2157
|
+
if (invalidState()) {
|
|
2158
|
+
return false;
|
|
2159
|
+
}
|
|
2160
|
+
if (options.ngControlState?.connected() && options.ngControlState.disabled()) {
|
|
2161
|
+
return null;
|
|
2162
|
+
}
|
|
2163
|
+
return true;
|
|
2164
|
+
}, /* @ts-ignore */
|
|
2165
|
+
...(ngDevMode ? [{ debugName: "validState" }] : /* istanbul ignore next */ []));
|
|
2049
2166
|
return {
|
|
2050
|
-
invalidState
|
|
2051
|
-
|
|
2052
|
-
|
|
2167
|
+
invalidState,
|
|
2168
|
+
pendingState,
|
|
2169
|
+
validState,
|
|
2170
|
+
errorsState,
|
|
2171
|
+
touchedState: computed(() => options.ngControlState?.connected() ? options.ngControlState.touched() : options.touched()),
|
|
2172
|
+
dirtyState: computed(() => options.ngControlState?.connected() ? options.ngControlState.dirty() : options.dirty() || dirtyValue()),
|
|
2053
2173
|
markDirty: () => dirtyValue.set(true),
|
|
2054
2174
|
markAsTouched: () => {
|
|
2055
2175
|
options.cva?.markAsTouched();
|
|
2056
2176
|
options.touched.set(true);
|
|
2057
2177
|
options.touch.emit();
|
|
2058
|
-
}
|
|
2178
|
+
},
|
|
2179
|
+
resetInteractionState: () => {
|
|
2180
|
+
options.touched.set(false);
|
|
2181
|
+
dirtyValue.set(false);
|
|
2182
|
+
},
|
|
2183
|
+
resetDirtyState: () => dirtyValue.set(false)
|
|
2059
2184
|
};
|
|
2060
2185
|
}
|
|
2061
2186
|
/** Spread helper to wire {@link RdxFormUiStateContext} fields from an {@link RdxFormUiState} into a context factory. */
|
|
2062
2187
|
function formUiStateContext(state) {
|
|
2063
2188
|
return {
|
|
2064
2189
|
invalidState: state.invalidState,
|
|
2190
|
+
pendingState: state.pendingState,
|
|
2191
|
+
validState: state.validState,
|
|
2065
2192
|
touchedState: state.touchedState,
|
|
2066
2193
|
dirtyState: state.dirtyState,
|
|
2067
2194
|
markAsTouched: () => state.markAsTouched()
|
|
@@ -2081,15 +2208,21 @@ const RDX_FIELD_VALIDITY = new InjectionToken('RdxFieldValidity');
|
|
|
2081
2208
|
/**
|
|
2082
2209
|
* Tri-state display validity: when inside a `Field` the field's gated `validState` is the **single
|
|
2083
2210
|
* source** (the control reflects it, including its `validationMode` neutral state); standalone, the
|
|
2084
|
-
* control's own
|
|
2211
|
+
* control's own tri-state validity (pending is neutral).
|
|
2085
2212
|
*
|
|
2086
2213
|
* **Contract:** inside a `Field` a control's own `invalid` / `errors` inputs are **not** displayed — the
|
|
2087
2214
|
* Field owns displayed validity. Drive validity through the Field instead: bind `rdxSignalField`
|
|
2088
2215
|
* (Signal Forms), or set `[invalid]` on `rdxFieldRoot`. The control's own `invalid`/`errors` are for
|
|
2089
2216
|
* standalone use.
|
|
2090
2217
|
*/
|
|
2091
|
-
function resolveDisplayValid(fieldValidity, ownInvalid) {
|
|
2092
|
-
|
|
2218
|
+
function resolveDisplayValid(fieldValidity, ownInvalid, ownPending) {
|
|
2219
|
+
if (fieldValidity) {
|
|
2220
|
+
return fieldValidity();
|
|
2221
|
+
}
|
|
2222
|
+
if (ownPending?.()) {
|
|
2223
|
+
return null;
|
|
2224
|
+
}
|
|
2225
|
+
return ownInvalid() ? false : true;
|
|
2093
2226
|
}
|
|
2094
2227
|
/**
|
|
2095
2228
|
* Provide a control's {@link RdxFormUiState} on its host element so {@link RdxFormUiStateHost} can
|
|
@@ -2126,7 +2259,7 @@ class RdxFormUiStateHost {
|
|
|
2126
2259
|
this.formUi = inject(RDX_FORM_UI_STATE);
|
|
2127
2260
|
this.fieldValidity = inject(RDX_FIELD_VALIDITY, { optional: true });
|
|
2128
2261
|
/** @ignore Tri-state display validity (enclosing Field's gated state, else own invalidity). */
|
|
2129
|
-
this.displayValid = computed(() =>
|
|
2262
|
+
this.displayValid = computed(() => this.fieldValidity ? this.fieldValidity() : this.formUi.validState(), /* @ts-ignore */
|
|
2130
2263
|
...(ngDevMode ? [{ debugName: "displayValid" }] : /* istanbul ignore next */ []));
|
|
2131
2264
|
}
|
|
2132
2265
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxFormUiStateHost, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
@@ -2149,7 +2282,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
2149
2282
|
}] });
|
|
2150
2283
|
/**
|
|
2151
2284
|
* Abstract base that declares the optional `FormUiControl` state inputs
|
|
2152
|
-
* (`invalid`/`errors`/`touched`/`dirty` + the `touch` output) once and builds the control's
|
|
2285
|
+
* (`invalid`/`pending`/`errors`/`touched`/`dirty` + the `touch` output) once and builds the control's
|
|
2153
2286
|
* {@link RdxFormUiState} from them, so a control directive can inherit the whole surface with a
|
|
2154
2287
|
* single `extends` instead of re-declaring it.
|
|
2155
2288
|
*
|
|
@@ -2167,6 +2300,8 @@ class RdxFormUiControlBase {
|
|
|
2167
2300
|
constructor() {
|
|
2168
2301
|
/** Whether the control is invalid. A non-empty {@link errors} list also marks it invalid. */
|
|
2169
2302
|
this.invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2303
|
+
/** Whether async validation is pending. Pending controls publish neither `data-valid` nor `data-invalid`. */
|
|
2304
|
+
this.pending = input(false, { ...(ngDevMode ? { debugName: "pending" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2170
2305
|
/** Whether the control has been touched. A `model()` so Signal Forms can write it; set on blur/focus-out. */
|
|
2171
2306
|
this.touched = model(false, /* @ts-ignore */
|
|
2172
2307
|
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
@@ -2177,15 +2312,20 @@ class RdxFormUiControlBase {
|
|
|
2177
2312
|
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
2178
2313
|
/** Emits when the control is touched, notifying Signal Forms (stable Angular 22 contract). */
|
|
2179
2314
|
this.touch = output();
|
|
2315
|
+
this.ngControlState = injectNgControlState();
|
|
2180
2316
|
/** The shared form-UI state derived from the inputs above. Call `formUi.markDirty()` on value change. */
|
|
2181
2317
|
this.formUi = createFormUiState({
|
|
2182
2318
|
invalid: this.invalid,
|
|
2319
|
+
pending: this.pending,
|
|
2183
2320
|
errors: this.errors,
|
|
2184
2321
|
touched: this.touched,
|
|
2185
2322
|
touch: this.touch,
|
|
2186
2323
|
dirty: this.dirty,
|
|
2324
|
+
ngControlState: this.ngControlState,
|
|
2187
2325
|
cva: this.formUiTouchTarget()
|
|
2188
2326
|
});
|
|
2327
|
+
/** Validation errors from the control inputs and a same-host Reactive/template-driven form control. */
|
|
2328
|
+
this.validationErrors = this.formUi.errorsState;
|
|
2189
2329
|
/** The enclosing Field's tri-state display validity, if any. `protected` so a control whose own
|
|
2190
2330
|
* invalidity is richer than `formUi.invalidState` (e.g. date/time-field add a parse check) can build
|
|
2191
2331
|
* its own `displayValid` from it. */
|
|
@@ -2193,27 +2333,50 @@ class RdxFormUiControlBase {
|
|
|
2193
2333
|
/**
|
|
2194
2334
|
* Tri-state *displayed* validity for controls that bind their own host attributes (radio, switch,
|
|
2195
2335
|
* number-field): the enclosing Field's gated state when inside a `rdxFieldRoot`, else this control's
|
|
2196
|
-
* own
|
|
2336
|
+
* own validity (`formUi.pendingState` is neutral; otherwise `formUi.invalidState` is binary). Bind
|
|
2337
|
+
* `data-valid`/`data-invalid`/`aria-invalid` to
|
|
2197
2338
|
* this so a neutral field shows neither. Controls whose `invalidState` is richer than
|
|
2198
|
-
* `formUi.invalidState` override this with
|
|
2339
|
+
* `formUi.invalidState` override this with
|
|
2340
|
+
* `resolveDisplayValid(this.fieldValidity, this.invalidState, this.formUi.pendingState)`.
|
|
2199
2341
|
*/
|
|
2200
|
-
this.displayValid = computed(() =>
|
|
2342
|
+
this.displayValid = computed(() => this.fieldValidity ? this.fieldValidity() : this.formUi.validState(), /* @ts-ignore */
|
|
2201
2343
|
...(ngDevMode ? [{ debugName: "displayValid" }] : /* istanbul ignore next */ []));
|
|
2344
|
+
// Reactive/template-driven forms own their NgControl interaction state. Signal Forms instead
|
|
2345
|
+
// writes the public touched/dirty members, so standalone controls retain the existing fallback.
|
|
2346
|
+
effect(() => {
|
|
2347
|
+
if (this.ngControlState.connected()) {
|
|
2348
|
+
this.touched.set(this.ngControlState.touched());
|
|
2349
|
+
if (!this.ngControlState.dirty()) {
|
|
2350
|
+
this.formUi.resetDirtyState?.();
|
|
2351
|
+
}
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
if (!this.dirty()) {
|
|
2355
|
+
this.formUi.resetDirtyState?.();
|
|
2356
|
+
}
|
|
2357
|
+
}, { debugName: 'RdxFormUiControlBase.syncInteractionState' });
|
|
2202
2358
|
}
|
|
2203
2359
|
/**
|
|
2204
2360
|
* Override to bridge the control's `ControlValueAccessor` into `markAsTouched` (dual controls —
|
|
2205
2361
|
* return `injectControlValueAccessor()` or a `{ markAsTouched }` adapter). Default: no CVA
|
|
2206
|
-
* (Signal-Forms-only controls such as
|
|
2362
|
+
* (Signal-Forms-only controls such as date-field).
|
|
2207
2363
|
*/
|
|
2208
2364
|
formUiTouchTarget() {
|
|
2209
2365
|
return null;
|
|
2210
2366
|
}
|
|
2367
|
+
/**
|
|
2368
|
+
* Reset control-owned interaction state. Angular Signal Forms calls this optional custom-control
|
|
2369
|
+
* hook from `FieldState.reset()` after restoring the field value.
|
|
2370
|
+
*/
|
|
2371
|
+
reset() {
|
|
2372
|
+
this.formUi.resetInteractionState?.();
|
|
2373
|
+
}
|
|
2211
2374
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxFormUiControlBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2212
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxFormUiControlBase, isStandalone: true, inputs: { invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, dirty: { classPropertyName: "dirty", publicName: "dirty", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { touched: "touchedChange", touch: "touch" }, ngImport: i0 }); }
|
|
2375
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxFormUiControlBase, isStandalone: true, inputs: { invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, pending: { classPropertyName: "pending", publicName: "pending", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, dirty: { classPropertyName: "dirty", publicName: "dirty", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { touched: "touchedChange", touch: "touch" }, ngImport: i0 }); }
|
|
2213
2376
|
}
|
|
2214
2377
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxFormUiControlBase, decorators: [{
|
|
2215
2378
|
type: Directive
|
|
2216
|
-
}], propDecorators: { invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], dirty: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirty", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
|
|
2379
|
+
}], ctorParameters: () => [], propDecorators: { invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], pending: [{ type: i0.Input, args: [{ isSignal: true, alias: "pending", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], dirty: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirty", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
|
|
2217
2380
|
|
|
2218
2381
|
/** The default validation-display mode when neither `rdxFieldRoot` nor `rdxFormRoot` sets one. */
|
|
2219
2382
|
const RDX_DEFAULT_VALIDATION_MODE = 'onBlur';
|
|
@@ -4281,5 +4444,5 @@ var RdxPositionAlign;
|
|
|
4281
4444
|
* Generated bundle index. Do not edit.
|
|
4282
4445
|
*/
|
|
4283
4446
|
|
|
4284
|
-
export { A, ALT, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, ASTERISK, BACKSPACE, CAPS_LOCK, CONTROL, CTRL, DELETE, DOCS_BASE_URL, END, ENTER, ESCAPE, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, HOME, META, P, PAGE_DOWN, PAGE_UP, RDX_DEFAULT_VALIDATION_MODE, RDX_FIELD_VALIDITY, RDX_FLOATING_REGISTRATION, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_TREE, RDX_FORM_UI_STATE, RDX_INTERNAL_BACKDROP_ATTR, RDX_SCROLL_LOCKED_ATTR, RdxControlValueAccessor, RdxFloatingNode, RdxFloatingNodeRegistration, RdxFloatingRegistrationContext, RdxFloatingRootContext, RdxFloatingTree, RdxFormUiControlBase, RdxFormUiStateHost, RdxIdGenerator, RdxLiveAnnouncer, RdxPositionAlign, RdxPositionSide, RdxTriggerRegistry, SHIFT, SPACE, SPACE_CODE, TAB, TIME_GRANULARITIES, a, areAllDaysBetweenValid, clamp, createCancelableChangeEventDetails, createContent, createContext, createFloatingEvents, createFloatingRootContext, createFormUiState, createFormatter, createMonth, createMonths, docsUrl, elementSize, formUiStateContext, getActiveElement, getDaysBetween, getDaysInMonth, getDefaultDate, getDefaultTime, getLastFirstDayOfWeek, getMaxTransitionDuration, getNextLastDayOfWeek, getOptsByGranularity, getPlaceholder, getSegmentElements, getWeekNumber, handleAndDispatchCustomEvent, handleCalendarInitialFocus, hasTime, initializeSegmentValues, injectControlValueAccessor, injectDocument, injectFloatingRootContext, injectId, isAcceptableSegmentKey, isAfter, isAfterOrSame, isBefore, isBeforeOrSame, isBetween, isBetweenInclusive, isCalendarDateTime, isEqual, isItemEqualToValue, isNullish, isNumberString, isSegmentNavigationKey, isValidationRevealed, isZonedDateTime, itemToStringLabel, itemToStringValue, j, k, n, normalizeDateStep, normalizeHour12, normalizeHourCycle, p, provideFloatingRegistration, provideFloatingRootContext, provideFloatingTree, provideFormUiState,
|
|
4447
|
+
export { A, ALT, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, ASTERISK, BACKSPACE, CAPS_LOCK, CONTROL, CTRL, DELETE, DOCS_BASE_URL, END, ENTER, ESCAPE, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, HOME, META, P, PAGE_DOWN, PAGE_UP, RDX_DEFAULT_VALIDATION_MODE, RDX_FIELD_VALIDITY, RDX_FLOATING_REGISTRATION, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_TREE, RDX_FORM_UI_STATE, RDX_INTERNAL_BACKDROP_ATTR, RDX_SCROLL_LOCKED_ATTR, RdxControlValueAccessor, RdxFloatingNode, RdxFloatingNodeRegistration, RdxFloatingRegistrationContext, RdxFloatingRootContext, RdxFloatingTree, RdxFormUiControlBase, RdxFormUiStateHost, RdxIdGenerator, RdxLiveAnnouncer, RdxPositionAlign, RdxPositionSide, RdxTriggerRegistry, SHIFT, SPACE, SPACE_CODE, TAB, TIME_GRANULARITIES, a, areAllDaysBetweenValid, clamp, createCancelableChangeEventDetails, createContent, createContext, createFloatingEvents, createFloatingRootContext, createFormUiState, createFormatter, createMonth, createMonths, docsUrl, elementSize, formUiStateContext, getActiveElement, getDaysBetween, getDaysInMonth, getDefaultDate, getDefaultTime, getLastFirstDayOfWeek, getMaxTransitionDuration, getNextLastDayOfWeek, getOptsByGranularity, getPlaceholder, getSegmentElements, getWeekNumber, handleAndDispatchCustomEvent, handleCalendarInitialFocus, hasTime, initializeSegmentValues, injectControlValueAccessor, injectDocument, injectFloatingRootContext, injectId, injectNgControlState, isAcceptableSegmentKey, isAfter, isAfterOrSame, isBefore, isBeforeOrSame, isBetween, isBetweenInclusive, isCalendarDateTime, isEqual, isItemEqualToValue, isNullish, isNumberString, isSegmentNavigationKey, isValidationRevealed, isZonedDateTime, itemToStringLabel, itemToStringValue, j, k, n, normalizeDateStep, normalizeHour12, normalizeHourCycle, p, provideExistingToken, provideFloatingRegistration, provideFloatingRootContext, provideFloatingTree, provideFormUiState, provideValueAccessor, rdxCheckLabelElement, rdxCheckTriggerElement, rdxDevError, rdxDevWarning, resetRdxDevWarnings, resizeEffect, resolveDisplayValid, resolveFloatingTree, roundToStepPrecision, segmentBuilders, setupInternalBackdrop, snapValueToStep, syncSegmentValues, syncTimeSegmentValues, toDate, useAnchoredScrollLock, useArrowNavigation, useDateField, useFilter, useGraceArea, useListHighlight, usePointerDrag, useScrollLock, useTransitionStatus, watch };
|
|
4285
4448
|
//# sourceMappingURL=radix-ng-primitives-core.mjs.map
|