@qoretechnologies/reqore 0.29.9 → 0.30.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 (73) hide show
  1. package/__tests__/collection.test.tsx +1 -1
  2. package/__tests__/{control_group.text.tsx → control_group.test.tsx} +4 -2
  3. package/__tests__/panel.test.tsx +22 -24
  4. package/__tests__/tabs.test.tsx +32 -0
  5. package/dist/components/Button/index.d.ts.map +1 -1
  6. package/dist/components/Button/index.js +5 -2
  7. package/dist/components/Button/index.js.map +1 -1
  8. package/dist/components/Collection/index.d.ts.map +1 -1
  9. package/dist/components/Collection/index.js +14 -37
  10. package/dist/components/Collection/index.js.map +1 -1
  11. package/dist/components/Columns/column.d.ts.map +1 -1
  12. package/dist/components/Columns/column.js +1 -1
  13. package/dist/components/Columns/column.js.map +1 -1
  14. package/dist/components/ControlGroup/index.d.ts +5 -4
  15. package/dist/components/ControlGroup/index.d.ts.map +1 -1
  16. package/dist/components/ControlGroup/index.js +117 -24
  17. package/dist/components/ControlGroup/index.js.map +1 -1
  18. package/dist/components/Drawer/index.d.ts.map +1 -1
  19. package/dist/components/Drawer/index.js +6 -4
  20. package/dist/components/Drawer/index.js.map +1 -1
  21. package/dist/components/Effect/index.d.ts.map +1 -1
  22. package/dist/components/Effect/index.js +1 -1
  23. package/dist/components/Effect/index.js.map +1 -1
  24. package/dist/components/Input/index.js +2 -2
  25. package/dist/components/Input/index.js.map +1 -1
  26. package/dist/components/InternalPopover/index.d.ts.map +1 -1
  27. package/dist/components/InternalPopover/index.js +5 -3
  28. package/dist/components/InternalPopover/index.js.map +1 -1
  29. package/dist/components/MultiSelect/index.d.ts +1 -1
  30. package/dist/components/MultiSelect/index.d.ts.map +1 -1
  31. package/dist/components/MultiSelect/index.js +10 -10
  32. package/dist/components/MultiSelect/index.js.map +1 -1
  33. package/dist/components/Panel/index.d.ts +4 -1
  34. package/dist/components/Panel/index.d.ts.map +1 -1
  35. package/dist/components/Panel/index.js +39 -16
  36. package/dist/components/Panel/index.js.map +1 -1
  37. package/dist/components/Spacer/index.d.ts +2 -0
  38. package/dist/components/Spacer/index.d.ts.map +1 -1
  39. package/dist/components/Spacer/index.js +29 -7
  40. package/dist/components/Spacer/index.js.map +1 -1
  41. package/dist/components/Tabs/index.d.ts.map +1 -1
  42. package/dist/components/Tabs/index.js +2 -1
  43. package/dist/components/Tabs/index.js.map +1 -1
  44. package/dist/components/Tag/index.d.ts +2 -2
  45. package/dist/components/Tag/index.d.ts.map +1 -1
  46. package/dist/components/Tag/index.js +7 -1
  47. package/dist/components/Tag/index.js.map +1 -1
  48. package/dist/components/Tree/index.js +1 -1
  49. package/dist/components/Tree/index.js.map +1 -1
  50. package/dist/constants/animations.js +1 -1
  51. package/dist/containers/PopoverProvider.js +3 -3
  52. package/dist/containers/PopoverProvider.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/components/Button/index.tsx +2 -1
  55. package/src/components/Collection/index.tsx +14 -37
  56. package/src/components/Columns/column.tsx +1 -0
  57. package/src/components/ControlGroup/index.tsx +386 -232
  58. package/src/components/Drawer/index.tsx +6 -4
  59. package/src/components/Effect/index.tsx +1 -0
  60. package/src/components/Input/index.tsx +1 -1
  61. package/src/components/InternalPopover/index.tsx +127 -123
  62. package/src/components/MultiSelect/index.tsx +180 -188
  63. package/src/components/Panel/index.tsx +127 -42
  64. package/src/components/Spacer/index.tsx +63 -14
  65. package/src/components/Tabs/index.tsx +3 -2
  66. package/src/components/Tag/index.tsx +5 -1
  67. package/src/components/Tree/index.tsx +1 -1
  68. package/src/constants/animations.ts +1 -1
  69. package/src/containers/PopoverProvider.tsx +3 -3
  70. package/src/stories/ControlGroup/ControlGroup.stories.tsx +55 -5
  71. package/src/stories/Panel/Panel.stories.tsx +60 -17
  72. package/src/stories/Popover/Popover.stories.tsx +5 -5
  73. package/tests.json +1 -1
