@qoretechnologies/reqore 0.33.0 → 0.34.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 (60) hide show
  1. package/__tests__/collection.test.tsx +22 -1
  2. package/__tests__/containers/paging.test.tsx +106 -0
  3. package/__tests__/hooks/useReqorePaging.test.tsx +4 -0
  4. package/__tests__/pagination.test.tsx +4 -0
  5. package/dist/components/Collection/index.d.ts +1 -6
  6. package/dist/components/Collection/index.d.ts.map +1 -1
  7. package/dist/components/Collection/index.js +7 -15
  8. package/dist/components/Collection/index.js.map +1 -1
  9. package/dist/components/Dropdown/index.d.ts +5 -1
  10. package/dist/components/Dropdown/index.d.ts.map +1 -1
  11. package/dist/components/Dropdown/index.js +2 -2
  12. package/dist/components/Dropdown/index.js.map +1 -1
  13. package/dist/components/Dropdown/list.d.ts +3 -1
  14. package/dist/components/Dropdown/list.d.ts.map +1 -1
  15. package/dist/components/Dropdown/list.js +13 -5
  16. package/dist/components/Dropdown/list.js.map +1 -1
  17. package/dist/components/Menu/index.d.ts +1 -0
  18. package/dist/components/Menu/index.d.ts.map +1 -1
  19. package/dist/components/Menu/index.js +4 -1
  20. package/dist/components/Menu/index.js.map +1 -1
  21. package/dist/components/Paging/index.d.ts +3 -1
  22. package/dist/components/Paging/index.d.ts.map +1 -1
  23. package/dist/components/Paging/index.js +31 -10
  24. package/dist/components/Paging/index.js.map +1 -1
  25. package/dist/components/Panel/index.d.ts +1 -0
  26. package/dist/components/Panel/index.d.ts.map +1 -1
  27. package/dist/components/Panel/index.js +2 -2
  28. package/dist/components/Panel/index.js.map +1 -1
  29. package/dist/constants/paging.d.ts +11 -4
  30. package/dist/constants/paging.d.ts.map +1 -1
  31. package/dist/constants/paging.js +7 -3
  32. package/dist/constants/paging.js.map +1 -1
  33. package/dist/containers/Paging.d.ts +15 -0
  34. package/dist/containers/Paging.d.ts.map +1 -0
  35. package/dist/containers/Paging.js +51 -0
  36. package/dist/containers/Paging.js.map +1 -0
  37. package/dist/hooks/usePaging.d.ts +1 -0
  38. package/dist/hooks/usePaging.d.ts.map +1 -1
  39. package/dist/hooks/usePaging.js +2 -0
  40. package/dist/hooks/usePaging.js.map +1 -1
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +3 -1
  44. package/dist/index.js.map +1 -1
  45. package/package.json +1 -1
  46. package/src/components/Collection/index.tsx +45 -68
  47. package/src/components/Dropdown/index.tsx +21 -12
  48. package/src/components/Dropdown/list.tsx +43 -24
  49. package/src/components/Menu/index.tsx +3 -2
  50. package/src/components/Paging/index.tsx +32 -4
  51. package/src/components/Panel/index.tsx +4 -1
  52. package/src/constants/paging.ts +27 -8
  53. package/src/containers/Paging.tsx +90 -0
  54. package/src/hooks/usePaging.ts +4 -0
  55. package/src/index.tsx +1 -0
  56. package/src/stories/Collection/Collection.stories.tsx +20 -1
  57. package/src/stories/Dropdown/Dropdown.stories.tsx +54 -35
  58. package/src/stories/Menu/Menu.stories.tsx +4 -0
  59. package/src/stories/Paging/Paging.stories.tsx +19 -1
  60. package/tests.json +1 -1
@@ -2,25 +2,16 @@ import { size } from 'lodash';
2
2
  import { useCallback, useMemo, useState } from 'react';
3
3
  import { useDebounce, useUpdateEffect } from 'react-use';
4
4
  import styled, { css } from 'styled-components';
5
- import { getPaginationOptionsFromType, TReqorePaginationType } from '../../constants/paging';
6
- import { PADDING_FROM_SIZE } from '../../constants/sizes';
7
- import { IReqorePagingOptions, useReqorePaging } from '../../hooks/usePaging';
5
+ import { TReqorePaginationType } from '../../constants/paging';
6
+ import { ReqorePaginationContainer } from '../../containers/Paging';
8
7
  import { IReqoreIconName } from '../../types/icons';
