@odx/foundation 1.0.0-beta.80 → 1.0.0-beta.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import { OdxTooltip } from '../main.js';
1
2
  import { OdxIconName } from '@odx/icons';
2
3
  import { PropertyValues, TemplateResult } from 'lit';
3
4
  import { OdxButton } from '../button/button.js';
@@ -8,6 +9,7 @@ declare global {
8
9
  }
9
10
  export declare class OdxActionButton extends OdxButton {
10
11
  #private;
12
+ protected tooltip?: OdxTooltip;
11
13
  /** @internal */
12
14
  protected isStatusMessageShown: boolean;
13
15
  done: boolean;
@@ -17,7 +19,6 @@ export declare class OdxActionButton extends OdxButton {
17
19
  statusTimeout: number;
18
20
  statusMessageTimeout: number;
19
21
  replaceContent: boolean;
20
- connectedCallback(): void;
21
22
  protected firstUpdated(props: PropertyValues<this>): void;
22
23
  protected render(): TemplateResult;
23
24
  protected renderContent(): TemplateResult;
@@ -18,7 +18,7 @@ export declare class OdxDropdown extends PopoverHost {
18
18
  get options(): PopoverPlacementOptions;
19
19
  connectedCallback(): void;
20
20
  mountPopover(referenceElement: HTMLElement | null): void;
21
- unmountPopover(referenceElement: HTMLElement): void;
21
+ unmountPopover(referenceElement: HTMLElement | null): void;
22
22
  protected render(): TemplateResult;
23
23
  protected willUpdate(props: PropertyValues<this>): void;
24
24
  }
@@ -1,11 +1,11 @@
1
1
  import { CanBeDisabled, CustomElement, Placement, PopoverPlacementOptions } from '../../lib/main.js';
2
+ import { PropertyValues } from 'lit';
2
3
  import { OdxPopover } from './popover.js';
3
4
  declare const PopoverHost_base: import('../../lib/main.js').Constructor<CanBeDisabled> & typeof CustomElement;
4
- export declare class PopoverHost extends PopoverHost_base {
5
+ export declare abstract class PopoverHost extends PopoverHost_base {
5
6
  #private;
6
7
  static readonly styles: import('lit').CSSResult;
7
- private isOpen;
8
- popoverElement: OdxPopover;
8
+ protected popoverElement: OdxPopover;
9
9
  referenceElement: HTMLElement | null;
10
10
  anchor: string;
11
11
  placement: Placement;
@@ -14,18 +14,21 @@ export declare class PopoverHost extends PopoverHost_base {
14
14
  constructor();
15
15
  connectedCallback(): void;
16
16
  disconnectedCallback(): void;
17
+ hasMountedPopover(element: HTMLElement): boolean;
17
18
  mountPopover(referenceElement: HTMLElement | null): void;
18
- unmountPopover(_referenceElement: HTMLElement): void;
19
+ unmountPopover(_referenceElement: HTMLElement | null): void;
19
20
  isPopoverOpen(): boolean;
20
21
  showPopover(): Promise<void>;
21
22
  hidePopover(): void;
22
23
  togglePopover(state?: boolean): boolean;
23
24
  canPopoverShow?(): boolean;
24
- onBeforePopoverShow?(): void;
25
- onPopoverShow?(): void;
26
- onBeforePopoverHide?(): void;
27
- onPopoverHide?(): void;
25
+ onBeforePopoverShow?(): Promise<void> | void;
26
+ onPopoverShow?(): Promise<void> | void;
28
27
  canPopoverHide?(): boolean;
28
+ onBeforePopoverHide?(): Promise<void> | void;
29
+ onPopoverHide?(): Promise<void> | void;
30
+ updateReferenceElementFromEvent(event: Event): void;
31
+ protected willUpdate(props: PropertyValues<this>): void;
29
32
  }
30
33
  export {};
31
34
  //# sourceMappingURL=popover-host.d.ts.map
@@ -4,6 +4,7 @@ export declare class PopoverObserver {
4
4
  readonly host: PopoverHost;
5
5
  readonly root: ShadowRoot | Document;
6
6
  constructor(host: PopoverHost);
7
+ hasMountedPopover(element: HTMLElement): boolean;
7
8
  observe(): void;
8
9
  disconnect(): void;
9
10
  updateReferenceElements(): void;
@@ -10,9 +10,11 @@ declare global {
10
10
  export declare class OdxSelect extends ListboxFormControl<OptionControl> {
11
11
  #private;
12
12
  protected options: OdxOption[];
13
+ protected readonly trigger: HTMLElement;
13
14
  protected readonly dropdown: OdxDropdown;
14
15
  maxVisibleSelectedOptions: number;
15
16
  constructor();
17
+ firstUpdated(): void;
16
18
  isDropdownOpen(): boolean;
17
19
  showDropdown(): void;
18
20
  hideDropdown(): void;
@@ -1,6 +1,6 @@
1
1
  import { _ as __decorateClass } from './_virtual_class-decorator-runtime.js';
2
- import { CustomElement, ExpandableItemManager, customElement, CanBeExpanded, InteractiveElement, getUniqueId, toAriaBooleanAttribute, Size, Variant, optionalAttr, InteractiveLink, getElementFromEvent, Shape, CanBeDisabled, optionalSlot, CheckboxFormControl, CheckboxGroupFormControl, SharedResizeObserver, Placement, waitForAnimations, PopoverPlacementOptions, computePopoverPlacement, findClosestDocument, getKeyInfo, FormControl, ActiveDescendantsController, getAssignedElement, parseDate, forwardEvent, OptionControl, toPx, RadioGroupFormControl, ListboxFormControl, IsDraggable, NumberFormControl, IS_DRAG_ACTIVE_ATTRIBUTE, DragController } from '@odx/foundation';
3
- import { queryAssignedElements, property, state, queryAll, query } from 'lit/decorators.js';
2
+ import { CustomElement, ExpandableItemManager, customElement, CanBeExpanded, InteractiveElement, getUniqueId, toAriaBooleanAttribute, Size, Variant, optionalAttr, InteractiveLink, getElementFromEvent, Shape, CanBeDisabled, optionalSlot, CheckboxFormControl, CheckboxGroupFormControl, SharedResizeObserver, findClosestDocument, Placement, waitForAnimations, PopoverPlacementOptions, computePopoverPlacement, getKeyInfo, FormControl, ActiveDescendantsController, getAssignedElement, parseDate, forwardEvent, OptionControl, toPx, RadioGroupFormControl, ListboxFormControl, IsDraggable, NumberFormControl, IS_DRAG_ACTIVE_ATTRIBUTE, DragController } from '@odx/foundation';
3
+ import { queryAssignedElements, property, query, state, queryAll } from 'lit/decorators.js';
4
4
  import { html, isServer, unsafeCSS, css, nothing } from 'lit';
5
5
  import { when } from 'lit/directives/when.js';
6
6
  import { p as pick, e, a as autoUpdate, t as throttle, R as RovingTabindexController, r as round, g as debounce, n, i as c, j as e$1 } from './vendor.js';
@@ -187,10 +187,6 @@ const _OdxActionButton = class _OdxActionButton extends OdxButton {
187
187
  }
188
188
  /** @internal */
189
189
  #statusTimeout;
190
- connectedCallback() {
191
- super.connectedCallback();
192
- this.id ||= getUniqueId(this.localName);
193
- }
194
190
  firstUpdated(props) {
195
191
  super.firstUpdated(props);
196
192
  this.nativeElement.addEventListener("click", this.#handleClick);
@@ -233,6 +229,7 @@ const _OdxActionButton = class _OdxActionButton extends OdxButton {
233
229
  #handleDoneChange() {
234
230
  const timeoutDuration = Math.max(0, this.statusTimeout);
235
231
  this.isStatusMessageShown = true;
232
+ this.tooltip?.mountPopover(this.nativeElement);
236
233
  if (this.statusTimeout <= 0) return;
237
234
  clearTimeout(this.#statusTimeout);
238
235
  this.#statusTimeout = window.setTimeout(() => {
@@ -242,6 +239,9 @@ const _OdxActionButton = class _OdxActionButton extends OdxButton {
242
239
  }
243
240
  #handleClick;
244
241
  };
242
+ __decorateClass([
243
+ query("odx-tooltip")
244
+ ], _OdxActionButton.prototype, "tooltip", 2);
245
245
  __decorateClass([
246
246
  state()
247
247
  ], _OdxActionButton.prototype, "isStatusMessageShown", 2);
@@ -735,17 +735,13 @@ const _OdxCheckboxGroup = class _OdxCheckboxGroup extends CheckboxGroupFormContr
735
735
  super(...arguments);
736
736
  this.layout = CheckboxGroupLayout.VERTICAL;
737
737
  this.#handleSlotChange = () => {
738
- for (const control of this.groupControls) {
739
- control.addEventListener("change", this.#handleGroupControlChange);
740
- }
738
+ this.groupControl?.removeEventListener("change", this.#handleGroupControlChange);
739
+ this.groupControl?.addEventListener("change", this.#handleGroupControlChange);
741
740
  };
742
741
  this.#handleGroupControlChange = (event) => {
743
- if (!this.isControl(event.target)) return;
742
+ if (!this.isGroupControl(event.target)) return;
744
743
  event.stopImmediatePropagation();
745
744
  this.updateValue(event.target.checked ? this.controls.map((control) => control.value) : [], true);
746
- for (const groupControl of this.childGroups.flatMap((group) => group.groupControls)) {
747
- groupControl.checked = event.target.checked;
748
- }
749
745
  };
750
746
  }
751
747
  static {
@@ -940,25 +936,64 @@ let OdxCircularProgressBar = _OdxCircularProgressBar;
940
936
 
941
937
  const styles$Z = "@layer base{:host{--max-block-size: 100%;--max-inline-size: 100%;--_popover-color-background: var(--odx-color-background-level-2);--_popover-color-foreground: var(--odx-color-foreground-rest);--_popover-min-block-size: inherit;--_popover-max-block-size: 100%;--_popover-min-inline-size: inherit;--_popover-max-inline-size: 100%;--_popover-transition: var(--odx-popover-transition, var(--odx-motion-duration-default));--_popover-offset: var(--odx-size-50);--_popover-shadow: var(--odx-popover-shadow, var(--odx-shadow-level-1));--_popover-outer-padding: var(--odx-size-75);--_popover-arrow-size: var(--odx-size-50);--_popover-arrow-offset: calc(var(--_popover-offset) - var(--_popover-arrow-size) / 2);--_max-block-size: min(var(--_popover-max-block-size), var(--max-block-size));--_max-inline-size: min(var(--_popover-max-inline-size), var(--max-inline-size));top:var(--_popover-position-y);left:var(--_popover-position-x);transform:translate3d(var(--_popover-transition-offset-x),var(--_popover-transition-offset-y),0);transition:opacity var(--_popover-transition),transform var(--_popover-transition) allow-discrete,overlay var(--_popover-transition) allow-discrete,display var(--_popover-transition) allow-discrete;opacity:0;margin:0;background-color:transparent;max-block-size:var(--_max-block-size);max-inline-size:var(--_max-inline-size)}:host(:not(:popover-open)){display:none}:host(:popover-open){display:flex;transform:translate(0);opacity:1;@starting-style{transform:translate(var(--_popover-transition-offset-x),var(--_popover-transition-offset-y));opacity:0}}odx-popover::part(arrow){transform:rotate(45deg)}}@layer state{:host([popover-placement^=\"top\"]){--_popover-transition-offset-x: 0;--_popover-transition-offset-y: calc(-1 * var(--_popover-offset));padding-block:var(--_popover-outer-padding) var(--_popover-offset);max-block-size:calc(var(--_max-block-size) - var(--_popover-outer-padding));odx-popover::part(arrow){bottom:var(--_popover-arrow-offset)}}:host([popover-placement^=\"right\"]){--_popover-transition-offset-x: var(--_popover-offset);--_popover-transition-offset-y: 0;padding-inline:var(--_popover-offset) var(--_popover-outer-padding);max-inline-size:calc(var(--_max-inline-size) - var(--_popover-outer-padding));odx-popover::part(arrow){left:var(--_popover-arrow-offset)}}:host([popover-placement^=\"bottom\"]){--_popover-transition-offset-x: 0;--_popover-transition-offset-y: var(--_popover-offset);--_popover-arrow-offset-y: calc(var(--_popover-offset) - 6px);padding-block:var(--_popover-offset) var(--_popover-outer-padding);max-block-size:calc(var(--_max-block-size) - var(--_popover-outer-padding));odx-popover::part(arrow){top:var(--_popover-arrow-offset)}}:host([popover-placement^=\"left\"]){--_popover-transition-offset-x: calc(-1 * var(--_popover-offset));--_popover-transition-offset-y: 0;--_popover-arrow-offset-x: calc(var(--_popover-offset) - var(--_arrow-size) / 2);--_popover-arrow-offset-y: 0;padding-inline:var(--_popover-offset) var(--_popover-outer-padding);max-inline-size:calc(var(--_max-inline-size) - var(--_popover-outer-padding));odx-popover::part(arrow){right:var(--_popover-offset)}}}";
942
938
 
939
+ const popoverTargetAttribute = "odx-popovertarget";
940
+ class PopoverObserver {
941
+ #referenceElements = /* @__PURE__ */ new WeakMap();
942
+ #mutationObserver;
943
+ constructor(host) {
944
+ this.host = host;
945
+ this.root = findClosestDocument(host) ?? document;
946
+ this.#mutationObserver = new MutationObserver(async (mutations) => {
947
+ if (mutations.length === 0) return;
948
+ await 0;
949
+ this.updateReferenceElements();
950
+ });
951
+ }
952
+ hasMountedPopover(element) {
953
+ return this.#referenceElements.has(element);
954
+ }
955
+ observe() {
956
+ this.#mutationObserver?.observe(this.root, { attributes: true, subtree: true, childList: true, attributeFilter: [popoverTargetAttribute] });
957
+ this.updateReferenceElements();
958
+ }
959
+ disconnect() {
960
+ this.#mutationObserver?.disconnect();
961
+ }
962
+ updateReferenceElements() {
963
+ for (const element of Array.from(this.root.querySelectorAll(`[${popoverTargetAttribute}]`))) {
964
+ if (this.#referenceElements.has(element)) continue;
965
+ const popoverId = element.getAttribute(popoverTargetAttribute);
966
+ if (popoverId !== this.host.id) continue;
967
+ this.#referenceElements.set(element, popoverId);
968
+ this.host.mountPopover?.(element);
969
+ }
970
+ }
971
+ }
972
+
943
973
  class PopoverHost extends CanBeDisabled(CustomElement) {
944
974
  constructor() {
945
975
  super();
946
- this.isOpen = false;
976
+ this.#openPopovers = /* @__PURE__ */ new WeakSet();
947
977
  this.referenceElement = null;
948
978
  this.anchor = "";
949
979
  this.placement = Placement.BOTTOM;
950
980
  this.fpsLimit = 60;
951
981
  this.#handleToggle = async (event) => {
952
- if (this.isOpen) {
953
- this.onBeforePopoverShow?.();
982
+ if (this.isPopoverOpen()) {
983
+ await this.onBeforePopoverShow?.();
954
984
  await waitForAnimations(this);
955
- this.onPopoverShow?.();
985
+ await this.onPopoverShow?.();
956
986
  } else {
957
- this.onBeforePopoverHide?.();
987
+ await this.onBeforePopoverHide?.();
958
988
  await waitForAnimations(this);
959
- this.onPopoverHide?.();
989
+ await this.onPopoverHide?.();
990
+ }
991
+ if (!this.referenceElement) return;
992
+ if (event.newState === "open") {
993
+ this.#openPopovers.add(this.referenceElement);
994
+ } else {
995
+ this.#openPopovers.delete(this.referenceElement);
960
996
  }
961
- this.isOpen = event.newState === "open";
962
997
  };
963
998
  if (!isServer) {
964
999
  this.addEventListener("toggle", this.#handleToggle);
@@ -967,6 +1002,7 @@ class PopoverHost extends CanBeDisabled(CustomElement) {
967
1002
  static {
968
1003
  this.styles = unsafeCSS(styles$Z);
969
1004
  }
1005
+ #openPopovers;
970
1006
  #positionUpdater;
971
1007
  #observer;
972
1008
  get options() {
@@ -981,26 +1017,28 @@ class PopoverHost extends CanBeDisabled(CustomElement) {
981
1017
  super.disconnectedCallback();
982
1018
  this.#disconnectObserver();
983
1019
  if (!this.referenceElement) return;
984
- this.unmountPopover(this.referenceElement);
1020
+ this.unmountPopover?.(this.referenceElement);
1021
+ this.hidePopover();
1022
+ }
1023
+ hasMountedPopover(element) {
1024
+ return this.#observer?.hasMountedPopover(element) ?? false;
985
1025
  }
986
1026
  mountPopover(referenceElement) {
987
- if (this.referenceElement && this.referenceElement !== referenceElement) {
988
- this.unmountPopover(this.referenceElement);
989
- }
990
- this.referenceElement = referenceElement ?? null;
1027
+ if (!referenceElement) return;
1028
+ this.referenceElement = referenceElement;
991
1029
  }
992
1030
  unmountPopover(_referenceElement) {
993
- this.hidePopover();
1031
+ this.referenceElement = null;
994
1032
  }
995
1033
  isPopoverOpen() {
996
- return this.isOpen;
1034
+ return !!this.referenceElement && this.#openPopovers.has(this.referenceElement);
997
1035
  }
998
1036
  async showPopover() {
999
- if (!this.referenceElement || this.isOpen || this.disabled || this.canPopoverShow?.() === false) return;
1037
+ if (!this.referenceElement || this.disabled || this.canPopoverShow?.() === false) return;
1000
1038
  await 0;
1001
1039
  computePopoverPlacement(this.referenceElement, this, this.options);
1002
1040
  super.showPopover();
1003
- this.isOpen = true;
1041
+ this.#openPopovers.add(this.referenceElement);
1004
1042
  this.#positionUpdater?.();
1005
1043
  this.#positionUpdater = autoUpdate(
1006
1044
  this.referenceElement,
@@ -1009,14 +1047,16 @@ class PopoverHost extends CanBeDisabled(CustomElement) {
1009
1047
  );
1010
1048
  }
1011
1049
  hidePopover() {
1012
- if (!this.isOpen || this.canPopoverHide?.() === false) return;
1050
+ if (!this.isPopoverOpen() || this.canPopoverHide?.() === false) return;
1013
1051
  this.#positionUpdater?.();
1014
1052
  super.hidePopover();
1015
- this.isOpen = false;
1053
+ if (this.referenceElement) {
1054
+ this.#openPopovers.delete(this.referenceElement);
1055
+ }
1016
1056
  this.#positionUpdater = void 0;
1017
1057
  }
1018
- togglePopover(state2) {
1019
- const isOpen = state2 ?? !this.isPopoverOpen();
1058
+ togglePopover(state) {
1059
+ const isOpen = state ?? !this.isPopoverOpen();
1020
1060
  if (isOpen) {
1021
1061
  this.showPopover();
1022
1062
  } else {
@@ -1024,18 +1064,24 @@ class PopoverHost extends CanBeDisabled(CustomElement) {
1024
1064
  }
1025
1065
  return isOpen;
1026
1066
  }
1027
- #findReferenceElement(anchor) {
1028
- return findClosestDocument(this)?.getElementById(anchor) ?? null;
1067
+ updateReferenceElementFromEvent(event) {
1068
+ const referenceElement = getElementFromEvent(event, (element) => this.hasMountedPopover(element));
1069
+ if (!referenceElement) return;
1070
+ this.referenceElement = getElementFromEvent(event, (element) => this.hasMountedPopover(element));
1071
+ }
1072
+ willUpdate(props) {
1073
+ super.willUpdate(props);
1074
+ if (props.has("referenceElement") && this.referenceElement !== props.get("referenceElement")) {
1075
+ const previousReferenceElement = props.get("referenceElement");
1076
+ if (previousReferenceElement) {
1077
+ this.#openPopovers.delete(previousReferenceElement);
1078
+ }
1079
+ }
1029
1080
  }
1030
1081
  #connectObserver() {
1031
1082
  this.#disconnectObserver();
1032
- this.mountPopover(this.#findReferenceElement(this.anchor));
1033
- this.#observer = new MutationObserver(() => {
1034
- const referenceElement = this.anchor ? this.#findReferenceElement(this.anchor) : null;
1035
- if (this.referenceElement === referenceElement) return;
1036
- this.mountPopover(referenceElement);
1037
- });
1038
- this.#observer?.observe(document.documentElement, { subtree: true, attributeFilter: ["id", "anchor"] });
1083
+ this.#observer = new PopoverObserver(this);
1084
+ this.#observer?.observe();
1039
1085
  }
1040
1086
  #disconnectObserver() {
1041
1087
  this.#observer?.disconnect();
@@ -1043,14 +1089,11 @@ class PopoverHost extends CanBeDisabled(CustomElement) {
1043
1089
  }
1044
1090
  #handleToggle;
1045
1091
  }
1046
- __decorateClass([
1047
- state()
1048
- ], PopoverHost.prototype, "isOpen", 2);
1049
1092
  __decorateClass([
1050
1093
  query("odx-popover", true)
1051
1094
  ], PopoverHost.prototype, "popoverElement", 2);
1052
1095
  __decorateClass([
1053
- state()
1096
+ property({ attribute: false })
1054
1097
  ], PopoverHost.prototype, "referenceElement", 2);
1055
1098
  __decorateClass([
1056
1099
  property({ reflect: true, useDefault: true })
@@ -1072,10 +1115,12 @@ const _OdxDropdown = class _OdxDropdown extends PopoverHost {
1072
1115
  this.placement = DropdownPlacement.BOTTOM;
1073
1116
  this.#handleClick = (event) => {
1074
1117
  if (event?.defaultPrevented) return;
1118
+ this.updateReferenceElementFromEvent(event);
1075
1119
  this.togglePopover();
1076
1120
  };
1077
1121
  this.#handleKeyboardEvent = (event) => {
1078
- if (!getKeyInfo(event).enter || event.defaultPrevented) return;
1122
+ const key = getKeyInfo(event);
1123
+ if (!key.enter || event.defaultPrevented) return;
1079
1124
  if (!this.isPopoverOpen()) {
1080
1125
  event.stopImmediatePropagation();
1081
1126
  }
@@ -1096,14 +1141,15 @@ const _OdxDropdown = class _OdxDropdown extends PopoverHost {
1096
1141
  this.id ||= getUniqueId("odx-dropdown");
1097
1142
  }
1098
1143
  mountPopover(referenceElement) {
1099
- super.mountPopover(referenceElement);
1144
+ super.mountPopover?.(referenceElement);
1100
1145
  if (this.disabled || !referenceElement) return;
1101
1146
  this.#updateAriaAttributes(referenceElement, this.id);
1102
1147
  referenceElement.addEventListener("click", this.#handleClick);
1103
1148
  referenceElement.addEventListener("keydown", this.#handleKeyboardEvent);
1104
1149
  }
1105
1150
  unmountPopover(referenceElement) {
1106
- super.unmountPopover(referenceElement);
1151
+ super.unmountPopover?.(referenceElement);
1152
+ if (!referenceElement) return;
1107
1153
  this.#updateAriaAttributes(referenceElement, null);
1108
1154
  referenceElement.removeEventListener("click", this.#handleClick);
1109
1155
  referenceElement.removeEventListener("keydown", this.#handleKeyboardEvent);
@@ -1122,7 +1168,7 @@ const _OdxDropdown = class _OdxDropdown extends PopoverHost {
1122
1168
  }
1123
1169
  if (props.has("disabled")) {
1124
1170
  if (this.disabled) {
1125
- this.referenceElement && this.unmountPopover(this.referenceElement);
1171
+ this.unmountPopover(this.referenceElement);
1126
1172
  } else {
1127
1173
  this.mountPopover(this.referenceElement);
1128
1174
  }
@@ -1244,13 +1290,12 @@ const _OdxAutocomplete = class _OdxAutocomplete extends FormControl {
1244
1290
  if (!this.control) return;
1245
1291
  this.control.addEventListener("blur", this.#handleControlFocusOut);
1246
1292
  this.control.addEventListener("clear", this.#handleControlClear);
1247
- this.control.addEventListener("click", (event) => {
1248
- event.preventDefault();
1249
- });
1293
+ this.control.addEventListener("click", (event) => event.preventDefault());
1250
1294
  this.control.addEventListener("focus", this.#handleControlFocusIn);
1251
1295
  this.control.addEventListener("input", this.#handleControlInput);
1252
1296
  this.control.addEventListener("keydown", this.#handleControlKeyboardEvent);
1253
- this.dropdown.referenceElement = this.control;
1297
+ this.dropdown.popover = "manual";
1298
+ this.dropdown.mountPopover(this.control);
1254
1299
  };
1255
1300
  this.#handleControlFocusIn = () => {
1256
1301
  if (this.dropdown.canPopoverShow?.() === false) return;
@@ -1293,7 +1338,7 @@ const _OdxAutocomplete = class _OdxAutocomplete extends FormControl {
1293
1338
  customElement("odx-autocomplete", styles$W)(_OdxAutocomplete);
1294
1339
  }
1295
1340
  get control() {
1296
- return getAssignedElement(this, { slot: "control" });
1341
+ return getAssignedElement(this.shadowRoot, { slot: "control" });
1297
1342
  }
1298
1343
  get controlValue() {
1299
1344
  return this.control?.value ?? "";
@@ -1308,7 +1353,13 @@ const _OdxAutocomplete = class _OdxAutocomplete extends FormControl {
1308
1353
  render() {
1309
1354
  return html`
1310
1355
  <slot name="control" @slotchange=${this.#handleControlSlotChange}></slot>
1311
- <odx-dropdown part="dropdown" role="listbox" tabindex="-1" ?disabled=${this.disabled} match-reference-width .popover="${"manual"}">
1356
+ <odx-dropdown
1357
+ part="dropdown"
1358
+ role="listbox"
1359
+ tabindex="-1"
1360
+ ?disabled=${this.disabled}
1361
+ match-reference-width
1362
+ >
1312
1363
  <odx-highlight minlength=${this.minQueryLength} selector="odx-option" subtle .root=${this}>
1313
1364
  <odx-stack gap="xs">
1314
1365
  <slot></slot>
@@ -2448,14 +2499,14 @@ class OdxMenu extends PopoverHost {
2448
2499
  this.role ||= "menu";
2449
2500
  }
2450
2501
  mountPopover(referenceElement) {
2451
- super.mountPopover(referenceElement);
2502
+ super.mountPopover?.(referenceElement);
2452
2503
  referenceElement?.addEventListener("click", this.#handleReferenceInteraction);
2453
2504
  referenceElement?.addEventListener("keydown", this.#handleReferenceKeyDown);
2454
2505
  }
2455
2506
  unmountPopover(referenceElement) {
2456
2507
  referenceElement.removeEventListener("keydown", this.#handleReferenceKeyDown);
2457
2508
  referenceElement.removeEventListener("click", this.#handleReferenceInteraction);
2458
- super.unmountPopover(referenceElement);
2509
+ super.unmountPopover?.(referenceElement);
2459
2510
  }
2460
2511
  onBeforePopoverShow() {
2461
2512
  this.#tabindexController.update({ elements: () => this.getItems() });
@@ -2480,7 +2531,8 @@ class OdxMenu extends PopoverHost {
2480
2531
  </odx-popover>
2481
2532
  `;
2482
2533
  }
2483
- #handleReferenceInteraction = () => {
2534
+ #handleReferenceInteraction = (event) => {
2535
+ this.updateReferenceElementFromEvent(event);
2484
2536
  this.togglePopover();
2485
2537
  };
2486
2538
  #handleClick = (event) => {
@@ -2494,16 +2546,10 @@ class OdxMenu extends PopoverHost {
2494
2546
  };
2495
2547
  #handleKeyDown = (event) => {
2496
2548
  const key = getKeyInfo(event);
2497
- if ((key.tab || key.backTab) && this.isPopoverOpen()) {
2498
- event?.preventDefault();
2499
- event.stopPropagation();
2500
- this.hidePopover();
2501
- return;
2502
- }
2503
- if (!(key.enter || key.space)) return;
2504
- const [activeItem] = this.getItems().filter((item) => item.tabIndex === 0);
2505
- if (!activeItem) return;
2506
- activeItem.click();
2549
+ if (!(this.isPopoverOpen() && (key.tab || key.backTab))) return;
2550
+ event?.preventDefault();
2551
+ event.stopPropagation();
2552
+ this.hidePopover();
2507
2553
  };
2508
2554
  #handleReferenceKeyDown = (event) => {
2509
2555
  if (!getKeyInfo(event).down) return;
@@ -3217,6 +3263,10 @@ const _OdxSelect = class _OdxSelect extends ListboxFormControl {
3217
3263
  static {
3218
3264
  customElement("odx-select", styles$o)(_OdxSelect);
3219
3265
  }
3266
+ firstUpdated() {
3267
+ const referenceElement = this.shadowRoot?.querySelector(".base") ?? null;
3268
+ this.dropdown.mountPopover(referenceElement);
3269
+ }
3220
3270
  isDropdownOpen() {
3221
3271
  return this.dropdown.isPopoverOpen();
3222
3272
  }
@@ -3228,7 +3278,7 @@ const _OdxSelect = class _OdxSelect extends ListboxFormControl {
3228
3278
  }
3229
3279
  render() {
3230
3280
  return html`
3231
- <div id="select-trigger" class="base" tabindex="0">
3281
+ <div class="base" tabindex="0">
3232
3282
  <div class="value" odxPreventTextOverflow>
3233
3283
  ${when(
3234
3284
  this.selectedOptions.length,
@@ -3242,7 +3292,7 @@ const _OdxSelect = class _OdxSelect extends ListboxFormControl {
3242
3292
  )}
3243
3293
  <odx-icon class="indicator" name="core::chevron-down"></odx-icon>
3244
3294
  </div>
3245
- <odx-dropdown part="dropdown" anchor="select-trigger" role="listbox" tabindex="-1" ?disabled=${this.disabled || this.readonly} match-reference-width>
3295
+ <odx-dropdown part="dropdown" role="listbox" tabindex="-1" ?disabled=${this.disabled || this.readonly} match-reference-width .referenceElement=${this.trigger}>
3246
3296
  <slot @slotchange=${this.#handleSlotChange}></slot>
3247
3297
  </odx-dropdown>
3248
3298
  `;
@@ -3278,6 +3328,9 @@ const _OdxSelect = class _OdxSelect extends ListboxFormControl {
3278
3328
  __decorateClass([
3279
3329
  queryAssignedElements({ selector: '[role="option"]', flatten: true })
3280
3330
  ], _OdxSelect.prototype, "options", 2);
3331
+ __decorateClass([
3332
+ query(".base", true)
3333
+ ], _OdxSelect.prototype, "trigger", 2);
3281
3334
  __decorateClass([
3282
3335
  query(OdxDropdown.tagName, true)
3283
3336
  ], _OdxSelect.prototype, "dropdown", 2);
@@ -4263,13 +4316,15 @@ const _OdxTooltip = class _OdxTooltip extends PopoverHost {
4263
4316
  if (event.relatedTarget === this) return;
4264
4317
  this.#handleMouseEvents(event);
4265
4318
  };
4266
- this.#handleMouseEvents = ({ type }) => {
4267
- if (type !== "mouseenter" && type !== "mouseleave") return;
4268
- this.togglePopover(type === "mouseenter");
4319
+ this.#handleMouseEvents = (event) => {
4320
+ if (event.type !== "mouseenter" && event.type !== "mouseleave") return;
4321
+ this.updateReferenceElementFromEvent(event);
4322
+ this.togglePopover(event.type === "mouseenter");
4269
4323
  };
4270
- this.#handleKeyboardFocus = ({ type, key }) => {
4271
- if (key !== "Tab") return;
4272
- this.togglePopover(type !== "keydown");
4324
+ this.#handleKeyboardFocus = (event) => {
4325
+ if (event.key !== "Tab") return;
4326
+ this.updateReferenceElementFromEvent(event);
4327
+ this.togglePopover(event.type !== "keydown");
4273
4328
  };
4274
4329
  }
4275
4330
  static {
@@ -4291,9 +4346,10 @@ const _OdxTooltip = class _OdxTooltip extends PopoverHost {
4291
4346
  clearTimeout(this.#timeout);
4292
4347
  }
4293
4348
  mountPopover(referenceElement) {
4349
+ super.mountPopover?.(referenceElement);
4294
4350
  if (!referenceElement) return;
4295
- super.mountPopover(referenceElement);
4296
4351
  this.#updateAriaAttributes(referenceElement, this.id);
4352
+ if (this.popover === "manual") return;
4297
4353
  this.addEventListener("mouseleave", this.#handleMouseEvents);
4298
4354
  referenceElement.addEventListener("mouseenter", this.#handleMouseEvents);
4299
4355
  referenceElement.addEventListener("mouseleave", this.#handleReferenceMouseLeave);
@@ -4303,7 +4359,7 @@ const _OdxTooltip = class _OdxTooltip extends PopoverHost {
4303
4359
  referenceElement.addEventListener("keydown", this.#handleKeyboardFocus);
4304
4360
  }
4305
4361
  unmountPopover(referenceElement) {
4306
- super.unmountPopover(referenceElement);
4362
+ super.unmountPopover?.(referenceElement);
4307
4363
  this.#updateAriaAttributes(referenceElement, null);
4308
4364
  referenceElement.removeEventListener("keyup", this.#handleKeyboardFocus);
4309
4365
  referenceElement.removeEventListener("keydown", this.#handleKeyboardFocus);
@@ -4327,7 +4383,6 @@ const _OdxTooltip = class _OdxTooltip extends PopoverHost {
4327
4383
  this.#updateAriaAttributes(this.referenceElement, this.id);
4328
4384
  }
4329
4385
  if (props.has("show")) {
4330
- this.popover = this.show ? "manual" : "auto";
4331
4386
  this.togglePopover(this.show);
4332
4387
  }
4333
4388
  }
@@ -6,11 +6,14 @@ export declare abstract class CheckboxGroupFormControl extends FormControl<strin
6
6
  private elements;
7
7
  protected get childGroups(): CheckboxGroupFormControl[];
8
8
  get controls(): CheckboxFormControl[];
9
- get groupControls(): CheckboxFormControl[];
9
+ get selectedControls(): CheckboxFormControl[];
10
+ get groupControl(): CheckboxFormControl | null;
10
11
  value: string[];
11
12
  constructor();
13
+ protected firstUpdated(_changedProperties: PropertyValues): Promise<void>;
12
14
  toFormValue(): FormData;
13
15
  protected isControl(element: unknown): element is CheckboxFormControl;
16
+ protected isGroupControl(element: unknown): element is CheckboxFormControl;
14
17
  protected isControlChecked(control: CheckboxFormControl): boolean;
15
18
  protected updated(props: PropertyValues<this>): void;
16
19
  protected updateControls(updateFn: (control: CheckboxFormControl, index: number) => void): void;
@@ -4,13 +4,13 @@ export declare function forwardEvent(target: HTMLElement, eventInit?: EventInit)
4
4
  export declare function getElementFromEvent<T = HTMLElement>(event: Event, filterFn: (node: Element) => boolean): T;
5
5
  export declare function waitForAnimations(element?: Element | null, subtree?: boolean): Promise<Animation[]>;
6
6
  export declare function toAriaBooleanAttribute(value: boolean, removeOnFalse?: boolean): 'true' | 'false';
7
- export declare function toPx(value?: string | number | null): string | null;
7
+ export declare function toPx(value?: number | null): string | null;
8
8
  export interface GetAssignedElementOptions {
9
9
  slot?: string;
10
10
  selector?: string;
11
11
  flatten?: boolean;
12
12
  }
13
- export declare function getAssignedElement<T = HTMLElement>(root: Element, options?: GetAssignedElementOptions): T | undefined;
13
+ export declare function getAssignedElement<T = HTMLElement>(root: Element | ShadowRoot | null, options?: GetAssignedElementOptions): T | undefined;
14
14
  export declare function getKeyInfo(event: KeyboardEvent): {
15
15
  up: boolean;
16
16
  right: boolean;
package/dist/main.js CHANGED
@@ -68,14 +68,13 @@ function toAriaBooleanAttribute(value, removeOnFalse = true) {
68
68
  return removeOnFalse && value === false ? null : String(value);
69
69
  }
70
70
  function toPx(value) {
71
- const coercedValue = Number(value);
72
- if (value == null || value === "" || Number.isNaN(coercedValue)) return null;
73
- return `${round(coercedValue, 2)}px`;
71
+ if (value == null || Number.isNaN(value)) return null;
72
+ return `${round(value, 2)}px`;
74
73
  }
75
74
  function getAssignedElement(root, options) {
76
75
  const { slot, selector } = options ?? {};
77
76
  const slotSelector = `slot${slot ? `[name=${slot}]` : ":not([name])"}`;
78
- const slotEl = root.querySelector(slotSelector);
77
+ const slotEl = root?.querySelector(slotSelector);
79
78
  const elements = slotEl?.assignedElements(options) ?? [];
80
79
  if (selector == null) {
81
80
  return elements[0];
@@ -120,10 +119,10 @@ const CanBeDisabled = (superClass) => {
120
119
  this.ariaDisabled = toAriaBooleanAttribute(this.disabled);
121
120
  if (this.disabled) {
122
121
  this.tabIndex = -1;
123
- } else if (this.#initialTabIndex !== void 0) {
124
- this.tabIndex = this.#initialTabIndex;
125
- } else {
122
+ } else if (this.#initialTabIndex === void 0) {
126
123
  this.removeAttribute("tabindex");
124
+ } else {
125
+ this.tabIndex = this.#initialTabIndex;
127
126
  }
128
127
  }
129
128
  }
@@ -244,7 +243,7 @@ class CheckboxFormControl extends FormControl {
244
243
  const newState = state ?? !currentState;
245
244
  if (this.disabled || this.readonly || newState === currentState) return;
246
245
  this.checked = newState;
247
- if (!(emitEvent && this.emit("change"))) return;
246
+ if (!(emitEvent && this.emit("change", { bubbles: true }))) return;
248
247
  this.checked = currentState;
249
248
  }
250
249
  connectedCallback() {
@@ -272,16 +271,20 @@ const _CheckboxGroupFormControl = class _CheckboxGroupFormControl extends FormCo
272
271
  super();
273
272
  this.value = [];
274
273
  this.#handleChangeEvent = (event) => {
275
- const { target } = event;
276
- if (!this.isControl(target)) return;
277
- if (target.checked) {
278
- this.updateValue([...this.value, target.value], true);
279
- return;
274
+ if (this.isControl(event.target)) {
275
+ this.#handleControlChange(event);
276
+ }
277
+ };
278
+ this.#handleControlChange = (event) => {
279
+ if (!this.isControl(event.target)) return;
280
+ if (event.target.checked) {
281
+ this.updateValue([...this.value, event.target.value], true);
282
+ } else {
283
+ this.updateValue(
284
+ this.value.filter((value) => value !== event.target.value),
285
+ true
286
+ );
280
287
  }
281
- this.updateValue(
282
- this.value.filter((value) => value !== target.value),
283
- true
284
- );
285
288
  };
286
289
  if (!isServer) {
287
290
  this.addEventListener("change", this.#handleChangeEvent);
@@ -293,8 +296,15 @@ const _CheckboxGroupFormControl = class _CheckboxGroupFormControl extends FormCo
293
296
  get controls() {
294
297
  return this.#findControls((element) => !element.hasAttribute(GROUP_CONTROL_SELECTOR)).concat(this.childGroups.flatMap((group) => group.controls));
295
298
  }
296
- get groupControls() {
297
- return this.#findControls((element) => element.hasAttribute(GROUP_CONTROL_SELECTOR));
299
+ get selectedControls() {
300
+ return this.controls.filter((control) => this.isControlChecked(control));
301
+ }
302
+ get groupControl() {
303
+ return this.#findControls((element) => element.hasAttribute(GROUP_CONTROL_SELECTOR))[0] ?? null;
304
+ }
305
+ async firstUpdated(_changedProperties) {
306
+ await 0;
307
+ this.value = this.controls.filter((control) => control.checked).map((control) => control.value);
298
308
  }
299
309
  toFormValue() {
300
310
  const formData = new FormData();
@@ -306,6 +316,9 @@ const _CheckboxGroupFormControl = class _CheckboxGroupFormControl extends FormCo
306
316
  isControl(element) {
307
317
  return element instanceof CheckboxFormControl;
308
318
  }
319
+ isGroupControl(element) {
320
+ return this.isControl(element) && element.hasAttribute(GROUP_CONTROL_SELECTOR);
321
+ }
309
322
  isControlChecked(control) {
310
323
  return this.value.includes(control.value);
311
324
  }
@@ -315,10 +328,10 @@ const _CheckboxGroupFormControl = class _CheckboxGroupFormControl extends FormCo
315
328
  this.updateControls((control) => {
316
329
  control.checked = this.isControlChecked(control);
317
330
  });
318
- for (const groupControl of this.groupControls) {
319
- if (groupControl instanceof CheckboxFormControl && "indeterminate" in groupControl) {
320
- groupControl.indeterminate = this.value.length > 0 && this.value.length < this.controls.length;
321
- groupControl.checked = this.controls.length > 0 && this.value.length === this.controls.length;
331
+ if (this.groupControl) {
332
+ this.groupControl.checked = this.controls.length > 0 && this.value.length === this.controls.length;
333
+ if ("indeterminate" in this.groupControl) {
334
+ this.groupControl.indeterminate = this.value.length > 0 && this.value.length < this.controls.length;
322
335
  }
323
336
  }
324
337
  }
@@ -343,13 +356,18 @@ const _CheckboxGroupFormControl = class _CheckboxGroupFormControl extends FormCo
343
356
  }
344
357
  updateValue(value, dispatchEvent) {
345
358
  this.value = value;
359
+ for (const group of this.childGroups) {
360
+ const groupValue = this.value.filter((value2) => group.controls.some((control) => control.value === value2));
361
+ group.updateValue(groupValue, false);
362
+ }
346
363
  if (!dispatchEvent) return;
347
- this.emit("change");
364
+ this.emit("change", { bubbles: true });
348
365
  }
349
- #handleChangeEvent;
350
366
  #findControls(predicate) {
351
367
  return this.elements.filter((element) => this.isControl(element) && predicate(element));
352
368
  }
369
+ #handleChangeEvent;
370
+ #handleControlChange;
353
371
  };
354
372
  __decorateClass([
355
373
  queryAssignedElements({ flatten: true })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@odx/foundation",
3
3
  "description": "A library of Web Component building blocks for ODX",
4
- "version": "1.0.0-beta.80",
4
+ "version": "1.0.0-beta.82",
5
5
  "author": "Drägerwerk AG & Co.KGaA",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "homepage": "https://odx.draeger.com",