@qoretechnologies/reqore 0.31.1 → 0.32.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 (65) hide show
  1. package/__tests__/collection.test.tsx +5 -1
  2. package/__tests__/input.test.tsx +417 -24
  3. package/__tests__/multiselect.test.tsx +8 -10
  4. package/__tests__/textarea.test.tsx +405 -24
  5. package/dist/components/Button/index.d.ts.map +1 -1
  6. package/dist/components/Button/index.js +1 -1
  7. package/dist/components/Button/index.js.map +1 -1
  8. package/dist/components/Collection/index.d.ts +5 -2
  9. package/dist/components/Collection/index.d.ts.map +1 -1
  10. package/dist/components/Collection/index.js +10 -5
  11. package/dist/components/Collection/index.js.map +1 -1
  12. package/dist/components/Collection/item.d.ts.map +1 -1
  13. package/dist/components/Collection/item.js +1 -1
  14. package/dist/components/Collection/item.js.map +1 -1
  15. package/dist/components/ControlGroup/index.d.ts.map +1 -1
  16. package/dist/components/ControlGroup/index.js +17 -56
  17. package/dist/components/ControlGroup/index.js.map +1 -1
  18. package/dist/components/Icon/index.d.ts +2 -0
  19. package/dist/components/Icon/index.d.ts.map +1 -1
  20. package/dist/components/Icon/index.js +10 -5
  21. package/dist/components/Icon/index.js.map +1 -1
  22. package/dist/components/Input/index.d.ts +4 -0
  23. package/dist/components/Input/index.d.ts.map +1 -1
  24. package/dist/components/Input/index.js +18 -6
  25. package/dist/components/Input/index.js.map +1 -1
  26. package/dist/components/InputClearButton/index.d.ts +1 -0
  27. package/dist/components/InputClearButton/index.d.ts.map +1 -1
  28. package/dist/components/InputClearButton/index.js +18 -8
  29. package/dist/components/InputClearButton/index.js.map +1 -1
  30. package/dist/components/Panel/index.d.ts +4 -2
  31. package/dist/components/Panel/index.d.ts.map +1 -1
  32. package/dist/components/Panel/index.js +18 -14
  33. package/dist/components/Panel/index.js.map +1 -1
  34. package/dist/components/Textarea/index.d.ts +2 -0
  35. package/dist/components/Textarea/index.d.ts.map +1 -1
  36. package/dist/components/Textarea/index.js +4 -2
  37. package/dist/components/Textarea/index.js.map +1 -1
  38. package/dist/hooks/useAutoFocus.d.ts +11 -0
  39. package/dist/hooks/useAutoFocus.d.ts.map +1 -0
  40. package/dist/hooks/useAutoFocus.js +80 -0
  41. package/dist/hooks/useAutoFocus.js.map +1 -0
  42. package/dist/styles.d.ts.map +1 -1
  43. package/dist/styles.js +1 -1
  44. package/dist/styles.js.map +1 -1
  45. package/dist/types/global.d.ts +11 -0
  46. package/dist/types/global.d.ts.map +1 -1
  47. package/package.json +3 -1
  48. package/src/components/Button/index.tsx +1 -0
  49. package/src/components/Collection/index.tsx +34 -7
  50. package/src/components/Collection/item.tsx +1 -0
  51. package/src/components/ControlGroup/index.tsx +25 -86
  52. package/src/components/Icon/index.tsx +18 -4
  53. package/src/components/Input/index.tsx +38 -3
  54. package/src/components/InputClearButton/index.tsx +41 -17
  55. package/src/components/Panel/index.tsx +34 -26
  56. package/src/components/Textarea/index.tsx +15 -8
  57. package/src/hooks/useAutoFocus.ts +104 -0
  58. package/src/mock/collectionData.tsx +1 -0
  59. package/src/stories/Collection/Collection.stories.tsx +84 -10
  60. package/src/stories/Input/Input.stories.tsx +29 -6
  61. package/src/stories/Panel/Panel.stories.tsx +20 -2
  62. package/src/stories/TextArea/TextArea.stories.tsx +1 -0
  63. package/src/styles.ts +0 -1
  64. package/src/types/global.ts +12 -0
  65. package/tests.json +1 -1
@@ -2,11 +2,13 @@ import { size } from 'lodash';
2
2
  import { useMemo, useState } from 'react';
3
3
  import { useUpdateEffect } from 'react-use';