9
8
  import { IReqoreColumnsProps, StyledColumns } from '../Columns';
10
9
  import ReqoreInput, { IReqoreInputProps } from '../Input';
11
10
  import ReqoreMessage from '../Message';
12
- import { IReqorePaginationComponentProps, ReqorePagination } from '../Paging';
13
11
  import { IReqorePanelAction, IReqorePanelProps, ReqorePanel, TReqorePanelActions } from '../Panel';
14
- import { ReqoreVerticalSpacer } from '../Spacer';
15
12
  import { sortTableData } from '../Table/helpers';
16
13
  import { IReqoreCollectionItemProps, ReqoreCollectionItem } from './item';
17
14
 
18
- export interface IReqoreCollectionPagingOptions
19
- extends Omit<IReqorePagingOptions<IReqoreCollectionItemProps>, 'items'>,
20
- IReqorePaginationComponentProps {
21
- pageControlsPosition?: 'top' | 'bottom' | 'both';
22
- }
23
-
24
15
  export interface IReqoreCollectionProps
25
16
  extends Pick<
26
17
  IReqorePanelProps,
@@ -69,7 +60,7 @@ export interface IReqoreCollectionProps
69
60
  displayButtonTooltip?: (display?: 'list' | 'grid') => string;
70
61
  inputPlaceholder?: (items?: IReqoreCollectionItemProps[]) => string;
71
62
 
72
- paging?: TReqorePaginationType;
63
+ paging?: TReqorePaginationType<IReqoreCollectionItemProps>;
73
64
  }
74
65
 
