@flywheel-io/vision 21.8.1 → 21.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2803,11 +2803,11 @@ class FwRadioComponent {
|
|
|
2803
2803
|
this.change.emit(event?.target?.value);
|
|
2804
2804
|
}
|
|
2805
2805
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FwRadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2806
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FwRadioComponent, isStandalone: true, selector: "fw-radio-button", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, focused: { classPropertyName: "focused", publicName: "focused", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", group: "groupChange", disabled: "disabledChange", size: "sizeChange", color: "colorChange", focused: "focusedChange", change: "change" }, ngImport: i0, template: "<
|
|
2806
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FwRadioComponent, isStandalone: true, selector: "fw-radio-button", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, focused: { classPropertyName: "focused", publicName: "focused", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", group: "groupChange", disabled: "disabledChange", size: "sizeChange", color: "colorChange", focused: "focusedChange", change: "change" }, ngImport: i0, template: "<label [ngClass]=\"['fw-radio-wrapper', disabled() ? 'disabled' : '']\">\n <div [ngClass]=\"radioStyles\">\n <input\n type=\"radio\"\n class=\"fw-radio\"\n [id]=\"group() + value()\"\n [value]=\"value()\"\n [checked]=\"checked()\"\n [name]=\"group()\"\n [disabled]=\"disabled()\"\n (change)=\"handleChange($event)\"\n (focusout)=\"focused.set(false)\"\n (focusin)=\"focused.set(true)\"\n />\n </div>\n\n @if (title()) {\n <span class=\"fw-radio-label vision-h4\"\n >{{ title() }}\n <ng-content></ng-content>\n </span>\n }\n</label>\n", styles: [":host .fw-radio-wrapper{display:flex;align-items:center;width:fit-content;margin:0;cursor:pointer}:host .fw-radio-wrapper .fw-radio-label{margin:0 6px 0 0;color:var(--typography-base)}:host .fw-radio-wrapper .fw-radio-label:empty{display:none}:host .fw-radio-wrapper .fw-radio{height:36px;width:36px;display:flex;align-items:center;justify-content:center;border-radius:50%}:host .fw-radio-wrapper .fw-radio:hover{background-color:var(--slate-hover)}:host .fw-radio-wrapper .fw-radio.focused{background-color:var(--slate-focus)}:host .fw-radio-wrapper .fw-radio input[type=radio]{appearance:none;display:inline-block;width:20px;height:20px;margin:6px;background-clip:content-box;border:2px solid var(--typography-light);background-color:transparent;border-radius:50%;cursor:inherit}:host .fw-radio-wrapper .fw-radio input[type=radio]:checked{width:16px;height:16px;background-color:var(--typography-light);outline:2px solid var(--typography-light);border:2px solid white;margin:8px}:host .fw-radio-wrapper .fw-radio.primary.focused{background-color:var(--primary-focus)}:host .fw-radio-wrapper .fw-radio.primary.focused input[type=radio]{border:2px solid var(--primary-base)}:host .fw-radio-wrapper .fw-radio.primary input[type=radio]:checked{border:2px solid white;background-color:var(--primary-base);outline:2px solid var(--primary-base)}:host .fw-radio-wrapper .fw-radio.secondary.focused{background-color:var(--secondary-focus)}:host .fw-radio-wrapper .fw-radio.secondary.focused input[type=radio]{border:2px solid var(--secondary-base)}:host .fw-radio-wrapper .fw-radio.secondary input[type=radio]:checked{border:2px solid white;background-color:var(--secondary-base);outline:2px solid var(--secondary-base)}:host .fw-radio-wrapper .fw-radio.disabled{cursor:not-allowed}:host .fw-radio-wrapper .fw-radio.disabled input[type=radio]:checked{border:2px solid white;background-color:var(--typography-light);outline:2px solid var(--typography-light)}:host .fw-radio-wrapper .fw-radio.compact{width:30px;height:30px}:host .fw-radio-wrapper .fw-radio.compact input[type=radio]{width:18px;height:18px;margin:5px}:host .fw-radio-wrapper .fw-radio.compact input[type=radio]:checked{width:14px;height:14px;margin:7px}:host .fw-radio-wrapper.disabled{cursor:not-allowed;opacity:.4}:host .fw-radio-wrapper.disabled .fw-radio-label{color:var(--typography-light)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
2807
2807
|
}
|
|
2808
2808
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FwRadioComponent, decorators: [{
|
|
2809
2809
|
type: Component,
|
|
2810
|
-
args: [{ selector: 'fw-radio-button', imports: [NgClass], template: "<
|
|
2810
|
+
args: [{ selector: 'fw-radio-button', imports: [NgClass], template: "<label [ngClass]=\"['fw-radio-wrapper', disabled() ? 'disabled' : '']\">\n <div [ngClass]=\"radioStyles\">\n <input\n type=\"radio\"\n class=\"fw-radio\"\n [id]=\"group() + value()\"\n [value]=\"value()\"\n [checked]=\"checked()\"\n [name]=\"group()\"\n [disabled]=\"disabled()\"\n (change)=\"handleChange($event)\"\n (focusout)=\"focused.set(false)\"\n (focusin)=\"focused.set(true)\"\n />\n </div>\n\n @if (title()) {\n <span class=\"fw-radio-label vision-h4\"\n >{{ title() }}\n <ng-content></ng-content>\n </span>\n }\n</label>\n", styles: [":host .fw-radio-wrapper{display:flex;align-items:center;width:fit-content;margin:0;cursor:pointer}:host .fw-radio-wrapper .fw-radio-label{margin:0 6px 0 0;color:var(--typography-base)}:host .fw-radio-wrapper .fw-radio-label:empty{display:none}:host .fw-radio-wrapper .fw-radio{height:36px;width:36px;display:flex;align-items:center;justify-content:center;border-radius:50%}:host .fw-radio-wrapper .fw-radio:hover{background-color:var(--slate-hover)}:host .fw-radio-wrapper .fw-radio.focused{background-color:var(--slate-focus)}:host .fw-radio-wrapper .fw-radio input[type=radio]{appearance:none;display:inline-block;width:20px;height:20px;margin:6px;background-clip:content-box;border:2px solid var(--typography-light);background-color:transparent;border-radius:50%;cursor:inherit}:host .fw-radio-wrapper .fw-radio input[type=radio]:checked{width:16px;height:16px;background-color:var(--typography-light);outline:2px solid var(--typography-light);border:2px solid white;margin:8px}:host .fw-radio-wrapper .fw-radio.primary.focused{background-color:var(--primary-focus)}:host .fw-radio-wrapper .fw-radio.primary.focused input[type=radio]{border:2px solid var(--primary-base)}:host .fw-radio-wrapper .fw-radio.primary input[type=radio]:checked{border:2px solid white;background-color:var(--primary-base);outline:2px solid var(--primary-base)}:host .fw-radio-wrapper .fw-radio.secondary.focused{background-color:var(--secondary-focus)}:host .fw-radio-wrapper .fw-radio.secondary.focused input[type=radio]{border:2px solid var(--secondary-base)}:host .fw-radio-wrapper .fw-radio.secondary input[type=radio]:checked{border:2px solid white;background-color:var(--secondary-base);outline:2px solid var(--secondary-base)}:host .fw-radio-wrapper .fw-radio.disabled{cursor:not-allowed}:host .fw-radio-wrapper .fw-radio.disabled input[type=radio]:checked{border:2px solid white;background-color:var(--typography-light);outline:2px solid var(--typography-light)}:host .fw-radio-wrapper .fw-radio.compact{width:30px;height:30px}:host .fw-radio-wrapper .fw-radio.compact input[type=radio]{width:18px;height:18px;margin:5px}:host .fw-radio-wrapper .fw-radio.compact input[type=radio]:checked{width:14px;height:14px;margin:7px}:host .fw-radio-wrapper.disabled{cursor:not-allowed;opacity:.4}:host .fw-radio-wrapper.disabled .fw-radio-label{color:var(--typography-light)}\n"] }]
|
|
2811
2811
|
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: false }] }, { type: i0.Output, args: ["groupChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }, { type: i0.Output, args: ["sizeChange"] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }, { type: i0.Output, args: ["colorChange"] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], focused: [{ type: i0.Input, args: [{ isSignal: true, alias: "focused", required: false }] }, { type: i0.Output, args: ["focusedChange"] }], change: [{
|
|
2812
2812
|
type: Output
|
|
2813
2813
|
}] } });
|
|
@@ -7160,6 +7160,17 @@ var FieldType;
|
|
|
7160
7160
|
FieldType["Float"] = "float";
|
|
7161
7161
|
})(FieldType || (FieldType = {}));
|
|
7162
7162
|
|
|
7163
|
+
// Field types with no keystroke-by-keystroke "typing" concept — their value only ever changes
|
|
7164
|
+
// as a single discrete commit (a click/selection), so visibility/requiredWhenVisible rules that
|
|
7165
|
+
// depend on them can and should be recomputed the instant that value changes, rather than
|
|
7166
|
+
// waiting for an unrelated focusout elsewhere on the form (see recomputeIfDiscreteChanged).
|
|
7167
|
+
const DISCRETE_FIELD_TYPES = new Set([
|
|
7168
|
+
FieldType.Checkbox,
|
|
7169
|
+
FieldType.Switch,
|
|
7170
|
+
FieldType.Radio,
|
|
7171
|
+
FieldType.Date,
|
|
7172
|
+
FieldType.Select,
|
|
7173
|
+
]);
|
|
7163
7174
|
/**
|
|
7164
7175
|
* Renders a {@link CustomForm} schema as a reactive form, resolving JSONLogic-driven
|
|
7165
7176
|
* visibility and validation rules for each field.
|
|
@@ -7298,8 +7309,17 @@ class FwFormRendererComponent {
|
|
|
7298
7309
|
const formGroup = new FormGroup(controls, {
|
|
7299
7310
|
validators: (fg) => this.validateFields(fg, fields),
|
|
7300
7311
|
});
|
|
7312
|
+
let previousValues = this.normalizeValues(formGroup.value, fields);
|
|
7301
7313
|
formGroup.valueChanges.pipe(takeUntil(this.formRebuilt$), takeUntilDestroyed(this.destroyRef)).subscribe((values) => {
|
|
7302
7314
|
const normalized = this.normalizeValues(values, fields);
|
|
7315
|
+
if (this.recomputeIfDiscreteChanged(fields, previousValues, normalized)) {
|
|
7316
|
+
// validateFields already ran for this tick (before valueChanges fired) against the
|
|
7317
|
+
// previous requiredByKey — re-run it so errors/form.valid reflect the new required
|
|
7318
|
+
// state now rather than on the next blur. emitEvent: false avoids re-entering this
|
|
7319
|
+
// subscription; the outer updateValueAndValidity still emits the refreshed status.
|
|
7320
|
+
formGroup.updateValueAndValidity({ emitEvent: false });
|
|
7321
|
+
}
|
|
7322
|
+
previousValues = normalized;
|
|
7303
7323
|
if (this.submitted() || fields.some((field) => formGroup.get(field.key)?.touched)) {
|
|
7304
7324
|
// Errors/errorText were just recomputed by the FormGroup validator (validateFields)
|
|
7305
7325
|
// as part of this same value-change tick — emit a new array reference so the
|
|
@@ -7322,8 +7342,9 @@ class FwFormRendererComponent {
|
|
|
7322
7342
|
* re-runs validation so the field that was just blurred shows its errorText immediately
|
|
7323
7343
|
* (validateFields only assigns errorText for touched controls, and the blurred control's
|
|
7324
7344
|
* CVA already marked it touched before this native `focusout` handler runs). Wired to the
|
|
7325
|
-
* form's `focusout` so fields only appear/disappear/error once a field
|
|
7326
|
-
* than on every keystroke.
|
|
7345
|
+
* form's `focusout` so continuous (typed) fields only appear/disappear/error once a field
|
|
7346
|
+
* loses focus, rather than on every keystroke. Discrete fields (radio/select/checkbox/switch/
|
|
7347
|
+
* date) don't need to wait for this — see recomputeIfDiscreteChanged.
|
|
7327
7348
|
*/
|
|
7328
7349
|
onFieldBlur() {
|
|
7329
7350
|
const formGroup = this.form();
|
|
@@ -7331,12 +7352,39 @@ class FwFormRendererComponent {
|
|
|
7331
7352
|
return;
|
|
7332
7353
|
}
|
|
7333
7354
|
const normalized = this.normalizeValues(formGroup.value, this.currentFields);
|
|
7355
|
+
this.applyVisibility(normalized);
|
|
7356
|
+
formGroup.updateValueAndValidity();
|
|
7357
|
+
this.flatFields.set([...this.currentFields]);
|
|
7358
|
+
}
|
|
7359
|
+
/**
|
|
7360
|
+
* Recomputes visibility immediately (independent of focus/blur) when a *discrete* field's
|
|
7361
|
+
* value changed on this tick — e.g. a radio/select/checkbox/switch/date selection. Those
|
|
7362
|
+
* controls commit their value in a single click rather than keystroke-by-keystroke, so there's
|
|
7363
|
+
* no "flicker while typing" concern gating them behind onFieldBlur, unlike text-style inputs.
|
|
7364
|
+
*
|
|
7365
|
+
* This also sidesteps a DOM event-ordering race specific to controls whose native element
|
|
7366
|
+
* fires its own `focusout` (e.g. a radio `<input>`): clicking a different option in the same
|
|
7367
|
+
* group fires `focusout` on the previously-focused option — bubbling up and running
|
|
7368
|
+
* onFieldBlur — before `change` fires on the newly-clicked option and writes its value into
|
|
7369
|
+
* the FormGroup. onFieldBlur alone would therefore always evaluate against the *previous*
|
|
7370
|
+
* selection. Recomputing here, from the value-change itself, evaluates against the value that
|
|
7371
|
+
* actually just changed.
|
|
7372
|
+
*
|
|
7373
|
+
* Returns whether visibility was recomputed, so the caller can re-run validation against the
|
|
7374
|
+
* updated requiredByKey.
|
|
7375
|
+
*/
|
|
7376
|
+
recomputeIfDiscreteChanged(fields, previousValues, normalized) {
|
|
7377
|
+
const discreteChanged = fields.some((field) => DISCRETE_FIELD_TYPES.has(field.type) && previousValues[field.key] !== normalized[field.key]);
|
|
7378
|
+
if (discreteChanged) {
|
|
7379
|
+
this.applyVisibility(normalized);
|
|
7380
|
+
}
|
|
7381
|
+
return discreteChanged;
|
|
7382
|
+
}
|
|
7383
|
+
applyVisibility(normalized) {
|
|
7334
7384
|
const { hide, required, hideSections } = this.computeVisibility(this.currentFields, normalized);
|
|
7335
7385
|
this.hideByKey.set(hide);
|
|
7336
7386
|
this.requiredByKey.set(required);
|
|
7337
7387
|
this.hideSectionByKey.set(hideSections);
|
|
7338
|
-
formGroup.updateValueAndValidity();
|
|
7339
|
-
this.flatFields.set([...this.currentFields]);
|
|
7340
7388
|
}
|
|
7341
7389
|
/**
|
|
7342
7390
|
* Marks every control touched/dirty and triggers validation immediately, then flips
|