4
4
  import styled, { css } from 'styled-components';
5
+ import { PADDING_FROM_SIZE } from '../../constants/sizes';
5
6
  import { IReqoreIconName } from '../../types/icons';
6
7
  import { IReqoreColumnsProps, StyledColumns } from '../Columns';
7
8
  import ReqoreInput, { IReqoreInputProps } from '../Input';
8
9
  import ReqoreMessage from '../Message';
9
10
  import { IReqorePanelAction, IReqorePanelProps, ReqorePanel, TReqorePanelActions } from '../Panel';
11
+ import { ReqoreVerticalSpacer } from '../Spacer';
10
12
  import { sortTableData } from '../Table/helpers';
11
13
  import { IReqoreCollectionItemProps, ReqoreCollectionItem } from './item';
12
14
 
@@ -26,6 +28,8 @@ export interface IReqoreCollectionProps
26
28
  | 'minimal'
27
29
  | 'transparent'
28
30
  | 'fluid'
31
+ | 'responsiveActions'
32
+ | 'padded'
29
33
  >,
30
34
  IReqoreColumnsProps {
31
35
  items?: IReqoreCollectionItemProps[];
@@ -43,6 +47,11 @@ export interface IReqoreCollectionProps
43
47
  showSelectedFirst?: boolean;
44
48
  selectedIcon?: IReqoreIconName;
45
49
 
50
+ childrenBefore?: React.ReactNode;
51
+ childrenAfter?: React.ReactNode;
52
+
53
+ onQueryChange?: (query: string) => void;
54
+
46
55
  emptyMessage?: string;
47
56
  sortButtonTooltip?: (sort?: 'asc' | 'desc') => string;
48
57
  displayButtonTooltip?: (display?: 'list' | 'grid') => string;
@@ -50,7 +59,7 @@ export interface IReqoreCollectionProps
50
59
  }
51
60
 
52
61
  export const StyledCollectionWrapper = styled(StyledColumns)`
53
- height: ${({ height }) => (height ? `${height}px` : 'auto')};
62
+ height: ${({ height }) => (height ? `${height}` : 'auto')};
54
63
 
55
64
  ${({ rounded, stacked }: IReqoreCollectionProps) =>
56
65
  (!rounded || stacked) &&
@@ -58,7 +67,7 @@ export const StyledCollectionWrapper = styled(StyledColumns)`
58
67
  border-radius: ${stacked && rounded ? '10px' : undefined};
59
68
  `}
60
69
 
61
- overflow: hidden;
70
+ overflow: auto;
62
71
  position: relative;
63
72
  `;
64
73
 
