@qoretechnologies/reqore 0.28.6 → 0.29.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.
Files changed (136) hide show
  1. package/.vscode/settings.json +2 -1
  2. package/__tests__/multiselect.test.tsx +361 -0
  3. package/__tests__/popover.test.tsx +35 -8
  4. package/dist/components/Collection/item.js +2 -2
  5. package/dist/components/Collection/item.js.map +1 -1
  6. package/dist/components/ControlGroup/index.d.ts +3 -4
  7. package/dist/components/ControlGroup/index.d.ts.map +1 -1
  8. package/dist/components/ControlGroup/index.js +7 -9
  9. package/dist/components/ControlGroup/index.js.map +1 -1
  10. package/dist/components/Dropdown/index.d.ts +2 -1
  11. package/dist/components/Dropdown/index.d.ts.map +1 -1
  12. package/dist/components/Dropdown/index.js +3 -3
  13. package/dist/components/Dropdown/index.js.map +1 -1
  14. package/dist/components/Dropdown/list.d.ts +6 -3
  15. package/dist/components/Dropdown/list.d.ts.map +1 -1
  16. package/dist/components/Dropdown/list.js +19 -1
  17. package/dist/components/Dropdown/list.js.map +1 -1
  18. package/dist/components/Effect/index.d.ts +3 -1
  19. package/dist/components/Effect/index.d.ts.map +1 -1
  20. package/dist/components/Effect/index.js +24 -19
  21. package/dist/components/Effect/index.js.map +1 -1
  22. package/dist/components/Input/index.js +3 -3
  23. package/dist/components/Input/index.js.map +1 -1
  24. package/dist/components/InputClearButton/index.d.ts.map +1 -1
  25. package/dist/components/InputClearButton/index.js +1 -1
  26. package/dist/components/InputClearButton/index.js.map +1 -1
  27. package/dist/components/InternalPopover/index.js +3 -3
  28. package/dist/components/InternalPopover/index.js.map +1 -1
  29. package/dist/components/Menu/divider.d.ts +3 -2
  30. package/dist/components/Menu/divider.d.ts.map +1 -1
  31. package/dist/components/Menu/divider.js +2 -2
  32. package/dist/components/Menu/divider.js.map +1 -1
  33. package/dist/components/Menu/index.js +4 -3
  34. package/dist/components/Menu/index.js.map +1 -1
  35. package/dist/components/MultiSelect/index.d.ts +32 -0
  36. package/dist/components/MultiSelect/index.d.ts.map +1 -0
  37. package/dist/components/MultiSelect/index.js +177 -0
  38. package/dist/components/MultiSelect/index.js.map +1 -0
  39. package/dist/components/Panel/index.d.ts.map +1 -1
  40. package/dist/components/Panel/index.js +1 -1
  41. package/dist/components/Panel/index.js.map +1 -1
  42. package/dist/components/Popover/index.d.ts +1 -1
  43. package/dist/components/Popover/index.d.ts.map +1 -1
  44. package/dist/components/Popover/index.js +5 -2
  45. package/dist/components/Popover/index.js.map +1 -1
  46. package/dist/components/Table/row.js +2 -2
  47. package/dist/components/Table/row.js.map +1 -1
  48. package/dist/components/Tabs/item.js +1 -1
  49. package/dist/components/Tabs/item.js.map +1 -1
  50. package/dist/components/Tabs/list.d.ts.map +1 -1
  51. package/dist/components/Tabs/list.js +5 -4
  52. package/dist/components/Tabs/list.js.map +1 -1
  53. package/dist/components/Tag/group.d.ts +3 -3
  54. package/dist/components/Tag/group.d.ts.map +1 -1
  55. package/dist/components/Tag/group.js +6 -3
  56. package/dist/components/Tag/group.js.map +1 -1
  57. package/dist/components/Tag/index.js +5 -5
  58. package/dist/components/Tag/index.js.map +1 -1
  59. package/dist/components/Tree/index.js +1 -1
  60. package/dist/components/Tree/index.js.map +1 -1
  61. package/dist/containers/PopoverProvider.d.ts.map +1 -1
  62. package/dist/containers/PopoverProvider.js +28 -3
  63. package/dist/containers/PopoverProvider.js.map +1 -1
  64. package/dist/containers/ReqoreProvider.d.ts.map +1 -1
  65. package/dist/containers/ReqoreProvider.js +6 -5
  66. package/dist/containers/ReqoreProvider.js.map +1 -1
  67. package/dist/containers/UIProvider.d.ts +1 -0
  68. package/dist/containers/UIProvider.d.ts.map +1 -1
  69. package/dist/containers/UIProvider.js.map +1 -1
  70. package/dist/context/PopoverContext.d.ts +1 -0
  71. package/dist/context/PopoverContext.d.ts.map +1 -1
  72. package/dist/context/PopoverContext.js.map +1 -1
  73. package/dist/context/ReqoreContext.d.ts +1 -0
  74. package/dist/context/ReqoreContext.d.ts.map +1 -1
  75. package/dist/context/ReqoreContext.js +2 -1
  76. package/dist/context/ReqoreContext.js.map +1 -1
  77. package/dist/helpers/colors.d.ts +3 -0
  78. package/dist/helpers/colors.d.ts.map +1 -1
  79. package/dist/helpers/colors.js +27 -3
  80. package/dist/helpers/colors.js.map +1 -1
  81. package/dist/hooks/usePopover.d.ts +8 -1
  82. package/dist/hooks/usePopover.d.ts.map +1 -1
  83. package/dist/hooks/usePopover.js +12 -7
  84. package/dist/hooks/usePopover.js.map +1 -1
  85. package/dist/hooks/useReqore.d.ts +3 -0
  86. package/dist/hooks/useReqore.d.ts.map +1 -0
  87. package/dist/hooks/useReqore.js +17 -0
  88. package/dist/hooks/useReqore.js.map +1 -0
  89. package/dist/hooks/useTheme.d.ts +1 -1
  90. package/dist/hooks/useTheme.d.ts.map +1 -1
  91. package/dist/hooks/useTheme.js +3 -0
  92. package/dist/hooks/useTheme.js.map +1 -1
  93. package/dist/index.d.ts +3 -0
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +8 -2
  96. package/dist/index.js.map +1 -1
  97. package/package.json +1 -1
  98. package/src/components/Collection/item.tsx +2 -2
  99. package/src/components/ControlGroup/index.tsx +46 -35
  100. package/src/components/Dropdown/index.tsx +5 -0
  101. package/src/components/Dropdown/list.tsx +29 -15
  102. package/src/components/Effect/index.tsx +58 -51
  103. package/src/components/Input/index.tsx +3 -3
  104. package/src/components/InputClearButton/index.tsx +1 -0
  105. package/src/components/InternalPopover/index.tsx +3 -3
  106. package/src/components/Menu/divider.tsx +15 -4
  107. package/src/components/Menu/index.tsx +3 -3
  108. package/src/components/MultiSelect/index.tsx +283 -0
  109. package/src/components/Panel/index.tsx +6 -4
  110. package/src/components/Popover/index.tsx +8 -3
  111. package/src/components/Table/row.tsx +2 -2
  112. package/src/components/Tabs/item.tsx +1 -1
  113. package/src/components/Tabs/list.tsx +11 -11
  114. package/src/components/Tag/group.tsx +10 -2
  115. package/src/components/Tag/index.tsx +7 -7
  116. package/src/components/Tree/index.tsx +3 -3
  117. package/src/containers/PopoverProvider.tsx +35 -3
  118. package/src/containers/ReqoreProvider.tsx +6 -4
  119. package/src/containers/UIProvider.tsx +1 -0
  120. package/src/context/PopoverContext.tsx +1 -0
  121. package/src/context/ReqoreContext.tsx +2 -0
  122. package/src/helpers/colors.ts +52 -4
  123. package/src/hooks/usePopover.tsx +24 -10
  124. package/src/hooks/useReqore.ts +12 -0
  125. package/src/hooks/useTheme.ts +6 -1
  126. package/src/index.tsx +3 -0
  127. package/src/mock/multiSelect.ts +61 -0
  128. package/src/stories/Button/Button.stories.tsx +20 -8
  129. package/src/stories/ControlGroup/ControlGroup.stories.tsx +7 -2
  130. package/src/stories/Dropdown/Dropdown.stories.tsx +6 -1
  131. package/src/stories/Input/Input.stories.tsx +25 -0
  132. package/src/stories/Menu/Menu.stories.tsx +3 -2
  133. package/src/stories/MultiSelect/MultiSelect.stories.tsx +99 -0
  134. package/src/stories/Tabs/Tabs.stories.tsx +23 -3
  135. package/src/stories/Tag/Tag.stories.tsx +8 -1
  136. package/tests.json +1 -1
