@ainias42/react-bootstrap-mobile 0.2.1 → 0.2.3
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/bin/updateCopies.js +1 -1
- package/bootstrapReactMobile.ts +1 -0
- package/dist/bootstrapReactMobile.d.ts +1 -0
- package/dist/bootstrapReactMobile.js +422 -49
- package/dist/bootstrapReactMobile.js.map +1 -1
- package/dist/src/Components/Dialog/ButtonDialog.d.ts +5 -1
- package/dist/src/Components/Flavor.d.ts +3 -1
- package/dist/src/Components/FormElements/Input/FileInput/FileInput.d.ts +1 -0
- package/dist/src/Components/FormElements/Input/FileInput/MultipleFileInput.d.ts +2 -1
- package/dist/src/Components/Layout/Grid/Grid.d.ts +1 -3
- package/dist/src/Components/List/InfiniteList.d.ts +7 -0
- package/dist/src/Components/List/List.d.ts +4 -2
- package/package.json +1 -1
- package/src/Components/Dialog/ButtonDialog.tsx +19 -10
- package/src/Components/Dialog/ConfirmDialog.tsx +1 -0
- package/src/Components/Dialog/buttonDialog.scss +20 -8
- package/src/Components/Flavor.ts +2 -0
- package/src/Components/FormElements/Input/FileInput/FileInput.tsx +9 -10
- package/src/Components/FormElements/Input/FileInput/MultipleFileInput.tsx +24 -19
- package/src/Components/FormElements/Input/Input.tsx +5 -2
- package/src/Components/Layout/Grid/Grid.tsx +5 -4
- package/src/Components/List/InfiniteList.tsx +57 -0
- package/src/Components/List/List.tsx +14 -5
- package/src/scss/_flavorMixin.scss +6 -0
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { ReactElement } from 'react';
|
|
2
3
|
import { RbmComponentProps, WithNoChildren } from '../RbmComponentProps';
|
|
4
|
+
import { Flavor } from "../Flavor";
|
|
3
5
|
export type ButtonDialogProps = RbmComponentProps<{
|
|
4
6
|
title?: string;
|
|
5
7
|
message: string;
|
|
6
8
|
buttons: {
|
|
7
9
|
text: string;
|
|
8
10
|
callback: () => void;
|
|
11
|
+
flavor?: Flavor;
|
|
9
12
|
}[];
|
|
13
|
+
extraContent?: ReactElement | null;
|
|
10
14
|
}, WithNoChildren>;
|
|
11
|
-
declare function ButtonDialog({ title, message, buttons, style, className }: ButtonDialogProps): React.JSX.Element;
|
|
15
|
+
declare function ButtonDialog({ title, message, buttons, style, className, extraContent }: ButtonDialogProps): React.JSX.Element;
|
|
12
16
|
declare const ButtonDialogMemo: typeof ButtonDialog;
|
|
13
17
|
export { ButtonDialogMemo as ButtonDialog };
|
|
@@ -8,5 +8,6 @@ export type FileInputProps<OnChangeFileData> = RbmComponentProps<Override<Omit<M
|
|
|
8
8
|
value?: FileType;
|
|
9
9
|
mimeType?: string;
|
|
10
10
|
required?: boolean;
|
|
11
|
+
"data-test-id"?: string;
|
|
11
12
|
} & Listener<'onChangeFile', OnChangeFileData, FileType | undefined>>>;
|
|
12
13
|
export declare const FileInput: <OnChangeData>({ value, mimeType, required, ...otherProps }: FileInputProps<OnChangeData>) => React.JSX.Element;
|
|
@@ -14,5 +14,6 @@ export type MultipleFileInputProps<OnChangeFilesData> = RbmComponentProps<Overri
|
|
|
14
14
|
allowOverride?: boolean;
|
|
15
15
|
showDeleteButton?: boolean;
|
|
16
16
|
error?: string;
|
|
17
|
+
"data-test-id"?: string;
|
|
17
18
|
} & Listener<'onChangeFiles', OnChangeFilesData, FileType[]>>>;
|
|
18
|
-
export declare const MultipleFileInput: <OnChangeData>({ className, style, value, label, mimeTypes, maxFiles, maxSizePerFile, allowOverride, onError, showDeleteButton, error, ...otherProps }: MultipleFileInputProps<OnChangeData>) => React.JSX.Element;
|
|
19
|
+
export declare const MultipleFileInput: <OnChangeData>({ className, style, value, label, mimeTypes, maxFiles, maxSizePerFile, allowOverride, onError, showDeleteButton, error, "data-test-id": testId, ...otherProps }: MultipleFileInputProps<OnChangeData>) => React.JSX.Element;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
1
|
import { RbmComponentProps } from '../../RbmComponentProps';
|
|
3
2
|
export type GridProps = RbmComponentProps<{
|
|
4
3
|
columns?: number;
|
|
5
4
|
rows?: number;
|
|
6
5
|
useContainerWidth?: boolean;
|
|
7
6
|
}>;
|
|
8
|
-
declare
|
|
9
|
-
declare const GridMemo: typeof Grid;
|
|
7
|
+
declare const GridMemo: import("../../../helper/withForwardRef").RefComponent<GridProps, HTMLDivElement>;
|
|
10
8
|
export { GridMemo as Grid };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ListProps } from "./List";
|
|
3
|
+
export type InfiniteListProps<ItemType> = ListProps<ItemType> & {
|
|
4
|
+
hasNextPage: boolean;
|
|
5
|
+
loadNextPage: () => unknown | Promise<unknown>;
|
|
6
|
+
};
|
|
7
|
+
export declare const InfiniteList: <ItemType>({ renderItem, itemHeight, items, renderBefore, keyExtractor, hasNextPage, loadNextPage, style, className }: InfiniteListProps<ItemType>) => React.JSX.Element;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
1
|
import { CSSProperties, ReactElement, ReactNode } from 'react';
|
|
3
2
|
import { RbmComponentProps } from '../RbmComponentProps';
|
|
3
|
+
import { FixedSizeList, FixedSizeListProps } from 'react-window';
|
|
4
4
|
export type ListProps<ItemType> = RbmComponentProps<{
|
|
5
5
|
renderItem: (item: ItemType, style: CSSProperties, index: number) => ReactElement;
|
|
6
6
|
itemHeight?: number;
|
|
7
7
|
items: ItemType[];
|
|
8
8
|
renderBefore?: (item: ItemType, index: number) => ReactNode;
|
|
9
9
|
keyExtractor?: (item: ItemType, index: number) => string;
|
|
10
|
+
onItemsRendered?: FixedSizeListProps<ItemType>['onItemsRendered'];
|
|
11
|
+
autoSizeClassName?: string;
|
|
10
12
|
}>;
|
|
11
|
-
export declare const List: <
|
|
13
|
+
export declare const List: import("../../helper/withForwardRef").RefComponent<ListProps<unknown>, FixedSizeList<unknown>>;
|
package/package.json
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { ReactElement } from 'react';
|
|
2
3
|
import { withMemo } from '../../helper/withMemo';
|
|
3
4
|
import { Block } from '../Layout/Block';
|
|
4
|
-
import { Text } from '../Text/Text';
|
|
5
|
+
import { Text, TEXT_SIZE } from '../Text/Text';
|
|
5
6
|
import { Clickable } from '../Clickable/Clickable';
|
|
6
7
|
|
|
7
8
|
import styles from './buttonDialog.scss';
|
|
8
9
|
import { RbmComponentProps, WithNoChildren } from '../RbmComponentProps';
|
|
9
10
|
import classNames from 'classnames';
|
|
11
|
+
import { Flavor } from "../Flavor";
|
|
12
|
+
import { Size } from "../../Size";
|
|
10
13
|
|
|
11
14
|
export type ButtonDialogProps = RbmComponentProps<
|
|
12
|
-
{
|
|
15
|
+
{
|
|
16
|
+
title?: string;
|
|
17
|
+
message: string;
|
|
18
|
+
buttons: { text: string; callback: () => void, flavor?: Flavor }[]
|
|
19
|
+
extraContent?: ReactElement|null
|
|
20
|
+
},
|
|
13
21
|
WithNoChildren
|
|
14
22
|
>;
|
|
15
23
|
|
|
16
|
-
function ButtonDialog({ title, message, buttons, style, className }: ButtonDialogProps) {
|
|
24
|
+
function ButtonDialog({ title, message, buttons, style, className, extraContent }: ButtonDialogProps) {
|
|
17
25
|
// Variables
|
|
18
26
|
|
|
19
27
|
// Refs
|
|
@@ -31,20 +39,21 @@ function ButtonDialog({ title, message, buttons, style, className }: ButtonDialo
|
|
|
31
39
|
// Render Functions
|
|
32
40
|
|
|
33
41
|
return (
|
|
34
|
-
<Block className={classNames(styles.buttonDialog, className)} style={style}>
|
|
42
|
+
<Block className={classNames(styles.buttonDialog, className)} style={style} >
|
|
35
43
|
{!!title && (
|
|
36
|
-
<Block
|
|
37
|
-
<Text>{title}</Text>
|
|
44
|
+
<Block>
|
|
45
|
+
<Text size={TEXT_SIZE.large} className={styles.title}>{title}</Text>
|
|
38
46
|
</Block>
|
|
39
47
|
)}
|
|
40
|
-
<Block
|
|
41
|
-
<Text>{message}</Text>
|
|
48
|
+
<Block>
|
|
49
|
+
<Text className={styles.message}>{message}</Text>
|
|
42
50
|
</Block>
|
|
51
|
+
{extraContent}
|
|
43
52
|
<Block className={styles.buttonContainer}>
|
|
44
53
|
{buttons.map((b, i) => (
|
|
45
54
|
// eslint-disable-next-line react/no-array-index-key
|
|
46
|
-
<Clickable onClick={b.callback} className={styles.button} key={i + b.text}>
|
|
47
|
-
<Text>{b.text}</Text>
|
|
55
|
+
<Clickable onClick={b.callback} className={classNames(styles.button)} key={i + b.text}>
|
|
56
|
+
<Text className={classNames(styles.buttonText, b.flavor ?? Flavor.Accent)}>{b.text}</Text>
|
|
48
57
|
</Clickable>
|
|
49
58
|
))}
|
|
50
59
|
</Block>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@import "../../scss/variables";
|
|
2
2
|
@import "../../scss/designMixin";
|
|
3
|
+
@import "../../scss/flavorMixin";
|
|
3
4
|
|
|
4
5
|
.buttonDialog {
|
|
5
6
|
font-weight: 400;
|
|
@@ -18,8 +19,13 @@
|
|
|
18
19
|
width: 100%;
|
|
19
20
|
|
|
20
21
|
.button {
|
|
21
|
-
color: var(--flavor-accent);
|
|
22
22
|
padding: 0 8px;
|
|
23
|
+
|
|
24
|
+
.buttonText {
|
|
25
|
+
@include flavorSelection(--text-primary-default-color);
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
}
|
|
24
30
|
}
|
|
25
31
|
|
|
@@ -51,15 +57,18 @@
|
|
|
51
57
|
display: block;
|
|
52
58
|
border-left: 1px solid var(--border-light);
|
|
53
59
|
border-top: 1px solid var(--border-light);
|
|
54
|
-
text-overflow: ellipsis;
|
|
55
|
-
letter-spacing: 0;
|
|
56
60
|
vertical-align: middle;
|
|
57
|
-
font-size: 16px;
|
|
58
61
|
text-align: center;
|
|
59
62
|
height: 44px;
|
|
60
|
-
line-height: 44px;
|
|
61
63
|
overflow: hidden;
|
|
62
64
|
|
|
65
|
+
.buttonText {
|
|
66
|
+
line-height: 44px;
|
|
67
|
+
text-overflow: ellipsis;
|
|
68
|
+
letter-spacing: 0;
|
|
69
|
+
font-size: 16px;
|
|
70
|
+
}
|
|
71
|
+
|
|
63
72
|
&:first-child {
|
|
64
73
|
border-left: none;
|
|
65
74
|
}
|
|
@@ -102,12 +111,15 @@
|
|
|
102
111
|
width: auto;
|
|
103
112
|
float: right;
|
|
104
113
|
border-radius: 2px;
|
|
105
|
-
font-size: 14px;
|
|
106
|
-
font-weight: 500;
|
|
107
114
|
height: 36px;
|
|
108
|
-
line-height: 36px;
|
|
109
115
|
margin: 8px 8px 8px 0;
|
|
110
116
|
min-width: 50px;
|
|
117
|
+
|
|
118
|
+
.buttonText {
|
|
119
|
+
line-height: 36px;
|
|
120
|
+
font-size: 14px;
|
|
121
|
+
font-weight: 500;
|
|
122
|
+
}
|
|
111
123
|
}
|
|
112
124
|
}
|
|
113
125
|
}
|
package/src/Components/Flavor.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {RbmComponentProps} from '../../../RbmComponentProps';
|
|
3
|
-
import {Override} from '../../../../TypeHelpers';
|
|
4
|
-
import {useCallback, useMemo} from 'react';
|
|
5
|
-
import {Listener, useListenerWithExtractedProps} from '../../../Hooks/useListener';
|
|
6
|
-
|
|
2
|
+
import { RbmComponentProps } from '../../../RbmComponentProps';
|
|
3
|
+
import { Override } from '../../../../TypeHelpers';
|
|
4
|
+
import { useCallback, useMemo } from 'react';
|
|
5
|
+
import { Listener, useListenerWithExtractedProps } from '../../../Hooks/useListener';
|
|
7
6
|
import styles from './fileInput.scss';
|
|
8
|
-
import {withMemo} from '../../../../helper/withMemo';
|
|
9
|
-
import {FileType} from "./FileType";
|
|
10
|
-
import {MultipleFileInput, MultipleFileInputProps} from "./MultipleFileInput";
|
|
7
|
+
import { withMemo } from '../../../../helper/withMemo';
|
|
8
|
+
import { FileType } from "./FileType";
|
|
9
|
+
import { MultipleFileInput, MultipleFileInputProps } from "./MultipleFileInput";
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
export type FileInputProps<OnChangeFileData> = RbmComponentProps<
|
|
14
13
|
Override<
|
|
15
|
-
Omit<MultipleFileInputProps<unknown>, "onChangeFiles" | "maxFiles" | "onChangeFilesData" | "mimeTypes"|"showDeleteButton">, {
|
|
14
|
+
Omit<MultipleFileInputProps<unknown>, "onChangeFiles" | "maxFiles" | "onChangeFilesData" | "mimeTypes" | "showDeleteButton">, {
|
|
16
15
|
value?: FileType,
|
|
17
16
|
mimeType?: string,
|
|
18
17
|
required?: boolean,
|
|
18
|
+
"data-test-id"?: string;
|
|
19
19
|
} & Listener<'onChangeFile', OnChangeFileData, FileType | undefined>>
|
|
20
20
|
>;
|
|
21
21
|
|
|
22
|
-
// TODO use MultipleFileInput internal
|
|
23
22
|
export const FileInput = withMemo(function FileInput<OnChangeData>({
|
|
24
23
|
value,
|
|
25
24
|
mimeType,
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {RbmComponentProps} from '../../../RbmComponentProps';
|
|
3
|
-
import {Override} from '../../../../TypeHelpers';
|
|
4
|
-
import {ChangeEventHandler, DragEvent, InputHTMLAttributes, useCallback, useRef} from 'react';
|
|
5
|
-
import {Listener, useListenerWithExtractedProps} from '../../../Hooks/useListener';
|
|
2
|
+
import { RbmComponentProps } from '../../../RbmComponentProps';
|
|
3
|
+
import { Override } from '../../../../TypeHelpers';
|
|
4
|
+
import { ChangeEventHandler, DragEvent, InputHTMLAttributes, useCallback, useRef } from 'react';
|
|
5
|
+
import { Listener, useListenerWithExtractedProps } from '../../../Hooks/useListener';
|
|
6
6
|
import styles from './fileInput.scss';
|
|
7
|
-
import {withMemo} from '../../../../helper/withMemo';
|
|
7
|
+
import { withMemo } from '../../../../helper/withMemo';
|
|
8
8
|
import classNames from 'classnames';
|
|
9
|
-
import {Block} from '../../../Layout/Block';
|
|
10
|
-
import {Text} from '../../../Text/Text';
|
|
11
|
-
import {Flex} from '../../../Layout/Flex';
|
|
12
|
-
import {Grow} from '../../../Layout/Grow';
|
|
13
|
-
import {Icon} from '../../../Icon/Icon';
|
|
14
|
-
import {faFile, faPlus, faTimesCircle} from '@fortawesome/free-solid-svg-icons';
|
|
15
|
-
import {Image} from '../../../Image/Image';
|
|
16
|
-
import {Clickable} from '../../../Clickable/Clickable';
|
|
17
|
-
import {Inline} from '../../../Layout/Inline';
|
|
18
|
-
import {FileType} from "./FileType";
|
|
9
|
+
import { Block } from '../../../Layout/Block';
|
|
10
|
+
import { Text } from '../../../Text/Text';
|
|
11
|
+
import { Flex } from '../../../Layout/Flex';
|
|
12
|
+
import { Grow } from '../../../Layout/Grow';
|
|
13
|
+
import { Icon } from '../../../Icon/Icon';
|
|
14
|
+
import { faFile, faPlus, faTimesCircle } from '@fortawesome/free-solid-svg-icons';
|
|
15
|
+
import { Image } from '../../../Image/Image';
|
|
16
|
+
import { Clickable } from '../../../Clickable/Clickable';
|
|
17
|
+
import { Inline } from '../../../Layout/Inline';
|
|
18
|
+
import { FileType } from "./FileType";
|
|
19
19
|
import { InlineBlock } from "../../../Layout/InlineBlock";
|
|
20
20
|
|
|
21
21
|
|
|
@@ -32,6 +32,7 @@ export type MultipleFileInputProps<OnChangeFilesData> = RbmComponentProps<
|
|
|
32
32
|
allowOverride?: boolean
|
|
33
33
|
showDeleteButton?: boolean
|
|
34
34
|
error?: string,
|
|
35
|
+
"data-test-id"?: string;
|
|
35
36
|
} & Listener<'onChangeFiles', OnChangeFilesData, FileType[]>
|
|
36
37
|
>
|
|
37
38
|
>;
|
|
@@ -47,7 +48,8 @@ export const MultipleFileInput = withMemo(function MultipleImageInput<OnChangeDa
|
|
|
47
48
|
allowOverride = maxFiles === 1,
|
|
48
49
|
onError,
|
|
49
50
|
showDeleteButton = true,
|
|
50
|
-
|
|
51
|
+
error,
|
|
52
|
+
"data-test-id": testId,
|
|
51
53
|
...otherProps
|
|
52
54
|
}: MultipleFileInputProps<OnChangeData>) {
|
|
53
55
|
// Variables
|
|
@@ -78,7 +80,7 @@ export const MultipleFileInput = withMemo(function MultipleImageInput<OnChangeDa
|
|
|
78
80
|
[mimeTypes]
|
|
79
81
|
);
|
|
80
82
|
|
|
81
|
-
const [onChangeFiles,
|
|
83
|
+
const [onChangeFiles, props] = useListenerWithExtractedProps('onChangeFiles', otherProps);
|
|
82
84
|
const getBase64 = useCallback((inputFiles: Blob[]) => {
|
|
83
85
|
const promises = inputFiles.map(
|
|
84
86
|
(file) =>
|
|
@@ -220,6 +222,7 @@ export const MultipleFileInput = withMemo(function MultipleImageInput<OnChangeDa
|
|
|
220
222
|
<span
|
|
221
223
|
className={classNames(styles.fileInput, className)}
|
|
222
224
|
style={style}
|
|
225
|
+
data-test-id={testId}
|
|
223
226
|
>
|
|
224
227
|
<Flex horizontal={true}>
|
|
225
228
|
{!!label && (
|
|
@@ -248,13 +251,15 @@ export const MultipleFileInput = withMemo(function MultipleImageInput<OnChangeDa
|
|
|
248
251
|
>
|
|
249
252
|
{renderFile(file)}
|
|
250
253
|
{showDeleteButton &&
|
|
251
|
-
<Clickable className={styles.previewRemove} onClick={removeFile}
|
|
254
|
+
<Clickable className={styles.previewRemove} onClick={removeFile}
|
|
255
|
+
onClickData={index}>
|
|
252
256
|
<Icon icon={faTimesCircle}/>
|
|
253
257
|
</Clickable>}
|
|
254
258
|
</Clickable>
|
|
255
259
|
</Grow>;
|
|
256
260
|
})}
|
|
257
|
-
<Grow className={classNames(styles.addFile, {[styles.hidden]: value.length >= maxFiles})}
|
|
261
|
+
<Grow className={classNames(styles.addFile, {[styles.hidden]: value.length >= maxFiles})}
|
|
262
|
+
center={true}>
|
|
258
263
|
<Clickable
|
|
259
264
|
className={styles.addFileButton}
|
|
260
265
|
onDrop={onDrop}
|
|
@@ -19,6 +19,7 @@ import { useOnChangeDone } from '../hooks/useOnChangeDone';
|
|
|
19
19
|
import { InlineBlock } from "../../Layout/InlineBlock";
|
|
20
20
|
import { Text } from "../../Text/Text";
|
|
21
21
|
import { useSendFormContext } from "../Controller/SendFormContext";
|
|
22
|
+
import { useDebounced } from "../../Hooks/useDebounced";
|
|
22
23
|
|
|
23
24
|
export type InputProps<OnChangeData, OnBlurData, OnChangeDoneData> = RbmComponentProps<
|
|
24
25
|
Override<
|
|
@@ -119,10 +120,12 @@ export const Input = withForwardRef(function Input<OnChangeData, OnBlurData, OnC
|
|
|
119
120
|
otherPropsWithoutOnchange
|
|
120
121
|
);
|
|
121
122
|
|
|
122
|
-
const [
|
|
123
|
+
const [onChangeDoneWithoutDeboune, otherPropsWithoutData] = useListenerWithExtractedProps<'onChangeDone', OnChangeDoneData>(
|
|
123
124
|
'onChangeDone',
|
|
124
125
|
otherPropsWithoutBlur
|
|
125
126
|
);
|
|
127
|
+
const onChangeDone = useDebounced(onChangeDoneWithoutDeboune, [onChangeDoneWithoutDeboune]);
|
|
128
|
+
|
|
126
129
|
|
|
127
130
|
const realOnKeyDown = useCallback(
|
|
128
131
|
(e: KeyboardEvent<HTMLInputElement>) => {
|
|
@@ -156,7 +159,7 @@ export const Input = withForwardRef(function Input<OnChangeData, OnBlurData, OnC
|
|
|
156
159
|
}
|
|
157
160
|
}
|
|
158
161
|
},
|
|
159
|
-
[onKeyDown,
|
|
162
|
+
[onKeyDown, onEnter, otherProps.type, otherProps.step, otherProps.max, otherProps.min, onChange]
|
|
160
163
|
);
|
|
161
164
|
|
|
162
165
|
// Effects
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { withMemo } from '../../../helper/withMemo';
|
|
3
2
|
import { RbmComponentProps } from '../../RbmComponentProps';
|
|
4
3
|
import { Block } from '../Block';
|
|
5
4
|
import classNames from 'classnames';
|
|
6
5
|
import styles from './grid.scss';
|
|
7
|
-
import { useMemo } from 'react';
|
|
6
|
+
import { ForwardedRef, useMemo } from 'react';
|
|
7
|
+
import { withForwardRef } from "../../../helper/withForwardRef";
|
|
8
8
|
|
|
9
9
|
export type GridProps = RbmComponentProps<{
|
|
10
10
|
columns?: number;
|
|
@@ -12,7 +12,7 @@ export type GridProps = RbmComponentProps<{
|
|
|
12
12
|
useContainerWidth?: boolean;
|
|
13
13
|
}>;
|
|
14
14
|
|
|
15
|
-
function Grid({ style, children, columns = 12, rows = 1, useContainerWidth = false, className, __allowChildren }: GridProps) {
|
|
15
|
+
function Grid({ style, children, columns = 12, rows = 1, useContainerWidth = false, className, __allowChildren }: GridProps, ref?:ForwardedRef<HTMLDivElement>) {
|
|
16
16
|
// Variables
|
|
17
17
|
const appliedStyle = useMemo(
|
|
18
18
|
() => ({
|
|
@@ -39,6 +39,7 @@ function Grid({ style, children, columns = 12, rows = 1, useContainerWidth = fal
|
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
41
|
<Block
|
|
42
|
+
ref={ref}
|
|
42
43
|
style={appliedStyle}
|
|
43
44
|
className={classNames(styles.grid, className, {[styles.useContainerWidth]: useContainerWidth})}
|
|
44
45
|
__allowChildren={__allowChildren as 'all'}
|
|
@@ -49,5 +50,5 @@ function Grid({ style, children, columns = 12, rows = 1, useContainerWidth = fal
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
// Need RowMemo for autocompletion of phpstorm
|
|
52
|
-
const GridMemo =
|
|
53
|
+
const GridMemo = withForwardRef(Grid, styles);
|
|
53
54
|
export { GridMemo as Grid };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React, { CSSProperties, ReactElement, ReactNode, useCallback, useState } from 'react';
|
|
2
|
+
import InfiniteLoader from "react-window-infinite-loader";
|
|
3
|
+
import { RbmComponentProps } from "../RbmComponentProps";
|
|
4
|
+
import { List, ListProps } from "./List";
|
|
5
|
+
import { withMemo } from "../../helper/withMemo";
|
|
6
|
+
|
|
7
|
+
export type InfiniteListProps<ItemType> = ListProps<ItemType> &{
|
|
8
|
+
hasNextPage: boolean,
|
|
9
|
+
loadNextPage: () => unknown | Promise<unknown>,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const InfiniteList = withMemo(function InfiniteList<ItemType>({
|
|
13
|
+
renderItem,
|
|
14
|
+
itemHeight,
|
|
15
|
+
items,
|
|
16
|
+
renderBefore,
|
|
17
|
+
keyExtractor,
|
|
18
|
+
hasNextPage,
|
|
19
|
+
loadNextPage,
|
|
20
|
+
style,
|
|
21
|
+
className
|
|
22
|
+
}: InfiniteListProps<ItemType>) {
|
|
23
|
+
// Refs
|
|
24
|
+
|
|
25
|
+
// States/Variables/Selectors
|
|
26
|
+
const [isPageLoading, setIsPageLoading] = useState(false);
|
|
27
|
+
|
|
28
|
+
// Dispatch
|
|
29
|
+
|
|
30
|
+
// Callbacks
|
|
31
|
+
const isItemLoaded = useCallback((index: number) => !hasNextPage || index < items.length, [hasNextPage, items]);
|
|
32
|
+
|
|
33
|
+
const loadMore = useCallback(async () => {
|
|
34
|
+
if (!isPageLoading) {
|
|
35
|
+
setIsPageLoading(true);
|
|
36
|
+
try {
|
|
37
|
+
loadNextPage()
|
|
38
|
+
} finally {
|
|
39
|
+
setIsPageLoading(false);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}, [isPageLoading, loadNextPage])
|
|
43
|
+
|
|
44
|
+
// Effects
|
|
45
|
+
|
|
46
|
+
// Other
|
|
47
|
+
|
|
48
|
+
// RenderFunctions
|
|
49
|
+
|
|
50
|
+
return <InfiniteLoader isItemLoaded={isItemLoaded} loadMoreItems={loadMore}
|
|
51
|
+
itemCount={hasNextPage ? items.length + 1 : items.length}>{({onItemsRendered, ref}) => (
|
|
52
|
+
<List
|
|
53
|
+
items={items} renderItem={renderItem} style={style} className={className} itemHeight={itemHeight}
|
|
54
|
+
renderBefore={renderBefore} keyExtractor={keyExtractor} ref={ref} onItemsRendered={onItemsRendered}/>
|
|
55
|
+
)}
|
|
56
|
+
</InfiniteLoader>;
|
|
57
|
+
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { ComponentType, CSSProperties, ReactElement, ReactNode, useCallback, useState } from 'react';
|
|
2
|
+
import { ComponentType, CSSProperties, ForwardedRef, ReactElement, ReactNode, useCallback, useState } from 'react';
|
|
3
3
|
import { RbmComponentProps } from '../RbmComponentProps';
|
|
4
4
|
|
|
5
5
|
import styles from './list.scss';
|
|
6
6
|
import { withMemo } from '../../helper/withMemo';
|
|
7
|
-
import { FixedSizeList, ListChildComponentProps } from 'react-window';
|
|
7
|
+
import { FixedSizeList, FixedSizeListProps, ListChildComponentProps } from 'react-window';
|
|
8
8
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
|
9
9
|
import { SizeCalculator, SizeCalculatorProps } from '../SizeCalculator/SizeCalculator';
|
|
10
|
+
import { withForwardRef } from "../../helper/withForwardRef";
|
|
10
11
|
|
|
11
12
|
export type ListProps<ItemType> = RbmComponentProps<{
|
|
12
13
|
renderItem: (item: ItemType, style: CSSProperties, index: number) => ReactElement;
|
|
@@ -14,15 +15,19 @@ export type ListProps<ItemType> = RbmComponentProps<{
|
|
|
14
15
|
items: ItemType[];
|
|
15
16
|
renderBefore?: (item: ItemType, index: number) => ReactNode;
|
|
16
17
|
keyExtractor?: (item: ItemType, index: number) => string;
|
|
18
|
+
onItemsRendered?: FixedSizeListProps<ItemType>['onItemsRendered'];
|
|
19
|
+
autoSizeClassName?: string;
|
|
17
20
|
}>;
|
|
18
21
|
|
|
19
|
-
export const List =
|
|
22
|
+
export const List = withForwardRef(function List<ItemType>({
|
|
20
23
|
items,
|
|
21
24
|
renderItem,
|
|
22
25
|
itemHeight: initialItemHeight = 0,
|
|
23
26
|
className,
|
|
24
27
|
style,
|
|
25
|
-
|
|
28
|
+
onItemsRendered,
|
|
29
|
+
autoSizeClassName,
|
|
30
|
+
}: ListProps<ItemType>, ref: ForwardedRef<FixedSizeList<ItemType>>) {
|
|
26
31
|
// Variables
|
|
27
32
|
|
|
28
33
|
// States
|
|
@@ -45,7 +50,7 @@ export const List = withMemo(function List<ItemType>({
|
|
|
45
50
|
// Render Functions
|
|
46
51
|
|
|
47
52
|
return (
|
|
48
|
-
<AutoSizer>
|
|
53
|
+
<AutoSizer className={autoSizeClassName}>
|
|
49
54
|
{({ height, width }: { height?: number; width?: number }) => {
|
|
50
55
|
return (
|
|
51
56
|
<>
|
|
@@ -54,6 +59,8 @@ export const List = withMemo(function List<ItemType>({
|
|
|
54
59
|
{renderItem(
|
|
55
60
|
items[0],
|
|
56
61
|
{
|
|
62
|
+
visibility: 'hidden',
|
|
63
|
+
pointerEvents: 'none',
|
|
57
64
|
position: 'relative',
|
|
58
65
|
top: '0px',
|
|
59
66
|
left: '0px',
|
|
@@ -66,6 +73,8 @@ export const List = withMemo(function List<ItemType>({
|
|
|
66
73
|
)}
|
|
67
74
|
{height !== undefined && width !== undefined && (
|
|
68
75
|
<FixedSizeList
|
|
76
|
+
onItemsRendered={onItemsRendered}
|
|
77
|
+
ref={ref}
|
|
69
78
|
height={height}
|
|
70
79
|
itemCount={items.length}
|
|
71
80
|
width={width}
|