@db-ux/react-core-components 2.0.10-popover-d7e8b9a → 2.1.0

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.
@@ -2,7 +2,7 @@
2
2
  import * as React from "react";
3
3
  import { filterPassingProps, getRootProps } from "../../utils/react";
4
4
  import { useState, useRef, useEffect, forwardRef } from "react";
5
- import { cls, delay, getBoolean, getBooleanAsString, getHideProp, getSearchInput, hasVoiceOver, stringPropVisible, uuid, } from "../../utils";
5
+ import { cls, delay, getBoolean, getBooleanAsString, getHideProp, getSearchInput, handleDataOutside, hasVoiceOver, stringPropVisible, uuid, } from "../../utils";
6
6
  import { DEFAULT_CLOSE_BUTTON, DEFAULT_INVALID_MESSAGE, DEFAULT_INVALID_MESSAGE_ID_SUFFIX, DEFAULT_LABEL, DEFAULT_LABEL_ID_SUFFIX, DEFAULT_MESSAGE, DEFAULT_MESSAGE_ID_SUFFIX, DEFAULT_PLACEHOLDER_ID_SUFFIX, DEFAULT_REMOVE, DEFAULT_SELECT_ID_SUFFIX, DEFAULT_SELECTED, DEFAULT_VALID_MESSAGE, DEFAULT_VALID_MESSAGE_ID_SUFFIX, } from "../../shared/constants";
7
7
  import DBCustomSelectList from "../custom-select-list/custom-select-list";
8
8
  import DBCustomSelectListItem from "../custom-select-list-item/custom-select-list-item";
@@ -13,8 +13,6 @@ import DBButton from "../button/button";
13
13
  import DBTooltip from "../tooltip/tooltip";
14
14
  import DBInput from "../input/input";
15
15
  import { DocumentClickListener } from "../../utils/document-click-listener";