@@ -1,10 +1,15 @@
1
- import React, { forwardRef, memo, useCallback, useMemo } from 'react';
2
- import styled from 'styled-components';
1
+ import { debounce } from 'lodash';
2
+ import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { useMount, useUnmount } from 'react-use';
4
+ import styled, { css } from 'styled-components';
5
+ import { ReqoreButton, ReqoreModal } from '../..';
3
6
  import { GAP_FROM_SIZE, RADIUS_FROM_SIZE, TSizes } from '../../constants/sizes';
4
7
  import { IReqoreTheme } from '../../constants/theme';
5
8
  import {
6
9
  IReqoreIntent,
10
+ IWithReqoreCustomTheme,
7
11
  IWithReqoreFlat,
12
+ IWithReqoreFluid,
8
13
  IWithReqoreMinimal,
9
14
  IWithReqoreSize,
10
15
  } from '../../types/global';
@@ -14,11 +19,14 @@ export interface IReqoreControlGroupProps
14
19
  IWithReqoreFlat,
15
20
  IWithReqoreSize,
16
21
  IWithReqoreMinimal,
17
- IReqoreIntent {
22
+ IReqoreIntent,
23
+ IWithReqoreFluid,
24
+ IWithReqoreCustomTheme {
18
25
  stack?: boolean;
19
26
  children: any;
20
- fluid?: boolean;
27
+ fixed?: boolean;
21
28
  rounded?: boolean;
29
+ responsive?: boolean;
22
30
  gapSize?: TSizes;
23
31
  /*
24
32
  * Whether the contents of the control group should be stacked vertically
@@ -48,167 +56,211 @@ export interface IReqoreControlGroupStyle extends IReqoreControlGroupProps {
48
56
  export const StyledReqoreControlGroup = styled.div<IReqoreControlGroupStyle>`
49
57
  display: flex;
50
58
  flex: 0 auto;
51
- width: ${({ fluid }) => (fluid ? '100%' : undefined)};
52
- justify-content: ${({ fluid, vertical, horizontalAlign, verticalAlign }) =>
53
- vertical ? verticalAlign : fluid ? 'stretch' : horizontalAlign};
54
- align-items: ${({ vertical, horizontalAlign, verticalAlign, fluid }) =>
55
- vertical ? (fluid ? 'stretch' : horizontalAlign) : verticalAlign};
59
+ width: ${({ fluid, fixed }) => (fluid && !fixed ? '100%' : undefined)};
60
+ justify-content: ${({ fluid, vertical }) => (vertical && fluid ? 'stretch' : undefined)};
61
+ align-items: ${({ vertical, fluid, horizontalAlign }) =>
62
+ vertical ? (fluid ? 'stretch' : horizontalAlign) : undefined};
56
63
  flex-flow: ${({ vertical }) => (vertical ? 'column' : 'row')};
57
64
  gap: ${({ gapSize, stack }) => (!stack ? `${GAP_FROM_SIZE[gapSize]}px` : undefined)};
58
65
  flex-wrap: ${({ wrap }) => (wrap ? 'wrap' : 'nowrap')};
59
66
 
67
+ > * {
68
+ margin-top: ${({ verticalAlign }) =>
69
+ verticalAlign === 'flex-end' || verticalAlign === 'center' ? 'auto' : undefined};
70
+ margin-bottom: ${({ verticalAlign }) =>
71
+ verticalAlign === 'flex-start' || verticalAlign === 'center' ? 'auto' : undefined};
72
+ margin-right: ${({ horizontalAlign }) => (horizontalAlign === 'center' ? 'auto' : undefined)};
73
+
74
+ ${({ vertical }) =>
75
+ vertical &&
76
+ css`
77
+ margin-left: ${({ horizontalAlign }) =>
78
+ horizontalAlign === 'flex-end' || horizontalAlign === 'center' ? 'auto' : undefined};
79
+ `}
80
+ }
81
+
82
+ ${({ vertical }) =>
83
+ !vertical &&
84
+ css`
85
+ > *:first-child {
86
+ margin-left: ${({ horizontalAlign }) =>
87
+ horizontalAlign === 'flex-end' || horizontalAlign === 'center' ? 'auto' : undefined};
88
+ }
89
+ `}
90
+
60
91
  > * {
61
92
  border-radius: ${({ stack }) => (!stack ? undefined : 0)};
62
93
  }
63
94
  `;
64
95
 
65
96
  const ReqoreControlGroup = memo(
66
- forwardRef<HTMLDivElement, IReqoreControlGroupProps>(
67
- (
68
- {
69
- children,
70
- className,
71
- minimal,
72
- size = 'normal',
73
- gapSize = 'normal',
74
- fluid,
75
- flat,
76
- rounded = true,
77
- wrap,
78
- stack,
79
- isInsideStackGroup,
80
- isInsideVerticalGroup,
81
- isFirstInLastGroup,
82
- isLastInFirstGroup,
83
- isLastInLastGroup,
84
- intent,
85
- isFirst,
86
- isLast,
87
- vertical,
88
- childrenCount,
89
- childId,
90
- isChild,
91
- isFirstGroup,
92
- isLastGroup,
93
- verticalAlign = 'center',
94
- horizontalAlign = 'flex-start',
95
- ...rest
96
- }: IReqoreControlGroupProps,
97
- ref
98
- ) => {
99
- const isStack = stack || isInsideStackGroup;
100
- const isVertical = vertical || isInsideVerticalGroup;
101
- const realChildCount = useMemo((): number => {
102
- let count = 0;
103
-
104
- const countChildren = (children: React.ReactNode) =>
105
- React.Children.forEach(children, (child: any) => {
106
- if (child && React.isValidElement(child)) {
107
- if (child.type === React.Fragment) {
108
- // just compare to `React.Fragment`
109
- countChildren((child.props as any).children);
110
- } else {
111
- count++;
112
- }
97
+ ({
98
+ children,
99
+ className,
100
+ minimal,
101
+ size = 'normal',
102
+ gapSize = 'normal',
103
+ fluid,
104
+ flat,
105
+ fixed,
106
+ rounded = true,
107
+ wrap,
108
+ stack,
109
+ isInsideStackGroup,
110
+ isInsideVerticalGroup,
111
+ isFirstInLastGroup,
112
+ isLastInFirstGroup,
113
+ isLastInLastGroup,
114
+ intent,
115
+ customTheme,
116
+ isFirst,
117
+ isLast,
118
+ vertical,
119
+ childrenCount,
120
+ childId,
121
+ isChild,
122
+ isFirstGroup,
123
+ isLastGroup,
124
+ verticalAlign = 'center',
125
+ horizontalAlign = 'flex-start',
126
+ responsive,
127
+ ...rest
128
+ }: IReqoreControlGroupProps) => {
129
+ const isStack = stack || isInsideStackGroup;
130
+ const isVertical = vertical || isInsideVerticalGroup;
131
+
132
+ const [overflowingChildren, setOverflowingChildren] = useState<number | undefined>(0);
133
+ const [lastReSize, setLastReSize] = useState<number>(0);
134
+ const [isResizing, setIsResizing] = useState<boolean>(false);
135
+ const [isOverflownDialogOpen, setIsOverflownDialogOpen] = useState<boolean>(false);
136
+ const observer = useRef<ResizeObserver | null>(null);
137
+ const ref = useRef<HTMLDivElement>(null);
138
+ const resizeTimerObserver = useRef<ResizeObserver | null>(null);
139
+ const resizingTimer = useRef<any>(null);
140
+
141
+ const realChildCount = useMemo((): number => {
142
+ let count = 0;
143
+
144
+ const countChildren = (children: React.ReactNode) =>
145
+ React.Children.forEach(children, (child: any) => {
146
+ if (child && React.isValidElement(child)) {
147
+ if (child.type === React.Fragment) {
148
+ // just compare to `React.Fragment`
149
+ countChildren((child.props as any).children);
150
+ } else {
151
+ count++;
113
152
  }
114
- });
115
-
116
- countChildren(children);
117
-
118
- return count < 0 ? 0 : count;
119
- }, [children]);
120
-
121
- const getIsFirst = (index: number): boolean => {
122
- return !isInsideStackGroup || childrenCount === 1
123
- ? index === 0
124
- : (isFirst || isFirst !== false) && index === 0;
125
- };
126
- const getIsLast = (index: number): boolean => {
127
- return !isInsideStackGroup
128
- ? index === realChildCount - 1
129
- : (isLast || isLast !== false) && index === realChildCount - 1;
130
- };
131
- const getIsLastInFirstGroup = (index: number): boolean => {
132
- return !isInsideStackGroup
133
- ? index === 0
134
- : isFirstGroup && (isLast || isLast !== false) && index === realChildCount - 1;
135
- };
136
- const getIsFirstInLastGroup = (index: number): boolean => {
137
- return !isInsideStackGroup
138
- ? index === realChildCount - 1
139
- : isLastGroup && (isFirst || isFirst !== false) && childId === 1 && index === 0;
140
- };
141
- const getIsLastInLastGroup = (index: number): boolean => {
142
- return !isInsideStackGroup
143
- ? index === realChildCount - 1
144
- : isLastGroup && (isLast || isLast !== false) && index === realChildCount - 1;
145
- };
146
-
147
- const getBorderTopLeftRadius = (index: number): number | undefined => {
148
- const _isFirstGroup =
149
- !isInsideStackGroup || childrenCount === 1 ? true : isChild ? isFirstGroup : index === 0;
150
-
151
- // If this is the first item
152
- if (_isFirstGroup && getIsFirst(index)) {
153
- return RADIUS_FROM_SIZE[size];
154
- }
155
-
156
- return undefined;
157
- };
158
-
159
- const getBorderTopRightRadius = (index: number): number | undefined => {
160
- // If this group is not vertical we need to style the very last item
161
- if (!isVertical || !isStack) {
162
- const _isLastGroup =
163
- !isInsideStackGroup || childrenCount === 1
164
- ? true
165
- : isChild
166
- ? isLastGroup
167
- : index === realChildCount - 1;
168
-
169
- if (_isLastGroup && getIsLast(index)) {
170
- return RADIUS_FROM_SIZE[size];
171
153
  }
154
+ });
172
155
 
173
- return undefined;
174
- }
175
-
176
- // This border only needs to apply when this group is vertical
177
- // AND this item is the last item of the first group
178
- if (getIsLastInFirstGroup(index)) {
179
- return RADIUS_FROM_SIZE[size];
180
- }
156
+ countChildren(children);
181
157
 
182
- return undefined;
183
- };
184
-
185
- const getBorderBottomLeftRadius = (index: number): number | undefined => {
186
- // If this group is not vertical we need to style the very first item
187
- if (!isVertical || !isStack) {
188
- const _isFirstGroup =
189
- !isInsideStackGroup || childrenCount === 1
190
- ? true
191
- : isChild
192
- ? isFirstGroup
193
- : index === 0;
194
-
195
- if (_isFirstGroup && getIsFirst(index)) {
196
- return RADIUS_FROM_SIZE[size];
197
- }
158
+ return count < 0 ? 0 : count;
159
+ }, [children]);
198
160
 
199
- return undefined;
200
- }
161
+ const checkIfOverflowing = useCallback(() => {
162
+ return (
163
+ ref.current.clientWidth > 40 &&
164
+ !vertical &&
165
+ responsive &&
166
+ ref.current &&
167
+ ref.current.scrollWidth > ref.current.clientWidth
168
+ );
169
+ }, [vertical, responsive, ref, children]);
170
+
171
+ useMount(() => {
172
+ if (!vertical && responsive) {
173
+ if ('ResizeObserver' in window) {
174
+ observer.current = new ResizeObserver(
175
+ debounce(() => {
176
+ if (ref && ref.current) {
177
+ setOverflowingChildren(0);
178
+ setLastReSize(ref.current.clientWidth);
179
+ }
180
+ }, 200)
181
+ );
182
+
183
+ resizeTimerObserver.current = new ResizeObserver(() => {
184
+ if (ref && ref.current) {
185
+ setIsResizing(true);
186
+ clearTimeout(resizingTimer.current);
187
+ resizingTimer.current = setTimeout(() => {
188
+ setIsResizing(false);
189
+ }, 500);
190
+ }
191
+ });
201
192
 
202
- // This border only needs to apply when this group is vertical
203
- // AND this item is the first item of the last group
204
- if (getIsFirstInLastGroup(index)) {
205
- return RADIUS_FROM_SIZE[size];
193
+ observer.current.observe(ref.current);
194
+ resizeTimerObserver.current.observe(ref.current);
206
195
  }
207
-
208
- return undefined;
209
- };
210
-
211
- const getBorderBottomRightRadius = (index: number): number | undefined => {
196
+ }
197
+ });
198
+
199
+ useUnmount(() => {
200
+ if (observer.current) {
201
+ observer.current.disconnect();
202
+ }
203
+
204
+ if (resizeTimerObserver.current) {
205
+ resizeTimerObserver.current.disconnect();
206
+ }
207
+
208
+ clearTimeout(resizingTimer.current);
209
+ });
210
+
211
+ useEffect(() => {
212
+ // Is the control group still overflowing?
213
+ if ((overflowingChildren || overflowingChildren === 0) && checkIfOverflowing()) {
214
+ setOverflowingChildren(overflowingChildren + 1);
215
+ }
216
+
217
+ // Hide the dialog if the group was resized and the dialog was open
218
+ if (overflowingChildren === 0) {
219
+ setIsOverflownDialogOpen(false);
220
+ }
221
+ }, [overflowingChildren, lastReSize]);
222
+
223
+ const getIsFirst = (index: number): boolean => {
224
+ return !isInsideStackGroup || childrenCount === 1
225
+ ? index === 0
226
+ : (isFirst || isFirst !== false) && index === 0;
227
+ };
228
+ const getIsLast = (index: number): boolean => {
229
+ return !isInsideStackGroup
230
+ ? index === realChildCount - 1
231
+ : (isLast || isLast !== false) && index === realChildCount - 1;
232
+ };
233
+ const getIsLastInFirstGroup = (index: number): boolean => {
234
+ return !isInsideStackGroup
235
+ ? index === 0
236
+ : isFirstGroup && (isLast || isLast !== false) && index === realChildCount - 1;
237
+ };
238
+ const getIsFirstInLastGroup = (index: number): boolean => {
239
+ return !isInsideStackGroup
240
+ ? index === realChildCount - 1
241
+ : isLastGroup && (isFirst || isFirst !== false) && childId === 1 && index === 0;
242
+ };
243
+ const getIsLastInLastGroup = (index: number): boolean => {
244
+ return !isInsideStackGroup
245
+ ? index === realChildCount - 1
246
+ : isLastGroup && (isLast || isLast !== false) && index === realChildCount - 1;
247
+ };
248
+
249
+ const getBorderTopLeftRadius = (index: number): number | undefined => {
250
+ const _isFirstGroup =
251
+ !isInsideStackGroup || childrenCount === 1 ? true : isChild ? isFirstGroup : index === 0;
252
+
253
+ // If this is the first item
254
+ if (_isFirstGroup && getIsFirst(index)) {
255
+ return RADIUS_FROM_SIZE[size];
256
+ }
257
+
258
+ return undefined;
259
+ };
260
+
261
+ const getBorderTopRightRadius = (index: number): number | undefined => {
262
+ // If this group is not vertical we need to style the very last item
263
+ if (!isVertical || !isStack) {
212
264
  const _isLastGroup =
213
265
  !isInsideStackGroup || childrenCount === 1
214
266
  ? true
@@ -216,119 +268,221 @@ const ReqoreControlGroup = memo(
216
268
  ? isLastGroup
217
269
  : index === realChildCount - 1;
218
270
 
219
- // If this is the last item
220
271
  if (_isLastGroup && getIsLast(index)) {
221
272
  return RADIUS_FROM_SIZE[size];
222
273
  }
223
274
 
224
275
  return undefined;
225
- };
276
+ }
226
277
 
227
- let index = 0;
278
+ // This border only needs to apply when this group is vertical
279
+ // AND this item is the last item of the first group
280
+ if (getIsLastInFirstGroup(index)) {
281
+ return RADIUS_FROM_SIZE[size];
282
+ }
228
283
 
229
- const cloneThroughFragments = useCallback(
230
- (children: React.ReactNode): React.ReactNode => {
231
- return React.Children.map(children, (child) => {
232
- if (child && React.isValidElement(child)) {
233
- if (child.type === React.Fragment) {
234
- // just compare to `React.Fragment`
235
- return cloneThroughFragments(child.props.children);
236
- }
284
+ return undefined;
285
+ };
237
286
 
238
- let newProps = {
239
- ...child.props,
240
- key: index,
241
- minimal:
242
- child.props?.minimal || child.props?.minimal === false
243
- ? child.props.minimal
244
- : minimal,
245
- size: child.props?.size || size,
246
- flat: child.props?.flat || child.props?.flat === false ? child.props.flat : flat,
247
- fluid:
248
- child.props?.fluid || child.props?.fluid === false ? child.props.fluid : fluid,
249
- stack:
250
- child.props?.stack || child.props?.stack === false ? child.props.stack : isStack,
251
- intent: child.props?.intent || intent,
252
- };
287
+ const getBorderBottomLeftRadius = (index: number): number | undefined => {
288
+ // If this group is not vertical we need to style the very first item
289
+ if (!isVertical || !isStack) {
290
+ const _isFirstGroup =
291
+ !isInsideStackGroup || childrenCount === 1 ? true : isChild ? isFirstGroup : index === 0;
253
292
 
254
- if (isStack) {
255
- newProps = {
256
- ...newProps,
257
- style: {
258
- borderTopLeftRadius: getBorderTopLeftRadius(index),
259
- borderBottomLeftRadius: getBorderBottomLeftRadius(index),
260
- borderTopRightRadius: getBorderTopRightRadius(index),
261
- borderBottomRightRadius: getBorderBottomRightRadius(index),
262
- ...(child.props?.style || {}),
263
- },
264
- rounded: !isStack,
265
- isInsideStackGroup: isStack,
266
- isInsideVerticalGroup: isVertical,
267
- isFirst: isChild ? getIsFirst(index) : undefined,
268
- isLast: isChild ? getIsLast(index) : undefined,
269
- isLastInFirstGroup: getIsLastInFirstGroup(index),
270
- isLastInLastGroup: getIsLastInLastGroup(index),
271
- isFirstInLastGroup: getIsFirstInLastGroup(index),
272
- childrenCount: realChildCount,
273
- childId: index + 1,
274
- isChild: true,
275
- isFirstGroup: isChild ? isFirstGroup : index === 0,
276
- isLastGroup: isChild ? isLastGroup : index === realChildCount - 1,
277
- };
278
- }
293
+ if (_isFirstGroup && getIsFirst(index)) {
294
+ return RADIUS_FROM_SIZE[size];
295
+ }
279
296
 
280
- /*
281
- * Because of the way React.Children.map works, we have to
282
- * manually decrement the index for every child that is `null`
283
- * because react maps through null children and returns them in `Count`
284
- * We filter these children out in the `realChildCount` variable,
285
- * but the index is still incremented
286
- * */
287
- index = index + 1;
297
+ return undefined;
298
+ }
299
+
300
+ // This border only needs to apply when this group is vertical
301
+ // AND this item is the first item of the last group
302
+ if (getIsFirstInLastGroup(index)) {
303
+ return RADIUS_FROM_SIZE[size];
304
+ }
305
+
306
+ return undefined;
307
+ };
308
+
309
+ const getBorderBottomRightRadius = (index: number): number | undefined => {
310
+ const _isLastGroup =
311
+ !isInsideStackGroup || childrenCount === 1
312
+ ? true
313
+ : isChild
314
+ ? isLastGroup
315
+ : index === realChildCount - 1;
316
+
317
+ // If this is the last item
318
+ if (_isLastGroup && getIsLast(index)) {
319
+ return RADIUS_FROM_SIZE[size];
320
+ }
321
+
322
+ return undefined;
323
+ };
324
+
325
+ let index = 0;
326
+
327
+ const cloneThroughFragments = useCallback(
328
+ (children: React.ReactNode): React.ReactNode => {
329
+ return React.Children.map(children, (child) => {
330
+ if (child && React.isValidElement(child)) {
331
+ if (child.type === React.Fragment) {
332
+ // just compare to `React.Fragment`
333
+ return cloneThroughFragments(child.props.children);
334
+ }
288
335
 
289
- return React.cloneElement(child, newProps);
336
+ let newProps = {
337
+ ...child.props,
338
+ key: index,
339
+ minimal:
340
+ child.props?.minimal || child.props?.minimal === false
341
+ ? child.props.minimal
342
+ : minimal,
343
+ size: child.props?.size || size,
344
+ flat: child.props?.flat || child.props?.flat === false ? child.props.flat : flat,
345
+ fluid: child.props?.fluid || child.props?.fluid === false ? child.props.fluid : fluid,
346
+ fixed: child.props?.fixed || child.props?.fixed === false ? child.props.fixed : fixed,
347
+ stack:
348
+ child.props?.stack || child.props?.stack === false ? child.props.stack : isStack,
349
+ intent: child.props?.intent || intent,
350
+ customTheme: child.props?.customTheme || customTheme,
351
+ };
352
+
353
+ if (isStack) {
354
+ newProps = {
355
+ ...newProps,
356
+ style: {
357
+ borderTopLeftRadius: getBorderTopLeftRadius(index),
358
+ borderBottomLeftRadius: getBorderBottomLeftRadius(index),
359
+ borderTopRightRadius: getBorderTopRightRadius(index),
360
+ borderBottomRightRadius: getBorderBottomRightRadius(index),
361
+ ...(child.props?.style || {}),
362
+ },
363
+ isChild: true,
364
+ rounded: !isStack,
365
+ isInsideStackGroup: isStack,
366
+ isInsideVerticalGroup: isVertical,
367
+ isFirst: isChild ? getIsFirst(index) : undefined,
368
+ isLast: isChild ? getIsLast(index) : undefined,
369
+ isLastInFirstGroup: getIsLastInFirstGroup(index),
370
+ isLastInLastGroup: getIsLastInLastGroup(index),
371
+ isFirstInLastGroup: getIsFirstInLastGroup(index),
372
+ childrenCount: realChildCount,
373
+ childId: index + 1,
374
+
375
+ isFirstGroup: isChild ? isFirstGroup : index === 0,
376
+ isLastGroup: isChild ? isLastGroup : index === realChildCount - 1,
377
+ };
290
378
  }
291
379
 
292
- return child;
293
- });
294
- },
295
- [
296
- children,
297
- isStack,
298
- isVertical,
299
- isChild,
300
- isLastGroup,
301
- isFirstGroup,
302
- realChildCount,
303
- index,
304
- fluid,
305
- flat,
306
- minimal,
307
- size,
308
- intent,
309
- ]
310
- );
380
+ /*
381
+ * Because of the way React.Children.map works, we have to
382
+ * manually decrement the index for every child that is `null`
383
+ * because react maps through null children and returns them in `Count`
384
+ * We filter these children out in the `realChildCount` variable,
385
+ * but the index is still incremented
386
+ * */
387
+ index = index + 1;
311
388
 
312
- return (
389
+ return React.cloneElement(child, newProps);
390
+ }
391
+
392
+ return child;
393
+ });
394
+ },
395
+ [
396
+ children,
397
+ isStack,
398
+ isVertical,
399
+ isChild,
400
+ isLastGroup,
401
+ isFirstGroup,
402
+ realChildCount,
403
+ index,
404
+ fluid,
405
+ flat,
406
+ minimal,
407
+ size,
408
+ intent,
409
+ ]
410
+ );
411
+
412
+ // Get the overflown children
413
+ const overflownChildren = useMemo(
414
+ () =>
415
+ overflowingChildren ? React.Children.toArray(children).slice(0, overflowingChildren) : [],
416
+ [children, overflowingChildren]
417
+ );
418
+
419
+ // Remove the overflowing children count from the start of the children array
420
+ const _children = useMemo(
421
+ () =>
422
+ overflowingChildren && responsive
423
+ ? [
424
+ children.slice(overflowingChildren),
425
+ <ReqoreButton
426
+ icon='MenuLine'
427
+ customTheme={customTheme}
428
+ tooltip={{
429
+ content: `Show ${React.Children.count(overflownChildren)} hidden items`,
430
+ closeOnAnyClick: true,
431
+ }}
432
+ fixed
433
+ onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
434
+ e.stopPropagation();
435
+ setIsOverflownDialogOpen(true);
436
+ }}
437
+ flat
438
+ />,
439
+ ]
440
+ : children,
441
+ [children, overflowingChildren, responsive, overflownChildren]
442
+ );
443
+
444
+ return (
445
+ <>
446
+ {isOverflownDialogOpen ? (
447
+ <ReqoreModal
448
+ isOpen
449
+ onClose={() => setIsOverflownDialogOpen(false)}
450
+ flat
451
+ minimal
452
+ hasBackdrop={false}
453
+ intent='info'
454
+ label='Hidden items'
455
+ >
456
+ <ReqoreControlGroup fluid wrap>
457
+ {overflownChildren}
458
+ </ReqoreControlGroup>
459
+ </ReqoreModal>
460
+ ) : null}
313
461
  <StyledReqoreControlGroup
314
462
  {...rest}
315
463
  vertical={vertical}
464
+ style={{
465
+ overflowX: responsive ? 'auto' : undefined,
466
+ visibility: isResizing ? 'hidden' : undefined,
467
+ ...rest.style,
468
+ }}
316
469
  size={size}
317
470
  gapSize={gapSize}
318
471
  ref={ref}
319
472
  rounded={rounded}
320
473
  fluid={fluid}
474
+ fixed={fixed}
321
475
  wrap={wrap}
322
476
  stack={isStack}
323
477
  verticalAlign={verticalAlign}
324
478
  horizontalAlign={horizontalAlign}
325
479
  className={`${className || ''} reqore-control-group`}
326
480
  >
327
- {cloneThroughFragments(children)}
481
+ {cloneThroughFragments(_children)}
328
482
  </StyledReqoreControlGroup>
329
- );
330
- }
331
- )
483
+ </>
484
+ );
485
+ }
332
486
  );
333
487
 
334
488
  export default ReqoreControlGroup;