@@ -0,0 +1,283 @@
1
+ import { size } from 'lodash';
2
+ import { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { useUpdateEffect } from 'react-use';
4
+ import { ReqoreDropdown, ReqoreInput } from '../..';
5
+ import { TSizes } from '../../constants/sizes';
6
+ import { IPopoverControls } from '../../hooks/usePopover';
7
+ import ReqoreControlGroup, { IReqoreControlGroupProps } from '../ControlGroup';
8
+ import { IReqoreDropdownProps } from '../Dropdown';
9
+ import { IReqoreDropdownItem } from '../Dropdown/list';
10
+ import { IReqoreEffect } from '../Effect';
11
+ import { IReqoreInputProps } from '../Input';
12
+ import ReqoreTag, { IReqoreTagProps } from '../Tag';
13
+ import ReqoreTagGroup from '../Tag/group';
14
+
15
+ export type TReqoreMultiSelectItem = Omit<IReqoreDropdownItem, 'color'> &
16
+ Pick<IReqoreTagProps, 'badge'> & { isNew?: boolean };
17
+
18
+ export interface IReqoreMultiSelectProps
19
+ extends Omit<IReqoreControlGroupProps, 'children' | 'vertical' | 'stack'> {
20
+ value?: string[];
21
+ onValueChange?: (value: string[]) => void;
22
+ items?: TReqoreMultiSelectItem[];
23
+ onItemClick?: (item: IReqoreDropdownItem) => void;
24
+ canRemoveItems?: boolean;
25
+ canCreateItems?: boolean;
26
+ selectedItemEffect?: IReqoreEffect;
27
+ selectedItemSize?: TSizes;
28
+ selectorProps?: Omit<IReqoreInputProps, 'value' | 'onValueChange'> & IReqoreDropdownProps;
29
+ openOnMount?: IReqoreDropdownProps['openOnMount'];
30
+ enterKeySelects?: boolean;
31
+ }
32
+
33
+ export interface IReqoreMultiSelectItemProps
34
+ extends Pick<IReqoreMultiSelectProps, 'selectedItemEffect' | 'selectedItemSize'> {
35
+ item: TReqoreMultiSelectItem;
36
+ onClick?: () => void;
37
+ onRemoveClick?: () => void;
38
+ }
39
+
40
+ export const ReqoreMultiSelectItem = memo(
41
+ ({
42
+ item,
43
+ onRemoveClick,
44
+ onClick,
45
+ selectedItemEffect,
46
+ selectedItemSize,
47
+ }: IReqoreMultiSelectItemProps) => {
48
+ if (!item) {
49
+ return null;
50
+ }
51
+
52
+ return (
53
+ <ReqoreTag
54
+ {...item}
55
+ label={item.label || item.value}
56
+ onRemoveClick={onRemoveClick}
57
+ intent={item.intent}
58
+ effect={!item.intent ? item.effect || selectedItemEffect : undefined}
59
+ size={selectedItemSize}
60
+ onClick={onClick}
61
+ />
62
+ );
63
+ }
64
+ );
65
+
66
+ export const ReqoreMultiSelect = forwardRef<HTMLDivElement, IReqoreMultiSelectProps>(
67
+ (
68
+ {
69
+ value,
70
+ onValueChange,
71
+ onItemClick,
72
+ canRemoveItems,
73
+ canCreateItems,
74
+ items = [],
75
+ selectedItemEffect,
76
+ selectedItemSize,
77
+ selectorProps,
78
+ openOnMount,
79
+ enterKeySelects,
80
+ ...rest
81
+ }: IReqoreMultiSelectProps,
82
+ ref
83
+ ) => {
84
+ const [_value, setValue] = useState<string[]>(value || []);
85
+ const [createdItems, setCreatedItems] = useState<TReqoreMultiSelectItem[]>([]);
86
+ const [query, setQuery] = useState<string>('');
87
+ const popoverData = useRef<IPopoverControls>(undefined);
88
+
89
+ useUpdateEffect(() => {
90
+ onValueChange?.(_value);
91
+ }, [_value]);
92
+
93
+ useEffect(() => {
94
+ setValue(value || []);
95
+ }, [value]);
96
+
97
+ useEffect(() => {
98
+ if (query && !popoverData.current?.isOpen()) {
99
+ popoverData.current?.open();
100
+ }
101
+ }, [query]);
102
+
103
+ useEffect(() => {
104
+ if (enterKeySelects) {
105
+ document.addEventListener('keydown', handleKeyDown);
106
+ }
107
+
108
+ return () => {
109
+ document.removeEventListener('keydown', handleKeyDown);
110
+ };
111
+ });
112
+
113
+ const addRemoveItem = useCallback(
114
+ (item: TReqoreMultiSelectItem): void => {
115
+ if (_value.includes(item.value)) {
116
+ setValue(_value.filter((v) => v !== item.value));
117
+ } else {
118
+ setValue([..._value, item.value]);
119
+ }
120
+ },
121
+ [_value, value]
122
+ );
123
+
124
+ const handleItemSelect = useCallback(
125
+ (item: Partial<TReqoreMultiSelectItem>) => {
126
+ addRemoveItem(item);
127
+
128
+ if (item.isNew) {
129
+ setCreatedItems([
130
+ ...createdItems,
131
+ {
132
+ value: item.value,
133
+ },
134
+ ]);
135
+ }
136
+
137
+ setQuery('');
138
+ },
139
+ [createdItems, _value]
140
+ );
141
+
142
+ /*
143
+ This code creates a list of all items that are available
144
+ for selection in the dropdown. It will include the items
145
+ that are passed in as props, as well as any items that the
146
+ user has created.
147
+ */
148
+ const allItems: TReqoreMultiSelectItem[] = useMemo(() => {
149
+ const customItems: TReqoreMultiSelectItem[] = size(createdItems)
150
+ ? [{ divider: true, label: 'Custom Items' }, ...createdItems]
151
+ : [];
152
+
153
+ let filteredItems: TReqoreMultiSelectItem[] = [...items, ...customItems].filter((item) =>
154
+ query
155
+ ? item.divider
156
+ ? false
157
+ : (item.label || item.value)?.toString().toLowerCase().indexOf(query.toLowerCase()) !==
158
+ -1
159
+ : true
160
+ );
161
+
162
+ // Mark selected items as selected
163
+ filteredItems = filteredItems.map((item: TReqoreMultiSelectItem) => ({
164
+ ...item,
165
+ selected: _value.includes(item.value),
166
+ }));
167
+
168
+ if (query && !size(filteredItems)) {
169
+ filteredItems = [
170
+ { label: 'No existing items found', readOnly: true, minimal: true, icon: 'ForbidLine' },
171
+ ];
172
+ }
173
+
174
+ // If there is a query and there are filtered items
175
+ // and there is no item that exactly matches the query, add it to the list
176
+ if (
177
+ query &&
178
+ !filteredItems.some((item: TReqoreMultiSelectItem) => item.value === query) &&
179
+ canCreateItems
180
+ ) {
181
+ filteredItems = [
182
+ {
183
+ label: `Create new "${query}"`,
184
+ value: query,
185
+ isNew: true,
186
+ icon: 'AddCircleLine',
187
+ minimal: true,
188
+ flat: false,
189
+ effect: {
190
+ gradient: {
191
+ colors: {
192
+ 0: 'success',
193
+ 100: 'success:darken:1',
194
+ },
195
+ },
196
+ },
197
+ },
198
+ { divider: true, label: 'Items matching your query' },
199
+ ...filteredItems,
200
+ ];
201
+ }
202
+
203
+ return filteredItems;
204
+ }, [items, createdItems, query, _value]);
205
+
206
+ const handleKeyDown = useCallback(
207
+ (e: KeyboardEvent) => {
208
+ if (e.key === 'Enter') {
209
+ const item = [...items, ...createdItems].find((item) => item.value === query);
210
+
211
+ if (item) {
212
+ handleItemSelect(item);
213
+ } else if (canCreateItems) {
214
+ handleItemSelect({
215
+ value: query,
216
+ isNew: true,
217
+ });
218
+ }
219
+ }
220
+ },
221
+ [items, createdItems, query, canCreateItems]
222
+ );
223
+
224
+ const getItemByValue = useCallback(
225
+ (value: string): TReqoreMultiSelectItem => {
226
+ return [...items, ...createdItems].find((item) => item.value === value);
227
+ },
228
+ [items, createdItems, _value]
229
+ );
230
+
231
+ return (
232
+ <ReqoreControlGroup vertical fluid {...rest} ref={ref}>
233
+ <ReqoreTagGroup minimal={rest.minimal} size={rest.size}>
234
+ {size(_value) ? (
235
+ _value.map((v) => (
236
+ <ReqoreMultiSelectItem
237
+ key={v}
238
+ item={getItemByValue(v)}
239
+ onRemoveClick={canRemoveItems ? () => addRemoveItem(getItemByValue(v)) : undefined}
240
+ onClick={
241
+ onItemClick
242
+ ? () => {
243
+ onItemClick(getItemByValue(v));
244
+ }
245
+ : undefined
246
+ }
247
+ selectedItemEffect={selectedItemEffect}
248
+ selectedItemSize={selectedItemSize}
249
+ />
250
+ ))
251
+ ) : (
252
+ <ReqoreTag color='transparent' icon='ForbidLine' label='No items selected' />
253
+ )}
254
+ </ReqoreTagGroup>
255
+ <ReqoreControlGroup minimal={rest.minimal} flat={rest.flat} size={rest.size}>
256
+ <ReqoreDropdown<IReqoreInputProps>
257
+ {...selectorProps}
258
+ multiSelect
259
+ handler='focus'
260
+ passPopoverData={(data) => (popoverData.current = data)}
261
+ component={ReqoreInput}
262
+ onClearClick={() => setQuery('')}
263
+ value={query}
264
+ isDefaultOpen={openOnMount}
265
+ onItemSelect={handleItemSelect}
266
+ placeholder={
267
+ canCreateItems ? 'Type to search or create an item...' : 'Type to search...'
268
+ }
269
+ onChange={(e: any) => setQuery(e.target.value)}
270
+ items={
271
+ size(allItems)
272
+ ? allItems
273
+ : canCreateItems
274
+ ? [{ label: 'No items exist', readOnly: true, minimal: true, icon: 'ForbidLine' }]
275
+ : []
276
+ }
277
+ useTargetWidth
278
+ />
279
+ </ReqoreControlGroup>
280
+ </ReqoreControlGroup>
281
+ );
282
+ }
283
+ );
@@ -8,14 +8,14 @@ import {
8
8
  PADDING_FROM_SIZE,
9
9
  RADIUS_FROM_SIZE,
10
10
  TEXT_FROM_SIZE,
11
- TSizes
11
+ TSizes,
12
12
  } from '../../constants/sizes';
13
13
  import { IReqoreTheme } from '../../constants/theme';
14
14
  import {
15
15
  changeDarkness,
16
16
  changeLightness,
17
17
  getMainBackgroundColor,
18
- getReadableColor
18
+ getReadableColor,
19
19
  } from '../../helpers/colors';
20
20
  import { useCombinedRefs } from '../../hooks/useCombinedRefs';
21
21
  import { useReqoreTheme } from '../../hooks/useTheme';
@@ -26,7 +26,7 @@ import {
26
26
  IWithReqoreCustomTheme,
27
27
  IWithReqoreFlat,
28
28
  IWithReqoreSize,
29
- IWithReqoreTooltip
29
+ IWithReqoreTooltip,
30
30
  } from '../../types/global';
31
31
  import { IReqoreIconName } from '../../types/icons';
32
32
  import ReqoreButton, { ButtonBadge, IReqoreButtonProps, TReqoreBadge } from '../Button';
@@ -323,7 +323,9 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
323
323
  (actionOrActions: IReqorePanelAction | IReqorePanelAction[], index: number) => {
324
324
  if (Array.isArray(actionOrActions)) {
325
325
  return (
326
- <ReqoreControlGroup stack>{actionOrActions.map(renderActions)}</ReqoreControlGroup>
326
+ <ReqoreControlGroup key={index} stack>
327
+ {actionOrActions.map(renderActions)}
328
+ </ReqoreControlGroup>
327
329
  );
328
330
  }
329
331
 
@@ -1,6 +1,6 @@
1
- import React, { memo, useState } from 'react';
1
+ import React, { memo, useEffect, useState } from 'react';
2
2
  import styled from 'styled-components';
3
- import usePopover, { IPopoverOptions } from '../../hooks/usePopover';
3
+ import usePopover, { IPopoverControls, IPopoverOptions } from '../../hooks/usePopover';
4
4
  import { IReqoreComponent } from '../../types/global';
5
5
 
6
6
  export interface IReqorePopoverProps extends IReqoreComponent, IPopoverOptions {
@@ -26,13 +26,18 @@ const Popover = memo(
26
26
  noWrapper,
27
27
  wrapperTag = 'span',
28
28
  wrapperStyle = {},
29
+ passPopoverData,
29
30
  _insidePopover,
30
31
  _popoverId,
31
32
  ...rest
32
33
  }: IReqorePopoverProps) => {
33
34
  const [ref, setRef] = useState(undefined);
34
35
 
35
- usePopover({ targetElement: ref, ...rest });
36
+ const popoverData: IPopoverControls = usePopover({ targetElement: ref, ...rest });
37
+
38
+ useEffect(() => {
39
+ passPopoverData?.(popoverData);
40
+ }, [popoverData]);
36
41
 
37
42
  if (isReqoreComponent || noWrapper) {
38
43
  return (
@@ -277,10 +277,10 @@ const ReqoreTableRow = ({
277
277
  onClick: (e: React.MouseEvent<HTMLDivElement>) => {
278
278
  if (onCellClick) {
279
279
  e.stopPropagation();
280
- onCellClick?.(data[index]);
280
+ onCellClick(data[index]);
281
281
  } else if (onRowClick) {
282
282
  e.stopPropagation();
283
- onRowClick?.(data[index]);
283
+ onRowClick(data[index]);
284
284
  } else if (selectable && data[index]._selectId) {
285
285
  // Otherwise select the row if selectable
286
286
  onSelectClick(data[index]._selectId);
@@ -35,7 +35,7 @@ export const StyledTabListItem = styled.div<IReqoreTabListItemStyle>`
35
35
  position: relative;
36
36
  align-items: center;
37
37
  width: ${vertical ? `100%` : undefined};
38
- padding: ${vertical ? `${TABS_PADDING_TO_PX[size!]}px` : `${TABS_PADDING_TO_PX[size!]}px`};
38
+ padding: ${`${TABS_PADDING_TO_PX[size!]}px`};
39
39
  padding-bottom: ${!vertical && 0};
40
40
  padding-right: ${vertical && 0};
41
41
 
@@ -13,9 +13,14 @@ import {
13
13
  } from '../../constants/sizes';
14
14
  import { IReqoreBreadcrumbsTheme, IReqoreCustomTheme, IReqoreTheme } from '../../constants/theme';
15
15
  import ReqoreThemeProvider from '../../containers/ThemeProvider';
16
- import { changeLightness } from '../../helpers/colors';
16
+ import {
17
+ changeLightness,
18
+ getColorFromMaybeString,
19
+ getNthGradientColor,
20
+ } from '../../helpers/colors';
17
21
  import { calculateStringSizeInPixels } from '../../helpers/utils';
18
22
  import { useReqoreTheme } from '../../hooks/useTheme';
23
+ import { TReqoreHexColor } from '../Effect';
19
24
  import ReqoreMenu from '../Menu';
20
25
  import ReqoreMenuItem, { IReqoreMenuItemProps } from '../Menu/item';
21
26
  import { StyledPopover } from '../Popover';
@@ -159,23 +164,18 @@ const ReqoreTabsList = ({
159
164
  intent,
160
165
  ...rest
161
166
  }: IReqoreTabsListProps) => {
162
- const [ref, { width, height }] = useMeasure();
167
+ const [ref, { width }] = useMeasure();
163
168
  const theme = useReqoreTheme('main', customTheme, intent);
164
169
  const activeTabData = tabs.find((tab) => tab.id === activeTab);
165
- const currentTabColor =
170
+ const currentTabColor: TReqoreHexColor =
166
171
  activeTabIntent || activeTabData?.intent
167
172
  ? theme.intents[activeTabIntent || activeTabData.intent]
168
- : activeTabData?.customTheme?.main || theme.main;
173
+ : getNthGradientColor(theme, activeTabData?.effect?.gradient?.colors) ||
174
+ getColorFromMaybeString(theme, activeTabData?.customTheme?.main || theme.main);
169
175
 
170
176
  const transformedItems = vertical
171
177
  ? tabs
172
- : getTransformedItems(
173
- tabs,
174
- vertical ? height : _testWidth || width,
175
- vertical ? 'height' : 'width',
176
- activeTab,
177
- size
178
- );
178
+ : getTransformedItems(tabs, _testWidth || width, 'width', activeTab, size);
179
179
 
180
180
  return (
181
181
  <ReqoreThemeProvider theme={theme}>
@@ -2,10 +2,13 @@ import React from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { StyledTag } from '.';
4
4
  import { GAP_FROM_SIZE, TSizes } from '../../constants/sizes';
5
+ import { IWithReqoreMinimal, IWithReqoreSize } from '../../types/global';
5
6
 
6
- export interface IReqoreTagGroup extends React.HTMLAttributes<HTMLDivElement> {
7
+ export interface IReqoreTagGroup
8
+ extends React.HTMLAttributes<HTMLDivElement>,
9
+ IWithReqoreSize,
10
+ IWithReqoreMinimal {
7
11
  children: any;
8
- size?: TSizes;
9
12
  gapSize?: TSizes;
10
13
  columns?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
11
14
  hasBottomMargin?: boolean;
@@ -27,6 +30,7 @@ const ReqoreTagGroup = ({
27
30
  children,
28
31
  size,
29
32
  gapSize = 'normal',
33
+ minimal,
30
34
  className,
31
35
  columns,
32
36
  hasBottomMargin = true,
@@ -43,6 +47,10 @@ const ReqoreTagGroup = ({
43
47
  ? React.cloneElement(child, {
44
48
  size: child.props?.size || size,
45
49
  width: columns ? `calc(${100 / columns}% - 5px)` : child.props.width,
50
+ minimal:
51
+ child.props?.minimal || child.props?.minimal === false
52
+ ? child.props.minimal
53
+ : minimal,
46
54
  })
47
55
  : null
48
56
  )}
@@ -265,7 +265,7 @@ const ReqoreTag = forwardRef<HTMLSpanElement, IReqoreTagProps>(
265
265
  <StyledTagKeyWrapper
266
266
  size={size}
267
267
  className='reqore-tag-key-content'
268
- onClick={onClick}
268
+ onClick={rest.disabled ? undefined : onClick}
269
269
  wrap={wrap}
270
270
  hasWidth={!!width}
271
271
  hasKey={!!labelKey}
@@ -282,13 +282,13 @@ const ReqoreTag = forwardRef<HTMLSpanElement, IReqoreTagProps>(
282
282
  <StyledTagContentWrapper
283
283
  size={size}
284
284
  className='reqore-tag-content'
285
- onClick={onClick}
285
+ onClick={rest.disabled ? undefined : onClick}
286
286
  wrap={wrap}
287
287
  hasWidth={!!width}
288
288
  hasKey={!!labelKey}
289
289
  >
290
290
  {label || label === 0 ? (
291
- <StyledTagContent size={size} onClick={onClick} wrap={wrap} hasWidth={!!width}>
291
+ <StyledTagContent size={size} wrap={wrap} hasWidth={!!width}>
292
292
  {label}
293
293
  </StyledTagContent>
294
294
  ) : null}
@@ -302,8 +302,8 @@ const ReqoreTag = forwardRef<HTMLSpanElement, IReqoreTagProps>(
302
302
  </StyledTagContentWrapper>
303
303
  ) : null}
304
304
  {_size(actions)
305
- ? actions.map((action) => (
306
- <>
305
+ ? actions.map((action, index) => (
306
+ <React.Fragment key={index}>
307
307
  <ReqorePopover
308
308
  component={StyledButtonWrapper}
309
309
  componentProps={{
@@ -316,13 +316,13 @@ const ReqoreTag = forwardRef<HTMLSpanElement, IReqoreTagProps>(
316
316
  {...(action.tooltip
317
317
  ? typeof action.tooltip === 'string'
318
318
  ? { tooltip: action.tooltip }
319
- : action.tooltip || {}
319
+ : action.tooltip
320
320
  : {})}
321
321
  noWrapper
322
322
  >
323
323
  <ReqoreIcon icon={action.icon} size={size} />
324
324
  </ReqorePopover>
325
- </>
325
+ </React.Fragment>
326
326
  ))
327
327
  : null}
328
328
  {onRemoveClick && !rest.disabled ? (
@@ -106,9 +106,9 @@ export const ReqoreTree = ({
106
106
  <ReqoreControlGroup size={size}>
107
107
  <ReqoreButton
108
108
  className='reqore-tree-toggle'
109
- icon={isObject ? (isExpandable ? 'ArrowDownSFill' : 'ArrowRightSFill') : undefined}
110
- intent={isObject && isExpandable ? 'info' : undefined}
111
- onClick={isObject ? () => handleItemClick(stateKey, isExpandable) : undefined}
109
+ icon={isExpandable ? 'ArrowDownSFill' : 'ArrowRightSFill'}
110
+ intent={isExpandable ? 'info' : undefined}
111
+ onClick={() => handleItemClick(stateKey, isExpandable)}
112
112
  flat
113
113
  >
114
114
  {displayKey}
@@ -3,6 +3,7 @@ import React, { MutableRefObject, useCallback, useEffect, useState } from 'react
3
3
  import Popover from '../components/InternalPopover';
4
4
  import PopoverContext from '../context/PopoverContext';
5
5
  import { IPopoverOptions } from '../hooks/usePopover';
6
+ import { useReqore } from '../hooks/useReqore';
6
7
 
7
8
  export interface IReqorePopoverProviderProps {
8
9
  children: any;
@@ -16,6 +17,7 @@ export interface IPopoverData extends IPopoverOptions {
16
17
 
17
18
  const PopoverProvider: React.FC<IReqorePopoverProviderProps> = ({ children, uiScale }) => {
18
19
  const [popovers, setPopovers] = useState<IPopoverData[]>([]);
20
+ const { closePopoversOnEscPress } = useReqore();
19
21
 
20
22
  const handleClick = useCallback(
21
23
  (event: MouseEvent) => {
@@ -27,8 +29,6 @@ const PopoverProvider: React.FC<IReqorePopoverProviderProps> = ({ children, uiSc
27
29
  !popperRef.current.contains(event.target) &&
28
30
  !targetElement?.contains(event.target as Node))
29
31
  ) {
30
- targetElement.style.position = 'initial';
31
- targetElement.style.zIndex = 'initial';
32
32
  removePopover(id);
33
33
  }
34
34
  });
@@ -36,21 +36,52 @@ const PopoverProvider: React.FC<IReqorePopoverProviderProps> = ({ children, uiSc
36
36
  [popovers]
37
37
  );
38
38
 
39
+ // Close last popover when ESC is pressed
40
+ const handleKeyDown = useCallback(
41
+ (event: KeyboardEvent) => {
42
+ if (event.key === 'Escape') {
43
+ const lastPopover = popovers[popovers.length - 1];
44
+
45
+ if (lastPopover) {
46
+ removePopover(lastPopover.id);
47
+ }
48
+ }
49
+ },
50
+ [popovers]
51
+ );
52
+
39
53
  const removePopover = (id: string) => {
40
- setPopovers((cur: IPopoverData[]) => [...cur].filter((p) => p.id !== id));
54
+ // Get the popover
55
+ const popover = popovers.find((p) => p.id === id);
56
+
57
+ if (popover) {
58
+ // Remove the blur
59
+ if (popover.blur > 0) {
60
+ popover.targetElement.style.position = 'initial';
61
+ popover.targetElement.style.zIndex = 'initial';
62
+ }
63
+ // Remove the popover
64
+ setPopovers((cur: IPopoverData[]) => [...cur].filter((p) => p.id !== id));
65
+ }
41
66
  };
42
67
 
43
68
  useEffect(() => {
44
69
  document.addEventListener('click', handleClick);
45
70
 
71
+ if (closePopoversOnEscPress) {
72
+ document.addEventListener('keydown', handleKeyDown);
73
+ }
74
+
46
75
  return () => {
47
76
  document.removeEventListener('click', handleClick);
77
+ document.removeEventListener('keydown', handleKeyDown);
48
78
  };
49
79
  }, [popovers]);
50
80
 
51
81
  useEffect(() => {
52
82
  if (!size(popovers)) {
53
83
  document.removeEventListener('click', handleClick);
84
+ document.removeEventListener('keydown', handleKeyDown);
54
85
  }
55
86
  }, [popovers]);
56
87
 
@@ -72,6 +103,7 @@ const PopoverProvider: React.FC<IReqorePopoverProviderProps> = ({ children, uiSc
72
103
  }, [])
73
104
  );
74
105
  },
106
+ isPopoverOpen: (popoverId: string) => !!popovers.find((p) => p.id === popoverId),
75
107
  removePopover,
76
108
  popovers,
77
109
  }}
@@ -47,7 +47,7 @@ export interface IReqoreConfirmationModal {
47
47
  intent?: TReqoreIntent;
48
48
  }
49
49
 
50
- const ReqoreProvider: React.FC<IReqoreNotifications> = ({ children, options }) => {
50
+ const ReqoreProvider: React.FC<IReqoreNotifications> = ({ children, options = {} }) => {
51
51
  const [notifications, setNotifications] = useState<IReqoreNotificationData[] | null>([]);
52
52
  const [confirmationModal, setConfirmationModal] = useState<IReqoreConfirmationModal>({});
53
53
  const latestZIndex = useRef<number>(9000);
@@ -121,17 +121,19 @@ const ReqoreProvider: React.FC<IReqoreNotifications> = ({ children, options }) =
121
121
  isTablet,
122
122
  isMobileOrTablet,
123
123
  getAndIncreaseZIndex,
124
- animations: options?.animations || { buttons: true, dialogs: true },
124
+ animations: options.animations || { buttons: true, dialogs: true },
125
+ closePopoversOnEscPress:
126
+ 'closePopoversOnEscPress' in options ? options.closePopoversOnEscPress : true,
125
127
  }}
126
128
  >
127
129
  {size(notifications) > 0 ? (
128
- <ReqoreNotificationsWrapper position={options?.notificationsPosition}>
130
+ <ReqoreNotificationsWrapper position={options.notificationsPosition}>
129
131
  {notifications.map((notification) => (
130
132
  <ReqoreNotification
131
133
  {...notification}
132
134
  key={notification.id}
133
135
  onClick={
134
- notification?.onClick
136
+ notification.onClick
135
137
  ? () => void notification.onClick(notification.id)
136
138
  : undefined
137
139
  }
@@ -18,6 +18,7 @@ export interface IReqoreOptions {
18
18
  buttons?: boolean;
19
19
  dialogs?: boolean;
20
20
  };
21
+ closePopoversOnEscPress?: boolean;
21
22
  }
22
23
  export interface IReqoreUIProviderProps {
23
24
  children?: any;
@@ -5,6 +5,7 @@ export default createContext<{
5
5
  addPopover?: (popoverData: IPopoverData) => void;
6
6
  removePopover?: (popoverId: string) => void;
7
7
  updatePopover?: (popoverId: string, popoverData: Partial<IPopoverData>) => void;
8
+ isPopoverOpen?: (popoverId: string) => boolean;
8
9
  popovers?: IPopoverData[];
9
10
  uiScale?: number;
10
11
  }>({});
@@ -12,6 +12,7 @@ export interface IReqoreContext {
12
12
  readonly isMobileOrTablet?: boolean;
13
13
  readonly getAndIncreaseZIndex?: () => number;
14
14
  readonly animations?: IReqoreOptions['animations'];
15
+ readonly closePopoversOnEscPress?: boolean;
15
16
  }
16
17
 
17
18
  export default createContext<IReqoreContext>({
@@ -23,4 +24,5 @@ export default createContext<IReqoreContext>({
23
24
  buttons: true,
24
25
  dialogs: true,
25
26
  },
27
+ closePopoversOnEscPress: true,
26
28
  });