@fountain-ui/lab 1.21.1 → 2.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/commonjs/BottomSheet/BottomSheetNative.js +7 -1
- package/build/commonjs/BottomSheet/BottomSheetNative.js.map +1 -1
- package/build/commonjs/ComicViewer/ComicViewer.js +163 -0
- package/build/commonjs/ComicViewer/ComicViewer.js.map +1 -0
- package/build/commonjs/ComicViewer/ComicViewerItemProps.js +6 -0
- package/build/commonjs/ComicViewer/ComicViewerItemProps.js.map +1 -0
- package/build/commonjs/ComicViewer/ComicViewerProps.js +2 -0
- package/build/commonjs/ComicViewer/ComicViewerProps.js.map +1 -0
- package/build/commonjs/ComicViewer/ViewerItem.js +87 -0
- package/build/commonjs/ComicViewer/ViewerItem.js.map +1 -0
- package/build/commonjs/ComicViewer/index.js +16 -0
- package/build/commonjs/ComicViewer/index.js.map +1 -0
- package/build/commonjs/DateTimePicker/DateTimePicker.js +7 -1
- package/build/commonjs/DateTimePicker/DateTimePicker.js.map +1 -1
- package/build/commonjs/DateTimePicker/YearPicker.js +1 -0
- package/build/commonjs/DateTimePicker/YearPicker.js.map +1 -1
- package/build/commonjs/index.js +21 -0
- package/build/commonjs/index.js.map +1 -1
- package/build/module/BottomSheet/BottomSheetNative.js +7 -1
- package/build/module/BottomSheet/BottomSheetNative.js.map +1 -1
- package/build/module/ComicViewer/ComicViewer.js +146 -0
- package/build/module/ComicViewer/ComicViewer.js.map +1 -0
- package/build/module/ComicViewer/ComicViewerItemProps.js +2 -0
- package/build/module/ComicViewer/ComicViewerItemProps.js.map +1 -0
- package/build/module/ComicViewer/ComicViewerProps.js +2 -0
- package/build/module/ComicViewer/ComicViewerProps.js.map +1 -0
- package/build/module/ComicViewer/ViewerItem.js +71 -0
- package/build/module/ComicViewer/ViewerItem.js.map +1 -0
- package/build/module/ComicViewer/index.js +2 -0
- package/build/module/ComicViewer/index.js.map +1 -0
- package/build/module/DateTimePicker/DateTimePicker.js +7 -1
- package/build/module/DateTimePicker/DateTimePicker.js.map +1 -1
- package/build/module/DateTimePicker/YearPicker.js +1 -0
- package/build/module/DateTimePicker/YearPicker.js.map +1 -1
- package/build/module/index.js +2 -0
- package/build/module/index.js.map +1 -1
- package/build/typescript/ComicViewer/ComicViewer.d.ts +3 -0
- package/build/typescript/ComicViewer/ComicViewerItemProps.d.ts +12 -0
- package/build/typescript/ComicViewer/ComicViewerProps.d.ts +76 -0
- package/build/typescript/ComicViewer/ViewerItem.d.ts +7 -0
- package/build/typescript/ComicViewer/index.d.ts +3 -0
- package/build/typescript/index.d.ts +2 -0
- package/package.json +5 -6
- package/src/BottomSheet/BottomSheetNative.tsx +6 -0
- package/src/ComicViewer/ComicViewer.tsx +162 -0
- package/src/ComicViewer/ComicViewerItemProps.ts +15 -0
- package/src/ComicViewer/ComicViewerProps.ts +90 -0
- package/src/ComicViewer/ViewerItem.tsx +76 -0
- package/src/ComicViewer/index.ts +3 -0
- package/src/DateTimePicker/DateTimePicker.tsx +6 -0
- package/src/DateTimePicker/YearPicker.tsx +2 -1
- package/src/index.ts +3 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { FlatList, ListRenderItem, ViewToken } from 'react-native';
|
|
3
|
+
import * as R from 'ramda';
|
|
4
|
+
import { ComicViewerItemData, default as ComicViewerProps, ErrorInfo } from './ComicViewerProps';
|
|
5
|
+
import type ComicViewerItemProps from './ComicViewerItemProps';
|
|
6
|
+
import ViewerItem from './ViewerItem';
|
|
7
|
+
|
|
8
|
+
const getItemHeights = <T, >(items: ComicViewerItemProps<T>[]): number[] => R.map((content: ComicViewerItemProps<T>) => content.height)(items);
|
|
9
|
+
const appender = (left: number, right: number): [number, number] => [left + right, left + right];
|
|
10
|
+
const getHeightAccum = (itemHeights: number[]): [number, number[]] => R.mapAccum(appender, 0, itemHeights);
|
|
11
|
+
|
|
12
|
+
const keyExtractor = <T, >(item: ComicViewerItemProps<T>) => item.id;
|
|
13
|
+
|
|
14
|
+
export default function ComicViewer<T>(props: ComicViewerProps<T>) {
|
|
15
|
+
const {
|
|
16
|
+
data,
|
|
17
|
+
errorDebounceMillis = 500,
|
|
18
|
+
errorRetryCount = 3,
|
|
19
|
+
initialNumToRender = 1,
|
|
20
|
+
initialScrollPercentage = 0,
|
|
21
|
+
itemVisiblePercentThreshold = 0,
|
|
22
|
+
onError,
|
|
23
|
+
viewerWidth,
|
|
24
|
+
windowSize = 3,
|
|
25
|
+
...otherProps
|
|
26
|
+
} = props;
|
|
27
|
+
|
|
28
|
+
const flatListRef = useRef<FlatList>(null);
|
|
29
|
+
|
|
30
|
+
const errors = useRef<Map<string, number>>(new Map());
|
|
31
|
+
const debounceTimeOut = useRef<NodeJS.Timeout | null>(null);
|
|
32
|
+
|
|
33
|
+
const resourceString = R.toString(R.map((itemData: ComicViewerItemData) => itemData.sourceUrl)(data));
|
|
34
|
+
|
|
35
|
+
const initialItems = R.map((itemData: ComicViewerItemData<T>) => ({
|
|
36
|
+
...itemData,
|
|
37
|
+
isViewable: false,
|
|
38
|
+
width: viewerWidth,
|
|
39
|
+
height: (itemData.height * viewerWidth) / itemData.width,
|
|
40
|
+
}))(data);
|
|
41
|
+
|
|
42
|
+
const [items, setItems] = useState<ComicViewerItemProps<T>[]>(initialItems);
|
|
43
|
+
|
|
44
|
+
const itemHeights = getItemHeights(items);
|
|
45
|
+
const itemHeightAccum = getHeightAccum(itemHeights);
|
|
46
|
+
|
|
47
|
+
const viewabilityConfig = useMemo(() => ({
|
|
48
|
+
itemVisiblePercentThreshold,
|
|
49
|
+
}), [itemVisiblePercentThreshold]);
|
|
50
|
+
|
|
51
|
+
const getItemLayout = useCallback((data: any, index: number) => {
|
|
52
|
+
const offsets = R.prepend(0, itemHeightAccum[1]);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
length: itemHeights[index],
|
|
56
|
+
offset: offsets[index],
|
|
57
|
+
index,
|
|
58
|
+
};
|
|
59
|
+
}, [itemHeights]);
|
|
60
|
+
|
|
61
|
+
const onViewableItemsChanged = useRef(({ viewableItems }: {
|
|
62
|
+
viewableItems: Array<ViewToken>,
|
|
63
|
+
}) => {
|
|
64
|
+
setItems((prev: ComicViewerItemProps<T>[]) => {
|
|
65
|
+
const viewableItemIds = R.map((viewableItem: ViewToken) => viewableItem.item.id)(viewableItems);
|
|
66
|
+
|
|
67
|
+
return R.map((prevItem: ComicViewerItemProps<T>) => ({
|
|
68
|
+
...prevItem,
|
|
69
|
+
isViewable: R.includes(prevItem.id, viewableItemIds),
|
|
70
|
+
}))([...prev]);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const onErrorHandler = (errors: ErrorInfo[]) => {
|
|
75
|
+
const isRetryLimited = R.any((error: ErrorInfo) => error.count >= errorRetryCount)(errors);
|
|
76
|
+
|
|
77
|
+
if (isRetryLimited) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
onError && onError(errors);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const itemErrorHandler = useCallback((errorInfo: ErrorInfo) => {
|
|
85
|
+
errors.current.set(errorInfo.id, errorInfo.count);
|
|
86
|
+
|
|
87
|
+
if (debounceTimeOut.current) {
|
|
88
|
+
clearTimeout(debounceTimeOut.current);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
debounceTimeOut.current = setTimeout(function () {
|
|
92
|
+
const errorsArray = Array.from(errors.current.entries());
|
|
93
|
+
const errorsInfo = R.map(([key, value]: [string, number]) => ({
|
|
94
|
+
id: key,
|
|
95
|
+
count: value,
|
|
96
|
+
}))(errorsArray);
|
|
97
|
+
|
|
98
|
+
onErrorHandler([...errorsInfo]);
|
|
99
|
+
errors.current.clear();
|
|
100
|
+
}, errorDebounceMillis);
|
|
101
|
+
}, [errorDebounceMillis, errors.current]);
|
|
102
|
+
|
|
103
|
+
const renderItem: ListRenderItem<ComicViewerItemProps<T>> = useCallback(({ item }) => {
|
|
104
|
+
const props = {
|
|
105
|
+
...item,
|
|
106
|
+
onError: itemErrorHandler,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return <ViewerItem props={props}/>;
|
|
110
|
+
}, []);
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
setItems((prev: ComicViewerItemProps<T>[]) => {
|
|
114
|
+
return R.map((prevItem: ComicViewerItemProps<T>) => {
|
|
115
|
+
const currentData = R.find((currentItemData: ComicViewerItemData<T>) => prevItem.id === currentItemData.id)(data);
|
|
116
|
+
|
|
117
|
+
if (currentData && (currentData.sourceUrl !== prevItem.sourceUrl)) {
|
|
118
|
+
return {
|
|
119
|
+
...prevItem,
|
|
120
|
+
sourceUrl: currentData.sourceUrl,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return prevItem;
|
|
125
|
+
})([...prev]);
|
|
126
|
+
});
|
|
127
|
+
}, [resourceString]);
|
|
128
|
+
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
const newItems = R.map((item: ComicViewerItemProps<T>) => ({
|
|
131
|
+
...item,
|
|
132
|
+
width: viewerWidth,
|
|
133
|
+
height: (item.height * viewerWidth) / item.width,
|
|
134
|
+
}))(items);
|
|
135
|
+
|
|
136
|
+
setItems(newItems);
|
|
137
|
+
}, [viewerWidth]);
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
const totalHeight = itemHeightAccum[0];
|
|
141
|
+
const offset = Math.floor((initialScrollPercentage / 100) * totalHeight);
|
|
142
|
+
|
|
143
|
+
if (flatListRef.current) {
|
|
144
|
+
flatListRef.current.scrollToOffset({ offset, animated: false });
|
|
145
|
+
}
|
|
146
|
+
}, [flatListRef.current]);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<FlatList
|
|
150
|
+
data={items}
|
|
151
|
+
getItemLayout={getItemLayout}
|
|
152
|
+
initialNumToRender={initialNumToRender}
|
|
153
|
+
keyExtractor={keyExtractor}
|
|
154
|
+
onViewableItemsChanged={onViewableItemsChanged.current}
|
|
155
|
+
ref={flatListRef}
|
|
156
|
+
renderItem={renderItem}
|
|
157
|
+
viewabilityConfig={viewabilityConfig}
|
|
158
|
+
windowSize={windowSize}
|
|
159
|
+
{...otherProps}
|
|
160
|
+
/>
|
|
161
|
+
);
|
|
162
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ComicViewerItemData, ErrorInfo } from './ComicViewerProps';
|
|
2
|
+
|
|
3
|
+
type ComicViewerItemProps<T> = ComicViewerItemData<T> & {
|
|
4
|
+
/**
|
|
5
|
+
* FlatListItem is viewable in screen.
|
|
6
|
+
*/
|
|
7
|
+
isViewable: boolean;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Error handler
|
|
11
|
+
*/
|
|
12
|
+
onError?: (errorInfo: ErrorInfo) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default ComicViewerItemProps;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ComponentProps } from '@fountain-ui/core';
|
|
2
|
+
|
|
3
|
+
export interface ErrorInfo {
|
|
4
|
+
/**
|
|
5
|
+
* ComicViewerItemData.id.
|
|
6
|
+
*/
|
|
7
|
+
id: string;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Number of times an error occurred.
|
|
11
|
+
*/
|
|
12
|
+
count: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type ComicViewerItemData<T = {}> = T & {
|
|
16
|
+
/**
|
|
17
|
+
* Image height.
|
|
18
|
+
*/
|
|
19
|
+
height: number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Unique value for identifying.
|
|
23
|
+
*/
|
|
24
|
+
id: string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Image sourceUrl for displaying.
|
|
28
|
+
*/
|
|
29
|
+
sourceUrl: string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Image width.
|
|
33
|
+
*/
|
|
34
|
+
width: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default interface ComicViewerProps<T> extends ComponentProps <{
|
|
38
|
+
/**
|
|
39
|
+
* Data for render.
|
|
40
|
+
*/
|
|
41
|
+
data: ComicViewerItemData<T>[];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Delay Time to call the error handler.
|
|
45
|
+
* @default 500
|
|
46
|
+
*/
|
|
47
|
+
errorDebounceMillis?: number;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* How many times retry onError when same item error occur
|
|
51
|
+
* @default 3
|
|
52
|
+
*/
|
|
53
|
+
errorRetryCount?: number;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* How many items to render in the initial batch.
|
|
57
|
+
* @default 1
|
|
58
|
+
*/
|
|
59
|
+
initialNumToRender?: number;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Start at initialScrollPercentage.
|
|
63
|
+
* If over 100, scroll to end.
|
|
64
|
+
* @default 0
|
|
65
|
+
*/
|
|
66
|
+
initialScrollPercentage?: number;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The value for FlatList viewabilityConfig.itemVisiblePercentThreshold.
|
|
70
|
+
* @default 0
|
|
71
|
+
*/
|
|
72
|
+
itemVisiblePercentThreshold?: number;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Handling all viewerItem errors at once.
|
|
76
|
+
* @param errors Array of ViewerItems errorInfo.
|
|
77
|
+
*/
|
|
78
|
+
onError?: (errors: ErrorInfo[]) => void;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Comic viewer width.
|
|
82
|
+
*/
|
|
83
|
+
viewerWidth: number;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The value for FlatList windowSize.
|
|
87
|
+
* @default 3
|
|
88
|
+
*/
|
|
89
|
+
windowSize?: number;
|
|
90
|
+
}> {}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { Image, StyleSheet } from '@fountain-ui/core';
|
|
4
|
+
import ComicViewerItemProps from './ComicViewerItemProps';
|
|
5
|
+
|
|
6
|
+
const styles = StyleSheet.create({
|
|
7
|
+
placeholder: {
|
|
8
|
+
backgroundColor: '#abcabc',
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
function ViewerItem<T>({ props }: { props: ComicViewerItemProps<T> }) {
|
|
13
|
+
const {
|
|
14
|
+
height,
|
|
15
|
+
id,
|
|
16
|
+
isViewable,
|
|
17
|
+
onError,
|
|
18
|
+
sourceUrl,
|
|
19
|
+
width,
|
|
20
|
+
} = props;
|
|
21
|
+
|
|
22
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
23
|
+
|
|
24
|
+
const errorCount = useRef<number>(0);
|
|
25
|
+
|
|
26
|
+
const onLoad = useCallback(() => {
|
|
27
|
+
errorCount.current = 0;
|
|
28
|
+
setIsLoaded(true);
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const handleError = useCallback(() => {
|
|
32
|
+
errorCount.current = errorCount.current + 1;
|
|
33
|
+
|
|
34
|
+
onError && onError({
|
|
35
|
+
id,
|
|
36
|
+
count: errorCount.current
|
|
37
|
+
});
|
|
38
|
+
}, [id]);
|
|
39
|
+
|
|
40
|
+
const viewStyle = { width, height };
|
|
41
|
+
|
|
42
|
+
const Placeholder = () => (
|
|
43
|
+
<View style={[
|
|
44
|
+
viewStyle,
|
|
45
|
+
styles.placeholder,
|
|
46
|
+
]}/>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (!isViewable && !isLoaded) {
|
|
50
|
+
return <Placeholder/>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Image
|
|
55
|
+
disableOutline={true}
|
|
56
|
+
key={sourceUrl}
|
|
57
|
+
onLoad={onLoad}
|
|
58
|
+
onError={handleError}
|
|
59
|
+
source={{ uri: sourceUrl }}
|
|
60
|
+
style={viewStyle}
|
|
61
|
+
square={true}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default React.memo(ViewerItem, (prevProps, nextProps) => {
|
|
67
|
+
if (prevProps.props.isViewable !== nextProps.props.isViewable) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (prevProps.props.sourceUrl !== nextProps.props.sourceUrl) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return true;
|
|
76
|
+
});
|
|
@@ -81,6 +81,12 @@ export default function DateTimePicker(props: DateTimePickerProps) {
|
|
|
81
81
|
onPress={() => setYearPickerVisible(true)}
|
|
82
82
|
/>
|
|
83
83
|
)}
|
|
84
|
+
theme={{
|
|
85
|
+
backgroundColor: theme.palette.background.default,
|
|
86
|
+
calendarBackground: theme.palette.background.default,
|
|
87
|
+
dayTextColor: theme.palette.text.primary,
|
|
88
|
+
textDisabledColor: theme.palette.text.hint,
|
|
89
|
+
}}
|
|
84
90
|
/>
|
|
85
91
|
);
|
|
86
92
|
};
|
|
@@ -61,6 +61,7 @@ const YearPicker = ({
|
|
|
61
61
|
<Column>
|
|
62
62
|
<Typography
|
|
63
63
|
children={formatDate(date, locale)}
|
|
64
|
+
color={'textPrimary'}
|
|
64
65
|
variant={'subtitle2'}
|
|
65
66
|
/>
|
|
66
67
|
<FlatList
|
|
@@ -84,4 +85,4 @@ const YearPicker = ({
|
|
|
84
85
|
);
|
|
85
86
|
};
|
|
86
87
|
|
|
87
|
-
export default YearPicker;
|
|
88
|
+
export default YearPicker;
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,9 @@ export * from './FlipCard';
|
|
|
13
13
|
export { default as ViewPager } from './ViewPager';
|
|
14
14
|
export * from './ViewPager';
|
|
15
15
|
|
|
16
|
+
export { default as ComicViewer } from './ComicViewer';
|
|
17
|
+
export * from './ComicViewer';
|
|
18
|
+
|
|
16
19
|
export { default as StatusBarProvider, useStatusBarContext } from './StatusBarProvider';
|
|
17
20
|
export * from './StatusBarProvider';
|
|
18
21
|
|