@sbb-esta/lyne-elements-dev 5.13.1-dev.1794294068 → 5.13.1-dev.1794307456

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.
@@ -5101,7 +5101,7 @@
5101
5101
  }
5102
5102
  }
5103
5103
  ],
5104
- "description": "Add a language change handler."
5104
+ "description": "Add a language change handler.\nTODO: Refactor to EventTarget."
5105
5105
  },
5106
5106
  {
5107
5107
  "kind": "method",
@@ -6139,6 +6139,22 @@
6139
6139
  "text": "Record<string, string>"
6140
6140
  },
6141
6141
  "default": "{ de: 'Jahre', en: 'years', fr: 'ans', it: 'anni', }"
6142
+ },
6143
+ {
6144
+ "kind": "variable",
6145
+ "name": "i18nButton",
6146
+ "type": {
6147
+ "text": "Record<string, string>"
6148
+ },
6149
+ "default": "{ de: 'Schaltfläche', en: 'Button', fr: 'Bouton', it: 'Pulsante', }"
6150
+ },
6151
+ {
6152
+ "kind": "variable",
6153
+ "name": "i18nAutocompleteActionsHint",
6154
+ "type": {
6155
+ "text": "Record<string, string>"
6156
+ },
6157
+ "default": "{ de: 'Drücken Sie Pfeil links oder rechts für verfügbare Aktionen', en: 'Press arrow left or right for available actions', fr: 'Appuyez sur les flèches gauche ou droite pour les actions disponibles', it: 'Usare i tasti freccia destra o sinistra per le azioni disponibili', }"
6142
6158
  }
6143
6159
  ],
6144
6160
  "exports": [
@@ -6997,6 +7013,22 @@
6997
7013
  "name": "i18nLogo125AnniversaryYears",
6998
7014
  "module": "core/i18n/i18n.js"
6999
7015
  }
7016
+ },
7017
+ {
7018
+ "kind": "js",
7019
+ "name": "i18nButton",
7020
+ "declaration": {
7021
+ "name": "i18nButton",
7022
+ "module": "core/i18n/i18n.js"
7023
+ }
7024
+ },
7025
+ {
7026
+ "kind": "js",
7027
+ "name": "i18nAutocompleteActionsHint",
7028
+ "declaration": {
7029
+ "name": "i18nAutocompleteActionsHint",
7030
+ "module": "core/i18n/i18n.js"
7031
+ }
7000
7032
  }
7001
7033
  ]
7002
7034
  },
@@ -30812,7 +30844,8 @@
30812
30844
  }
30813
30845
  }
30814
30846
  ],
30815
- "description": "Add meaningful aria attributes to trigger element of role='combobox'"
30847
+ "description": "Add meaningful aria attributes to trigger element of role='combobox'",
30848
+ "deprecated": "true"
30816
30849
  },
30817
30850
  {
30818
30851
  "kind": "function",
@@ -30831,7 +30864,8 @@
30831
30864
  }
30832
30865
  }
30833
30866
  ],
30834
- "description": "Remove aria attributes from trigger elements."
30867
+ "description": "Remove aria attributes from trigger elements.",
30868
+ "deprecated": "true"
30835
30869
  }
30836
30870
  ],
30837
30871
  "exports": [
@@ -66655,6 +66689,7 @@
66655
66689
  "text": "string"
66656
66690
  },
66657
66691
  "privacy": "protected",
66692
+ "deprecated": "*",
66658
66693
  "readonly": true
66659
66694
  },
66660
66695
  {
@@ -66663,15 +66698,9 @@
66663
66698
  "type": {
66664
66699
  "text": "string"
66665
66700
  },
66666
- "privacy": "protected"
66667
- },
66668
- {
66669
- "kind": "field",
66670
- "name": "panelRole",
66671
- "type": {
66672
- "text": "string"
66673
- },
66674
- "privacy": "protected"
66701
+ "privacy": "protected",
66702
+ "default": "''",
66703
+ "deprecated": "*"
66675
66704
  },
66676
66705
  {
66677
66706
  "kind": "field",
@@ -67010,6 +67039,16 @@
67010
67039
  }