16
- import { DocumentScrollListener } from "../../utils/document-scroll-listener";
17
- import { handleFixedDropdown } from "../../utils/floating-components";
18
16
  function DBCustomSelectFn(props, component) {
19
17
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
20
18
  props = Object.assign({ clearSelectionText: "Clear selection", showClearSelection: true }, props);
@@ -47,15 +45,7 @@ function DBCustomSelectFn(props, component) {
47
45
  const [_hasNoOptions, set_hasNoOptions] = useState(() => false);
48
46
  const [_documentClickListenerCallbackId, set_documentClickListenerCallbackId,] = useState(() => undefined);
49
47
  const [_internalChangeTimestamp, set_internalChangeTimestamp] = useState(() => 0);
50
- const [_documentScrollListenerCallbackId, set_documentScrollListenerCallbackId,] = useState(() => undefined);
51
- const [_observer, set_observer] = useState(() => undefined);
52
- function handleDocumentScroll(event) {
53
- var _a, _b;
54
- if (((_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.contains) &&
55
- ((_b = event === null || event === void 0 ? void 0 : event.target) === null || _b === void 0 ? void 0 : _b.contains(detailsRef.current))) {
56
- handleAutoPlacement();
57
- }
58
- }
48
+ const [_searchValue, set_searchValue] = useState(() => undefined);
59
49
  function hasValidState() {
60
50
  var _a;
61
51
  return !!((_a = props.validMessage) !== null && _a !== void 0 ? _a : props.validation === "valid");
@@ -103,9 +93,7 @@ function DBCustomSelectFn(props, component) {
103
93
  }
104
94
  if (event.target.open) {
105
95
  set_documentClickListenerCallbackId(new DocumentClickListener().addCallback((event) => handleDocumentClose(event)));
106
- set_documentScrollListenerCallbackId(new DocumentScrollListener().addCallback((event) => handleDocumentScroll(event)));
107
96
  handleAutoPlacement();
108
- _observer === null || _observer === void 0 ? void 0 : _observer.observe(detailsRef.current);
109
97
  if (!event.target.dataset.test) {
110
98
  // We need this workaround for snapshot testing
111
99
  handleOpenByKeyboardFocus();
@@ -115,10 +103,6 @@ function DBCustomSelectFn(props, component) {
115
103
  if (_documentClickListenerCallbackId) {
116
104
  new DocumentClickListener().removeCallback(_documentClickListenerCallbackId);
117
105
  }
118
- if (_documentScrollListenerCallbackId) {
119
- new DocumentScrollListener().removeCallback(_documentScrollListenerCallbackId);
120
- }
121
- _observer === null || _observer === void 0 ? void 0 : _observer.unobserve(detailsRef.current);
122
106
  }
123
107
  }
124
108
  function getNativeSelectValue() {
@@ -173,10 +157,8 @@ function DBCustomSelectFn(props, component) {
173
157
  if (detailsRef.current) {
174
158
  const dropdown = detailsRef.current.querySelector("article");
175
159
  if (dropdown) {
176
- // This is a workaround for Angular
177
160
  delay(() => {
178
- var _a;
179
- handleFixedDropdown(dropdown, detailsRef.current, (_a = props.placement) !== null && _a !== void 0 ? _a : "bottom");
161
+ handleDataOutside(dropdown);
180
162
  }, 1);
181
163
  }
182
164
  }
@@ -375,11 +357,24 @@ function DBCustomSelectFn(props, component) {
375
357
  }
376
358
  }
377
359
  function handleSearch(event) {
378
- event.stopPropagation();
379
- const filterText = event.target.value;
360
+ let filterText;
361
+ if (typeof event === "string") {
362
+ filterText = event;
363
+ }
364
+ else {
365
+ event.stopPropagation();
366
+ if (props.onSearch) {
367
+ props.onSearch(event);
368
+ }
369
+ filterText = event.target.value;
370
+ set_searchValue(filterText);
371
+ }
380
372
  if (!props.options || !filterText || filterText.length === 0) {
381
373
  set_options(props.options);
382
374
  }
375
+ else if (props.searchFilter) {
376
+ set_options(props.options.filter((option) => props.searchFilter(option, filterText)));
377
+ }
383
378
  else {
384
379
  set_options(props.options.filter((option) => !option.isGroupTitle &&
385
380
  getOptionLabel(option)
@@ -419,14 +414,6 @@ function DBCustomSelectFn(props, component) {
419
414
  set_selectedLabelsId(mId + "-selected-labels");
420
415
  set_infoTextId(mId + "-info");
421
416
  set_invalidMessage(props.invalidMessage || DEFAULT_INVALID_MESSAGE);
422
- set_observer(new IntersectionObserver((payload) => {
423
- if (detailsRef.current) {
424
- const entry = payload.find(({ target }) => target === detailsRef.current);
425
- if (entry && !entry.isIntersecting && detailsRef.current.open) {
426
- detailsRef.current.open = false;
427
- }
428
- }
429
- }));
430
417
  }, []);
431
418
  useEffect(() => {
432
419
  if (detailsRef.current) {
@@ -506,6 +493,13 @@ function DBCustomSelectFn(props, component) {
506
493
  set_options(props.options);
507
494
  setAmountOptions((_b = (_a = props.options) === null || _a === void 0 ? void 0 : _a.filter((option) => !option.isGroupTitle).length) !== null && _b !== void 0 ? _b : 0);
508
495
  }, [props.options]);
496
+ useEffect(() => {
497
+ set_searchValue(props.searchValue);
498
+ if (props.searchValue) {
499
+ const sValue = props.searchValue; // <- workaround for Angular
500
+ handleSearch(sValue);
501
+ }
502
+ }, [props.searchValue]);
509
503
  useEffect(() => {
510
504
  var _a, _b;
511
505
  if ((_a = props.options) === null || _a === void 0 ? void 0 : _a.length) {
@@ -518,7 +512,18 @@ function DBCustomSelectFn(props, component) {
518
512
  }
519
513
  }, [props.options, _values]);
520
514
  useEffect(() => {
515
+ if (props.selectedLabels) {
516
+ set_selectedLabels(props.selectedLabels);
517
+ return;
518
+ }
521
519
  if (_selectedOptions === null || _selectedOptions === void 0 ? void 0 : _selectedOptions.length) {
520
+ if (props.transformSelectedLabels) {
521
+ // We need to add this to another ``const`` for Angular generated output to work
522
+ const selectedOptions = _selectedOptions;
523
+ const transformFn = props.transformSelectedLabels;
524
+ set_selectedLabels(transformFn(selectedOptions));
525
+ return;
526
+ }
522
527
  if (props.selectedType === "amount") {
523
528
  set_selectedLabels(props.amountText
524
529
  ? props.amountText
@@ -531,7 +536,13 @@ function DBCustomSelectFn(props, component) {
531
536
  else {
532
537
  set_selectedLabels("");
533
538
  }
534
- }, [_selectedOptions, props.selectedType, props.amountText]);
539
+ }, [
540
+ _selectedOptions,
541
+ props.selectedType,
542
+ props.amountText,
543
+ props.selectedLabels,
544
+ props.transformSelectedLabels,
545
+ ]);
535
546
  useEffect(() => {
536
547
  var _a;
537
548
  if (props.onAmountChange) {
@@ -564,7 +575,7 @@ function DBCustomSelectFn(props, component) {
564
575
  props.selectedType === "tag" ? (React.createElement("div", null, _selectedOptions === null || _selectedOptions === void 0 ? void 0 : _selectedOptions.map((option, index) => (React.createElement(DBTag, { emphasis: "strong", behavior: "removable", removeButton: getTagRemoveLabel(index), onRemove: (event) => handleTagRemove(option, event), key: "tag-" + getOptionKey(option) }, getOptionLabel(option)))))) : null),
565
576
  React.createElement(DBCustomSelectDropdown, { width: props.dropdownWidth },
566
577
  searchEnabled ? (React.createElement("div", null,
567
- React.createElement(DBInput, { type: "search", ref: searchInputRef, name: _id, form: _id, showLabel: false, label: (_d = props.searchLabel) !== null && _d !== void 0 ? _d : DEFAULT_LABEL, placeholder: (_e = props.searchPlaceholder) !== null && _e !== void 0 ? _e : props.searchLabel, ariaDescribedBy: _hasNoOptions || props.showLoading
578
+ React.createElement(DBInput, { type: "search", ref: searchInputRef, name: _id, form: _id, showLabel: false, value: _searchValue, label: (_d = props.searchLabel) !== null && _d !== void 0 ? _d : DEFAULT_LABEL, placeholder: (_e = props.searchPlaceholder) !== null && _e !== void 0 ? _e : props.searchLabel, ariaDescribedBy: _hasNoOptions || props.showLoading
568
579
  ? _infoTextId
569
580
  : undefined, onInput: (event) => handleSearch(event) }))) : null,
570
581
  _hasNoOptions || props.showLoading ? (React.createElement(DBInfotext, { id: _infoTextId, icon: _hasNoOptions ? undefined : "circular_arrows", semantic: _hasNoOptions ? "warning" : "informational" }, (_f = (_hasNoOptions ? props.noResultsText : props.loadingText)) !== null && _f !== void 0 ? _f : DEFAULT_MESSAGE)) : (React.createElement(React.Fragment, null,
@@ -1,4 +1,4 @@
1
- import { BaseFormProps, CloseEventState, CustomFormProps, DocumentScrollState, FormMessageProps, FormState, FromValidState, GlobalProps, GlobalState, IconProps, PlacementVerticalType, RequiredProps, ShowIconProps, ShowLabelProps, ValidationType, WidthType } from '../../shared/model';
1
+ import { BaseFormProps, CloseEventState, CustomFormProps, FormMessageProps, FormState, FromValidState, GlobalProps, GlobalState, IconProps, PlacementVerticalType, PopoverState, RequiredProps, ShowIconProps, ShowLabelProps, ValidationType, WidthType } from '../../shared/model';
2
2
  import { DBCustomSelectFormFieldDefaultProps } from '../custom-select-form-field/model';
3
3
  import { CustomSelectDropdownWidthType } from '../custom-select-dropdown/model';
4
4
  import { DBCustomSelectListItemExtraProps } from '../custom-select-list-item/model';
@@ -51,16 +51,24 @@ export type DBCustomSelectEvents = {
51
51
  * Informs the user when dropdown was toggled.
52
52
  */
53
53
  dropdownToggle?: (event: any) => void;
54
- };
55
- export type DBCustomSelectDefaultProps = {
56
54
  /**
57
- * Overwrite the default aria-label (props.label) for the custom-select-list
55
+ * Informs the user when a search was performed.
58
56
  */
59
- ariaListLabel?: string;
57
+ onSearch?: (event: any) => void;
58
+ /**
59
+ * Informs the user when a search was performed.
60
+ */
61
+ search?: (event: any) => void;
62
+ };
63
+ export type DBCustomSelectDefaultProps = {
60
64
  /**
61
65
  * Optional: if select-type="amount" change the shown text
62
66
  */
63
67
  amountText?: string;
68
+ /**
69
+ * Overwrite the default aria-label (props.label) for the custom-select-list
70
+ */
71
+ ariaListLabel?: string;
64
72
  /**
65
73
  * Label for the clear selection button
66
74
  */
@@ -107,6 +115,10 @@ export type DBCustomSelectDefaultProps = {
107
115
  * Optional: if you use selectedType=tag and options, you need to set the removeTagsTexts for screen reader users
108
116
  */
109
117
  removeTagsTexts?: string[];
118
+ /**
119
+ * Optional: Change the filter function for the search input
120
+ */
121
+ searchFilter?: (option: CustomSelectOptionType, filterText: string) => boolean;
110
122
  /**
111
123
  * Search label
112
124
  */
@@ -115,10 +127,19 @@ export type DBCustomSelectDefaultProps = {
115
127
  * Search placeholder
116
128
  */
117
129
  searchPlaceholder?: string;
130
+ /**
131
+ * Optional: Prefill the value of the search input
132
+ */
133
+ searchValue?: string;
118
134
  /**
119
135
  * Select all checkbox label
120
136
  */
121
137
  selectAllLabel?: string;
138
+ /**
139
+ * Optional: If you want to show a custom label for the selected values.
140
+ * You need to define the empty state as well based on selected options.
141
+ */
142
+ selectedLabels?: string;
122
143
  /**
123
144
  * Change the selected type for values shown in multi select
124
145
  */
@@ -143,6 +164,10 @@ export type DBCustomSelectDefaultProps = {
143
164
  * Forces select all checkbox (only for multiple).
144
165
  */
145
166
  showSelectAll?: boolean;
167
+ /**
168
+ * Optional: If you want to show a custom label based on the selected options.
169
+ */
170
+ transformSelectedLabels?: (selectedOptions?: CustomSelectOptionType[]) => string;
146
171
  /**
147
172
  * Initial value for multi select
148
173
  */
@@ -164,6 +189,7 @@ export type DBCustomSelectDefaultState = {
164
189
  _infoTextId?: string;
165
190
  _internalChangeTimestamp: number;
166
191
  _documentClickListenerCallbackId?: string;
192
+ _searchValue?: string;
167
193
  getNativeSelectValue: () => string;
168
194
  getOptionLabel: (option: CustomSelectOptionType) => string;
169
195
  getOptionChecked: (value?: string) => boolean;
@@ -189,6 +215,5 @@ export type DBCustomSelectDefaultState = {
189
215
  getSelectAllLabel: () => string;
190
216
  selectAllChecked: boolean;
191
217
  selectAllIndeterminate: boolean;
192
- handleAutoPlacement: () => void;
193
218
  };
194
- export type DBCustomSelectState = DBCustomSelectDefaultState & GlobalState & FormState & FromValidState & CloseEventState & DocumentScrollState;
219
+ export type DBCustomSelectState = DBCustomSelectDefaultState & GlobalState & FormState & FromValidState & CloseEventState & PopoverState;
@@ -13,5 +13,6 @@ export type DBPopoverProps = DBPopoverDefaultProps & GlobalProps & SpacingProps
13
13
  export type DBPopoverDefaultState = {
14
14
  isExpanded?: boolean;
15
15
  getTrigger: () => Element | null;
16
+ handleLeave: (event: any) => void;
16
17
  };
17
18
  export type DBPopoverState = DBPopoverDefaultState & GlobalState & PopoverState & InitializedState;
@@ -2,66 +2,29 @@
2
2
  import * as React from "react";
3
3
  import { filterPassingProps, getRootProps } from "../../utils/react";
4
4
  import { useState, useRef, useEffect, forwardRef } from "react";
5
- import { cls, delay as utilsDelay, getBooleanAsString } from "../../utils";
6
- import { handleFixedPopover } from "../../utils/floating-components";
7
- import { DocumentScrollListener } from "../../utils/document-scroll-listener";
5
+ import { cls, getBooleanAsString, handleDataOutside } from "../../utils";
8
6
  function DBPopoverFn(props, component) {
9
7
  var _a;
10
8
  const _ref = component || useRef(component);
11
9
  const [initialized, setInitialized] = useState(() => false);
12
10
  const [isExpanded, setIsExpanded] = useState(() => false);
13
- const [_documentScrollListenerCallbackId, set_documentScrollListenerCallbackId,] = useState(() => undefined);
14
- const [_observer, set_observer] = useState(() => undefined);
15
- function handleEscape(event) {
16
- if (!event || event.key === "Escape") {
17
- // TODO: Recursive for any child
18
- for (const child of Array.from(_ref.current.children)) {
19
- child.blur();
20
- }
21
- }
22
- }
23
11
  function handleAutoPlacement() {
12
+ setIsExpanded(true);
24
13
  if (!_ref.current)
25
14
  return;
26
15
  const article = _ref.current.querySelector("article");
27
- if (article) {
28
- // This is a workaround for angular
29
- utilsDelay(() => {
30
- var _a;
31
- handleFixedPopover(article, _ref.current, (_a = props.placement) !== null && _a !== void 0 ? _a : "bottom");
32
- }, 1);
33
- }
34
- }
35
- function handleDocumentScroll(event) {
36
- var _a, _b;
37
- if (((_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.contains) && ((_b = event === null || event === void 0 ? void 0 : event.target) === null || _b === void 0 ? void 0 : _b.contains(_ref.current))) {
38
- handleAutoPlacement();
39
- }
40
- }
41
- function handleEnter() {
42
- setIsExpanded(true);
43
- set_documentScrollListenerCallbackId(new DocumentScrollListener().addCallback((event) => handleDocumentScroll(event)));
44
- handleAutoPlacement();
45
- const child = getTrigger();
46
- if (child) {
47
- _observer === null || _observer === void 0 ? void 0 : _observer.observe(child);
48
- }
16
+ if (!article)
17
+ return;
18
+ handleDataOutside(article);
49
19
  }
50
20
  function handleLeave(event) {
51
- const element = event === null || event === void 0 ? void 0 : event.target;
52
- const parent = element === null || element === void 0 ? void 0 : element.parentNode;
21
+ const element = event.target;
22
+ const parent = element.parentNode;
53
23
  if (!parent ||
54
24
  (element.parentNode.querySelector(":focus") !== element &&
55
25
  element.parentNode.querySelector(":focus-within") !== element &&
56
26
  element.parentNode.querySelector(":hover") !== element)) {
57
27
  setIsExpanded(false);
58
- if (_documentScrollListenerCallbackId) {
59
- new DocumentScrollListener().removeCallback(_documentScrollListenerCallbackId);
60
- }
61
- const child = getTrigger();
62
- if (child) {
63
- _observer === null || _observer === void 0 ? void 0 : _observer.unobserve(child);
64
- }
65
28
  }
66
29
  }
67
30
  function getTrigger() {
@@ -88,25 +51,11 @@ function DBPopoverFn(props, component) {
88
51
  }, []);
89
52
  useEffect(() => {
90
53
  if (_ref.current && initialized) {
91
- setInitialized(false);
92
54
  const child = getTrigger();
93
55
  if (child) {
94
56
  child.ariaHasPopup = "true";
95
57
  }
96
- handleAutoPlacement();
97
- _ref.current.addEventListener("keydown", (event) => handleEscape(event));
98
- ["mouseenter", "focusin"].forEach((event) => {
99
- _ref.current.addEventListener(event, () => handleEnter());
100
- });
101
- ["mouseleave", "focusout"].forEach((event) => {
102
- _ref.current.addEventListener(event, () => handleLeave());
103
- });
104
- set_observer(new IntersectionObserver((payload) => {
105
- const entry = payload.find(({ target }) => target === getTrigger());
106
- if (entry && !entry.isIntersecting) {
107
- handleEscape(false);
108
- }
109
- }));
58
+ setInitialized(false);
110
59
  }
111
60
  }, [_ref.current, initialized]);
112
61
  useEffect(() => {
@@ -117,7 +66,7 @@ function DBPopoverFn(props, component) {
117
66
  }
118
67
  }
119
68
  }, [_ref.current, isExpanded]);
120
- return (React.createElement("div", Object.assign({ ref: _ref }, filterPassingProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font"]), { id: props.id }, getRootProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font"]), { className: cls("db-popover", props.className) }),
69
+ return (React.createElement("div", Object.assign({ ref: _ref }, filterPassingProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font"]), { id: props.id }, getRootProps(props, ["data-icon-variant", "data-icon-variant-before", "data-icon-variant-after", "data-icon-weight", "data-icon-weight-before", "data-icon-weight-after", "data-interactive", "data-force-mobile", "data-color", "data-container-color", "data-bg-color", "data-on-bg-color", "data-color-scheme", "data-font-size", "data-headline-size", "data-divider", "data-focus", "data-font"]), { className: cls("db-popover", props.className), onFocus: (event) => handleAutoPlacement(), onBlur: (event) => handleLeave(event), onMouseEnter: (event) => handleAutoPlacement(), onMouseLeave: (event) => handleLeave(event) }),
121
70
  React.createElement(React.Fragment, null, props.trigger),
122
71
  React.createElement("article", { className: "db-popover-content", "data-spacing": props.spacing, "data-gap": getBooleanAsString(props.gap), "data-animation": getBooleanAsString((_a = props.animation) !== null && _a !== void 0 ? _a : true), "data-open": getBooleanAsString(props.open), "data-delay": props.delay, "data-width": props.width, "data-placement": props.placement }, props.children)));
123
72
  }
@@ -4,6 +4,7 @@ import { filterPassingProps, getRootProps } from "../../utils/react";
4
4
  import { useState, useRef, useEffect, forwardRef } from "react";
5
5
  import { cls, getBooleanAsString, getHideProp } from "../../utils";
6
6
  import { DEFAULT_REMOVE } from "../../shared/constants";
7
+ import DBTooltip from "../tooltip/tooltip";
7
8
  function DBTagFn(props, component) {
8
9
  var _a;
9
10
  const _ref = component || useRef(component);
@@ -42,7 +43,8 @@ function DBTagFn(props, component) {
42
43
  React.createElement(React.Fragment, null, props.content),
43
44
  props.children,
44
45
  props.text ? React.createElement(React.Fragment, null, props.text) : null,
45
- props.behavior === "removable" ? (React.createElement("button", { className: "db-button db-tab-remove-button", "data-icon": "cross", "data-size": "small", "data-no-text": "true", "data-variant": "ghost", onClick: (event) => handleRemove(event), title: getRemoveButtonText() }, getRemoveButtonText())) : null));
46
+ props.behavior === "removable" ? (React.createElement("button", { className: "db-button db-tab-remove-button", "data-icon": "cross", "data-size": "small", "data-no-text": "true", "data-variant": "ghost", type: "button", onClick: (event) => handleRemove(event) },
47
+ React.createElement(DBTooltip, { variant: "label" }, getRemoveButtonText()))) : null));
46
48
  }
47
49
  const DBTag = forwardRef(DBTagFn);
48
50
  export default DBTag;
@@ -1,9 +1,18 @@
1
- import { ClickEventState, DocumentScrollState, EmphasisProps, GlobalProps, GlobalState, InitializedState, PlacementProps, PopoverProps, PopoverState } from '../../shared/model';
1
+ import { ClickEventState, EmphasisProps, GlobalProps, GlobalState, InitializedState, PlacementProps, PopoverProps, PopoverState } from '../../shared/model';
2
+ export declare const TooltipVariantList: readonly ["description", "label"];
3
+ export type TooltipVariantType = (typeof TooltipVariantList)[number];
2
4
  export type DBTooltipDefaultProps = {
5
+ /**
6
+ * Show/Hides arrow
7
+ */
3
8
  showArrow?: boolean | string;
9
+ /**
10
+ * Change the behavior of the tooltip:
11
+ * - description: Adds `aria-describedby` to parent
12
+ * - label: Adds `aria-labelledby` to parent
13
+ */
14
+ variant?: TooltipVariantType;
4
15
  };
5
16
  export type DBTooltipProps = DBTooltipDefaultProps & GlobalProps & EmphasisProps & PlacementProps & PopoverProps;
6
- export type DBTooltipDefaultState = {
7
- getParent: () => HTMLElement;
8
- };
9
- export type DBTooltipState = DBTooltipDefaultState & GlobalState & ClickEventState<HTMLElement> & PopoverState & InitializedState & DocumentScrollState;
17
+ export type DBTooltipDefaultState = {};
18
+ export type DBTooltipState = DBTooltipDefaultState & GlobalState & ClickEventState<HTMLElement> & PopoverState & InitializedState;
@@ -1 +1 @@
1
- export {};
1
+ export const TooltipVariantList = ['description', 'label'];
@@ -1,3 +1,3 @@
1
1
  import * as React from "react";
2
- declare const DBTooltip: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<any>, keyof import("../../shared/model").GlobalProps | keyof import("../../shared/model").PopoverProps | "placement" | "emphasis" | "showArrow"> & import("./model").DBTooltipDefaultProps & import("../../shared/model").GlobalProps & import("../../shared/model").EmphasisProps & import("../../shared/model").PlacementProps & import("../../shared/model").PopoverProps & React.RefAttributes<any>>;
2
+ declare const DBTooltip: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<any>, keyof import("../../shared/model").GlobalProps | keyof import("../../shared/model").PopoverProps | "placement" | "emphasis" | keyof import("./model").DBTooltipDefaultProps> & import("./model").DBTooltipDefaultProps & import("../../shared/model").GlobalProps & import("../../shared/model").EmphasisProps & import("../../shared/model").PlacementProps & import("../../shared/model").PopoverProps & React.RefAttributes<any>>;
3
3
  export default DBTooltip;
@@ -2,62 +2,19 @@
2
2
  import * as React from "react";
3
3
  import { filterPassingProps, getRootProps } from "../../utils/react";
4
4
  import { useState, useRef, useEffect, forwardRef } from "react";
5
- import { cls, delay as utilsDelay, getBooleanAsString, uuid, } from "../../utils";
5
+ import { cls, getBooleanAsString, handleDataOutside, uuid } from "../../utils";
6
6
  import { DEFAULT_ID } from "../../shared/constants";
7
- import { handleFixedPopover } from "../../utils/floating-components";
8
- import { DocumentScrollListener } from "../../utils/document-scroll-listener";
9
7
  function DBTooltipFn(props, component) {
10
8
  var _a, _b;
11
9
  const _ref = component || useRef(component);
12
10
  const [_id, set_id] = useState(() => DEFAULT_ID);
13
11
  const [initialized, setInitialized] = useState(() => false);
14
- const [_documentScrollListenerCallbackId, set_documentScrollListenerCallbackId,] = useState(() => undefined);
15
- const [_observer, set_observer] = useState(() => undefined);
16
12
  function handleClick(event) {
17
13
  event.stopPropagation();
18
14
  }
19
- function handleEscape(event) {
20
- if ((!event || event.key === "Escape") &&
21
- _ref.current &&
22
- getComputedStyle(_ref.current).visibility === "visible") {
23
- getParent().blur();
24
- }
25
- }
26
- function getParent() {
27
- let parent = _ref.current.parentElement;
28
- if (parent && parent.localName.includes("tooltip")) {
29
- // Angular workaround
30
- parent = parent.parentElement;
31
- }
32
- return parent;
33
- }
34
- function handleAutoPlacement(parent) {
35
- if (!parent)
36
- return;
37
- if (_ref.current) {
38
- // This is a workaround for angular
39
- utilsDelay(() => {
40
- var _a;
41
- handleFixedPopover(_ref.current, parent, (_a = props.placement) !== null && _a !== void 0 ? _a : "bottom");
42
- }, 1);
43
- }
44
- }
45
- function handleDocumentScroll(event, parent) {
46
- var _a, _b;
47
- if (((_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.contains) && ((_b = event === null || event === void 0 ? void 0 : event.target) === null || _b === void 0 ? void 0 : _b.contains(_ref.current))) {
48
- handleAutoPlacement(parent);
49
- }
50
- }
51
- function handleLeave() {
52
- if (_documentScrollListenerCallbackId) {
53
- new DocumentScrollListener().removeCallback(_documentScrollListenerCallbackId);
54
- }
55
- _observer === null || _observer === void 0 ? void 0 : _observer.unobserve(getParent());
56
- }
57
- function handleEnter(parent) {
58
- set_documentScrollListenerCallbackId(new DocumentScrollListener().addCallback((event) => handleDocumentScroll(event, parent)));
59
- handleAutoPlacement(parent);
60
- _observer === null || _observer === void 0 ? void 0 : _observer.observe(getParent());
15
+ function handleAutoPlacement() {
16
+ if (_ref.current)
17
+ handleDataOutside(_ref.current);
61
18
  }
62
19
  useEffect(() => {
63
20
  set_id(props.id || "tooltip-" + uuid());
@@ -65,24 +22,23 @@ function DBTooltipFn(props, component) {
65
22
  }, []);
66
23
  useEffect(() => {
67
24
  if (_ref.current && initialized && _id) {
68
- const parent = getParent();
25
+ let parent = _ref.current.parentElement;
26
+ if (parent && parent.localName.includes("tooltip")) {
27
+ // Angular workaround
28
+ parent = parent.parentElement;
29
+ }
69
30
  if (parent) {
70
- ["mouseenter", "focusin"].forEach((event) => {
71
- parent.addEventListener(event, () => handleEnter(parent));
72
- });
73
- parent.addEventListener("keydown", (event) => handleEscape(event));
74
- ["mouseleave", "focusout"].forEach((event) => {
75
- parent.addEventListener(event, () => handleLeave());
31
+ ["mouseenter", "focus"].forEach((event) => {
32
+ parent.addEventListener(event, () => handleAutoPlacement());
76
33
  });
77
34
  parent.setAttribute("data-has-tooltip", "true");
78
- parent.setAttribute("aria-describedby", _id);
79
- }
80
- set_observer(new IntersectionObserver((payload) => {
81
- const entry = payload.find(({ target }) => target === getParent());
82
- if (entry && !entry.isIntersecting) {
83
- handleEscape(false);
35
+ if (props.variant === "label") {
36
+ parent.setAttribute("aria-labelledby", _id);
37
+ }
38
+ else {
39
+ parent.setAttribute("aria-describedby", _id);
84
40
  }
85
- }));
41
+ }
86
42
  setInitialized(false);
87
43
  }
88
44
  }, [_ref.current, initialized]);
@@ -150,6 +150,9 @@ export type PopoverProps = {
150
150
  */
151
151
  width?: PopoverWidthType;
152
152
  };
153
+ export type PopoverState = {
154
+ handleAutoPlacement: () => void;
155
+ };
153
156
  export type NameProps = {
154
157
  /**
155
158
  * The name attribute gives the name of the element to group it.
@@ -490,14 +493,3 @@ export type ValueLabelType = {
490
493
  value: string;
491
494
  label?: string;
492
495
  };
493
- export type DocumentScrollState = {
494
- _documentScrollListenerCallbackId?: string;
495
- handleDocumentScroll: (event: any, parent?: HTMLElement) => void;
496
- _observer?: IntersectionObserver;
497
- };
498
- export type PopoverState = {
499
- handleEscape: (event: any) => void;
500
- handleAutoPlacement: (parent?: HTMLElement) => void;
501
- handleEnter: (parent?: HTMLElement) => void;
502
- handleLeave: (event?: any) => void;
503
- } & DocumentScrollState;
@@ -7,6 +7,19 @@ export type ClassNameArg = string | {
7
7
  [key: string]: boolean | undefined;
8
8
  } | undefined;
9
9
  export declare const cls: (...args: ClassNameArg[]) => string;
10
+ export declare const visibleInVX: (el: Element) => boolean;
11
+ export declare const visibleInVY: (el: Element) => boolean;
12
+ export declare const isInView: (el: Element) => {
13
+ outTop: boolean;
14
+ outBottom: boolean;
15
+ outLeft: boolean;
16
+ outRight: boolean;
17
+ };
18
+ export interface DBDataOutsidePair {
19
+ vx?: 'left' | 'right';
20
+ vy?: 'top' | 'bottom';
21
+ }
22
+ export declare const handleDataOutside: (el: Element) => DBDataOutsidePair;
10
23
  export declare const isArrayOfStrings: (value: unknown) => value is string[];
11
24
  export declare const hasVoiceOver: () => boolean;
12
25
  export declare const delay: (fn: () => void, ms: number) => Promise<unknown>;
@@ -37,6 +37,76 @@ export const cls = (...args) => {
37
37
  }
38
38
  return result.trim();
39
39
  };
40
+ export const visibleInVX = (el) => {
41
+ const { left, right } = el.getBoundingClientRect();
42
+ const { innerWidth } = window;
43
+ return left >= 0 && right <= innerWidth;
44
+ };
45
+ export const visibleInVY = (el) => {
46
+ const { top, bottom } = el.getBoundingClientRect();
47
+ const { innerHeight } = window;
48
+ return top >= 0 && bottom <= innerHeight;
49
+ };
50
+ export const isInView = (el) => {
51
+ var _a;
52
+ const { top, bottom, left, right } = el.getBoundingClientRect();
53
+ const { innerHeight, innerWidth } = window;
54
+ let outTop = top < 0;
55
+ let outBottom = bottom > innerHeight;
56
+ let outLeft = left < 0;
57
+ let outRight = right > innerWidth;
58
+ // We need to check if it was already outside
59
+ const outsideY = el.hasAttribute('data-outside-vy');
60
+ const outsideX = el.hasAttribute('data-outside-vx');
61
+ const parentRect = (_a = el === null || el === void 0 ? void 0 : el.parentElement) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
62
+ if (parentRect) {
63
+ if (outsideY) {
64
+ const position = el.getAttribute('data-outside-vy');
65
+ if (position === 'top') {
66
+ outTop = parentRect.top - (bottom - parentRect.bottom) < 0;
67
+ }
68
+ else {
69
+ outBottom = parentRect.bottom + (parentRect.top - top) > innerHeight;
70
+ }
71
+ }
72
+ if (outsideX) {
73
+ const position = el.getAttribute('data-outside-vx');
74
+ if (position === 'left') {
75
+ outLeft = parentRect.left - (right - parentRect.right) < 0;
76
+ }
77
+ else {
78
+ outRight = parentRect.right + (parentRect.left - left) > innerWidth;
79
+ }
80
+ }
81
+ }
82
+ return {
83
+ outTop,
84
+ outBottom,
85
+ outLeft,
86
+ outRight
87
+ };
88
+ };
89
+ export const handleDataOutside = (el) => {
90
+ const { outTop, outBottom, outLeft, outRight } = isInView(el);
91
+ let dataOutsidePair = {};
92
+ if (outTop || outBottom) {
93
+ dataOutsidePair = {
94
+ vy: outTop ? 'top' : 'bottom'
95
+ };
96
+ el.setAttribute('data-outside-vy', dataOutsidePair.vy);
97
+ }
98
+ else {
99
+ el.removeAttribute('data-outside-vy');
100
+ }
101
+ if (outLeft || outRight) {
102
+ dataOutsidePair = Object.assign(Object.assign({}, dataOutsidePair), { vx: outRight ? 'right' : 'left' });
103
+ el.setAttribute('data-outside-vx', dataOutsidePair.vx);
104
+ }
105
+ else {
106
+ el.removeAttribute('data-outside-vx');
107
+ }
108
+ return dataOutsidePair;
109
+ };
40
110
  export const isArrayOfStrings = (value) => Array.isArray(value) && value.every(item => typeof item === 'string');
41
111
  const appleOs = ['Mac', 'iPhone', 'iPad', 'iPod'];
42
112
  export const hasVoiceOver = () => typeof window !== 'undefined' && appleOs.some(os => window.navigator.userAgent.includes(os));
@@ -15,7 +15,7 @@ export declare class NavigationItemSafeTriangle {
15
15
  private initialized;
16
16
  private mouseX;
17
17
  private mouseY;
18
- constructor(element: HTMLElement | null, subNavigation: HTMLElement | null);
18
+ constructor(element: HTMLElement | null, subNavigation: Element | null);
19
19
  private init;
20
20
  enableFollow(): void;
21
21
  disableFollow(): void;
@@ -1,4 +1,4 @@
1
- import { handleDataOutside } from './floating-components';
1
+ import { handleDataOutside } from './index';
2
2
  export const isEventTargetNavigationItem = (event) => {
3
3
  var _a, _b;
4
4
  const { target } = event;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@db-ux/react-core-components",
3
- "version": "2.0.10-popover-d7e8b9a",
3
+ "version": "2.1.0",
4
4
  "description": "React components for @db-ux/core-components",
5
5
  "repository": {
6
6
  "type": "git",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "sideEffects": false,
40
40
  "dependencies": {
41
- "@db-ux/core-components": "2.0.10-popover-d7e8b9a",
42
- "@db-ux/core-foundations": "2.0.10-popover-d7e8b9a"
41
+ "@db-ux/core-components": "2.1.0",
42
+ "@db-ux/core-foundations": "2.1.0"
43
43
  }
44
44
  }
@@ -1,9 +0,0 @@
1
- export declare class DocumentScrollListener {
2
- private static callbacks;
3
- private static _instance;
4
- private static runCallbacks;
5
- private ticking;
6
- constructor();
7
- addCallback(callback: (event: any) => void): string;
8
- removeCallback(id: string): void;
9
- }
@@ -1,38 +0,0 @@
1
- import { uuid } from './index';
2
- export class DocumentScrollListener {
3
- static runCallbacks(event) {
4
- for (const callback of Object.values(DocumentScrollListener.callbacks)) {
5
- if (typeof callback === 'function') {
6
- callback(event);
7
- }
8
- }
9
- }
10
- constructor() {
11
- this.ticking = false;
12
- if (DocumentScrollListener._instance) {
13
- return DocumentScrollListener._instance;
14
- }
15
- DocumentScrollListener._instance = this;
16
- if (self.document) {
17
- self.document.addEventListener('scroll', event => {
18
- if (!this.ticking) {
19
- window.requestAnimationFrame(() => {
20
- DocumentScrollListener.runCallbacks(event);
21
- this.ticking = false;
22
- });
23
- this.ticking = true;
24
- }
25
- }, true);
26
- }
27
- }
28
- addCallback(callback) {
29
- const callbackID = uuid();
30
- DocumentScrollListener.callbacks[callbackID] = callback;
31
- return callbackID;
32
- }
33
- removeCallback(id) {
34
- delete DocumentScrollListener.callbacks[id];
35
- }
36
- }
37
- DocumentScrollListener.callbacks = {};
38
- DocumentScrollListener._instance = null;
@@ -1,7 +0,0 @@
1
- export interface DBDataOutsidePair {
2
- vx?: 'left' | 'right';
3
- vy?: 'top' | 'bottom';
4
- }
5
- export declare const handleDataOutside: (el: HTMLElement) => DBDataOutsidePair;
6
- export declare const handleFixedDropdown: (element: HTMLElement, parent: HTMLElement, placement: string) => void;
7
- export declare const handleFixedPopover: (element: HTMLElement, parent: HTMLElement, placement: string) => void;
@@ -1,290 +0,0 @@
1
- // TODO: We should reevaluate this as soon as CSS Anchor Positioning is supported in all relevant browsers
2
- const isInView = (el) => {
3
- var _a;
4
- const { top, bottom, left, right } = el.getBoundingClientRect();
5
- const { innerHeight, innerWidth } = window;
6
- let outTop = top < 0;
7
- let outBottom = bottom > innerHeight;
8
- let outLeft = left < 0;
9
- let outRight = right > innerWidth;
10
- // We need to check if it was already outside
11
- const outsideY = el.dataset['outsideVy'];
12
- const outsideX = el.dataset['outsideVx'];
13
- const parentRect = (_a = el === null || el === void 0 ? void 0 : el.parentElement) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
14
- if (parentRect) {
15
- if (outsideY) {
16
- const position = el.dataset['outsideVy'];
17
- if (position === 'top') {
18
- outTop = parentRect.top - (bottom - parentRect.bottom) < 0;
19
- }
20
- else {
21
- outBottom = parentRect.bottom + (parentRect.top - top) > innerHeight;
22
- }
23
- }
24
- if (outsideX) {
25
- const position = el.dataset['outsideVx'];
26
- if (position === 'left') {
27
- outLeft = parentRect.left - (right - parentRect.right) < 0;
28
- }
29
- else {
30
- outRight = parentRect.right + (parentRect.left - left) > innerWidth;
31
- }
32
- }
33
- }
34
- return {
35
- outTop,
36
- outBottom,
37
- outLeft,
38
- outRight
39
- };
40
- };
41
- export const handleDataOutside = (el) => {
42
- const { outTop, outBottom, outLeft, outRight } = isInView(el);
43
- let dataOutsidePair = {};
44
- if (outTop || outBottom) {
45
- dataOutsidePair = {
46
- vy: outTop ? 'top' : 'bottom'
47
- };
48
- el.dataset['outsideVy'] = dataOutsidePair.vy;
49
- }
50
- else {
51
- delete el.dataset['outsideVy'];
52
- }
53
- if (outLeft || outRight) {
54
- dataOutsidePair = Object.assign(Object.assign({}, dataOutsidePair), { vx: outRight ? 'right' : 'left' });
55
- el.dataset['outsideVx'] = dataOutsidePair.vx;
56
- }
57
- else {
58
- delete el.dataset['outsideVx'];
59
- }
60
- return dataOutsidePair;
61
- };
62
- export const handleFixedDropdown = (element, parent, placement) => {
63
- // We skip this if we are in mobile it's already fixed
64
- if (getComputedStyle(element).zIndex === '9999')
65
- return;
66
- const { top, bottom, childHeight, childWidth, width, right, left, correctedPlacement } = getFloatingProps(element, parent, placement);
67
- const fullWidth = element.dataset['width'] === 'full';
68
- if (fullWidth) {
69
- element.style.inlineSize = `${width}px`;
70
- }
71
- if (correctedPlacement === 'top' || correctedPlacement === 'bottom' || correctedPlacement === 'top-start' || correctedPlacement === 'bottom-start') {
72
- element.style.insetInlineStart = `${left}px`;
73
- }
74
- else if (correctedPlacement === 'top-end' || correctedPlacement === 'bottom-end') {
75
- element.style.insetInlineStart = `${right - childWidth}px`;
76
- }
77
- if (correctedPlacement === null || correctedPlacement === void 0 ? void 0 : correctedPlacement.startsWith('top')) {
78
- element.style.insetBlockStart = `${top - childHeight}px`;
79
- }
80
- else if (correctedPlacement === null || correctedPlacement === void 0 ? void 0 : correctedPlacement.startsWith('bottom')) {
81
- element.style.insetBlockStart = `${bottom}px`;
82
- }
83
- element.style.position = 'fixed';
84
- };
85
- const getFloatingProps = (element, parent, placement) => {
86
- const childRect = element.getBoundingClientRect();
87
- const { top, height, bottom, right, left, width } = parent.getBoundingClientRect();
88
- const { innerHeight, innerWidth } = window;
89
- let childHeight = childRect.height;
90
- let childWidth = childRect.width;
91
- if (placement === 'bottom' || placement === 'top') {
92
- childWidth = childWidth / 2;
93
- }
94
- if (placement === 'left' || placement === 'right') {
95
- childHeight = childHeight / 2;
96
- }
97
- const outsideBottom = bottom + childHeight > innerHeight;
98
- const outsideTop = top - childHeight < 0;
99
- const outsideLeft = left - childWidth < 0;
100
- const outsideRight = right + childWidth > innerWidth;
101
- let correctedPlacement = placement;
102
- if (placement.startsWith('bottom')) {
103
- if (outsideBottom) {
104
- correctedPlacement = placement === null || placement === void 0 ? void 0 : placement.replace('bottom', 'top');
105
- if (outsideLeft && outsideRight) {
106
- correctedPlacement = 'top';
107
- }
108
- else if (outsideLeft) {
109
- correctedPlacement = 'top-start';
110
- }
111
- else if (outsideRight) {
112
- correctedPlacement = 'top-end';
113
- }
114
- }
115
- else {
116
- if (outsideLeft && outsideRight) {
117
- correctedPlacement = 'bottom';
118
- }
119
- else if (outsideLeft) {
120
- correctedPlacement = 'bottom-start';
121
- }
122
- else if (outsideRight) {
123
- correctedPlacement = 'bottom-end';
124
- }
125
- }
126
- }
127
- else if (placement.startsWith('top')) {
128
- if (outsideTop) {
129
- correctedPlacement = placement === null || placement === void 0 ? void 0 : placement.replace('top', 'bottom');
130
- if (outsideLeft && outsideRight) {
131
- correctedPlacement = 'bottom';
132
- }
133
- else if (outsideLeft) {
134
- correctedPlacement = 'bottom-start';
135
- }
136
- else if (outsideRight) {
137
- correctedPlacement = 'bottom-end';
138
- }
139
- }
140
- else {
141
- if (outsideLeft && outsideRight) {
142
- correctedPlacement = 'top';
143
- }
144
- else if (outsideLeft) {
145
- correctedPlacement = 'top-start';
146
- }
147
- else if (outsideRight) {
148
- correctedPlacement = 'top-end';
149
- }
150
- }
151
- }
152
- else if (placement.startsWith('left')) {
153
- if (outsideLeft) {
154
- correctedPlacement = placement === null || placement === void 0 ? void 0 : placement.replace('left', 'right');
155
- if (outsideBottom && outsideTop) {
156
- correctedPlacement = 'right';
157
- }
158
- else if (outsideBottom) {
159
- correctedPlacement = 'right-end';
160
- }
161
- else if (outsideTop) {
162
- correctedPlacement = 'right-start';
163
- }
164
- }
165
- else {
166
- if (outsideBottom && outsideTop) {
167
- correctedPlacement = 'left';
168
- }
169
- else if (outsideBottom) {
170
- correctedPlacement = 'left-end';
171
- }
172
- else if (outsideTop) {
173
- correctedPlacement = 'left-start';
174
- }
175
- }
176
- }
177
- else if (correctedPlacement.startsWith('right')) {
178
- if (outsideRight) {
179
- correctedPlacement = placement === null || placement === void 0 ? void 0 : placement.replace('right', 'left');
180
- if (outsideBottom && outsideTop) {
181
- correctedPlacement = 'left';
182
- }
183
- else if (outsideBottom) {
184
- correctedPlacement = 'left-end';
185
- }
186
- else if (outsideTop) {
187
- correctedPlacement = 'left-start';
188
- }
189
- }
190
- else {
191
- if (outsideBottom && outsideTop) {
192
- correctedPlacement = 'right';
193
- }
194
- else if (outsideBottom) {
195
- correctedPlacement = 'right-end';
196
- }
197
- else if (outsideTop) {
198
- correctedPlacement = 'right-start';
199
- }
200
- }
201
- }
202
- return {
203
- top,
204
- bottom,
205
- right,
206
- height,
207
- width,
208
- left,
209
- childHeight: childRect.height,
210
- childWidth: childRect.width,
211
- correctedPlacement,
212
- innerWidth,
213
- innerHeight
214
- };
215
- };
216
- export const handleFixedPopover = (element, parent, placement) => {
217
- var _a;
218
- const distance = (_a = getComputedStyle(element).getPropertyValue('--db-popover-distance')) !== null && _a !== void 0 ? _a : '0px';
219
- const { top, height, width, childHeight, childWidth, right, left, bottom, correctedPlacement, innerWidth, innerHeight } = getFloatingProps(element, parent, placement);
220
- // Tooltip arrow position
221
- if (childWidth > width && (correctedPlacement.startsWith('bottom') || correctedPlacement.startsWith('top'))) {
222
- const diff = width / 2 / childWidth * 100;
223
- if (correctedPlacement.endsWith('start')) {
224
- element.style.setProperty('--db-tooltip-arrow-inline-start', `${diff}%`);
225
- }
226
- else if (correctedPlacement.endsWith('end')) {
227
- element.style.setProperty('--db-tooltip-arrow-inline-start', `${100 - diff}%`);
228
- }
229
- }
230
- if (childHeight > height && (correctedPlacement.startsWith('left') || correctedPlacement.startsWith('bottom'))) {
231
- const diff = height / 2 / childHeight * 100;
232
- if (correctedPlacement.endsWith('start')) {
233
- element.style.setProperty('--db-tooltip-arrow-block-start', `${diff}%`);
234
- }
235
- else if (correctedPlacement.endsWith('end')) {
236
- element.style.setProperty('--db-tooltip-arrow-block-start', `${100 - diff}%`);
237
- }
238
- }
239
- // Popover position
240
- if (correctedPlacement === 'right' || correctedPlacement === 'left') {
241
- // center horizontally
242
- element.style.insetBlockStart = `${top + height / 2}px`;
243
- }
244
- else if (correctedPlacement === 'right-start' || correctedPlacement === 'left-start') {
245
- const end = top + childHeight;
246
- element.style.insetBlockStart = `${top}px`;
247
- element.style.insetBlockEnd = `${end > innerHeight ? innerHeight : end}px`;
248
- }
249
- else if (correctedPlacement === 'right-end' || correctedPlacement === 'left-end') {
250
- const start = bottom - childHeight;
251
- element.style.insetBlockStart = `${start < 0 ? 0 : start}px`;
252
- element.style.insetBlockEnd = `${bottom}px`;
253
- }
254
- else if (correctedPlacement === 'top' || correctedPlacement === 'bottom') {
255
- // center vertically
256
- element.style.insetInlineStart = `${left + width / 2}px`;
257
- }
258
- else if (correctedPlacement === 'top-start' || correctedPlacement === 'bottom-start') {
259
- const end = left + childWidth;
260
- element.style.insetInlineStart = `${left}px`;
261
- element.style.insetInlineEnd = `${end > innerWidth ? innerWidth : end}px`;
262
- }
263
- else if (correctedPlacement === 'top-end' || correctedPlacement === 'bottom-end') {
264
- const start = left - childWidth;
265
- element.style.insetInlineStart = `${right - childWidth}px`;
266
- element.style.insetInlineEnd = `${start < 0 ? 0 : start}px`;
267
- }
268
- if (correctedPlacement === null || correctedPlacement === void 0 ? void 0 : correctedPlacement.startsWith('right')) {
269
- const end = right + childWidth;
270
- element.style.insetInlineStart = `calc(${right}px + ${distance})`;
271
- element.style.insetInlineEnd = `calc(${end > innerWidth ? innerWidth : end}px + ${distance})`;
272
- }
273
- else if (correctedPlacement === null || correctedPlacement === void 0 ? void 0 : correctedPlacement.startsWith('left')) {
274
- const start = left - childWidth;
275
- element.style.insetInlineStart = `calc(${start < 0 ? 0 : start}px - ${distance})`;
276
- element.style.insetInlineEnd = `calc(${right}px - ${distance})`;
277
- }
278
- else if (correctedPlacement === null || correctedPlacement === void 0 ? void 0 : correctedPlacement.startsWith('top')) {
279
- const start = top - childHeight;
280
- element.style.insetBlockStart = `calc(${start < 0 ? 0 : start}px - ${distance})`;
281
- element.style.insetBlockEnd = `calc(${bottom}px - ${distance})`;
282
- }
283
- else if (correctedPlacement === null || correctedPlacement === void 0 ? void 0 : correctedPlacement.startsWith('bottom')) {
284
- const end = bottom + childHeight;
285
- element.style.insetBlockStart = `calc(${bottom}px + ${distance})`;
286
- element.style.insetBlockEnd = `calc(${end > innerHeight ? innerHeight : end}px + ${distance})`;
287
- }
288
- element.style.position = 'fixed';
289
- element.setAttribute('data-corrected-placement', correctedPlacement);
290
- };