@qoretechnologies/reqore 0.34.2 → 0.34.4

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 (66) hide show
  1. package/__tests__/button.test.tsx +30 -1
  2. package/__tests__/containers/paging.test.tsx +1 -1
  3. package/__tests__/drawer.test.tsx +1 -1
  4. package/__tests__/heading.test.tsx +58 -0
  5. package/__tests__/hooks/useReqorePaging.test.tsx +16 -0
  6. package/__tests__/pagination.test.tsx +104 -3
  7. package/__tests__/panel.test.tsx +23 -2
  8. package/dist/components/Button/index.d.ts +3 -1
  9. package/dist/components/Button/index.d.ts.map +1 -1
  10. package/dist/components/Button/index.js +9 -7
  11. package/dist/components/Button/index.js.map +1 -1
  12. package/dist/components/Collection/index.js +1 -2
  13. package/dist/components/Collection/index.js.map +1 -1
  14. package/dist/components/ControlGroup/index.d.ts.map +1 -1
  15. package/dist/components/ControlGroup/index.js +3 -3
  16. package/dist/components/ControlGroup/index.js.map +1 -1
  17. package/dist/components/Drawer/index.d.ts.map +1 -1
  18. package/dist/components/Drawer/index.js +3 -10
  19. package/dist/components/Drawer/index.js.map +1 -1
  20. package/dist/components/Dropdown/list.js +1 -1
  21. package/dist/components/Dropdown/list.js.map +1 -1
  22. package/dist/components/Header/index.d.ts +3 -3
  23. package/dist/components/Header/index.d.ts.map +1 -1
  24. package/dist/components/Header/index.js +5 -2
  25. package/dist/components/Header/index.js.map +1 -1
  26. package/dist/components/Paging/index.d.ts +2 -1
  27. package/dist/components/Paging/index.d.ts.map +1 -1
  28. package/dist/components/Paging/index.js +41 -7
  29. package/dist/components/Paging/index.js.map +1 -1
  30. package/dist/components/Panel/NonResponsiveActions.d.ts +18 -0
  31. package/dist/components/Panel/NonResponsiveActions.d.ts.map +1 -0
  32. package/dist/components/Panel/NonResponsiveActions.js +47 -0
  33. package/dist/components/Panel/NonResponsiveActions.js.map +1 -0
  34. package/dist/components/Panel/index.d.ts +5 -1
  35. package/dist/components/Panel/index.d.ts.map +1 -1
  36. package/dist/components/Panel/index.js +32 -19
  37. package/dist/components/Panel/index.js.map +1 -1
  38. package/dist/components/Tag/group.js +4 -1
  39. package/dist/components/Tag/group.js.map +1 -1
  40. package/dist/containers/Paging.d.ts +3 -3
  41. package/dist/containers/Paging.d.ts.map +1 -1
  42. package/dist/containers/Paging.js +6 -6
  43. package/dist/containers/Paging.js.map +1 -1
  44. package/dist/hooks/usePaging.d.ts +5 -0
  45. package/dist/hooks/usePaging.d.ts.map +1 -1
  46. package/dist/hooks/usePaging.js +7 -1
  47. package/dist/hooks/usePaging.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/components/Button/index.tsx +19 -14
  50. package/src/components/Collection/index.tsx +2 -2
  51. package/src/components/ControlGroup/index.tsx +2 -3
  52. package/src/components/Drawer/index.tsx +4 -10
  53. package/src/components/Dropdown/list.tsx +1 -1
  54. package/src/components/Header/index.tsx +9 -3
  55. package/src/components/Paging/index.tsx +46 -6
  56. package/src/components/Panel/NonResponsiveActions.tsx +79 -0
  57. package/src/components/Panel/index.tsx +128 -75
  58. package/src/components/Tag/group.tsx +1 -1
  59. package/src/containers/Paging.tsx +83 -37
  60. package/src/hooks/usePaging.ts +14 -0
  61. package/src/stories/Drawer/Drawer.stories.tsx +6 -1
  62. package/src/stories/Dropdown/Dropdown.stories.tsx +1 -0
  63. package/src/stories/Header/Header.stories.tsx +6 -8
  64. package/src/stories/Paging/Paging.stories.tsx +22 -2
  65. package/src/stories/Panel/Panel.stories.tsx +13 -3
  66. package/tests.json +1 -1
