@eui/eds 22.0.0-alpha.33 → 22.0.0-alpha.35

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.
@@ -12653,12 +12653,12 @@
12653
12653
  },
12654
12654
  {
12655
12655
  "name": "EdsMaxlengthDirective",
12656
- "id": "directive-EdsMaxlengthDirective-c80304ea49d2dc5f5235c4e3d0542f114e9ffc94d0188a9c5b52c7c60ca08c012f7106d6e56fb76648573d14f3418af240d90d5cd78f1f8e25923b580e16d1ed",
12656
+ "id": "directive-EdsMaxlengthDirective-d07ceb165ce10f9c8985f2d45d61ab1abf9f3c483945664731038dfef7b24962c038f2bb852d93f8fb6d10f57d8f6e64978e5961d64fbcd799148cd9a53398d1",
12657
12657
  "file": "packages/eds/shared/eds-maxlength.directive.ts",
12658
12658
  "type": "directive",
12659
12659
  "description": "<p>Directive that adds maxLength functionality to input/textarea elements with visual feedback.\nShows remaining characters count and provides visual indication when the limit is reached.</p>\n<b>Example :</b><div><pre class=\"line-numbers\"><code class=\"language-html\">&lt;input edsInputText [edsMaxlength]=&quot;100&quot; /&gt;\n&lt;textarea [edsMaxlength]=&quot;200&quot; [isShowMaxlength]=&quot;false&quot;&gt;&lt;/textarea&gt;</code></pre></div>",
12660
12660
  "rawdescription": "\n\nDirective that adds maxLength functionality to input/textarea elements with visual feedback.\nShows remaining characters count and provides visual indication when the limit is reached.\n\n```html\n<input edsInputText [edsMaxlength]=\"100\" />\n<textarea [edsMaxlength]=\"200\" [isShowMaxlength]=\"false\"></textarea>\n```\n",
12661
- "sourceCode": "import {\n Directive,\n ElementRef,\n Renderer2,\n inject,\n input,\n output,\n numberAttribute,\n booleanAttribute,\n AfterContentInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n} from '@angular/core';\nimport { NgControl } from '@angular/forms';\nimport { Subscription } from 'rxjs';\n\n/**\n * @description\n * Directive that adds maxLength functionality to input/textarea elements with visual feedback.\n * Shows remaining characters count and provides visual indication when the limit is reached.\n *\n * @usageNotes\n * ```html\n * <input edsInputText [edsMaxlength]=\"100\" />\n * <textarea [edsMaxlength]=\"200\" [isShowMaxlength]=\"false\"></textarea>\n * ```\n */\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: 'input[edsMaxlength], textarea[edsMaxlength], [ngModel][edsMaxlength], [formControlName][edsMaxlength]',\n host: {\n '(input)': '_onInput()',\n '(change)': '_onInput()',\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n '(paste)': '_onPaste($any($event))',\n },\n})\nexport class EdsMaxlengthDirective implements AfterContentInit, OnDestroy, OnChanges {\n /** Maximum character length. Set to null to disable. */\n edsMaxlength = input(0, { transform: numberAttribute });\n\n /** Controls visibility of the maxLength counter indicator. */\n isShowMaxlength = input(true, { transform: booleanAttribute });\n\n /** Emits true when max length is reached, false when below limit. */\n maxLengthReached = output<boolean>();\n\n private _el = inject(ElementRef);\n private _renderer = inject(Renderer2);\n private _control = inject(NgControl, { self: true, optional: true });\n private _maxLengthSpan: HTMLSpanElement | undefined;\n private _wrapper: HTMLDivElement | undefined;\n private _valueChangesSub: Subscription | undefined;\n\n ngAfterContentInit(): void {\n if (this.edsMaxlength()) {\n this._createMaxLengthBox();\n this._refresh();\n }\n\n if (this._control?.valueChanges) {\n this._valueChangesSub = this._control.valueChanges.subscribe(() => this._refresh());\n }\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['isShowMaxlength']) {\n if (this.isShowMaxlength()) {\n this._createMaxLengthBox();\n this._refresh();\n } else {\n this._destroyMaxLengthBox();\n }\n }\n if (changes['edsMaxlength']) {\n const maxLen = this.edsMaxlength();\n if (maxLen < 0) {\n throw new Error('edsMaxlength cannot be negative.');\n }\n if (maxLen) {\n if (this.isShowMaxlength() && !this._maxLengthSpan) {\n this._createMaxLengthBox();\n }\n this._refresh();\n } else {\n this._renderer.removeAttribute(this._el.nativeElement, 'maxLength');\n this._destroyMaxLengthBox();\n }\n }\n }\n\n ngOnDestroy(): void {\n this._destroyMaxLengthBox();\n this._valueChangesSub?.unsubscribe();\n }\n\n protected _onInput(): void {\n this._refresh();\n }\n\n protected _onPaste(event: ClipboardEvent): void {\n const value = (event.clipboardData || (window as unknown as Record<string, unknown>)['clipboardData'] as DataTransfer | undefined)?.getData('text') || '';\n const input = this._el.nativeElement as HTMLInputElement;\n if (this.edsMaxlength()) {\n this._renderer.setProperty(input, 'maxLength', this._calculateMaxLength(value));\n }\n }\n\n private _calculateMaxLength(pastedValue?: string): number {\n const currentValue = this._getValue();\n const currentLen = currentValue?.length || 0;\n const valueLen = this._getValueLength();\n return (pastedValue?.length || currentLen - valueLen) + this.edsMaxlength();\n }\n\n private _getValueLength(): number {\n return this._getValue()?.length || 0;\n }\n\n private _getValue(): string {\n const input = this._el.nativeElement as HTMLInputElement;\n return this._control && this._control.control?.updateOn === 'change'\n ? this._control.value\n : input.value;\n }\n\n private _refresh(): void {\n const maxLen = this.edsMaxlength();\n if (isNaN(maxLen) || !maxLen) {\n return;\n }\n const input = this._el.nativeElement as HTMLInputElement;\n const valueLength = this._getValueLength();\n const remaining = maxLen - valueLength;\n\n this._renderer.setProperty(input, 'maxLength', this._calculateMaxLength());\n\n if (this._maxLengthSpan) {\n if (remaining <= 0) {\n this._maxLengthSpan.style.backgroundColor = 'var(--eds-c-surface-warning)';\n this._maxLengthSpan.style.borderColor = 'var(--eds-c-border-warning)';\n this._maxLengthSpan.style.color = 'var(--eds-c-foreground-critical)';\n this.maxLengthReached.emit(true);\n } else {\n this._maxLengthSpan.style.backgroundColor = 'var(--eds-c-surface-elevation)';\n this._maxLengthSpan.style.borderColor = 'var(--eds-c-border-neutral)';\n this._maxLengthSpan.style.color = 'var(--eds-c-foreground)';\n this.maxLengthReached.emit(false);\n }\n\n if (remaining < 0) {\n if (this._control) {\n this._control.control!.setValue(input.value.substring(0, this._calculateMaxLength()));\n } else {\n input.value = input.value.substring(0, this._calculateMaxLength());\n }\n this._renderer.setProperty(this._maxLengthSpan, 'textContent', '0');\n } else {\n this._renderer.setProperty(this._maxLengthSpan, 'textContent', remaining.toString());\n }\n }\n }\n\n private _createMaxLengthBox(): void {\n if (this._maxLengthSpan) {\n this._destroyMaxLengthBox();\n }\n\n this._maxLengthSpan = this._renderer.createElement('span');\n this._renderer.addClass(this._maxLengthSpan, 'eds-maxlength');\n this._renderer.setProperty(this._maxLengthSpan, 'textContent', (this.edsMaxlength() || 0).toString());\n // Inline styles (self-contained, no global stylesheet dependency)\n this._maxLengthSpan.setAttribute('style', [\n 'font: var(--eds-f-microcopy-xs)',\n 'background-color: var(--eds-c-surface-neutral-subtle, #f0f0f0)',\n 'border: 1px solid var(--eds-c-border-neutral)',\n 'border-radius: var(--eds-br-xs)',\n 'color: var(--eds-c-foreground)',\n 'padding: 0 var(--eds-sp-3xs)',\n 'position: absolute',\n 'right: calc(-1 * var(--eds-sp-xs))',\n 'top: calc(-1 * var(--eds-sp-xs))',\n 'z-index: 1',\n 'line-height: 1.6',\n ].join(';'));\n\n const nativeElement: HTMLElement = this._el.nativeElement;\n const parent = nativeElement.parentNode;\n if (parent && this.isShowMaxlength()) {\n this._renderer.setStyle(nativeElement, 'width', '100%');\n this._wrapper = this._renderer.createElement('div');\n this._renderer.addClass(this._wrapper, 'eds-maxlength-wrapper');\n this._wrapper.setAttribute('style', 'position: relative; width: 100%;');\n parent.replaceChild(this._wrapper, nativeElement);\n this._renderer.appendChild(this._wrapper, nativeElement);\n this._renderer.appendChild(this._wrapper, this._maxLengthSpan);\n }\n }\n\n private _destroyMaxLengthBox(): void {\n if (this._maxLengthSpan) {\n this._renderer.removeChild(this._maxLengthSpan.parentElement, this._maxLengthSpan);\n this._maxLengthSpan = undefined;\n }\n if (this._wrapper) {\n const parent = this._wrapper.parentNode;\n const nativeElement = this._el.nativeElement;\n parent?.replaceChild(nativeElement, this._wrapper);\n this._wrapper = undefined;\n }\n }\n}\n",
12661
+ "sourceCode": "import {\n Directive,\n ElementRef,\n Renderer2,\n inject,\n input,\n output,\n numberAttribute,\n booleanAttribute,\n AfterContentInit,\n OnDestroy,\n OnChanges,\n SimpleChanges,\n} from '@angular/core';\nimport { NgControl } from '@angular/forms';\nimport { Subscription } from 'rxjs';\n\n/**\n * @description\n * Directive that adds maxLength functionality to input/textarea elements with visual feedback.\n * Shows remaining characters count and provides visual indication when the limit is reached.\n *\n * @usageNotes\n * ```html\n * <input edsInputText [edsMaxlength]=\"100\" />\n * <textarea [edsMaxlength]=\"200\" [isShowMaxlength]=\"false\"></textarea>\n * ```\n */\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: 'input[edsMaxlength], textarea[edsMaxlength], [ngModel][edsMaxlength], [formControlName][edsMaxlength]',\n host: {\n '(input)': '_onInput()',\n '(change)': '_onInput()',\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n '(paste)': '_onPaste($any($event))',\n },\n})\nexport class EdsMaxlengthDirective implements AfterContentInit, OnDestroy, OnChanges {\n /** Maximum character length. Set to null to disable. */\n edsMaxlength = input(0, { transform: numberAttribute });\n\n /** Controls visibility of the maxLength counter indicator. */\n isShowMaxlength = input(true, { transform: booleanAttribute });\n\n /** Emits true when max length is reached, false when below limit. */\n maxLengthReached = output<boolean>();\n\n private _el = inject(ElementRef);\n private _renderer = inject(Renderer2);\n private _control = inject(NgControl, { self: true, optional: true });\n private _maxLengthSpan: HTMLSpanElement | undefined;\n private _wrapper: HTMLDivElement | undefined;\n private _valueChangesSub: Subscription | undefined;\n\n ngAfterContentInit(): void {\n if (this.edsMaxlength()) {\n this._createMaxLengthBox();\n this._refresh();\n }\n\n if (this._control?.valueChanges) {\n this._valueChangesSub = this._control.valueChanges.subscribe(() => this._refresh());\n }\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['isShowMaxlength']) {\n if (this.isShowMaxlength()) {\n this._createMaxLengthBox();\n this._refresh();\n } else {\n this._destroyMaxLengthBox();\n }\n }\n if (changes['edsMaxlength']) {\n const maxLen = this.edsMaxlength();\n if (maxLen < 0) {\n throw new Error('edsMaxlength cannot be negative.');\n }\n if (maxLen) {\n if (this.isShowMaxlength() && !this._maxLengthSpan) {\n this._createMaxLengthBox();\n }\n this._refresh();\n } else {\n this._renderer.removeAttribute(this._el.nativeElement, 'maxLength');\n this._destroyMaxLengthBox();\n }\n }\n }\n\n ngOnDestroy(): void {\n this._destroyMaxLengthBox();\n this._valueChangesSub?.unsubscribe();\n }\n\n protected _onInput(): void {\n this._refresh();\n }\n\n protected _onPaste(event: ClipboardEvent): void {\n const value = (event.clipboardData || (window as unknown as Record<string, unknown>)['clipboardData'] as DataTransfer | undefined)?.getData('text') || '';\n const input = this._el.nativeElement as HTMLInputElement;\n if (this.edsMaxlength()) {\n this._renderer.setProperty(input, 'maxLength', this._calculateMaxLength(value));\n }\n }\n\n private _calculateMaxLength(pastedValue?: string): number {\n const currentValue = this._getValue();\n const currentLen = currentValue?.length || 0;\n const valueLen = this._getValueLength();\n return (pastedValue?.length || currentLen - valueLen) + this.edsMaxlength();\n }\n\n private _getValueLength(): number {\n return this._getValue()?.length || 0;\n }\n\n private _getValue(): string {\n const input = this._el.nativeElement as HTMLInputElement;\n return this._control && this._control.control?.updateOn === 'change'\n ? this._control.value\n : input.value;\n }\n\n private _refresh(): void {\n const maxLen = this.edsMaxlength();\n if (isNaN(maxLen) || !maxLen) {\n return;\n }\n const input = this._el.nativeElement as HTMLInputElement;\n const valueLength = this._getValueLength();\n const remaining = maxLen - valueLength;\n\n this._renderer.setProperty(input, 'maxLength', this._calculateMaxLength());\n\n if (this._maxLengthSpan) {\n if (remaining <= 0) {\n this._maxLengthSpan.style.color = 'var(--eds-c-foreground-critical)';\n this.maxLengthReached.emit(true);\n } else {\n this._maxLengthSpan.style.color = 'var(--eds-c-foreground-subtler)';\n this.maxLengthReached.emit(false);\n }\n\n if (remaining < 0) {\n if (this._control) {\n this._control.control!.setValue(input.value.substring(0, this._calculateMaxLength()));\n } else {\n input.value = input.value.substring(0, this._calculateMaxLength());\n }\n this._renderer.setProperty(this._maxLengthSpan, 'textContent', `${maxLen} / ${maxLen}`);\n } else {\n // Display current character count over the maximum, e.g. \"12 / 100\".\n this._renderer.setProperty(this._maxLengthSpan, 'textContent', `${valueLength} / ${maxLen}`);\n }\n }\n }\n\n private _createMaxLengthBox(): void {\n if (this._maxLengthSpan) {\n this._destroyMaxLengthBox();\n }\n\n this._maxLengthSpan = this._renderer.createElement('span');\n this._renderer.addClass(this._maxLengthSpan, 'eds-maxlength');\n const initialLength = Math.min(this._getValueLength(), this.edsMaxlength() || 0);\n this._renderer.setProperty(this._maxLengthSpan, 'textContent', `${initialLength} / ${this.edsMaxlength() || 0}`);\n // Inline styles (self-contained, no global stylesheet dependency).\n // No border, no background — only the foreground color. The counter sits\n // above the top-right corner of the element it is attached to.\n this._maxLengthSpan.setAttribute('style', [\n 'font: var(--eds-f-microcopy-xs)',\n 'color: var(--eds-c-foreground-subtler)',\n 'padding: 0 var(--eds-sp-3xs)',\n 'position: absolute',\n 'right: 0',\n 'bottom: 100%',\n 'z-index: 1',\n 'line-height: 1.6',\n 'pointer-events: none',\n ].join(';'));\n\n const nativeElement: HTMLElement = this._el.nativeElement;\n const parent = nativeElement.parentNode;\n if (parent && this.isShowMaxlength()) {\n this._renderer.setStyle(nativeElement, 'width', '100%');\n this._wrapper = this._renderer.createElement('div');\n this._renderer.addClass(this._wrapper, 'eds-maxlength-wrapper');\n this._wrapper.setAttribute('style', 'position: relative; width: 100%;');\n parent.replaceChild(this._wrapper, nativeElement);\n this._renderer.appendChild(this._wrapper, nativeElement);\n this._renderer.appendChild(this._wrapper, this._maxLengthSpan);\n }\n }\n\n private _destroyMaxLengthBox(): void {\n if (this._maxLengthSpan) {\n this._renderer.removeChild(this._maxLengthSpan.parentElement, this._maxLengthSpan);\n this._maxLengthSpan = undefined;\n }\n if (this._wrapper) {\n const parent = this._wrapper.parentNode;\n const nativeElement = this._el.nativeElement;\n parent?.replaceChild(nativeElement, this._wrapper);\n this._wrapper = undefined;\n }\n }\n}\n",
12662
12662
  "selector": "input[edsMaxlength], textarea[edsMaxlength], [ngModel][edsMaxlength], [formControlName][edsMaxlength]",
12663
12663
  "providers": [],
12664
12664
  "hostDirectives": [],
@@ -51416,7 +51416,7 @@
51416
51416
  },
