@ultraviolet/ui 3.0.0-beta.18 → 3.0.0-beta.19

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.
Files changed (64) hide show
  1. package/dist/components/Checkbox/index.cjs +1 -1
  2. package/dist/components/Checkbox/index.js +1 -1
  3. package/dist/components/Chip/index.d.ts +1 -1
  4. package/dist/components/EmptyState/index.cjs +11 -74
  5. package/dist/components/EmptyState/index.d.ts +1 -6
  6. package/dist/components/EmptyState/index.js +8 -69
  7. package/dist/components/EmptyState/styles.css.cjs +10 -0
  8. package/dist/components/EmptyState/styles.css.d.ts +29 -0
  9. package/dist/components/EmptyState/styles.css.js +10 -0
  10. package/dist/components/Popup/helpers.cjs +8 -4
  11. package/dist/components/Popup/helpers.js +8 -4
  12. package/dist/components/ProgressBar/styles.css.cjs +1 -0
  13. package/dist/components/ProgressBar/styles.css.js +1 -0
  14. package/dist/components/ProgressBar/variables.css.cjs +0 -1
  15. package/dist/components/ProgressBar/variables.css.js +0 -1
  16. package/dist/components/SearchInput/KeyGroup.cjs +4 -22
  17. package/dist/components/SearchInput/KeyGroup.js +2 -18
  18. package/dist/components/SearchInput/index.cjs +2 -39
  19. package/dist/components/SearchInput/index.js +2 -37
  20. package/dist/components/SearchInput/styles.css.cjs +10 -0
  21. package/dist/components/SearchInput/styles.css.d.ts +3 -0
  22. package/dist/components/SearchInput/styles.css.js +10 -0
  23. package/dist/components/SelectInput/components/Dropdown.cjs +342 -0
  24. package/dist/components/SelectInput/{Dropdown.d.ts → components/Dropdown.d.ts} +1 -1
  25. package/dist/components/SelectInput/components/Dropdown.js +342 -0
  26. package/dist/components/SelectInput/components/DropdownOption.cjs +47 -0
  27. package/dist/components/SelectInput/{DropdownOption.d.ts → components/DropdownOption.d.ts} +1 -1
  28. package/dist/components/SelectInput/components/DropdownOption.js +47 -0
  29. package/dist/components/SelectInput/components/SearchBarDropdown.cjs +117 -0
  30. package/dist/components/SelectInput/{SearchBarDropdown.d.ts → components/SearchBarDropdown.d.ts} +1 -1
  31. package/dist/components/SelectInput/components/SearchBarDropdown.js +117 -0
  32. package/dist/components/SelectInput/components/SelectBar.cjs +237 -0
  33. package/dist/components/SelectInput/{SelectBar.d.ts → components/SelectBar.d.ts} +1 -12
  34. package/dist/components/SelectInput/components/SelectBar.js +237 -0
  35. package/dist/components/SelectInput/components/dropdown.css.cjs +34 -0
  36. package/dist/components/SelectInput/components/dropdown.css.d.ts +35 -0
  37. package/dist/components/SelectInput/components/dropdown.css.js +34 -0
  38. package/dist/components/SelectInput/components/selectBar.css.cjs +24 -0
  39. package/dist/components/SelectInput/components/selectBar.css.d.ts +108 -0
  40. package/dist/components/SelectInput/components/selectBar.css.js +24 -0
  41. package/dist/components/SelectInput/index.cjs +6 -32
  42. package/dist/components/SelectInput/index.js +6 -30
  43. package/dist/components/SelectInput/styles.css.cjs +7 -0
  44. package/dist/components/SelectInput/styles.css.d.ts +2 -0
  45. package/dist/components/SelectInput/styles.css.js +7 -0
  46. package/dist/components/SelectableCardOptionGroup/components/Option.cjs +7 -7
  47. package/dist/components/SelectableCardOptionGroup/components/Option.js +7 -7
  48. package/dist/components/Tag/index.cjs +2 -1
  49. package/dist/components/Tag/index.d.ts +3 -2
  50. package/dist/components/Tag/index.js +2 -1
  51. package/dist/components/Text/style.css.cjs +1 -0
  52. package/dist/components/Text/style.css.js +1 -0
  53. package/dist/components/Text/variables.css.cjs +0 -1
  54. package/dist/components/Text/variables.css.js +0 -1
  55. package/dist/ui.css +1 -1
  56. package/package.json +3 -3
  57. package/dist/components/SelectInput/Dropdown.cjs +0 -488
  58. package/dist/components/SelectInput/Dropdown.js +0 -486
  59. package/dist/components/SelectInput/DropdownOption.cjs +0 -91
  60. package/dist/components/SelectInput/DropdownOption.js +0 -89
  61. package/dist/components/SelectInput/SearchBarDropdown.cjs +0 -133
  62. package/dist/components/SelectInput/SearchBarDropdown.js +0 -131
  63. package/dist/components/SelectInput/SelectBar.cjs +0 -366
  64. package/dist/components/SelectInput/SelectBar.js +0 -364
