@energycap/components 0.45.2-multi-select-component.20260305-0759 → 0.45.2-multi-select-component.20260305-0828

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.
@@ -8453,6 +8453,12 @@ class MultiselectComponent extends FormControlBase {
8453
8453
  if (this.popup)
8454
8454
  this.popup.hide();
8455
8455
  }
8456
+ /**
8457
+ * Whether the maximum number of selections has been reached
8458
+ */
8459
+ get maxSelectionsReached() {
8460
+ return this.maxSelections > 0 && this.selectedItems.length >= this.maxSelections;
8461
+ }
8456
8462
  /**
8457
8463
  * Whether all currently visible selectable options are selected
8458
8464
  */
@@ -8979,6 +8985,7 @@ class MultiselectComponent extends FormControlBase {
8979
8985
  this.updateTags();
8980
8986
  this.selectAllItem.checked = this.allSelected;
8981
8987
  this.syncCheckboxFormControls();
8988
+ this.updateMaxSelectionsDisabledState();
8982
8989
  }
8983
8990
  /**
8984
8991
  * Synchronize the checked state on options based on selectedItems
@@ -9125,6 +9132,37 @@ class MultiselectComponent extends FormControlBase {
9125
9132
  controls: selectableItems.map(item => this.getCheckboxFormControl(item))
9126
9133
  };
9127
9134
  this.syncCheckboxFormControls();
9135
+ this.updateMaxSelectionsDisabledState();
9136
+ }
9137
+ /**
9138
+ * Enable or disable individual checkbox FormControls based on whether the max selections
9139
+ * limit has been reached. When the limit is reached, unselected items are disabled to
9140
+ * indicate they cannot be selected. The select all checkbox is always left enabled since
9141
+ * it can be used to deselect items.
9142
+ */
9143
+ updateMaxSelectionsDisabledState() {
9144
+ if (this.maxSelections <= 0) {
9145
+ this.checkboxFormControls.forEach((control) => {
9146
+ if (control.disabled) {
9147
+ control.enable({ emitEvent: false });
9148
+ }
9149
+ });
9150
+ return;
9151
+ }
9152
+ const limitReached = this.maxSelectionsReached;
9153
+ this.checkboxFormControls.forEach((control, item) => {
9154
+ const isSelected = this.selectedItems.some(selected => this.isSameItem(selected, item));
9155
+ if (limitReached && !isSelected) {
9156
+ if (control.enabled) {
9157
+ control.disable({ emitEvent: false });
9158
+ }
9159
+ }
9160
+ else {
9161
+ if (control.disabled) {
9162
+ control.enable({ emitEvent: false });
9163
+ }
9164
+ }
9165
+ });
9128
9166
  }