75
66
  export const StyledCollectionWrapper = styled(StyledColumns)`
@@ -118,6 +109,7 @@ export const ReqoreCollection = ({
118
109
  const [sort, setSort] = useState<'asc' | 'desc'>('asc');
119
110
  const [query, setQuery] = useState<string>('');
120
111
  const [preQuery, setPreQuery] = useState<string>('');
112
+ const [contentRef, setContentRef] = useState<HTMLDivElement>(undefined);
121
113
 
122
114
  useUpdateEffect(() => {
123
115
  setShowAs(showAs);
@@ -183,18 +175,6 @@ export const ReqoreCollection = ({
183
175
  });
184
176
  }, [items, query, sortedItems]);
185
177
 
186
- const {
187
- pagingOptions = {},
188
- componentOptions = {},
189
- pageControlsPosition,
190
- } = useMemo(() => getPaginationOptionsFromType(paging), [paging]);
191
-
192
- const { items: finalItems, ...pagingData } = useReqorePaging({
193
- items: filteredItems,
194
- ...pagingOptions,
195
- enabled: !!paging,
196
- });
197
-
198
178
  const handlePreQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => {
199
179
  setPreQuery(event.target.value);
200
180
  };
@@ -209,7 +189,7 @@ export const ReqoreCollection = ({
209
189
  icon: _showAs === 'grid' ? 'ListUnordered' : 'GridLine',
210
190
  onClick: () => setShowAs(_showAs === 'grid' ? 'list' : 'grid'),
211
191
  tooltip: displayButtonTooltip(_showAs),
212
- disabled: !size(finalItems),
192
+ disabled: !size(filteredItems),
213
193
  textAlign: 'center',
214
194
  ...displayButtonProps,
215
195
  },
@@ -221,7 +201,7 @@ export const ReqoreCollection = ({
221
201
  icon: sort === 'desc' ? 'SortDesc' : 'SortAsc',
222
202
  onClick: () => setSort(sort === 'desc' ? 'asc' : 'desc'),
223
203
  tooltip: sortButtonTooltip(sort),
224
- disabled: !size(finalItems),
204
+ disabled: !size(filteredItems),
225
205
  textAlign: 'center',
226
206
  ...sortButtonProps,
227
207
  });
@@ -251,56 +231,53 @@ export const ReqoreCollection = ({
251
231
  }
252
232
 
253
233
  return [...actions, toolbarGroup];
254
- }, [filterable, preQuery, query, rest.actions, _showAs, sort, sortable, finalItems]);
234
+ }, [filterable, preQuery, query, rest.actions, _showAs, sort, sortable, filteredItems]);
255
235
 
256
236
  const renderContent = useCallback(() => {
257
237
  return contentRenderer(
258
238
  <>
259
- {!!paging && (pageControlsPosition === 'top' || pageControlsPosition === 'both') ? (
260
- <>
261
- <ReqorePagination size={rest.size} {...pagingData} {...componentOptions} />
262
- <ReqoreVerticalSpacer height={PADDING_FROM_SIZE[rest.size || 'normal']} />
263
- </>
264
- ) : null}
265
- {!size(finalItems) ? (
266
- <ReqoreMessage flat icon='Search2Line'>
267
- {emptyMessage}
268
- </ReqoreMessage>
269
- ) : (
270
- <StyledCollectionWrapper
271
- columns={_showAs === 'grid' ? 'auto-fit' : 1}
272
- columnsGap={stacked ? '0px' : columnsGap}
273
- rounded={rounded}
274
- stacked={stacked}
275
- {...rest}
276
- >
277
- {finalItems?.map((item, index) => (
278
- <ReqoreCollectionItem
279
- size={rest.size}
280
- {...item}
281
- icon={item.icon || (item.selected ? selectedIcon : undefined)}
282
- key={index}
283
- rounded={!stacked}
284
- maxContentHeight={maxItemHeight}
285
- />
286
- ))}
287
- </StyledCollectionWrapper>
288
- )}
289
- {!!paging &&
290
- (!pageControlsPosition ||
291
- pageControlsPosition === 'bottom' ||
292
- pageControlsPosition === 'both') ? (
293
- <>
294
- <ReqoreVerticalSpacer height={PADDING_FROM_SIZE[rest.size || 'normal']} />
295
- <ReqorePagination size={rest.size} {...pagingData} {...componentOptions} />
296
- </>
297
- ) : null}
239
+ <ReqorePaginationContainer
240
+ items={filteredItems}
241
+ type={paging}
242
+ size={rest.size}
243
+ scrollContainer={contentRef}
244
+ >
245
+ {(finalItems) => (
246
+ <>
247
+ {!size(finalItems) ? (
248
+ <ReqoreMessage flat icon='Search2Line'>
249
+ {emptyMessage}
250
+ </ReqoreMessage>
251
+ ) : (
252
+ <StyledCollectionWrapper
253
+ columns={_showAs === 'grid' ? 'auto-fit' : 1}
254
+ columnsGap={stacked ? '0px' : columnsGap}
255
+ rounded={rounded}
256
+ stacked={stacked}
257
+ ref={setContentRef}
258
+ {...rest}
259
+ >
260
+ {finalItems?.map((item, index) => (
261
+ <ReqoreCollectionItem
262
+ size={rest.size}
263
+ {...item}
264
+ icon={item.icon || (item.selected ? selectedIcon : undefined)}
265
+ key={index}
266
+ rounded={!stacked}
267
+ maxContentHeight={maxItemHeight}
268
+ />
269
+ ))}
270
+ </StyledCollectionWrapper>
271
+ )}
272
+ </>
273
+ )}
274
+ </ReqorePaginationContainer>
298
275
  </>,
299
- finalItems
276
+ filteredItems
300
277
  );
301
278
  }, [
302
279
  contentRenderer,
303
- finalItems,
280
+ filteredItems,
304
281
  _showAs,
305
282
  columnsGap,
306
283
  emptyMessage,
@@ -1,10 +1,12 @@
1
1
  import { size } from 'lodash';
2
2
  import React, { memo, useMemo } from 'react';
3
- import { ReqorePopover } from '../..';
3
+ import { ReqorePanel, ReqorePopover } from '../..';
4
+ import { TReqorePaginationType } from '../../constants/paging';
4
5
  import { IPopoverOptions } from '../../hooks/usePopover';
5
6
  import { IReqoreIconName } from '../../types/icons';
6
7
  import ReqoreButton, { IReqoreButtonProps } from '../Button';
7
8
  import { IReqoreInputProps } from '../Input';
9
+ import { IReqorePanelProps } from '../Panel';
8
10
  import ReqoreDropdownList, { IReqoreDropdownItem, TDropdownItemOnClick } from './list';
9
11
 
10
12
  export interface IReqoreDropdownProps extends Partial<Omit<IPopoverOptions, 'openOnMount'>> {
@@ -25,7 +27,9 @@ export interface IReqoreDropdownProps extends Partial<Omit<IPopoverOptions, 'ope
25
27
  onItemSelect?: TDropdownItemOnClick;
26
28
  style?: React.CSSProperties;
27
29
  inputProps?: IReqoreInputProps;
30
+ wrapperProps?: IReqorePanelProps;
28
31
  scrollToSelected?: boolean;
32
+ paging?: TReqorePaginationType<IReqoreDropdownItem>;
29
33
  }
30
34
 
31
35
  function ReqoreDropdown<T extends unknown = IReqoreButtonProps>({
@@ -61,6 +65,8 @@ function ReqoreDropdown<T extends unknown = IReqoreButtonProps>({
61
65
  passPopoverData,
62
66
  inputProps,
63
67
  scrollToSelected,
68
+ wrapperProps,
69
+ paging,
64
70
  ...rest
65
71
  }: IReqoreDropdownProps & T) {
66
72
  const componentProps = useMemo(
@@ -78,17 +84,20 @@ function ReqoreDropdown<T extends unknown = IReqoreButtonProps>({
78
84
 
79
85
  const popoverContent = useMemo(() => {
80
86
  return size(items) ? (
81
- <ReqoreDropdownList
82
- multiSelect={multiSelect}
83
- listStyle={listStyle}
84
- width={useTargetWidth ? '100%' : listWidth}
85
- height={listHeight}
86
- items={items}
87
- filterable={filterable}
88
- onItemSelect={onItemSelect}
89
- inputProps={inputProps}
90
- scrollToSelected={scrollToSelected}
91
- />
87
+ <ReqorePanel flat size='small' {...wrapperProps}>
88
+ <ReqoreDropdownList
89
+ multiSelect={multiSelect}
90
+ listStyle={listStyle}
91
+ width={useTargetWidth ? '100%' : listWidth}
92
+ height={listHeight}
93
+ items={items}
94
+ filterable={filterable}
95
+ onItemSelect={onItemSelect}
96
+ inputProps={inputProps}
97
+ scrollToSelected={scrollToSelected}
98
+ paging={paging}
99
+ />
100
+ </ReqorePanel>
92
101
  ) : undefined;
93
102
  }, [items]);
94
103
 
@@ -1,9 +1,14 @@
1
+ import { size } from 'lodash';
1
2
  import React, { memo, useEffect, useMemo, useState } from 'react';
3
+ import { TReqorePaginationType } from '../../constants/paging';
4
+ import { PADDING_FROM_SIZE } from '../../constants/sizes';
5
+ import { ReqorePaginationContainer } from '../../containers/Paging';
2
6
  import { IReqoreComponent } from '../../types/global';
3
7
  import ReqoreInput, { IReqoreInputProps } from '../Input';
4
8
  import ReqoreMenu from '../Menu';
5
9
  import ReqoreMenuDivider from '../Menu/divider';
6
10
  import ReqoreMenuItem, { IReqoreMenuItemProps } from '../Menu/item';
11
+ import { ReqoreVerticalSpacer } from '../Spacer';
7
12
 
8
13
  export type TDropdownItemOnClick = (item: IReqoreDropdownItem) => void;
9
14
  export interface IReqoreDropdownItem
@@ -27,6 +32,7 @@ export interface IReqoreDropdownListProps extends IReqoreComponent {
27
32
  onItemSelect?: TDropdownItemOnClick;
28
33
  inputProps?: IReqoreInputProps;
29
34
  scrollToSelected?: boolean;
35
+ paging?: TReqorePaginationType<TReqoreDropdownItem>;
30
36
  }
31
37
 
32
38
  const ReqoreDropdownList = memo(
@@ -41,9 +47,11 @@ const ReqoreDropdownList = memo(
41
47
  onItemSelect,
42
48
  inputProps,
43
49
  scrollToSelected,
50
+ paging,
44
51
  }: IReqoreDropdownListProps) => {
45
52
  const [_items, setItems] = useState<TReqoreDropdownItems>(items);
46
53
  const [query, setQuery] = useState<string>('');
54
+ const [menuRef, setMenuRef] = useState<HTMLDivElement>(undefined);
47
55
 
48
56
  useEffect(() => {
49
57
  setItems(items);
@@ -84,40 +92,51 @@ const ReqoreDropdownList = memo(
84
92
  };
85
93
 
86
94
  return (
87
- <ReqoreMenu
88
- _insidePopover={!multiSelect}
89
- _popoverId={_popoverId}
90
- style={listStyle}
91
- width={width}
92
- maxHeight={height || '300px'}
93
- >
95
+ <>
94
96
  {filterable && (
95
97
  <>
96
98
  <ReqoreInput
97
99
  value={query}
100
+ icon='SearchLine'
98
101
  onChange={handleQueryChange}
99
- placeholder='Filter'
102
+ placeholder={`Search ${size(_items)} items...`}
100
103
  onClearClick={() => setQuery('')}
101
104
  {...inputProps}
102
105
  />
106
+ <ReqoreVerticalSpacer height={PADDING_FROM_SIZE.normal} />
103
107
  </>
104
108
  )}
105
- {filteredItems.map(
106
- ({ onClick, dividerAlign, divider, ...item }: IReqoreDropdownItem, index: number) =>
107
- divider ? (
108
- <ReqoreMenuDivider key={index} {...item} align={dividerAlign} />
109
- ) : (
110
- <ReqoreMenuItem
111
- key={index}
112
- {...item}
113
- label={item.label || item.value}
114
- onClick={() => handleItemClick({ ...item, onClick })}
115
- rightIcon={item.selected ? 'CheckLine' : undefined}
116
- scrollIntoView={scrollToSelected && item.selected && !multiSelect}
117
- />
118
- )
119
- )}
120
- </ReqoreMenu>
109
+ <ReqorePaginationContainer type={paging} items={filteredItems} scrollContainer={menuRef}>
110
+ {(finalItems, Controls, { includeBottomControls }) => (
111
+ <ReqoreMenu
112
+ _insidePopover={!multiSelect}
113
+ _popoverId={_popoverId}
114
+ style={listStyle}
115
+ width={width}
116
+ maxHeight={height || '300px'}
117
+ padded={false}
118
+ ref={setMenuRef}
119
+ >
120
+ {finalItems.map(
121
+ ({ onClick, dividerAlign, divider, ...item }: IReqoreDropdownItem, index: number) =>
122
+ divider ? (
123
+ <ReqoreMenuDivider key={index} {...item} align={dividerAlign} />
124
+ ) : (
125
+ <ReqoreMenuItem
126
+ key={index}
127
+ {...item}
128
+ label={item.label || item.value}
129
+ onClick={() => handleItemClick({ ...item, onClick })}
130
+ rightIcon={item.selected ? 'CheckLine' : undefined}
131
+ scrollIntoView={scrollToSelected && item.selected && !multiSelect}
132
+ />
133
+ )
134
+ )}
135
+ {!includeBottomControls && <Controls />}
136
+ </ReqoreMenu>
137
+ )}
138
+ </ReqorePaginationContainer>
139
+ </>
121
140
  );
122
141
  }
123
142
  );
@@ -21,6 +21,7 @@ export interface IReqoreMenuProps
21
21
  wrapText?: boolean;
22
22
  flat?: boolean;
23
23
  rounded?: boolean;
24
+ padded?: boolean;
24
25
  }
25
26
 
26
27
  export interface IReqoreMenuStyle extends IReqoreMenuProps {
@@ -30,7 +31,7 @@ export interface IReqoreMenuStyle extends IReqoreMenuProps {
30
31
  const StyledReqoreMenu = styled.div<IReqoreMenuStyle>`
