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

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.
@@ -6,8 +6,8 @@ import { cls, getBoolean, getBooleanAsString, getHideProp } from "../../utils";
6
6
  function DBButtonFn(props, component) {
7
7
  const _ref = component || useRef(component);
8
8
  function handleClick(event) {
9
- event.stopPropagation();
10
9
  if (props.onClick) {
10
+ event.stopPropagation();
11
11
  props.onClick(event);
12
12
  }
13
13
  }
@@ -6,8 +6,8 @@ import { cls } from "../../utils";
6
6
  function DBCardFn(props, component) {
7
7
  const _ref = component || useRef(component);
8
8
  function handleClick(event) {
9
- event.stopPropagation();
10
9
  if (props.onClick) {
10
+ event.stopPropagation();
11
11
  props.onClick(event);
12
12
  }
13
13
  }
@@ -6,6 +6,7 @@ import { DEFAULT_INVALID_MESSAGE, DEFAULT_INVALID_MESSAGE_ID_SUFFIX, DEFAULT_MES
6
6
  import DBInfotext from "../infotext/infotext";
7
7
  import { cls, delay, getBoolean, getHideProp, hasVoiceOver, stringPropVisible, uuid, } from "../../utils";
8
8
  function DBCheckboxFn(props, component) {
9
+ var _a;
9
10
  const _ref = component || useRef(component);
10
11
  const [initialized, setInitialized] = useState(() => false);
11
12
  const [_id, set_id] = useState(() => undefined);
@@ -114,7 +115,7 @@ function DBCheckboxFn(props, component) {
114
115
  }, [initialized, _ref.current, props.checked]);
115
116
  return (React.createElement("div", Object.assign({}, 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-checkbox", props.className), "data-size": props.size, "data-hide-label": getHideProp(props.showLabel) }),
116
117
  React.createElement("label", { htmlFor: _id },
117
- React.createElement("input", Object.assign({ type: "checkbox", "aria-invalid": props.validation === "invalid", "data-custom-validity": props.validation, 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: _id, name: props.name, checked: getBoolean(props.checked, "checked"), disabled: getBoolean(props.disabled, "disabled"), value: props.value, required: getBoolean(props.required, "required"), onChange: (event) => handleChange(event), onBlur: (event) => handleBlur(event), onFocus: (event) => handleFocus(event), "aria-describedby": _descByIds })),
118
+ React.createElement("input", Object.assign({ type: "checkbox", "aria-invalid": props.validation === "invalid", "data-custom-validity": props.validation, 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: _id, name: props.name, checked: getBoolean(props.checked, "checked"), disabled: getBoolean(props.disabled, "disabled"), value: props.value, required: getBoolean(props.required, "required"), onChange: (event) => handleChange(event), onBlur: (event) => handleBlur(event), onFocus: (event) => handleFocus(event), "aria-describedby": (_a = props.ariaDescribedBy) !== null && _a !== void 0 ? _a : _descByIds })),
118
119
  props.label ? React.createElement(React.Fragment, null, props.label) : React.createElement(React.Fragment, null, props.children)),
119
120
  stringPropVisible(props.message, props.showMessage) ? (React.createElement(DBInfotext, { size: "small", icon: props.messageIcon, id: _messageId }, props.message)) : null,
120
121
  hasValidState() ? (React.createElement(DBInfotext, { size: "small", semantic: "successful", id: _validMessageId }, props.validMessage || DEFAULT_VALID_MESSAGE)) : null,
@@ -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, handleDataOutside, hasVoiceOver, stringPropVisible, uuid, } from "../../utils";
5
+ import { cls, delay, getBoolean, getBooleanAsString, getHideProp, getSearchInput, 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,10 @@ 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";
16
18
  function DBCustomSelectFn(props, component) {
17
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
19
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
18
20
  props = Object.assign({ clearSelectionText: "Clear selection", showClearSelection: true }, props);
19
21
  const _ref = component || useRef(component);
20
22
  const detailsRef = useRef(null);
@@ -45,6 +47,15 @@ function DBCustomSelectFn(props, component) {
45
47
  const [_hasNoOptions, set_hasNoOptions] = useState(() => false);
46
48
  const [_documentClickListenerCallbackId, set_documentClickListenerCallbackId,] = useState(() => undefined);
47
49
  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
59
  function hasValidState() {
49
60
  var _a;
50
61
  return !!((_a = props.validMessage) !== null && _a !== void 0 ? _a : props.validation === "valid");
@@ -87,15 +98,27 @@ function DBCustomSelectFn(props, component) {
87
98
  }
88
99
  function handleDropdownToggle(event) {
89
100
  if (props.onDropdownToggle) {
101
+ event.stopPropagation();
90
102
  props.onDropdownToggle(event);
91
103
  }
92
104
  if (event.target.open) {
93
105
  set_documentClickListenerCallbackId(new DocumentClickListener().addCallback((event) => handleDocumentClose(event)));
106
+ set_documentScrollListenerCallbackId(new DocumentScrollListener().addCallback((event) => handleDocumentScroll(event)));
107
+ handleAutoPlacement();
108
+ _observer === null || _observer === void 0 ? void 0 : _observer.observe(detailsRef.current);
109
+ if (!event.target.dataset.test) {
110
+ // We need this workaround for snapshot testing
111
+ handleOpenByKeyboardFocus();
112
+ }
94
113
  }
95
114
  else {
96
115
  if (_documentClickListenerCallbackId) {
97
116
  new DocumentClickListener().removeCallback(_documentClickListenerCallbackId);
98
117
  }
118
+ if (_documentScrollListenerCallbackId) {
119
+ new DocumentScrollListener().removeCallback(_documentScrollListenerCallbackId);
120
+ }
121
+ _observer === null || _observer === void 0 ? void 0 : _observer.unobserve(detailsRef.current);
99
122
  }
100
123
  }
101
124
  function getNativeSelectValue() {
@@ -134,9 +157,12 @@ function DBCustomSelectFn(props, component) {
134
157
  return ((_b = (_a = option.id) !== null && _a !== void 0 ? _a : option.value) !== null && _b !== void 0 ? _b : uuid()).toString();
135
158
  }
136
159
  function getTagRemoveLabel(index) {
137
- return props.removeTagsTexts && props.removeTagsTexts.length > index
138
- ? props.removeTagsTexts.at(index)
139
- : `${DEFAULT_REMOVE} ${_selectedOptions ? getOptionLabel(_selectedOptions[index]) : ""}`;
160
+ if (props.removeTagsTexts && props.removeTagsTexts.length > index) {
161
+ return props.removeTagsTexts.at(index);
162
+ }
163
+ else {
164
+ return `${DEFAULT_REMOVE} ${_selectedOptions ? getOptionLabel(_selectedOptions[index]) : ""}`;
165
+ }
140
166
  }
141
167
  function handleTagRemove(option, event) {
142
168
  event.stopPropagation();
@@ -147,17 +173,19 @@ function DBCustomSelectFn(props, component) {
147
173
  if (detailsRef.current) {
148
174
  const dropdown = detailsRef.current.querySelector("article");
149
175
  if (dropdown) {
176
+ // This is a workaround for Angular
150
177
  delay(() => {
151
- handleDataOutside(dropdown);
152
- }, 100);
178
+ var _a;
179
+ handleFixedDropdown(dropdown, detailsRef.current, (_a = props.placement) !== null && _a !== void 0 ? _a : "bottom");
180
+ }, 1);
153
181
  }
154
182
  }
155
183
  }
156
184
  function handleArrowDownUp(event) {
157
185
  var _a, _b, _c, _d, _e, _f, _g;
158
186
  if ((_a = detailsRef.current) === null || _a === void 0 ? void 0 : _a.open) {
159
- if (document) {
160
- const activeElement = document.activeElement;
187
+ if (self.document) {
188
+ const activeElement = self.document.activeElement;
161
189
  if (activeElement) {
162
190
  // 1. we check if we are currently focusing a checkbox in the dropdown
163
191
  const isCheckbox = activeElement.getAttribute("type") === "checkbox" ||
@@ -214,8 +242,6 @@ function DBCustomSelectFn(props, component) {
214
242
  }
215
243
  }
216
244
  }
217
- event.stopPropagation();
218
- event.preventDefault();
219
245
  }
220
246
  else if (event.key === "ArrowDown" || event.key === "ArrowRight") {
221
247
  // Open dropdown with arrows see https://www.w3.org/WAI/ARIA/apg/patterns/combobox/#keyboardinteraction
@@ -224,15 +250,14 @@ function DBCustomSelectFn(props, component) {
224
250
  detailsRef.current.open = true;
225
251
  }
226
252
  handleOpenByKeyboardFocus();
227
- event.stopPropagation();
228
- event.preventDefault();
229
253
  }
254
+ event.stopPropagation();
255
+ event.preventDefault();
230
256
  }
231
257
  function handleKeyboardPress(event) {
232
258
  var _a;
233
- if (event.key === "Escape" &&
234
- detailsRef.current &&
235
- ((_a = detailsRef.current) === null || _a === void 0 ? void 0 : _a.open)) {
259
+ event.stopPropagation();
260
+ if (event.key === "Escape" && ((_a = detailsRef.current) === null || _a === void 0 ? void 0 : _a.open)) {
236
261
  handleClose("close");
237
262
  handleSummaryFocus();
238
263
  }
@@ -252,25 +277,29 @@ function DBCustomSelectFn(props, component) {
252
277
  else if (detailsRef.current.open && (event === null || event === void 0 ? void 0 : event.relatedTarget)) {
253
278
  const relatedTarget = event.relatedTarget;
254
279
  if (!detailsRef.current.contains(relatedTarget)) {
255
- detailsRef.current.open = false;
280
+ // We need to use delay here because the combination of `contains`
281
+ // and changing the DOM element causes a race condition inside browser
282
+ delay(() => (detailsRef.current.open = false), 1);
256
283
  }
257
284
  }
258
285
  }
259
286
  }
260
287
  function handleDocumentClose(event) {
261
288
  var _a;
262
- // stencil is sending a custom event which wraps the pointer event into details
263
- const target = event.target;
264
- if (((_a = detailsRef.current) === null || _a === void 0 ? void 0 : _a.open) && !detailsRef.current.contains(target)) {
265
- detailsRef.current.open = false;
289
+ if (event) {
290
+ // stencil is sending a custom event which wraps the pointer event into details
291
+ const target = event.target;
292
+ if (((_a = detailsRef.current) === null || _a === void 0 ? void 0 : _a.open) && !detailsRef.current.contains(target)) {
293
+ detailsRef.current.open = false;
294
+ }
266
295
  }
267
296
  }
268
297
  function handleOptionSelected(values) {
269
298
  const skip = new Date().getTime() - _internalChangeTimestamp < 200;
270
299
  if (skip)
271
300
  return;
301
+ set_values(values);
272
302
  if (props.onOptionSelected) {
273
- set_values(values);
274
303
  props.onOptionSelected(values !== null && values !== void 0 ? values : []);
275
304
  }
276
305
  set_internalChangeTimestamp(new Date().getTime());
@@ -291,7 +320,8 @@ function DBCustomSelectFn(props, component) {
291
320
  }
292
321
  }
293
322
  }
294
- function handleSelectAll() {
323
+ function handleSelectAll(event) {
324
+ event.stopPropagation();
295
325
  if ((_values === null || _values === void 0 ? void 0 : _values.length) === amountOptions) {
296
326
  handleOptionSelected([]);
297
327
  }
@@ -323,12 +353,12 @@ function DBCustomSelectFn(props, component) {
323
353
  delay(() => {
324
354
  // Takes some time until element can be focused
325
355
  checkbox.focus();
326
- }, 100);
356
+ }, 1);
327
357
  }
328
358
  }
329
359
  }
330
360
  }
331
- function handleOpenByKeyboardFocus(onlySearch) {
361
+ function handleOpenByKeyboardFocus() {
332
362
  if (detailsRef.current) {
333
363
  // Focus search if possible
334
364
  const search = getSearchInput(detailsRef.current);
@@ -336,9 +366,9 @@ function DBCustomSelectFn(props, component) {
336
366
  delay(() => {
337
367
  // Takes some time until element can be focused
338
368
  search.focus();
339
- }, 100);
369
+ }, 1);
340
370
  }
341
- else if (!onlySearch) {
371
+ else {
342
372
  // Focus first checkbox otherwise
343
373
  handleFocusFirstDropdownCheckbox();
344
374
  }
@@ -347,25 +377,33 @@ function DBCustomSelectFn(props, component) {
347
377
  function handleSearch(event) {
348
378
  event.stopPropagation();
349
379
  const filterText = event.target.value;
350
- set_options(!props.options || !filterText || filterText.length === 0
351
- ? props.options
352
- : props.options.filter((option) => !option.isGroupTitle &&
380
+ if (!props.options || !filterText || filterText.length === 0) {
381
+ set_options(props.options);
382
+ }
383
+ else {
384
+ set_options(props.options.filter((option) => !option.isGroupTitle &&
353
385
  getOptionLabel(option)
354
386
  .toLowerCase()
355
387
  .includes(filterText.toLowerCase())));
388
+ }
356
389
  }
357
- function handleClearAll() {
390
+ function handleClearAll(event) {
391
+ event.stopPropagation();
358
392
  handleOptionSelected([]);
359
393
  handleSummaryFocus();
360
394
  }
361
395
  function handleSummaryFocus() {
362
- var _a, _b;
363
- (_b = (_a = detailsRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("summary")) === null || _b === void 0 ? void 0 : _b.focus();
396
+ var _a;
397
+ if (detailsRef.current) {
398
+ (_a = detailsRef.current
399
+ .querySelector("summary")) === null || _a === void 0 ? void 0 : _a.focus();
400
+ }
364
401
  }
365
402
  const [selectAllChecked, setSelectAllChecked] = useState(() => false);
366
403
  const [selectAllIndeterminate, setSelectAllIndeterminate] = useState(() => false);
367
- function satisfyReact() {
368
- // This is an empty function to satisfy React
404
+ function satisfyReact(event) {
405
+ // This is a function to satisfy React
406
+ event.stopPropagation();
369
407
  }
370
408
  useEffect(() => {
371
409
  var _a;
@@ -381,22 +419,17 @@ function DBCustomSelectFn(props, component) {
381
419
  set_selectedLabelsId(mId + "-selected-labels");
382
420
  set_infoTextId(mId + "-info");
383
421
  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
+ }));
384
430
  }, []);
385
431
  useEffect(() => {
386
432
  if (detailsRef.current) {
387
- const summary = detailsRef.current.querySelector("summary");
388
- if (summary) {
389
- summary.addEventListener("click", () => {
390
- handleAutoPlacement();
391
- handleOpenByKeyboardFocus(true);
392
- });
393
- summary.addEventListener("keydown", (event) => {
394
- var _a;
395
- if (event.code === "Space" && !((_a = detailsRef.current) === null || _a === void 0 ? void 0 : _a.open)) {
396
- handleOpenByKeyboardFocus();
397
- }
398
- });
399
- }
400
433
  detailsRef.current.addEventListener("focusout", (event) => handleClose(event));
401
434
  }
402
435
  }, [detailsRef.current]);
@@ -417,10 +450,11 @@ function DBCustomSelectFn(props, component) {
417
450
  }
418
451
  }, [_id]);
419
452
  useEffect(() => {
453
+ var _a;
420
454
  if (detailsRef.current) {
421
455
  const summary = detailsRef.current.querySelector("summary");
422
456
  if (summary) {
423
- summary.setAttribute("aria-describedby", _descByIds || "");
457
+ summary.setAttribute("aria-describedby", (_a = props.ariaDescribedBy) !== null && _a !== void 0 ? _a : (_descByIds || ""));
424
458
  }
425
459
  }
426
460
  }, [detailsRef.current, _descByIds]);
@@ -433,10 +467,12 @@ function DBCustomSelectFn(props, component) {
433
467
  }
434
468
  }, [props.showNoResults, _options]);
435
469
  useEffect(() => {
436
- setSelectAllEnabled(Boolean(props.multiple && (props.showSelectAll || amountOptions > 5)));
470
+ var _a;
471
+ setSelectAllEnabled(Boolean(props.multiple && ((_a = props.showSelectAll) !== null && _a !== void 0 ? _a : amountOptions > 5)));
437
472
  }, [props.showSelectAll, amountOptions, props.multiple]);
438
473
  useEffect(() => {
439
- setSearchEnabled(props.showSearch || amountOptions > 9);
474
+ var _a;
475
+ setSearchEnabled((_a = props.showSearch) !== null && _a !== void 0 ? _a : amountOptions > 9);
440
476
  }, [props.showSearch, amountOptions]);
441
477
  useEffect(() => {
442
478
  var _a;
@@ -471,15 +507,16 @@ function DBCustomSelectFn(props, component) {
471
507
  setAmountOptions((_b = (_a = props.options) === null || _a === void 0 ? void 0 : _a.filter((option) => !option.isGroupTitle).length) !== null && _b !== void 0 ? _b : 0);
472
508
  }, [props.options]);
473
509
  useEffect(() => {
474
- if (_options === null || _options === void 0 ? void 0 : _options.length) {
475
- set_selectedOptions(_options === null || _options === void 0 ? void 0 : _options.filter((option) => {
510
+ var _a, _b;
511
+ if ((_a = props.options) === null || _a === void 0 ? void 0 : _a.length) {
512
+ set_selectedOptions((_b = props.options) === null || _b === void 0 ? void 0 : _b.filter((option) => {
476
513
  if (!option.value || !(_values === null || _values === void 0 ? void 0 : _values["includes"])) {
477
514
  return false;
478
515
  }
479
516
  return !option.isGroupTitle && (_values === null || _values === void 0 ? void 0 : _values.includes(option.value));
480
517
  }));
481
518
  }
482
- }, [_options, _values]);
519
+ }, [props.options, _values]);
483
520
  useEffect(() => {
484
521
  if (_selectedOptions === null || _selectedOptions === void 0 ? void 0 : _selectedOptions.length) {
485
522
  if (props.selectedType === "amount") {
@@ -518,7 +555,7 @@ function DBCustomSelectFn(props, component) {
518
555
  ? "above"
519
556
  : props.variant, "data-required": getBooleanAsString(props.required), "data-placement": props.placement, "data-selected-type": props.multiple ? props.selectedType : "text", "data-hide-label": getHideProp(props.showLabel), "data-icon": props.icon, "data-hide-icon": getHideProp(props.showIcon) }),
520
557
  React.createElement("label", { id: _labelId }, (_a = props.label) !== null && _a !== void 0 ? _a : DEFAULT_LABEL,
521
- React.createElement("select", { role: "none", hidden: true, id: _selectId, tabIndex: -1, ref: selectRef, form: props.form, name: props.name, multiple: getBoolean(props.multiple, "multiple"), disabled: getBoolean(props.disabled, "disabled"), required: getBoolean(props.required, "required"), onChange: (event) => satisfyReact() }, (_options === null || _options === void 0 ? void 0 : _options.length) ? (React.createElement(React.Fragment, null, _options === null || _options === void 0 ? void 0 : _options.map((option) => (React.createElement("option", { disabled: option.disabled, value: option.value, key: "native-select-option-" + getOptionKey(option) }, getOptionLabel(option)))))) : null)),
558
+ React.createElement("select", { role: "none", hidden: true, id: _selectId, tabIndex: -1, ref: selectRef, form: props.form, name: props.name, multiple: getBoolean(props.multiple, "multiple"), disabled: getBoolean(props.disabled, "disabled"), required: getBoolean(props.required, "required"), onChange: (event) => satisfyReact(event) }, ((_b = props.options) === null || _b === void 0 ? void 0 : _b.length) ? (React.createElement(React.Fragment, null, (_c = props.options) === null || _c === void 0 ? void 0 : _c.map((option) => (React.createElement("option", { disabled: option.disabled, value: option.value, key: "native-select-option-" + getOptionKey(option) }, getOptionLabel(option)))))) : null)),
522
559
  React.createElement("details", { ref: detailsRef, open: props.open, onToggle: (event) => handleDropdownToggle(event), onKeyDown: (event) => handleKeyboardPress(event) },
523
560
  props.children,
524
561
  props.options ? (React.createElement(React.Fragment, null,
@@ -527,23 +564,23 @@ function DBCustomSelectFn(props, component) {
527
564
  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),
528
565
  React.createElement(DBCustomSelectDropdown, { width: props.dropdownWidth },
529
566
  searchEnabled ? (React.createElement("div", null,
530
- React.createElement(DBInput, { type: "search", ref: searchInputRef, name: _id, form: _id, showLabel: false, label: (_b = props.searchLabel) !== null && _b !== void 0 ? _b : DEFAULT_LABEL, placeholder: (_c = props.searchPlaceholder) !== null && _c !== void 0 ? _c : props.searchLabel, ariaDescribedBy: _hasNoOptions || props.showLoading
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
531
568
  ? _infoTextId
532
569
  : undefined, onInput: (event) => handleSearch(event) }))) : null,
533
- _hasNoOptions || props.showLoading ? (React.createElement(DBInfotext, { id: _infoTextId, icon: _hasNoOptions ? undefined : "circular_arrows", semantic: _hasNoOptions ? "warning" : "informational" }, (_d = (_hasNoOptions ? props.noResultsText : props.loadingText)) !== null && _d !== void 0 ? _d : DEFAULT_MESSAGE)) : (React.createElement(React.Fragment, null,
570
+ _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,
534
571
  React.createElement(React.Fragment, null,
535
572
  selectAllEnabled ? (React.createElement("div", null,
536
573
  React.createElement("div", { className: "db-checkbox db-custom-select-list-item" },
537
574
  React.createElement("label", null,
538
- React.createElement("input", { type: "checkbox", value: "select-all", ref: selectAllRef, form: _id, checked: selectAllChecked, onChange: (event) => handleSelectAll() }),
575
+ React.createElement("input", { type: "checkbox", value: "select-all", ref: selectAllRef, form: _id, checked: selectAllChecked, onChange: (event) => handleSelectAll(event) }),
539
576
  getSelectAllLabel())))) : null,
540
- React.createElement(DBCustomSelectList, { multiple: getBoolean(props.multiple, "multiple"), label: (_f = (_e = props.ariaListLabel) !== null && _e !== void 0 ? _e : props.label) !== null && _f !== void 0 ? _f : DEFAULT_LABEL }, _options === null || _options === void 0 ? void 0 : _options.map((option) => (React.createElement(DBCustomSelectListItem, { type: props.multiple ? "checkbox" : "radio", showDivider: option.showDivider, icon: option.icon, isGroupTitle: option.isGroupTitle, groupTitle: getOptionLabel(option), name: _id, checked: getOptionChecked(option.value), disabled: option.disabled, value: option.value, onChange: (event) => handleSelect(option.value), key: "custom-select-list-item-" + getOptionKey(option) }, !option.isGroupTitle ? (React.createElement(React.Fragment, null, getOptionLabel(option))) : null))))))),
577
+ React.createElement(DBCustomSelectList, { multiple: getBoolean(props.multiple, "multiple"), label: (_h = (_g = props.ariaListLabel) !== null && _g !== void 0 ? _g : props.label) !== null && _h !== void 0 ? _h : DEFAULT_LABEL }, _options === null || _options === void 0 ? void 0 : _options.map((option) => (React.createElement(DBCustomSelectListItem, { type: props.multiple ? "checkbox" : "radio", showDivider: option.showDivider, icon: option.icon, isGroupTitle: option.isGroupTitle, groupTitle: getOptionLabel(option), name: _id, checked: getOptionChecked(option.value), disabled: option.disabled, value: option.value, onChange: (event) => handleSelect(option.value), key: "custom-select-list-item-" + getOptionKey(option) }, !option.isGroupTitle ? (React.createElement(React.Fragment, null, getOptionLabel(option))) : null))))))),
541
578
  React.createElement("div", null,
542
- React.createElement(DBButton, { variant: "ghost", width: "full", icon: "cross", size: "small", name: _id, form: _id, onClick: (event) => handleClose("close") }, (_g = props.mobileCloseButtonText) !== null && _g !== void 0 ? _g : DEFAULT_CLOSE_BUTTON))))) : null),
543
- ((_h = props.showClearSelection) !== null && _h !== void 0 ? _h : true) && (_values === null || _values === void 0 ? void 0 : _values.length) ? (React.createElement(DBButton, { icon: "cross", variant: "ghost", size: "small", noText: true, name: _id, form: _id, onClick: (event) => handleClearAll() },
579
+ React.createElement(DBButton, { variant: "ghost", width: "full", icon: "cross", size: "small", name: _id, form: _id, onClick: (event) => handleClose("close") }, (_j = props.mobileCloseButtonText) !== null && _j !== void 0 ? _j : DEFAULT_CLOSE_BUTTON))))) : null),
580
+ ((_k = props.showClearSelection) !== null && _k !== void 0 ? _k : true) && (_values === null || _values === void 0 ? void 0 : _values.length) ? (React.createElement(DBButton, { icon: "cross", variant: "ghost", size: "small", noText: true, name: _id, form: _id, onClick: (event) => handleClearAll(event) },
544
581
  props.clearSelectionText,
545
582
  React.createElement(DBTooltip, { placement: "top" }, props.clearSelectionText))) : null,
546
- React.createElement("span", { "aria-hidden": getBooleanAsString(true), id: _placeholderId }, (_j = props.placeholder) !== null && _j !== void 0 ? _j : props.label),
583
+ React.createElement("span", { "aria-hidden": getBooleanAsString(true), id: _placeholderId }, (_l = props.placeholder) !== null && _l !== void 0 ? _l : props.label),
547
584
  stringPropVisible(props.message, props.showMessage) ? (React.createElement(DBInfotext, { size: "small", icon: props.messageIcon, id: _messageId }, props.message)) : null,
548
585
  hasValidState() ? (React.createElement(DBInfotext, { size: "small", semantic: "successful", id: _validMessageId }, props.validMessage || DEFAULT_VALID_MESSAGE)) : null,
549
586
  React.createElement(DBInfotext, { size: "small", semantic: "critical", id: _invalidMessageId }, _invalidMessage),
@@ -1,4 +1,4 @@
1
- import { BaseFormProps, CloseEventState, CustomFormProps, FormMessageProps, FormState, FromValidState, GlobalProps, GlobalState, IconProps, PlacementVerticalType, PopoverState, RequiredProps, ShowIconProps, ShowLabelProps, ValidationType, WidthType } from '../../shared/model';
1
+ import { BaseFormProps, CloseEventState, CustomFormProps, DocumentScrollState, FormMessageProps, FormState, FromValidState, GlobalProps, GlobalState, IconProps, PlacementVerticalType, 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';
@@ -176,11 +176,11 @@ export type DBCustomSelectDefaultState = {
176
176
  handleTagRemove: (option: CustomSelectOptionType, event?: any) => void;
177
177
  handleSummaryFocus: () => void;
178
178
  handleSelect: (value?: string) => void;
179
- handleSelectAll: () => void;
180
- handleClearAll: () => void;
179
+ handleSelectAll: (event: any) => void;
180
+ handleClearAll: (event: any) => void;
181
181
  handleDropdownToggle: (event: any) => void;
182
182
  handleDocumentClose: (event: any) => void;
183
- handleOpenByKeyboardFocus: (onlySearch?: boolean) => void;
183
+ handleOpenByKeyboardFocus: () => void;
184
184
  handleFocusFirstDropdownCheckbox: (activeElement?: Element) => void;
185
185
  handleKeyboardPress: (event: any) => void;
186
186
  handleArrowDownUp: (event: any) => void;
@@ -189,5 +189,6 @@ export type DBCustomSelectDefaultState = {
189
189
  getSelectAllLabel: () => string;
190
190
  selectAllChecked: boolean;
191
191
  selectAllIndeterminate: boolean;
192
+ handleAutoPlacement: () => void;
192
193
  };
193
- export type DBCustomSelectState = DBCustomSelectDefaultState & GlobalState & FormState & FromValidState & CloseEventState & PopoverState;
194
+ export type DBCustomSelectState = DBCustomSelectDefaultState & GlobalState & FormState & FromValidState & CloseEventState & DocumentScrollState;
@@ -1,10 +1,8 @@
1
1
  import { GlobalProps, GlobalState, IconProps, TextProps } from '../../shared/model';
2
- export declare const IconVariantList: readonly ["default", "inverted", "filled"];
3
- export type IconVariantType = (typeof IconVariantList)[number];
4
2
  export declare const IconWeightList: readonly ["16", "20", "24", "32", "48", "64"];
5
3
  export type IconWeightType = (typeof IconWeightList)[number];
6
4
  export type DBIconDefaultProps = {
7
- variant?: IconVariantType;
5
+ variant?: string;
8
6
  weight?: IconWeightType;
9
7
  };
10
8
  export type DBIconProps = DBIconDefaultProps & GlobalProps & IconProps & TextProps;
@@ -1,2 +1 @@
1
- export const IconVariantList = ['default', 'inverted', 'filled'];
2
1
  export const IconWeightList = ['16', '20', '24', '32', '48', '64'];
@@ -7,8 +7,8 @@ function DBLinkFn(props, component) {
7
7
  var _a;
8
8
  const _ref = component || useRef(component);
9
9
  function handleClick(event) {
10
- event.stopPropagation();
11
10
  if (props.onClick) {
11
+ event.stopPropagation();
12
12
  props.onClick(event);
13
13
  }
14
14
  }
@@ -25,8 +25,8 @@ function DBNavigationItemFn(props, component) {
25
25
  }
26
26
  }
27
27
  function handleClick(event) {
28
- event.stopPropagation();
29
28
  if (props.onClick) {
29
+ event.stopPropagation();
30
30
  props.onClick(event);
31
31
  }
32
32
  if (hasAreaPopup) {
@@ -13,6 +13,5 @@ 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;
17
16
  };
18
17
  export type DBPopoverState = DBPopoverDefaultState & GlobalState & PopoverState & InitializedState;
@@ -2,29 +2,66 @@
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, getBooleanAsString, handleDataOutside } from "../../utils";
5
+ import { cls, delay as utilsDelay, getBooleanAsString } from "../../utils";
6
+ import { handleFixedPopover } from "../../utils/floating-components";
7
+ import { DocumentScrollListener } from "../../utils/document-scroll-listener";
6
8
  function DBPopoverFn(props, component) {
7
9
  var _a;
8
10
  const _ref = component || useRef(component);
9
11
  const [initialized, setInitialized] = useState(() => false);
10
12
  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
+ }
11
23
  function handleAutoPlacement() {
12
- setIsExpanded(true);
13
24
  if (!_ref.current)
14
25
  return;
15
26
  const article = _ref.current.querySelector("article");
16
- if (!article)
17
- return;
18
- handleDataOutside(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
+ }
19
49
  }
20
50
  function handleLeave(event) {
21
- const element = event.target;
22
- const parent = element.parentNode;
51
+ const element = event === null || event === void 0 ? void 0 : event.target;
52
+ const parent = element === null || element === void 0 ? void 0 : element.parentNode;
23
53
  if (!parent ||
24
54
  (element.parentNode.querySelector(":focus") !== element &&
25
55
  element.parentNode.querySelector(":focus-within") !== element &&
26
56
  element.parentNode.querySelector(":hover") !== element)) {
27
57
  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
+ }
28
65
  }
29
66
  }
30
67
  function getTrigger() {
@@ -51,11 +88,25 @@ function DBPopoverFn(props, component) {
51
88
  }, []);
52
89
  useEffect(() => {
53
90
  if (_ref.current && initialized) {
91
+ setInitialized(false);
54
92
  const child = getTrigger();
55
93
  if (child) {
56
94
  child.ariaHasPopup = "true";
57
95
  }
58
- setInitialized(false);
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
+ }));
59
110
  }
60
111
  }, [_ref.current, initialized]);
61
112
  useEffect(() => {
@@ -66,7 +117,7 @@ function DBPopoverFn(props, component) {
66
117
  }
67
118
  }
68
119
  }, [_ref.current, isExpanded]);
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) }),
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) }),
70
121
  React.createElement(React.Fragment, null, props.trigger),