67011
67040
  }
67012
67041
  },
67042
+ {
67043
+ "kind": "method",
67044
+ "name": "handleOptionChange",
67045
+ "privacy": "protected",
67046
+ "return": {
67047
+ "type": {
67048
+ "text": "void"
67049
+ }
67050
+ }
67051
+ },
67013
67052
  {
67014
67053
  "kind": "method",
67015
67054
  "name": "_setNextActiveOptionIfAutoActiveFirstOption",
@@ -67772,6 +67811,7 @@
67772
67811
  "name": "SbbOpenCloseBaseElement",
67773
67812
  "module": "core.js"
67774
67813
  },
67814
+ "deprecated": "Will be inlined into autocompelte with next major release.",
67775
67815
  "classGenerics": "T = string"
67776
67816
  }
67777
67817
  ],
@@ -67828,7 +67868,7 @@
67828
67868
  "kind": "field",
67829
67869
  "name": "role",
67830
67870
  "type": {
67831
- "text": "string"
67871
+ "text": "ElementInternals['role'] | undefined"
67832
67872
  },
67833
67873
  "privacy": "public",
67834
67874
  "static": true,
@@ -67836,8 +67876,7 @@
67836
67876
  "name": "SbbElement",
67837
67877
  "module": "core/base-elements/element.js"
67838
67878
  },
67839
- "readonly": true,
67840
- "default": "'button'"
67879
+ "readonly": true
67841
67880
  },
67842
67881
  {
67843
67882
  "kind": "field",
@@ -67859,6 +67898,30 @@
67859
67898
  "description": "Gets the parent SbbOptGroupElement, if present.",
67860
67899
  "readonly": true
67861
67900
  },
67901
+ {
67902
+ "kind": "field",
67903
+ "name": "_languageController",
67904
+ "privacy": "private",
67905
+ "default": "new SbbLanguageController(this)"
67906
+ },
67907
+ {
67908
+ "kind": "method",
67909
+ "name": "_textContent",
67910
+ "privacy": "private",
67911
+ "return": {
67912
+ "type": {
67913
+ "text": "string"
67914
+ }
67915
+ },
67916
+ "parameters": [
67917
+ {
67918
+ "name": "elements",
67919
+ "type": {
67920
+ "text": "readonly Element[] | null | undefined"
67921
+ }
67922
+ }
67923
+ ]
67924
+ },
67862
67925
  {
67863
67926
  "kind": "method",
67864
67927
  "name": "isDisabledExternally",
@@ -68428,13 +68491,14 @@
68428
68491
  },