31
32
  width: ${({ width }) => width || undefined};
32
33
  min-width: ${({ width }) => (width ? undefined : '160px')};
33
- padding: 5px;
34
+ padding: ${({ padded = true }) => (padded ? '5px' : undefined)};
34
35
  max-height: ${({ maxHeight }) => maxHeight || undefined};
35
36
  overflow-y: auto;
36
37
  overflow-x: hidden;
@@ -68,7 +69,7 @@ const ReqoreMenu = forwardRef<HTMLDivElement, IReqoreMenuProps>(
68
69
  minimal,
69
70
  ...rest
70
71
  }: IReqoreMenuProps,
71
- ref: any
72
+ ref
72
73
  ) => {
73
74
  const theme = useReqoreTheme('main', customTheme, intent);
74
75
 
@@ -27,6 +27,8 @@ export interface IReqorePaginationComponentProps
27
27
  showLabels?: boolean;
28
28
  scrollOnLoadMore?: boolean;
29
29
  autoLoadMore?: boolean;
30
+ scrollContainer?: HTMLElement;
31
+ scrollToTopOnPageChange?: boolean;
30
32
  }
31
33
  export interface IReqorePaginationProps<T>
32
34
  extends Omit<IReqorePagingResult<T>, 'items'>,
@@ -34,7 +36,7 @@ export interface IReqorePaginationProps<T>
34
36
 
