@qoretechnologies/reqore 0.29.0 → 0.29.1

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 (54) hide show
  1. package/__tests__/checkbox.test.tsx +7 -12
  2. package/__tests__/multiselect.test.tsx +46 -0
  3. package/dist/components/Button/index.js +1 -1
  4. package/dist/components/Button/index.js.map +1 -1
  5. package/dist/components/Checkbox/index.d.ts +7 -2
  6. package/dist/components/Checkbox/index.d.ts.map +1 -1
  7. package/dist/components/Checkbox/index.js +60 -26
  8. package/dist/components/Checkbox/index.js.map +1 -1
  9. package/dist/components/Effect/index.d.ts +12 -1
  10. package/dist/components/Effect/index.d.ts.map +1 -1
  11. package/dist/components/Effect/index.js +39 -7
  12. package/dist/components/Effect/index.js.map +1 -1
  13. package/dist/components/Icon/index.d.ts +2 -9
  14. package/dist/components/Icon/index.d.ts.map +1 -1
  15. package/dist/components/Icon/index.js +4 -28
  16. package/dist/components/Icon/index.js.map +1 -1
  17. package/dist/components/InternalPopover/index.d.ts.map +1 -1
  18. package/dist/components/InternalPopover/index.js +16 -2
  19. package/dist/components/InternalPopover/index.js.map +1 -1
  20. package/dist/components/MultiSelect/index.d.ts +4 -2
  21. package/dist/components/MultiSelect/index.d.ts.map +1 -1
  22. package/dist/components/MultiSelect/index.js +15 -14
  23. package/dist/components/MultiSelect/index.js.map +1 -1
  24. package/dist/components/RadioGroup/index.d.ts +3 -1
  25. package/dist/components/RadioGroup/index.d.ts.map +1 -1
  26. package/dist/components/RadioGroup/index.js +2 -2
  27. package/dist/components/RadioGroup/index.js.map +1 -1
  28. package/dist/components/Tag/index.d.ts +1 -1
  29. package/dist/components/Tag/index.d.ts.map +1 -1
  30. package/dist/components/Tag/index.js +17 -14
  31. package/dist/components/Tag/index.js.map +1 -1
  32. package/dist/components/Textarea/index.js +3 -3
  33. package/dist/components/Textarea/index.js.map +1 -1
  34. package/package.json +1 -1
  35. package/src/components/Button/index.tsx +1 -1
  36. package/src/components/Checkbox/index.tsx +189 -34
  37. package/src/components/Effect/index.tsx +68 -7
  38. package/src/components/Icon/index.tsx +4 -52
  39. package/src/components/InternalPopover/index.tsx +3 -0
  40. package/src/components/MultiSelect/index.tsx +20 -11
  41. package/src/components/RadioGroup/index.tsx +6 -0
  42. package/src/components/Tag/index.tsx +19 -13
  43. package/src/components/Textarea/index.tsx +3 -3
  44. package/src/mock/collectionData.tsx +6 -6
  45. package/src/mock/multiSelect.ts +8 -0
  46. package/src/stories/Checkbox/Checkbox.stories.tsx +42 -7
  47. package/src/stories/Icon/Icon.stories.tsx +42 -12
  48. package/src/stories/Menu/Menu.stories.tsx +2 -1
  49. package/src/stories/MultiSelect/MultiSelect.stories.tsx +9 -2
  50. package/src/stories/Popover/Popover.stories.tsx +19 -2
  51. package/src/stories/RadioGroup/RadioGroup.stories.tsx +51 -1
  52. package/src/stories/Tag/Tag.stories.tsx +2 -2
  53. package/src/stories/utils/args.ts +1 -1
  54. package/tests.json +1 -1
@@ -1,27 +1,40 @@
1
1
  import { rgba } from 'polished';
2
- import React, { forwardRef } from 'react';
2
+ import React, { forwardRef, useMemo } from 'react';
3
+ import { useMeasure } from 'react-use';
3
4
  import styled, { css } from 'styled-components';
4
5
  import { PADDING_FROM_SIZE, SIZE_TO_PX, TEXT_FROM_SIZE, TSizes } from '../../constants/sizes';
5
6
  import { IReqoreTheme } from '../../constants/theme';