68429
68492
  {
68430
68493
  "kind": "field",
68431
- "name": "['_$sbbElement$']",
68494
+ "name": "role",
68432
68495
  "type": {
68433
- "text": "boolean"
68496
+ "text": "string"
68434
68497
  },
68435
68498
  "privacy": "public",
68436
68499
  "static": true,
68437
- "default": "true",
68500
+ "readonly": true,
68501
+ "default": "'group'",
68438
68502
  "inheritedFrom": {
68439
68503
  "name": "SbbElement",
68440
68504
  "module": "core/base-elements/element.js"
@@ -68442,12 +68506,36 @@
68442
68506
  },
68443
68507
  {
68444
68508
  "kind": "field",
68445
- "name": "elementDependencies",
68509
+ "name": "_assignedDescription",
68446
68510
  "type": {
68447
- "text": "SbbElementType[] | undefined"
68511
+ "text": "string | undefined"
68512
+ },
68513
+ "privacy": "private"
68514
+ },
68515
+ {
68516
+ "kind": "field",
68517
+ "name": "_languageController",
68518
+ "privacy": "private"
68519
+ },
68520
+ {
68521
+ "kind": "method",
68522
+ "name": "_updateAriaDescription",
68523
+ "privacy": "private",
68524
+ "return": {
68525
+ "type": {
68526
+ "text": "void"
68527
+ }
68528
+ }
68529
+ },
68530
+ {
68531
+ "kind": "field",
68532
+ "name": "['_$sbbElement$']",
68533
+ "type": {
68534
+ "text": "boolean"
68448
68535
  },
68449
68536
  "privacy": "public",
68450
68537
  "static": true,
68538
+ "default": "true",
68451
68539
  "inheritedFrom": {
68452
68540
  "name": "SbbElement",
68453
68541
  "module": "core/base-elements/element.js"
@@ -68455,9 +68543,9 @@
68455
68543
  },
68456
68544
  {
68457
68545
  "kind": "field",
68458
- "name": "role",
68546
+ "name": "elementDependencies",
68459
68547
  "type": {
68460
- "text": "ElementInternals['role'] | undefined"
68548
+ "text": "SbbElementType[] | undefined"
68461
68549
  },
68462
68550
  "privacy": "public",
68463
68551
  "static": true,
@@ -68779,7 +68867,7 @@
68779
68867
  "kind": "field",
68780
68868
  "name": "role",
68781
68869
  "type": {
68782
- "text": "ElementInternals['role'] | undefined"
68870
+ "text": "string"
68783
68871
  },
68784
68872
  "privacy": "public",
68785
68873
  "static": true,
@@ -68787,33 +68875,8 @@
68787
68875
  "name": "SbbElement",
68788
68876
  "module": "core/base-elements/element.js"
68789
68877
  },
68790
- "readonly": true
68791
- },
68792
- {
68793
- "kind": "field",
68794
- "name": "generatedId",
68795
- "type": {
68796
- "text": "string"
68797
- },
68798
- "privacy": "protected",
68799
- "default": "`sbb-autocomplete-${++nextId}`",
68800
- "inheritedFrom": {
68801
- "name": "SbbAutocompleteBaseElement",
68802
- "module": "autocomplete/autocomplete/autocomplete-base-element.js"
68803
- }
68804
- },
68805
- {
68806
- "kind": "field",
68807
- "name": "panelRole",
68808
- "type": {
68809
- "text": "string"
68810
- },
68811
- "privacy": "protected",
68812
- "default": "'listbox'",
68813
- "inheritedFrom": {
68814
- "name": "SbbAutocompleteBaseElement",
68815
- "module": "autocomplete/autocomplete/autocomplete-base-element.js"
68816
- }
68878
+ "readonly": true,
68879
+ "default": "'listbox'"
68817
68880
  },
68818
68881
  {
68819
68882
  "kind": "field",
@@ -68920,6 +68983,20 @@
68920
68983
  "module": "autocomplete/autocomplete/autocomplete-base-element.js"
68921
68984
  }
68922
68985
  },
68986
+ {
68987
+ "kind": "method",
68988
+ "name": "handleOptionChange",
68989
+ "privacy": "protected",
68990
+ "return": {
68991
+ "type": {
68992
+ "text": "void"
68993
+ }
68994
+ },
68995
+ "inheritedFrom": {
68996
+ "name": "SbbAutocompleteBaseElement",
68997
+ "module": "autocomplete/autocomplete/autocomplete-base-element.js"
68998
+ }
68999
+ },
68923
69000
  {
68924
69001
  "kind": "method",
68925
69002
  "name": "_setNextHorizontalActiveElement",
@@ -69215,12 +69292,27 @@
69215
69292
  "text": "string"
69216
69293
  },
69217
69294
  "privacy": "protected",
69295
+ "deprecated": "*",
69218
69296
  "readonly": true,
69219
69297
  "inheritedFrom": {
69220
69298
  "name": "SbbAutocompleteBaseElement",
69221
69299
  "module": "autocomplete/autocomplete/autocomplete-base-element.js"
69222
69300
  }
69223
69301
  },