71
122
  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)));
72
123
  }
@@ -1,7 +1,5 @@
1
1
  import { ChangeEventProps, ChangeEventState, FocusEventProps, FocusEventState, FormCheckProps, FormProps, FormState, GlobalProps, GlobalState, InitializedState, SizeProps } from '../../shared/model';
2
- export type DBRadioDefaultProps = {
3
- describedbyid?: string;
4
- };
2
+ export type DBRadioDefaultProps = {};
5
3
  export type DBRadioProps = DBRadioDefaultProps & GlobalProps & ChangeEventProps<HTMLInputElement> & FocusEventProps<HTMLInputElement> & FormProps & FormCheckProps & SizeProps;
6
4
  export type DBRadioDefaultState = {};
7
5
  export type DBRadioState = DBRadioDefaultState & GlobalState & ChangeEventState<HTMLInputElement> & FocusEventState<HTMLInputElement> & FormState & InitializedState;
@@ -1,3 +1,3 @@
1
1
  import * as React from "react";
2
- declare const DBRadio: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<any>, "required" | "value" | "size" | "checked" | keyof import("../../shared/model").GlobalProps | keyof import("../../shared/model").ChangeEventProps<HTMLInputElement> | keyof import("../../shared/model").FocusEventProps<HTMLInputElement> | keyof import("../../shared/model").CustomFormProps | keyof import("../../shared/model").BaseFormProps | "showLabel"> & import("./model").DBRadioDefaultProps & import("../../shared/model").GlobalProps & import("../../shared/model").ChangeEventProps<HTMLInputElement> & import("../../shared/model").FocusEventProps<HTMLInputElement> & import("../../shared/model").CustomFormProps & import("../../shared/model").BaseFormProps & import("../../shared/model").RequiredProps & import("../../shared/model").ShowLabelProps & import("../../shared/model").ValueProps & import("../../shared/model").FormCheckProps & import("../../shared/model").SizeProps & React.RefAttributes<any>>;
2
+ declare const DBRadio: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<any>, "required" | "value" | "size" | "checked" | keyof import("../../shared/model").GlobalProps | keyof import("../../shared/model").ChangeEventProps<HTMLInputElement> | keyof import("../../shared/model").FocusEventProps<HTMLInputElement> | keyof import("../../shared/model").CustomFormProps | keyof import("../../shared/model").BaseFormProps | "showLabel"> & import("../../shared/model").GlobalProps & import("../../shared/model").ChangeEventProps<HTMLInputElement> & import("../../shared/model").FocusEventProps<HTMLInputElement> & import("../../shared/model").CustomFormProps & import("../../shared/model").BaseFormProps & import("../../shared/model").RequiredProps & import("../../shared/model").ShowLabelProps & import("../../shared/model").ValueProps & import("../../shared/model").FormCheckProps & import("../../shared/model").SizeProps & React.RefAttributes<any>>;
3
3
  export default DBRadio;