6
- import { getReadableColor } from '../../helpers/colors';
7
+ import {
8
+ changeLightness,
9
+ getNthGradientColor,
10
+ getReadableColor,
11
+ getReadableColorFrom,
12
+ } from '../../helpers/colors';
13
+ import { getOneLessSize } from '../../helpers/utils';
7
14
  import { useCombinedRefs } from '../../hooks/useCombinedRefs';
15
+ import { useReqoreTheme } from '../../hooks/useTheme';
8
16
  import { useTooltip } from '../../hooks/useTooltip';
9
17
  import { DisabledElement, ReadOnlyElement } from '../../styles';
10
18
  import {
11
19
  IReqoreDisabled,
20
+ IReqoreIntent,
12
21
  IReqoreReadOnly,
22
+ IWithReqoreCustomTheme,
13
23
  IWithReqoreEffect,
14
24
  TReqoreTooltipProp,
15
25
  } from '../../types/global';
16
26
  import { IReqoreIconName } from '../../types/icons';
17
- import { ReqoreTextEffect } from '../Effect';
18
- import ReqoreIcon from '../Icon';
27
+ import { IReqoreEffect, ReqoreTextEffect, StyledEffect, StyledTextEffect } from '../Effect';
28
+ import ReqoreIcon, { StyledIconWrapper } from '../Icon';
29
+ import { ReqoreSpacer } from '../Spacer';
19
30
 
20
31
  export interface IReqoreCheckboxProps
21
32
  extends React.HTMLAttributes<HTMLDivElement>,
22
33
  IReqoreDisabled,
23
34
  IReqoreReadOnly,
24
- IWithReqoreEffect {
35
+ IWithReqoreEffect,
36
+ IReqoreIntent,
37
+ IWithReqoreCustomTheme {
25
38
  label?: string;
26
39
  labelDetail?: any;
27
40
  labelDetailPosition?: 'left' | 'right';
@@ -35,6 +48,10 @@ export interface IReqoreCheckboxProps
35
48
  uncheckedIcon?: IReqoreIconName;
36
49
  checkedIcon?: IReqoreIconName;
37
50
  image?: string;
51
+ onText?: string | number;
52
+ offText?: string | number;
53
+ switchTextEffect?: IReqoreEffect;
54
+ labelEffect?: IReqoreEffect;
38
55
  }
39
56
 
40
57
  export interface IReqoreCheckboxStyle extends IReqoreCheckboxProps {
@@ -48,38 +65,65 @@ const StyledSwitchToggle = styled.div`
48
65
  align-items: center;
49
66
  justify-content: center;
50
67
  position: absolute;
51
- height: ${({ size }) => SIZE_TO_PX[size] - 13}px;
52
- width: ${({ size }) => SIZE_TO_PX[size] - 13}px;
68
+ height: ${({ size }) => SIZE_TO_PX[size] - 4}px;
69
+ width: ${({ size, width }) => width || SIZE_TO_PX[size] - 4}px;
53
70
  top: 50%;
54
71
  transform: translateY(-50%);
55
- left: ${({ checked, size }) =>
56
- !checked ? '5px' : `${SIZE_TO_PX[size] * 1.8 - (SIZE_TO_PX[size] - 7)}px`};
57
- border-radius: 100%;
58
- background-color: ${({ theme, checked, transparent }) =>
72
+ left: ${({ checked, size, width }) =>
73
+ !checked ? '1px' : `calc(100% - ${width || SIZE_TO_PX[size] - 4}px - 1px)`};
74
+ border-radius: 50px;
75
+ background-color: ${({ theme, checked, transparent, parentEffect }) =>
59
76
  transparent
60
77
  ? 'transparent'
61
78
  : !checked
62
- ? rgba(getReadableColor(theme, undefined, undefined), 0.2)
63
- : getReadableColor(theme, undefined, undefined)};
79
+ ? parentEffect?.gradient
80
+ ? changeLightness(getNthGradientColor(theme, parentEffect?.gradient?.colors, 1), 0.2)
81
+ : theme.main
82
+ : parentEffect?.gradient
83
+ ? changeLightness(getNthGradientColor(theme, parentEffect?.gradient?.colors, 2), 0.2)
84
+ : theme.main};
64
85
  `;
65
86
 
66
- const StyledSwitch = styled.div<IReqoreCheckboxStyle>`
87
+ const StyledSwitch = styled(StyledEffect)<IReqoreCheckboxStyle>`
67
88
  transition: all 0.2s ease-out;
68
89
  position: relative;
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: center;
69
93
 
70
- height: ${({ size }) => SIZE_TO_PX[size] - 5}px;
71
- width: ${({ size }) => SIZE_TO_PX[size] * 1.8}px;
94
+ height: ${({ size }) => SIZE_TO_PX[size]}px;
95
+ min-width: ${({ size }) => SIZE_TO_PX[size] * 1.8}px;
72
96
 
73
- border: 1px solid
74
- ${({ theme, checked }) =>
75
- rgba(getReadableColor(theme, undefined, undefined), checked ? 0.8 : 0.2)};
97
+ border: 1px solid ${({ theme, checked }) => changeLightness(theme.main, checked ? 0.25 : 0.2)};
76
98
  border-radius: 50px;
77
99
 
78
100
  margin-right: ${({ size }) => PADDING_FROM_SIZE[size]}px;
79
101
  margin-left: ${({ size }) => PADDING_FROM_SIZE[size]}px;
80
102
 
81
103
  background-color: ${({ theme, checked }) =>
82
- checked ? rgba(getReadableColor(theme, undefined, undefined), 0.1) : 'transparent'};
104
+ checked ? rgba(changeLightness(theme.main, 0.15), 0.5) : 'transparent'};
105
+
106
+ ${StyledIconWrapper} {
107
+ z-index: 1;
108
+ }
109
+ `;
110
+
111
+ const StyledSwitchTextWrapper = styled(StyledTextEffect)`
112
+ margin: 0 ${({ size }) => PADDING_FROM_SIZE[size]}px;
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ z-index: 1;
117
+ `;
118
+
119
+ const StyledOnSwitchText = styled(StyledSwitchTextWrapper)<IReqoreCheckboxStyle>`
120
+ color: ${({ theme, checked }) =>
121
+ getReadableColorFrom(!checked ? theme.originalMain : theme.main)};
122
+ `;
123
+
124
+ const StyledOffSwitchText = styled(StyledSwitchTextWrapper)<IReqoreCheckboxStyle>`
125
+ color: ${({ theme, checked }) =>
126
+ getReadableColorFrom(!checked ? theme.main : changeLightness(theme.main, 0.1))};
83
127
  `;
84
128
 
85
129
  const StyledCheckbox = styled.div<IReqoreCheckboxStyle>`
@@ -106,14 +150,15 @@ const StyledCheckbox = styled.div<IReqoreCheckboxStyle>`
106
150
  ${ReadOnlyElement};
107
151
  `}
