@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.
- package/.vscode/settings.json +2 -1
- package/__tests__/multiselect.test.tsx +361 -0
- package/__tests__/popover.test.tsx +35 -8
- package/dist/components/Collection/item.js +2 -2
- package/dist/components/Collection/item.js.map +1 -1
- package/dist/components/ControlGroup/index.d.ts +3 -4
- package/dist/components/ControlGroup/index.d.ts.map +1 -1
- package/dist/components/ControlGroup/index.js +7 -9
- package/dist/components/ControlGroup/index.js.map +1 -1
- package/dist/components/Dropdown/index.d.ts +2 -1
- package/dist/components/Dropdown/index.d.ts.map +1 -1
- package/dist/components/Dropdown/index.js +3 -3
- package/dist/components/Dropdown/index.js.map +1 -1
- package/dist/components/Dropdown/list.d.ts +6 -3
- package/dist/components/Dropdown/list.d.ts.map +1 -1
- package/dist/components/Dropdown/list.js +19 -1
- package/dist/components/Dropdown/list.js.map +1 -1
- package/dist/components/Effect/index.d.ts +3 -1
- package/dist/components/Effect/index.d.ts.map +1 -1
- package/dist/components/Effect/index.js +24 -19
- package/dist/components/Effect/index.js.map +1 -1
- package/dist/components/Input/index.js +3 -3
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/InputClearButton/index.d.ts.map +1 -1
- package/dist/components/InputClearButton/index.js +1 -1
- package/dist/components/InputClearButton/index.js.map +1 -1
- package/dist/components/InternalPopover/index.js +3 -3
- package/dist/components/InternalPopover/index.js.map +1 -1
- package/dist/components/Menu/divider.d.ts +3 -2
- package/dist/components/Menu/divider.d.ts.map +1 -1
- package/dist/components/Menu/divider.js +2 -2
- package/dist/components/Menu/divider.js.map +1 -1
- package/dist/components/Menu/index.js +4 -3
- package/dist/components/Menu/index.js.map +1 -1
- package/dist/components/MultiSelect/index.d.ts +32 -0
- package/dist/components/MultiSelect/index.d.ts.map +1 -0
- package/dist/components/MultiSelect/index.js +177 -0
- package/dist/components/MultiSelect/index.js.map +1 -0
- package/dist/components/Panel/index.d.ts.map +1 -1
- package/dist/components/Panel/index.js +1 -1
- package/dist/components/Panel/index.js.map +1 -1
- package/dist/components/Popover/index.d.ts +1 -1
- package/dist/components/Popover/index.d.ts.map +1 -1
- package/dist/components/Popover/index.js +5 -2
- package/dist/components/Popover/index.js.map +1 -1
- package/dist/components/Table/row.js +2 -2
- package/dist/components/Table/row.js.map +1 -1
- package/dist/components/Tabs/item.js +1 -1
- package/dist/components/Tabs/item.js.map +1 -1
- package/dist/components/Tabs/list.d.ts.map +1 -1
- package/dist/components/Tabs/list.js +5 -4
- package/dist/components/Tabs/list.js.map +1 -1
- package/dist/components/Tag/group.d.ts +3 -3
- package/dist/components/Tag/group.d.ts.map +1 -1
- package/dist/components/Tag/group.js +6 -3
- package/dist/components/Tag/group.js.map +1 -1
- package/dist/components/Tag/index.js +5 -5
- package/dist/components/Tag/index.js.map +1 -1
- package/dist/components/Tree/index.js +1 -1
- package/dist/components/Tree/index.js.map +1 -1
- package/dist/containers/PopoverProvider.d.ts.map +1 -1
- package/dist/containers/PopoverProvider.js +28 -3
- package/dist/containers/PopoverProvider.js.map +1 -1
- package/dist/containers/ReqoreProvider.d.ts.map +1 -1
- package/dist/containers/ReqoreProvider.js +6 -5
- package/dist/containers/ReqoreProvider.js.map +1 -1
- package/dist/containers/UIProvider.d.ts +1 -0
- package/dist/containers/UIProvider.d.ts.map +1 -1
- package/dist/containers/UIProvider.js.map +1 -1
- package/dist/context/PopoverContext.d.ts +1 -0
- package/dist/context/PopoverContext.d.ts.map +1 -1
- package/dist/context/PopoverContext.js.map +1 -1
- package/dist/context/ReqoreContext.d.ts +1 -0
- package/dist/context/ReqoreContext.d.ts.map +1 -1
- package/dist/context/ReqoreContext.js +2 -1
- package/dist/context/ReqoreContext.js.map +1 -1
- package/dist/helpers/colors.d.ts +3 -0
- package/dist/helpers/colors.d.ts.map +1 -1
- package/dist/helpers/colors.js +27 -3
- package/dist/helpers/colors.js.map +1 -1
- package/dist/hooks/usePopover.d.ts +8 -1
- package/dist/hooks/usePopover.d.ts.map +1 -1
- package/dist/hooks/usePopover.js +12 -7
- package/dist/hooks/usePopover.js.map +1 -1
- package/dist/hooks/useReqore.d.ts +3 -0
- package/dist/hooks/useReqore.d.ts.map +1 -0
- package/dist/hooks/useReqore.js +17 -0
- package/dist/hooks/useReqore.js.map +1 -0
- package/dist/hooks/useTheme.d.ts +1 -1
- package/dist/hooks/useTheme.d.ts.map +1 -1
- package/dist/hooks/useTheme.js +3 -0
- package/dist/hooks/useTheme.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Collection/item.tsx +2 -2
- package/src/components/ControlGroup/index.tsx +46 -35
- package/src/components/Dropdown/index.tsx +5 -0
- package/src/components/Dropdown/list.tsx +29 -15
- package/src/components/Effect/index.tsx +58 -51
- package/src/components/Input/index.tsx +3 -3
- package/src/components/InputClearButton/index.tsx +1 -0
- package/src/components/InternalPopover/index.tsx +3 -3
- package/src/components/Menu/divider.tsx +15 -4
- package/src/components/Menu/index.tsx +3 -3
- package/src/components/MultiSelect/index.tsx +283 -0
- package/src/components/Panel/index.tsx +6 -4
- package/src/components/Popover/index.tsx +8 -3
- package/src/components/Table/row.tsx +2 -2
- package/src/components/Tabs/item.tsx +1 -1
- package/src/components/Tabs/list.tsx +11 -11
- package/src/components/Tag/group.tsx +10 -2
- package/src/components/Tag/index.tsx +7 -7
- package/src/components/Tree/index.tsx +3 -3
- package/src/containers/PopoverProvider.tsx +35 -3
- package/src/containers/ReqoreProvider.tsx +6 -4
- package/src/containers/UIProvider.tsx +1 -0
- package/src/context/PopoverContext.tsx +1 -0
- package/src/context/ReqoreContext.tsx +2 -0
- package/src/helpers/colors.ts +52 -4
- package/src/hooks/usePopover.tsx +24 -10
- package/src/hooks/useReqore.ts +12 -0
- package/src/hooks/useTheme.ts +6 -1
- package/src/index.tsx +3 -0
- package/src/mock/multiSelect.ts +61 -0
- package/src/stories/Button/Button.stories.tsx +20 -8
- package/src/stories/ControlGroup/ControlGroup.stories.tsx +7 -2
- package/src/stories/Dropdown/Dropdown.stories.tsx +6 -1
- package/src/stories/Input/Input.stories.tsx +25 -0
- package/src/stories/Menu/Menu.stories.tsx +3 -2
- package/src/stories/MultiSelect/MultiSelect.stories.tsx +99 -0
- package/src/stories/Tabs/Tabs.stories.tsx +23 -3
- package/src/stories/Tag/Tag.stories.tsx +8 -1
- 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
|
|
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
|
|
280
|
+
onCellClick(data[index]);
|
|
281
281
|
} else if (onRowClick) {
|
|
282
282
|
e.stopPropagation();
|
|
283
|
-
onRowClick
|
|
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: ${
|
|
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 {
|
|
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
|
|
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?.
|
|
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
|
|
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}
|
|
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={
|
|
110
|
-
intent={
|
|
111
|
-
onClick={
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
136
|
+
notification.onClick
|
|
135
137
|
? () => void notification.onClick(notification.id)
|
|
136
138
|
: undefined
|
|
137
139
|
}
|
|
@@ -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
|
});
|