@flywheel-io/vision 21.1.1 → 21.1.3
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NgClass, CommonModule, NgStyle, NgTemplateOutlet, SlicePipe, Location } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, input, HostBinding, ChangeDetectionStrategy, Component, output, computed, NgModule, model, signal, effect, HostListener, ViewEncapsulation, EventEmitter, Output, ElementRef, ViewContainerRef, Directive, contentChildren, viewChild, ChangeDetectorRef, ContentChildren, Input, ViewChild, forwardRef, DestroyRef, Injectable, ContentChild, contentChild, NgZone, untracked, ViewChildren, viewChildren, linkedSignal, TemplateRef } from '@angular/core';
|
|
3
|
+
import { inject, input, HostBinding, ChangeDetectionStrategy, Component, output, computed, NgModule, model, signal, effect, HostListener, ViewEncapsulation, EventEmitter, Output, ElementRef, ViewContainerRef, Directive, contentChildren, viewChild, ChangeDetectorRef, ContentChildren, Input, ViewChild, forwardRef, DestroyRef, Injectable, ContentChild, contentChild, NgZone, untracked, DOCUMENT, ViewChildren, viewChildren, linkedSignal, TemplateRef } from '@angular/core';
|
|
4
4
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
5
5
|
import { BehaviorSubject, debounce, timer, distinctUntilChanged, of, Subscription, min, combineLatest, tap, switchMap, map } from 'rxjs';
|
|
6
6
|
import { CdkConnectedOverlay, OverlayModule, Overlay, CdkOverlayOrigin, OverlayContainer } from '@angular/cdk/overlay';
|
|
@@ -3177,10 +3177,26 @@ class FwDialogComponent {
|
|
|
3177
3177
|
return classes;
|
|
3178
3178
|
}
|
|
3179
3179
|
ngOnInit() {
|
|
3180
|
-
|
|
3180
|
+
// `<fw-dialog>` is usable outside an overlay, so there may be no dialogRef at all.
|
|
3181
|
+
const container = this.dialogRef?.containerInstance;
|
|
3182
|
+
if (container) {
|
|
3183
|
+
// Deferred by one microtask on purpose: `_addAriaLabelledBy` mutates the container's
|
|
3184
|
+
// `_ariaLabelledByQueue`, which `CdkDialogContainer` exposes as a host binding. A component's
|
|
3185
|
+
// host bindings are evaluated in its parent view *before* Angular descends into child
|
|
3186
|
+
// components, so mutating the queue synchronously here changes a value the container already
|
|
3187
|
+
// read in the same change-detection pass, which throws NG0100 in dev builds. Same approach as
|
|
3188
|
+
// Angular Material's `MatDialogLayoutSection`.
|
|
3189
|
+
Promise.resolve().then(() => container._addAriaLabelledBy(this.headerId));
|
|
3190
|
+
}
|
|
3181
3191
|
}
|
|
3182
3192
|
ngOnDestroy() {
|
|
3183
|
-
this.dialogRef?.containerInstance
|
|
3193
|
+
const container = this.dialogRef?.containerInstance;
|
|
3194
|
+
if (container) {
|
|
3195
|
+
// Deferred to stay ordered behind the deferred add in `ngOnInit` — otherwise a dialog created
|
|
3196
|
+
// and destroyed within the same task would remove the id before it was ever added and leave
|
|
3197
|
+
// it orphaned in the queue.
|
|
3198
|
+
Promise.resolve().then(() => container._removeAriaLabelledBy(this.headerId));
|
|
3199
|
+
}
|
|
3184
3200
|
}
|
|
3185
3201
|
handleCloseButton() {
|
|
3186
3202
|
this.closeWithAnimation();
|
|
@@ -4324,6 +4340,16 @@ class FwMenuItemComponent {
|
|
|
4324
4340
|
this.focused = model(false, ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ []));
|
|
4325
4341
|
this.selected = model(false, ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
4326
4342
|
this.subscriptions = [];
|
|
4343
|
+
this.isDestroyed = false;
|
|
4344
|
+
}
|
|
4345
|
+
/**
|
|
4346
|
+
* Whether this item's view has been torn down. Owners that iterate their items (`fw-menu`) have to
|
|
4347
|
+
* check this before writing to the item's `model()`s: neither `QueryList` nor a signal query is
|
|
4348
|
+
* emptied by view destruction, so an item can still be reachable from a query after the menu it
|
|
4349
|
+
* lives in was destroyed - writing to it then logs NG0953.
|
|
4350
|
+
*/
|
|
4351
|
+
get destroyed() {
|
|
4352
|
+
return this.isDestroyed;
|
|
4327
4353
|
}
|
|
4328
4354
|
scrollIntoView(options = { behavior: 'smooth', block: 'nearest' }) {
|
|
4329
4355
|
// eslint-disable-next-line @rx-angular/prefer-no-layout-sensitive-apis
|
|
@@ -4341,6 +4367,7 @@ class FwMenuItemComponent {
|
|
|
4341
4367
|
this.updateLayout();
|
|
4342
4368
|
}
|
|
4343
4369
|
ngOnDestroy() {
|
|
4370
|
+
this.isDestroyed = true;
|
|
4344
4371
|
for (const subscription of this.subscriptions) {
|
|
4345
4372
|
subscription.unsubscribe();
|
|
4346
4373
|
}
|
|
@@ -4487,9 +4514,15 @@ class FwMenuComponent {
|
|
|
4487
4514
|
}
|
|
4488
4515
|
writeValue(value) {
|
|
4489
4516
|
this.value.set(value);
|
|
4517
|
+
// Lay the items out BEFORE calling out to any listener. `change` is wired to `fw-select`'s
|
|
4518
|
+
// `handleClick`, which closes the options panel and so synchronously destroys every
|
|
4519
|
+
// `fw-menu-item` rendered in it. View teardown does not empty a `QueryList`, so laying out
|
|
4520
|
+
// afterwards writes to `model()`s on already-destroyed nodes, which logs NG0953 once per item
|
|
4521
|
+
// whose `selected` flag changed. Everything `updateLayout()` reads derives from `this.value()`,
|
|
4522
|
+
// set on the line above, so the resulting layout is identical either way.
|
|
4523
|
+
this.updateLayout();
|
|
4490
4524
|
this.onChange(value);
|
|
4491
4525
|
this.change.emit(value);
|
|
4492
|
-
this.updateLayout();
|
|
4493
4526
|
}
|
|
4494
4527
|
registerOnChange(fn) {
|
|
4495
4528
|
this.onChange = fn;
|
|
@@ -4528,6 +4561,12 @@ class FwMenuComponent {
|
|
|
4528
4561
|
if (this.menuItems) {
|
|
4529
4562
|
const itemRole = this.computeItemRole();
|
|
4530
4563
|
this.menuItems.forEach((item) => {
|
|
4564
|
+
// A destroyed item is still reachable through the QueryList; writing to its models would
|
|
4565
|
+
// emit on a dead OutputRef (NG0953). Belt and braces for any other listener that tears the
|
|
4566
|
+
// menu down from underneath a layout pass.
|
|
4567
|
+
if (item.destroyed) {
|
|
4568
|
+
return;
|
|
4569
|
+
}
|
|
4531
4570
|
item.itemRole.set(itemRole);
|
|
4532
4571
|
const size = this.size();
|
|
4533
4572
|
if (size !== undefined) {
|
|
@@ -4884,7 +4923,7 @@ class FwTextInputComponent {
|
|
|
4884
4923
|
useExisting: FwTextInputComponent,
|
|
4885
4924
|
multi: true,
|
|
4886
4925
|
},
|
|
4887
|
-
], queries: [{ propertyName: "contentChildInputElement", first: true, predicate: ["textInput"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["input"], descendants: true }], ngImport: i0, template: "<div class=\"full-container\" [ngClass]=\"{ disabled: disabled() }\">\n <div class=\"input-container\" [class]=\"size()\">\n @if (!!leftIcon()) {\n @if (useActionableIcons()) {\n <div fwClickableDiv (click)=\"onLeftIconClick()\" class=\"actionable\" [attr.aria-label]=\"leftIconLabel()\">\n <fw-icon>{{ leftIcon() }}</fw-icon>\n </div>\n } @else {\n <fw-icon>{{ leftIcon() }}</fw-icon>\n }\n }\n @if (!!prefix()) {\n <p class=\"vision-p2 context\">{{ prefix() }}</p>\n }\n\n @if (!contentChildInputElement()) {\n <input\n #input\n [type]=\"type()\"\n (input)=\"changeHandler($event)\"\n (blur)=\"blurHandler()\"\n [attr.maxlength]=\"maxLength()\"\n [placeholder]=\"placeholder() || ''\"\n [readOnly]=\"readOnly()\"\n [disabled]=\"disabled()\"\n [autofocus]=\"autofocus()\"\n [autocomplete]=\"autocomplete()\"\n [required]=\"required()\"\n [attr.aria-required]=\"required()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"computedDescribedBy()\"\n [attr.role]=\"role() || null\"\n [attr.aria-expanded]=\"ariaExpanded() === undefined ? null : ariaExpanded()\"\n [attr.aria-controls]=\"ariaControls() || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete() || null\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant() || null\"\n />\n }\n <ng-content select=\"input\"></ng-content>\n @if (!!context()) {\n <p class=\"vision-p2 context\">{{ context() }}</p>\n }\n\n <fw-icon class=\"error-icon\" [fwTooltip]=\"errorInIconTooltip() ? errorText() || '' : ''\">warning-circle</fw-icon>\n @if (!!rightIcon()) {\n @if (useActionableIcons()) {\n <div fwClickableDiv (click)=\"onRightIconClick()\" class=\"actionable\" [attr.aria-label]=\"rightIconLabel()\">\n <fw-icon>{{ rightIcon() }}</fw-icon>\n </div>\n } @else {\n <fw-icon>{{ rightIcon() }}</fw-icon>\n }\n }\n <ng-content></ng-content>\n </div>\n @if (!!helperText()) {\n <p class=\"vision-p4 helper-text\" [id]=\"helperTextId\">{{ helperText() }}</p>\n }\n @if (!!errorText() && !errorInIconTooltip()) {\n <p class=\"vision-p4 error-text\" [id]=\"errorTextId\">{{ errorText() }}</p>\n }\n</div>\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700\";.vision-h1{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:22px}.vision-h2{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:18px}.vision-h3{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:16px}.vision-h4{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:14px}.vision-h5{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:12px;line-height:130%}.vision-h6{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:10px;line-height:120%}.vision-p1{font-size:18px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p2{font-size:14px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p3{font-size:12px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p4{font-size:10px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p5{font-size:8px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-link{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link:hover{text-decoration:none}.vision-link:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link:visited{color:var(--secondary-base)}.vision-link-inherited{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link-inherited:hover{text-decoration:none}.vision-link-inherited:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link-inherited:visited{color:var(--secondary-base)}.vision-link-inherited,.vision-link-inherited:visited{color:inherit}.vision-link-no-visited{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link-no-visited:hover{text-decoration:none}.vision-link-no-visited:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link-no-visited:visited{color:var(--secondary-base)}.vision-link-no-visited:visited{color:var(--primary-base)}.full-container.disabled{cursor:not-allowed}.full-container.disabled fw-icon{cursor:not-allowed!important}.full-container{display:flex;flex-direction:column;line-height:21px}.full-container .input-container{box-sizing:border-box;color:var(--typography-light);background:var(--page-light);display:flex;padding:8px;align-items:center;gap:5px;border-radius:6px;border:1px solid var(--separations-input);font-family:Inter,sans-serif}.full-container .input-container:focus-within{border:1px solid var(--primary-base)}.full-container .input-container input{min-width:0;font-size:14px;flex-grow:1;color:var(--typography-base);background:var(--page-light);border:none}.full-container .input-container input:focus{outline:none;border:none}.full-container .input-container input::placeholder{color:var(--typography-light)}.full-container .input-container .context{color:var(--typography-light)}.full-container .error-icon{display:none}.full-container .helper-text,.full-container .error-text{margin-top:4px;color:var(--typography-light);line-height:13px;margin-left:6px;margin-bottom:0}.full-container .error-text{text-align:left;color:var(--red-base);display:none}.actionable{cursor:pointer}.actionable:hover{color:var(--primary-base);background-color:var(--primary-hover);border-radius:50%}.small{height:30px}.small fw-icon{font-size:18px;min-width:18px;width:18px}.medium{height:36px}.medium fw-icon{font-size:20px;min-width:20px;width:20px}.large{height:40px}.large fw-icon{font-size:24px;min-width:24px;width:24px}:host.errored .input-container,:host.ng-touched.ng-invalid .input-container{border:1px solid var(--red-base)}:host.errored .error-icon,:host.ng-touched.ng-invalid .error-icon{color:var(--red-base);display:inline!important}:host.errored .helper-text,:host.errored .full-container .error-text,:host.ng-touched.ng-invalid .helper-text,:host.ng-touched.ng-invalid .full-container .error-text{display:none}:host.errored .error-text,:host.ng-touched.ng-invalid .error-text{display:block!important}:disabled{opacity:.4;cursor:not-allowed}.disabled .actionable:hover{color:var(--typography-light);background-color:transparent}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: FwIconComponent, selector: "fw-icon", inputs: ["ariaLabel", "size", "color"] }, { kind: "directive", type: FwTooltipDirective, selector: "[fwTooltip]", inputs: ["fwTooltip", "fwTooltipPosition", "fwTooltipMaxWidthPx", "fwTooltipClass", "fwTooltipDelay", "fwTooltipCaret", "fwTooltipEnabled"], outputs: ["fwTooltipChange", "fwTooltipPositionChange", "fwTooltipMaxWidthPxChange", "fwTooltipClassChange", "fwTooltipEnabledChange"] }, { kind: "directive", type: FwClickableDivDirective, selector: "[fwClickableDiv]", inputs: ["fwClickableDivDisabled", "fwClickableDivRole"] }] }); }
|
|
4926
|
+
], queries: [{ propertyName: "contentChildInputElement", first: true, predicate: ["textInput"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["input"], descendants: true }], ngImport: i0, template: "<div class=\"full-container\" [ngClass]=\"{ disabled: disabled() }\">\n <div class=\"input-container\" [class]=\"size()\">\n @if (!!leftIcon()) {\n @if (useActionableIcons()) {\n <div fwClickableDiv (click)=\"onLeftIconClick()\" class=\"actionable\" [attr.aria-label]=\"leftIconLabel()\">\n <fw-icon>{{ leftIcon() }}</fw-icon>\n </div>\n } @else {\n <fw-icon>{{ leftIcon() }}</fw-icon>\n }\n }\n @if (!!prefix()) {\n <p class=\"vision-p2 context\">{{ prefix() }}</p>\n }\n\n @if (!contentChildInputElement()) {\n <input\n #input\n [type]=\"type()\"\n (input)=\"changeHandler($event)\"\n (blur)=\"blurHandler()\"\n [attr.maxlength]=\"maxLength()\"\n [placeholder]=\"placeholder() || ''\"\n [readOnly]=\"readOnly()\"\n [disabled]=\"disabled()\"\n [autofocus]=\"autofocus()\"\n [autocomplete]=\"autocomplete()\"\n [required]=\"required()\"\n [attr.aria-required]=\"required()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"computedDescribedBy()\"\n [attr.role]=\"role() || null\"\n [attr.aria-expanded]=\"ariaExpanded() === undefined ? null : ariaExpanded()\"\n [attr.aria-controls]=\"ariaControls() || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete() || null\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant() || null\"\n />\n }\n <ng-content select=\"input\"></ng-content>\n @if (!!context()) {\n <p class=\"vision-p2 context\">{{ context() }}</p>\n }\n\n <fw-icon class=\"error-icon\" [fwTooltip]=\"errorInIconTooltip() ? errorText() || '' : ''\">warning-circle</fw-icon>\n @if (!!rightIcon()) {\n @if (useActionableIcons()) {\n <div fwClickableDiv (click)=\"onRightIconClick()\" class=\"actionable\" [attr.aria-label]=\"rightIconLabel()\">\n <fw-icon>{{ rightIcon() }}</fw-icon>\n </div>\n } @else {\n <fw-icon>{{ rightIcon() }}</fw-icon>\n }\n }\n <ng-content></ng-content>\n </div>\n @if (!!helperText()) {\n <p class=\"vision-p4 helper-text\" [id]=\"helperTextId\">{{ helperText() }}</p>\n }\n @if (!!errorText() && !errorInIconTooltip()) {\n <p class=\"vision-p4 error-text\" [id]=\"errorTextId\">{{ errorText() }}</p>\n }\n</div>\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700\";.vision-h1{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:22px}.vision-h2{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:18px}.vision-h3{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:16px}.vision-h4{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:14px}.vision-h5{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:12px;line-height:130%}.vision-h6{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:10px;line-height:120%}.vision-p1{font-size:18px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p2{font-size:14px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p3{font-size:12px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p4{font-size:10px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p5{font-size:8px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-link{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link:hover{text-decoration:none}.vision-link:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link:visited{color:var(--secondary-base)}.vision-link-inherited{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link-inherited:hover{text-decoration:none}.vision-link-inherited:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link-inherited:visited{color:var(--secondary-base)}.vision-link-inherited,.vision-link-inherited:visited{color:inherit}.vision-link-no-visited{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link-no-visited:hover{text-decoration:none}.vision-link-no-visited:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link-no-visited:visited{color:var(--secondary-base)}.vision-link-no-visited:visited{color:var(--primary-base)}.full-container.disabled{cursor:not-allowed}.full-container.disabled fw-icon{cursor:not-allowed!important}.full-container{display:flex;flex-direction:column;line-height:21px}.full-container .input-container{box-sizing:border-box;color:var(--typography-light);background:var(--page-light);display:flex;padding:8px;align-items:center;gap:5px;border-radius:6px;border:1px solid var(--separations-input);font-family:Inter,sans-serif}.full-container .input-container:focus-within{border:1px solid var(--primary-base)}.full-container .input-container input{min-width:0;font-size:14px;flex-grow:1;color:var(--typography-base);background:var(--page-light);border:none}.full-container .input-container input:focus{outline:none;border:none}.full-container .input-container input::placeholder{color:var(--typography-light)}.full-container .input-container .context{color:var(--typography-light)}.full-container .error-icon{display:none}.full-container .helper-text,.full-container .error-text{margin-top:4px;color:var(--typography-light);line-height:13px;margin-left:6px;margin-bottom:0}.full-container .error-text{text-align:left;color:var(--red-base);display:none}.actionable{cursor:pointer}.actionable:hover{color:var(--primary-base);background-color:var(--primary-hover);border-radius:50%}.small{height:30px}.small fw-icon{font-size:18px;min-width:18px;width:18px}.medium{height:36px}.medium fw-icon{font-size:20px;min-width:20px;width:20px}.large{height:40px}.large fw-icon{font-size:24px;min-width:24px;width:24px}:host.errored .input-container,:host.ng-touched.ng-invalid .input-container{border:1px solid var(--red-base)}:host.errored .input-container:focus-within,:host.ng-touched.ng-invalid .input-container:focus-within{border:1px solid var(--red-base)}:host.errored .error-icon,:host.ng-touched.ng-invalid .error-icon{color:var(--red-base);display:inline!important}:host.errored .helper-text,:host.errored .full-container .error-text,:host.ng-touched.ng-invalid .helper-text,:host.ng-touched.ng-invalid .full-container .error-text{display:none}:host.errored .error-text,:host.ng-touched.ng-invalid .error-text{display:block!important}:disabled{opacity:.4;cursor:not-allowed}.disabled .actionable:hover{color:var(--typography-light);background-color:transparent}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: FwIconComponent, selector: "fw-icon", inputs: ["ariaLabel", "size", "color"] }, { kind: "directive", type: FwTooltipDirective, selector: "[fwTooltip]", inputs: ["fwTooltip", "fwTooltipPosition", "fwTooltipMaxWidthPx", "fwTooltipClass", "fwTooltipDelay", "fwTooltipCaret", "fwTooltipEnabled"], outputs: ["fwTooltipChange", "fwTooltipPositionChange", "fwTooltipMaxWidthPxChange", "fwTooltipClassChange", "fwTooltipEnabledChange"] }, { kind: "directive", type: FwClickableDivDirective, selector: "[fwClickableDiv]", inputs: ["fwClickableDivDisabled", "fwClickableDivRole"] }] }); }
|
|
4888
4927
|
}
|
|
4889
4928
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FwTextInputComponent, decorators: [{
|
|
4890
4929
|
type: Component,
|
|
@@ -4897,7 +4936,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
4897
4936
|
], host: {
|
|
4898
4937
|
'[class.errored]': 'error()',
|
|
4899
4938
|
'[style.width]': 'width()',
|
|
4900
|
-
}, imports: [NgClass, FwIconComponent, FwTooltipDirective, FwClickableDivDirective], template: "<div class=\"full-container\" [ngClass]=\"{ disabled: disabled() }\">\n <div class=\"input-container\" [class]=\"size()\">\n @if (!!leftIcon()) {\n @if (useActionableIcons()) {\n <div fwClickableDiv (click)=\"onLeftIconClick()\" class=\"actionable\" [attr.aria-label]=\"leftIconLabel()\">\n <fw-icon>{{ leftIcon() }}</fw-icon>\n </div>\n } @else {\n <fw-icon>{{ leftIcon() }}</fw-icon>\n }\n }\n @if (!!prefix()) {\n <p class=\"vision-p2 context\">{{ prefix() }}</p>\n }\n\n @if (!contentChildInputElement()) {\n <input\n #input\n [type]=\"type()\"\n (input)=\"changeHandler($event)\"\n (blur)=\"blurHandler()\"\n [attr.maxlength]=\"maxLength()\"\n [placeholder]=\"placeholder() || ''\"\n [readOnly]=\"readOnly()\"\n [disabled]=\"disabled()\"\n [autofocus]=\"autofocus()\"\n [autocomplete]=\"autocomplete()\"\n [required]=\"required()\"\n [attr.aria-required]=\"required()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"computedDescribedBy()\"\n [attr.role]=\"role() || null\"\n [attr.aria-expanded]=\"ariaExpanded() === undefined ? null : ariaExpanded()\"\n [attr.aria-controls]=\"ariaControls() || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete() || null\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant() || null\"\n />\n }\n <ng-content select=\"input\"></ng-content>\n @if (!!context()) {\n <p class=\"vision-p2 context\">{{ context() }}</p>\n }\n\n <fw-icon class=\"error-icon\" [fwTooltip]=\"errorInIconTooltip() ? errorText() || '' : ''\">warning-circle</fw-icon>\n @if (!!rightIcon()) {\n @if (useActionableIcons()) {\n <div fwClickableDiv (click)=\"onRightIconClick()\" class=\"actionable\" [attr.aria-label]=\"rightIconLabel()\">\n <fw-icon>{{ rightIcon() }}</fw-icon>\n </div>\n } @else {\n <fw-icon>{{ rightIcon() }}</fw-icon>\n }\n }\n <ng-content></ng-content>\n </div>\n @if (!!helperText()) {\n <p class=\"vision-p4 helper-text\" [id]=\"helperTextId\">{{ helperText() }}</p>\n }\n @if (!!errorText() && !errorInIconTooltip()) {\n <p class=\"vision-p4 error-text\" [id]=\"errorTextId\">{{ errorText() }}</p>\n }\n</div>\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700\";.vision-h1{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:22px}.vision-h2{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:18px}.vision-h3{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:16px}.vision-h4{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:14px}.vision-h5{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:12px;line-height:130%}.vision-h6{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:10px;line-height:120%}.vision-p1{font-size:18px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p2{font-size:14px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p3{font-size:12px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p4{font-size:10px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p5{font-size:8px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-link{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link:hover{text-decoration:none}.vision-link:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link:visited{color:var(--secondary-base)}.vision-link-inherited{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link-inherited:hover{text-decoration:none}.vision-link-inherited:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link-inherited:visited{color:var(--secondary-base)}.vision-link-inherited,.vision-link-inherited:visited{color:inherit}.vision-link-no-visited{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link-no-visited:hover{text-decoration:none}.vision-link-no-visited:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link-no-visited:visited{color:var(--secondary-base)}.vision-link-no-visited:visited{color:var(--primary-base)}.full-container.disabled{cursor:not-allowed}.full-container.disabled fw-icon{cursor:not-allowed!important}.full-container{display:flex;flex-direction:column;line-height:21px}.full-container .input-container{box-sizing:border-box;color:var(--typography-light);background:var(--page-light);display:flex;padding:8px;align-items:center;gap:5px;border-radius:6px;border:1px solid var(--separations-input);font-family:Inter,sans-serif}.full-container .input-container:focus-within{border:1px solid var(--primary-base)}.full-container .input-container input{min-width:0;font-size:14px;flex-grow:1;color:var(--typography-base);background:var(--page-light);border:none}.full-container .input-container input:focus{outline:none;border:none}.full-container .input-container input::placeholder{color:var(--typography-light)}.full-container .input-container .context{color:var(--typography-light)}.full-container .error-icon{display:none}.full-container .helper-text,.full-container .error-text{margin-top:4px;color:var(--typography-light);line-height:13px;margin-left:6px;margin-bottom:0}.full-container .error-text{text-align:left;color:var(--red-base);display:none}.actionable{cursor:pointer}.actionable:hover{color:var(--primary-base);background-color:var(--primary-hover);border-radius:50%}.small{height:30px}.small fw-icon{font-size:18px;min-width:18px;width:18px}.medium{height:36px}.medium fw-icon{font-size:20px;min-width:20px;width:20px}.large{height:40px}.large fw-icon{font-size:24px;min-width:24px;width:24px}:host.errored .input-container,:host.ng-touched.ng-invalid .input-container{border:1px solid var(--red-base)}:host.errored .error-icon,:host.ng-touched.ng-invalid .error-icon{color:var(--red-base);display:inline!important}:host.errored .helper-text,:host.errored .full-container .error-text,:host.ng-touched.ng-invalid .helper-text,:host.ng-touched.ng-invalid .full-container .error-text{display:none}:host.errored .error-text,:host.ng-touched.ng-invalid .error-text{display:block!important}:disabled{opacity:.4;cursor:not-allowed}.disabled .actionable:hover{color:var(--typography-light);background-color:transparent}\n"] }]
|
|
4939
|
+
}, imports: [NgClass, FwIconComponent, FwTooltipDirective, FwClickableDivDirective], template: "<div class=\"full-container\" [ngClass]=\"{ disabled: disabled() }\">\n <div class=\"input-container\" [class]=\"size()\">\n @if (!!leftIcon()) {\n @if (useActionableIcons()) {\n <div fwClickableDiv (click)=\"onLeftIconClick()\" class=\"actionable\" [attr.aria-label]=\"leftIconLabel()\">\n <fw-icon>{{ leftIcon() }}</fw-icon>\n </div>\n } @else {\n <fw-icon>{{ leftIcon() }}</fw-icon>\n }\n }\n @if (!!prefix()) {\n <p class=\"vision-p2 context\">{{ prefix() }}</p>\n }\n\n @if (!contentChildInputElement()) {\n <input\n #input\n [type]=\"type()\"\n (input)=\"changeHandler($event)\"\n (blur)=\"blurHandler()\"\n [attr.maxlength]=\"maxLength()\"\n [placeholder]=\"placeholder() || ''\"\n [readOnly]=\"readOnly()\"\n [disabled]=\"disabled()\"\n [autofocus]=\"autofocus()\"\n [autocomplete]=\"autocomplete()\"\n [required]=\"required()\"\n [attr.aria-required]=\"required()\"\n [attr.aria-invalid]=\"error() ? true : null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-describedby]=\"computedDescribedBy()\"\n [attr.role]=\"role() || null\"\n [attr.aria-expanded]=\"ariaExpanded() === undefined ? null : ariaExpanded()\"\n [attr.aria-controls]=\"ariaControls() || null\"\n [attr.aria-autocomplete]=\"ariaAutocomplete() || null\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant() || null\"\n />\n }\n <ng-content select=\"input\"></ng-content>\n @if (!!context()) {\n <p class=\"vision-p2 context\">{{ context() }}</p>\n }\n\n <fw-icon class=\"error-icon\" [fwTooltip]=\"errorInIconTooltip() ? errorText() || '' : ''\">warning-circle</fw-icon>\n @if (!!rightIcon()) {\n @if (useActionableIcons()) {\n <div fwClickableDiv (click)=\"onRightIconClick()\" class=\"actionable\" [attr.aria-label]=\"rightIconLabel()\">\n <fw-icon>{{ rightIcon() }}</fw-icon>\n </div>\n } @else {\n <fw-icon>{{ rightIcon() }}</fw-icon>\n }\n }\n <ng-content></ng-content>\n </div>\n @if (!!helperText()) {\n <p class=\"vision-p4 helper-text\" [id]=\"helperTextId\">{{ helperText() }}</p>\n }\n @if (!!errorText() && !errorInIconTooltip()) {\n <p class=\"vision-p4 error-text\" [id]=\"errorTextId\">{{ errorText() }}</p>\n }\n</div>\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700\";.vision-h1{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:22px}.vision-h2{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:18px}.vision-h3{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:16px}.vision-h4{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:14px}.vision-h5{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:12px;line-height:130%}.vision-h6{font-family:Inter,sans-serif;color:var(--typography-base);font-weight:500;font-size:10px;line-height:120%}.vision-p1{font-size:18px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p2{font-size:14px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p3{font-size:12px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p4{font-size:10px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-p5{font-size:8px;font-family:Inter,sans-serif;color:var(--typography-base);font-weight:400}.vision-link{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link:hover{text-decoration:none}.vision-link:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link:visited{color:var(--secondary-base)}.vision-link-inherited{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link-inherited:hover{text-decoration:none}.vision-link-inherited:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link-inherited:visited{color:var(--secondary-base)}.vision-link-inherited,.vision-link-inherited:visited{color:inherit}.vision-link-no-visited{text-decoration:underline;color:var(--primary-base);cursor:pointer}.vision-link-no-visited:hover{text-decoration:none}.vision-link-no-visited:active{text-decoration:none;outline:2px solid var(--primary-dark);border-radius:4px}.vision-link-no-visited:visited{color:var(--secondary-base)}.vision-link-no-visited:visited{color:var(--primary-base)}.full-container.disabled{cursor:not-allowed}.full-container.disabled fw-icon{cursor:not-allowed!important}.full-container{display:flex;flex-direction:column;line-height:21px}.full-container .input-container{box-sizing:border-box;color:var(--typography-light);background:var(--page-light);display:flex;padding:8px;align-items:center;gap:5px;border-radius:6px;border:1px solid var(--separations-input);font-family:Inter,sans-serif}.full-container .input-container:focus-within{border:1px solid var(--primary-base)}.full-container .input-container input{min-width:0;font-size:14px;flex-grow:1;color:var(--typography-base);background:var(--page-light);border:none}.full-container .input-container input:focus{outline:none;border:none}.full-container .input-container input::placeholder{color:var(--typography-light)}.full-container .input-container .context{color:var(--typography-light)}.full-container .error-icon{display:none}.full-container .helper-text,.full-container .error-text{margin-top:4px;color:var(--typography-light);line-height:13px;margin-left:6px;margin-bottom:0}.full-container .error-text{text-align:left;color:var(--red-base);display:none}.actionable{cursor:pointer}.actionable:hover{color:var(--primary-base);background-color:var(--primary-hover);border-radius:50%}.small{height:30px}.small fw-icon{font-size:18px;min-width:18px;width:18px}.medium{height:36px}.medium fw-icon{font-size:20px;min-width:20px;width:20px}.large{height:40px}.large fw-icon{font-size:24px;min-width:24px;width:24px}:host.errored .input-container,:host.ng-touched.ng-invalid .input-container{border:1px solid var(--red-base)}:host.errored .input-container:focus-within,:host.ng-touched.ng-invalid .input-container:focus-within{border:1px solid var(--red-base)}:host.errored .error-icon,:host.ng-touched.ng-invalid .error-icon{color:var(--red-base);display:inline!important}:host.errored .helper-text,:host.errored .full-container .error-text,:host.ng-touched.ng-invalid .helper-text,:host.ng-touched.ng-invalid .full-container .error-text{display:none}:host.errored .error-text,:host.ng-touched.ng-invalid .error-text{display:block!important}:disabled{opacity:.4;cursor:not-allowed}.disabled .actionable:hover{color:var(--typography-light);background-color:transparent}\n"] }]
|
|
4901
4940
|
}], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], useActionableIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "useActionableIcons", required: false }] }], leftIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "leftIcon", required: false }] }], rightIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "rightIcon", required: false }] }], leftIconLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "leftIconLabel", required: false }] }], rightIconLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rightIconLabel", required: false }] }], prefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefix", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], ariaExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaExpanded", required: false }] }], ariaControls: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaControls", required: false }] }], ariaAutocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaAutocomplete", required: false }] }], ariaActiveDescendant: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaActiveDescendant", required: false }] }], helperText: [{ type: i0.Input, args: [{ isSignal: true, alias: "helperText", required: false }] }], errorText: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorText", required: false }] }], errorInIconTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorInIconTooltip", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], autofocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autofocus", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], value: [{
|
|
4902
4941
|
type: Input
|
|
4903
4942
|
}], inputRef: [{
|
|
@@ -5059,6 +5098,11 @@ class FwMenuContainerComponent {
|
|
|
5059
5098
|
constructor() {
|
|
5060
5099
|
this.sanitizer = inject(DomSanitizer);
|
|
5061
5100
|
this.ngZone = inject(NgZone);
|
|
5101
|
+
/**
|
|
5102
|
+
* The container's own element. Exposed because this component is usually rendered into a cdk
|
|
5103
|
+
* overlay, so a consumer holding the instance has no other way to reach the rendered panel.
|
|
5104
|
+
*/
|
|
5105
|
+
this.hostElement = inject(ElementRef).nativeElement;
|
|
5062
5106
|
this.menuWrapperRef = viewChild('menuWrapper', { ...(ngDevMode ? { debugName: "menuWrapperRef" } : /* istanbul ignore next */ {}), read: HTMLElement });
|
|
5063
5107
|
this.canScrollUp = signal(false, ...(ngDevMode ? [{ debugName: "canScrollUp" }] : /* istanbul ignore next */ []));
|
|
5064
5108
|
this.canScrollDown = signal(false, ...(ngDevMode ? [{ debugName: "canScrollDown" }] : /* istanbul ignore next */ []));
|
|
@@ -5831,6 +5875,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
5831
5875
|
args: ['click']
|
|
5832
5876
|
}] } });
|
|
5833
5877
|
|
|
5878
|
+
/**
|
|
5879
|
+
* Whether an event originated on, or inside, any of the given elements.
|
|
5880
|
+
*
|
|
5881
|
+
* Shared by `fw-select` and `fw-multi-select` so that both decide "inside vs outside" identically -
|
|
5882
|
+
* the trigger lives in the component's own view while the options panel is rendered into a cdk
|
|
5883
|
+
* overlay somewhere else in the document, so containment has to be checked against both.
|
|
5884
|
+
*/
|
|
5885
|
+
function isEventInside(event, ...elements) {
|
|
5886
|
+
const candidates = elements.filter((element) => Boolean(element));
|
|
5887
|
+
if (candidates.length === 0) {
|
|
5888
|
+
return false;
|
|
5889
|
+
}
|
|
5890
|
+
// The path is resolved at dispatch time, so it still holds up for targets that a handler earlier
|
|
5891
|
+
// in the chain has already detached from the DOM
|
|
5892
|
+
const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
|
|
5893
|
+
if (path.length > 0) {
|
|
5894
|
+
return candidates.some((element) => path.includes(element));
|
|
5895
|
+
}
|
|
5896
|
+
const target = event.target;
|
|
5897
|
+
if (!(target instanceof Node)) {
|
|
5898
|
+
return false;
|
|
5899
|
+
}
|
|
5900
|
+
return candidates.some((element) => element.contains(target));
|
|
5901
|
+
}
|
|
5902
|
+
|
|
5834
5903
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5835
5904
|
/**
|
|
5836
5905
|
* Form control component for selecting multiple options from a dropdown, displaying selected values as chips
|
|
@@ -5839,7 +5908,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
5839
5908
|
class FwMultiSelectMenuComponent {
|
|
5840
5909
|
constructor() {
|
|
5841
5910
|
this.elementRef = inject(ElementRef);
|
|
5911
|
+
this.document = inject(DOCUMENT);
|
|
5842
5912
|
this.listboxId = inject(_IdGenerator).getId('fw-multi-select-listbox-');
|
|
5913
|
+
this.documentClickListener = (event) => this.outsideClick(event);
|
|
5843
5914
|
// options
|
|
5844
5915
|
this.options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
5845
5916
|
this.valueProperty = input('value', ...(ngDevMode ? [{ debugName: "valueProperty" }] : /* istanbul ignore next */ []));
|
|
@@ -5864,6 +5935,12 @@ class FwMultiSelectMenuComponent {
|
|
|
5864
5935
|
this.minOptionsHeight = input(...(ngDevMode ? [undefined, { debugName: "minOptionsHeight" }] : /* istanbul ignore next */ []));
|
|
5865
5936
|
this.maxOptionsHeight = input('400px', ...(ngDevMode ? [{ debugName: "maxOptionsHeight" }] : /* istanbul ignore next */ []));
|
|
5866
5937
|
this.size = input('medium', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
5938
|
+
/**
|
|
5939
|
+
* The control itself, which is only as wide as `width()`. The host element is a block element and
|
|
5940
|
+
* so can be considerably wider, and a click in that gap is an outside click.
|
|
5941
|
+
*/
|
|
5942
|
+
this.wrapper = viewChild('wrapper', { ...(ngDevMode ? { debugName: "wrapper" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
5943
|
+
/** The options panel, `undefined` while it is closed since it only exists inside the cdk overlay */
|
|
5867
5944
|
this.menuFilter = viewChild(FwMenuContainerComponent, ...(ngDevMode ? [{ debugName: "menuFilter" }] : /* istanbul ignore next */ []));
|
|
5868
5945
|
// need this for the template
|
|
5869
5946
|
this.createArray = (d) => Array.from(d);
|
|
@@ -5874,7 +5951,6 @@ class FwMultiSelectMenuComponent {
|
|
|
5874
5951
|
this.focusedIndex = signal(0, ...(ngDevMode ? [{ debugName: "focusedIndex" }] : /* istanbul ignore next */ []));
|
|
5875
5952
|
this.touched = false;
|
|
5876
5953
|
this.subscriptions = [];
|
|
5877
|
-
this._isOpen = false;
|
|
5878
5954
|
// eslint-disable-next-line @angular-eslint/no-output-native
|
|
5879
5955
|
this.change = new EventEmitter();
|
|
5880
5956
|
this.defaultFilter = (filter, menuItems) => {
|
|
@@ -5935,7 +6011,7 @@ class FwMultiSelectMenuComponent {
|
|
|
5935
6011
|
/* eslint-enable */
|
|
5936
6012
|
// needs to be an arrow function to lock the scope
|
|
5937
6013
|
this.handleKeyDown = (event) => {
|
|
5938
|
-
const handler = this.
|
|
6014
|
+
const handler = this.isMenuOpen()
|
|
5939
6015
|
? this.keyboardActionMap[event.key]
|
|
5940
6016
|
: this.closedTriggerKeyboardActionsMap[event.key];
|
|
5941
6017
|
if (handler) {
|
|
@@ -5944,18 +6020,35 @@ class FwMultiSelectMenuComponent {
|
|
|
5944
6020
|
}
|
|
5945
6021
|
};
|
|
5946
6022
|
}
|
|
6023
|
+
/**
|
|
6024
|
+
* Closes the options panel when a click lands outside of both the trigger and the panel.
|
|
6025
|
+
*
|
|
6026
|
+
* Registered by hand instead of through `@HostListener('document:click')` so that it runs in the
|
|
6027
|
+
* capture phase: a number of components in the library (`fw-menu-item`, `fw-chip`, `fw-navbar-item`,
|
|
6028
|
+
* the dialog backdrop, ...) call `stopPropagation()` on click, which would otherwise keep this
|
|
6029
|
+
* listener from ever seeing the click and leave the panel open.
|
|
6030
|
+
*/
|
|
5947
6031
|
outsideClick(event) {
|
|
5948
|
-
if (this.
|
|
5949
|
-
|
|
5950
|
-
if (clickedInside) {
|
|
5951
|
-
return;
|
|
5952
|
-
}
|
|
5953
|
-
this.trigger.close();
|
|
5954
|
-
this._isOpen = false;
|
|
5955
|
-
}
|
|
5956
|
-
if (this.trigger && this.trigger.isOpen()) {
|
|
5957
|
-
this._isOpen = true;
|
|
6032
|
+
if (!this.isMenuOpen() || this.isEventInsideSelect(event)) {
|
|
6033
|
+
return;
|
|
5958
6034
|
}
|
|
6035
|
+
this.onTouched();
|
|
6036
|
+
this.trigger.close();
|
|
6037
|
+
}
|
|
6038
|
+
/**
|
|
6039
|
+
* Whether the options panel is currently open. The single place this class asks that question -
|
|
6040
|
+
* the trigger owns the state, so there is no local flag to keep in sync.
|
|
6041
|
+
*
|
|
6042
|
+
* ! Deliberately a plain method, not a `computed()` ! The trigger reports its state from the
|
|
6043
|
+
* overlay ref rather than from a signal, so a computed would memoize the first answer and stop
|
|
6044
|
+
* tracking, silently freezing `aria-expanded`.
|
|
6045
|
+
*/
|
|
6046
|
+
isMenuOpen() {
|
|
6047
|
+
return this.trigger?.isOpen() ?? false;
|
|
6048
|
+
}
|
|
6049
|
+
/** Whether an event originated on this select's control box or inside its rendered options panel */
|
|
6050
|
+
isEventInsideSelect(event) {
|
|
6051
|
+
return isEventInside(event, this.wrapper()?.nativeElement, this.menuFilter()?.hostElement);
|
|
5959
6052
|
}
|
|
5960
6053
|
get value() {
|
|
5961
6054
|
return this._value;
|
|
@@ -5978,7 +6071,11 @@ class FwMultiSelectMenuComponent {
|
|
|
5978
6071
|
writeValue(value) {
|
|
5979
6072
|
this.value = value;
|
|
5980
6073
|
}
|
|
6074
|
+
ngOnInit() {
|
|
6075
|
+
this.document.addEventListener('click', this.documentClickListener, true);
|
|
6076
|
+
}
|
|
5981
6077
|
ngOnDestroy() {
|
|
6078
|
+
this.document.removeEventListener('click', this.documentClickListener, true);
|
|
5982
6079
|
for (const subscription of this.subscriptions) {
|
|
5983
6080
|
subscription.unsubscribe();
|
|
5984
6081
|
}
|
|
@@ -6117,13 +6214,13 @@ class FwMultiSelectMenuComponent {
|
|
|
6117
6214
|
}
|
|
6118
6215
|
}
|
|
6119
6216
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FwMultiSelectMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6120
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FwMultiSelectMenuComponent, isStandalone: true, selector: "fw-multi-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, valueProperty: { classPropertyName: "valueProperty", publicName: "valueProperty", isSignal: true, isRequired: false, transformFunction: null }, titleProperty: { classPropertyName: "titleProperty", publicName: "titleProperty", isSignal: true, isRequired: false, transformFunction: null }, iconProperty: { classPropertyName: "iconProperty", publicName: "iconProperty", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, useCheckbox: { classPropertyName: "useCheckbox", publicName: "useCheckbox", isSignal: true, isRequired: false, transformFunction: null }, closeOnSelect: { classPropertyName: "closeOnSelect", publicName: "closeOnSelect", isSignal: true, isRequired: false, transformFunction: null }, maxSelectedShown: { classPropertyName: "maxSelectedShown", publicName: "maxSelectedShown", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, showSelectionInfo: { classPropertyName: "showSelectionInfo", publicName: "showSelectionInfo", isSignal: true, isRequired: false, transformFunction: null }, filterItemsOnSelect: { classPropertyName: "filterItemsOnSelect", publicName: "filterItemsOnSelect", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, optionsWidth: { classPropertyName: "optionsWidth", publicName: "optionsWidth", isSignal: true, isRequired: false, transformFunction: null }, minOptionsHeight: { classPropertyName: "minOptionsHeight", publicName: "minOptionsHeight", isSignal: true, isRequired: false, transformFunction: null }, maxOptionsHeight: { classPropertyName: "maxOptionsHeight", publicName: "maxOptionsHeight", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, filterFn: { classPropertyName: "filterFn", publicName: "filterFn", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", change: "change" }, host: { listeners: { "
|
|
6217
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FwMultiSelectMenuComponent, isStandalone: true, selector: "fw-multi-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, valueProperty: { classPropertyName: "valueProperty", publicName: "valueProperty", isSignal: true, isRequired: false, transformFunction: null }, titleProperty: { classPropertyName: "titleProperty", publicName: "titleProperty", isSignal: true, isRequired: false, transformFunction: null }, iconProperty: { classPropertyName: "iconProperty", publicName: "iconProperty", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, useCheckbox: { classPropertyName: "useCheckbox", publicName: "useCheckbox", isSignal: true, isRequired: false, transformFunction: null }, closeOnSelect: { classPropertyName: "closeOnSelect", publicName: "closeOnSelect", isSignal: true, isRequired: false, transformFunction: null }, maxSelectedShown: { classPropertyName: "maxSelectedShown", publicName: "maxSelectedShown", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, showSelectionInfo: { classPropertyName: "showSelectionInfo", publicName: "showSelectionInfo", isSignal: true, isRequired: false, transformFunction: null }, filterItemsOnSelect: { classPropertyName: "filterItemsOnSelect", publicName: "filterItemsOnSelect", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, optionsWidth: { classPropertyName: "optionsWidth", publicName: "optionsWidth", isSignal: true, isRequired: false, transformFunction: null }, minOptionsHeight: { classPropertyName: "minOptionsHeight", publicName: "minOptionsHeight", isSignal: true, isRequired: false, transformFunction: null }, maxOptionsHeight: { classPropertyName: "maxOptionsHeight", publicName: "maxOptionsHeight", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, filterFn: { classPropertyName: "filterFn", publicName: "filterFn", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", change: "change" }, host: { listeners: { "keydown": "handleKeyDown($event)" }, properties: { "tabIndex": "this.tabIndex", "class.disabled": "this.disabledClass" } }, providers: [
|
|
6121
6218
|
{
|
|
6122
6219
|
provide: NG_VALUE_ACCESSOR,
|
|
6123
6220
|
useExisting: forwardRef(() => FwMultiSelectMenuComponent),
|
|
6124
6221
|
multi: true,
|
|
6125
6222
|
},
|
|
6126
|
-
], queries: [{ propertyName: "customMenuItems", predicate: FwMenuItemComponent, descendants: true }], viewQueries: [{ propertyName: "menuFilter", first: true, predicate: FwMenuContainerComponent, descendants: true, isSignal: true }, { propertyName: "trigger", first: true, predicate: CdkMenuTrigger, descendants: true }, { propertyName: "menu", first: true, predicate: FwMenuComponent, descendants: true }, { propertyName: "renderedMenuItems", predicate: FwMenuItemComponent, descendants: true }], ngImport: i0, template: "<div [ngStyle]=\"{ width: width() }\" #wrapper>\n <div\n fwMenuRegister\n class=\"chip-grid\"\n [ngClass]=\"[size()]\"\n [ngStyle]=\"{ minHeight: minHeight(), maxHeight: maxHeight() }\"\n [cdkMenuTriggerFor]=\"selectMenu\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"trigger?.isOpen() || false\"\n [attr.aria-controls]=\"listboxId\"\n >\n @if (value.length === 0) {\n <span class=\"placeholder\">{{ placeholder() }}</span>\n }\n\n <!-- CHIPS -->\n @if (value && value.length > 0) {\n <div class=\"inner-chip-grid\">\n @for (chip of selectedOptions | slice: 0 : maxSelectedShown(); track chip) {\n <fw-chip\n [title]=\"chip[titleProperty()]\"\n [value]=\"chip[valueProperty()]\"\n [icon]=\"chip[iconProperty()]\"\n color=\"primary\"\n [showClose]=\"true\"\n (close)=\"handleChipClose(chip)\"\n [selectable]=\"false\"\n >\n </fw-chip>\n }\n @if (maxSelectedShown() === 0 && value.length > 0) {\n <span class=\"selected-text\"> {{ value.length }} selected </span>\n }\n @if (value.length > maxSelectedShown() && maxSelectedShown() > 0) {\n <span class=\"max-exceeded\"> +{{ value.length - maxSelectedShown() }} more </span>\n }\n </div>\n }\n\n @if (showClear() && value.length > 0) {\n <fw-icon (click)=\"updateValue([])\">close</fw-icon>\n }\n <fw-icon>chevron-down</fw-icon>\n </div>\n\n <!-- MENU -->\n <ng-template #selectMenu>\n <fw-menu-filter\n [keyHandler]=\"handleKeyDown\"\n [showFilter]=\"showFilter()\"\n [focusFilterOnMount]=\"showFilter()\"\n [width]=\"optionsWidth() || wrapper.offsetWidth - 2 + 'px'\"\n [additionalMenuItems]=\"this.customMenuItems.toArray()\"\n [emptyText]=\"emptyText()\"\n [maxHeight]=\"maxOptionsHeight()\"\n [minHeight]=\"minOptionsHeight()\"\n [filterFn]=\"filterFn()\"\n (filteredMenuItemChange)=\"displayedOptions.set($event)\"\n >\n @if (showSelectionInfo()) {\n <div class=\"filter-content\">\n <p>{{ value.length }} selections</p>\n <fw-button variant=\"ghost\" (click)=\"updateValue([])\">Clear</fw-button>\n </div>\n }\n <fw-menu\n role=\"listbox\"\n [id]=\"listboxId\"\n [multiSelect]=\"true\"\n [disabled]=\"disabled()\"\n [value]=\"createArray(selectedValues)\"\n (change)=\"handleMenuChange($event)\"\n >\n @if (customMenuItems.length === 0) {\n @for (item of options(); track item) {\n <fw-menu-item\n [title]=\"item[titleProperty()]\"\n [value]=\"item[valueProperty()]\"\n [icon]=\"item[iconProperty()]\"\n [multiSelect]=\"true\"\n [selected]=\"value.includes(item[valueProperty()])\"\n [showCheckbox]=\"useCheckbox()\"\n (mouseenter)=\"setFocusedIndex(item)\"\n >\n </fw-menu-item>\n }\n }\n <ng-content\n ngProjectAs=\"custom-menu-items\"\n select=\"[fw-menu-item, fw-menu-separator, fw-menu-item-group, fw-menu-header]\"\n ></ng-content>\n </fw-menu>\n </fw-menu-filter>\n </ng-template>\n</div>\n", styles: [":host{pointer-events:auto}:host.disabled{opacity:.4;cursor:not-allowed}:host.disabled>div{pointer-events:none}:host .placeholder{color:var(--typography-light);font-size:14px;padding-left:4px;text-wrap:nowrap;text-overflow:ellipsis;overflow:hidden}:host .chip-grid{min-height:36px;width:100%;box-sizing:border-box;color:var(--typography-light);background:var(--page-light);display:flex;justify-content:space-between;padding:4px;align-items:center;border-radius:6px;overflow:hidden;border:1px solid var(--separations-input);cursor:pointer;font-family:Inter,sans-serif}:host .chip-grid.small{min-height:30px;padding:2px}:host .chip-grid.large{min-height:40px;padding:6px}:host .chip-grid:focus-within{border-color:var(--primary-base)}:host .chip-grid .inner-chip-grid{display:flex;flex-wrap:wrap;gap:4px;overflow:hidden auto;align-items:center;flex:1}:host .chip-grid .inner-chip-grid input{display:inline-flex;width:10px;flex-grow:1}:host .chip-grid fw-icon{font-size:20px}:host .chip-grid fw-icon:hover{color:var(--primary-base)}:host .chip-grid .selected-text{color:var(--typography-base);font-size:14px;padding:0 2px 0 4px;text-wrap:nowrap;text-overflow:ellipsis;overflow:hidden}:host .chip-grid .max-exceeded{font-size:12px}.filter-content{display:flex;align-items:center;justify-content:space-between;padding-left:4px;padding-top:4px}.filter-content p{color:var(--typography-muted);width:fit-content;margin:0}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: MenuRegisterDirective, selector: "[fwMenuRegister]" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: CdkMenuTrigger, selector: "[cdkMenuTriggerFor]", inputs: ["cdkMenuTriggerFor", "cdkMenuPosition", "cdkMenuTriggerData", "cdkMenuTriggerTransformOriginOn"], outputs: ["cdkMenuOpened", "cdkMenuClosed"], exportAs: ["cdkMenuTriggerFor"] }, { kind: "component", type: FwChipComponent, selector: "fw-chip", inputs: ["maxWidth", "value", "variant", "color", "icon", "title", "description", "showClose", "closeLabel", "disabled", "selected", "textWrap", "selectable"], outputs: ["close", "select"] }, { kind: "component", type: FwIconComponent, selector: "fw-icon", inputs: ["ariaLabel", "size", "color"] }, { kind: "component", type: FwMenuContainerComponent, selector: "fw-menu-container, fw-menu-filter", inputs: ["width", "maxHeight", "minHeight", "border", "shadow", "showFilter", "filterText", "focusFilterOnMount", "offset", "emptyText", "filterFn", "additionalMenuItems", "additionalGroups", "additionalSeparators", "keyHandler"], outputs: ["filteredMenuItemChange", "filterChanged"] }, { kind: "component", type: FwButtonComponent, selector: "fw-button", inputs: ["color", "size", "variant", "type", "disabled", "fullWidth", "leftIcon", "rightIcon", "focusInitial"] }, { kind: "component", type: FwMenuComponent, selector: "fw-menu", inputs: ["disabled", "size", "multiSelect", "useCheckbox", "value", "role", "id"], outputs: ["disabledChange", "valueChange", "change"] }, { kind: "component", type: FwMenuItemComponent, selector: "fw-menu-item", inputs: ["itemRole", "value", "size", "title", "description", "icon", "iconColor", "disabled", "showCheckbox", "checkboxColor", "multiSelect", "hidden", "collapsed", "href", "target", "subItemsOpen", "mouseEnterHandler", "focused", "selected"], outputs: ["itemRoleChange", "sizeChange", "disabledChange", "showCheckboxChange", "multiSelectChange", "hiddenChange", "subItemsOpenChange", "mouseEnterHandlerChange", "click", "focusedChange", "selectedChange"] }, { kind: "pipe", type: SlicePipe, name: "slice" }] }); }
|
|
6223
|
+
], queries: [{ propertyName: "customMenuItems", predicate: FwMenuItemComponent, descendants: true }], viewQueries: [{ propertyName: "wrapper", first: true, predicate: ["wrapper"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "menuFilter", first: true, predicate: FwMenuContainerComponent, descendants: true, isSignal: true }, { propertyName: "trigger", first: true, predicate: CdkMenuTrigger, descendants: true }, { propertyName: "menu", first: true, predicate: FwMenuComponent, descendants: true }, { propertyName: "renderedMenuItems", predicate: FwMenuItemComponent, descendants: true }], ngImport: i0, template: "<div [ngStyle]=\"{ width: width() }\" #wrapper>\n <div\n fwMenuRegister\n class=\"chip-grid\"\n [ngClass]=\"[size()]\"\n [ngStyle]=\"{ minHeight: minHeight(), maxHeight: maxHeight() }\"\n [cdkMenuTriggerFor]=\"selectMenu\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"trigger?.isOpen() || false\"\n [attr.aria-controls]=\"listboxId\"\n >\n @if (value.length === 0) {\n <span class=\"placeholder\">{{ placeholder() }}</span>\n }\n\n <!-- CHIPS -->\n @if (value && value.length > 0) {\n <div class=\"inner-chip-grid\">\n @for (chip of selectedOptions | slice: 0 : maxSelectedShown(); track chip) {\n <fw-chip\n [title]=\"chip[titleProperty()]\"\n [value]=\"chip[valueProperty()]\"\n [icon]=\"chip[iconProperty()]\"\n color=\"primary\"\n [showClose]=\"true\"\n (close)=\"handleChipClose(chip)\"\n [selectable]=\"false\"\n >\n </fw-chip>\n }\n @if (maxSelectedShown() === 0 && value.length > 0) {\n <span class=\"selected-text\"> {{ value.length }} selected </span>\n }\n @if (value.length > maxSelectedShown() && maxSelectedShown() > 0) {\n <span class=\"max-exceeded\"> +{{ value.length - maxSelectedShown() }} more </span>\n }\n </div>\n }\n\n @if (showClear() && value.length > 0) {\n <fw-icon (click)=\"updateValue([])\">close</fw-icon>\n }\n <fw-icon>chevron-down</fw-icon>\n </div>\n\n <!-- MENU -->\n <ng-template #selectMenu>\n <fw-menu-filter\n [keyHandler]=\"handleKeyDown\"\n [showFilter]=\"showFilter()\"\n [focusFilterOnMount]=\"showFilter()\"\n [width]=\"optionsWidth() || wrapper.offsetWidth - 2 + 'px'\"\n [additionalMenuItems]=\"this.customMenuItems.toArray()\"\n [emptyText]=\"emptyText()\"\n [maxHeight]=\"maxOptionsHeight()\"\n [minHeight]=\"minOptionsHeight()\"\n [filterFn]=\"filterFn()\"\n (filteredMenuItemChange)=\"displayedOptions.set($event)\"\n >\n @if (showSelectionInfo()) {\n <div class=\"filter-content\">\n <p>{{ value.length }} selections</p>\n <fw-button variant=\"ghost\" (click)=\"updateValue([])\">Clear</fw-button>\n </div>\n }\n <fw-menu\n role=\"listbox\"\n [id]=\"listboxId\"\n [multiSelect]=\"true\"\n [disabled]=\"disabled()\"\n [value]=\"createArray(selectedValues)\"\n (change)=\"handleMenuChange($event)\"\n >\n @if (customMenuItems.length === 0) {\n @for (item of options(); track item) {\n <fw-menu-item\n [title]=\"item[titleProperty()]\"\n [value]=\"item[valueProperty()]\"\n [icon]=\"item[iconProperty()]\"\n [multiSelect]=\"true\"\n [selected]=\"value.includes(item[valueProperty()])\"\n [showCheckbox]=\"useCheckbox()\"\n (mouseenter)=\"setFocusedIndex(item)\"\n >\n </fw-menu-item>\n }\n }\n <ng-content\n ngProjectAs=\"custom-menu-items\"\n select=\"[fw-menu-item, fw-menu-separator, fw-menu-item-group, fw-menu-header]\"\n ></ng-content>\n </fw-menu>\n </fw-menu-filter>\n </ng-template>\n</div>\n", styles: [":host{pointer-events:auto}:host.disabled{opacity:.4;cursor:not-allowed}:host.disabled>div{pointer-events:none}:host .placeholder{color:var(--typography-light);font-size:14px;padding-left:4px;text-wrap:nowrap;text-overflow:ellipsis;overflow:hidden}:host .chip-grid{min-height:36px;width:100%;box-sizing:border-box;color:var(--typography-light);background:var(--page-light);display:flex;justify-content:space-between;padding:4px;align-items:center;border-radius:6px;overflow:hidden;border:1px solid var(--separations-input);cursor:pointer;font-family:Inter,sans-serif}:host .chip-grid.small{min-height:30px;padding:2px}:host .chip-grid.large{min-height:40px;padding:6px}:host .chip-grid:focus-within{border-color:var(--primary-base)}:host .chip-grid .inner-chip-grid{display:flex;flex-wrap:wrap;gap:4px;overflow:hidden auto;align-items:center;flex:1}:host .chip-grid .inner-chip-grid input{display:inline-flex;width:10px;flex-grow:1}:host .chip-grid fw-icon{font-size:20px}:host .chip-grid fw-icon:hover{color:var(--primary-base)}:host .chip-grid .selected-text{color:var(--typography-base);font-size:14px;padding:0 2px 0 4px;text-wrap:nowrap;text-overflow:ellipsis;overflow:hidden}:host .chip-grid .max-exceeded{font-size:12px}.filter-content{display:flex;align-items:center;justify-content:space-between;padding-left:4px;padding-top:4px}.filter-content p{color:var(--typography-muted);width:fit-content;margin:0}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: MenuRegisterDirective, selector: "[fwMenuRegister]" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: CdkMenuTrigger, selector: "[cdkMenuTriggerFor]", inputs: ["cdkMenuTriggerFor", "cdkMenuPosition", "cdkMenuTriggerData", "cdkMenuTriggerTransformOriginOn"], outputs: ["cdkMenuOpened", "cdkMenuClosed"], exportAs: ["cdkMenuTriggerFor"] }, { kind: "component", type: FwChipComponent, selector: "fw-chip", inputs: ["maxWidth", "value", "variant", "color", "icon", "title", "description", "showClose", "closeLabel", "disabled", "selected", "textWrap", "selectable"], outputs: ["close", "select"] }, { kind: "component", type: FwIconComponent, selector: "fw-icon", inputs: ["ariaLabel", "size", "color"] }, { kind: "component", type: FwMenuContainerComponent, selector: "fw-menu-container, fw-menu-filter", inputs: ["width", "maxHeight", "minHeight", "border", "shadow", "showFilter", "filterText", "focusFilterOnMount", "offset", "emptyText", "filterFn", "additionalMenuItems", "additionalGroups", "additionalSeparators", "keyHandler"], outputs: ["filteredMenuItemChange", "filterChanged"] }, { kind: "component", type: FwButtonComponent, selector: "fw-button", inputs: ["color", "size", "variant", "type", "disabled", "fullWidth", "leftIcon", "rightIcon", "focusInitial"] }, { kind: "component", type: FwMenuComponent, selector: "fw-menu", inputs: ["disabled", "size", "multiSelect", "useCheckbox", "value", "role", "id"], outputs: ["disabledChange", "valueChange", "change"] }, { kind: "component", type: FwMenuItemComponent, selector: "fw-menu-item", inputs: ["itemRole", "value", "size", "title", "description", "icon", "iconColor", "disabled", "showCheckbox", "checkboxColor", "multiSelect", "hidden", "collapsed", "href", "target", "subItemsOpen", "mouseEnterHandler", "focused", "selected"], outputs: ["itemRoleChange", "sizeChange", "disabledChange", "showCheckboxChange", "multiSelectChange", "hiddenChange", "subItemsOpenChange", "mouseEnterHandlerChange", "click", "focusedChange", "selectedChange"] }, { kind: "pipe", type: SlicePipe, name: "slice" }] }); }
|
|
6127
6224
|
}
|
|
6128
6225
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FwMultiSelectMenuComponent, decorators: [{
|
|
6129
6226
|
type: Component,
|
|
@@ -6146,16 +6243,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
6146
6243
|
FwMenuItemComponent,
|
|
6147
6244
|
SlicePipe,
|
|
6148
6245
|
], template: "<div [ngStyle]=\"{ width: width() }\" #wrapper>\n <div\n fwMenuRegister\n class=\"chip-grid\"\n [ngClass]=\"[size()]\"\n [ngStyle]=\"{ minHeight: minHeight(), maxHeight: maxHeight() }\"\n [cdkMenuTriggerFor]=\"selectMenu\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"trigger?.isOpen() || false\"\n [attr.aria-controls]=\"listboxId\"\n >\n @if (value.length === 0) {\n <span class=\"placeholder\">{{ placeholder() }}</span>\n }\n\n <!-- CHIPS -->\n @if (value && value.length > 0) {\n <div class=\"inner-chip-grid\">\n @for (chip of selectedOptions | slice: 0 : maxSelectedShown(); track chip) {\n <fw-chip\n [title]=\"chip[titleProperty()]\"\n [value]=\"chip[valueProperty()]\"\n [icon]=\"chip[iconProperty()]\"\n color=\"primary\"\n [showClose]=\"true\"\n (close)=\"handleChipClose(chip)\"\n [selectable]=\"false\"\n >\n </fw-chip>\n }\n @if (maxSelectedShown() === 0 && value.length > 0) {\n <span class=\"selected-text\"> {{ value.length }} selected </span>\n }\n @if (value.length > maxSelectedShown() && maxSelectedShown() > 0) {\n <span class=\"max-exceeded\"> +{{ value.length - maxSelectedShown() }} more </span>\n }\n </div>\n }\n\n @if (showClear() && value.length > 0) {\n <fw-icon (click)=\"updateValue([])\">close</fw-icon>\n }\n <fw-icon>chevron-down</fw-icon>\n </div>\n\n <!-- MENU -->\n <ng-template #selectMenu>\n <fw-menu-filter\n [keyHandler]=\"handleKeyDown\"\n [showFilter]=\"showFilter()\"\n [focusFilterOnMount]=\"showFilter()\"\n [width]=\"optionsWidth() || wrapper.offsetWidth - 2 + 'px'\"\n [additionalMenuItems]=\"this.customMenuItems.toArray()\"\n [emptyText]=\"emptyText()\"\n [maxHeight]=\"maxOptionsHeight()\"\n [minHeight]=\"minOptionsHeight()\"\n [filterFn]=\"filterFn()\"\n (filteredMenuItemChange)=\"displayedOptions.set($event)\"\n >\n @if (showSelectionInfo()) {\n <div class=\"filter-content\">\n <p>{{ value.length }} selections</p>\n <fw-button variant=\"ghost\" (click)=\"updateValue([])\">Clear</fw-button>\n </div>\n }\n <fw-menu\n role=\"listbox\"\n [id]=\"listboxId\"\n [multiSelect]=\"true\"\n [disabled]=\"disabled()\"\n [value]=\"createArray(selectedValues)\"\n (change)=\"handleMenuChange($event)\"\n >\n @if (customMenuItems.length === 0) {\n @for (item of options(); track item) {\n <fw-menu-item\n [title]=\"item[titleProperty()]\"\n [value]=\"item[valueProperty()]\"\n [icon]=\"item[iconProperty()]\"\n [multiSelect]=\"true\"\n [selected]=\"value.includes(item[valueProperty()])\"\n [showCheckbox]=\"useCheckbox()\"\n (mouseenter)=\"setFocusedIndex(item)\"\n >\n </fw-menu-item>\n }\n }\n <ng-content\n ngProjectAs=\"custom-menu-items\"\n select=\"[fw-menu-item, fw-menu-separator, fw-menu-item-group, fw-menu-header]\"\n ></ng-content>\n </fw-menu>\n </fw-menu-filter>\n </ng-template>\n</div>\n", styles: [":host{pointer-events:auto}:host.disabled{opacity:.4;cursor:not-allowed}:host.disabled>div{pointer-events:none}:host .placeholder{color:var(--typography-light);font-size:14px;padding-left:4px;text-wrap:nowrap;text-overflow:ellipsis;overflow:hidden}:host .chip-grid{min-height:36px;width:100%;box-sizing:border-box;color:var(--typography-light);background:var(--page-light);display:flex;justify-content:space-between;padding:4px;align-items:center;border-radius:6px;overflow:hidden;border:1px solid var(--separations-input);cursor:pointer;font-family:Inter,sans-serif}:host .chip-grid.small{min-height:30px;padding:2px}:host .chip-grid.large{min-height:40px;padding:6px}:host .chip-grid:focus-within{border-color:var(--primary-base)}:host .chip-grid .inner-chip-grid{display:flex;flex-wrap:wrap;gap:4px;overflow:hidden auto;align-items:center;flex:1}:host .chip-grid .inner-chip-grid input{display:inline-flex;width:10px;flex-grow:1}:host .chip-grid fw-icon{font-size:20px}:host .chip-grid fw-icon:hover{color:var(--primary-base)}:host .chip-grid .selected-text{color:var(--typography-base);font-size:14px;padding:0 2px 0 4px;text-wrap:nowrap;text-overflow:ellipsis;overflow:hidden}:host .chip-grid .max-exceeded{font-size:12px}.filter-content{display:flex;align-items:center;justify-content:space-between;padding-left:4px;padding-top:4px}.filter-content p{color:var(--typography-muted);width:fit-content;margin:0}\n"] }]
|
|
6149
|
-
}], propDecorators: {
|
|
6150
|
-
type: HostListener,
|
|
6151
|
-
args: ['document:click', ['$event']]
|
|
6152
|
-
}], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], valueProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueProperty", required: false }] }], titleProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleProperty", required: false }] }], iconProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconProperty", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], useCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "useCheckbox", required: false }] }], closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnSelect", required: false }] }], maxSelectedShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelectedShown", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], showSelectionInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSelectionInfo", required: false }] }], filterItemsOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterItemsOnSelect", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], optionsWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionsWidth", required: false }] }], minOptionsHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minOptionsHeight", required: false }] }], maxOptionsHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxOptionsHeight", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], trigger: [{
|
|
6246
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], valueProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueProperty", required: false }] }], titleProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleProperty", required: false }] }], iconProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconProperty", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], useCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "useCheckbox", required: false }] }], closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnSelect", required: false }] }], maxSelectedShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelectedShown", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], showSelectionInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSelectionInfo", required: false }] }], filterItemsOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterItemsOnSelect", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], optionsWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionsWidth", required: false }] }], minOptionsHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minOptionsHeight", required: false }] }], maxOptionsHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxOptionsHeight", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], trigger: [{
|
|
6153
6247
|
type: ViewChild,
|
|
6154
6248
|
args: [CdkMenuTrigger]
|
|
6155
6249
|
}], menu: [{
|
|
6156
6250
|
type: ViewChild,
|
|
6157
6251
|
args: [FwMenuComponent]
|
|
6158
|
-
}], menuFilter: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FwMenuContainerComponent), { isSignal: true }] }], renderedMenuItems: [{
|
|
6252
|
+
}], wrapper: [{ type: i0.ViewChild, args: ['wrapper', { ...{ read: ElementRef }, isSignal: true }] }], menuFilter: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FwMenuContainerComponent), { isSignal: true }] }], renderedMenuItems: [{
|
|
6159
6253
|
type: ViewChildren,
|
|
6160
6254
|
args: [FwMenuItemComponent]
|
|
6161
6255
|
}], customMenuItems: [{
|
|
@@ -6182,28 +6276,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
6182
6276
|
* @see [Vision storybook](https://cdn.flywheel.io/docs/vision/master/?path=/docs/form-controls-select--docs)
|
|
6183
6277
|
*/
|
|
6184
6278
|
class FwSelectMenuComponent {
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6279
|
+
/**
|
|
6280
|
+
* Closes the options panel when a click lands outside of both the trigger and the panel.
|
|
6281
|
+
*
|
|
6282
|
+
* Registered by hand instead of through `@HostListener('document:click')` so that it runs in the
|
|
6283
|
+
* capture phase: a number of components in the library (`fw-menu-item`, `fw-chip`, `fw-navbar-item`,
|
|
6284
|
+
* the dialog backdrop, ...) call `stopPropagation()` on click, which would otherwise keep this
|
|
6285
|
+
* listener from ever seeing the click and leave the panel open.
|
|
6286
|
+
*/
|
|
6287
|
+
outsideClick(event) {
|
|
6288
|
+
if (!this.isMenuOpen() || this.isEventInsideSelect(event)) {
|
|
6192
6289
|
return;
|
|
6193
6290
|
}
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
if (
|
|
6201
|
-
this.
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
this._isOpen = false;
|
|
6291
|
+
// Typing moves `selectValue` onto the highlighted match without committing it, so a dismissed
|
|
6292
|
+
// filter has to be rolled back or the input ends up displaying an option that was never picked
|
|
6293
|
+
const hadUncommittedFilter = this.isTyping();
|
|
6294
|
+
this.onTouched();
|
|
6295
|
+
this.close();
|
|
6296
|
+
this.inFocusOpen = false;
|
|
6297
|
+
if (hadUncommittedFilter) {
|
|
6298
|
+
this.selectValue.set(this.displayValueOnOpen);
|
|
6299
|
+
this.selectTitle.set(this.displayTitleOnOpen);
|
|
6300
|
+
this.updateHighlighting();
|
|
6205
6301
|
}
|
|
6206
6302
|
}
|
|
6303
|
+
/** Whether an event originated on this select's control box or inside its rendered options panel */
|
|
6304
|
+
isEventInsideSelect(event) {
|
|
6305
|
+
return isEventInside(event, this.wrapper()?.nativeElement, this.panel()?.hostElement);
|
|
6306
|
+
}
|
|
6207
6307
|
get disabledClass() {
|
|
6208
6308
|
return this.disabled();
|
|
6209
6309
|
}
|
|
@@ -6218,7 +6318,9 @@ class FwSelectMenuComponent {
|
|
|
6218
6318
|
}
|
|
6219
6319
|
constructor() {
|
|
6220
6320
|
this.ngControl = inject(NgControl, { optional: true, self: true });
|
|
6321
|
+
this.document = inject(DOCUMENT);
|
|
6221
6322
|
this.listboxId = inject(_IdGenerator).getId('fw-select-listbox-');
|
|
6323
|
+
this.documentClickListener = (event) => this.outsideClick(event);
|
|
6222
6324
|
this.options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
6223
6325
|
this.valueProperty = input('value', ...(ngDevMode ? [{ debugName: "valueProperty" }] : /* istanbul ignore next */ []));
|
|
6224
6326
|
this.useFullOptionAsValue = input(false, ...(ngDevMode ? [{ debugName: "useFullOptionAsValue" }] : /* istanbul ignore next */ []));
|
|
@@ -6237,6 +6339,13 @@ class FwSelectMenuComponent {
|
|
|
6237
6339
|
this.size = input('medium', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
6238
6340
|
this.placeholder = input('Select something...', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
6239
6341
|
this.menu = viewChild(FwMenuComponent, ...(ngDevMode ? [{ debugName: "menu" }] : /* istanbul ignore next */ []));
|
|
6342
|
+
/**
|
|
6343
|
+
* The control itself, which is only as wide as `width()`. The host element is a block element and
|
|
6344
|
+
* so can be considerably wider, and a click in that gap is an outside click.
|
|
6345
|
+
*/
|
|
6346
|
+
this.wrapper = viewChild('wrapper', { ...(ngDevMode ? { debugName: "wrapper" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
6347
|
+
/** The options panel, `undefined` while it is closed since it only exists inside the cdk overlay */
|
|
6348
|
+
this.panel = viewChild(FwMenuContainerComponent, ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
|
|
6240
6349
|
this.menuItems = contentChildren(FwMenuItemComponent, { ...(ngDevMode ? { debugName: "menuItems" } : /* istanbul ignore next */ {}), descendants: true });
|
|
6241
6350
|
this.viewMenuItems = viewChildren(FwMenuItemComponent, ...(ngDevMode ? [{ debugName: "viewMenuItems" }] : /* istanbul ignore next */ []));
|
|
6242
6351
|
this.menuItemGroups = contentChildren(FwMenuItemGroupComponent, { ...(ngDevMode ? { debugName: "menuItemGroups" } : /* istanbul ignore next */ {}), descendants: true });
|
|
@@ -6248,9 +6357,11 @@ class FwSelectMenuComponent {
|
|
|
6248
6357
|
this.filterValue = signal('', ...(ngDevMode ? [{ debugName: "filterValue" }] : /* istanbul ignore next */ []));
|
|
6249
6358
|
this.menuItemClickSubscriptions = [];
|
|
6250
6359
|
this.subscriptions = [];
|
|
6251
|
-
this._isOpen = false;
|
|
6252
6360
|
this.focused = 0;
|
|
6253
6361
|
this.inFocusOpen = false;
|
|
6362
|
+
/** Displayed value and title as of the last time the panel was opened, used to undo a dismissed filter */
|
|
6363
|
+
this.displayValueOnOpen = '';
|
|
6364
|
+
this.displayTitleOnOpen = '';
|
|
6254
6365
|
this.isTyping = signal(false, ...(ngDevMode ? [{ debugName: "isTyping" }] : /* istanbul ignore next */ []));
|
|
6255
6366
|
this.valueDisplayFn = input((menuItem) => menuItem.title() || '', ...(ngDevMode ? [{ debugName: "valueDisplayFn" }] : /* istanbul ignore next */ []));
|
|
6256
6367
|
this.defaultFilterFn = (filter, items) => items.filter((item) => {
|
|
@@ -6289,7 +6400,9 @@ class FwSelectMenuComponent {
|
|
|
6289
6400
|
return displayFn(selectedMenuItem);
|
|
6290
6401
|
}
|
|
6291
6402
|
const selectedOption = options.find((opt) => opt[this.valueProperty()]?.toString() === currentValue);
|
|
6292
|
-
|
|
6403
|
+
// `||` would swallow a legitimate `0` title and leave the input showing its placeholder, and it
|
|
6404
|
+
// would also let a non-string title through into this `linkedSignal<string>` untouched
|
|
6405
|
+
return String(selectedOption?.[this.titleProperty()] ?? '');
|
|
6293
6406
|
}, ...(ngDevMode ? [{ debugName: "selectTitle" }] : /* istanbul ignore next */ []));
|
|
6294
6407
|
// Watch for menu items changes and re-subscribe
|
|
6295
6408
|
this.menuItemsWatcher = effect(() => {
|
|
@@ -6331,11 +6444,14 @@ class FwSelectMenuComponent {
|
|
|
6331
6444
|
}
|
|
6332
6445
|
}
|
|
6333
6446
|
ngOnInit() {
|
|
6447
|
+
this.document.addEventListener('click', this.documentClickListener, true);
|
|
6334
6448
|
const onOpenSub = this.trigger.opened.subscribe(() => {
|
|
6335
6449
|
// Initialize navigation state synchronously so the first arrow key press
|
|
6336
6450
|
// navigates relative to the selected item rather than being swallowed by setup
|
|
6337
6451
|
this.inFocusOpen = true;
|
|
6338
6452
|
this.preFocusValue = this.value;
|
|
6453
|
+
this.displayValueOnOpen = this.selectValue();
|
|
6454
|
+
this.displayTitleOnOpen = this.selectTitle();
|
|
6339
6455
|
this.initializeFocusedIndex();
|
|
6340
6456
|
const currentValue = this.selectValue();
|
|
6341
6457
|
setTimeout(() => {
|
|
@@ -6371,6 +6487,7 @@ class FwSelectMenuComponent {
|
|
|
6371
6487
|
}
|
|
6372
6488
|
}
|
|
6373
6489
|
ngOnDestroy() {
|
|
6490
|
+
this.document.removeEventListener('click', this.documentClickListener, true);
|
|
6374
6491
|
this.menuItemClickSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
6375
6492
|
this.subscriptions.forEach((sub) => sub.unsubscribe());
|
|
6376
6493
|
}
|
|
@@ -6418,14 +6535,22 @@ class FwSelectMenuComponent {
|
|
|
6418
6535
|
this.close();
|
|
6419
6536
|
this.inFocusOpen = false;
|
|
6420
6537
|
}
|
|
6538
|
+
/**
|
|
6539
|
+
* Whether the options panel is currently open. The single place this class asks that question -
|
|
6540
|
+
* the trigger owns the state, so there is no local flag to keep in sync.
|
|
6541
|
+
*
|
|
6542
|
+
* ! Deliberately a plain method, not a `computed()` ! The trigger reports its state from the
|
|
6543
|
+
* overlay ref rather than from a signal, so a computed would memoize the first answer and stop
|
|
6544
|
+
* tracking, silently freezing `aria-expanded`.
|
|
6545
|
+
*/
|
|
6421
6546
|
isMenuOpen() {
|
|
6422
|
-
return this.trigger
|
|
6547
|
+
return this.trigger?.isOpen() ?? false;
|
|
6423
6548
|
}
|
|
6424
6549
|
/**
|
|
6425
6550
|
* The DOM id of the currently active/highlighted option, for aria-activedescendant on the combobox input.
|
|
6426
6551
|
*/
|
|
6427
6552
|
getActiveDescendantId() {
|
|
6428
|
-
if (!this.
|
|
6553
|
+
if (!this.isMenuOpen()) {
|
|
6429
6554
|
return undefined;
|
|
6430
6555
|
}
|
|
6431
6556
|
const availableItems = this.getAvailableItems();
|
|
@@ -6446,7 +6571,7 @@ class FwSelectMenuComponent {
|
|
|
6446
6571
|
getAvailableItems() {
|
|
6447
6572
|
// If using options input, return filtered options excluding disabled ones
|
|
6448
6573
|
if (this.options().length > 0) {
|
|
6449
|
-
return this.filteredOptions().filter(opt => !opt['disabled']);
|
|
6574
|
+
return this.filteredOptions().filter((opt) => !opt['disabled']);
|
|
6450
6575
|
}
|
|
6451
6576
|
// If using content projection, filter by typeahead text and exclude disabled items
|
|
6452
6577
|
if (this.menuItems().length > 0) {
|
|
@@ -6589,7 +6714,6 @@ class FwSelectMenuComponent {
|
|
|
6589
6714
|
if (hasSelection) {
|
|
6590
6715
|
this.isTyping.set(true);
|
|
6591
6716
|
this.trigger.open();
|
|
6592
|
-
this._isOpen = true;
|
|
6593
6717
|
this.inFocusOpen = true;
|
|
6594
6718
|
this.preFocusValue = this.value;
|
|
6595
6719
|
this.initializeFocusedIndex();
|
|
@@ -6615,7 +6739,7 @@ class FwSelectMenuComponent {
|
|
|
6615
6739
|
'PageDown',
|
|
6616
6740
|
'Delete',
|
|
6617
6741
|
].includes(event.key);
|
|
6618
|
-
if (!this.
|
|
6742
|
+
if (!this.isMenuOpen() &&
|
|
6619
6743
|
!this.isTyping() &&
|
|
6620
6744
|
this.selectTitle() &&
|
|
6621
6745
|
event.key.length === 1 &&
|
|
@@ -6627,7 +6751,6 @@ class FwSelectMenuComponent {
|
|
|
6627
6751
|
// Just switch to typing mode and the input event will update filterValue
|
|
6628
6752
|
this.isTyping.set(true);
|
|
6629
6753
|
this.trigger.open();
|
|
6630
|
-
this._isOpen = true;
|
|
6631
6754
|
this.inFocusOpen = true;
|
|
6632
6755
|
this.preFocusValue = this.value;
|
|
6633
6756
|
this.initializeFocusedIndex();
|
|
@@ -6640,7 +6763,7 @@ class FwSelectMenuComponent {
|
|
|
6640
6763
|
// Let default behavior happen, onInputChange will handle it
|
|
6641
6764
|
return;
|
|
6642
6765
|
}
|
|
6643
|
-
if (this.
|
|
6766
|
+
if (this.isMenuOpen()) {
|
|
6644
6767
|
if (this.inFocusOpen) {
|
|
6645
6768
|
if (event.key === 'ArrowDown') {
|
|
6646
6769
|
event.preventDefault();
|
|
@@ -6752,7 +6875,7 @@ class FwSelectMenuComponent {
|
|
|
6752
6875
|
}
|
|
6753
6876
|
}
|
|
6754
6877
|
handleKeyUp(event) {
|
|
6755
|
-
if (this.
|
|
6878
|
+
if (this.isMenuOpen()) {
|
|
6756
6879
|
if (event.key === 'Escape') {
|
|
6757
6880
|
this.isTyping.set(false);
|
|
6758
6881
|
this.close();
|
|
@@ -6824,7 +6947,6 @@ class FwSelectMenuComponent {
|
|
|
6824
6947
|
this.trigger.close();
|
|
6825
6948
|
this.filterValue.set('');
|
|
6826
6949
|
this.filterChanged.emit(this.filterValue());
|
|
6827
|
-
this._isOpen = false;
|
|
6828
6950
|
this.isTyping.set(false);
|
|
6829
6951
|
}
|
|
6830
6952
|
onFilterChanged(value) {
|
|
@@ -6878,13 +7000,12 @@ class FwSelectMenuComponent {
|
|
|
6878
7000
|
}, 0);
|
|
6879
7001
|
this.filterChanged.emit(this.filterValue());
|
|
6880
7002
|
// Auto-open dropdown when user starts typing
|
|
6881
|
-
if (this.filterValue() && !this.
|
|
7003
|
+
if (this.filterValue() && !this.isMenuOpen()) {
|
|
6882
7004
|
this.trigger.open();
|
|
6883
|
-
this._isOpen = true;
|
|
6884
7005
|
}
|
|
6885
7006
|
}
|
|
6886
7007
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FwSelectMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6887
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FwSelectMenuComponent, isStandalone: true, selector: "fw-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, valueProperty: { classPropertyName: "valueProperty", publicName: "valueProperty", isSignal: true, isRequired: false, transformFunction: null }, useFullOptionAsValue: { classPropertyName: "useFullOptionAsValue", publicName: "useFullOptionAsValue", isSignal: true, isRequired: false, transformFunction: null }, titleProperty: { classPropertyName: "titleProperty", publicName: "titleProperty", isSignal: true, isRequired: false, transformFunction: null }, iconProperty: { classPropertyName: "iconProperty", publicName: "iconProperty", isSignal: true, isRequired: false, transformFunction: null }, staticIcon: { classPropertyName: "staticIcon", publicName: "staticIcon", isSignal: true, isRequired: false, transformFunction: null }, descriptionProperty: { classPropertyName: "descriptionProperty", publicName: "descriptionProperty", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, showReset: { classPropertyName: "showReset", publicName: "showReset", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, errored: { classPropertyName: "errored", publicName: "errored", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, optionsWidth: { classPropertyName: "optionsWidth", publicName: "optionsWidth", isSignal: true, isRequired: false, transformFunction: null }, minOptionsHeight: { classPropertyName: "minOptionsHeight", publicName: "minOptionsHeight", isSignal: true, isRequired: false, transformFunction: null }, maxOptionsHeight: { classPropertyName: "maxOptionsHeight", publicName: "maxOptionsHeight", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, valueDisplayFn: { classPropertyName: "valueDisplayFn", publicName: "valueDisplayFn", isSignal: true, isRequired: false, transformFunction: null }, filterFn: { classPropertyName: "filterFn", publicName: "filterFn", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", change: "change", filterChanged: "filterChanged" }, host: {
|
|
7008
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FwSelectMenuComponent, isStandalone: true, selector: "fw-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, valueProperty: { classPropertyName: "valueProperty", publicName: "valueProperty", isSignal: true, isRequired: false, transformFunction: null }, useFullOptionAsValue: { classPropertyName: "useFullOptionAsValue", publicName: "useFullOptionAsValue", isSignal: true, isRequired: false, transformFunction: null }, titleProperty: { classPropertyName: "titleProperty", publicName: "titleProperty", isSignal: true, isRequired: false, transformFunction: null }, iconProperty: { classPropertyName: "iconProperty", publicName: "iconProperty", isSignal: true, isRequired: false, transformFunction: null }, staticIcon: { classPropertyName: "staticIcon", publicName: "staticIcon", isSignal: true, isRequired: false, transformFunction: null }, descriptionProperty: { classPropertyName: "descriptionProperty", publicName: "descriptionProperty", isSignal: true, isRequired: false, transformFunction: null }, showFilter: { classPropertyName: "showFilter", publicName: "showFilter", isSignal: true, isRequired: false, transformFunction: null }, showReset: { classPropertyName: "showReset", publicName: "showReset", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, errored: { classPropertyName: "errored", publicName: "errored", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, optionsWidth: { classPropertyName: "optionsWidth", publicName: "optionsWidth", isSignal: true, isRequired: false, transformFunction: null }, minOptionsHeight: { classPropertyName: "minOptionsHeight", publicName: "minOptionsHeight", isSignal: true, isRequired: false, transformFunction: null }, maxOptionsHeight: { classPropertyName: "maxOptionsHeight", publicName: "maxOptionsHeight", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, valueDisplayFn: { classPropertyName: "valueDisplayFn", publicName: "valueDisplayFn", isSignal: true, isRequired: false, transformFunction: null }, filterFn: { classPropertyName: "filterFn", publicName: "filterFn", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", change: "change", filterChanged: "filterChanged" }, host: { properties: { "class.disabled": "this.disabledClass" } }, queries: [{ propertyName: "menuItems", predicate: FwMenuItemComponent, descendants: true, isSignal: true }, { propertyName: "menuItemGroups", predicate: FwMenuItemGroupComponent, descendants: true, isSignal: true }, { propertyName: "menuSeparators", predicate: FwMenuSeparatorComponent, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "menu", first: true, predicate: FwMenuComponent, descendants: true, isSignal: true }, { propertyName: "wrapper", first: true, predicate: ["wrapper"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "panel", first: true, predicate: FwMenuContainerComponent, descendants: true, isSignal: true }, { propertyName: "viewMenuItems", predicate: FwMenuItemComponent, descendants: true, isSignal: true }, { propertyName: "trigger", first: true, predicate: CdkMenuTrigger, descendants: true, static: true }, { propertyName: "textInput", first: true, predicate: FwTextInputComponent, descendants: true }], ngImport: i0, template: "<div #wrapper [style.width]=\"width()\">\n <fw-text-input\n fwMenuRegister\n [cdkMenuTriggerFor]=\"selectMenu\"\n [value]=\"inputDisplayValue()\"\n [leftIcon]=\"staticIcon() || selectIcon || null\"\n [rightIcon]=\"(selectTitle()&&showReset())?'close-circled':'chevron-down'\"\n (rightIconAction)=\"handleReset()\"\n [useActionableIcons]=\"true\"\n [placeholder]=\"placeholder()\"\n [size]=\"size()\"\n [error]=\"errored() || (invalid && touched)\"\n (input)=\"onInputChange($event)\"\n (keyup)=\"handleKeyUp($event)\"\n (keydown)=\"handleKeyDown($event)\"\n (focus)=\"handleFocus()\"\n (click)=\"handleInputClick()\"\n [readOnly]=\"false\"\n role=\"combobox\"\n [ariaExpanded]=\"isMenuOpen()\"\n [ariaControls]=\"listboxId\"\n ariaAutocomplete=\"list\"\n [ariaActiveDescendant]=\"getActiveDescendantId()\">\n </fw-text-input>\n <ng-template #selectMenu>\n @if (!disabled()) {\n <fw-menu-container\n [filterFn]=\"filterFn()\"\n [filterText]=\"filterValue()\"\n [additionalMenuItems]=\"menuItems()\"\n [additionalGroups]=\"menuItemGroups()\"\n [additionalSeparators]=\"menuSeparators()\"\n [showFilter]=\"showFilter()\" [width]=\"optionsWidth() || wrapper.offsetWidth + 'px'\"\n [maxHeight]=\"maxOptionsHeight()\" [minHeight]=\"minOptionsHeight()\" (filterChanged)=\"onFilterChanged($event)\">\n <fw-menu\n role=\"listbox\"\n [id]=\"listboxId\"\n [disabled]=\"disabled()\"\n [value]=\"selectValue()\"\n (change)=\"handleClick($any($event))\"\n >\n @if (menuItems().length === 0) {\n @for (item of optionsWithValues(); track item.trackingId) {\n <fw-menu-item\n [title]=\"item.raw[titleProperty()]?.toString()\"\n [description]=\"$any(item.raw[descriptionProperty()])\"\n [value]=\"item.value\"\n [icon]=\"$any(item.raw[iconProperty()])\"\n [disabled]=\"$any(item.raw).disabled\"\n />\n }\n }\n <div #menuContentWrapper>\n <ng-content select=\"[fw-menu-item, fw-menu-separator, fw-menu-item-group]\"></ng-content>\n </div>\n </fw-menu>\n </fw-menu-container>\n }\n </ng-template>\n</div>\n", styles: [":host{box-sizing:border-box;max-width:100%}:host>div{cursor:pointer}:host.disabled{opacity:.4;cursor:not-allowed}:host.disabled>div{pointer-events:none}\n"], dependencies: [{ kind: "component", type: FwTextInputComponent, selector: "fw-text-input", inputs: ["disabled", "useActionableIcons", "leftIcon", "rightIcon", "leftIconLabel", "rightIconLabel", "prefix", "context", "ariaLabel", "required", "role", "ariaExpanded", "ariaControls", "ariaAutocomplete", "ariaActiveDescendant", "helperText", "errorText", "errorInIconTooltip", "placeholder", "readOnly", "size", "type", "maxLength", "autofocus", "autocomplete", "value", "error", "width"], outputs: ["disabledChange", "leftIconAction", "rightIconAction"] }, { kind: "directive", type: MenuRegisterDirective, selector: "[fwMenuRegister]" }, { kind: "directive", type: CdkMenuTrigger, selector: "[cdkMenuTriggerFor]", inputs: ["cdkMenuTriggerFor", "cdkMenuPosition", "cdkMenuTriggerData", "cdkMenuTriggerTransformOriginOn"], outputs: ["cdkMenuOpened", "cdkMenuClosed"], exportAs: ["cdkMenuTriggerFor"] }, { kind: "component", type: FwMenuContainerComponent, selector: "fw-menu-container, fw-menu-filter", inputs: ["width", "maxHeight", "minHeight", "border", "shadow", "showFilter", "filterText", "focusFilterOnMount", "offset", "emptyText", "filterFn", "additionalMenuItems", "additionalGroups", "additionalSeparators", "keyHandler"], outputs: ["filteredMenuItemChange", "filterChanged"] }, { kind: "component", type: FwMenuComponent, selector: "fw-menu", inputs: ["disabled", "size", "multiSelect", "useCheckbox", "value", "role", "id"], outputs: ["disabledChange", "valueChange", "change"] }, { kind: "component", type: FwMenuItemComponent, selector: "fw-menu-item", inputs: ["itemRole", "value", "size", "title", "description", "icon", "iconColor", "disabled", "showCheckbox", "checkboxColor", "multiSelect", "hidden", "collapsed", "href", "target", "subItemsOpen", "mouseEnterHandler", "focused", "selected"], outputs: ["itemRoleChange", "sizeChange", "disabledChange", "showCheckboxChange", "multiSelectChange", "hiddenChange", "subItemsOpenChange", "mouseEnterHandlerChange", "click", "focusedChange", "selectedChange"] }] }); }
|
|
6888
7009
|
}
|
|
6889
7010
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FwSelectMenuComponent, decorators: [{
|
|
6890
7011
|
type: Component,
|
|
@@ -6896,10 +7017,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
6896
7017
|
FwMenuComponent,
|
|
6897
7018
|
FwMenuItemComponent,
|
|
6898
7019
|
], template: "<div #wrapper [style.width]=\"width()\">\n <fw-text-input\n fwMenuRegister\n [cdkMenuTriggerFor]=\"selectMenu\"\n [value]=\"inputDisplayValue()\"\n [leftIcon]=\"staticIcon() || selectIcon || null\"\n [rightIcon]=\"(selectTitle()&&showReset())?'close-circled':'chevron-down'\"\n (rightIconAction)=\"handleReset()\"\n [useActionableIcons]=\"true\"\n [placeholder]=\"placeholder()\"\n [size]=\"size()\"\n [error]=\"errored() || (invalid && touched)\"\n (input)=\"onInputChange($event)\"\n (keyup)=\"handleKeyUp($event)\"\n (keydown)=\"handleKeyDown($event)\"\n (focus)=\"handleFocus()\"\n (click)=\"handleInputClick()\"\n [readOnly]=\"false\"\n role=\"combobox\"\n [ariaExpanded]=\"isMenuOpen()\"\n [ariaControls]=\"listboxId\"\n ariaAutocomplete=\"list\"\n [ariaActiveDescendant]=\"getActiveDescendantId()\">\n </fw-text-input>\n <ng-template #selectMenu>\n @if (!disabled()) {\n <fw-menu-container\n [filterFn]=\"filterFn()\"\n [filterText]=\"filterValue()\"\n [additionalMenuItems]=\"menuItems()\"\n [additionalGroups]=\"menuItemGroups()\"\n [additionalSeparators]=\"menuSeparators()\"\n [showFilter]=\"showFilter()\" [width]=\"optionsWidth() || wrapper.offsetWidth + 'px'\"\n [maxHeight]=\"maxOptionsHeight()\" [minHeight]=\"minOptionsHeight()\" (filterChanged)=\"onFilterChanged($event)\">\n <fw-menu\n role=\"listbox\"\n [id]=\"listboxId\"\n [disabled]=\"disabled()\"\n [value]=\"selectValue()\"\n (change)=\"handleClick($any($event))\"\n >\n @if (menuItems().length === 0) {\n @for (item of optionsWithValues(); track item.trackingId) {\n <fw-menu-item\n [title]=\"item.raw[titleProperty()]?.toString()\"\n [description]=\"$any(item.raw[descriptionProperty()])\"\n [value]=\"item.value\"\n [icon]=\"$any(item.raw[iconProperty()])\"\n [disabled]=\"$any(item.raw).disabled\"\n />\n }\n }\n <div #menuContentWrapper>\n <ng-content select=\"[fw-menu-item, fw-menu-separator, fw-menu-item-group]\"></ng-content>\n </div>\n </fw-menu>\n </fw-menu-container>\n }\n </ng-template>\n</div>\n", styles: [":host{box-sizing:border-box;max-width:100%}:host>div{cursor:pointer}:host.disabled{opacity:.4;cursor:not-allowed}:host.disabled>div{pointer-events:none}\n"] }]
|
|
6899
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
6900
|
-
type: HostListener,
|
|
6901
|
-
args: ['document:click', ['$event']]
|
|
6902
|
-
}], disabledClass: [{
|
|
7020
|
+
}], ctorParameters: () => [], propDecorators: { disabledClass: [{
|
|
6903
7021
|
type: HostBinding,
|
|
6904
7022
|
args: ['class.disabled']
|
|
6905
7023
|
}], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], valueProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueProperty", required: false }] }], useFullOptionAsValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "useFullOptionAsValue", required: false }] }], titleProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleProperty", required: false }] }], iconProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconProperty", required: false }] }], staticIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "staticIcon", required: false }] }], descriptionProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "descriptionProperty", required: false }] }], showFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFilter", required: false }] }], showReset: [{ type: i0.Input, args: [{ isSignal: true, alias: "showReset", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], errored: [{ type: i0.Input, args: [{ isSignal: true, alias: "errored", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], optionsWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionsWidth", required: false }] }], minOptionsHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minOptionsHeight", required: false }] }], maxOptionsHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxOptionsHeight", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], trigger: [{
|
|
@@ -6908,7 +7026,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
6908
7026
|
}], textInput: [{
|
|
6909
7027
|
type: ViewChild,
|
|
6910
7028
|
args: [FwTextInputComponent]
|
|
6911
|
-
}], menu: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FwMenuComponent), { isSignal: true }] }], menuItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FwMenuItemComponent), { ...{ descendants: true }, isSignal: true }] }], viewMenuItems: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => FwMenuItemComponent), { isSignal: true }] }], menuItemGroups: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FwMenuItemGroupComponent), { ...{ descendants: true }, isSignal: true }] }], menuSeparators: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FwMenuSeparatorComponent), { ...{ descendants: true }, isSignal: true }] }], change: [{
|
|
7029
|
+
}], menu: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FwMenuComponent), { isSignal: true }] }], wrapper: [{ type: i0.ViewChild, args: ['wrapper', { ...{ read: ElementRef }, isSignal: true }] }], panel: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FwMenuContainerComponent), { isSignal: true }] }], menuItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FwMenuItemComponent), { ...{ descendants: true }, isSignal: true }] }], viewMenuItems: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => FwMenuItemComponent), { isSignal: true }] }], menuItemGroups: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FwMenuItemGroupComponent), { ...{ descendants: true }, isSignal: true }] }], menuSeparators: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FwMenuSeparatorComponent), { ...{ descendants: true }, isSignal: true }] }], change: [{
|
|
6912
7030
|
type: Output
|
|
6913
7031
|
}], filterChanged: [{
|
|
6914
7032
|
type: Output
|
|
@@ -8449,8 +8567,12 @@ class FwStepperComponent {
|
|
|
8449
8567
|
return;
|
|
8450
8568
|
}
|
|
8451
8569
|
this.activeStep.set(step);
|
|
8452
|
-
|
|
8570
|
+
// Same ordering rule as FwMenuComponent.writeValue: lay the steps out before calling out to a
|
|
8571
|
+
// listener, so a handler that removes the stepper cannot leave `updateSteps()` writing to
|
|
8572
|
+
// `model()`s on destroyed `fw-step`s (NG0953). `updateSteps()` only reads `activeStep()`, set
|
|
8573
|
+
// on the line above, so the layout is unchanged.
|
|
8453
8574
|
this.updateSteps();
|
|
8575
|
+
this.stepChange.emit(parseInt(step?.toString())); // weirdly passing out step directly fails the eqeqeq
|
|
8454
8576
|
}
|
|
8455
8577
|
updateSteps() {
|
|
8456
8578
|
if (this.steps && this.steps.length > 0) {
|