@@ -11,7 +11,7 @@ import {
11
11
  IWithReqoreFlat,
12
12
  IWithReqoreFluid,
13
13
  IWithReqoreMinimal,
14
- IWithReqoreSize
14
+ IWithReqoreSize,
15
15
  } from '../../types/global';
16
16
  import { StyledHeader } from '../Header';
17
17
  import { StyledParagraph } from '../Paragraph';
@@ -336,7 +336,6 @@ const ReqoreControlGroup = memo(
336
336
 
337
337
  let newProps = {
338
338
  ...child.props,
339
- key: child.props.reactKey || child.props.key || index,
340
339
  minimal:
341
340
  child.props?.minimal || child.props?.minimal === false
342
341
  ? child.props.minimal
@@ -430,13 +429,13 @@ const ReqoreControlGroup = memo(
430
429
  tooltip={{
431
430
  content: `Show ${React.Children.count(overflownChildren)} hidden items`,
432
431
  }}
433
- fixed
434
432
  active={isOverflownDialogOpen}
435
433
  onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
436
434
  e.stopPropagation();
437
435
  setIsOverflownDialogOpen(!isOverflownDialogOpen);
438
436
  }}
439
437
  flat
438
+ fixed
440
439
  />,
441
440
  ]
442
441
  : children,
@@ -218,16 +218,6 @@ export const ReqoreDrawer: React.FC<IReqoreDrawerProps> = ({
218
218
  });
219
219
  }
220
220
 
221
- /* Adding a close button to the drawer. */
222
- if (onClose) {
223
- builtActions.push({
224
- responsive: false,
225
- icon: 'CloseLine',
226
- onClick: onClose,
227
- className: 'reqore-drawer-close-button',
228
- });
229
- }
230
-
231
221
  return builtActions;
232
222
  }, [hidable, position, onClose]);
233
223
 
@@ -370,6 +360,10 @@ export const ReqoreDrawer: React.FC<IReqoreDrawerProps> = ({
370
360
  intent={intent}
371
361
  rounded={floating || _isModal ? true : false}
372
362
  flat={flat}
363
+ onClose={onClose}
364
+ closeButtonProps={{
365
+ className: 'reqore-drawer-close-button',
366
+ }}
373
367
  className={`reqore-drawer`}
374
368
  style={{
375
369
  width: '100%',
@@ -132,7 +132,7 @@ const ReqoreDropdownList = memo(
132
132
  />
133
133
  )
134
134
  )}
135
- {!includeBottomControls && <Controls />}
135
+ {!includeBottomControls && Controls}
136
136
  </ReqoreMenu>
137
137
  )}
138
138
  </ReqorePaginationContainer>
@@ -1,14 +1,16 @@
1
- import { memo, useMemo } from 'react';
1
+ import { memo, useMemo, useState } from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { HEADER_SIZE_TO_NUMBER, TSizes } from '../../constants/sizes';
4
4
  import { IReqoreTheme, TReqoreIntent } from '../../constants/theme';
5
5
  import { isStringSize } from '../../helpers/utils';
6
6
  import { useReqoreTheme } from '../../hooks/useTheme';
7
- import { IWithReqoreEffect } from '../../types/global';
7
+ import { useTooltip } from '../../hooks/useTooltip';
8
+ import { IWithReqoreEffect, IWithReqoreTooltip } from '../../types/global';
8
9
  import { StyledTextEffect } from '../Effect';
9
10
 
