@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.
- package/__tests__/collection.test.tsx +22 -1
- package/__tests__/containers/paging.test.tsx +106 -0
- package/__tests__/hooks/useReqorePaging.test.tsx +4 -0
- package/__tests__/pagination.test.tsx +4 -0
- package/dist/components/Collection/index.d.ts +1 -6
- package/dist/components/Collection/index.d.ts.map +1 -1
- package/dist/components/Collection/index.js +7 -15
- package/dist/components/Collection/index.js.map +1 -1
- package/dist/components/Dropdown/index.d.ts +5 -1
- package/dist/components/Dropdown/index.d.ts.map +1 -1
- package/dist/components/Dropdown/index.js +2 -2
- package/dist/components/Dropdown/index.js.map +1 -1
- package/dist/components/Dropdown/list.d.ts +3 -1
- package/dist/components/Dropdown/list.d.ts.map +1 -1
- package/dist/components/Dropdown/list.js +13 -5
- package/dist/components/Dropdown/list.js.map +1 -1
- package/dist/components/Menu/index.d.ts +1 -0
- package/dist/components/Menu/index.d.ts.map +1 -1
- package/dist/components/Menu/index.js +4 -1
- package/dist/components/Menu/index.js.map +1 -1
- package/dist/components/Paging/index.d.ts +3 -1
- package/dist/components/Paging/index.d.ts.map +1 -1
- package/dist/components/Paging/index.js +31 -10
- package/dist/components/Paging/index.js.map +1 -1
- package/dist/components/Panel/index.d.ts +1 -0
- package/dist/components/Panel/index.d.ts.map +1 -1
- package/dist/components/Panel/index.js +2 -2
- package/dist/components/Panel/index.js.map +1 -1
- package/dist/constants/paging.d.ts +11 -4
- package/dist/constants/paging.d.ts.map +1 -1
- package/dist/constants/paging.js +7 -3
- package/dist/constants/paging.js.map +1 -1
- package/dist/containers/Paging.d.ts +15 -0
- package/dist/containers/Paging.d.ts.map +1 -0
- package/dist/containers/Paging.js +51 -0
- package/dist/containers/Paging.js.map +1 -0
- package/dist/hooks/usePaging.d.ts +1 -0
- package/dist/hooks/usePaging.d.ts.map +1 -1
- package/dist/hooks/usePaging.js +2 -0
- package/dist/hooks/usePaging.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Collection/index.tsx +45 -68
- package/src/components/Dropdown/index.tsx +21 -12
- package/src/components/Dropdown/list.tsx +43 -24
- package/src/components/Menu/index.tsx +3 -2
- package/src/components/Paging/index.tsx +32 -4
- package/src/components/Panel/index.tsx +4 -1
- package/src/constants/paging.ts +27 -8
- package/src/containers/Paging.tsx +90 -0
- package/src/hooks/usePaging.ts +4 -0
- package/src/index.tsx +1 -0
- package/src/stories/Collection/Collection.stories.tsx +20 -1
- package/src/stories/Dropdown/Dropdown.stories.tsx +54 -35
- package/src/stories/Menu/Menu.stories.tsx +4 -0
- package/src/stories/Paging/Paging.stories.tsx +19 -1
- 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 {
|
|
6
|
-
import {
|
|
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(
|
|
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(
|
|
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,
|
|
234
|
+
}, [filterable, preQuery, query, rest.actions, _showAs, sort, sortable, filteredItems]);
|
|
255
235
|
|
|
256
236
|
const renderContent = useCallback(() => {
|
|
257
237
|
return contentRenderer(
|
|
258
238
|
<>
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
276
|
+
filteredItems
|
|
300
277
|
);
|
|
301
278
|
}, [
|
|
302
279
|
contentRenderer,
|
|
303
|
-
|
|
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
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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=
|
|
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
|
|
106
|
-
(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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 {
|
|
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}
|
package/src/constants/paging.ts
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
|
4
|
+
export type TReqorePaginationType<T> =
|
|
5
|
+
| 'buttons'
|
|
7
6
|
| 'list'
|
|
8
7
|
| 'infinite'
|
|
9
8
|
| 'auto-load'
|
|
10
|
-
|
|
|
9
|
+
| IReqoreComponentPagingOptions<T>;
|
|
11
10
|
|
|
12
|
-
export
|
|
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
|
|
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;
|
package/src/hooks/usePaging.ts
CHANGED
|
@@ -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),
|