108
152
 
109
- color: ${({ theme, checked }) => getReadableColor(theme, undefined, undefined, !checked)};
153
+ color: ${({ theme, checked }) =>
154
+ getReadableColor(theme, undefined, undefined, !checked, theme.originalMain)};
110
155
 
111
156
  &:hover {
112
- color: ${({ theme }) => getReadableColor(theme, undefined, undefined, false)};
157
+ color: ${({ theme }) =>
158
+ getReadableColor(theme, undefined, undefined, false, theme.originalMain)};
113
159
 
114
160
  > ${StyledSwitch} {
115
- border-color: ${({ theme, checked }) =>
116
- rgba(getReadableColor(theme, undefined, undefined, false), checked ? 0.8 : 0.5)};
161
+ border-color: ${({ theme, checked }) => changeLightness(theme.main, checked ? 0.3 : 0.25)};
117
162
  }
118
163
  }
119
164
  `;
@@ -131,23 +176,45 @@ const Checkbox = forwardRef<HTMLDivElement, IReqoreCheckboxProps>(
131
176
  labelPosition = 'right',
132
177
  tooltip,
133
178
  asSwitch,
134
- uncheckedIcon = 'CheckboxBlankCircleLine',
135
- checkedIcon = 'CheckboxCircleFill',
179
+ uncheckedIcon,
180
+ checkedIcon,
136
181
  readOnly,
137
- effect,
182
+ labelEffect,
183
+ switchTextEffect,
138
184
  image,
185
+ onText,
186
+ offText,
187
+ intent,
188
+ effect,
189
+ customTheme,
139
190
  ...rest
140
191
  }: IReqoreCheckboxProps,
141
192
  ref
142
193
  ) => {
143
194
  const { targetRef } = useCombinedRefs(ref);
195
+ const [offRef, { width: offWidth }] = useMeasure();
196
+ const [onRef, { width: onWidth }] = useMeasure();
197
+ const theme = useReqoreTheme('main', customTheme, intent);
144
198
 
145
199
  useTooltip(targetRef.current, tooltip);
146
200
 
201
+ const width = useMemo(() => {
202
+ if ((!image && !onText) || !offText) return undefined;
203
+
204
+ const selectedWidth = checked ? onWidth : offWidth;
205
+
206
+ return selectedWidth + PADDING_FROM_SIZE[size] * 2;
207
+ }, [checked, offWidth, onWidth, size]);
208
+
209
+ const hasText = useMemo(() => {
210
+ return !!onText || !!offText;
211
+ }, [onText, offText]);
212
+
147
213
  return (
148
214
  <StyledCheckbox
149
215
  {...rest}
150
216
  ref={targetRef}
217
+ theme={theme}
151
218
  size={size}
152
219
  disabled={disabled}
153
220
  checked={checked}
@@ -156,9 +223,11 @@ const Checkbox = forwardRef<HTMLDivElement, IReqoreCheckboxProps>(
156
223
  >
157
224
  {label && labelPosition === 'left' ? (
158
225
  <>
226
+ <ReqoreSpacer width={PADDING_FROM_SIZE[size]} />
159
227
  {labelDetailPosition === 'left' && labelDetail}
160
228
  <ReqoreTextEffect
161
- effect={{ ...effect, interactive: !disabled && !readOnly && !checked }}
229
+ active={checked}
230
+ effect={{ ...labelEffect, interactive: !disabled && !readOnly && !checked }}
162
231
  >
163
232
  {label}
164
233
  </ReqoreTextEffect>
@@ -166,25 +235,111 @@ const Checkbox = forwardRef<HTMLDivElement, IReqoreCheckboxProps>(
166
235
  </>
167
236
  ) : null}
168
237
  {asSwitch ? (
169
- <StyledSwitch size={size} labelPosition={labelPosition} checked={checked}>
170
- <StyledSwitchToggle size={size} checked={checked} transparent={!!image}>
171
- {image ? <ReqoreIcon size={size} image={image} grayscale={!checked} /> : null}
238
+ <StyledSwitch
239
+ size={size}
240
+ labelPosition={labelPosition}
241
+ checked={checked}
242
+ theme={theme}
243
+ as='div'
244
+ effect={effect}
245
+ >
246
+ <>
247
+ {offText || offText === 0 ? (
248
+ <StyledOffSwitchText
249
+ ref={offRef}
250
+ size={size}
251
+ theme={theme}
252
+ checked={checked}
253
+ effect={
254
+ {
255
+ uppercase: true,
256
+ textSize: getOneLessSize(size),
257
+ weight: 'bold',
258
+ ...switchTextEffect,
259
+ opacity: checked ? 0.8 : 1,
260
+ } as IReqoreEffect
261
+ }
262
+ >
263
+ {image || uncheckedIcon ? (
264
+ <ReqoreIcon
265
+ size={size}
266
+ image={image}
267
+ icon={uncheckedIcon}
268
+ effect={{ grayscale: true }}
269
+ margin={offText ? 'right' : undefined}
270
+ />
271
+ ) : null}
272
+ {offText}
273
+ </StyledOffSwitchText>
274
+ ) : null}
275
+ {onText || onText === 0 ? (
276
+ <StyledOnSwitchText
277
+ ref={onRef}
278
+ size={size}
279
+ theme={theme}
280
+ checked={checked}
281
+ effect={
282
+ {
283
+ uppercase: true,
284
+ textSize: getOneLessSize(size),
285
+ weight: 'thick',
286
+ ...switchTextEffect,
287
+ opacity: checked ? 1 : 0.5,
288
+ } as IReqoreEffect
289
+ }
290
+ >
291
+ {image || checkedIcon ? (
292
+ <ReqoreIcon
293
+ size={size}
294
+ image={image}
295
+ margin={onText ? 'right' : undefined}
296
+ icon={checkedIcon}
297
+ />
298
+ ) : null}
299
+ {onText}
300
+ </StyledOnSwitchText>
301
+ ) : null}
302
+ </>
303
+ <StyledSwitchToggle
304
+ size={size}
305
+ checked={checked}
306
+ width={width}
307
+ theme={theme}
308
+ parentEffect={effect}
309
+ transparent={(image || checkedIcon) && !hasText}
310
+ >
311
+ {!onText && !offText ? (
312
+ <ReqoreIcon
313
+ size={size}
314
+ image={image}
315
+ icon={checked ? checkedIcon : uncheckedIcon}
316
+ effect={{ grayscale: !checked, opacity: checked ? 1 : 0.5 }}
317
+ />
318
+ ) : null}
172
319
  </StyledSwitchToggle>
173
320
  </StyledSwitch>
174
321
  ) : (
175
322
  <ReqoreIcon
176
323
  size={size}
177
- icon={!image ? (checked ? checkedIcon : uncheckedIcon) : undefined}
324
+ icon={
325
+ !image
326
+ ? checked
327
+ ? checkedIcon || 'CheckboxCircleFill'
328
+ : uncheckedIcon || 'CheckboxBlankCircleLine'
329
+ : undefined
330
+ }
178
331
  image={image}
179
- grayscale={image ? !checked : undefined}
332
+ effect={{ grayscale: image ? !checked : undefined, opacity: checked ? 1 : 0.5 }}
180
333
  margin='both'
334
+ intent={intent}
181
335
  />
182
336
  )}
183
337
  {label && labelPosition === 'right' ? (
184
338
  <>
185
339
  {labelDetailPosition === 'left' && labelDetail}
186
340
  <ReqoreTextEffect
187
- effect={{ ...effect, interactive: !disabled && !readOnly && !checked }}
341
+ active={checked}
342
+ effect={{ ...labelEffect, interactive: !disabled && !readOnly && !checked }}
188
343
  >
189
344
  {label}
190
345
  </ReqoreTextEffect>
@@ -30,7 +30,18 @@ export type TReqoreEffectColorList = [
30
30
  TReqoreEffectColorManipulationMultiplier | undefined
31
31
  ];
32
32
 
33
- export interface IReqoreEffect {
33
+ export interface IReqoreEffectFilters {
34
+ grayscale?: boolean;
35
+ blur?: number;
36
+ sepia?: boolean;
37
+ invert?: boolean;
38
+ opacity?: number;
39
+ brightness?: number;
40
+ contrast?: number;
41
+ saturate?: number;
42
+ }
43
+
44
+ export interface IReqoreEffect extends IReqoreEffectFilters {
34
45
  gradient?: {
35
46
  type?: 'linear' | 'radial';
36
47
  shape?: 'circle' | 'ellipse';
@@ -63,13 +74,14 @@ export interface IReqoreTextEffectProps
63
74
  as?: React.ElementType;
64
75
  theme?: IReqoreTheme;
65
76
  active?: boolean;
77
+ isText?: boolean;
66
78
  }
67
79
 
68
80
  export const StyledEffect = styled.span`
