@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.
- package/dist/components/custom-select/custom-select.js +44 -33
- package/dist/components/custom-select/model.d.ts +32 -7
- package/dist/components/popover/model.d.ts +1 -0
- package/dist/components/popover/popover.js +9 -60
- package/dist/components/tag/tag.js +3 -1
- package/dist/components/tooltip/model.d.ts +14 -5
- package/dist/components/tooltip/model.js +1 -1
- package/dist/components/tooltip/tooltip.d.ts +1 -1
- package/dist/components/tooltip/tooltip.js +17 -61
- package/dist/shared/model.d.ts +3 -11
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.js +70 -0
- package/dist/utils/navigation.d.ts +1 -1
- package/dist/utils/navigation.js +1 -1
- package/package.json +3 -3
- package/dist/utils/document-scroll-listener.d.ts +0 -9
- package/dist/utils/document-scroll-listener.js +0 -38
- package/dist/utils/floating-components.d.ts +0 -7
- package/dist/utils/floating-components.js +0 -290
|
@@ -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 [
|
|
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
|
-
|
|
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
|
-
|
|
379
|
-
|
|
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
|
-
}, [
|
|
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,
|
|
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
|
-
*
|
|
55
|
+
* Informs the user when a search was performed.
|
|
58
56
|
*/
|
|
59
|
-
|
|
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 &
|
|
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,
|
|
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
|
-
|
|
29
|
-
|
|
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
|
|
52
|
-
const parent = element
|
|
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
|
-
|
|
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)
|
|
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,
|
|
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
|
-
|
|
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" | "
|
|
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,
|
|
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
|
|
20
|
-
if (
|
|
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
|
-
|
|
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", "
|
|
71
|
-
parent.addEventListener(event, () =>
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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]);
|
package/dist/shared/model.d.ts
CHANGED
|
@@ -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;
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -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>;
|
package/dist/utils/index.js
CHANGED
|
@@ -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:
|
|
18
|
+
constructor(element: HTMLElement | null, subNavigation: Element | null);
|
|
19
19
|
private init;
|
|
20
20
|
enableFollow(): void;
|
|
21
21
|
disableFollow(): void;
|
package/dist/utils/navigation.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@db-ux/react-core-components",
|
|
3
|
-
"version": "2.0
|
|
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
|
|
42
|
-
"@db-ux/core-foundations": "2.0
|
|
41
|
+
"@db-ux/core-components": "2.1.0",
|
|
42
|
+
"@db-ux/core-foundations": "2.1.0"
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -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
|
-
};
|