@ainias42/react-bootstrap-mobile 0.2.0 → 0.2.2
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 +3 -2
- package/bootstrapReactMobile.ts +10 -0
- package/dist/bootstrapReactMobile.d.ts +10 -0
- package/dist/bootstrapReactMobile.js +1187 -606
- package/dist/bootstrapReactMobile.js.map +1 -1
- package/dist/src/Components/Clickable/Clickable.d.ts +6 -3
- package/dist/src/Components/Dialog/ButtonDialog.d.ts +5 -1
- package/dist/src/Components/Flavor.d.ts +3 -1
- package/dist/src/Components/FormElements/Button/Button.d.ts +4 -1
- package/dist/src/Components/FormElements/Controller/HookForm.d.ts +8 -0
- package/dist/src/Components/FormElements/Controller/InputController.d.ts +4 -0
- package/dist/src/Components/FormElements/Controller/MultipleFileInputController.d.ts +3 -0
- package/dist/src/Components/FormElements/Controller/PasswordInputController.d.ts +4 -0
- package/dist/src/Components/FormElements/Controller/SelectController.d.ts +3 -0
- package/dist/src/Components/FormElements/Controller/SendFormContext.d.ts +3 -0
- package/dist/src/Components/FormElements/Controller/TextareaController.d.ts +4 -0
- package/dist/src/Components/FormElements/Controller/withHookController.d.ts +4 -0
- package/dist/src/Components/FormElements/Input/FileInput/MultipleFileInput.d.ts +2 -1
- package/dist/src/Components/FormElements/Input/Input.d.ts +1 -0
- package/dist/src/Components/FormElements/Select/Select.d.ts +2 -1
- package/dist/src/Components/FormElements/Textarea/Textarea.d.ts +1 -0
- package/dist/src/Components/Hooks/useComposedRef.d.ts +1 -1
- package/dist/src/Components/Image/Image.d.ts +2 -2
- 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/dist/src/Size.d.ts +8 -0
- package/package.json +5 -3
- package/src/Components/Clickable/Clickable.tsx +41 -6
- 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/Button/Button.tsx +14 -8
- package/src/Components/FormElements/Button/button.scss +17 -0
- package/src/Components/FormElements/Controller/HookForm.tsx +43 -0
- package/src/Components/FormElements/Controller/InputController.ts +4 -0
- package/src/Components/FormElements/Controller/MultipleFileInputController.ts +4 -0
- package/src/Components/FormElements/Controller/PasswordInputController.ts +4 -0
- package/src/Components/FormElements/Controller/SelectController.ts +4 -0
- package/src/Components/FormElements/Controller/SendFormContext.ts +7 -0
- package/src/Components/FormElements/Controller/TextareaController.ts +4 -0
- package/src/Components/FormElements/Controller/withHookController.tsx +53 -0
- package/src/Components/FormElements/Input/FileInput/MultipleFileInput.tsx +4 -1
- package/src/Components/FormElements/Input/FileInput/fileInput.scss +5 -6
- package/src/Components/FormElements/Input/Input.tsx +17 -6
- package/src/Components/FormElements/Input/PasswordInput/PasswordInput.tsx +1 -1
- package/src/Components/FormElements/Input/input.scss +5 -0
- package/src/Components/FormElements/Select/Select.tsx +5 -0
- package/src/Components/FormElements/Select/select.scss +5 -0
- package/src/Components/FormElements/Textarea/Textarea.tsx +9 -4
- package/src/Components/FormElements/Textarea/textarea.scss +10 -1
- package/src/Components/FormElements/hooks/useOnChangeDone.ts +1 -1
- package/src/Components/Hooks/useComposedRef.ts +13 -16
- package/src/Components/Image/Image.tsx +2 -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/Size.ts +9 -0
- package/src/scss/_baseClasses.scss +7 -11
- package/src/scss/_colors.scss +10 -5
- package/src/scss/_default.scss +2 -2
- package/src/scss/_flavorMixin.scss +6 -0
- package/src/scss/_variables.scss +2 -2
- package/webpack.config.js +1 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React, { ComponentProps, ComponentRef, ComponentType, ForwardedRef, useCallback } from "react";
|
|
2
|
+
import { FieldPath, FieldValues, useController, useFormContext } from "react-hook-form";
|
|
3
|
+
import { useComposedRef } from "../../Hooks/useComposedRef";
|
|
4
|
+
import { withForwardRef } from "../../../helper/withForwardRef";
|
|
5
|
+
|
|
6
|
+
export function withHookController<C extends ComponentType<any>, OnChangeProp extends keyof ComponentProps<C>>(Comp: C, onChangeProp: OnChangeProp, emptyValue: any = null) {
|
|
7
|
+
type RefType = ComponentRef<C>;
|
|
8
|
+
type OldProps = ComponentProps<C>;
|
|
9
|
+
|
|
10
|
+
type Props<Values extends FieldValues, Name extends FieldPath<Values> = FieldPath<Values>> = Omit<
|
|
11
|
+
OldProps,
|
|
12
|
+
'name' | 'onBlur' | OnChangeProp | 'ref' | 'value'
|
|
13
|
+
> & { name: Name };
|
|
14
|
+
|
|
15
|
+
function WithHookComponent<Values extends FieldValues, Name extends FieldPath<Values> = FieldPath<Values>>(
|
|
16
|
+
{name, ...otherProps}: Props<Values, Name>,
|
|
17
|
+
ref?: ForwardedRef<RefType>
|
|
18
|
+
) {
|
|
19
|
+
const children = "children" in otherProps ? otherProps.children : undefined;
|
|
20
|
+
|
|
21
|
+
const {field, fieldState} = useController({name});
|
|
22
|
+
const {clearErrors} = useFormContext();
|
|
23
|
+
const composedRef = useComposedRef(ref, field.ref);
|
|
24
|
+
const errorMessage = fieldState.error?.message;
|
|
25
|
+
|
|
26
|
+
const internalOnChange = useCallback(
|
|
27
|
+
(arg: any) => {
|
|
28
|
+
clearErrors(name);
|
|
29
|
+
field.onChange(arg);
|
|
30
|
+
},
|
|
31
|
+
[clearErrors, field, name]
|
|
32
|
+
);
|
|
33
|
+
const onChangeProps = {
|
|
34
|
+
[onChangeProp]: internalOnChange
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
// @ts-expect-error Because of the prop spreading, typescript is really confused here
|
|
39
|
+
<Comp
|
|
40
|
+
{...otherProps}
|
|
41
|
+
{...field}
|
|
42
|
+
{...onChangeProps}
|
|
43
|
+
value={field.value ?? emptyValue}
|
|
44
|
+
ref={composedRef}
|
|
45
|
+
error={errorMessage}
|
|
46
|
+
>
|
|
47
|
+
{children}
|
|
48
|
+
</Comp>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return withForwardRef(WithHookComponent);
|
|
53
|
+
}
|
|
@@ -3,7 +3,6 @@ import {RbmComponentProps} from '../../../RbmComponentProps';
|
|
|
3
3
|
import {Override} from '../../../../TypeHelpers';
|
|
4
4
|
import {ChangeEventHandler, DragEvent, InputHTMLAttributes, useCallback, useRef} from 'react';
|
|
5
5
|
import {Listener, useListenerWithExtractedProps} from '../../../Hooks/useListener';
|
|
6
|
-
|
|
7
6
|
import styles from './fileInput.scss';
|
|
8
7
|
import {withMemo} from '../../../../helper/withMemo';
|
|
9
8
|
import classNames from 'classnames';
|
|
@@ -17,6 +16,7 @@ import {Image} from '../../../Image/Image';
|
|
|
17
16
|
import {Clickable} from '../../../Clickable/Clickable';
|
|
18
17
|
import {Inline} from '../../../Layout/Inline';
|
|
19
18
|
import {FileType} from "./FileType";
|
|
19
|
+
import { InlineBlock } from "../../../Layout/InlineBlock";
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
export type MultipleFileInputProps<OnChangeFilesData> = RbmComponentProps<
|
|
@@ -31,6 +31,7 @@ export type MultipleFileInputProps<OnChangeFilesData> = RbmComponentProps<
|
|
|
31
31
|
onError?: (error: string) => void;
|
|
32
32
|
allowOverride?: boolean
|
|
33
33
|
showDeleteButton?: boolean
|
|
34
|
+
error?: string,
|
|
34
35
|
} & Listener<'onChangeFiles', OnChangeFilesData, FileType[]>
|
|
35
36
|
>
|
|
36
37
|
>;
|
|
@@ -46,6 +47,7 @@ export const MultipleFileInput = withMemo(function MultipleImageInput<OnChangeDa
|
|
|
46
47
|
allowOverride = maxFiles === 1,
|
|
47
48
|
onError,
|
|
48
49
|
showDeleteButton = true,
|
|
50
|
+
error,
|
|
49
51
|
...otherProps
|
|
50
52
|
}: MultipleFileInputProps<OnChangeData>) {
|
|
51
53
|
// Variables
|
|
@@ -275,6 +277,7 @@ export const MultipleFileInput = withMemo(function MultipleImageInput<OnChangeDa
|
|
|
275
277
|
</Clickable>
|
|
276
278
|
</Grow>
|
|
277
279
|
</Flex>
|
|
280
|
+
{error && <InlineBlock className={styles.error}><Text>{error}</Text></InlineBlock>}
|
|
278
281
|
</span>
|
|
279
282
|
);
|
|
280
283
|
},
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
KeyboardEvent,
|
|
6
6
|
MutableRefObject,
|
|
7
7
|
useCallback, useEffect,
|
|
8
|
-
useMemo,
|
|
8
|
+
useMemo, useRef,
|
|
9
9
|
} from 'react';
|
|
10
10
|
import { RbmComponentProps } from '../../RbmComponentProps';
|
|
11
11
|
import { Override } from '../../../TypeHelpers';
|
|
@@ -16,6 +16,9 @@ import styles from './input.scss';
|
|
|
16
16
|
import classNames from 'classnames';
|
|
17
17
|
import { useComposedRef } from '../../Hooks/useComposedRef';
|
|
18
18
|
import { useOnChangeDone } from '../hooks/useOnChangeDone';
|
|
19
|
+
import { InlineBlock } from "../../Layout/InlineBlock";
|
|
20
|
+
import { Text } from "../../Text/Text";
|
|
21
|
+
import { useSendFormContext } from "../Controller/SendFormContext";
|
|
19
22
|
|
|
20
23
|
export type InputProps<OnChangeData, OnBlurData, OnChangeDoneData> = RbmComponentProps<
|
|
21
24
|
Override<
|
|
@@ -25,6 +28,7 @@ export type InputProps<OnChangeData, OnBlurData, OnChangeDoneData> = RbmComponen
|
|
|
25
28
|
inline?: boolean;
|
|
26
29
|
onChangeText?: (newText: string) => void;
|
|
27
30
|
onEnter?: (newText: string) => void;
|
|
31
|
+
error?: string,
|
|
28
32
|
} & OptionalListener<'onChange', OnChangeData> &
|
|
29
33
|
OptionalListener<'onBlur', OnBlurData> &
|
|
30
34
|
OptionalListener<'onChangeDone', OnChangeDoneData>
|
|
@@ -39,7 +43,9 @@ export const Input = withForwardRef(function Input<OnChangeData, OnBlurData, OnC
|
|
|
39
43
|
onKeyDown,
|
|
40
44
|
inline = false,
|
|
41
45
|
value,
|
|
46
|
+
error,
|
|
42
47
|
onChangeText,
|
|
48
|
+
onEnter,
|
|
43
49
|
...otherProps
|
|
44
50
|
}: InputProps<OnChangeData, OnBlurData, OnChangeDoneData>,
|
|
45
51
|
ref: MutableRefObject<HTMLInputElement> | null
|
|
@@ -77,9 +83,13 @@ export const Input = withForwardRef(function Input<OnChangeData, OnBlurData, OnC
|
|
|
77
83
|
return "";
|
|
78
84
|
}, [value, otherProps.max, otherProps.min, otherProps.type]);
|
|
79
85
|
// Refs
|
|
80
|
-
const innerRef =
|
|
86
|
+
const innerRef = useRef<HTMLInputElement>(null);
|
|
87
|
+
const refFunction = useComposedRef(ref, innerRef);
|
|
81
88
|
|
|
82
89
|
// Callbacks
|
|
90
|
+
const sendForm = useSendFormContext();
|
|
91
|
+
onEnter ??= sendForm;
|
|
92
|
+
|
|
83
93
|
const [onChangeWithData, otherPropsWithoutOnchange] = useListenerWithExtractedProps<'onChange', OnChangeData>(
|
|
84
94
|
'onChange',
|
|
85
95
|
otherProps
|
|
@@ -117,13 +127,13 @@ export const Input = withForwardRef(function Input<OnChangeData, OnBlurData, OnC
|
|
|
117
127
|
const realOnKeyDown = useCallback(
|
|
118
128
|
(e: KeyboardEvent<HTMLInputElement>) => {
|
|
119
129
|
onKeyDown?.(e);
|
|
120
|
-
if (
|
|
130
|
+
if (onEnter && e.key === 'Enter' && !e.defaultPrevented) {
|
|
121
131
|
if (otherProps.type === "number") {
|
|
122
132
|
const stringValue = (e.target as HTMLInputElement).value;
|
|
123
133
|
const val = !Number.isNaN(Number(stringValue)) ? stringValue : !Number.isNaN(parseFloat(stringValue)) ? String(parseFloat(stringValue)) : "";
|
|
124
|
-
|
|
134
|
+
onEnter(val);
|
|
125
135
|
} else {
|
|
126
|
-
|
|
136
|
+
onEnter((e.target as HTMLInputElement).value);
|
|
127
137
|
}
|
|
128
138
|
}
|
|
129
139
|
|
|
@@ -177,12 +187,13 @@ export const Input = withForwardRef(function Input<OnChangeData, OnBlurData, OnC
|
|
|
177
187
|
{...otherPropsWithoutData}
|
|
178
188
|
value={usedValue}
|
|
179
189
|
type={otherProps.type === "number" ? "text" : otherProps.type}
|
|
180
|
-
ref={
|
|
190
|
+
ref={refFunction}
|
|
181
191
|
className={styles.text}
|
|
182
192
|
onBlur={onBlur}
|
|
183
193
|
onChange={onChange}
|
|
184
194
|
onKeyDown={realOnKeyDown}
|
|
185
195
|
/>
|
|
196
|
+
{error && <InlineBlock className={styles.error}><Text>{error}</Text></InlineBlock>}
|
|
186
197
|
</label>
|
|
187
198
|
);
|
|
188
199
|
},
|
|
@@ -45,7 +45,7 @@ export const PasswordInput = withForwardRef(function PasswordInput<OnChangeData,
|
|
|
45
45
|
<Grow>
|
|
46
46
|
<Input {...props} type={isVisible ? 'text' : 'password'} ref={ref} />
|
|
47
47
|
</Grow>
|
|
48
|
-
<Clickable onClick={toggleVisible} className={styles.showButton}>
|
|
48
|
+
<Clickable onClick={toggleVisible} className={styles.showButton} interactable={false}>
|
|
49
49
|
<Icon icon={isVisible ? faEye : faEyeSlash} />
|
|
50
50
|
</Clickable>
|
|
51
51
|
</Flex>
|
|
@@ -7,6 +7,8 @@ import {OptionalListener, useListenerWithExtractedProps} from '../../Hooks/useLi
|
|
|
7
7
|
import styles from './select.scss';
|
|
8
8
|
import { withMemo } from '../../../helper/withMemo';
|
|
9
9
|
import classNames from 'classnames';
|
|
10
|
+
import { InlineBlock } from "../../Layout/InlineBlock";
|
|
11
|
+
import { Text } from "../../Text/Text";
|
|
10
12
|
|
|
11
13
|
export type SelectOption = {
|
|
12
14
|
label: string;
|
|
@@ -23,6 +25,7 @@ export type SelectProps<OnChangeData> = RbmComponentProps<
|
|
|
23
25
|
onChangeValue?: (newValue: string) => void;
|
|
24
26
|
inline?: boolean;
|
|
25
27
|
small?: boolean;
|
|
28
|
+
error?: string;
|
|
26
29
|
} & OptionalListener<'onChange', OnChangeData>
|
|
27
30
|
>
|
|
28
31
|
>;
|
|
@@ -35,6 +38,7 @@ export const Select = withMemo(function Select<OnChangeData>({
|
|
|
35
38
|
onChangeValue,
|
|
36
39
|
inline = false,
|
|
37
40
|
small = false,
|
|
41
|
+
error,
|
|
38
42
|
...otherProps
|
|
39
43
|
}: SelectProps<OnChangeData>) {
|
|
40
44
|
// Variables
|
|
@@ -72,6 +76,7 @@ export const Select = withMemo(function Select<OnChangeData>({
|
|
|
72
76
|
</option>
|
|
73
77
|
))}
|
|
74
78
|
</select>
|
|
79
|
+
{error && <InlineBlock className={styles.error}><Text>{error}</Text></InlineBlock>}
|
|
75
80
|
</label>
|
|
76
81
|
);
|
|
77
82
|
},
|
|
@@ -7,15 +7,16 @@ import {
|
|
|
7
7
|
KeyboardEvent,
|
|
8
8
|
ChangeEvent,
|
|
9
9
|
MutableRefObject,
|
|
10
|
-
CSSProperties
|
|
10
|
+
CSSProperties, useRef
|
|
11
11
|
} from 'react';
|
|
12
12
|
import { OptionalListener, useListenerWithExtractedProps } from '../../Hooks/useListener';
|
|
13
|
-
|
|
14
13
|
import styles from './textarea.scss';
|
|
15
14
|
import classNames from 'classnames';
|
|
16
15
|
import { withForwardRef } from "../../../helper/withForwardRef";
|
|
17
16
|
import { useOnChangeDone } from "../hooks/useOnChangeDone";
|
|
18
17
|
import { useComposedRef } from "../../Hooks/useComposedRef";
|
|
18
|
+
import { InlineBlock } from "../../Layout/InlineBlock";
|
|
19
|
+
import { Text } from "../../Text/Text";
|
|
19
20
|
|
|
20
21
|
export type TextareaProps<OnChangeData, OnChangeDoneData> = RbmComponentProps<
|
|
21
22
|
Override<
|
|
@@ -27,6 +28,7 @@ export type TextareaProps<OnChangeData, OnChangeDoneData> = RbmComponentProps<
|
|
|
27
28
|
onEscape?: (newText: string) => void;
|
|
28
29
|
textareaStyles?: CSSProperties & Record<`--${string}`, string | number | undefined>,
|
|
29
30
|
containerRef?: MutableRefObject<HTMLLabelElement|null>
|
|
31
|
+
error?: string,
|
|
30
32
|
} & OptionalListener<'onChange', OnChangeData>
|
|
31
33
|
& OptionalListener<'onChangeDone', OnChangeDoneData>
|
|
32
34
|
>
|
|
@@ -42,10 +44,12 @@ export const Textarea = withForwardRef(function Textarea<OnChangeData, OnChangeD
|
|
|
42
44
|
onEscape,
|
|
43
45
|
textareaStyles,
|
|
44
46
|
containerRef,
|
|
47
|
+
error,
|
|
45
48
|
...otherProps
|
|
46
49
|
}: TextareaProps<OnChangeData, OnChangeDoneData>, ref: MutableRefObject<HTMLTextAreaElement> | null) {
|
|
47
50
|
// Refs
|
|
48
|
-
const innerRef =
|
|
51
|
+
const innerRef = useRef<HTMLTextAreaElement>(null);
|
|
52
|
+
const refSetter = useComposedRef(ref, innerRef);
|
|
49
53
|
|
|
50
54
|
// Variables
|
|
51
55
|
|
|
@@ -98,7 +102,8 @@ export const Textarea = withForwardRef(function Textarea<OnChangeData, OnChangeD
|
|
|
98
102
|
<label className={classNames(styles.container, className)} style={style} ref={containerRef}>
|
|
99
103
|
{label ? <span className={styles.label}>{label}</span> : null}
|
|
100
104
|
<textarea {...otherPropsWithoutData} style={textareaStyles} onKeyUp={realOnKeyPress}
|
|
101
|
-
className={styles.textarea} onChange={onChange} ref={
|
|
105
|
+
className={styles.textarea} onChange={onChange} ref={refSetter}/>
|
|
106
|
+
{error && <InlineBlock className={styles.error}><Text>{error}</Text></InlineBlock>}
|
|
102
107
|
</label>
|
|
103
108
|
);
|
|
104
109
|
}, styles);
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
.container {
|
|
2
2
|
width: 100%;
|
|
3
|
-
height: 10rem;
|
|
3
|
+
min-height: 10rem;
|
|
4
|
+
height: 100%;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
4
7
|
|
|
5
8
|
.label {
|
|
6
9
|
display: block;
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
textarea {
|
|
13
|
+
flex: 1;
|
|
10
14
|
background-color: #efeff4;
|
|
11
15
|
color: #1f1f21;
|
|
12
16
|
box-shadow: none;
|
|
@@ -19,4 +23,9 @@
|
|
|
19
23
|
border-radius: 4px;
|
|
20
24
|
border: 1px solid var(--border-light);
|
|
21
25
|
}
|
|
26
|
+
|
|
27
|
+
.error {
|
|
28
|
+
font-size: 0.7rem;
|
|
29
|
+
--text-primary-default-color: var(--text-error)
|
|
30
|
+
}
|
|
22
31
|
}
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import { ForwardedRef, useEffect, useRef } from 'react';
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} else {
|
|
12
|
-
ref.current = targetRef.current;
|
|
1
|
+
import { ForwardedRef, useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useComposedRef<RefVal>(...refs: (ForwardedRef<RefVal> | undefined)[]) {
|
|
4
|
+
return useCallback((val: RefVal | null) => {
|
|
5
|
+
for (const ref of refs) {
|
|
6
|
+
if (typeof ref === 'function') {
|
|
7
|
+
ref(val);
|
|
8
|
+
} else if (ref) {
|
|
9
|
+
ref.current = val;
|
|
10
|
+
}
|
|
13
11
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
12
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
13
|
+
}, refs);
|
|
14
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { withMemo } from '../../helper/withMemo';
|
|
3
3
|
import { RbmComponentProps, WithNoChildren } from '../RbmComponentProps';
|
|
4
|
-
import { CSSProperties
|
|
4
|
+
import { CSSProperties } from 'react';
|
|
5
5
|
import { Override } from '../../TypeHelpers';
|
|
6
6
|
|
|
7
7
|
import styles from './image.scss';
|
|
@@ -9,7 +9,7 @@ import classNames from 'classnames';
|
|
|
9
9
|
|
|
10
10
|
export type ImageProps = RbmComponentProps<
|
|
11
11
|
Override<
|
|
12
|
-
|
|
12
|
+
React.ComponentPropsWithoutRef<'img'>,
|
|
13
13
|
{
|
|
14
14
|
src: string;
|
|
15
15
|
style?: CSSProperties;
|
|
@@ -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}
|
package/src/Size.ts
ADDED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
.full-height {
|
|
2
2
|
height: 100%;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
.full-min-height {
|
|
6
6
|
min-height: 100%;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
.scrollable {
|
|
10
10
|
overflow-y: auto;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -14,14 +14,10 @@
|
|
|
14
14
|
width: 100%;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
display: none;
|
|
20
|
-
}
|
|
17
|
+
.#{$flat} .#{$flat}-hidden {
|
|
18
|
+
display: none;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
display: none;
|
|
26
|
-
}
|
|
21
|
+
.#{$material} .#{$material}-hidden {
|
|
22
|
+
display: none;
|
|
27
23
|
}
|