69
81
  transition: all 0.2s ease-in-out;
70
82
 
71
83
  // If gradient was supplied
72
- ${({ effect, theme, minimal, active }: IReqoreTextEffectProps) => {
84
+ ${({ effect, theme, minimal, active, isText }: IReqoreTextEffectProps) => {
73
85
  if (!effect || !effect.gradient) {
74
86
  return undefined;
75
87
  }
@@ -79,7 +91,7 @@ export const StyledEffect = styled.span`
79
91
  theme,
80
92
  effect.gradient.colors,
81
93
  0,
82
- minimal && !active
94
+ !isText && minimal && !active
83
95
  );
84
96
  const gradientColorsActive = createEffectGradient(theme, effect.gradient.colors, 0.05);
85
97
 
@@ -107,7 +119,7 @@ export const StyledEffect = styled.span`
107
119
  getColorFromMaybeString(theme, effect.gradient.borderColor),
108
120
  0.15
109
121
  )}`;
110
- } else {
122
+ } else if (!isText) {
111
123
  // We will use border-image and create a gradient border
112
124
  borderColor = 'transparent';
113
125
  borderHoverColor = 'transparent';
@@ -231,16 +243,65 @@ export const StyledEffect = styled.span`
231
243
  text-align: ${effect.textAlign};
232
244
  `
233
245
  : undefined}
246
+
247
+ ${({ effect }) =>
248
+ effect &&
249
+ effect.grayscale &&
250
+ css`
251
+ filter: grayscale(100%);
252
+ `};
253
+ ${({ effect }) =>
254
+ effect &&
255
+ effect.blur &&
256
+ css`
257
+ filter: blur(${effect.blur}px);
258
+ `};
259
+ ${({ effect }) =>
260
+ effect &&
261
+ effect.sepia &&
262
+ css`
263
+ filter: sepia(100%);
264
+ `};
265
+ ${({ effect }) =>
266
+ effect &&
267
+ effect.invert &&
268
+ css`
269
+ filter: invert(100%);
270
+ `};
271
+ ${({ effect }) =>
272
+ effect && (effect.opacity || effect.opacity === 0)
273
+ ? css`
274
+ opacity: ${effect.opacity};
275
+ `
276
+ : undefined};
277
+ ${({ effect }) =>
278
+ effect &&
279
+ effect.brightness &&
280
+ css`
281
+ filter: brightness(${effect.brightness}%);
282
+ `};
283
+ ${({ effect }) =>
284
+ effect &&
285
+ effect.contrast &&
286
+ css`
287
+ filter: contrast(${effect.contrast}%);
288
+ `};
289
+ ${({ effect }) =>
290
+ effect &&
291
+ effect.saturate &&
292
+ css`
293
+ filter: saturate(${effect.saturate}%);
294
+ `};
234
295
  `;
235
296
 
236
- export const StyledTextEffect = styled(StyledEffect)`
297
+ export const StyledTextEffect = styled(StyledEffect).attrs((props) => ({ ...props, isText: true }))`
237
298
  display: inline-block;
238
299
 
239
300
  ${({ effect }: IReqoreTextEffectProps) =>
240
301
  effect && effect.gradient
241
302
  ? css`
242
- -webkit-background-clip: text;
243
- -webkit-text-fill-color: transparent;
303
+ -webkit-background-clip: text !important;
304
+ -webkit-text-fill-color: transparent !important;
244
305
  `
245
306
  : undefined}
246
307
  `;
@@ -7,9 +7,11 @@ import { ReqoreThemeContext } from '../..';
7
7
  import { ICON_FROM_SIZE, PADDING_FROM_SIZE, TSizes } from '../../constants/sizes';
8
8
  import { TReqoreIntent } from '../../constants/theme';
9
9
  import { isStringSize } from '../../helpers/utils';
10
+ import { IWithReqoreEffect } from '../../types/global';
10
11
  import { IReqoreIconName } from '../../types/icons';
12
+ import { StyledEffect } from '../Effect';
11
13
 
12
- export interface IReqoreIconProps extends React.HTMLAttributes<HTMLSpanElement> {
14
+ export interface IReqoreIconProps extends React.HTMLAttributes<HTMLSpanElement>, IWithReqoreEffect {
13
15
  icon?: IReqoreIconName;
14
16
  color?: string;
15
17
  size?: TSizes | string;
@@ -18,17 +20,9 @@ export interface IReqoreIconProps extends React.HTMLAttributes<HTMLSpanElement>
18
20
  margin?: 'right' | 'left' | 'both';
19
21
  image?: string;
20
22
  rounded?: boolean;
21
- grayscale?: boolean;
22
- blur?: number;
23
- sepia?: boolean;
24
- invert?: boolean;
25
- opacity?: number;
26
- brightness?: number;
27
- contrast?: number;
28
- saturate?: number;
29
23
  }
30
24
 
31
- export const StyledIconWrapper = styled.span<{ margin: 'right' | 'left' | 'both' }>`
25
+ export const StyledIconWrapper = styled(StyledEffect)<{ margin: 'right' | 'left' | 'both' }>`
32
26
  display: inline-block;
33
27
  flex: 0 0 auto;
34
28
  vertical-align: text-bottom;
@@ -37,48 +31,6 @@ export const StyledIconWrapper = styled.span<{ margin: 'right' | 'left' | 'both'
37
31
 
38
32
  border-radius: ${({ rounded }) => (rounded ? '50%' : undefined)};
39
33
 
40
- ${({ grayscale }) =>
41
- grayscale &&
42
- css`
43
- filter: grayscale(100%);
44
- `};
45
- ${({ blur }) =>
46
- blur &&
47
- css`
48
- filter: blur(${blur}px);
49
- `};
50
- ${({ sepia }) =>
51
- sepia &&
52
- css`
53
- filter: sepia(100%);
54
- `};
55
- ${({ invert }) =>
56
- invert &&
57
- css`
58
- filter: invert(100%);
59
- `};
60
- ${({ opacity }) =>
61
- opacity || opacity === 0
62
- ? css`
63
- opacity: ${opacity};
64
- `
65
- : undefined};
66
- ${({ brightness }) =>
67
- brightness &&
68
- css`
69
- filter: brightness(${brightness}%);
70
- `};
71
- ${({ contrast }) =>
72
- contrast &&
73
- css`
74
- filter: contrast(${contrast}%);
75
- `};
76
- ${({ saturate }) =>
77
- saturate &&
78
- css`
79
- filter: saturate(${saturate}%);
80
- `};
81
-
82
34
  ${({ margin, size }) =>
83
35
  margin &&
84
36
  css`
@@ -62,6 +62,8 @@ const StyledPopoverWrapper = styled.div<{ theme: IReqoreTheme }>`
62
62
  max-height: ${({ maxHeight = '80vh' }) => maxHeight};
63
63
  z-index: 999999;
64
64
  border-radius: ${RADIUS_FROM_SIZE.normal}px;
65
+ border: ${({ flat, isString, ...rest }: any) =>
66
+ !flat && !isString ? `1px solid ${getPopoverArrowColor({ ...rest, flat })}` : undefined};
65
67
 
66
68
  ${({ transparent }) =>
67
69
  !transparent &&
@@ -217,6 +219,7 @@ const InternalPopover: React.FC<IReqoreInternalPopoverProps> = ({
217
219
  isOpaque={!transparent && !minimal}
218
220
  intent={intent}
219
221
  flat={flat}
222
+ isString={isString(content)}
220
223
  dim={flat && !transparent && !effect && minimal}
221
224
  className='reqore-popover-content'
222
225
  ref={(el) => {
@@ -1,4 +1,4 @@
1
- import { size } from 'lodash';
1
+ import { omit, size } from 'lodash';
2
2
  import { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { useUpdateEffect } from 'react-use';
4
4
  import { ReqoreDropdown, ReqoreInput } from '../..';
@@ -13,7 +13,7 @@ import ReqoreTag, { IReqoreTagProps } from '../Tag';
13
13
  import ReqoreTagGroup from '../Tag/group';
14
14
 
15
15
  export type TReqoreMultiSelectItem = Omit<IReqoreDropdownItem, 'color'> &
16
- Pick<IReqoreTagProps, 'badge'> & { isNew?: boolean };
16
+ Pick<IReqoreTagProps, 'asBadge' | 'rightIcon' | 'actions'> & { isNew?: boolean };
17
17
 
18
18
  export interface IReqoreMultiSelectProps
19
19
  extends Omit<IReqoreControlGroupProps, 'children' | 'vertical' | 'stack'> {
@@ -21,6 +21,7 @@ export interface IReqoreMultiSelectProps
21
21
  onValueChange?: (value: string[]) => void;
22
22
  items?: TReqoreMultiSelectItem[];
23
23
  onItemClick?: (item: IReqoreDropdownItem) => void;
24
+ onItemClickIcon?: IReqoreTagProps['rightIcon'];
24
25
  canRemoveItems?: boolean;
25
26
  canCreateItems?: boolean;
26
27
  selectedItemEffect?: IReqoreEffect;
@@ -35,6 +36,7 @@ export interface IReqoreMultiSelectItemProps
35
36
  item: TReqoreMultiSelectItem;
36
37
  onClick?: () => void;
37
38
  onRemoveClick?: () => void;
39
+ onItemClickIcon?: IReqoreTagProps['rightIcon'];
38
40
  }
39
41
 
40
42
  export const ReqoreMultiSelectItem = memo(
@@ -44,6 +46,7 @@ export const ReqoreMultiSelectItem = memo(
44
46
  onClick,
45
47
  selectedItemEffect,
46
48
  selectedItemSize,
49
+ onItemClickIcon,
47
50
  }: IReqoreMultiSelectItemProps) => {
48
51
  if (!item) {
49
52
  return null;
@@ -58,6 +61,7 @@ export const ReqoreMultiSelectItem = memo(
58
61
  effect={!item.intent ? item.effect || selectedItemEffect : undefined}
59
62
  size={selectedItemSize}
60
63
  onClick={onClick}
64
+ rightIcon={item.rightIcon || item.disabled ? undefined : onItemClickIcon}
61
65
  />
62
66
  );
63
67
  }
@@ -77,6 +81,7 @@ export const ReqoreMultiSelect = forwardRef<HTMLDivElement, IReqoreMultiSelectPr
77
81
  selectorProps,
78
82
  openOnMount,
79
83
  enterKeySelects,
84
+ onItemClickIcon,
80
85
  ...rest
81
86
  }: IReqoreMultiSelectProps,
82
87
  ref
@@ -85,6 +90,7 @@ export const ReqoreMultiSelect = forwardRef<HTMLDivElement, IReqoreMultiSelectPr
85
90
  const [createdItems, setCreatedItems] = useState<TReqoreMultiSelectItem[]>([]);
86
91
  const [query, setQuery] = useState<string>('');
87
92
  const popoverData = useRef<IPopoverControls>(undefined);
93
+ const [focused, setFocused] = useState<boolean>(false);
88
94
 
89
95
  useUpdateEffect(() => {
90
96
  onValueChange?.(_value);
@@ -140,11 +146,11 @@ export const ReqoreMultiSelect = forwardRef<HTMLDivElement, IReqoreMultiSelectPr
140
146
  );
141
147
 
142
148
  /*
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
- */
149
+ This code creates a list of all items that are available
150
+ for selection in the dropdown. It will include the items
151
+ that are passed in as props, as well as any items that the
152
+ user has created.
153
+ */
148
154
  const allItems: TReqoreMultiSelectItem[] = useMemo(() => {
149
155
  const customItems: TReqoreMultiSelectItem[] = size(createdItems)
150
156
  ? [{ divider: true, label: 'Custom Items' }, ...createdItems]
@@ -161,7 +167,7 @@ export const ReqoreMultiSelect = forwardRef<HTMLDivElement, IReqoreMultiSelectPr
161
167
 
162
168
  // Mark selected items as selected
163
169
  filteredItems = filteredItems.map((item: TReqoreMultiSelectItem) => ({
164
- ...item,
170
+ ...omit(item, ['actions', 'asBadge', 'rightIcon']),
165
171
  selected: _value.includes(item.value),
166
172
  }));
167
173
 
@@ -205,12 +211,12 @@ export const ReqoreMultiSelect = forwardRef<HTMLDivElement, IReqoreMultiSelectPr
205
211
 
206
212
  const handleKeyDown = useCallback(
207
213
  (e: KeyboardEvent) => {
208
- if (e.key === 'Enter') {
214
+ if (e.key === 'Enter' && focused) {
209
215
  const item = [...items, ...createdItems].find((item) => item.value === query);
210
216
 
211
217
  if (item) {
212
218
  handleItemSelect(item);
213
- } else if (canCreateItems) {
219
+ } else if (canCreateItems && query) {
214
220
  handleItemSelect({
215
221
  value: query,
216
222
  isNew: true,
@@ -218,7 +224,7 @@ export const ReqoreMultiSelect = forwardRef<HTMLDivElement, IReqoreMultiSelectPr
218
224
  }
219
225
  }
220
226
  },
221
- [items, createdItems, query, canCreateItems]
227
+ [items, createdItems, query, canCreateItems, focused]
222
228
  );
223
229
 
224
230
  const getItemByValue = useCallback(
@@ -236,6 +242,7 @@ export const ReqoreMultiSelect = forwardRef<HTMLDivElement, IReqoreMultiSelectPr
236
242
  <ReqoreMultiSelectItem
237
243
  key={v}
238
244
  item={getItemByValue(v)}
245
+ onItemClickIcon={onItemClickIcon}
239
246
  onRemoveClick={canRemoveItems ? () => addRemoveItem(getItemByValue(v)) : undefined}
240
247
  onClick={
241
248
  onItemClick
@@ -257,6 +264,8 @@ export const ReqoreMultiSelect = forwardRef<HTMLDivElement, IReqoreMultiSelectPr
257
264
  {...selectorProps}
258
265
  multiSelect
259
266
  handler='focus'
267
+ onFocus={() => setFocused(true)}
268
+ onBlur={() => setFocused(false)}
260
269
  passPopoverData={(data) => (popoverData.current = data)}
261
270
  component={ReqoreInput}
262
271
  onClearClick={() => setQuery('')}