51417
51417
  {
51418
51418
  "name": "EdsMultiselect",
51419
- "id": "component-EdsMultiselect-38cfb435a8a2aadd759f9db4802d988bb624c697085ce6512b133361b054af31b2b9b858e35bdfbe55a65359cbb2d11a2982d46569894ddbd28843b49bd58a54",
51419
+ "id": "component-EdsMultiselect-e011ce4eae884a3661c043c9015b190fbfdfeda0d6aab7c04238498731ec74efc62c28f94dbda0f7bc30e9bec3121bd0c9fb4abdff01e26a243d8f6509f58cb3",
51420
51420
  "file": "packages/eds/eds-multiselect/eds-multiselect.ts",
51421
51421
  "changeDetection": "ChangeDetectionStrategy.OnPush",
51422
51422
  "encapsulation": [],
@@ -51451,20 +51451,20 @@
51451
51451
  "indexKey": "",
51452
51452
  "optional": false,
51453
51453
  "description": "<p>Aria-label for the combobox trigger.\n&#39;Select options&#39;</p>\n",
51454
- "line": 119,
51454
+ "line": 121,
51455
51455
  "rawdescription": "\n\nAria-label for the combobox trigger.\n'Select options'\n",
51456
51456
  "jsdoctags": [
51457
51457
  {
51458
- "pos": 3662,
51459
- "end": 3693,
51458
+ "pos": 3816,
51459
+ "end": 3847,
51460
51460
  "kind": 328,
51461
51461
  "id": 0,
51462
51462
  "flags": 16842752,
51463
51463
  "modifierFlagsCache": 0,
51464
51464
  "transformFlags": 0,
51465
51465
  "tagName": {
51466
- "pos": 3663,
51467
- "end": 3670,
51466
+ "pos": 3817,
51467
+ "end": 3824,
51468
51468
  "kind": 80,
51469
51469
  "id": 0,
51470
51470
  "flags": 16842752,
@@ -51476,6 +51476,76 @@
51476
51476
  ],
51477
51477
  "required": false
51478
51478
  },
51479
+ {
51480
+ "name": "cancelLabel",
51481
+ "coverageIgnore": false,
51482
+ "defaultValue": "'Cancel'",
51483
+ "deprecated": false,
51484
+ "deprecationMessage": "",
51485
+ "type": "string",
51486
+ "indexKey": "",
51487
+ "optional": false,
51488
+ "description": "<p>Label for the panel-footer &quot;Cancel&quot; button.\n&#39;Cancel&#39;</p>\n",
51489
+ "line": 127,
51490
+ "rawdescription": "\n\nLabel for the panel-footer \"Cancel\" button.\n'Cancel'\n",
51491
+ "jsdoctags": [
51492
+ {
51493
+ "pos": 3958,
51494
+ "end": 3981,
51495
+ "kind": 328,
51496
+ "id": 0,
51497
+ "flags": 16842752,
51498
+ "modifierFlagsCache": 0,
51499
+ "transformFlags": 0,
51500
+ "tagName": {
51501
+ "pos": 3959,
51502
+ "end": 3966,
51503
+ "kind": 80,
51504
+ "id": 0,
51505
+ "flags": 16842752,
51506
+ "transformFlags": 0,
51507
+ "escapedText": "default"
51508
+ },
51509
+ "comment": "<p>&#39;Cancel&#39;</p>\n"
51510
+ }
51511
+ ],
51512
+ "required": false
51513
+ },
51514
+ {
51515
+ "name": "confirmLabel",
51516
+ "coverageIgnore": false,
51517
+ "defaultValue": "'Confirm choice'",
51518
+ "deprecated": false,
51519
+ "deprecationMessage": "",
51520
+ "type": "string",
51521
+ "indexKey": "",
51522
+ "optional": false,
51523
+ "description": "<p>Label for the panel-footer &quot;Confirm choice&quot; button.\n&#39;Confirm choice&#39;</p>\n",
51524
+ "line": 133,
51525
+ "rawdescription": "\n\nLabel for the panel-footer \"Confirm choice\" button.\n'Confirm choice'\n",
51526
+ "jsdoctags": [
51527
+ {
51528
+ "pos": 4094,
51529
+ "end": 4125,
51530
+ "kind": 328,
51531
+ "id": 0,
51532
+ "flags": 16842752,
51533
+ "modifierFlagsCache": 0,
51534
+ "transformFlags": 0,
51535
+ "tagName": {
51536
+ "pos": 4095,
51537
+ "end": 4102,
51538
+ "kind": 80,
51539
+ "id": 0,
51540
+ "flags": 16842752,
51541
+ "transformFlags": 0,
51542
+ "escapedText": "default"
51543
+ },
51544
+ "comment": "<p>&#39;Confirm choice&#39;</p>\n"
51545
+ }
51546
+ ],
51547
+ "required": false
51548
+ },
51479
51549
  {
51480
51550
  "name": "disabled",
51481
51551
  "coverageIgnore": false,
@@ -51486,20 +51556,20 @@
51486
51556
  "indexKey": "",
51487
51557
  "optional": false,
51488
51558
  "description": "<p>Controls whether the multiselect is disabled.\nfalse</p>\n",
51489
- "line": 107,
51559
+ "line": 109,
51490
51560
  "rawdescription": "\n\nControls whether the multiselect is disabled.\nfalse\n",
51491
51561
  "jsdoctags": [
51492
51562
  {
51493
- "pos": 3345,
51494
- "end": 3365,
51563
+ "pos": 3499,
51564
+ "end": 3519,
51495
51565
  "kind": 328,
51496
51566
  "id": 0,
51497
51567
  "flags": 16842752,
51498
51568
  "modifierFlagsCache": 0,
51499
51569
  "transformFlags": 0,
51500
51570
  "tagName": {
51501
- "pos": 3346,
51502
- "end": 3353,
51571
+ "pos": 3500,
51572
+ "end": 3507,
51503
51573
  "kind": 80,
51504
51574
  "id": 0,
51505
51575
  "flags": 16842752,
@@ -51521,20 +51591,20 @@
51521
51591
  "indexKey": "",
51522
51592
  "optional": false,
51523
51593
  "description": "<p>Data attribute value for end-to-end testing identification.\n&#39;eds-multiselect&#39;</p>\n",
51524
- "line": 95,
51594
+ "line": 97,
51525
51595
  "rawdescription": "\n\nData attribute value for end-to-end testing identification.\n'eds-multiselect'\n",
51526
51596
  "jsdoctags": [
51527
51597
  {
51528
- "pos": 3044,
51529
- "end": 3076,
51598
+ "pos": 3198,
51599
+ "end": 3230,
51530
51600
  "kind": 328,
51531
51601
  "id": 0,
51532
51602
  "flags": 16842752,
51533
51603
  "modifierFlagsCache": 0,
51534
51604
  "transformFlags": 0,
51535
51605
  "tagName": {
51536
- "pos": 3045,
51537
- "end": 3052,
51606
+ "pos": 3199,
51607
+ "end": 3206,
51538
51608
  "kind": 80,
51539
51609
  "id": 0,
51540
51610
  "flags": 16842752,
@@ -51556,20 +51626,20 @@
51556
51626
  "indexKey": "",
51557
51627
  "optional": false,
51558
51628
  "description": "<p>Controls whether the multiselect shows an invalid/error state.\nfalse</p>\n",
51559
- "line": 113,
51629
+ "line": 115,
51560
51630
  "rawdescription": "\n\nControls whether the multiselect shows an invalid/error state.\nfalse\n",
51561
51631
  "jsdoctags": [
51562
51632
  {
51563
- "pos": 3516,
51564
- "end": 3536,
51633
+ "pos": 3670,
51634
+ "end": 3690,
51565
51635
  "kind": 328,
51566
51636
  "id": 0,
51567
51637
  "flags": 16842752,
51568
51638
  "modifierFlagsCache": 0,
51569
51639
  "transformFlags": 0,
51570
51640
  "tagName": {
51571
- "pos": 3517,
51572
- "end": 3524,
51641
+ "pos": 3671,
51642
+ "end": 3678,
51573
51643
  "kind": 80,
51574
51644
  "id": 0,
51575
51645
  "flags": 16842752,
@@ -51591,20 +51661,20 @@
51591
51661
  "indexKey": "",
51592
51662
  "optional": false,
51593
51663
  "description": "<p>Placeholder text displayed when no options are selected.\n&#39;Select options&#39;</p>\n",
51594
- "line": 101,
51664
+ "line": 103,
51595
51665
  "rawdescription": "\n\nPlaceholder text displayed when no options are selected.\n'Select options'\n",
51596
51666
  "jsdoctags": [
51597
51667
  {
51598
- "pos": 3199,
51599
- "end": 3230,
51668
+ "pos": 3353,
51669
+ "end": 3384,
51600
51670
  "kind": 328,
51601
51671
  "id": 0,
51602
51672
  "flags": 16842752,
51603
51673
  "modifierFlagsCache": 0,
51604
51674
  "transformFlags": 0,
51605
51675
  "tagName": {
51606
- "pos": 3200,
51607
- "end": 3207,
51676
+ "pos": 3354,
51677
+ "end": 3361,
51608
51678
  "kind": 80,
51609
51679
  "id": 0,
51610
51680
  "flags": 16842752,
@@ -51618,6 +51688,32 @@
51618
51688
  }
51619
51689
  ],
51620
51690
  "outputsClass": [
51691
+ {
51692
+ "name": "cancelled",
51693
+ "coverageIgnore": false,
51694
+ "deprecated": false,
51695
+ "deprecationMessage": "",
51696
+ "type": "void",
51697
+ "indexKey": "",
51698
+ "optional": false,
51699
+ "description": "<p>Emits when the &quot;Cancel&quot; button is clicked (selection reverted to its pre-open state).</p>\n",
51700
+ "line": 148,
51701
+ "rawdescription": "\n\nEmits when the \"Cancel\" button is clicked (selection reverted to its pre-open state).\n",
51702
+ "required": false
51703
+ },
51704
+ {
51705
+ "name": "confirmed",
51706
+ "coverageIgnore": false,
51707
+ "deprecated": false,
51708
+ "deprecationMessage": "",
51709
+ "type": "string[]",
51710
+ "indexKey": "",
51711
+ "optional": false,
51712
+ "description": "<p>Emits the confirmed selection when the &quot;Confirm choice&quot; button is clicked.</p>\n",
51713
+ "line": 143,
51714
+ "rawdescription": "\n\nEmits the confirmed selection when the \"Confirm choice\" button is clicked.\n",
51715
+ "required": false
51716
+ },
51621
51717
  {
51622
51718
  "name": "valueChange",
51623
51719
  "coverageIgnore": false,
@@ -51627,7 +51723,7 @@
51627
51723
  "indexKey": "",
51628
51724
  "optional": false,
51629
51725
  "description": "<p>Emits the current selection whenever it changes.</p>\n",
51630
- "line": 124,
51726
+ "line": 138,
51631
51727
  "rawdescription": "\n\nEmits the current selection whenever it changes.\n",
51632
51728
  "required": false
51633
51729
  }
@@ -51643,7 +51739,7 @@
51643
51739
  "indexKey": "",
51644
51740
  "optional": false,
51645
51741
  "description": "<p>The active descendant ID for screen readers.</p>\n",
51646
- "line": 165,
51742
+ "line": 192,
51647
51743
  "rawdescription": "\nThe active descendant ID for screen readers.",
51648
51744
  "modifierKind": [
51649
51745
  124
@@ -51659,7 +51755,7 @@
51659
51755
  "indexKey": "",
51660
51756
  "optional": false,
51661
51757
  "description": "<p>The display value shown in the trigger.</p>\n",
51662
- "line": 148,
51758
+ "line": 175,
51663
51759
  "rawdescription": "\nThe display value shown in the trigger.",
51664
51760
  "modifierKind": [
51665
51761
  124
@@ -51675,7 +51771,7 @@
51675
51771
  "indexKey": "",
51676
51772
  "optional": false,
51677
51773
  "description": "<p>Reference to the dropdown template for the overlay.</p>\n",
51678
- "line": 130,
51774
+ "line": 154,
51679
51775
  "rawdescription": "\nReference to the dropdown template for the overlay.",
51680
51776
  "modifierKind": [
51681
51777
  148
@@ -51691,7 +51787,7 @@
51691
51787
  "indexKey": "",
51692
51788
  "optional": false,
51693
51789
  "description": "<p>Internal disabled state (supports CVA setDisabledState).</p>\n",
51694
- "line": 142,
51790
+ "line": 169,
51695
51791
  "rawdescription": "\nInternal disabled state (supports CVA setDisabledState).",
51696
51792
  "modifierKind": [
51697
51793
  124
@@ -51707,7 +51803,7 @@
51707
51803
  "indexKey": "",
51708
51804
  "optional": false,
51709
51805
  "description": "<p>Internal invalid state.</p>\n",
51710
- "line": 145,
51806
+ "line": 172,
51711
51807
  "rawdescription": "\nInternal invalid state.",
51712
51808
  "modifierKind": [
51713
51809
  124
@@ -51723,7 +51819,7 @@
51723
51819
  "indexKey": "",
51724
51820
  "optional": false,
51725
51821
  "description": "<p>Internal selected values.</p>\n",
51726
- "line": 139,
51822
+ "line": 163,
51727
51823
  "rawdescription": "\nInternal selected values.",
51728
51824
  "modifierKind": [
51729
51825
  124
@@ -51739,7 +51835,7 @@
51739
51835
  "indexKey": "",
51740
51836
  "optional": false,
51741
51837
  "description": "<p>Whether the dropdown popup is open.</p>\n",
51742
- "line": 136,
51838
+ "line": 160,
51743
51839
  "rawdescription": "\nWhether the dropdown popup is open."
51744
51840
  },
51745
51841
  {
@@ -51752,7 +51848,7 @@
51752
51848
  "indexKey": "",
51753
51849
  "optional": false,
51754
51850
  "description": "<p>Whether the placeholder is shown (no selection).</p>\n",
51755
- "line": 162,
51851
+ "line": 189,
51756
51852
  "rawdescription": "\nWhether the placeholder is shown (no selection).",
51757
51853
  "modifierKind": [
51758
51854
  124
@@ -51768,7 +51864,7 @@
51768
51864
  "indexKey": "",
51769
51865
  "optional": false,
51770
51866
  "description": "<p>The projected option components.</p>\n",
51771
- "line": 127,
51867
+ "line": 151,
51772
51868
  "rawdescription": "\nThe projected option components.",
51773
51869
  "modifierKind": [
51774
51870
  148
@@ -51784,7 +51880,7 @@
51784
51880
  "indexKey": "",
51785
51881
  "optional": false,
51786
51882
  "description": "<p>Reference to the trigger element.</p>\n",
51787
- "line": 133,
51883
+ "line": 157,
51788
51884
  "rawdescription": "\nReference to the trigger element.",
51789
51885
  "modifierKind": [
51790
51886
  148
@@ -51799,7 +51895,7 @@
51799
51895
  "optional": false,
51800
51896
  "returnType": "void",
51801
51897
  "typeParameters": [],
51802
- "line": 189,
51898
+ "line": 216,
51803
51899
  "deprecated": false,
51804
51900
  "deprecationMessage": "",
51805
51901
  "rawdescription": "\n",
@@ -51812,20 +51908,36 @@
51812
51908
  "optional": false,
51813
51909
  "returnType": "void",
51814
51910
  "typeParameters": [],
51815
- "line": 194,
51911
+ "line": 221,
51816
51912
  "deprecated": false,
51817
51913
  "deprecationMessage": "",
51818
51914
  "rawdescription": "\n",
51819
51915
  "description": ""
51820
51916
  },
51821
51917
  {
51822
- "name": "onDoneClick",
51918
+ "name": "onCancelClick",
51823
51919
  "coverageIgnore": false,
51824
51920
  "args": [],
51825
51921
  "optional": false,
51826
51922
  "returnType": "void",
51827
51923
  "typeParameters": [],
51828
- "line": 276,
51924
+ "line": 308,
51925
+ "deprecated": false,
51926
+ "deprecationMessage": "",
51927
+ "rawdescription": "\n",
51928
+ "description": "",
51929
+ "modifierKind": [
51930
+ 124
51931
+ ]
51932
+ },
51933
+ {
51934
+ "name": "onConfirmClick",
51935
+ "coverageIgnore": false,
51936
+ "args": [],
51937
+ "optional": false,
51938
+ "returnType": "void",
51939
+ "typeParameters": [],
51940
+ "line": 303,
51829
51941
  "deprecated": false,
51830
51942
  "deprecationMessage": "",
51831
51943
  "rawdescription": "\n",
@@ -51850,7 +51962,7 @@
51850
51962
  "optional": false,
51851
51963
  "returnType": "void",
51852
51964
  "typeParameters": [],
51853
- "line": 266,
51965
+ "line": 293,
51854
51966
  "deprecated": false,
51855
51967
  "deprecationMessage": "",
51856
51968
  "rawdescription": "\n",
@@ -51888,7 +52000,7 @@
51888
52000
  "optional": false,
51889
52001
  "returnType": "void",
51890
52002
  "typeParameters": [],
51891
- "line": 280,
52003
+ "line": 318,
51892
52004
  "deprecated": false,
51893
52005
  "deprecationMessage": "",
51894
52006
  "rawdescription": "\n",
@@ -51917,7 +52029,7 @@
51917
52029
  "optional": false,
51918
52030
  "returnType": "void",
51919
52031
  "typeParameters": [],
51920
- "line": 221,
52032
+ "line": 248,
51921
52033
  "deprecated": false,
51922
52034
  "deprecationMessage": "",
51923
52035
  "rawdescription": "\n",
@@ -51942,7 +52054,7 @@
51942
52054
  "optional": false,
51943
52055
  "returnType": "void",
51944
52056
  "typeParameters": [],
51945
- "line": 232,
52057
+ "line": 259,
51946
52058
  "deprecated": false,
51947
52059
  "deprecationMessage": "",
51948
52060
  "rawdescription": "\n",
@@ -51990,7 +52102,7 @@
51990
52102
  "optional": false,
51991
52103
  "returnType": "void",
51992
52104
  "typeParameters": [],
51993
- "line": 207,
52105
+ "line": 234,
51994
52106
  "deprecated": false,
51995
52107
  "deprecationMessage": "",
51996
52108
  "rawdescription": "\n",
@@ -52036,7 +52148,7 @@
52036
52148
  "optional": false,
52037
52149
  "returnType": "void",
52038
52150
  "typeParameters": [],
52039
- "line": 211,
52151
+ "line": 238,
52040
52152
  "deprecated": false,
52041
52153
  "deprecationMessage": "",
52042
52154
  "rawdescription": "\n",
@@ -52072,7 +52184,7 @@
52072
52184
  "optional": false,
52073
52185
  "returnType": "void",
52074
52186
  "typeParameters": [],
52075
- "line": 215,
52187
+ "line": 242,
52076
52188
  "deprecated": false,
52077
52189
  "deprecationMessage": "",
52078
52190
  "rawdescription": "\n",
@@ -52107,7 +52219,7 @@
52107
52219
  "optional": false,
52108
52220
  "returnType": "void",
52109
52221
  "typeParameters": [],
52110
- "line": 202,
52222
+ "line": 229,
52111
52223
  "deprecated": false,
52112
52224
  "deprecationMessage": "",
52113
52225
  "rawdescription": "\n",
@@ -52144,33 +52256,39 @@
52144
52256
  },
52145
52257
  {
52146
52258
  "name": "EDS_ICON"
52259
+ },
52260
+ {
52261
+ "name": "EDS_INPUT_CHECKBOX"
52262
+ },
52263
+ {
52264
+ "name": "EDS_BUTTON"
52147
52265
  }
52148
52266
  ],
52149
52267
  "description": "<p>EDS Multiselect component — a combobox-style dropdown that allows multiple selections.\nImplements the WAI-ARIA multiselect listbox pattern with full keyboard navigation.\nStyled to match eds-input-text default/hover/focus/disabled states.</p>\n<p>Integrates with Angular Reactive Forms and Template-driven Forms via ControlValueAccessor.</p>\n<h3>Basic usage</h3>\n<b>Example :</b><div><pre class=\"line-numbers\"><code class=\"language-html\">&lt;eds-multiselect placeholder=&quot;Select labels&quot; [(value)]=&quot;selectedLabels&quot;&gt;\n &lt;eds-multiselect-option value=&quot;important&quot; label=&quot;Important&quot; /&gt;\n &lt;eds-multiselect-option value=&quot;starred&quot; label=&quot;Starred&quot; /&gt;\n &lt;eds-multiselect-option value=&quot;work&quot; label=&quot;Work&quot; /&gt;\n&lt;/eds-multiselect&gt;</code></pre></div><h3>With Reactive Forms</h3>\n<b>Example :</b><div><pre class=\"line-numbers\"><code class=\"language-html\">&lt;eds-multiselect placeholder=&quot;Select options&quot; [formControl]=&quot;myControl&quot;&gt;\n &lt;eds-multiselect-option value=&quot;a&quot; label=&quot;Option A&quot; /&gt;\n &lt;eds-multiselect-option value=&quot;b&quot; label=&quot;Option B&quot; /&gt;\n&lt;/eds-multiselect&gt;</code></pre></div><h3>Accessibility</h3>\n<ul>\n<li>Implements ARIA combobox pattern with <code>role=&quot;combobox&quot;</code> on the trigger</li>\n<li>Listbox popup uses <code>role=&quot;listbox&quot;</code> with <code>aria-multiselectable=&quot;true&quot;</code></li>\n<li>Arrow keys navigate options, Space toggles selection, Escape closes</li>\n<li>Active descendant pattern for screen reader announcements</li>\n</ul>\n",
52150
52268
  "rawdescription": "\n\nEDS Multiselect component — a combobox-style dropdown that allows multiple selections.\nImplements the WAI-ARIA multiselect listbox pattern with full keyboard navigation.\nStyled to match eds-input-text default/hover/focus/disabled states.\n\nIntegrates with Angular Reactive Forms and Template-driven Forms via ControlValueAccessor.\n\n### Basic usage\n```html\n<eds-multiselect placeholder=\"Select labels\" [(value)]=\"selectedLabels\">\n <eds-multiselect-option value=\"important\" label=\"Important\" />\n <eds-multiselect-option value=\"starred\" label=\"Starred\" />\n <eds-multiselect-option value=\"work\" label=\"Work\" />\n</eds-multiselect>\n```\n\n### With Reactive Forms\n```html\n<eds-multiselect placeholder=\"Select options\" [formControl]=\"myControl\">\n <eds-multiselect-option value=\"a\" label=\"Option A\" />\n <eds-multiselect-option value=\"b\" label=\"Option B\" />\n</eds-multiselect>\n```\n\n### Accessibility\n- Implements ARIA combobox pattern with `role=\"combobox\"` on the trigger\n- Listbox popup uses `role=\"listbox\"` with `aria-multiselectable=\"true\"`\n- Arrow keys navigate options, Space toggles selection, Escape closes\n- Active descendant pattern for screen reader announcements\n",
52151
52269
  "type": "component",
52152
- "sourceCode": "import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n contentChildren,\n viewChild,\n ElementRef,\n TemplateRef,\n ViewContainerRef,\n inject,\n booleanAttribute,\n effect,\n OnDestroy,\n AfterContentInit,\n DestroyRef,\n} from '@angular/core';\n/* eslint-disable @typescript-eslint/member-ordering */\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport {\n Overlay,\n OverlayRef,\n OverlayModule,\n FlexibleConnectedPositionStrategy,\n ConnectionPositionPair,\n} from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { ActiveDescendantKeyManager } from '@angular/cdk/a11y';\nimport { A11yModule } from '@angular/cdk/a11y';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { EDS_ICON } from '@eui/eds/eds-icon';\n\nimport { EdsMultiselectOption } from './eds-multiselect-option';\n\n/**\n * @description\n * EDS Multiselect component — a combobox-style dropdown that allows multiple selections.\n * Implements the WAI-ARIA multiselect listbox pattern with full keyboard navigation.\n * Styled to match eds-input-text default/hover/focus/disabled states.\n *\n * Integrates with Angular Reactive Forms and Template-driven Forms via ControlValueAccessor.\n *\n * @usageNotes\n * ### Basic usage\n * ```html\n * <eds-multiselect placeholder=\"Select labels\" [(value)]=\"selectedLabels\">\n * <eds-multiselect-option value=\"important\" label=\"Important\" />\n * <eds-multiselect-option value=\"starred\" label=\"Starred\" />\n * <eds-multiselect-option value=\"work\" label=\"Work\" />\n * </eds-multiselect>\n * ```\n *\n * ### With Reactive Forms\n * ```html\n * <eds-multiselect placeholder=\"Select options\" [formControl]=\"myControl\">\n * <eds-multiselect-option value=\"a\" label=\"Option A\" />\n * <eds-multiselect-option value=\"b\" label=\"Option B\" />\n * </eds-multiselect>\n * ```\n *\n * ### Accessibility\n * - Implements ARIA combobox pattern with `role=\"combobox\"` on the trigger\n * - Listbox popup uses `role=\"listbox\"` with `aria-multiselectable=\"true\"`\n * - Arrow keys navigate options, Space toggles selection, Escape closes\n * - Active descendant pattern for screen reader announcements\n */\n@Component({\n selector: 'eds-multiselect',\n templateUrl: './eds-multiselect.html',\n styleUrl: './eds-multiselect.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [OverlayModule, A11yModule, ...EDS_ICON],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: EdsMultiselect,\n multi: true,\n },\n ],\n host: {\n class: 'eds-multiselect',\n '[attr.disabled]': 'internalDisabled() || null',\n '[attr.invalid]': 'internalInvalid() || null',\n '[attr.open]': 'isOpen() || null',\n '[attr.data-e2e]': 'e2eAttr()',\n },\n})\nexport class EdsMultiselect implements ControlValueAccessor, OnDestroy, AfterContentInit {\n /**\n * Data attribute value for end-to-end testing identification.\n * @default 'eds-multiselect'\n */\n e2eAttr = input('eds-multiselect');\n\n /**\n * Placeholder text displayed when no options are selected.\n * @default 'Select options'\n */\n placeholder = input('Select options');\n\n /**\n * Controls whether the multiselect is disabled.\n * @default false\n */\n disabled = input(false, { transform: booleanAttribute });\n\n /**\n * Controls whether the multiselect shows an invalid/error state.\n * @default false\n */\n isInvalid = input(false, { transform: booleanAttribute });\n\n /**\n * Aria-label for the combobox trigger.\n * @default 'Select options'\n */\n ariaLabel = input('Select options');\n\n /**\n * Emits the current selection whenever it changes.\n */\n valueChange = output<string[]>();\n\n /** The projected option components. */\n readonly options = contentChildren(EdsMultiselectOption);\n\n /** Reference to the dropdown template for the overlay. */\n readonly dropdownTemplate = viewChild<TemplateRef<unknown>>('dropdownTemplate');\n\n /** Reference to the trigger element. */\n readonly triggerRef = viewChild<ElementRef<HTMLElement>>('triggerRef');\n\n /** Whether the dropdown popup is open. */\n isOpen = signal(false);\n\n /** Internal selected values. */\n protected internalValue = signal<string[]>([]);\n\n /** Internal disabled state (supports CVA setDisabledState). */\n protected internalDisabled = signal(false);\n\n /** Internal invalid state. */\n protected internalInvalid = signal(false);\n\n /** The display value shown in the trigger. */\n protected displayValue = computed(() => {\n const selected = this.internalValue();\n if (selected.length === 0) {\n return this.placeholder();\n }\n const opts = this.options();\n const firstLabel = opts.find(o => o.value() === selected[0])?.label() ?? selected[0];\n if (selected.length === 1) {\n return firstLabel;\n }\n return `${firstLabel} + ${selected.length - 1} more`;\n });\n\n /** Whether the placeholder is shown (no selection). */\n protected isPlaceholderShown = computed(() => this.internalValue().length === 0);\n\n /** The active descendant ID for screen readers. */\n protected activeDescendantId = signal<string | null>(null);\n\n private overlay = inject(Overlay);\n private viewContainerRef = inject(ViewContainerRef);\n private destroyRef = inject(DestroyRef);\n private overlayRef: OverlayRef | null = null;\n private templatePortal: TemplatePortal | null = null;\n private keyManager: ActiveDescendantKeyManager<EdsMultiselectOption> | null = null;\n\n constructor() {\n // Sync disabled input → internal state\n effect(() => {\n this.internalDisabled.set(this.disabled());\n });\n\n // Sync isInvalid input → internal state\n effect(() => {\n this.internalInvalid.set(this.isInvalid());\n });\n }\n\n private _onChange: (value: string[]) => void = () => { /* noop */ };\n private _onTouched: () => void = () => { /* noop */ };\n\n ngAfterContentInit(): void {\n // Sync option selected states from internalValue whenever options change\n this.syncOptionsFromValue();\n }\n\n ngOnDestroy(): void {\n this.closeDropdown();\n this.overlayRef?.dispose();\n this.overlayRef = null;\n }\n\n // --- ControlValueAccessor ---\n\n writeValue(value: string[]): void {\n this.internalValue.set(value ?? []);\n this.syncOptionsFromValue();\n }\n\n registerOnChange(fn: (value: string[]) => void): void {\n this._onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.internalDisabled.set(isDisabled);\n }\n\n // --- Trigger interactions ---\n\n protected onTriggerClick(): void {\n if (this.internalDisabled()) {\n return;\n }\n if (this.isOpen()) {\n this.closeDropdown();\n } else {\n this.openDropdown();\n }\n }\n\n protected onTriggerKeydown(event: KeyboardEvent): void {\n if (this.internalDisabled()) {\n return;\n }\n\n switch (event.key) {\n case 'Enter':\n case ' ':\n event.preventDefault();\n if (!this.isOpen()) {\n this.openDropdown();\n }\n break;\n case 'ArrowDown':\n event.preventDefault();\n if (!this.isOpen()) {\n this.openDropdown();\n }\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (!this.isOpen()) {\n this.openDropdown();\n }\n break;\n case 'Escape':\n event.preventDefault();\n if (this.isOpen()) {\n this.closeDropdown();\n }\n break;\n }\n }\n\n protected onOptionClick(option: EdsMultiselectOption): void {\n if (option.isDisabled()) {\n return;\n }\n option.onSelect();\n this.syncValueFromOptions();\n this.keyManager?.setActiveItem(option);\n this.updateActiveDescendant();\n }\n\n protected onDoneClick(): void {\n this.closeDropdown();\n }\n\n protected onPanelKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.keyManager?.setNextItemActive();\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n case 'ArrowUp':\n event.preventDefault();\n this.keyManager?.setPreviousItemActive();\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n case 'Home':\n event.preventDefault();\n this.keyManager?.setFirstItemActive();\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n case 'End':\n event.preventDefault();\n this.keyManager?.setLastItemActive();\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n this.toggleActiveOption();\n break;\n case 'Escape':\n event.preventDefault();\n this.closeDropdown();\n break;\n default:\n // Type-ahead: single printable character moves focus to next matching option\n if (event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey) {\n this.handleTypeAhead(event.key);\n }\n break;\n }\n }\n\n // --- Dropdown management (private) ---\n\n private openDropdown(): void {\n if (this.isOpen() || !this.dropdownTemplate()) {\n return;\n }\n\n const triggerEl = this.triggerRef()?.nativeElement;\n if (!triggerEl) {\n return;\n }\n\n const positionStrategy = this.overlay\n .position()\n .flexibleConnectedTo(triggerEl)\n .withPositions([\n new ConnectionPositionPair(\n { originX: 'start', originY: 'bottom' },\n { overlayX: 'start', overlayY: 'top' },\n ),\n new ConnectionPositionPair(\n { originX: 'start', originY: 'top' },\n { overlayX: 'start', overlayY: 'bottom' },\n ),\n ])\n .withFlexibleDimensions(false)\n .withPush(true);\n\n const scrollStrategy = this.overlay.scrollStrategies.reposition();\n\n this.overlayRef = this.overlay.create({\n positionStrategy,\n scrollStrategy,\n hasBackdrop: true,\n backdropClass: 'cdk-overlay-transparent-backdrop',\n width: triggerEl.offsetWidth,\n disposeOnNavigation: true,\n });\n\n this.templatePortal = new TemplatePortal(this.dropdownTemplate(), this.viewContainerRef);\n this.overlayRef.attach(this.templatePortal);\n\n // Focus the panel wrapper so keyboard events are captured\n const panelEl = this.overlayRef.overlayElement.querySelector<HTMLElement>('.eds-multiselect__panel-wrapper');\n panelEl?.focus();\n\n this.overlayRef.backdropClick()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this.closeDropdown());\n\n this.overlayRef.keydownEvents()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe((event) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n this.closeDropdown();\n }\n });\n\n // Setup key manager\n const opts = this.options();\n if (opts.length > 0) {\n // Reset all active states from previous opening\n opts.forEach(o => o.setInactiveStyles());\n\n this.keyManager = new ActiveDescendantKeyManager(opts as unknown as EdsMultiselectOption[])\n .withVerticalOrientation(true)\n .withHomeAndEnd(true)\n .withWrap();\n\n // APG: If options are selected, focus first selected option; otherwise first option\n const firstSelectedIndex = opts.findIndex(o => o.selected());\n if (firstSelectedIndex >= 0) {\n this.keyManager.setActiveItem(firstSelectedIndex);\n } else {\n this.keyManager.setFirstItemActive();\n }\n this.updateActiveDescendant();\n }\n\n this.isOpen.set(true);\n }\n\n private closeDropdown(): void {\n if (!this.isOpen()) {\n return;\n }\n // Clear active styles on all options\n this.options().forEach(o => o.setInactiveStyles());\n\n this.overlayRef?.detach();\n this.overlayRef?.dispose();\n this.overlayRef = null;\n this.templatePortal = null;\n this.keyManager = null;\n this.activeDescendantId.set(null);\n this.isOpen.set(false);\n this._onTouched();\n this.triggerRef()?.nativeElement?.focus();\n }\n\n private toggleActiveOption(): void {\n const active = this.keyManager?.activeItem;\n if (active && !active.isDisabled()) {\n active.onSelect();\n this.syncValueFromOptions();\n }\n }\n\n private handleTypeAhead(char: string): void {\n const opts = this.options();\n const activeIndex = this.keyManager?.activeItemIndex ?? 0;\n const lowerChar = char.toLowerCase();\n\n // Search from after the active item, then wrap around\n for (let i = 1; i <= opts.length; i++) {\n const index = (activeIndex + i) % opts.length;\n const option = opts[index];\n if (!option.isDisabled() && option.label().toLowerCase().startsWith(lowerChar)) {\n this.keyManager?.setActiveItem(index);\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n }\n }\n }\n\n private scrollActiveOptionIntoView(): void {\n const activeId = this.activeDescendantId();\n if (!activeId || !this.overlayRef) {\n return;\n }\n const activeEl = this.overlayRef.overlayElement.querySelector<HTMLElement>(`#${activeId}`);\n if (activeEl && typeof activeEl.scrollIntoView === 'function') {\n activeEl.scrollIntoView({ block: 'nearest' });\n }\n }\n\n private updateActiveDescendant(): void {\n const active = this.keyManager?.activeItem;\n this.activeDescendantId.set(active ? active.optionId() : null);\n }\n\n private syncValueFromOptions(): void {\n const selectedValues = this.options()\n .filter(o => o.selected())\n .map(o => o.value());\n this.internalValue.set(selectedValues);\n this._onChange(selectedValues);\n this.valueChange.emit(selectedValues);\n }\n\n private syncOptionsFromValue(): void {\n const currentValue = this.internalValue();\n this.options().forEach(option => {\n option.selected.set(currentValue.includes(option.value()));\n });\n }\n}\n",
52270
+ "sourceCode": "import {\n Component,\n ChangeDetectionStrategy,\n input,\n output,\n signal,\n computed,\n contentChildren,\n viewChild,\n ElementRef,\n TemplateRef,\n ViewContainerRef,\n inject,\n booleanAttribute,\n effect,\n OnDestroy,\n AfterContentInit,\n DestroyRef,\n} from '@angular/core';\n/* eslint-disable @typescript-eslint/member-ordering */\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport {\n Overlay,\n OverlayRef,\n OverlayModule,\n FlexibleConnectedPositionStrategy,\n ConnectionPositionPair,\n} from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { ActiveDescendantKeyManager } from '@angular/cdk/a11y';\nimport { A11yModule } from '@angular/cdk/a11y';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { EDS_ICON } from '@eui/eds/eds-icon';\nimport { EDS_INPUT_CHECKBOX } from '@eui/eds/eds-input-checkbox';\nimport { EDS_BUTTON } from '@eui/eds/eds-button';\n\nimport { EdsMultiselectOption } from './eds-multiselect-option';\n\n/**\n * @description\n * EDS Multiselect component — a combobox-style dropdown that allows multiple selections.\n * Implements the WAI-ARIA multiselect listbox pattern with full keyboard navigation.\n * Styled to match eds-input-text default/hover/focus/disabled states.\n *\n * Integrates with Angular Reactive Forms and Template-driven Forms via ControlValueAccessor.\n *\n * @usageNotes\n * ### Basic usage\n * ```html\n * <eds-multiselect placeholder=\"Select labels\" [(value)]=\"selectedLabels\">\n * <eds-multiselect-option value=\"important\" label=\"Important\" />\n * <eds-multiselect-option value=\"starred\" label=\"Starred\" />\n * <eds-multiselect-option value=\"work\" label=\"Work\" />\n * </eds-multiselect>\n * ```\n *\n * ### With Reactive Forms\n * ```html\n * <eds-multiselect placeholder=\"Select options\" [formControl]=\"myControl\">\n * <eds-multiselect-option value=\"a\" label=\"Option A\" />\n * <eds-multiselect-option value=\"b\" label=\"Option B\" />\n * </eds-multiselect>\n * ```\n *\n * ### Accessibility\n * - Implements ARIA combobox pattern with `role=\"combobox\"` on the trigger\n * - Listbox popup uses `role=\"listbox\"` with `aria-multiselectable=\"true\"`\n * - Arrow keys navigate options, Space toggles selection, Escape closes\n * - Active descendant pattern for screen reader announcements\n */\n@Component({\n selector: 'eds-multiselect',\n templateUrl: './eds-multiselect.html',\n styleUrl: './eds-multiselect.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [OverlayModule, A11yModule, ...EDS_ICON, ...EDS_INPUT_CHECKBOX, ...EDS_BUTTON],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: EdsMultiselect,\n multi: true,\n },\n ],\n host: {\n class: 'eds-multiselect',\n '[attr.disabled]': 'internalDisabled() || null',\n '[attr.invalid]': 'internalInvalid() || null',\n '[attr.open]': 'isOpen() || null',\n '[attr.data-e2e]': 'e2eAttr()',\n },\n})\nexport class EdsMultiselect implements ControlValueAccessor, OnDestroy, AfterContentInit {\n /**\n * Data attribute value for end-to-end testing identification.\n * @default 'eds-multiselect'\n */\n e2eAttr = input('eds-multiselect');\n\n /**\n * Placeholder text displayed when no options are selected.\n * @default 'Select options'\n */\n placeholder = input('Select options');\n\n /**\n * Controls whether the multiselect is disabled.\n * @default false\n */\n disabled = input(false, { transform: booleanAttribute });\n\n /**\n * Controls whether the multiselect shows an invalid/error state.\n * @default false\n */\n isInvalid = input(false, { transform: booleanAttribute });\n\n /**\n * Aria-label for the combobox trigger.\n * @default 'Select options'\n */\n ariaLabel = input('Select options');\n\n /**\n * Label for the panel-footer \"Cancel\" button.\n * @default 'Cancel'\n */\n cancelLabel = input('Cancel');\n\n /**\n * Label for the panel-footer \"Confirm choice\" button.\n * @default 'Confirm choice'\n */\n confirmLabel = input('Confirm choice');\n\n /**\n * Emits the current selection whenever it changes.\n */\n valueChange = output<string[]>();\n\n /**\n * Emits the confirmed selection when the \"Confirm choice\" button is clicked.\n */\n confirmed = output<string[]>();\n\n /**\n * Emits when the \"Cancel\" button is clicked (selection reverted to its pre-open state).\n */\n cancelled = output<void>();\n\n /** The projected option components. */\n readonly options = contentChildren(EdsMultiselectOption);\n\n /** Reference to the dropdown template for the overlay. */\n readonly dropdownTemplate = viewChild<TemplateRef<unknown>>('dropdownTemplate');\n\n /** Reference to the trigger element. */\n readonly triggerRef = viewChild<ElementRef<HTMLElement>>('triggerRef');\n\n /** Whether the dropdown popup is open. */\n isOpen = signal(false);\n\n /** Internal selected values. */\n protected internalValue = signal<string[]>([]);\n\n /** Snapshot of the selection taken when the dropdown opens, used to revert on Cancel. */\n private valueSnapshot: string[] = [];\n\n /** Internal disabled state (supports CVA setDisabledState). */\n protected internalDisabled = signal(false);\n\n /** Internal invalid state. */\n protected internalInvalid = signal(false);\n\n /** The display value shown in the trigger. */\n protected displayValue = computed(() => {\n const selected = this.internalValue();\n if (selected.length === 0) {\n return this.placeholder();\n }\n const opts = this.options();\n const firstLabel = opts.find(o => o.value() === selected[0])?.label() ?? selected[0];\n if (selected.length === 1) {\n return firstLabel;\n }\n return `${firstLabel} + ${selected.length - 1} more`;\n });\n\n /** Whether the placeholder is shown (no selection). */\n protected isPlaceholderShown = computed(() => this.internalValue().length === 0);\n\n /** The active descendant ID for screen readers. */\n protected activeDescendantId = signal<string | null>(null);\n\n private overlay = inject(Overlay);\n private viewContainerRef = inject(ViewContainerRef);\n private destroyRef = inject(DestroyRef);\n private overlayRef: OverlayRef | null = null;\n private templatePortal: TemplatePortal | null = null;\n private keyManager: ActiveDescendantKeyManager<EdsMultiselectOption> | null = null;\n\n constructor() {\n // Sync disabled input → internal state\n effect(() => {\n this.internalDisabled.set(this.disabled());\n });\n\n // Sync isInvalid input → internal state\n effect(() => {\n this.internalInvalid.set(this.isInvalid());\n });\n }\n\n private _onChange: (value: string[]) => void = () => { /* noop */ };\n private _onTouched: () => void = () => { /* noop */ };\n\n ngAfterContentInit(): void {\n // Sync option selected states from internalValue whenever options change\n this.syncOptionsFromValue();\n }\n\n ngOnDestroy(): void {\n this.closeDropdown();\n this.overlayRef?.dispose();\n this.overlayRef = null;\n }\n\n // --- ControlValueAccessor ---\n\n writeValue(value: string[]): void {\n this.internalValue.set(value ?? []);\n this.syncOptionsFromValue();\n }\n\n registerOnChange(fn: (value: string[]) => void): void {\n this._onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.internalDisabled.set(isDisabled);\n }\n\n // --- Trigger interactions ---\n\n protected onTriggerClick(): void {\n if (this.internalDisabled()) {\n return;\n }\n if (this.isOpen()) {\n this.closeDropdown();\n } else {\n this.openDropdown();\n }\n }\n\n protected onTriggerKeydown(event: KeyboardEvent): void {\n if (this.internalDisabled()) {\n return;\n }\n\n switch (event.key) {\n case 'Enter':\n case ' ':\n event.preventDefault();\n if (!this.isOpen()) {\n this.openDropdown();\n }\n break;\n case 'ArrowDown':\n event.preventDefault();\n if (!this.isOpen()) {\n this.openDropdown();\n }\n break;\n case 'ArrowUp':\n event.preventDefault();\n if (!this.isOpen()) {\n this.openDropdown();\n }\n break;\n case 'Escape':\n event.preventDefault();\n if (this.isOpen()) {\n this.closeDropdown();\n }\n break;\n }\n }\n\n protected onOptionClick(option: EdsMultiselectOption): void {\n if (option.isDisabled() || option.isGroup()) {\n return;\n }\n option.onSelect();\n this.syncValueFromOptions();\n this.keyManager?.setActiveItem(option);\n this.updateActiveDescendant();\n }\n\n protected onConfirmClick(): void {\n this.confirmed.emit(this.internalValue());\n this.closeDropdown();\n }\n\n protected onCancelClick(): void {\n // Revert selection to the snapshot taken when the dropdown opened.\n this.internalValue.set([...this.valueSnapshot]);\n this.syncOptionsFromValue();\n this._onChange(this.valueSnapshot);\n this.valueChange.emit(this.valueSnapshot);\n this.cancelled.emit();\n this.closeDropdown();\n }\n\n protected onPanelKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.keyManager?.setNextItemActive();\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n case 'ArrowUp':\n event.preventDefault();\n this.keyManager?.setPreviousItemActive();\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n case 'Home':\n event.preventDefault();\n this.keyManager?.setFirstItemActive();\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n case 'End':\n event.preventDefault();\n this.keyManager?.setLastItemActive();\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n this.toggleActiveOption();\n break;\n case 'Escape':\n event.preventDefault();\n this.closeDropdown();\n break;\n default:\n // Type-ahead: single printable character moves focus to next matching option\n if (event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey) {\n this.handleTypeAhead(event.key);\n }\n break;\n }\n }\n\n // --- Dropdown management (private) ---\n\n private openDropdown(): void {\n if (this.isOpen() || !this.dropdownTemplate()) {\n return;\n }\n\n const triggerEl = this.triggerRef()?.nativeElement;\n if (!triggerEl) {\n return;\n }\n\n // Snapshot current selection so Cancel can revert to it.\n this.valueSnapshot = [...this.internalValue()];\n\n const positionStrategy = this.overlay\n .position()\n .flexibleConnectedTo(triggerEl)\n .withPositions([\n new ConnectionPositionPair(\n { originX: 'start', originY: 'bottom' },\n { overlayX: 'start', overlayY: 'top' },\n ),\n new ConnectionPositionPair(\n { originX: 'start', originY: 'top' },\n { overlayX: 'start', overlayY: 'bottom' },\n ),\n ])\n .withFlexibleDimensions(false)\n .withPush(true);\n\n const scrollStrategy = this.overlay.scrollStrategies.reposition();\n\n this.overlayRef = this.overlay.create({\n positionStrategy,\n scrollStrategy,\n hasBackdrop: true,\n backdropClass: 'cdk-overlay-transparent-backdrop',\n width: triggerEl.offsetWidth,\n disposeOnNavigation: true,\n });\n\n this.templatePortal = new TemplatePortal(this.dropdownTemplate(), this.viewContainerRef);\n this.overlayRef.attach(this.templatePortal);\n\n // Focus the panel wrapper so keyboard events are captured\n const panelEl = this.overlayRef.overlayElement.querySelector<HTMLElement>('.eds-multiselect__panel-wrapper');\n panelEl?.focus();\n\n this.overlayRef.backdropClick()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this.closeDropdown());\n\n this.overlayRef.keydownEvents()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe((event) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n this.closeDropdown();\n }\n });\n\n // Setup key manager\n const opts = this.options();\n if (opts.length > 0) {\n // Reset all active states from previous opening\n opts.forEach(o => o.setInactiveStyles());\n\n this.keyManager = new ActiveDescendantKeyManager(opts as unknown as EdsMultiselectOption[])\n .withVerticalOrientation(true)\n .withHomeAndEnd(true)\n .withWrap();\n\n // APG: If options are selected, focus first selected option; otherwise first option\n const firstSelectedIndex = opts.findIndex(o => o.selected());\n if (firstSelectedIndex >= 0) {\n this.keyManager.setActiveItem(firstSelectedIndex);\n } else {\n this.keyManager.setFirstItemActive();\n }\n this.updateActiveDescendant();\n }\n\n this.isOpen.set(true);\n }\n\n private closeDropdown(): void {\n if (!this.isOpen()) {\n return;\n }\n // Clear active styles on all options\n this.options().forEach(o => o.setInactiveStyles());\n\n this.overlayRef?.detach();\n this.overlayRef?.dispose();\n this.overlayRef = null;\n this.templatePortal = null;\n this.keyManager = null;\n this.activeDescendantId.set(null);\n this.isOpen.set(false);\n this._onTouched();\n this.triggerRef()?.nativeElement?.focus();\n }\n\n private toggleActiveOption(): void {\n const active = this.keyManager?.activeItem;\n if (active && !active.isDisabled()) {\n active.onSelect();\n this.syncValueFromOptions();\n }\n }\n\n private handleTypeAhead(char: string): void {\n const opts = this.options();\n const activeIndex = this.keyManager?.activeItemIndex ?? 0;\n const lowerChar = char.toLowerCase();\n\n // Search from after the active item, then wrap around\n for (let i = 1; i <= opts.length; i++) {\n const index = (activeIndex + i) % opts.length;\n const option = opts[index];\n if (!option.isDisabled() && option.label().toLowerCase().startsWith(lowerChar)) {\n this.keyManager?.setActiveItem(index);\n this.updateActiveDescendant();\n this.scrollActiveOptionIntoView();\n break;\n }\n }\n }\n\n private scrollActiveOptionIntoView(): void {\n const activeId = this.activeDescendantId();\n if (!activeId || !this.overlayRef) {\n return;\n }\n const activeEl = this.overlayRef.overlayElement.querySelector<HTMLElement>(`#${activeId}`);\n if (activeEl && typeof activeEl.scrollIntoView === 'function') {\n activeEl.scrollIntoView({ block: 'nearest' });\n }\n }\n\n private updateActiveDescendant(): void {\n const active = this.keyManager?.activeItem;\n this.activeDescendantId.set(active ? active.optionId() : null);\n }\n\n private syncValueFromOptions(): void {\n const selectedValues = this.options()\n .filter(o => o.selected() && !o.isGroup())\n .map(o => o.value());\n this.internalValue.set(selectedValues);\n this._onChange(selectedValues);\n this.valueChange.emit(selectedValues);\n }\n\n private syncOptionsFromValue(): void {\n const currentValue = this.internalValue();\n this.options().forEach(option => {\n option.selected.set(!option.isGroup() && currentValue.includes(option.value()));\n });\n }\n}\n",
52153
52271
  "styleUrl": "./eds-multiselect.scss",
52154
52272
  "assetsDirs": [],
52155
52273
  "styleUrlsData": [
52156
52274
  {
52157
- "data": "@use '../base' as base;\n\n.eui-21 {\n :host {\n display: block;\n position: relative;\n width: 100%;\n\n .eds-multiselect__trigger {\n @include base.eds-ellipsis();\n display: flex;\n align-items: center;\n gap: var(--eds-sp-xs);\n font: var(--eds-f-paragraph-m);\n appearance: none;\n background-color: var(--eds-c-surface-elevation);\n border: var(--eds-bw-xs) solid var(--eds-c-border-neutral);\n border-radius: var(--eds-br-s);\n box-shadow: none;\n color: var(--eds-c-foreground-default);\n min-height: var(--eds-si-3xl);\n padding: var(--eds-sp-xs) var(--eds-sp-s);\n width: 100%;\n cursor: pointer;\n outline: none;\n transition: border-color 0.15s ease;\n\n &:hover:not([aria-disabled=\"true\"]) {\n border-color: var(--eds-c-border-primary);\n }\n\n &:focus-visible:not([aria-disabled=\"true\"]) {\n outline: 2px solid var(--eds-c-border-primary);\n outline-offset: -2px;\n }\n }\n\n .eds-multiselect__display {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n\n &--placeholder {\n color: var(--eds-c-foreground-subtler);\n }\n }\n\n .eds-multiselect__caret {\n flex-shrink: 0;\n font-size: 20px;\n color: var(--eds-c-foreground-default);\n transition: transform 0.2s ease;\n }\n\n }\n\n // Open state\n :host([open]) {\n .eds-multiselect__trigger {\n border-color: var(--eds-c-border-primary);\n outline: 2px solid var(--eds-c-border-primary);\n outline-offset: -2px;\n }\n\n .eds-multiselect__caret {\n transform: rotate(180deg);\n }\n }\n\n // Disabled state\n :host([disabled]) {\n .eds-multiselect__trigger {\n background-color: var(--eds-c-surface-disabled);\n border-color: var(--eds-c-border-disabled);\n color: var(--eds-c-foreground-subtler);\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .eds-multiselect__caret {\n color: var(--eds-c-foreground-subtler);\n }\n }\n\n // Invalid state\n :host([invalid]) {\n .eds-multiselect__trigger {\n border-color: var(--eds-c-border-critical);\n }\n\n .eds-multiselect__trigger:focus-visible {\n outline-color: var(--eds-c-border-critical);\n }\n }\n}\n\n// Dropdown panel (rendered in CDK overlay, outside :host)\n.eds-multiselect__panel-wrapper {\n background-color: var(--eds-c-surface-elevation);\n border: var(--eds-bw-xs) solid var(--eds-c-border-neutral);\n border-radius: var(--eds-br-s);\n box-shadow: var(--eds-sh-1);\n display: flex;\n flex-direction: column;\n max-height: 280px;\n min-width: 100%;\n box-sizing: border-box;\n outline: none;\n}\n\n.eds-multiselect__panel {\n flex: 1;\n max-height: 240px;\n overflow-y: auto;\n padding: var(--eds-sp-3xs) 0;\n outline: none;\n\n .eds-multiselect-option {\n display: flex;\n align-items: center;\n gap: var(--eds-sp-s);\n padding: var(--eds-sp-xs) var(--eds-sp-s);\n cursor: pointer;\n font: var(--eds-f-paragraph-m);\n color: var(--eds-c-foreground-default);\n user-select: none;\n transition: background-color 0.1s ease;\n\n .eds-multiselect-option__label {\n flex: 1;\n }\n\n .eds-multiselect-option__check {\n opacity: 0;\n font-size: 16px;\n color: var(--eds-c-foreground-primary);\n transition: opacity 0.1s ease;\n }\n\n &:hover:not(.eds-multiselect-option--disabled) {\n background-color: var(--eds-c-surface-hover);\n }\n\n &--active:not(.eds-multiselect-option--disabled) {\n background-color: var(--eds-c-surface-hover);\n outline: var(--eds-bw-s) solid var(--eds-c-border-primary);\n outline-offset: -2px;\n }\n\n &--selected {\n .eds-multiselect-option__check {\n opacity: 1;\n }\n }\n\n &--disabled {\n color: var(--eds-c-foreground-subtler);\n cursor: not-allowed;\n pointer-events: none;\n }\n }\n}\n\n.eds-multiselect__panel-footer {\n display: flex;\n justify-content: flex-start;\n padding: var(--eds-sp-xs) var(--eds-sp-s);\n border-top: var(--eds-bw-xs) solid var(--eds-c-border-divider);\n}\n\n.eds-multiselect__done-button {\n font: var(--eds-f-label-m-semibold);\n color: var(--eds-c-foreground-primary);\n background: none;\n border: none;\n cursor: pointer;\n padding: var(--eds-sp-3xs) var(--eds-sp-s);\n border-radius: var(--eds-br-s);\n transition: background-color 0.1s ease;\n\n &:hover {\n background-color: var(--eds-c-surface-hover);\n }\n\n &:focus-visible {\n outline: 2px solid var(--eds-c-border-primary);\n outline-offset: -2px;\n }\n}\n",
52275
+ "data": "@use '../base' as base;\n\n.eui-21 {\n :host {\n display: block;\n position: relative;\n width: 100%;\n\n .eds-multiselect__trigger {\n @include base.eds-ellipsis();\n display: flex;\n align-items: center;\n gap: var(--eds-sp-xs);\n font: var(--eds-f-paragraph-m);\n appearance: none;\n background-color: var(--eds-c-surface-elevation);\n border: var(--eds-bw-xs) solid var(--eds-c-border-neutral);\n border-radius: var(--eds-br-s);\n box-shadow: none;\n color: var(--eds-c-foreground-default);\n min-height: var(--eds-si-3xl);\n padding: var(--eds-sp-xs) var(--eds-sp-s);\n width: 100%;\n cursor: pointer;\n outline: none;\n transition: border-color 0.15s ease;\n\n &:hover:not([aria-disabled=\"true\"]) {\n border-color: var(--eds-c-border-primary);\n }\n\n &:focus-visible:not([aria-disabled=\"true\"]) {\n outline: 2px solid var(--eds-c-border-primary);\n outline-offset: -2px;\n }\n }\n\n .eds-multiselect__display {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n\n &--placeholder {\n color: var(--eds-c-foreground-subtler);\n }\n }\n\n .eds-multiselect__caret {\n flex-shrink: 0;\n font-size: 20px;\n color: var(--eds-c-foreground-default);\n transition: transform 0.2s ease;\n }\n\n }\n\n // Open state\n :host([open]) {\n .eds-multiselect__trigger {\n border-color: var(--eds-c-border-primary);\n outline: 2px solid var(--eds-c-border-primary);\n outline-offset: -2px;\n }\n\n .eds-multiselect__caret {\n transform: rotate(180deg);\n }\n }\n\n // Disabled state\n :host([disabled]) {\n .eds-multiselect__trigger {\n background-color: var(--eds-c-surface-disabled);\n border-color: var(--eds-c-border-disabled);\n color: var(--eds-c-foreground-subtler);\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .eds-multiselect__caret {\n color: var(--eds-c-foreground-subtler);\n }\n }\n\n // Invalid state\n :host([invalid]) {\n .eds-multiselect__trigger {\n border-color: var(--eds-c-border-critical);\n }\n\n .eds-multiselect__trigger:focus-visible {\n outline-color: var(--eds-c-border-critical);\n }\n }\n}\n\n// Dropdown panel (rendered in CDK overlay, outside :host)\n.eds-multiselect__panel-wrapper {\n background-color: var(--eds-c-surface-elevation);\n border: var(--eds-bw-xs) solid var(--eds-c-border-neutral);\n border-radius: var(--eds-br-s);\n box-shadow: var(--eds-sh-1);\n display: flex;\n flex-direction: column;\n max-height: 280px;\n min-width: 100%;\n box-sizing: border-box;\n outline: none;\n}\n\n.eds-multiselect__panel {\n flex: 1;\n max-height: 240px;\n overflow-y: auto;\n padding: var(--eds-sp-3xs) 0;\n outline: none;\n\n .eds-multiselect-group {\n padding: var(--eds-sp-xs) var(--eds-sp-s);\n font: var(--eds-f-label-m-medium);\n color: var(--eds-c-foreground-subtle);\n cursor: default;\n user-select: none;\n }\n\n .eds-multiselect-option {\n display: flex;\n align-items: center;\n gap: var(--eds-sp-s);\n padding: var(--eds-sp-xs) var(--eds-sp-s);\n cursor: pointer;\n font: var(--eds-f-paragraph-m);\n color: var(--eds-c-foreground-default);\n user-select: none;\n transition: background-color 0.1s ease;\n\n .eds-multiselect-option__label {\n flex: 1;\n }\n\n .eds-multiselect-option__checkbox {\n flex-shrink: 0;\n pointer-events: none;\n }\n\n &:hover:not(.eds-multiselect-option--disabled) {\n background-color: var(--eds-c-surface-hover);\n }\n\n &--active:not(.eds-multiselect-option--disabled) {\n outline: var(--eds-bw-s) solid var(--eds-c-border-primary);\n outline-offset: -2px;\n }\n\n &--selected {\n background-color: var(--eds-c-surface-selected);\n\n &:hover:not(.eds-multiselect-option--disabled) {\n background-color: var(--eds-c-surface-selected);\n }\n }\n\n &--disabled {\n color: var(--eds-c-foreground-subtler);\n cursor: not-allowed;\n pointer-events: none;\n }\n }\n}\n\n.eds-multiselect__panel-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: var(--eds-sp-xs);\n padding: var(--eds-sp-xs) var(--eds-sp-s);\n border-top: var(--eds-bw-xs) solid var(--eds-c-border-divider);\n}\n",
52158
52276
  "styleUrl": "./eds-multiselect.scss"
52159
52277
  }
52160
52278
  ],
52161
52279
  "stylesData": "",
52162
52280
  "jsdoctags": [
52163
52281
  {
52164
- "pos": 952,
52165
- "end": 1315,
52282
+ "pos": 1068,
52283
+ "end": 1431,
52166
52284
  "kind": 328,
52167
52285
  "id": 0,
52168
52286
  "flags": 16777216,
52169
52287
  "modifierFlagsCache": 0,
52170
52288
  "transformFlags": 0,
52171
52289
  "tagName": {
52172
- "pos": 953,
52173
- "end": 964,
52290
+ "pos": 1069,
52291
+ "end": 1080,
52174
52292
  "kind": 80,
52175
52293
  "id": 0,
52176
52294
  "flags": 16777216,
@@ -52180,16 +52298,16 @@
52180
52298
  "comment": "<p>EDS Multiselect component — a combobox-style dropdown that allows multiple selections.\nImplements the WAI-ARIA multiselect listbox pattern with full keyboard navigation.\nStyled to match eds-input-text default/hover/focus/disabled states.</p>\n<p>Integrates with Angular Reactive Forms and Template-driven Forms via ControlValueAccessor.</p>\n"
52181
52299
  },
52182
52300
  {
52183
- "pos": 1315,
52184
- "end": 2229,
52301
+ "pos": 1431,
52302
+ "end": 2345,
52185
52303
  "kind": 328,
52186
52304
  "id": 0,
52187
52305
  "flags": 16777216,
52188
52306
  "modifierFlagsCache": 0,
52189
52307
  "transformFlags": 0,
52190
52308
  "tagName": {
52191
- "pos": 1316,
52192
- "end": 1326,
52309
+ "pos": 1432,
52310
+ "end": 1442,
52193
52311
  "kind": 80,
52194
52312
  "id": 0,
52195
52313
  "flags": 16777216,
@@ -52205,7 +52323,7 @@
52205
52323
  "deprecated": false,
52206
52324
  "deprecationMessage": "",
52207
52325
  "args": [],
52208
- "line": 172,
52326
+ "line": 199,
52209
52327
  "rawdescription": "\n"
52210
52328
  },
52211
52329
  "extends": [],
@@ -52229,6 +52347,14 @@
52229
52347
  "name": "EDS_ICON",
52230
52348
  "type": "module"
52231
52349
  },
52350
+ {
52351
+ "name": "EDS_INPUT_CHECKBOX",
52352
+ "type": "module"
52353
+ },
52354
+ {
52355
+ "name": "EDS_BUTTON",
52356
+ "type": "module"
52357
+ },
52232
52358
  {
52233
52359
  "name": "ControlValueAccessor",
52234
52360
  "type": "interface"
@@ -52243,11 +52369,11 @@
52243
52369
  }
52244
52370
  ]
52245
52371
  },
52246
- "templateData": "<!-- Combobox trigger -->\n<div\n #triggerRef\n class=\"eds-multiselect__trigger\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-controls]=\"isOpen() ? 'eds-multiselect-listbox' : null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-disabled]=\"internalDisabled() || null\"\n [attr.tabindex]=\"internalDisabled() ? -1 : 0\"\n (click)=\"onTriggerClick()\"\n (keydown)=\"onTriggerKeydown($event)\"\n>\n <span\n class=\"eds-multiselect__display\"\n [class.eds-multiselect__display--placeholder]=\"isPlaceholderShown()\"\n >\n {{ displayValue() }}\n </span>\n <eds-icon\n class=\"eds-multiselect__caret\"\n [icon]=\"'caret-down'\"\n aria-hidden=\"true\"\n />\n</div>\n\n<!-- Dropdown panel (rendered in overlay) -->\n<ng-template #dropdownTemplate>\n <div class=\"eds-multiselect__panel-wrapper\" tabindex=\"-1\" cdkTrapFocus [cdkTrapFocusAutoCapture]=\"true\">\n <div\n class=\"eds-multiselect__panel\"\n id=\"eds-multiselect-listbox\"\n role=\"listbox\"\n aria-multiselectable=\"true\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n tabindex=\"0\"\n (keydown)=\"onPanelKeydown($event)\"\n >\n @for (option of options(); track option.value()) {\n <div\n class=\"eds-multiselect-option\"\n [class.eds-multiselect-option--selected]=\"option.selected()\"\n [class.eds-multiselect-option--active]=\"option.isActive()\"\n [class.eds-multiselect-option--disabled]=\"option.isDisabled()\"\n [attr.role]=\"'option'\"\n [attr.aria-selected]=\"option.selected()\"\n [attr.aria-disabled]=\"option.isDisabled() || null\"\n [id]=\"option.optionId()\"\n (click)=\"onOptionClick(option)\"\n >\n <eds-icon\n class=\"eds-multiselect-option__check\"\n [icon]=\"'check'\"\n edsSizeS\n aria-hidden=\"true\"\n />\n <span class=\"eds-multiselect-option__label\">{{ option.label() }}</span>\n </div>\n }\n </div>\n <div class=\"eds-multiselect__panel-footer\">\n <button\n type=\"button\"\n class=\"eds-multiselect__done-button\"\n (click)=\"onDoneClick()\"\n >\n Done\n </button>\n </div>\n </div>\n</ng-template>\n"
52372
+ "templateData": "<!-- Combobox trigger -->\n<div\n #triggerRef\n class=\"eds-multiselect__trigger\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-controls]=\"isOpen() ? 'eds-multiselect-listbox' : null\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n [attr.aria-disabled]=\"internalDisabled() || null\"\n [attr.tabindex]=\"internalDisabled() ? -1 : 0\"\n (click)=\"onTriggerClick()\"\n (keydown)=\"onTriggerKeydown($event)\"\n>\n <span\n class=\"eds-multiselect__display\"\n [class.eds-multiselect__display--placeholder]=\"isPlaceholderShown()\"\n >\n {{ displayValue() }}\n </span>\n <eds-icon\n class=\"eds-multiselect__caret\"\n [icon]=\"'caret-down'\"\n aria-hidden=\"true\"\n />\n</div>\n\n<!-- Dropdown panel (rendered in overlay) -->\n<ng-template #dropdownTemplate>\n <div class=\"eds-multiselect__panel-wrapper\" tabindex=\"-1\" cdkTrapFocus [cdkTrapFocusAutoCapture]=\"true\">\n <div\n class=\"eds-multiselect__panel\"\n id=\"eds-multiselect-listbox\"\n role=\"listbox\"\n aria-multiselectable=\"true\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-activedescendant]=\"activeDescendantId()\"\n tabindex=\"0\"\n (keydown)=\"onPanelKeydown($event)\"\n >\n @for (option of options(); track option.value()) {\n @if (option.isGroup()) {\n <div\n class=\"eds-multiselect-group\"\n role=\"presentation\"\n [id]=\"option.optionId()\"\n >\n {{ option.label() }}\n </div>\n } @else {\n <div\n class=\"eds-multiselect-option\"\n [class.eds-multiselect-option--selected]=\"option.selected()\"\n [class.eds-multiselect-option--active]=\"option.isActive()\"\n [class.eds-multiselect-option--disabled]=\"option.isDisabled()\"\n [attr.role]=\"'option'\"\n [attr.aria-selected]=\"option.selected()\"\n [attr.aria-disabled]=\"option.isDisabled() || null\"\n [id]=\"option.optionId()\"\n (click)=\"onOptionClick(option)\"\n >\n <input\n class=\"eds-multiselect-option__checkbox\"\n edsInputCheckbox\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [checked]=\"option.selected()\"\n [disabled]=\"option.isDisabled()\"\n />\n <span class=\"eds-multiselect-option__label\">{{ option.label() }}</span>\n </div>\n }\n }\n </div>\n <div class=\"eds-multiselect__panel-footer\">\n <button\n edsButton\n type=\"button\"\n variant=\"tertiary\"\n size=\"s\"\n (click)=\"onCancelClick()\"\n >\n {{ cancelLabel() }}\n </button>\n <button\n edsButton\n type=\"button\"\n variant=\"secondary\"\n size=\"s\"\n (click)=\"onConfirmClick()\"\n >\n {{ confirmLabel() }}\n </button>\n </div>\n </div>\n</ng-template>\n"
52247
52373
  },
52248
52374
  {
52249
52375
  "name": "EdsMultiselectOption",
52250
- "id": "component-EdsMultiselectOption-836eb5077243e6dc13b742dcf3bc11918de074d762c0c41faada04e58d314c8c6c0e0f58315b9355c8da99deb95ff39c54cb49764c05829c40255f427f98fe6e",
52376
+ "id": "component-EdsMultiselectOption-c797474cbcb53cae2916275084f3211f3783e34626856841c73443a831f1f4083fd8fb78aa4e8a203b72dcfe20e71dcec6232a4c5985e75f099f2de82b796fad",
52251
52377
  "file": "packages/eds/eds-multiselect/eds-multiselect-option.ts",
52252
52378
  "changeDetection": "ChangeDetectionStrategy.OnPush",
52253
52379
  "encapsulation": [],
@@ -52302,6 +52428,41 @@
52302
52428
  ],
52303
52429
  "required": false
52304
52430
  },
52431
+ {
52432
+ "name": "isGroup",
52433
+ "coverageIgnore": false,
52434
+ "defaultValue": "false",
52435
+ "deprecated": false,
52436
+ "deprecationMessage": "",
52437
+ "type": "boolean",
52438
+ "indexKey": "",
52439
+ "optional": false,
52440
+ "description": "<p>Whether this entry is a non-selectable group header.\nWhen true the entry renders as plain label text, is skipped by keyboard\nnavigation, and cannot be selected.\nfalse</p>\n",
52441
+ "line": 73,
52442
+ "rawdescription": "\n\nWhether this entry is a non-selectable group header.\nWhen true the entry renders as plain label text, is skipped by keyboard\nnavigation, and cannot be selected.\nfalse\n",
52443
+ "jsdoctags": [
52444
+ {
52445
+ "pos": 2017,
52446
+ "end": 2037,
52447
+ "kind": 328,
52448
+ "id": 0,
52449
+ "flags": 16842752,
52450
+ "modifierFlagsCache": 0,
52451
+ "transformFlags": 0,
52452
+ "tagName": {
52453
+ "pos": 2018,
52454
+ "end": 2025,
52455
+ "kind": 80,
52456
+ "id": 0,
52457
+ "flags": 16842752,
52458
+ "transformFlags": 0,
52459
+ "escapedText": "default"
52460
+ },
52461
+ "comment": "<p>false</p>\n"
52462
+ }
52463
+ ],
52464
+ "required": false
52465
+ },
52305
52466
  {
52306
52467
  "name": "label",
52307
52468
  "coverageIgnore": false,
@@ -52341,7 +52502,7 @@
52341
52502
  "indexKey": "",
52342
52503
  "optional": false,
52343
52504
  "description": "",
52344
- "line": 83,
52505
+ "line": 92,
52345
52506
  "rawdescription": "\n",
52346
52507
  "modifierKind": [
52347
52508
  148
@@ -52357,7 +52518,7 @@
52357
52518
  "indexKey": "",
52358
52519
  "optional": false,
52359
52520
  "description": "<p>Whether this option is the active descendant (highlighted via keyboard).</p>\n",
52360
- "line": 78,
52521
+ "line": 87,
52361
52522
  "rawdescription": "\nWhether this option is the active descendant (highlighted via keyboard)."
52362
52523
  },
52363
52524
  {
@@ -52370,7 +52531,7 @@
52370
52531
  "indexKey": "",
52371
52532
  "optional": false,
52372
52533
  "description": "<p>Unique ID for the option element (used for aria-activedescendant).</p>\n",
52373
- "line": 81,
52534
+ "line": 90,
52374
52535
  "rawdescription": "\nUnique ID for the option element (used for aria-activedescendant)."
52375
52536
  },
52376
52537
  {
@@ -52383,7 +52544,7 @@
52383
52544
  "indexKey": "",
52384
52545
  "optional": false,
52385
52546
  "description": "<p>Whether this option is currently selected.</p>\n",
52386
- "line": 75,
52547
+ "line": 84,
52387
52548
  "rawdescription": "\nWhether this option is currently selected."
52388
52549
  }
52389
52550
  ],
@@ -52395,7 +52556,7 @@
52395
52556
  "optional": false,
52396
52557
  "returnType": "void",
52397
52558
  "typeParameters": [],
52398
- "line": 97,
52559
+ "line": 106,
52399
52560
  "deprecated": false,
52400
52561
  "deprecationMessage": "",
52401
52562
  "rawdescription": "\n",
@@ -52408,7 +52569,7 @@
52408
52569
  "optional": false,
52409
52570
  "returnType": "void",
52410
52571
  "typeParameters": [],
52411
- "line": 87,
52572
+ "line": 96,
52412
52573
  "deprecated": false,
52413
52574
  "deprecationMessage": "",
52414
52575
  "rawdescription": "\n",
@@ -52421,7 +52582,7 @@
52421
52582
  "optional": false,
52422
52583
  "returnType": "void",
52423
52584
  "typeParameters": [],
52424
- "line": 91,
52585
+ "line": 100,
52425
52586
  "deprecated": false,
52426
52587
  "deprecationMessage": "",
52427
52588
  "rawdescription": "\n",
@@ -52442,7 +52603,7 @@
52442
52603
  "description": "<p>An individual option within the EDS Multiselect dropdown.\nSupports selection state, disabled state, and keyboard navigation via CDK ActiveDescendantKeyManager.</p>\n<b>Example :</b><div><pre class=\"line-numbers\"><code class=\"language-html\">&lt;eds-multiselect-option value=&quot;opt1&quot; label=&quot;Option 1&quot; /&gt;</code></pre></div>",
52443
52604
  "rawdescription": "\n\nAn individual option within the EDS Multiselect dropdown.\nSupports selection state, disabled state, and keyboard navigation via CDK ActiveDescendantKeyManager.\n\n```html\n<eds-multiselect-option value=\"opt1\" label=\"Option 1\" />\n```\n",
52444
52605
  "type": "component",
52445
- "sourceCode": "import {\n Component,\n ChangeDetectionStrategy,\n input,\n signal,\n computed,\n booleanAttribute,\n ElementRef,\n inject,\n} from '@angular/core';\nimport { Highlightable } from '@angular/cdk/a11y';\nimport { EDS_ICON } from '@eui/eds/eds-icon';\n\n/**\n * @description\n * An individual option within the EDS Multiselect dropdown.\n * Supports selection state, disabled state, and keyboard navigation via CDK ActiveDescendantKeyManager.\n *\n * @usageNotes\n * ```html\n * <eds-multiselect-option value=\"opt1\" label=\"Option 1\" />\n * ```\n */\n@Component({\n selector: 'eds-multiselect-option',\n template: `\n <span class=\"eds-multiselect-option__label\">{{ label() }}</span>\n <eds-icon\n class=\"eds-multiselect-option__check\"\n [icon]=\"'check'\"\n aria-hidden=\"true\"\n />\n `,\n styleUrl: './eds-multiselect-option.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [...EDS_ICON],\n host: {\n class: 'eds-multiselect-option',\n '[attr.selected]': 'selected() || null',\n '[attr.active]': 'isActive() || null',\n '[attr.disabled]': 'isDisabled() || null',\n role: 'option',\n '[attr.aria-selected]': 'selected()',\n '[attr.aria-disabled]': 'isDisabled() || null',\n '[attr.data-e2e]': '\"eds-multiselect-option\"',\n '[id]': 'optionId()',\n '(click)': 'onSelect()',\n },\n})\nexport class EdsMultiselectOption implements Highlightable {\n /**\n * The value associated with this option.\n */\n value = input.required<string>();\n\n /**\n * The display label for this option.\n */\n label = input.required<string>();\n\n /**\n * Whether this option is disabled.\n * @default false\n */\n isDisabled = input(false, { transform: booleanAttribute });\n\n /**\n * Required by Highlightable interface — reads the signal value.\n */\n get disabled(): boolean {\n return this.isDisabled();\n }\n\n /** Whether this option is currently selected. */\n selected = signal(false);\n\n /** Whether this option is the active descendant (highlighted via keyboard). */\n isActive = signal(false);\n\n /** Unique ID for the option element (used for aria-activedescendant). */\n optionId = computed(() => `eds-multiselect-option-${this.value()}`);\n\n readonly elementRef = inject(ElementRef);\n\n // --- Highlightable interface ---\n\n setActiveStyles(): void {\n this.isActive.set(true);\n }\n\n setInactiveStyles(): void {\n this.isActive.set(false);\n }\n\n // --- Interaction ---\n\n onSelect(): void {\n if (!this.isDisabled()) {\n this.selected.set(!this.selected());\n }\n }\n}\n",
52606
+ "sourceCode": "import {\n Component,\n ChangeDetectionStrategy,\n input,\n signal,\n computed,\n booleanAttribute,\n ElementRef,\n inject,\n} from '@angular/core';\nimport { Highlightable } from '@angular/cdk/a11y';\nimport { EDS_ICON } from '@eui/eds/eds-icon';\n\n/**\n * @description\n * An individual option within the EDS Multiselect dropdown.\n * Supports selection state, disabled state, and keyboard navigation via CDK ActiveDescendantKeyManager.\n *\n * @usageNotes\n * ```html\n * <eds-multiselect-option value=\"opt1\" label=\"Option 1\" />\n * ```\n */\n@Component({\n selector: 'eds-multiselect-option',\n template: `\n <span class=\"eds-multiselect-option__label\">{{ label() }}</span>\n <eds-icon\n class=\"eds-multiselect-option__check\"\n [icon]=\"'check'\"\n aria-hidden=\"true\"\n />\n `,\n styleUrl: './eds-multiselect-option.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [...EDS_ICON],\n host: {\n class: 'eds-multiselect-option',\n '[attr.selected]': 'selected() || null',\n '[attr.active]': 'isActive() || null',\n '[attr.disabled]': 'isDisabled() || null',\n role: 'option',\n '[attr.aria-selected]': 'selected()',\n '[attr.aria-disabled]': 'isDisabled() || null',\n '[attr.data-e2e]': '\"eds-multiselect-option\"',\n '[id]': 'optionId()',\n '(click)': 'onSelect()',\n },\n})\nexport class EdsMultiselectOption implements Highlightable {\n /**\n * The value associated with this option.\n */\n value = input.required<string>();\n\n /**\n * The display label for this option.\n */\n label = input.required<string>();\n\n /**\n * Whether this option is disabled.\n * @default false\n */\n isDisabled = input(false, { transform: booleanAttribute });\n\n /**\n * Whether this entry is a non-selectable group header.\n * When true the entry renders as plain label text, is skipped by keyboard\n * navigation, and cannot be selected.\n * @default false\n */\n isGroup = input(false, { transform: booleanAttribute });\n\n /**\n * Required by Highlightable interface — reads the signal value.\n * Group headers are treated as \"disabled\" so the key manager skips them.\n */\n get disabled(): boolean {\n return this.isDisabled() || this.isGroup();\n }\n\n /** Whether this option is currently selected. */\n selected = signal(false);\n\n /** Whether this option is the active descendant (highlighted via keyboard). */\n isActive = signal(false);\n\n /** Unique ID for the option element (used for aria-activedescendant). */\n optionId = computed(() => `eds-multiselect-option-${this.value()}`);\n\n readonly elementRef = inject(ElementRef);\n\n // --- Highlightable interface ---\n\n setActiveStyles(): void {\n this.isActive.set(true);\n }\n\n setInactiveStyles(): void {\n this.isActive.set(false);\n }\n\n // --- Interaction ---\n\n onSelect(): void {\n if (!this.isDisabled() && !this.isGroup()) {\n this.selected.set(!this.selected());\n }\n }\n}\n",
52446
52607
  "styleUrl": "./eds-multiselect-option.scss",
52447
52608
  "assetsDirs": [],
52448
52609
  "styleUrlsData": [
@@ -52503,9 +52664,9 @@
52503
52664
  "name": "disabled",
52504
52665
  "type": "boolean",
52505
52666
  "returnType": "boolean",
52506
- "line": 70,
52507
- "rawdescription": "\n\nRequired by Highlightable interface — reads the signal value.\n",
52508
- "description": "<p>Required by Highlightable interface — reads the signal value.</p>\n"
52667
+ "line": 79,
52668
+ "rawdescription": "\n\nRequired by Highlightable interface — reads the signal value.\nGroup headers are treated as \"disabled\" so the key manager skips them.\n",
52669
+ "description": "<p>Required by Highlightable interface — reads the signal value.\nGroup headers are treated as &quot;disabled&quot; so the key manager skips them.</p>\n"
52509
52670
  }
52510
52671
  }
52511
52672
  },