10
11
  export interface IReqoreHeadingProps
11
12
  extends IWithReqoreEffect,
13
+ IWithReqoreTooltip,
12
14
  React.HTMLAttributes<HTMLHeadingElement> {
13
15
  intent?: TReqoreIntent;
14
16
  size?: 1 | 2 | 3 | 4 | 5 | 6 | TSizes;
@@ -45,18 +47,22 @@ export const StyledHeader = styled(StyledTextEffect)`
45
47
  `;
46
48
 
47
49
  export const ReqoreHeading = memo(
48
- ({ size, children, customTheme, intent, className, ...rest }: IReqoreHeadingProps) => {
50
+ ({ size, children, customTheme, intent, className, tooltip, ...rest }: IReqoreHeadingProps) => {
51
+ const [ref, setRef] = useState<HTMLHeadingElement>(undefined);
49
52
  const theme = useReqoreTheme('main', customTheme, intent);
50
53
  const _size: number = isStringSize(size) ? HEADER_SIZE_TO_NUMBER[size] : size;
51
54
  const HTMLheaderElement = useMemo(() => {
52
55
  return `h${_size}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
53
56
  }, [_size]);
54
57
 
58
+ useTooltip(ref, tooltip);
59
+
55
60
  return (
56
61
  <StyledHeader
57
62
  as={HTMLheaderElement}
58
63
  theme={theme}
59
64
  intent={intent}
65
+ ref={setRef}
60
66
  {...rest}
61
67
  _size={_size}
62
68
  className={`${className || ''} reqore-heading`}
@@ -1,3 +1,4 @@
1
+ import { debounce } from 'lodash';
1
2
  import { memo, useEffect, useRef, useState } from 'react';
2
3
  import { useUnmount, useUpdateEffect } from 'react-use';
3
4
  import styled from 'styled-components';
@@ -27,6 +28,7 @@ export interface IReqorePaginationComponentProps
27
28
  showLabels?: boolean;
28
29
  scrollOnLoadMore?: boolean;
29
30
  autoLoadMore?: boolean;
31
+ changePageOnScroll?: 'vertical' | 'horizontal';
30
32
  scrollContainer?: HTMLElement;
31
33
  scrollToTopOnPageChange?: boolean;
32
34
  }
@@ -79,24 +81,29 @@ function Pagination<T>({
79
81
  renderControls,
80
82
  scrollContainer,
81
83
  scrollToTopOnPageChange = true,
84
+ changePageOnScroll,
82
85
  ...rest
83
86
  }: IReqorePaginationProps<T>) {
84
87
  const [loadMoreRef, setLoadMoreRef] = useState<HTMLButtonElement>(undefined);
85
88
  const [scrollTargetRef, setScrollTargetRef] = useState<HTMLDivElement>(undefined);
86
89
  const observer = useRef<IntersectionObserver>(null);
87
90
 
88
- useEffect(() => {
89
- if (scrollTargetRef && scrollOnLoadMore && !isLastPage && !isFirstPage) {
91
+ useUpdateEffect(() => {
92
+ // If scroll container is defined, do not scroll to load more button
93
+ // because it will be scrolled to the bottom of the container
94
+ // resulting in infinite scroll
95
+ if (!scrollContainer && scrollTargetRef && scrollOnLoadMore && !isLastPage && !isFirstPage) {
90
96
  scrollTargetRef.scrollIntoView({
91
97
  behavior: 'smooth',
92
98
  block: 'end',
93
- inline: 'nearest',
99
+ inline: 'start',
94
100
  });
95
101
  }
96
- }, [currentPage, scrollTargetRef, scrollOnLoadMore, isLastPage, isFirstPage]);
102
+ }, [currentPage, scrollTargetRef, scrollContainer, scrollOnLoadMore, isLastPage, isFirstPage]);
97
103
 
98
104
  // Scroll to bottom on load more
99
- useEffect(() => {
105
+ // Scroll container has power over scroll target
106
+ useUpdateEffect(() => {
100
107
  if (scrollContainer && scrollOnLoadMore && !isFirstPage) {
101
108
  scrollContainer.scrollBy?.({
102
109
  top: scrollContainer.scrollHeight,
@@ -106,7 +113,7 @@ function Pagination<T>({
106
113
  }, [currentPage, scrollContainer, scrollOnLoadMore, isFirstPage]);
107
114
 
108
115
  // Scroll to top on page change
109
- useEffect(() => {
116
+ useUpdateEffect(() => {
110
117
  if (scrollContainer && scrollToTopOnPageChange && !infinite) {
111
118
  scrollContainer.scrollTo?.({
112
119
  top: 0,
@@ -115,6 +122,39 @@ function Pagination<T>({
115
122
  }
116
123
  }, [currentPage, scrollContainer, scrollToTopOnPageChange, infinite]);
117
124
 
125
+ // Switch pages when user scrolls and holds the Shift key
126
+ useEffect(() => {
127
+ const handleScroll = debounce((e: WheelEvent) => {
128
+ const deltaKey = changePageOnScroll === 'vertical' ? 'deltaY' : 'deltaX';
129
+ // Only change pages when user holds the shift key
130
+ // aka horizontal scroll
131
+ if (
132
+ changePageOnScroll === 'vertical' ||
133
+ (changePageOnScroll === 'horizontal' && e.shiftKey)
134
+ ) {
135
+ // Do not horizontal scroll
136
+ e.preventDefault();
137
+ // If the user scrolls to the right, go to the next page
138
+ if (e[deltaKey] > 20) {
139
+ next();
140
+ // If the user scrolls to the left, go to the previous page
141
+ } else if (e[deltaKey] < -20) {
142
+ back();
143
+ }
144
+ }
145
+ }, 30);
146
+
147
+ if (scrollContainer && changePageOnScroll) {
148
+ scrollContainer.addEventListener('wheel', handleScroll);
149
+ }
150
+
151
+ return () => {
152
+ if (scrollContainer) {
153
+ scrollContainer.removeEventListener('wheel', handleScroll);
154
+ }
155
+ };
156
+ }, [currentPage, scrollContainer, changePageOnScroll]);
157
+
118
158
  useUpdateEffect(() => {
119
159
  if (loadMoreRef && autoLoadMore) {
120
160
  observer.current = new IntersectionObserver(
@@ -0,0 +1,79 @@
1
+ import { size } from 'lodash';
2
+ import { memo } from 'react';
3
+ import { IReqorePanelProps } from '.';
4
+ import { ReqoreButton } from '../..';
5
+ import { IWithReqoreCustomTheme } from '../../types/global';
6
+ import ReqoreControlGroup, { IReqoreControlGroupProps } from '../ControlGroup';
7
+
8
+ export interface IReqorePanelNonResponsiveActionsProps
9
+ extends IWithReqoreCustomTheme,
10
+ Omit<IReqoreControlGroupProps, 'children'> {
11
+ onCollapseClick?: () => void;
12
+ onCloseClick?: () => void;
13
+ hasResponsiveActions?: boolean;
14
+ isCollapsed?: boolean;
15
+ showControlButtons?: boolean;
16
+ show?: boolean;
17
+ children?: IReqoreControlGroupProps['children'] | undefined;
18
+ closeButtonProps?: IReqorePanelProps['closeButtonProps'];
19
+ collapseButtonProps?: IReqorePanelProps['collapseButtonProps'];
20
+ isSmall: boolean;
21
+ }
22
+
23
+ export const ReqorePanelNonResponsiveActions = memo(
24
+ ({
25
+ onCollapseClick,
26
+ onCloseClick,
27
+ hasResponsiveActions,
28
+ isCollapsed,
29
+ customTheme,
30
+ children,
31
+ showControlButtons,
32
+ show,
33
+ collapseButtonProps,
34
+ closeButtonProps,
35
+ isSmall,
36
+ ...rest
37
+ }: IReqorePanelNonResponsiveActionsProps) => {
38
+ if (!show || (!size(children) && !showControlButtons)) {
39
+ return null;
40
+ }
41
+
42
+ return (
43
+ <ReqoreControlGroup
44
+ fixed={isSmall ? false : hasResponsiveActions}
45
+ fluid={isSmall ? true : !hasResponsiveActions}
46
+ horizontalAlign='flex-end'
47
+ {...rest}
48
+ >
49
+ {children}
50
+ {!!onCollapseClick && showControlButtons ? (
51
+ <ReqoreButton
52
+ customTheme={customTheme}
53
+ icon={isCollapsed ? 'ArrowDownSLine' : 'ArrowUpSLine'}
54
+ onClick={(e) => {
55
+ e.stopPropagation();
56
+ onCollapseClick();
57
+ }}
58
+ tooltip={isCollapsed ? 'Expand' : 'Collapse'}
59
+ fixed
60
+ {...collapseButtonProps}
61
+ />
62
+ ) : null}
63
+ {!!onCloseClick && showControlButtons ? (
64
+ <ReqoreButton
65
+ fixed
66
+ customTheme={customTheme}
67
+ icon='CloseLine'
68
+ onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
69
+ e.stopPropagation();
70
+ onCloseClick();
71
+ }}
72
+ tooltip='Close'
73
+ {...closeButtonProps}
74
+ />
75
+ ) : null}
76
+ </ReqoreControlGroup>
77
+ );
78
+ }
79
+ );
@@ -1,7 +1,7 @@
1
1
  import { size } from 'lodash';
2
2
  import { darken, rgba } from 'polished';
3
- import { ReactElement, forwardRef, useCallback, useMemo, useState } from 'react';
4
- import { useUpdateEffect } from 'react-use';
3
+ import { forwardRef, ReactElement, useCallback, useMemo, useState } from 'react';
4
+ import { useMeasure, useUpdateEffect } from 'react-use';
5
5
  import styled, { css } from 'styled-components';
6
6
  import {
7
7
  GAP_FROM_SIZE,
@@ -41,8 +41,9 @@ import ReqoreControlGroup from '../ControlGroup';
41
41
  import ReqoreDropdown from '../Dropdown';
42
42
  import { IReqoreDropdownItem } from '../Dropdown/list';
43
43
  import { IReqoreEffect, StyledEffect, TReqoreEffectColor } from '../Effect';
44
- import { ReqoreHeading } from '../Header';
44
+ import { ReqoreHeading, StyledHeader } from '../Header';
45
45
  import ReqoreIcon, { IReqoreIconProps, StyledIconWrapper } from '../Icon';
46
+ import { ReqorePanelNonResponsiveActions } from './NonResponsiveActions';
46
47
 
47
48
  export interface IReqorePanelAction extends IReqoreButtonProps, IWithReqoreTooltip, IReqoreIntent {
48
49
  label?: string | number;
@@ -83,7 +84,11 @@ export interface IReqorePanelProps
83
84
  badge?: TReqoreBadge | TReqoreBadge[];
84
85
  collapsible?: boolean;
85
86
  isCollapsed?: boolean;
87
+ collapseButtonProps?: IReqoreButtonProps;
88
+
86
89
  onClose?: () => void;
90
+ closeButtonProps?: IReqoreButtonProps;
91
+
87
92
  rounded?: boolean;
88
93
  actions?: TReqorePanelActions;
89
94
  bottomActions?: TReqorePanelBottomActions;
@@ -104,6 +109,8 @@ export interface IReqorePanelProps
104
109
  responsiveActions?: boolean;
105
110
  responsiveTitle?: boolean;
106
111
  getContentRef?: (ref: HTMLDivElement) => any;
112
+
113
+ headerProps?: React.HTMLAttributes<unknown>;
107
114
  }
108
115
 
109
116
  export interface IStyledPanel extends IReqorePanelProps {
@@ -111,6 +118,40 @@ export interface IStyledPanel extends IReqorePanelProps {
111
118
  noHorizontalPadding?: boolean;
112
119
  }
113
120
 
121
+ export const StyledPanelTitleHeader = styled.div`
122
+ display: flex;
123
+ justify-content: flex-start;
124
+ align-items: center;
125
+ flex: 1 1 auto;
126
+ width: 100%;
127
+ overflow: hidden;
128
+ `;
129
+
130
+ export const StyledPanelTitleHeaderContent = styled.div`
131
+ display: flex;
132
+ justify-content: flex-start;
133
+ align-items: center;
134
+ flex: 0 1 auto;
135
+ overflow: hidden;
136
+ min-width: ${({ iconSize, hasLabel, hasIcon }) => {
137
+ let width = 0;
138
+
139
+ if (hasIcon) {
140
+ width += iconSize;
141
+ }
142
+
143
+ if (hasLabel) {
144
+ width += 50;
145
+ }
146
+
147
+ return width;
148
+ }}px;
149
+
150
+ ${StyledHeader} {
151
+ min-width: 50px;
152
+ }
153
+ `;
154
+
114
155
  export const StyledPanel = styled(StyledEffect)<IStyledPanel>`
115
156
  background-color: ${({ theme, opacity = 1 }: IStyledPanel) =>
116
157
  rgba(changeDarkness(getMainBackgroundColor(theme), 0.03), opacity)};
@@ -139,7 +180,7 @@ export const StyledPanel = styled(StyledEffect)<IStyledPanel>`
139
180
  cursor: pointer;
140
181
 
141
182
  &:hover {
142
- ${StyledPanelTitle} > div > ${StyledIconWrapper} {
183
+ ${StyledPanelTitle} ${StyledPanelTitleHeaderContent} > ${StyledIconWrapper} {
143
184
  transform: scale(${ACTIVE_ICON_SCALE});
144
185
  }
145
186
 
@@ -206,7 +247,7 @@ export const StyledPanelTitle = styled.div<IStyledPanel>`
206
247
  flex: 0 0 auto;
207
248
  gap: ${GAP_FROM_SIZE.normal}px;
208
249
 
209
- > div > ${StyledIconWrapper} {
250
+ ${StyledPanelTitleHeaderContent} > ${StyledIconWrapper} {
210
251
  transform: scale(${INACTIVE_ICON_SCALE});
211
252
  }
212
253
 
@@ -215,7 +256,7 @@ export const StyledPanelTitle = styled.div<IStyledPanel>`
215
256
  css`
216
257
  cursor: pointer;
217
258
  &:hover {
218
- > div > ${StyledIconWrapper} {
259
+ ${StyledPanelTitleHeaderContent} > ${StyledIconWrapper} {
219
260
  transform: scale(${ACTIVE_ICON_SCALE});
220
261
  }
221
262
 
@@ -255,23 +296,15 @@ export const StyledPanelContent = styled.div<IStyledPanel>`
255
296
  font-size: ${({ size }) => TEXT_FROM_SIZE[size]}px;
256
297
  `;
257
298
 
258
- export const StyledPanelTitleHeader = styled.div`
259
- display: flex;
260
- justify-content: flex-start;
261
- align-items: center;
262
- flex: 1 1 auto;
263
- width: 100%;
264
- overflow: hidden;
265
- min-width: 100px;
266
- `;
267
-
268
299
  export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
269
300
  (
270
301
  {
271
302
  children,
272
303
  label,
304
+ collapseButtonProps = {},
273
305
  collapsible,
274
306
  onClose,
307
+ closeButtonProps = {},
275
308
  rounded = true,
276
309
  actions = [],
277
310
  bottomActions = [],
@@ -300,6 +333,7 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
300
333
  responsiveTitle = true,
301
334
  size: panelSize = 'normal',
302
335
  getContentRef,
336
+ headerProps = {},
303
337
  ...rest
304
338
  }: IReqorePanelProps,
305
339
  ref
@@ -315,6 +349,7 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
315
349
  const isMobile = useReqoreProperty('isMobile');
316
350
  const { targetRef } = useCombinedRefs(ref);
317
351
  const [itemRef, setItemRef] = useState<HTMLDivElement>(undefined);
352
+ const [measureRef, { width }] = useMeasure();
318
353
 
319
354
  useTooltip(itemRef, tooltip);
320
355
 
@@ -340,6 +375,8 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
340
375
  [label, icon, badge]
341
376
  );
342
377
 
378
+ const isSmall = useMemo(() => width < 480 && process.env.NODE_ENV !== 'test', [width]);
379
+
343
380
  // If collapsible is true, toggle the isCollapsed state
344
381
  // If the isCollapsed state is true, the component is expanded
345
382
  // If the isCollapsed state is false, the component is collapsed
@@ -557,51 +594,80 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
557
594
  opacity={opacity ?? (minimal ? 0 : 1)}
558
595
  noHorizontalPadding={noHorizontalPadding}
559
596
  responsive={responsiveTitle}
560
- isMobile={isMobile}
597
+ isMobile={isMobile || isSmall}
598
+ ref={measureRef}
561
599
  >
562
600
  {hasTitleHeader && (
563
601
  <StyledPanelTitleHeader>
564
- {icon || iconImage ? (
565
- <ReqoreIcon
566
- size={`${
567
- ICON_FROM_HEADER_SIZE[headerSize || HEADER_SIZE_TO_NUMBER[panelSize]]
568
- }px`}
569
- icon={icon}
570
- image={iconImage}
571
- margin='right'
572
- color={iconColor}
573
- {...iconProps}
574
- />
575
- ) : null}
576
- {typeof label === 'string' ? (
577
- <ReqoreHeading
578
- size={headerSize || panelSize}
579
- customTheme={theme}
580
- effect={{
581
- noWrap: true,
582
- ...headerEffect,
583
- }}
602
+ {icon || iconImage || label ? (
603
+ <StyledPanelTitleHeaderContent
604
+ size={panelSize}
605
+ {...headerProps}
606
+ hasLabel={!!label}
607
+ hasIcon={!!icon || !!iconImage}
608
+ iconSize={ICON_FROM_HEADER_SIZE[headerSize || HEADER_SIZE_TO_NUMBER[panelSize]]}
584
609
  >
585
- {label}
586
- </ReqoreHeading>
587
- ) : (
588
- label
589
- )}
610
+ {icon || iconImage ? (
611
+ <ReqoreIcon
612
+ size={`${
613
+ ICON_FROM_HEADER_SIZE[headerSize || HEADER_SIZE_TO_NUMBER[panelSize]]
614
+ }px`}
615
+ icon={icon}
616
+ image={iconImage}
617
+ margin='right'
618
+ color={iconColor}
619
+ tooltip={{
620
+ content: label,
621
+ }}
622
+ {...iconProps}
623
+ />
624
+ ) : null}
625
+ {typeof label === 'string' ? (
626
+ <ReqoreHeading
627
+ size={headerSize || panelSize}
628
+ customTheme={theme}
629
+ effect={{
630
+ noWrap: true,
631
+ ...headerEffect,
632
+ }}
633
+ tooltip={label}
634
+ >
635
+ {label}
636
+ </ReqoreHeading>
637
+ ) : (
638
+ label
639
+ )}
640
+ </StyledPanelTitleHeaderContent>
641
+ ) : null}
590
642
  {badge || badge === 0 ? (
591
- <>
592
- <ButtonBadge
593
- color={changeLightness(theme.main, 0.18)}
594
- size={getOneHigherSize(panelSize)}
595
- content={badge}
596
- />
597
- </>
643
+ <ButtonBadge
644
+ color={changeLightness(theme.main, 0.18)}
645
+ size={getOneHigherSize(panelSize)}
646
+ content={badge}
647
+ wrapGroup={isSmall}
648
+ />
598
649
  ) : null}
650
+ <ReqorePanelNonResponsiveActions
651
+ show={isSmall}
652
+ isSmall={isSmall}
653
+ showControlButtons
654
+ size={panelSize}
655
+ hasResponsiveActions={hasResponsiveActions(actions)}
656
+ customTheme={theme}
657
+ isCollapsed={_isCollapsed}
658
+ onCollapseClick={collapsible ? handleCollapseClick : undefined}
659
+ onCloseClick={onClose}
660
+ closeButtonProps={closeButtonProps}
661
+ collapseButtonProps={collapseButtonProps}
662
+ fluid={false}
663
+ style={{ marginLeft: 'auto' }}
664
+ />
599
665
  </StyledPanelTitleHeader>
600
666
  )}
601
667
  {hasResponsiveActions(actions) && (
602
668
  <ReqoreControlGroup
603
669
  responsive={responsiveActions}
604
- fluid={responsiveActions || isMobile}
670
+ fluid={responsiveActions || isSmall}
605
671
  horizontalAlign='flex-end'
606
672
  customTheme={theme}
607
673
  size={panelSize}
@@ -611,34 +677,21 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
611
677
  )}
612
678
  {collapsible || onClose || hasNonResponsiveActions(actions) ? (
613
679
  <>
614
- <ReqoreControlGroup
615
- fixed={isMobile ? false : hasResponsiveActions(actions)}
616
- fluid={isMobile ? true : !hasResponsiveActions(actions)}
617
- horizontalAlign='flex-end'
680
+ <ReqorePanelNonResponsiveActions
681
+ show
682
+ isSmall={isSmall}
683
+ showControlButtons={!isSmall}
618
684
  size={panelSize}
685
+ hasResponsiveActions={hasResponsiveActions(actions)}
686
+ customTheme={theme}
687
+ isCollapsed={_isCollapsed}
688
+ onCollapseClick={collapsible ? handleCollapseClick : undefined}
689
+ onCloseClick={onClose}
690
+ closeButtonProps={closeButtonProps}
691
+ collapseButtonProps={collapseButtonProps}
619
692
  >
620
693
  {actions.map(renderNonResponsiveActions())}
621
- {collapsible && (
622
- <ReqoreButton
623
- customTheme={theme}
624
- icon={_isCollapsed ? 'ArrowDownSLine' : 'ArrowUpSLine'}
625
- onClick={handleCollapseClick}
626
- tooltip={_isCollapsed ? 'Expand' : 'Collapse'}
627
- fixed
628
- />
629
- )}
630
- {onClose && (
631
- <ReqoreButton
632
- fixed
633
- customTheme={theme}
634
- icon='CloseLine'
635
- onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
636
- e.stopPropagation();
637
- onClose?.();
638
- }}
639
- />
640
- )}
641
- </ReqoreControlGroup>
694
+ </ReqorePanelNonResponsiveActions>
642
695
  </>
643
696
  ) : null}
644
697
  </StyledPanelTitle>
@@ -14,7 +14,7 @@ export interface IReqoreTagGroup
14
14
  }
15
15
 
16
16
  const StyledTagGroup = styled.div`
17
- flex-shrink: 0;
17
+ flex-shrink: ${({ wrap }: IReqoreTagGroup) => (wrap ? 1 : 0)};
18
18
  display: flex;
19
19
  flex-wrap: ${({ wrap }: IReqoreTagGroup) => (wrap ? 'wrap' : 'nowrap')};
20
20
  gap: ${({ gapSize }: IReqoreTagGroup) => GAP_FROM_SIZE[gapSize]}px;