@@ -0,0 +1,342 @@
1
+ "use client";
2
+ import { jsx, jsxs, Fragment } from "@emotion/react/jsx-runtime";
3
+ import { useState, useRef, useContext, useEffect, useCallback, useMemo } from "react";
4
+ import "../../../theme/index.js";
5
+ import { Checkbox } from "../../Checkbox/index.js";
6
+ import { ModalContext } from "../../Modal/ModalProvider.js";
7
+ import { Popup } from "../../Popup/index.js";
8
+ import { Skeleton } from "../../Skeleton/index.js";
9
+ import { Stack } from "../../Stack/index.js";
10
+ import { Text } from "../../Text/index.js";
11
+ import { useSelectInput } from "../SelectInputProvider.js";
12
+ import { INPUT_SIZE_HEIGHT } from "../types.js";
13
+ import { DisplayOption } from "./DropdownOption.js";
14
+ import { footer, dropdown, dropdownEmptyState, dropdownContainer, dropdownItem, dropdownCheckbox, dropdownGroupWrapper, dropdownGroupSelectable, dropdownGroup, dropdownLoadMore, dropdownContainerUnGrouped } from "./dropdown.css.js";
15
+ import { SearchBarDropdown } from "./SearchBarDropdown.js";
16
+ import { useTheme } from "../../../theme/ThemeProvider.js";
17
+ const DROPDOWN_MAX_HEIGHT = 256;
18
+ const NON_SEARCHABLE_KEYS = ["Tab", " ", "Enter", "CapsLock", "Shift", "ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "Escape"];
19
+ const moveFocusDown = () => {
20
+ const options = document.querySelectorAll('#items > div[role="option"]:not([disabled])');
21
+ const activeItem = document.activeElement;
22
+ if (options) {
23
+ for (let i = 0; i < options?.length; i += 1) {
24
+ const listLength = options.length;
25
+ if (activeItem === options[i] && activeItem !== options[listLength - 1]) {
26
+ options[i + 1].focus();
27
+ }
28
+ }
29
+ }
30
+ };
31
+ const moveFocusUp = () => {
32
+ const options = document.querySelectorAll('#items > div[role="option"]:not([disabled])');
33
+ const activeItem = document.activeElement;
34
+ if (options) {
35
+ for (let i = 0; i < options.length; i += 1) {
36
+ if (activeItem === options[i] && activeItem !== options[0]) {
37
+ options[i - 1].focus();
38
+ }
39
+ }
40
+ }
41
+ };
42
+ const handleKeyDownSelect = (event) => {
43
+ if (event.key === "ArrowDown") {
44
+ event.preventDefault();
45
+ moveFocusDown();
46
+ }
47
+ if (event.key === "ArrowUp") {
48
+ event.preventDefault();
49
+ moveFocusUp();
50
+ }
51
+ if (event.key === " ") {
52
+ event.preventDefault();
53
+ }
54
+ };
55
+ const handleKeyDown = (event, ref, options, searchBarActive, setSearch, setDefaultSearch, search) => {
56
+ if (ref.current && !searchBarActive && !NON_SEARCHABLE_KEYS.includes(event.key) && document.activeElement?.ariaLabel !== "search-bar") {
57
+ const currentSearch = search + event.key;
58
+ setSearch(currentSearch);
59
+ ref.current.focus();
60
+ if (!Array.isArray(options)) {
61
+ const closestOptions = {
62
+ ...options
63
+ };
64
+ Object.keys(closestOptions).map((group) => {
65
+ closestOptions[group] = closestOptions[group].filter((option) => option.searchText ? option.searchText.toLocaleLowerCase().startsWith(currentSearch) : option.value.toLocaleLowerCase().startsWith(currentSearch));
66
+ return null;
67
+ });
68
+ const closestOption = closestOptions[Object.keys(closestOptions)[0]][0];
69
+ if (closestOption) {
70
+ setDefaultSearch(closestOption.searchText ?? closestOption.value);
71
+ } else {
72
+ setDefaultSearch(null);
73
+ }
74
+ } else {
75
+ const closestOption = [...options].find((option) => option.searchText ? option.searchText.toLocaleLowerCase().startsWith(currentSearch) : option.value.toLocaleLowerCase().startsWith(currentSearch));
76
+ if (closestOption) {
77
+ setDefaultSearch(closestOption.searchText ?? closestOption.value);
78
+ } else {
79
+ setDefaultSearch(null);
80
+ }
81
+ }
82
+ }
83
+ };
84
+ const CreateDropdown = ({
85
+ isEmpty,
86
+ emptyState,
87
+ descriptionDirection,
88
+ loadMore,
89
+ optionalInfoPlacement,
90
+ defaultSearchValue,
91
+ isLoading
92
+ }) => {
93
+ const {
94
+ setIsDropdownVisible,
95
+ onChange,
96
+ options,
97
+ multiselect,
98
+ selectAll,
99
+ selectAllGroup,
100
+ displayedOptions,
101
+ setSelectedData,
102
+ selectedData
103
+ } = useSelectInput();
104
+ const focusedItemRef = useRef(null);
105
+ useEffect(() => {
106
+ if (defaultSearchValue && focusedItemRef?.current) {
107
+ focusedItemRef.current.focus();
108
+ }
109
+ }, [defaultSearchValue]);
110
+ if (isEmpty) {
111
+ return /* @__PURE__ */ jsx(Stack, { alignItems: "center", className: dropdownEmptyState, gap: 2, children: emptyState ?? /* @__PURE__ */ jsx(Text, { as: "p", variant: "bodyStrong", children: "No options" }) });
112
+ }
113
+ const handleClick = (clickedOption, group) => {
114
+ setSelectedData({
115
+ clickedOption,
116
+ group,
117
+ type: "selectOption"
118
+ });
119
+ if (multiselect) {
120
+ if (selectedData.selectedValues.includes(clickedOption.value)) {
121
+ onChange?.(selectedData.selectedValues.filter((val) => val !== clickedOption.value));
122
+ } else {
123
+ onChange?.([...selectedData.selectedValues, clickedOption.value]);
124
+ }
125
+ } else {
126
+ onChange?.(clickedOption.value);
127
+ }
128
+ setIsDropdownVisible(multiselect);
129
+ };
130
+ const selectAllOptions = () => {
131
+ if (multiselect) {
132
+ setSelectedData({
133
+ type: "selectAll"
134
+ });
135
+ if (selectedData.allSelected && onChange) {
136
+ onChange([]);
137
+ } else {
138
+ const allValues = [];
139
+ if (!Array.isArray(options)) {
140
+ Object.keys(options).map((group) => options[group].map((option) => {
141
+ if (!option.disabled) {
142
+ allValues.push(option);
143
+ }
144
+ return null;
145
+ }));
146
+ } else {
147
+ options.map((option) => allValues.push(option));
148
+ }
149
+ onChange?.(allValues.map((value) => value.value));
150
+ }
151
+ }
152
+ };
153
+ const handleSelectGroup = (group) => {
154
+ if (multiselect) {
155
+ setSelectedData({
156
+ selectedGroup: group,
157
+ type: "selectGroup"
158
+ });
159
+ if (!Array.isArray(options)) {
160
+ if (selectedData.selectedGroups.includes(group)) {
161
+ const newSelectedValues = [...selectedData.selectedValues].filter((selectedValue) => !options[group].find((option) => option.value === selectedValue));
162
+ onChange?.(newSelectedValues);
163
+ } else {
164
+ const newSelectedValues = [...selectedData.selectedValues];
165
+ options[group].map((option) => newSelectedValues.includes(option.value) || option.disabled ? null : newSelectedValues.push(option.value));
166
+ onChange?.(newSelectedValues);
167
+ }
168
+ }
169
+ }
170
+ };
171
+ return !Array.isArray(displayedOptions) ? /* @__PURE__ */ jsxs(Stack, { className: dropdownContainer, "data-grouped": true, id: "select-dropdown", onKeyDown: handleKeyDownSelect, role: "listbox", children: [
172
+ isLoading ? /* @__PURE__ */ jsx(Skeleton, { variant: "block" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
173
+ selectAll && multiselect ? /* @__PURE__ */ jsx(Stack, { id: "items", children: /* @__PURE__ */ jsx("div", { "aria-disabled": false, "aria-label": "select-all", "aria-selected": selectedData.allSelected, className: dropdownItem({
174
+ selected: selectedData.allSelected
175
+ }), "data-testid": "select-all", id: "select-all", onClick: selectAllOptions, onKeyDown: (event) => [" ", "Enter"].includes(event.key) ? selectAllOptions() : null, role: "option", tabIndex: 0, children: /* @__PURE__ */ jsx(Checkbox, { checked: selectedData.allSelected, className: dropdownCheckbox, "data-testid": "select-all-checkbox", disabled: false, onChange: selectAllOptions, tabIndex: -1, value: "select-all", children: /* @__PURE__ */ jsxs(Stack, { direction: "column", children: [
176
+ /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", variant: "body", children: selectAll.label }),
177
+ /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: selectAll.description })
178
+ ] }) }) }) }) : null,
179
+ Object.keys(displayedOptions).map((group, index) => /* @__PURE__ */ jsxs(Stack, { gap: 0.25, children: [
180
+ displayedOptions[group].length > 0 ? /* @__PURE__ */ jsx("div", { className: dropdownGroupWrapper, id: selectAllGroup ? "items" : void 0, children: group ? /* @__PURE__ */ jsx("button", { className: `${selectAllGroup ? dropdownGroupSelectable : ""} ${dropdownGroup}`, "data-selectgroup": selectAllGroup, "data-testid": `group-${index}`, onClick: () => selectAllGroup ? handleSelectGroup(group) : null, onKeyDown: (event) => {
181
+ if ([" ", "Enter"].includes(event.key)) {
182
+ event.preventDefault();
183
+ handleSelectGroup(group);
184
+ }
185
+ }, role: "group", tabIndex: selectAllGroup ? 0 : -1, type: "button", children: selectAllGroup ? /* @__PURE__ */ jsx(Checkbox, { checked: selectedData.selectedGroups.includes(group), className: dropdownCheckbox, "data-testid": "select-group", disabled: false, onChange: () => selectAllGroup ? handleSelectGroup(group) : null, tabIndex: -1, value: group, children: /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", sentiment: "neutral", variant: "caption", children: group.toUpperCase() }) }) : /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", sentiment: "neutral", variant: "caption", children: group.toUpperCase() }) }, group) : null }) : null,
186
+ /* @__PURE__ */ jsx(Stack, { gap: "0.25", id: "items", children: displayedOptions[group].map((option, indexOption) => /* @__PURE__ */ jsx("div", { "aria-disabled": !!option.disabled, "aria-label": option.value, "aria-selected": selectedData.selectedValues.includes(option.value) && !option.disabled, className: dropdownItem({
187
+ disabled: !!option.disabled,
188
+ selected: selectedData.selectedValues.includes(option.value) && !option.disabled
189
+ }), "data-testid": `option-${option.value}`, id: `option-${indexOption}`, onClick: () => {
190
+ if (!option.disabled) {
191
+ handleClick(option, group);
192
+ }
193
+ }, onKeyDown: (event) => [" ", "Enter"].includes(event.key) ? handleClick(option, group) : null, ref: option.value === defaultSearchValue || option.searchText === defaultSearchValue ? focusedItemRef : null, role: "option", tabIndex: !option.disabled ? 0 : -1, children: multiselect ? /* @__PURE__ */ jsx(Checkbox, { checked: selectedData.selectedValues.includes(option.value) && !option.disabled, className: dropdownCheckbox, disabled: option.disabled, onChange: () => {
194
+ if (!option.disabled) {
195
+ handleClick(option, group);
196
+ }
197
+ }, tabIndex: -1, value: option.value, children: /* @__PURE__ */ jsx(DisplayOption, { descriptionDirection, option, optionalInfoPlacement }) }) : /* @__PURE__ */ jsx(DisplayOption, { descriptionDirection, option, optionalInfoPlacement }) }, option.value)) })
198
+ ] }, group))
199
+ ] }),
200
+ loadMore ? /* @__PURE__ */ jsx(Stack, { className: dropdownLoadMore, children: loadMore }) : null
201
+ ] }) : /* @__PURE__ */ jsxs(Stack, { className: `${dropdownContainer} ${dropdownContainerUnGrouped}`, gap: 0.25, id: "select-dropdown", onKeyDown: handleKeyDownSelect, role: "listbox", children: [
202
+ selectAll && multiselect ? /* @__PURE__ */ jsx(Stack, { gap: 0.25, id: "items", tabIndex: -1, children: /* @__PURE__ */ jsx("div", { "aria-disabled": false, "aria-label": "select-all", "aria-selected": selectedData.allSelected, className: dropdownItem({
203
+ selected: selectedData.allSelected
204
+ }), "data-testid": "select-all", onClick: selectAllOptions, onKeyDown: (event) => [" ", "Enter"].includes(event.key) ? selectAllOptions() : null, role: "option", tabIndex: 0, children: /* @__PURE__ */ jsx(Checkbox, { checked: selectedData.allSelected, className: dropdownCheckbox, "data-testid": "select-all-checkbox", disabled: false, onChange: selectAllOptions, tabIndex: -1, value: "select-all", children: /* @__PURE__ */ jsxs(Stack, { direction: "column", children: [
205
+ /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", variant: "body", children: selectAll.label }),
206
+ /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: selectAll.description })
207
+ ] }) }) }) }) : null,
208
+ /* @__PURE__ */ jsxs(Stack, { gap: 0.25, id: "items", children: [
209
+ isLoading ? /* @__PURE__ */ jsx(Skeleton, { variant: "block" }) : displayedOptions.map((option, index) => /* @__PURE__ */ jsx("div", { "aria-disabled": !!option.disabled, "aria-label": option.value, "aria-selected": selectedData.selectedValues.includes(option.value) && !option.disabled, className: dropdownItem({
210
+ disabled: !!option.disabled,
211
+ selected: selectedData.selectedValues.includes(option.value) && !option.disabled
212
+ }), "data-testid": `option-${option.value}`, id: `option-${index}`, onClick: () => {
213
+ if (!option.disabled) {
214
+ handleClick(option);
215
+ }
216
+ }, onKeyDown: (event) => [" ", "Enter"].includes(event.key) ? handleClick(option) : null, ref: option.value === defaultSearchValue || option.searchText === defaultSearchValue ? focusedItemRef : null, role: "option", tabIndex: !option.disabled ? 0 : -1, children: multiselect ? /* @__PURE__ */ jsx(Checkbox, { checked: selectedData.selectedValues.includes(option.value) && !option.disabled, className: dropdownCheckbox, disabled: option.disabled, onChange: () => {
217
+ if (!option.disabled) {
218
+ handleClick(option);
219
+ }
220
+ }, tabIndex: -1, value: option.value, children: /* @__PURE__ */ jsx(DisplayOption, { descriptionDirection, option, optionalInfoPlacement }) }) : /* @__PURE__ */ jsx(DisplayOption, { descriptionDirection, option, optionalInfoPlacement }) }, option.value)),
221
+ loadMore ? /* @__PURE__ */ jsx(Stack, { className: dropdownLoadMore, children: loadMore }) : null
222
+ ] })
223
+ ] });
224
+ };
225
+ const Dropdown = ({
226
+ children,
227
+ emptyState,
228
+ descriptionDirection,
229
+ searchable,
230
+ placeholder,
231
+ footer: footer$1,
232
+ refSelect,
233
+ loadMore,
234
+ optionalInfoPlacement,
235
+ isLoading,
236
+ size,
237
+ dropdownAlign,
238
+ portalTarget,
239
+ id
240
+ }) => {
241
+ const {
242
+ setIsDropdownVisible,
243
+ isDropdownVisible,
244
+ onSearch,
245
+ searchInput,
246
+ options,
247
+ displayedOptions,
248
+ numberOfOptions
249
+ } = useSelectInput();
250
+ const theme = useTheme();
251
+ const [searchBarActive, setSearchBarActive] = useState(false);
252
+ const [defaultSearchValue, setDefaultSearch] = useState(null);
253
+ const ref = useRef(null);
254
+ const [search, setSearch] = useState("");
255
+ const [maxWidth, setWidth] = useState(refSelect.current?.offsetWidth ?? "100%");
256
+ const modalContext = useContext(ModalContext);
257
+ useEffect(() => {
258
+ if (refSelect.current && isDropdownVisible) {
259
+ const position = refSelect.current.getBoundingClientRect().bottom + DROPDOWN_MAX_HEIGHT + Number(theme.sizing[INPUT_SIZE_HEIGHT[size]].replace("rem", "")) * 16 + Number.parseInt(theme.space["5"], 10);
260
+ const overflow = position - window.innerHeight + 32;
261
+ if (overflow > 0 && modalContext) {
262
+ const currentModal = modalContext.openedModals[0];
263
+ const modalElement = currentModal?.ref.current;
264
+ if (modalElement) {
265
+ const parentElement = modalElement.parentNode;
266
+ if (parentElement) {
267
+ parentElement.scrollBy({
268
+ behavior: "smooth",
269
+ top: overflow
270
+ });
271
+ }
272
+ } else {
273
+ window.scrollBy({
274
+ behavior: "smooth",
275
+ top: overflow
276
+ });
277
+ }
278
+ }
279
+ }
280
+ }, [isDropdownVisible, refSelect, size, ref.current]);
281
+ const resizeDropdown = useCallback(() => {
282
+ if (refSelect.current && refSelect.current.getBoundingClientRect().width > 0) {
283
+ setWidth(refSelect.current.getBoundingClientRect().width);
284
+ }
285
+ }, [refSelect]);
286
+ useEffect(() => {
287
+ resizeDropdown();
288
+ window.addEventListener("resize", resizeDropdown);
289
+ return () => window.removeEventListener("resize", resizeDropdown);
290
+ }, [resizeDropdown]);
291
+ useEffect(() => {
292
+ if (!searchInput) {
293
+ onSearch(options);
294
+ }
295
+ }, [onSearch, options, searchInput]);
296
+ useEffect(() => {
297
+ if (!isDropdownVisible) {
298
+ setDefaultSearch(null);
299
+ setSearch("");
300
+ }
301
+ if (!searchable) {
302
+ document.addEventListener("keydown", (event) => handleKeyDown(event, ref, options, searchBarActive, setSearch, setDefaultSearch, search));
303
+ }
304
+ return () => {
305
+ if (!searchable) {
306
+ document.removeEventListener("keydown", (event) => handleKeyDown(event, ref, options, searchBarActive, setSearch, setDefaultSearch, search));
307
+ }
308
+ };
309
+ }, [isDropdownVisible, searchBarActive, options, onSearch, search, refSelect, setDefaultSearch, setIsDropdownVisible, searchable]);
310
+ const isEmpty = useMemo(() => {
311
+ if (numberOfOptions === 0) {
312
+ return true;
313
+ }
314
+ if (Array.isArray(displayedOptions)) {
315
+ return displayedOptions.length === 0;
316
+ }
317
+ const groups = Object.keys(displayedOptions);
318
+ for (const group of groups) {
319
+ if (displayedOptions[group].length > 0) {
320
+ return false;
321
+ }
322
+ }
323
+ return true;
324
+ }, [displayedOptions, numberOfOptions]);
325
+ const computedFooter = useMemo(() => {
326
+ if (footer$1 && !isEmpty) {
327
+ if (typeof footer$1 === "function") {
328
+ return /* @__PURE__ */ jsx("div", { className: footer, children: footer$1(() => setIsDropdownVisible(false)) });
329
+ }
330
+ return /* @__PURE__ */ jsx("div", { className: footer, children: footer$1 });
331
+ }
332
+ return null;
333
+ }, [isEmpty, footer$1, setIsDropdownVisible]);
334
+ return /* @__PURE__ */ jsx(Popup, { align: dropdownAlign ?? "start", className: dropdown, containerFullWidth: true, debounceDelay: 0, disableAnimation: true, hasArrow: false, hideOnClickOutside: true, id, maxWidth: maxWidth ?? refSelect.current?.offsetWidth, onClose: () => setIsDropdownVisible(false), placement: "bottom", portalTarget, ref, role: "dialog", tabIndex: -1, text: /* @__PURE__ */ jsxs(Stack, { children: [
335
+ searchable && !isLoading && numberOfOptions >= 6 ? /* @__PURE__ */ jsx(SearchBarDropdown, { displayedOptions, placeholder, setSearchBarActive }) : null,
336
+ /* @__PURE__ */ jsx(CreateDropdown, { defaultSearchValue, descriptionDirection, emptyState, isEmpty, isLoading, loadMore, optionalInfoPlacement }),
337
+ computedFooter
338
+ ] }), visible: isDropdownVisible, children });
339
+ };
340
+ export {
341
+ Dropdown
342
+ };
@@ -0,0 +1,47 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const jsxRuntime = require("@emotion/react/jsx-runtime");
5
+ const index$1 = require("../../Stack/index.cjs");
6
+ const index$2 = require("../../Text/index.cjs");
7
+ const index = require("../../Tooltip/index.cjs");
8
+ const dropdown_css = require("./dropdown.css.cjs");
9
+ const DisplayOption = ({
10
+ option,
11
+ optionalInfoPlacement,
12
+ descriptionDirection
13
+ }) => {
14
+ if (descriptionDirection === "row" && optionalInfoPlacement === "left") {
15
+ return /* @__PURE__ */ jsxRuntime.jsx(index.Tooltip, { text: option.tooltip, children: /* @__PURE__ */ jsxRuntime.jsx(index$1.Stack, { "data-testid": `option-stack-${option.value}`, direction: "row", gap: 0.5, justifyContent: "left", children: /* @__PURE__ */ jsxRuntime.jsxs(index$1.Stack, { alignItems: "center", className: dropdown_css.dropdownInfoContainer, direction: "row", gap: 0.5, children: [
16
+ option.optionalInfo ?? null,
17
+ /* @__PURE__ */ jsxRuntime.jsx(index$2.Text, { as: "span", className: dropdown_css.dropdownInfoTextItem, placement: "left", variant: "body", children: option.label }),
18
+ option.description ? /* @__PURE__ */ jsxRuntime.jsx(index$2.Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: option.description }) : null
19
+ ] }) }) });
20
+ }
21
+ if (descriptionDirection === "row" && optionalInfoPlacement === "right") {
22
+ return /* @__PURE__ */ jsxRuntime.jsx(index.Tooltip, { text: option.tooltip, children: /* @__PURE__ */ jsxRuntime.jsxs(index$1.Stack, { alignItems: "baseline", "data-testid": `option-stack-${option.value}`, direction: "row", gap: 0.5, justifyContent: "space-between", children: [
23
+ /* @__PURE__ */ jsxRuntime.jsxs(index$1.Stack, { alignItems: "baseline", className: dropdown_css.dropdownInfoContainer, direction: "row", gap: 0.5, children: [
24
+ /* @__PURE__ */ jsxRuntime.jsx(index$2.Text, { as: "span", className: dropdown_css.dropdownInfoTextItem, placement: "left", variant: "body", children: option.label }),
25
+ option.description ? /* @__PURE__ */ jsxRuntime.jsx(index$2.Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: option.description }) : null
26
+ ] }),
27
+ option.optionalInfo ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: dropdown_css.dropdownInfo, children: option.optionalInfo }) : null
28
+ ] }) });
29
+ }
30
+ if (descriptionDirection === "column" && optionalInfoPlacement === "left") {
31
+ return /* @__PURE__ */ jsxRuntime.jsx(index.Tooltip, { text: option.tooltip, children: /* @__PURE__ */ jsxRuntime.jsxs(index$1.Stack, { alignItems: "normal", direction: "row", gap: 0.5, justifyContent: option.optionalInfo ? "left" : "space-between", children: [
32
+ option.optionalInfo ?? null,
33
+ /* @__PURE__ */ jsxRuntime.jsxs(index$1.Stack, { className: dropdown_css.dropdownInfoContainer, "data-testid": `option-stack-${option.value}`, direction: "column", gap: 0.5, children: [
34
+ /* @__PURE__ */ jsxRuntime.jsx(index$2.Text, { as: "span", className: dropdown_css.dropdownInfoTextItem, placement: "left", variant: "body", children: option.label }),
35
+ option.description ? /* @__PURE__ */ jsxRuntime.jsx(index$2.Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: option.description }) : null
36
+ ] })
37
+ ] }) });
38
+ }
39
+ return /* @__PURE__ */ jsxRuntime.jsx(index.Tooltip, { text: option.tooltip, children: /* @__PURE__ */ jsxRuntime.jsxs(index$1.Stack, { alignItems: "normal", "data-testid": `option-stack-${option.value}`, direction: "column", gap: 0.5, children: [
40
+ /* @__PURE__ */ jsxRuntime.jsxs(index$1.Stack, { className: dropdown_css.dropdownInfoContainer, direction: "row", gap: 0.5, justifyContent: "space-between", children: [
41
+ /* @__PURE__ */ jsxRuntime.jsx(index$2.Text, { as: "span", className: dropdown_css.dropdownInfoTextItem, placement: "left", variant: "body", children: option.label }),
42
+ option.optionalInfo ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: dropdown_css.dropdownInfo, children: option.optionalInfo }) : null
43
+ ] }),
44
+ option.description ? /* @__PURE__ */ jsxRuntime.jsx(index$2.Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: option.description }) : null
45
+ ] }) });
46
+ };
47
+ exports.DisplayOption = DisplayOption;
@@ -1,4 +1,4 @@
1
- import type { OptionType } from './types';
1
+ import type { OptionType } from '../types';
2
2
  type DisplayOptionProps = {
3
3
  option: OptionType;
4
4
  descriptionDirection: 'row' | 'column';
@@ -0,0 +1,47 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "@emotion/react/jsx-runtime";
3
+ import { Stack } from "../../Stack/index.js";
4
+ import { Text } from "../../Text/index.js";
5
+ import { Tooltip } from "../../Tooltip/index.js";
6
+ import { dropdownInfoContainer, dropdownInfoTextItem, dropdownInfo } from "./dropdown.css.js";
7
+ const DisplayOption = ({
8
+ option,
9
+ optionalInfoPlacement,
10
+ descriptionDirection
11
+ }) => {
12
+ if (descriptionDirection === "row" && optionalInfoPlacement === "left") {
13
+ return /* @__PURE__ */ jsx(Tooltip, { text: option.tooltip, children: /* @__PURE__ */ jsx(Stack, { "data-testid": `option-stack-${option.value}`, direction: "row", gap: 0.5, justifyContent: "left", children: /* @__PURE__ */ jsxs(Stack, { alignItems: "center", className: dropdownInfoContainer, direction: "row", gap: 0.5, children: [
14
+ option.optionalInfo ?? null,
15
+ /* @__PURE__ */ jsx(Text, { as: "span", className: dropdownInfoTextItem, placement: "left", variant: "body", children: option.label }),
16
+ option.description ? /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: option.description }) : null
17
+ ] }) }) });
18
+ }
19
+ if (descriptionDirection === "row" && optionalInfoPlacement === "right") {
20
+ return /* @__PURE__ */ jsx(Tooltip, { text: option.tooltip, children: /* @__PURE__ */ jsxs(Stack, { alignItems: "baseline", "data-testid": `option-stack-${option.value}`, direction: "row", gap: 0.5, justifyContent: "space-between", children: [
21
+ /* @__PURE__ */ jsxs(Stack, { alignItems: "baseline", className: dropdownInfoContainer, direction: "row", gap: 0.5, children: [
22
+ /* @__PURE__ */ jsx(Text, { as: "span", className: dropdownInfoTextItem, placement: "left", variant: "body", children: option.label }),
23
+ option.description ? /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: option.description }) : null
24
+ ] }),
25
+ option.optionalInfo ? /* @__PURE__ */ jsx("div", { className: dropdownInfo, children: option.optionalInfo }) : null
26
+ ] }) });
27
+ }
28
+ if (descriptionDirection === "column" && optionalInfoPlacement === "left") {
29
+ return /* @__PURE__ */ jsx(Tooltip, { text: option.tooltip, children: /* @__PURE__ */ jsxs(Stack, { alignItems: "normal", direction: "row", gap: 0.5, justifyContent: option.optionalInfo ? "left" : "space-between", children: [
30
+ option.optionalInfo ?? null,
31
+ /* @__PURE__ */ jsxs(Stack, { className: dropdownInfoContainer, "data-testid": `option-stack-${option.value}`, direction: "column", gap: 0.5, children: [
32
+ /* @__PURE__ */ jsx(Text, { as: "span", className: dropdownInfoTextItem, placement: "left", variant: "body", children: option.label }),
33
+ option.description ? /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: option.description }) : null
34
+ ] })
35
+ ] }) });
36
+ }
37
+ return /* @__PURE__ */ jsx(Tooltip, { text: option.tooltip, children: /* @__PURE__ */ jsxs(Stack, { alignItems: "normal", "data-testid": `option-stack-${option.value}`, direction: "column", gap: 0.5, children: [
38
+ /* @__PURE__ */ jsxs(Stack, { className: dropdownInfoContainer, direction: "row", gap: 0.5, justifyContent: "space-between", children: [
39
+ /* @__PURE__ */ jsx(Text, { as: "span", className: dropdownInfoTextItem, placement: "left", variant: "body", children: option.label }),
40
+ option.optionalInfo ? /* @__PURE__ */ jsx("div", { className: dropdownInfo, children: option.optionalInfo }) : null
41
+ ] }),
42
+ option.description ? /* @__PURE__ */ jsx(Text, { as: "span", placement: "left", prominence: "weak", sentiment: "neutral", variant: "bodySmall", children: option.description }) : null
43
+ ] }) });
44
+ };
45
+ export {
46
+ DisplayOption
47
+ };
@@ -0,0 +1,117 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const jsxRuntime = require("@emotion/react/jsx-runtime");
5
+ const Icon = require("@ultraviolet/icons");
6
+ const react = require("react");
7
+ const searchAlgorithm = require("../../../utils/searchAlgorithm.cjs");
8
+ const index = require("../../TextInput/index.cjs");
9
+ const SelectInputProvider = require("../SelectInputProvider.cjs");
10
+ const dropdown_css = require("./dropdown.css.cjs");
11
+ const getReferenceText = (option) => {
12
+ if (option.searchText) {
13
+ return searchAlgorithm.normalizeString(option.searchText);
14
+ }
15
+ if (typeof option.label === "string") {
16
+ return searchAlgorithm.normalizeString(option.label);
17
+ }
18
+ return "";
19
+ };
20
+ const searchRegex = (data, query) => data.filter((option) => {
21
+ const referenceText = getReferenceText(option);
22
+ const regex = new RegExp(query, "i");
23
+ return (query.length > 2 ? searchAlgorithm.isFuzzyMatch(query, referenceText) : referenceText.match(regex)) || typeof option.description === "string" && option.description.match(regex) || option.value.match(regex);
24
+ });
25
+ const findClosestOption = (options, searchInput) => {
26
+ if (searchInput) {
27
+ if (!Array.isArray(options)) {
28
+ const possibleOptions = {
29
+ ...options
30
+ };
31
+ Object.keys(possibleOptions).map((group) => {
32
+ possibleOptions[group] = possibleOptions[group].filter((option) => !option.disabled);
33
+ return null;
34
+ });
35
+ if (Object.keys(possibleOptions).some((group) => possibleOptions[group].length > 0)) {
36
+ const firstFit = Object.keys(possibleOptions).map((group) => possibleOptions[group][0]).find((value) => !!value);
37
+ return firstFit;
38
+ }
39
+ } else {
40
+ const possibleOption = [...options].find((option) => !option.disabled);
41
+ if (possibleOption) {
42
+ return possibleOption;
43
+ }
44
+ }
45
+ }
46
+ return null;
47
+ };
48
+ const escapeRegExp = (string) => string.replace(/[.*+?^{}()|[\]\\]/g, String.raw`\$&`);
49
+ const SearchBarDropdown = ({
50
+ placeholder,
51
+ displayedOptions,
52
+ setSearchBarActive
53
+ }) => {
54
+ const searchInputRef = react.useRef(null);
55
+ const {
56
+ onChange,
57
+ onSearch,
58
+ setSearchInput,
59
+ searchInput,
60
+ options,
61
+ multiselect,
62
+ setSelectedData,
63
+ selectedData
64
+ } = SelectInputProvider.useSelectInput();
65
+ const handleChange = (search) => {
66
+ if (search.length > 0) {
67
+ if (!Array.isArray(options)) {
68
+ const filteredOptions = {
69
+ ...options
70
+ };
71
+ Object.keys(filteredOptions).map((group) => {
72
+ filteredOptions[group] = searchRegex(filteredOptions[group], escapeRegExp(search.toString()));
73
+ return null;
74
+ });
75
+ onSearch(filteredOptions);
76
+ } else {
77
+ const filteredOptions = searchRegex([...options], escapeRegExp(search.toString()));
78
+ onSearch(filteredOptions);
79
+ }
80
+ } else {
81
+ onSearch(options);
82
+ }
83
+ setSearchInput(search);
84
+ };
85
+ const handleKeyDown = (key, search) => {
86
+ if (key === "Enter") {
87
+ const closestOption = findClosestOption(displayedOptions, search);
88
+ if (closestOption) {
89
+ if (multiselect) {
90
+ setSelectedData({
91
+ clickedOption: closestOption,
92
+ group: !Array.isArray(options) ? Object.keys(options).find((group) => options[group].includes(closestOption)) : void 0,
93
+ type: "selectOption"
94
+ });
95
+ onChange?.(selectedData.selectedValues.includes(closestOption.value) ? selectedData.selectedValues : [...selectedData.selectedValues, closestOption.value]);
96
+ } else {
97
+ setSelectedData({
98
+ clickedOption: closestOption,
99
+ type: "selectOption"
100
+ });
101
+ onChange?.(selectedData.selectedValues[0] ?? "");
102
+ }
103
+ }
104
+ } else if (key === "Tab") {
105
+ searchInputRef.current?.blur();
106
+ }
107
+ };
108
+ react.useEffect(() => {
109
+ setTimeout(() => {
110
+ searchInputRef.current?.focus();
111
+ }, 50);
112
+ }, []);
113
+ return /* @__PURE__ */ jsxRuntime.jsx(index.TextInput, { "aria-label": "search-bar", className: dropdown_css.searchBar, "data-testid": "search-bar", onBlur: () => setSearchBarActive(false), onChange: (event) => handleChange(event.target.value), onFocus: () => setSearchBarActive(true), onKeyDown: (event) => handleKeyDown(event.key, searchInput), placeholder, prefix: /* @__PURE__ */ jsxRuntime.jsx(Icon.SearchIcon, { sentiment: "neutral", size: "small" }), ref: searchInputRef, size: "medium", value: searchInput });
114
+ };
115
+ exports.SearchBarDropdown = SearchBarDropdown;
116
+ exports.getReferenceText = getReferenceText;
117
+ exports.searchRegex = searchRegex;
@@ -1,5 +1,5 @@
1
1
  import type { Dispatch, SetStateAction } from 'react';
2
- import type { DataType, OptionType } from './types';
2
+ import type { DataType, OptionType } from '../types';
3
3
  type SearchBarProps = {
4
4
  placeholder: string;
5
5
  displayedOptions: DataType;