@@ -68,7 +77,6 @@ export const ReqoreCollection = ({
68
77
  stacked,
69
78
  rounded = true,
70
79
  fill,
71
- height,
72
80
  maxItemHeight,
73
81
  filterable,
74
82
  sortable,
@@ -82,11 +90,13 @@ export const ReqoreCollection = ({
82
90
  flat = true,
83
91
  minimal,
84
92
  transparent = true,
85
-
93
+ onQueryChange,
94
+ childrenBefore,
95
+ childrenAfter,
86
96
  emptyMessage = 'No data in this collection, try changing your search query or filters',
87
97
  sortButtonTooltip = (sort) => (sort === 'desc' ? 'Sort ascending' : 'Sort descending'),
88
98
  displayButtonTooltip = (display) => (display === 'grid' ? 'Show as list' : 'Show as grid'),
89
- inputPlaceholder = (items) => `Filter ${size(items)} items`,
99
+ inputPlaceholder = (items) => `Search in ${size(items)} items`,
90
100
  ...rest
91
101
  }: IReqoreCollectionProps) => {
92
102
  const [_showAs, setShowAs] = useState<'list' | 'grid'>(showAs);
@@ -97,6 +107,10 @@ export const ReqoreCollection = ({
97
107
  setShowAs(showAs);
98
108
  }, [showAs]);
99
109
 
110
+ useUpdateEffect(() => {
111
+ onQueryChange?.(query);
112
+ }, [query]);
113
+
100
114
  const sortedItems: IReqoreCollectionItemProps[] = useMemo(() => {
101
115
  if (!sortable) {
102
116
  return items;
@@ -182,6 +196,8 @@ export const ReqoreCollection = ({
182
196
  {
183
197
  as: ReqoreInput,
184
198
  props: {
199
+ key: 'search',
200
+ fixed: false,
185
201
  placeholder: inputPlaceholder(finalItems),
186
202
  onClearClick: () => setQuery(''),
187
203
  onChange: handleQueryChange,
@@ -203,16 +219,20 @@ export const ReqoreCollection = ({
203
219
  headerSize={headerSize}
204
220
  style={{
205
221
  ...rest.style,
206
- height: height ?? undefined,
207
222
  }}
208
223
  fill={fill}
209
- padded
210
224
  transparent={transparent}
211
225
  minimal={minimal}
212
226
  flat={flat}
213
227
  actions={finalActions}
214
228
  className={`reqore-collection ${rest.className || ''}`}
215
229
  >
230
+ {childrenBefore ? (
231
+ <>
232
+ {childrenBefore}
233
+ <ReqoreVerticalSpacer height={PADDING_FROM_SIZE[rest.size || 'normal']} />
234
+ </>
235
+ ) : null}
216
236
  {!size(finalItems) ? (
217
237
  <ReqoreMessage flat icon='Search2Line'>
218
238
  {emptyMessage}
@@ -227,6 +247,7 @@ export const ReqoreCollection = ({
227
247
  >
228
248
  {finalItems?.map((item, index) => (
229
249
  <ReqoreCollectionItem
250
+ size={rest.size}
230
251
  {...item}
231
252
  icon={item.icon || (item.selected ? selectedIcon : undefined)}
232
253
  key={index}
@@ -236,6 +257,12 @@ export const ReqoreCollection = ({
236
257
  ))}
237
258
  </StyledCollectionWrapper>
238
259
  )}
260
+ {childrenAfter ? (
261
+ <>
262
+ <ReqoreVerticalSpacer height={PADDING_FROM_SIZE[rest.size || 'normal']} />
263
+ {childrenAfter}
264
+ </>
265
+ ) : null}
239
266
  </ReqorePanel>
240
267
  );
241
268
  };
@@ -206,6 +206,7 @@ export const ReqoreCollectionItem = ({
206
206
  ref={ref}
207
207
  minimal={minimal}
208
208
  {...rest}
209
+ responsiveActions={false}
209
210
  bottomActions={isSelected ? expandedActions : undefined}
210
211
  style={{
211
212
  ...style,
@@ -1,12 +1,5 @@
1
1
  import { debounce } from 'lodash';
2
- import React, {
3
- memo,
4
- useCallback,
5
- useEffect,
6
- useMemo,
7
- useRef,
8
- useState,
9
- } from 'react';
2
+ import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
10
3
  import { useMount, useUnmount } from 'react-use';
11
4
  import styled, { css } from 'styled-components';
12
5
  import { ReqoreButton, ReqoreDrawer } from '../..';
@@ -63,16 +56,13 @@ export interface IReqoreControlGroupStyle extends IReqoreControlGroupProps {
63
56
 
64
57
  export const StyledReqoreControlGroup = styled.div<IReqoreControlGroupStyle>`
65
58
  display: flex;
66
- flex: ${({ fluid, fixed }) =>
67
- fixed ? '0 0 auto' : fluid ? undefined : '0 0 auto'};
59
+ flex: ${({ fluid, fixed }) => (fixed ? '0 0 auto' : fluid ? undefined : '0 0 auto')};
68
60
  width: ${({ fluid, fixed }) => (fluid && !fixed ? '100%' : undefined)};
69
- justify-content: ${({ fluid, vertical }) =>
70
- vertical && fluid ? 'stretch' : undefined};
61
+ justify-content: ${({ fluid, vertical }) => (vertical && fluid ? 'stretch' : undefined)};
71
62
  align-items: ${({ vertical, fluid, horizontalAlign }) =>
72
63
  vertical ? (fluid ? 'stretch' : horizontalAlign) : undefined};
73
64
  flex-flow: ${({ vertical }) => (vertical ? 'column' : 'row')};
74
- gap: ${({ gapSize, stack }) =>
75
- !stack ? `${GAP_FROM_SIZE[gapSize]}px` : undefined};
65
+ gap: ${({ gapSize, stack }) => (!stack ? `${GAP_FROM_SIZE[gapSize]}px` : undefined)};
76
66
  flex-wrap: ${({ wrap }) => (wrap ? 'wrap' : 'nowrap')};
77
67
 
78
68
  // If the group has the fill prop,
@@ -95,23 +85,16 @@ export const StyledReqoreControlGroup = styled.div<IReqoreControlGroupStyle>`
95
85
 
96
86
  > * {
97
87
  margin-top: ${({ fill, verticalAlign }) =>
98
- !fill && (verticalAlign === 'flex-end' || verticalAlign === 'center')
99
- ? 'auto'
100
- : undefined};
88
+ !fill && (verticalAlign === 'flex-end' || verticalAlign === 'center') ? 'auto' : undefined};
101
89
  margin-bottom: ${({ fill, verticalAlign }) =>
102
- !fill && (verticalAlign === 'flex-start' || verticalAlign === 'center')
103
- ? 'auto'
104
- : undefined};
105
- margin-right: ${({ horizontalAlign }) =>
106
- horizontalAlign === 'center' ? 'auto' : undefined};
90
+ !fill && (verticalAlign === 'flex-start' || verticalAlign === 'center') ? 'auto' : undefined};
91
+ margin-right: ${({ horizontalAlign }) => (horizontalAlign === 'center' ? 'auto' : undefined)};
107
92
 
108
93
  ${({ vertical }) =>
109
94
  vertical &&
110
95
  css`
111
96
  margin-left: ${({ horizontalAlign }) =>
112
- horizontalAlign === 'flex-end' || horizontalAlign === 'center'
113
- ? 'auto'
114
- : undefined};
97
+ horizontalAlign === 'flex-end' || horizontalAlign === 'center' ? 'auto' : undefined};
115
98
  `}
116
99
  }
117
100
 
@@ -120,9 +103,7 @@ export const StyledReqoreControlGroup = styled.div<IReqoreControlGroupStyle>`
120
103
  css`
121
104
  > *:first-child {
122
105
  margin-left: ${({ horizontalAlign }) =>
123
- horizontalAlign === 'flex-end' || horizontalAlign === 'center'
124
- ? 'auto'
125
- : undefined};
106
+ horizontalAlign === 'flex-end' || horizontalAlign === 'center' ? 'auto' : undefined};
126
107
  }
127
108
  `}
128
109
 
@@ -168,12 +149,9 @@ const ReqoreControlGroup = memo(
168
149
  const isStack = stack || isInsideStackGroup;
169
150
  const isVertical = vertical || isInsideVerticalGroup;
170
151
 
171
- const [overflowingChildren, setOverflowingChildren] = useState<
172
- number | undefined
173
- >(0);
152
+ const [overflowingChildren, setOverflowingChildren] = useState<number | undefined>(0);
174
153
  const [lastReSize, setLastReSize] = useState<number>(0);
175
- const [isOverflownDialogOpen, setIsOverflownDialogOpen] =
176
- useState<boolean>(false);
154
+ const [isOverflownDialogOpen, setIsOverflownDialogOpen] = useState<boolean>(false);
177
155
  const observer = useRef<ResizeObserver | null>(null);
178
156
  const ref = useRef<HTMLDivElement>(null);
179
157
 
@@ -232,10 +210,7 @@ const ReqoreControlGroup = memo(
232
210
 
233
211
  useEffect(() => {
234
212
  // Is the control group still overflowing?
235
- if (
236
- (overflowingChildren || overflowingChildren === 0) &&
237
- checkIfOverflowing()
238
- ) {
213
+ if ((overflowingChildren || overflowingChildren === 0) && checkIfOverflowing()) {
239
214
  setOverflowingChildren(overflowingChildren + 1);
240
215
  }
241
216
 
@@ -258,33 +233,22 @@ const ReqoreControlGroup = memo(
258
233
  const getIsLastInFirstGroup = (index: number): boolean => {
259
234
  return !isInsideStackGroup
260
235
  ? index === 0
261
- : isFirstGroup &&
262
- (isLast || isLast !== false) &&
263
- index === realChildCount - 1;
236
+ : isFirstGroup && (isLast || isLast !== false) && index === realChildCount - 1;
264
237
  };
265
238
  const getIsFirstInLastGroup = (index: number): boolean => {
266
239
  return !isInsideStackGroup
267
240
  ? index === realChildCount - 1
268
- : isLastGroup &&
269
- (isFirst || isFirst !== false) &&
270
- childId === 1 &&
271
- index === 0;
241
+ : isLastGroup && (isFirst || isFirst !== false) && childId === 1 && index === 0;
272
242
  };
273
243
  const getIsLastInLastGroup = (index: number): boolean => {
274
244
  return !isInsideStackGroup
275
245
  ? index === realChildCount - 1
276
- : isLastGroup &&
277
- (isLast || isLast !== false) &&
278
- index === realChildCount - 1;
246
+ : isLastGroup && (isLast || isLast !== false) && index === realChildCount - 1;
279
247
  };
280
248
 
281
249
  const getBorderTopLeftRadius = (index: number): number | undefined => {
282
250
  const _isFirstGroup =
283
- !isInsideStackGroup || childrenCount === 1
284
- ? true
285
- : isChild
286
- ? isFirstGroup
287
- : index === 0;
251
+ !isInsideStackGroup || childrenCount === 1 ? true : isChild ? isFirstGroup : index === 0;
288
252
 
289
253
  // If this is the first item
290
254
  if (_isFirstGroup && getIsFirst(index)) {
@@ -324,11 +288,7 @@ const ReqoreControlGroup = memo(
324
288
  // If this group is not vertical we need to style the very first item
325
289
  if (!isVertical || !isStack) {
326
290
  const _isFirstGroup =
327
- !isInsideStackGroup || childrenCount === 1
328
- ? true
329
- : isChild
330
- ? isFirstGroup
331
- : index === 0;
291
+ !isInsideStackGroup || childrenCount === 1 ? true : isChild ? isFirstGroup : index === 0;
332
292
 
333
293
  if (_isFirstGroup && getIsFirst(index)) {
334
294
  return RADIUS_FROM_SIZE[size];
@@ -369,7 +329,6 @@ const ReqoreControlGroup = memo(
369
329
  return React.Children.map(children, (child) => {
370
330
  if (child && React.isValidElement(child)) {
371
331
  if (child.type === React.Fragment) {
372
- // just compare to `React.Fragment`
373
332
  return cloneThroughFragments(child.props.children);
374
333
  }
375
334
 
@@ -381,26 +340,12 @@ const ReqoreControlGroup = memo(
381
340
  ? child.props.minimal
382
341
  : minimal,
383
342
  size: child.props?.size || size,
384
- flat:
385
- child.props?.flat || child.props?.flat === false
386
- ? child.props.flat
387
- : flat,
388
- fluid:
389
- child.props?.fluid || child.props?.fluid === false
390
- ? child.props.fluid
391
- : fluid,
392
- fixed:
393
- child.props?.fixed || child.props?.fixed === false
394
- ? child.props.fixed
395
- : fixed,
396
- fill:
397
- child.props?.fill || child.props?.fill === false
398
- ? child.props.fill
399
- : fill,
343
+ flat: child.props?.flat || child.props?.flat === false ? child.props.flat : flat,
344
+ fluid: child.props?.fluid || child.props?.fluid === false ? child.props.fluid : fluid,
345
+ fixed: child.props?.fixed || child.props?.fixed === false ? child.props.fixed : fixed,
346
+ fill: child.props?.fill || child.props?.fill === false ? child.props.fill : fill,
400
347
  stack:
401
- child.props?.stack || child.props?.stack === false
402
- ? child.props.stack
403
- : isStack,
348
+ child.props?.stack || child.props?.stack === false ? child.props.stack : isStack,
404
349
  intent: child.props?.intent || intent,
405
350
  customTheme: child.props?.customTheme || customTheme,
406
351
  };
@@ -428,9 +373,7 @@ const ReqoreControlGroup = memo(
428
373
  childId: index + 1,
429
374
 
430
375
  isFirstGroup: isChild ? isFirstGroup : index === 0,
431
- isLastGroup: isChild
432
- ? isLastGroup
433
- : index === realChildCount - 1,
376
+ isLastGroup: isChild ? isLastGroup : index === realChildCount - 1,
434
377
  };
435
378
  }
436
379
 
@@ -469,9 +412,7 @@ const ReqoreControlGroup = memo(
469
412
  // Get the overflown children
470
413
  const overflownChildren = useMemo(
471
414
  () =>
472
- overflowingChildren
473
- ? React.Children.toArray(children).slice(0, overflowingChildren)
474
- : [],
415
+ overflowingChildren ? React.Children.toArray(children).slice(0, overflowingChildren) : [],
475
416
  [children, overflowingChildren]
476
417
  );
477
418
 
@@ -485,9 +426,7 @@ const ReqoreControlGroup = memo(
485
426
  icon='MenuLine'
486
427
  customTheme={customTheme}
487
428
  tooltip={{
488
- content: `Show ${React.Children.count(
489
- overflownChildren
490
- )} hidden items`,
429
+ content: `Show ${React.Children.count(overflownChildren)} hidden items`,
491
430
  }}
492
431
  fixed
493
432
  active={isOverflownDialogOpen}
@@ -21,6 +21,8 @@ export interface IReqoreIconProps
21
21
  icon?: IReqoreIconName;
22
22
  color?: TReqoreEffectColor;
23
23
  size?: TSizes | string;
24
+ wrapperSize?: TSizes | string;
25
+ wrapperElement?: any;
24
26
  iconProps?: IconBaseProps;
25
27
  margin?: 'right' | 'left' | 'both';
26
28
  image?: string;
@@ -38,8 +40,10 @@ const SpinKeyframes = keyframes`
38
40
  `;
39
41
 
40
42
  export const StyledIconWrapper = styled(StyledEffect)<{ margin: 'right' | 'left' | 'both' }>`
41
- display: inline-block;
43
+ display: inline-flex;
42
44
  flex: 0 0 auto;
45
+ justify-content: center;
46
+ align-items: center;
43
47
  vertical-align: text-bottom;
44
48
  transition: all 0.2s ease-out;
45
49
  overflow: hidden;
@@ -78,6 +82,8 @@ const ReqoreIcon = memo(
78
82
  {
79
83
  icon,
80
84
  size = 'normal',
85
+ wrapperSize,
86
+ wrapperElement,
81
87
  className,
82
88
  color,
83
89
  margin,
@@ -97,6 +103,11 @@ const ReqoreIcon = memo(
97
103
  ? theme.intents[intent]
98
104
  : getColorFromMaybeString(theme, color);
99
105
  const finalSize: string = isStringSize(size) ? ICON_FROM_SIZE[size] : size;
106
+ const finalWrapperSize: string = wrapperSize
107
+ ? isStringSize(wrapperSize)
108
+ ? ICON_FROM_SIZE[wrapperSize]
109
+ : wrapperSize
110
+ : finalSize;
100
111
 
101
112
  useTooltip(targetRef.current, tooltip);
102
113
 
@@ -104,11 +115,12 @@ const ReqoreIcon = memo(
104
115
  return (
105
116
  <StyledIconWrapper
106
117
  {...rest}
118
+ as={wrapperElement}
107
119
  ref={targetRef}
108
120
  size={size}
109
121
  margin={margin}
110
122
  className={`${className || ''} reqore-icon`}
111
- style={{ ...style, width: finalSize, height: finalSize }}
123
+ style={{ width: finalWrapperSize, height: finalWrapperSize, ...style }}
112
124
  >
113
125
  <img src={image} alt='' />
114
126
  </StyledIconWrapper>
@@ -119,11 +131,12 @@ const ReqoreIcon = memo(
119
131
  return (
120
132
  <StyledIconWrapper
121
133
  {...rest}
134
+ as={wrapperElement}
122
135
  ref={targetRef}
123
136
  size={size}
124
137
  margin={margin}
125
138
  className={`${className || ''} reqore-icon`}
126
- style={{ ...style, width: finalSize, height: finalSize }}
139
+ style={{ width: finalWrapperSize, height: finalWrapperSize, ...style }}
127
140
  />
128
141
  );
129
142
  }
@@ -131,10 +144,11 @@ const ReqoreIcon = memo(
131
144
  return (
132
145
  <StyledIconWrapper
133
146
  {...rest}
147
+ as={wrapperElement}
134
148
  ref={targetRef}
135
149
  margin={margin}
136
150
  size={size}
137
- style={{ ...style, width: finalSize, height: finalSize }}
151
+ style={{ width: finalWrapperSize, height: finalWrapperSize, ...style }}
138
152
  className={`${className || ''} reqore-icon`}
139
153
  >
140
154
  <IconContext.Provider
@@ -1,5 +1,5 @@
1
1
  import { rgba } from 'polished';
2
- import React, { forwardRef } from 'react';
2
+ import React, { forwardRef, useRef } from 'react';
3
3
  import styled, { css } from 'styled-components';
4
4
  import {
5
5
  PADDING_FROM_SIZE,
@@ -10,6 +10,7 @@ import {
10
10
  } from '../../constants/sizes';
11
11
  import { IReqoreTheme } from '../../constants/theme';
12
12
  import { changeLightness, getReadableColor } from '../../helpers/colors';
13
+ import { IReqoreAutoFocusRules, useAutoFocus } from '../../hooks/useAutoFocus';
13
14
  import { useCombinedRefs } from '../../hooks/useCombinedRefs';
14
15
  import { useReqoreTheme } from '../../hooks/useTheme';
15
16
  import { useTooltip } from '../../hooks/useTooltip';
@@ -44,11 +45,14 @@ export interface IReqoreInputProps
44
45
  onClearClick?: () => void;
45
46
  maxLength?: number;
46
47
  icon?: IReqoreIconName;
48
+ rightIcon?: IReqoreIconName;
47
49
  flat?: boolean;
48
50
  rounded?: boolean;
49
51
  type?: 'text' | 'password' | 'email' | 'number' | 'tel' | 'url';
50
52
  wrapperStyle?: React.CSSProperties;
51
53
  iconColor?: TReqoreEffectColor;
54
+ rightIconColor?: TReqoreEffectColor;
55
+ focusRules?: IReqoreAutoFocusRules;
52
56
  }
53
57
 
54
58
  export interface IReqoreInputStyle extends IReqoreInputProps {
@@ -76,6 +80,8 @@ const StyledIconWrapper = styled.div<IReqoreInputStyle>`
76
80
  position: absolute;
77
81
  height: ${({ _size }) => SIZE_TO_PX[_size]}px;
78
82
  width: ${({ _size }) => SIZE_TO_PX[_size]}px;
83
+ right: ${({ position }) => (position === 'right' ? 0 : undefined)};
84
+ top: 0;
79
85
  display: flex;
80
86
  justify-content: center;
81
87
  align-items: center;
@@ -87,7 +93,18 @@ export const StyledInput = styled(StyledEffect)<IReqoreInputStyle>`
87
93
  flex: 1;
88
94
  margin: 0;
89
95
  padding: ${({ _size }) => PADDING_FROM_SIZE[_size] / 2}px 7px;
90
- padding-right: ${({ clearable, _size }) => (clearable ? SIZE_TO_PX[_size] : 7)}px;
96
+ padding-right: ${({ clearable, hasRightIcon, _size }) => {
97
+ let padding = 7;
98
+
99
+ if (clearable || hasRightIcon) {
100
+ padding = 0;
101
+ padding += clearable ? SIZE_TO_PX[_size] : 0;
102
+ padding += hasRightIcon ? SIZE_TO_PX[_size] : 0;
103
+ }
104
+
105
+ return padding;
106
+ }}px;
107
+
91
108
  padding-left: ${({ hasIcon, _size }) => (hasIcon ? SIZE_TO_PX[_size] : 7)}px;
92
109
  font-size: ${({ _size }) => TEXT_FROM_SIZE[_size]}px;
93
110
  transition: all 0.2s ease-out;
@@ -154,7 +171,9 @@ const ReqoreInput = forwardRef<HTMLDivElement, IReqoreInputProps>(
154
171
  className,
155
172
  onClearClick,
156
173
  icon,
174
+ rightIcon,
157
175
  iconColor,
176
+ rightIconColor,
158
177
  flat,
159
178
  rounded = true,
160
179
  minimal,
@@ -163,14 +182,21 @@ const ReqoreInput = forwardRef<HTMLDivElement, IReqoreInputProps>(
163
182
  customTheme,
164
183
  intent,
165
184
  wrapperStyle,
185
+ focusRules,
166
186
  ...rest
167
187
  }: IReqoreInputProps,
168
188
  ref
169
189
  ) => {
170
190
  const { targetRef } = useCombinedRefs(ref);
191
+ const inputRef = useRef<HTMLInputElement>(null);
171
192
  const theme = useReqoreTheme('main', customTheme, intent);
172
193
 
173
194
  useTooltip(targetRef.current, tooltip);
195
+ useAutoFocus(
196
+ inputRef.current,
197
+ readOnly || rest.disabled ? undefined : focusRules,
198
+ rest.onChange
199
+ );
174
200
 
175
201
  return (
176
202
  <StyledInputWrapper
@@ -199,23 +225,32 @@ const ReqoreInput = forwardRef<HTMLDivElement, IReqoreInputProps>(
199
225
  interactive: !rest?.disabled && !readOnly,
200
226
  ...rest?.effect,
201
227
  }}
228
+ onChange={!readOnly && !rest?.disabled ? rest?.onChange : undefined}
202
229
  as='input'
230
+ ref={inputRef}
203
231
  theme={theme}
204
232
  _size={size}
205
233
  minimal={minimal}
206
234
  flat={flat}
207
235
  rounded={rounded}
208
236
  hasIcon={!!icon}
209
- clearable={!rest?.disabled && !!(onClearClick && rest?.onChange)}
237
+ hasRightIcon={!!rightIcon}
238
+ clearable={!rest?.disabled && !readOnly && !!(onClearClick && rest?.onChange)}
210
239
  className={`${className || ''} reqore-control reqore-input`}
211
240
  readOnly={readOnly}
212
241
  />
213
242
  <ReqoreInputClearButton
214
243
  enabled={!readOnly && !rest?.disabled && !!(onClearClick && rest?.onChange)}
215
244
  onClick={onClearClick}
245
+ hasRightIcon={!!rightIcon}
216
246
  size={size}
217
247
  show={rest?.value && rest.value !== '' ? true : false}
218
248
  />
249
+ {rightIcon && (
250
+ <StyledIconWrapper _size={size} position='right'>
251
+ <ReqoreIcon size={size} icon={rightIcon} color={rightIconColor} />
252
+ </StyledIconWrapper>
253
+ )}
219
254
  </StyledInputWrapper>
220
255
  );
221
256
  }
@@ -1,16 +1,28 @@
1
+ import { animated, useTransition } from '@react-spring/web';
1
2
  import styled from 'styled-components';
2
- import ReqoreButton, { IReqoreButtonProps } from '../Button';
3
+ import { SPRING_CONFIG } from '../../constants/animations';
4
+ import { SIZE_TO_PX } from '../../constants/sizes';
5
+ import { IReqoreButtonProps } from '../Button';
6
+ import ReqoreIcon from '../Icon';
3
7
 
4
8
  export interface IReqoreInputClearButtonProps extends IReqoreButtonProps {
5
9
  show: boolean;
6
10
  enabled: boolean;
11
+ hasRightIcon?: boolean;
7
12
  }
8
13
 
9
- export const StyledInputClearButton = styled(ReqoreButton)<{ show?: boolean }>`
14
+ export const StyledInputClearButton = styled(ReqoreIcon)<{ show?: boolean }>`
10
15
  position: absolute;
11
- right: ${({ show }) => (show ? 0 : -40)}px;
16
+ right: ${({ size, hasRightIcon }) => (hasRightIcon ? SIZE_TO_PX[size] : 0)}px;
12
17
  top: 0;
13
- opacity: ${({ show }) => (show ? 0.2 : 0)};
18
+ justify-content: center;
19
+ align-items: center;
20
+ transition: all 0.1s ease-out;
21
+ cursor: pointer;
22
+
23
+ &:hover {
24
+ opacity: 1 !important;
25
+ }
14
26
  `;
15
27
 
16
28
  const ReqoreInputClearButton = ({
@@ -18,18 +30,30 @@ const ReqoreInputClearButton = ({
18
30
  enabled,
19
31
  size = 'normal',
20
32
  ...rest
21
- }: IReqoreInputClearButtonProps) =>
22
- enabled ? (
23
- <StyledInputClearButton
24
- {...rest}
25
- className='reqore-clear-input-button'
26
- icon='CloseLine'
27
- minimal
28
- flat
29
- tabIndex={show ? 1 : -1}
30
- size={size}
31
- show={show}
32
- />
33
- ) : null;
33
+ }: IReqoreInputClearButtonProps) => {
34
+ const transitions = useTransition(show && enabled, {
35
+ from: { opacity: 0, filter: 'blur(10px)' },
36
+ enter: { opacity: 0.5, filter: 'blur(0px)' },
37
+ leave: { opacity: 0, filter: 'blur(10px)' },
38
+ config: SPRING_CONFIG,
39
+ });
40
+
41
+ return transitions((styles, item) =>
42
+ item ? (
43
+ <StyledInputClearButton
44
+ {...rest}
45
+ className='reqore-clear-input-button'
46
+ icon='CloseLine'
47
+ wrapperElement={animated.span}
48
+ size={size}
49
+ wrapperSize={SIZE_TO_PX[size]}
50
+ show={show}
51
+ style={{
52
+ opacity: styles.opacity,
53
+ }}
54
+ />
55
+ ) : null
56
+ );
57
+ };
34
58
 
35
59
  export default ReqoreInputClearButton;