69302
+ {
69303
+ "kind": "field",
69304
+ "name": "generatedId",
69305
+ "type": {
69306
+ "text": "string"
69307
+ },
69308
+ "privacy": "protected",
69309
+ "default": "''",
69310
+ "deprecated": "*",
69311
+ "inheritedFrom": {
69312
+ "name": "SbbAutocompleteBaseElement",
69313
+ "module": "autocomplete/autocomplete/autocomplete-base-element.js"
69314
+ }
69315
+ },
69224
69316
  {
69225
69317
  "kind": "field",
69226
69318
  "name": "activeOption",
@@ -12,6 +12,7 @@ declare const SbbAutocompleteBaseElement_base: import("../../core.ts").AbstractC
12
12
  *
13
13
  * @event {Event} change - The change event is fired on the autocomplete's trigger when the user modifies the element's value. Unlike the input event, the change event is not necessarily fired for each alteration to an element's value.
14
14
  * @event {InputEvent} input - The input event fires on the autocomplete's trigger when the value has been changed as a direct result of a user action.
15
+ * @deprecated Will be inlined into autocompelte with next major release.
15
16
  */
16
17
  export declare abstract class SbbAutocompleteBaseElement<T = string> extends SbbAutocompleteBaseElement_base {
17
18
  static styles: CSSResultGroup;
@@ -67,9 +68,10 @@ export declare abstract class SbbAutocompleteBaseElement<T = string> extends Sbb
67
68
  /** Returns the trigger element. */
68
69
  get triggerElement(): HTMLInputElement | null;
69
70
  private _triggerElement?;
71
+ /** @deprecated **/
70
72
  protected get overlayId(): string;
71
- protected abstract generatedId: string;
72
- protected abstract panelRole: string;
73
+ /** @deprecated **/
74
+ protected generatedId: string;
73
75
  protected activeOption: SbbOptionBaseElement<T> | null;
74
76
  protected pendingAutoSelectedOption: SbbOptionBaseElement<T> | null;
75
77
  private _resizeObserver;
@@ -116,6 +118,7 @@ export declare abstract class SbbAutocompleteBaseElement<T = string> extends Sbb
116
118
  /** Set the option value within the input element */
117
119
  private _setInputValue;
118
120
  private _handleSlotchange;
121
+ protected handleOptionChange(): void;
119
122
  private _setNextActiveOptionIfAutoActiveFirstOption;
120
123
  /** The autocomplete should inherit 'readonly' state from the trigger. */
121
124
  private _readonly;
@@ -13,9 +13,7 @@ import { SbbAutocompleteBaseElement } from './autocomplete-base-element.ts';
13
13
  */
14
14
  export declare class SbbAutocompleteElement<T = string> extends SbbAutocompleteBaseElement<T> {
15
15
  static readonly elementName: string;
16
- static readonly role: string | null;
17
- protected generatedId: string;
18
- protected panelRole: string;
16
+ static readonly role = "listbox";
19
17
  /** Index of the active element within the active option's row (0 = option, >0 = a button). */
20
18
  private _activeColumnIndex;
21
19
  protected get options(): SbbOptionElement<T>[];
@@ -27,6 +25,7 @@ export declare class SbbAutocompleteElement<T = string> extends SbbAutocompleteB
27
25
  */
28
26
  protected selectByKeyboard(event: KeyboardEvent): void;
29
27
  protected setNextActiveOption(event?: KeyboardEvent): void;
28
+ protected handleOptionChange(): void;
30
29
  /**
31
30
  * Moves the active element horizontally within the active option's `sbb-autocomplete-row`,
32
31
  * between the option itself and its `sbb-autocomplete-button`s.
@@ -9,18 +9,20 @@ declare const SbbAutocompleteButtonElement_base: import("../../core.ts").Abstrac
9
9
  */
10
10
  export declare class SbbAutocompleteButtonElement extends SbbAutocompleteButtonElement_base {
11
11
  static readonly elementName: string;
12
- static readonly role = "button";
12
+ static readonly role: string;
13
13
  static styles: CSSResultGroup;
14
14
  /** Gets the SbbOptionElement on the same row of the button. */
15
15
  get option(): SbbOptionElement | null;
16
16
  /** Gets the parent SbbOptGroupElement, if present. */
17
17
  get optgroup(): SbbOptGroupElement | null;
18
+ private _languageController;
18
19
  constructor();
19
20
  /**
20
21
  * Whether the button is currently active.
21
22
  * @internal
22
23
  */
23
24
  setActive(value: boolean): void;
25
+ private _textContent;
24
26
  protected willUpdate(changedProperties: PropertyValues<this>): void;
25
27
  connectedCallback(): void;
26
28
  protected isDisabledExternally(): boolean;
@@ -7,8 +7,12 @@ import { SbbElement } from '../../core.ts';
7
7
  */
8
8
  export declare class SbbAutocompleteRowElement extends SbbElement {
9
9
  static readonly elementName: string;
10
+ static readonly role: string;
10
11
  static styles: CSSResultGroup;
12
+ private _assignedDescription?;
13
+ private _languageController;
11
14
  constructor();
15
+ private _updateAriaDescription;
12
16
  protected render(): TemplateResult;
13
17
  }
14
18
  declare global {
@@ -1,9 +1,9 @@
1
1
  import { __esDecorate, __runInitializers } from "tslib";
2
2
  import { ResizeController } from "@lit-labs/observers/resize-controller.js";
3
- import { html, isServer, nothing, unsafeCSS } from "lit";
3
+ import { html, isServer, unsafeCSS } from "lit";
4
4
  import { property } from "lit/decorators.js";
5
5
  import { ref } from "lit/directives/ref.js";
6
- import { SbbActionBaseElement, SbbDisabledMixin, SbbElement, SbbEscapableOverlayController, SbbNegativeMixin, SbbOpenCloseBaseElement, SbbPropertyWatcherController, forceType, getNextElementIndex, idReference, isAndroid, isEventOnElement, isSafari, isZeroAnimationDuration, popoverResetStyles, removeAriaComboBoxAttributes, scrollbarStyles, setAriaComboBoxAttributes, setOverlayPosition } from "./core.js";
6
+ import { SbbActionBaseElement, SbbDisabledMixin, SbbElement, SbbEscapableOverlayController, SbbLanguageController, SbbNegativeMixin, SbbOpenCloseBaseElement, SbbPropertyWatcherController, appendAriaElements, forceType, getNextElementIndex, i18nAutocompleteActionsHint, i18nButton, idReference, isAndroid, isEventOnElement, isIOS, isMacOS, isSafari, isZeroAnimationDuration, popoverResetStyles, removeAriaComboBoxAttributes, sbbLiveAnnouncer, scrollbarStyles, setOverlayPosition } from "./core.js";
7
7
  import { optionPanelStyles } from "./option.pure.js";
8
8
  import { miniButtonStyle } from "./button.pure.js";
9
9
  import { SbbIconNameMixin } from "./icon.pure.js";
@@ -12,11 +12,6 @@ import { SbbIconNameMixin } from "./icon.pure.js";
12
12
  var autocomplete_base_element_default = ":host {\n display: none;\n}\n\n:host(:is(:is(:state(state-opening),[state--state-opening]), :is(:state(state-opened),[state--state-opened]), :is(:state(state-closing),[state--state-closing]))) {\n display: block;\n}\n";
13
13
 
14
14
  // src/elements/autocomplete/autocomplete/autocomplete-base-element.ts
15
- /**
16
- * On Safari, the aria role 'listbox' must be on the host element, or else VoiceOver won't work at all.
17
- * On the other hand, JAWS and NVDA need the role to be "closer" to the options, or else optgroups won't work.
18
- */
19
- const ariaRoleOnHost$1 = isSafari;
20
15
  var SbbInputAutocompleteEvent = class extends Event {
21
16
  get option() {
22
17
  return this._option;
@@ -34,6 +29,7 @@ var SbbInputAutocompleteEvent = class extends Event {
34
29
  *
35
30
  * @event {Event} change - The change event is fired on the autocomplete's trigger when the user modifies the element's value. Unlike the input event, the change event is not necessarily fired for each alteration to an element's value.
36
31
  * @event {InputEvent} input - The input event fires on the autocomplete's trigger when the value has been changed as a direct result of a user action.
32
+ * @deprecated Will be inlined into autocompelte with next major release.
37
33
  */
38
34
  let SbbAutocompleteBaseElement = (() => {
39
35
  let _classSuper = SbbNegativeMixin(SbbOpenCloseBaseElement);
@@ -366,8 +362,9 @@ let SbbAutocompleteBaseElement = (() => {
366
362
  get triggerElement() {
367
363
  return this._triggerElement ?? null;
368
364
  }
365
+ /** @deprecated **/
369
366
  get overlayId() {
370
- return `${this.id}-overlay`;
367
+ return this.id;
371
368
  }
372
369
  constructor() {
373
370
  super();
@@ -382,6 +379,8 @@ let SbbAutocompleteBaseElement = (() => {
382
379
  this.#requireSelection_accessor_storage = (__runInitializers(this, _autoSelectActiveOptionOnBlur_extraInitializers), __runInitializers(this, _requireSelection_initializers, false));
383
380
  this.#position_accessor_storage = (__runInitializers(this, _requireSelection_extraInitializers), __runInitializers(this, _position_initializers, "auto"));
384
381
  this._triggerElement = __runInitializers(this, _position_extraInitializers);
382
+ /** @deprecated **/
383
+ this.generatedId = "";
385
384
  this.activeOption = null;
386
385
  this.pendingAutoSelectedOption = null;
387
386
  this._resizeObserver = new ResizeController(this, {
@@ -458,7 +457,7 @@ let SbbAutocompleteBaseElement = (() => {
458
457
  connectedCallback() {
459
458
  this.popover = "manual";
460
459
  super.connectedCallback();
461
- this.id ||= this.generatedId;
460
+ this.tabIndex = -1;
462
461
  if (this.hasUpdated) this._componentSetup();
463
462
  this.syncNegative();
464
463
  }
@@ -536,7 +535,9 @@ let SbbAutocompleteBaseElement = (() => {
536
535
  if (this._optionsCount > 0 && this.options.length === 0) this.close();
537
536
  } else if (document?.activeElement === this.triggerElement && this._optionsCount === 0 && this.options.length > 0) this.open();
538
537
  this._optionsCount = this.options.length;
538
+ this.handleOptionChange();
539
539
  }
540
+ handleOptionChange() {}
540
541
  _setNextActiveOptionIfAutoActiveFirstOption() {
541
542
  if (this.autoActiveFirstOption) {
542
543
  this.resetActiveElement();
@@ -680,9 +681,6 @@ let SbbAutocompleteBaseElement = (() => {
680
681
  <div class="sbb-option-panel__overlay-container">
681
682
  <div
682
683
  class="sbb-option-panel__overlay ${this.negative ? "sbb-scrollbar-negative" : "sbb-scrollbar"}"
683
- role=${!ariaRoleOnHost$1 ? this.panelRole : nothing}
684
- id=${!ariaRoleOnHost$1 ? this.overlayId : nothing}
685
- tabindex="-1"
686
684
  @animationend=${this._onAnimationEnd}
687
685
  ${ref((overlayRef) => this._overlay = overlayRef)}
688
686
  >
@@ -705,7 +703,7 @@ var SbbAutocompleteButtonElement = class extends SbbDisabledMixin(SbbNegativeMix
705
703
  this.elementName = "sbb-autocomplete-button";
706
704
  }
707
705
  static {
708
- this.role = "button";
706
+ this.role = isSafari ? "button" : "option";
709
707
  }
710
708
  static {
711
709
  this.styles = [miniButtonStyle];
@@ -720,7 +718,14 @@ var SbbAutocompleteButtonElement = class extends SbbDisabledMixin(SbbNegativeMix
720
718
  }
721
719
  constructor() {
722
720
  super();
721
+ this._languageController = new SbbLanguageController(this);
723
722
  this.internals.states.add("button");
723
+ if (!isSafari) {
724
+ this.internals.ariaRoleDescription = "";
725
+ const updateDescription = () => this.internals.ariaDescription = i18nButton[this._languageController.current];
726
+ updateDescription();
727
+ this._languageController.withHandler(updateDescription);
728
+ }
724
729
  if (!isServer) {
725
730
  this.setupBaseEventHandlers();
726
731
  this.addController(new SbbPropertyWatcherController(this, () => this.closest("sbb-optgroup"), { disabled: () => this._updateInternals() }));
@@ -733,6 +738,18 @@ var SbbAutocompleteButtonElement = class extends SbbDisabledMixin(SbbNegativeMix
733
738
  */
734
739
  setActive(value) {
735
740
  this.toggleState("focus-visible", value);
741
+ if (value && isSafari && isMacOS) {
742
+ const output = [];
743
+ if (this.ariaLabel) output.push(this.ariaLabel);
744
+ else if (this.ariaLabelledByElements?.length) output.push(this._textContent(this.ariaLabelledByElements));
745
+ if (this.ariaDescription) output.push(this.ariaDescription);
746
+ else if (this.ariaDescribedByElements?.length) output.push(this._textContent(this.ariaDescribedByElements));
747
+ output.push(i18nButton[this._languageController.current]);
748
+ sbbLiveAnnouncer.announce(output.join(", "), "assertive");
749
+ }
750
+ }
751
+ _textContent(elements) {
752
+ return elements?.map((el) => el.textContent.trim().replace(/\.$/, "")).join(". ") ?? "";
736
753
  }
737
754
  willUpdate(changedProperties) {
738
755
  super.willUpdate(changedProperties);
@@ -772,26 +789,42 @@ var SbbAutocompleteRowElement = class extends SbbElement {
772
789
  static {
773
790
  this.elementName = "sbb-autocomplete-row";
774
791
  }
792
+ static {
793
+ this.role = "group";
794
+ }
775
795
  static {
776
796
  this.styles = [unsafeCSS(autocomplete_row_default)];
777
797
  }
778
798
  constructor() {
779
799
  super();
800
+ this._languageController = new SbbLanguageController(this).withHandler(() => this._updateAriaDescription());
780
801
  this.addController(new SbbPropertyWatcherController(this, () => this.closest("sbb-autocomplete"), { negative: (e) => this.toggleState("negative", e.negative) }));
781
802
  this.addController(new SbbPropertyWatcherController(this, () => this.querySelector("sbb-option"), { disabled: (e) => this.toggleState("disabled", e.disabled) }));
782
803
  }
804
+ _updateAriaDescription() {
805
+ if (isIOS || isAndroid) return;
806
+ const hasButton = !!this.querySelector("sbb-autocomplete-button");
807
+ const option = this.querySelector("sbb-option");
808
+ if (!option) return;
809
+ if (!option["internals"]) customElements.upgrade(option);
810
+ if (hasButton) option["internals"].ariaDescription = i18nAutocompleteActionsHint[this._languageController.current];
811
+ else if (!hasButton && option["internals"].ariaDescription) option["internals"].ariaDescription = null;
812
+ if (isSafari) {
813
+ if (hasButton && (!option.ariaDescription || this._assignedDescription && option.ariaDescription === this._assignedDescription && option.ariaDescription !== i18nAutocompleteActionsHint[this._languageController.current])) {
814
+ option.ariaDescription = i18nAutocompleteActionsHint[this._languageController.current];
815
+ this._assignedDescription = option.ariaDescription;
816
+ } else if (!hasButton && this._assignedDescription && option.ariaDescription === this._assignedDescription) {
817
+ option.ariaDescription = null;
818
+ this._assignedDescription = void 0;
819
+ }
820
+ }
821
+ }
783
822
  render() {
784
- return html`<slot></slot>`;
823
+ return html`<slot @slotchange=${this._updateAriaDescription}></slot>`;
785
824
  }
786
825
  };
787
826
 
788
827
  // src/elements/autocomplete/autocomplete/autocomplete.component.ts
789
- let nextId = 0;
790
- /**
791
- * On Safari, the aria role 'listbox' must be on the host element, or else VoiceOver won't work at all.
792
- * On the other hand, JAWS and NVDA need the role to be "closer" to the options, or else optgroups won't work.
793
- */
794
- const ariaRoleOnHost = isSafari;
795
828
  /**
796
829
  * Combined with a native input, it displays a panel with a list of available options.
797
830
  *
@@ -806,8 +839,6 @@ const ariaRoleOnHost = isSafari;
806
839
  var SbbAutocompleteElement = class extends SbbAutocompleteBaseElement {
807
840
  constructor() {
808
841
  super(...arguments);
809
- this.generatedId = `sbb-autocomplete-${++nextId}`;
810
- this.panelRole = "listbox";
811
842
  /** Index of the active element within the active option's row (0 = option, >0 = a button). */
812
843
  this._activeColumnIndex = 0;
813
844
  }
@@ -815,7 +846,7 @@ var SbbAutocompleteElement = class extends SbbAutocompleteBaseElement {
815
846
  this.elementName = "sbb-autocomplete";
816
847
  }
817
848
  static {
818
- this.role = ariaRoleOnHost ? "listbox" : null;
849
+ this.role = "listbox";
819
850
  }
820
851
  get options() {
821
852
  return Array.from(this.querySelectorAll?.("sbb-option") ?? []);
@@ -866,6 +897,19 @@ var SbbAutocompleteElement = class extends SbbAutocompleteBaseElement {
866
897
  if (event) event.preventDefault();
867
898
  if (this.autoSelectActiveOption && event) this.setPendingSelection(this.activeOption);
868
899
  }
900
+ handleOptionChange() {
901
+ const options = this.options;
902
+ const optionsCount = `${options.length}`;
903
+ let position = 1;
904
+ for (const option of options) {
905
+ const buttons = Array.from(option.closest("sbb-autocomplete-row")?.querySelectorAll("sbb-autocomplete-button") ?? []);
906
+ for (const element of [option, ...buttons]) {
907
+ element["internals"].ariaSetSize = optionsCount;
908
+ element["internals"].ariaPosInSet = `${position}`;
909
+ }
910
+ position++;
911
+ }
912
+ }
869
913
  /**
870
914
  * Moves the active element horizontally within the active option's `sbb-autocomplete-row`,
871
915
  * between the option itself and its `sbb-autocomplete-button`s.
@@ -900,7 +944,13 @@ var SbbAutocompleteElement = class extends SbbAutocompleteBaseElement {
900
944
  if (this.triggerElement) this.triggerElement.ariaActiveDescendantElement = null;
901
945
  }
902
946
  setTriggerAttributes(element) {
903
- setAriaComboBoxAttributes(element, ariaRoleOnHost ? this.id : this.overlayId, false);
947
+ if (!element) return;
948
+ element.autocomplete = "off";
949
+ element.role = "combobox";
950
+ element.ariaAutoComplete = "list";
951
+ element.ariaHasPopup = "listbox";
952
+ element.ariaControlsElements = appendAriaElements(element.ariaControlsElements, this);
953
+ element.ariaExpanded = "false";
904
954
  }
905
955
  };
906
956
 
@@ -23,7 +23,10 @@ export declare class SbbLanguageController implements ReactiveController {
23
23
  private _previousLanguage?;
24
24
  private _handlers;
25
25
  constructor(_host: ReactiveControllerHost);
26
- /** Add a language change handler. */
26
+ /**
27
+ * Add a language change handler.
28
+ * TODO: Refactor to EventTarget.
29
+ */
27
30
  withHandler(handler: () => void): this;
28
31
  hostConnected(): void;
29
32
  hostDisconnected(): void;
@@ -106,3 +106,5 @@ export declare const i18nSlide: Record<string, string>;
106
106
  export declare const i18nRemainingCharacters: Record<string, string>;
107
107
  export declare const i18nLogo125Anniversary: Record<string, string>;
108
108
  export declare const i18nLogo125AnniversaryYears: Record<string, string>;
109
+ export declare const i18nButton: Record<string, string>;
110
+ export declare const i18nAutocompleteActionsHint: Record<string, string>;
@@ -9,9 +9,11 @@ export declare function setAriaOverlayTriggerProperties(overlay: HTMLElement, tr
9
9
  export declare function removeAriaOverlayTriggerProperties(trigger: HTMLElement | null | undefined): void;
10
10
  /**
11
11
  * Add meaningful aria attributes to trigger element of role='combobox'
12
+ * @deprecated
12
13
  */
13
14
  export declare function setAriaComboBoxAttributes(trigger: HTMLElement, overlayId: string, expanded: boolean, hasPopup?: 'listbox' | 'grid'): void;
14
15
  /**
15
16
  * Remove aria attributes from trigger elements.
17
+ * @deprecated
16
18
  */
17
19
  export declare function removeAriaComboBoxAttributes(trigger?: HTMLElement | null): void;