9129
9167
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: MultiselectComponent, deps: [{ token: ValidationMessageService }, { token: FormGroupHelper }, { token: i3.TranslateService }, { token: ScrollService }], target: i0.ɵɵFactoryTarget.Component }); }
9130
9168
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: MultiselectComponent, isStandalone: false, selector: "ec-multiselect", inputs: { options: "options", menuPosition: "menuPosition", popupFixed: "popupFixed", placeholder: "placeholder", tagType: "tagType", hideNoMatches: "hideNoMatches", noMatchesText: "noMatchesText", truncateItems: "truncateItems", allowCustom: "allowCustom", maxSelections: "maxSelections" }, outputs: { search: "search", selectionChanged: "selectionChanged" }, host: { listeners: { "window:resize": "onResize()" } }, viewQueries: [{ propertyName: "filterInput", first: true, predicate: ["filterInput"], descendants: true }, { propertyName: "checkboxMenuTemplate", first: true, predicate: ["checkboxMenuTemplate"], descendants: true, static: true }, { propertyName: "popup", first: true, predicate: PopupContainerDirective, descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"control control-label-{{labelPosition}}\"\r\n [ngClass]=\"{'invalid': formModel.touched && formModel.invalid,\r\n 'is-readonly': readonly,\r\n 'is-disabled': formModel.disabled}\">\r\n\r\n @if (label) {\r\n <label [attr.id]=\"id + '_label'\" [attr.for]=\"id + '_filter'\">\r\n <span>{{label | translate}}</span>\r\n @if (validationErrors.length > 0 && formModel.touched && formModel.invalid) {\r\n <span>&nbsp;{{validationErrors}}</span>\r\n }\r\n @if (helpPopover) {\r\n <ec-help-popover id=\"{{id}}_helpPopover\"\r\n class=\"d-inline-block my-n3 mx-n1\"\r\n text=\"{{helpPopover | translate}}\"\r\n contentPosition=\"{{helpPopoverPosition}}\">\r\n </ec-help-popover>\r\n }\r\n </label>\r\n }\r\n\r\n <div class=\"textbox-group control-input\"\r\n *ecPopup=\"dropdownmenu\">\r\n <div class=\"multiselect-input\"\r\n [attr.id]=\"id + '_input_container'\"\r\n role=\"combobox\"\r\n [attr.aria-expanded]=\"menuStatus === 'visible'\"\r\n aria-haspopup=\"listbox\"\r\n [attr.aria-owns]=\"id + '_menu_list'\"\r\n [class.is-focused]=\"menuStatus === 'visible'\"\r\n [class.is-disabled]=\"formModel.disabled\"\r\n [class.is-empty]=\"selectedItems.length === 0\"\r\n [class.is-required]=\"required\"\r\n [class.has-selections]=\"selectedItems.length > 0\"\r\n (click)=\"toggleMenu($event)\">\r\n\r\n <!-- Selected tags -->\r\n @if (selectedTags.length > 0) {\r\n <ec-tags\r\n id=\"{{id}}_tags\"\r\n [tags]=\"selectedTags\"\r\n [wrap]=\"true\"\r\n [isCondensed]=\"false\"\r\n [isSubtle]=\"true\"\r\n (tagClosed)=\"removeTag($event)\">\r\n </ec-tags>\r\n }\r\n\r\n <!-- Filter input -->\r\n <input\r\n #filterInput\r\n class=\"multiselect-filter-input\"\r\n type=\"text\"\r\n [id]=\"id + '_filter'\"\r\n [formControl]=\"filterFormModel\"\r\n [placeholder]=\"effectivePlaceholder\"\r\n [tabindex]=\"tabindex\"\r\n (input)=\"onFilterInput()\"\r\n (keydown)=\"keyNavigate($event)\"\r\n (focusout)=\"onBlur()\"\r\n (click)=\"menuStatus === 'visible' ? $event.stopPropagation() : null\"\r\n autocomplete=\"off\"\r\n aria-autocomplete=\"list\"\r\n [attr.aria-controls]=\"menuStatus === 'visible' ? id + '_menu_list' : null\"\r\n [attr.aria-activedescendant]=\"activeDescendantId\"\r\n [attr.aria-labelledby]=\"label ? id + '_label' : null\"\r\n [attr.aria-label]=\"!label ? effectivePlaceholder : null\"\r\n [attr.aria-describedby]=\"selectedItems.length > 0 ? id + '_selection_status' : null\" />\r\n\r\n <i class=\"ec-icon icon-required\"></i>\r\n <i class=\"ec-icon icon-invalid\"></i>\r\n </div>\r\n\r\n <!-- Live region for selection announcements -->\r\n <span [id]=\"id + '_selection_status'\"\r\n class=\"sr-only\"\r\n aria-live=\"polite\"\r\n aria-atomic=\"true\">\r\n {{ 'Multiselect_LiveRegion_Status_SC' | translate:{ count: selectedItems.length } }}\r\n </span>\r\n\r\n <!-- Clear button -->\r\n @if (selectedItems.length > 0 && !formModel.disabled && !readonly) {\r\n <ec-button class=\"multiselect-clear-btn\"\r\n [id]=\"id + '_clear'\"\r\n icon=\"icon-cancel\"\r\n type=\"secondary\"\r\n [tabindex]=\"0\"\r\n attr.aria-label=\"{{'ClearAllSelections_SC' | translate}}\"\r\n (click)=\"clearAll(); $event.stopPropagation()\">\r\n </ec-button>\r\n }\r\n\r\n <!-- Toggle button -->\r\n <ec-button class=\"textbox-group-btn-right\"\r\n [id]=\"id + '_button'\"\r\n [disabled]=\"formModel.disabled\"\r\n icon=\"icon-caret-down\"\r\n [tabindex]=\"0\"\r\n type=\"secondary\"\r\n [attr.aria-label]=\"'Toggle dropdown'\"\r\n [attr.aria-expanded]=\"menuStatus === 'visible'\"\r\n (click)=\"toggleMenu($event)\"\r\n [class.active]=\"menuStatus === 'visible'\">\r\n </ec-button>\r\n </div>\r\n</div>\r\n\r\n<ng-template #dropdownmenu>\r\n <div class=\"popup\"\r\n ecOverlay\r\n role=\"listbox\"\r\n [attr.id]=\"id + '_listbox'\"\r\n [attr.aria-label]=\"label ? (label | translate) : effectivePlaceholder\"\r\n aria-multiselectable=\"true\"\r\n [status]=\"'hasData'\">\r\n\r\n <ec-menu id=\"{{id}}_menu\"\r\n [class.border-top-0]=\"!filteredOptions.length && hideNoMatches && !showAddCustomOption\"\r\n [templateType]=\"'checkAndLabel'\"\r\n [customMenuTemplate]=\"checkboxMenuTemplate\"\r\n [showNoItems]=\"!hideNoMatches && !showAddCustomOption\"\r\n [noDataText]=\"noMatchesText\"\r\n [items]=\"filteredOptions\"\r\n [selected]=\"highlightedItem\"\r\n (selectedChanged)=\"onItemSelected($event)\"\r\n [truncateItems]=\"truncateItems\"\r\n [maintainSelectedItem]=\"false\">\r\n </ec-menu>\r\n\r\n @if (showAddCustomOption) {\r\n <div class=\"add-custom-option\"\r\n role=\"option\"\r\n attr.aria-label=\"{{'AddCustomItem_TC' | translate:{ customItem: filterFormModel.value } }}\"\r\n tabindex=\"-1\"\r\n (click)=\"addCustomItem()\">\r\n <i class=\"ec-icon icon-plus pr-2\" aria-hidden=\"true\"></i>\r\n <span class=\"label\">{{ 'AddCustomItem_TC' | translate:{ customItem: filterFormModel.value } }}</span>\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n\r\n<!-- Custom checkbox menu item template -->\r\n<ng-template #checkboxMenuTemplate let-item>\r\n <div class=\"d-flex\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"!!item.checked\"\r\n [attr.aria-label]=\"item.label\">\r\n @if (item === selectAllItem) {\r\n <ec-checkbox\r\n id=\"{{id}}_selectAll_checkbox\"\r\n class=\"m-0 pr-3\"\r\n [formModel]=\"selectAllFormControl\"\r\n [dependentCheckboxesGroup]=\"selectAllDependentGroup\"\r\n [label]=\"item.label\"\r\n (click)=\"$event.stopPropagation()\">\r\n </ec-checkbox>\r\n } @else {\r\n @if (item.display !== 'heading' && item.display !== 'heading-divided-section') {\r\n <ec-checkbox\r\n id=\"{{item.id || id + '_checkbox_' + item.label}}\"\r\n class=\"m-0 pr-3\"\r\n [formModel]=\"getCheckboxFormControl(item)\"\r\n [attr.aria-label]=\"item.label\"\r\n [label]=\"item.label\"\r\n [icon]=\"item.icon\"\r\n (click)=\"$event.stopPropagation()\">\r\n </ec-checkbox>\r\n } @else {\r\n <span>{{item.label}}</span>\r\n }\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: [":host{color:var(--ec-form-control-color);font-size:var(--ec-form-control-font-size);display:block;margin-bottom:1rem;width:100%}:host :host-context(.form-condensed){margin-bottom:.5rem}:host .control{width:100%;display:flex;flex-direction:column}:host .control.control-label-bottom{flex-direction:column-reverse}:host .control.control-label-left{flex-direction:row}:host .control.control-label-left label{margin-right:.25rem}:host .control.control-label-right{flex-direction:row-reverse}:host .control.control-label-right label{margin-left:.25rem}:host .control.control-label-left,:host .control.control-label-right{align-items:center}:host .control.control-label-left label,:host .control.control-label-right label{flex:1 1;margin-top:0;margin-bottom:0}:host .control.control-label-left .control-input,:host .control.control-label-right .control-input{flex:2 2}:host .control.is-readonly input,:host .control.is-readonly select,:host .control.is-readonly textarea{border-color:var(--ec-form-control-border-color-readonly);background-color:var(--ec-form-control-background-color-readonly);background-clip:border-box;background-image:none;color:var(--ec-form-control-color-readonly);opacity:1;-webkit-user-select:none;user-select:none;pointer-events:none;overflow:hidden;white-space:nowrap}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid)}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid:focus,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid:focus{border-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid:hover:not(:disabled):not(:focus),:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid:hover:not(:disabled):not(:focus){border-color:var(--ec-form-control-border-color-hover)}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid{background-repeat:no-repeat;background-position:.5rem center;background-size:1rem,1rem;padding-left:1.75rem;background-image:none}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-invalid,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-invalid~.icon-required,:host .control.invalid .textbox-group-input ::ng-deep .control input.ng-valid~.icon-required{display:none}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button:not(:focus){border-color:var(--ec-form-control-border-color-invalid)}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button{background-color:var(--ec-form-control-background-color-invalid)}:host .control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button:hover:not(:disabled):not(:focus){border-color:var(--ec-form-control-border-color-hover)}:host .textbox-group{display:flex;position:relative}:host textarea:focus,:host input:focus,:host select:focus{outline:none}:host .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.control.invalid .multiselect-input{background-color:var(--ec-form-control-background-color-invalid);border-color:var(--ec-form-control-border-color-invalid)}.control.invalid .multiselect-input.is-focused{border-color:var(--ec-form-control-background-color-invalid)}.control.invalid .multiselect-input .icon-invalid{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}.control.invalid .multiselect-input .icon-required{display:none}.control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button:not(:focus){border-color:var(--ec-form-control-border-color-invalid)}.control.invalid:not(.open) .textbox-group-btn-right ::ng-deep button{background-color:var(--ec-form-control-background-color-invalid)}.control.invalid:not(.open) .multiselect-clear-btn ::ng-deep button:not(:focus){border-color:var(--ec-form-control-border-color-invalid)}.control.invalid:not(.open) .multiselect-clear-btn ::ng-deep button{background-color:var(--ec-form-control-background-color-invalid)}.control.invalid:not(.open) .textbox-group:hover .multiselect-input:not(.is-focused){border-color:var(--ec-form-control-border-color-hover)}.control.invalid:not(.open) .textbox-group:hover .multiselect-clear-btn ::ng-deep button:not(:focus){border-color:var(--ec-form-control-border-color-hover)}.control.invalid:not(.open) .textbox-group:hover .textbox-group-btn-right ::ng-deep button:not(:disabled):not(:focus){border-color:var(--ec-form-control-border-color-hover)}.control{position:relative}.control.is-disabled:not(.is-readonly) .multiselect-input{opacity:var(--ec-form-control-opacity-disabled);background-color:var(--ec-form-control-background-color-disabled);border-color:var(--ec-form-control-border-color-disabled);pointer-events:none}.control.is-readonly .multiselect-input{border-right-width:1px;border-top-right-radius:var(--ec-border-radius);border-bottom-right-radius:var(--ec-border-radius)}.control.is-readonly ec-button{display:none}.control:not(.is-disabled):not(.is-readonly) .textbox-group:hover .multiselect-input:not(.is-focused){border-color:var(--ec-form-control-border-color-hover)}.control:not(.is-disabled):not(.is-readonly) .textbox-group:hover .multiselect-clear-btn{--ec-button-border-color-secondary: var(--ec-form-control-border-color-hover)}.control:not(.is-disabled):not(.is-readonly) .textbox-group:hover ec-button.textbox-group-btn-right{--ec-button-border-color-secondary: var(--ec-form-control-border-color-hover)}label{color:var(--ec-form-control-label-color, var(--ec-color-secondary-dark));display:block;font-size:var(--ec-font-size-label);line-height:1;margin:calc(var(--ec-font-size-label) / 2) 0}.textbox-group{align-items:stretch}.multiselect-input{display:flex;flex-wrap:wrap;align-items:center;min-height:2rem;max-height:6rem;overflow-y:auto;padding:0 .5rem;background-color:var(--ec-form-control-background-color);border:1px solid var(--ec-form-control-border-color);border-top-left-radius:var(--ec-border-radius);border-bottom-left-radius:var(--ec-border-radius);border-right:0;cursor:text;flex:1 1;box-sizing:border-box;position:relative}.multiselect-input .icon-required,.multiselect-input .icon-invalid{display:none;color:var(--ec-form-control-border-color-invalid)}.multiselect-input.is-required.is-empty{padding-left:1.75rem}.multiselect-input.is-required.is-empty .icon-required{display:inline-flex;position:absolute;left:.5rem;top:.5rem;z-index:1}.multiselect-input.is-focused{border-color:var(--ec-form-control-border-color-focus);box-shadow:var(--ec-form-control-box-shadow-focus);z-index:1}.multiselect-filter-input{border:0;outline:none;background:transparent;flex:1 1 100%;min-width:4rem;height:1.625rem;padding:0;font-size:var(--ec-form-control-font-size);color:var(--ec-form-control-color);line-height:1.25rem;overflow:hidden;text-overflow:ellipsis}.multiselect-filter-input::placeholder{color:var(--ec-form-control-color-placeholder);text-overflow:ellipsis}.has-selections:not(.is-focused) .multiselect-filter-input{width:0;min-width:0;flex:0 0 0;height:0;overflow:hidden;padding:0;margin:0}.popup{background-color:var(--ec-background-color);border-radius:var(--ec-border-radius-card);box-shadow:var(--ec-box-shadow-overlay);margin-top:.25rem;overflow:hidden;z-index:var(--ec-z-index-popup)}ec-menu{display:block}ec-menu ::ng-deep ul{max-height:30vh}ec-menu ::ng-deep ul li{white-space:nowrap}ec-menu ::ng-deep ul li.is-checked .icon-check{opacity:1;color:var(--ec-color-interactive)}ec-menu ::ng-deep ul li.select-all-item{font-weight:600}.multiselect-clear-btn{--ec-button-border-color-secondary: var(--ec-form-control-border-color);--ec-button-border-color-secondary-focus: var(--ec-form-control-border-color);--ec-button-color-icon-secondary: var(--ec-color-icon)}.multiselect-clear-btn ::ng-deep button{height:100%;border-left:0;border-right:0;border-radius:0;padding:0 .375rem}.multiselect-clear-btn ::ng-deep button .ec-icon{font-size:.75rem}ec-button.textbox-group-btn-right{--ec-button-border-color-secondary: var(--ec-form-control-border-color);--ec-button-border-color-secondary-focus: var(--ec-form-control-border-color);--ec-button-color-icon-secondary: var(--ec-color-icon)}ec-button.textbox-group-btn-right ::ng-deep button{height:100%;border-top-right-radius:var(--ec-border-radius);border-bottom-right-radius:var(--ec-border-radius)}.open .textbox-group ec-textbox{--ec-form-control-box-shadow-focus: none;--ec-form-control-border-color-focus: var(--ec-form-control-border-color)}.open .textbox-group ec-button{--ec-button-box-shadow-focus-secondary: none;--ec-button-background-color-secondary: var(--ec-background-color-selected)}.add-custom-option{display:flex;align-items:center;padding:.5rem .75rem;cursor:pointer;border-top:1px solid var(--ec-form-control-border-color, #ccc);color:var(--ec-color-text);font-size:.875rem}.add-custom-option:hover{background-color:var(--ec-color-background-hover, #f5f5f5)}.add-custom-option .label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "ec-button", inputs: ["id", "disabled", "lockButton", "icon", "iconColor", "label", "badge", "tabindex", "type", "pending", "pendingIcon", "customTemplate", "isSubmit", "autofocus"], outputs: ["clicked"] }, { kind: "component", type: ViewOverlayComponent, selector: "[ecOverlay]", inputs: ["status", "message", "action", "noDataTemplate", "displayAsMask", "overlayClassList"] }, { kind: "component", type: MenuComponent, selector: "ec-menu", inputs: ["id", "items", "selected", "parent", "templateType", "customMenuTemplate", "title", "showNoItems", "noDataText", "enableKeyNav", "highlightedItem", "maintainSelectedItem", "truncateItems", "preserveIconSpace", "dropdownToggleButton"], outputs: ["selectedChanged", "menuClosed"] }, { kind: "directive", type: PopupContainerDirective, selector: "[ecPopup]", inputs: ["ecPopup", "options"] }, { kind: "component", type: CheckboxComponent, selector: "ec-checkbox", inputs: ["name", "icon", "dependentCheckboxesGroup", "ignoreDisabledDependents"] }, { kind: "component", type: TagsComponent, selector: "ec-tags", inputs: ["id", "tags", "wrap", "isCondensed", "isSubtle", "highlightText"], outputs: ["tagClosed"] }, { kind: "component", type: HelpPopoverComponent, selector: "ec-help-popover", inputs: ["id", "text", "contentPosition", "maxWidth"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }] }); }