35
37
  export const StyledPagesWrapper = styled(ReqoreControlGroup)`
36
38
  width: unset;
37
- max-width: 100%;
39
+ flex: 0 auto;
38
40
  justify-content: center;
39
41
 
40
42
  & > ${StyledButton} {
@@ -74,6 +76,9 @@ function Pagination<T>({
74
76
  scrollOnLoadMore,
75
77
  autoLoadMore,
76
78
  itemsPerPage,
79
+ renderControls,
80
+ scrollContainer,
81
+ scrollToTopOnPageChange = true,
77
82
  ...rest
78
83
  }: IReqorePaginationProps<T>) {
79
84
  const [loadMoreRef, setLoadMoreRef] = useState<HTMLButtonElement>(undefined);
@@ -90,6 +95,26 @@ function Pagination<T>({
90
95
  }
91
96
  }, [currentPage, scrollTargetRef, scrollOnLoadMore, isLastPage, isFirstPage]);
92
97
 
98
+ // Scroll to bottom on load more
99
+ useEffect(() => {
100
+ if (scrollContainer && scrollOnLoadMore && !isFirstPage) {
101
+ scrollContainer.scrollBy?.({
102
+ top: scrollContainer.scrollHeight,
103
+ behavior: 'smooth',
104
+ });
105
+ }
106
+ }, [currentPage, scrollContainer, scrollOnLoadMore, isFirstPage]);
107
+
108
+ // Scroll to top on page change
109
+ useEffect(() => {
110
+ if (scrollContainer && scrollToTopOnPageChange && !infinite) {
111
+ scrollContainer.scrollTo?.({
112
+ top: 0,
113
+ behavior: 'smooth',
114
+ });
115
+ }
116
+ }, [currentPage, scrollContainer, scrollToTopOnPageChange, infinite]);
117
+
93
118
  useUpdateEffect(() => {
94
119
  if (loadMoreRef && autoLoadMore) {
95
120
  observer.current = new IntersectionObserver(
@@ -113,7 +138,9 @@ function Pagination<T>({
113
138
  observer.current?.disconnect();
114
139
  });
115
140
 
116
- if (pageCount === 1 || pageCount === 0) return null;
141
+ if (!renderControls) {
142
+ return null;
143
+ }
117
144
 
118
145
  return (
119
146
  <ReqoreControlGroup
@@ -138,7 +165,8 @@ function Pagination<T>({
138
165
  {showLabels && loadMoreLabel}
139
166
  </ReqoreButton>
140
167
  </ReqoreControlGroup>
141
- ) : (
168
+ ) : null}
169
+ {!infinite ? (
142
170
  <>
143
171
  {showPagesAs === 'list' || (showControls && !showPages) ? (
144
172
  <ReqoreButton
@@ -224,7 +252,7 @@ function Pagination<T>({
224
252
  </ReqoreButton>
225
253
  ) : null}
226
254
  </>
227
- )}
255
+ ) : null}
228
256
  </ReqoreControlGroup>
229
257
  );
230
258
  }
@@ -1,6 +1,6 @@
1
1
  import { size } from 'lodash';
2
2
  import { darken, rgba } from 'polished';
3
- import { forwardRef, ReactElement, useCallback, useMemo, useState } from 'react';
3
+ import { ReactElement, forwardRef, useCallback, useMemo, useState } from 'react';
4
4
  import { useUpdateEffect } from 'react-use';
5
5
  import styled, { css } from 'styled-components';
6
6
  import {
@@ -103,6 +103,7 @@ export interface IReqorePanelProps
103
103
  iconColor?: TReqoreEffectColor;
104
104
  responsiveActions?: boolean;
105
105
  responsiveTitle?: boolean;
106
+ getContentRef?: (ref: HTMLDivElement) => any;
106
107
  }
107
108
 
108
109
  export interface IStyledPanel extends IReqorePanelProps {
@@ -298,6 +299,7 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
298
299
  responsiveActions = true,
299
300
  responsiveTitle = true,
300
301
  size: panelSize = 'normal',
302
+ getContentRef,
301
303
  ...rest
302
304
  }: IReqorePanelProps,
303
305
  ref
@@ -643,6 +645,7 @@ export const ReqorePanel = forwardRef<HTMLDivElement, IReqorePanelProps>(
643
645
  padded={padded}
644
646
  minimal={minimal}
645
647
  size={contentSize || panelSize}
648
+ ref={getContentRef}
646
649
  noHorizontalPadding={noHorizontalPadding}
647
650
  >
648
651
  {children}
@@ -1,19 +1,32 @@
1
- import { IReqoreCollectionPagingOptions } from '../components/Collection';
2
- import { IReqorePaginationProps } from '../components/Paging';
1
+ import { IReqorePaginationComponentProps, IReqorePaginationProps } from '../components/Paging';
3
2
  import { IReqorePagingOptions } from '../hooks/usePaging';
4
3
 
5
- export type TReqorePaginationType =
6
- | true
4
+ export type TReqorePaginationType<T> =
5
+ | 'buttons'
7
6
  | 'list'
8
7
  | 'infinite'
9
8
  | 'auto-load'
10
- | IReqoreCollectionPagingOptions;
9
+ | IReqoreComponentPagingOptions<T>;
11
10
 
12
- export function getPaginationOptionsFromType<T>(type: TReqorePaginationType): {
11
+ export interface IReqoreComponentPagingOptions<T>
12
+ extends Omit<IReqorePagingOptions<T>, 'items'>,
13
+ IReqorePaginationComponentProps {
14
+ pageControlsPosition?: 'top' | 'bottom' | 'both';
15
+ includeTopControls?: boolean;
16
+ includeBottomControls?: boolean;
17
+ }
18
+
19
+ export type TReqorePaginationTypeResult<T> = {
13
20
  pagingOptions?: Omit<IReqorePagingOptions<T>, 'items'>;
14
21
  componentOptions?: Partial<Omit<IReqorePaginationProps<T>, 'items'>>;
15
22
  pageControlsPosition?: 'top' | 'bottom' | 'both';
16
- } {
23
+ includeTopControls?: boolean;
24
+ includeBottomControls?: boolean;
25
+ };
26
+
27
+ export function getPaginationOptionsFromType<T>(
28
+ type: TReqorePaginationType<T>
29
+ ): TReqorePaginationTypeResult<T> {
17
30
  if (!type) return {};
18
31
 
19
32
  if (typeof type === 'object') {
@@ -23,17 +36,21 @@ export function getPaginationOptionsFromType<T>(type: TReqorePaginationType): {
23
36
  pagesToShow,
24
37
  startPage,
25
38
  pageControlsPosition = 'bottom',
39
+ includeBottomControls,
40
+ includeTopControls,
26
41
  ...componentOptions
27
42
  } = type;
28
43
  return {
29
44
  pagingOptions: { infinite, itemsPerPage, pagesToShow, startPage },
30
45
  pageControlsPosition,
31
46
  componentOptions,
47
+ includeBottomControls,
48
+ includeTopControls,
32
49
  };
33
50
  }
34
51
 
35
52
  switch (type) {
36
- case true:
53
+ case 'buttons':
37
54
  return {};
38
55
  case 'list':
39
56
  return {
@@ -44,5 +61,7 @@ export function getPaginationOptionsFromType<T>(type: TReqorePaginationType): {
44
61
  case 'infinite':
45
62
  case 'auto-load':
46
63
  return { pagingOptions: { infinite: true } };
64
+ default:
65
+ return {};
47
66
  }
48
67
  }
@@ -0,0 +1,90 @@
1
+ import { memo, useMemo } from 'react';
2
+ import { IReqorePaginationProps, ReqorePagination } from '../components/Paging';
3
+ import { ReqoreVerticalSpacer } from '../components/Spacer';
4
+ import {
5
+ TReqorePaginationType,
6
+ TReqorePaginationTypeResult,
7
+ getPaginationOptionsFromType,
8
+ } from '../constants/paging';
9
+ import { PADDING_FROM_SIZE, TSizes } from '../constants/sizes';
10
+ import { useReqorePaging } from '../hooks/usePaging';
11
+
12
+ export interface IReqorePagingContainerProps<T> {
13
+ type?: TReqorePaginationType<T>;
14
+ items: T[];
15
+ scrollContainer?: HTMLElement;
16
+ children: (
17
+ items: T[],
18
+ Controls: React.FC<Partial<IReqorePaginationProps<T>>>,
19
+ options: TReqorePaginationTypeResult<T>
20
+ ) => React.ReactNode;
21
+ size?: TSizes;
22
+ }
23
+
24
+ function PagingContainer<T>({
25
+ type,
26
+ items,
27
+ scrollContainer,
28
+ children,
29
+ size = 'normal',
30
+ }: IReqorePagingContainerProps<T>) {
31
+ const {
32
+ pagingOptions = {},
33
+ componentOptions = {},
34
+ pageControlsPosition = 'bottom',
35
+ includeTopControls = true,
36
+ includeBottomControls = true,
37
+ } = useMemo(() => getPaginationOptionsFromType(type), [type]);
38
+
39
+ const { items: finalItems, ...pagingData } = useReqorePaging({
40
+ items,
41
+ ...pagingOptions,
42
+ enabled: !!type,
43
+ });
44
+
45
+ const Controls = ({
46
+ withSpacer,
47
+ position,
48
+ ...pagingProps
49
+ }: Partial<IReqorePaginationProps<T>> & { withSpacer?: boolean; position?: 'top' | 'bottom' }) =>
50
+ pagingData.renderControls ? (
51
+ <>
52
+ {withSpacer && position === 'bottom' ? (
53
+ <ReqoreVerticalSpacer height={PADDING_FROM_SIZE[size]} />
54
+ ) : null}
55
+ <ReqorePagination
56
+ size={size}
57
+ {...pagingData}
58
+ {...componentOptions}
59
+ scrollContainer={scrollContainer}
60
+ {...pagingProps}
61
+ />
62
+ {withSpacer && position === 'top' ? (
63
+ <ReqoreVerticalSpacer height={PADDING_FROM_SIZE[size]} />
64
+ ) : null}
65
+ </>
66
+ ) : null;
67
+
68
+ return (
69
+ <>
70
+ {includeTopControls && (pageControlsPosition === 'top' || pageControlsPosition === 'both') ? (
71
+ <Controls autoLoadMore={false} scrollOnLoadMore={false} position='top' withSpacer />
72
+ ) : null}
73
+
74
+ {children(finalItems, Controls, {
75
+ pagingOptions,
76
+ componentOptions,
77
+ pageControlsPosition,
78
+ includeTopControls,
79
+ includeBottomControls,
80
+ })}
81
+
82
+ {includeBottomControls &&
83
+ (pageControlsPosition === 'bottom' || pageControlsPosition === 'both') ? (
84
+ <Controls position='bottom' withSpacer />
85
+ ) : null}
86
+ </>
87
+ );
88
+ }
89
+
90
+ export const ReqorePaginationContainer = memo(PagingContainer) as typeof PagingContainer;
@@ -27,6 +27,7 @@ export interface IReqorePagingResult<T>
27
27
  last: () => void;
28
28
  pageCount: number;
29
29
  infinite: boolean;
30
+ renderControls: boolean;
30
31
  }
31
32
 
32
33
  export const defaultPagingOptions: IReqorePagingOptions<any> = {
@@ -102,6 +103,9 @@ export const useReqorePaging = <T>(
102
103
  currentPage,
103
104
  next: goNext,
104
105
  back: goBefore,
106
+ renderControls:
107
+ enabled &&
108
+ !(allPageCount === 1 || allPageCount === 0 || (infinite && currentPage === allPageCount)),
105
109
  isLastPage: currentPage === allPageCount,
106
110
  isFirstPage: currentPage === 1,
107
111
  first: () => setPage(1),