@fountain-ui/lab 2.0.0-beta.10 → 2.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/build/commonjs/ViewPager/ChildrenMemoizedPage.js +90 -22
  2. package/build/commonjs/ViewPager/ChildrenMemoizedPage.js.map +1 -1
  3. package/build/commonjs/ViewPager/ViewPagerNative.js +41 -34
  4. package/build/commonjs/ViewPager/ViewPagerNative.js.map +1 -1
  5. package/build/commonjs/ViewPager/ViewPagerProps.js.map +1 -1
  6. package/build/commonjs/ViewPager/ViewPagerWeb.js +32 -25
  7. package/build/commonjs/ViewPager/ViewPagerWeb.js.map +1 -1
  8. package/build/commonjs/ViewPager/index.js.map +1 -1
  9. package/build/commonjs/ViewPager/utils.js +3 -41
  10. package/build/commonjs/ViewPager/utils.js.map +1 -1
  11. package/build/module/ViewPager/ChildrenMemoizedPage.js +85 -23
  12. package/build/module/ViewPager/ChildrenMemoizedPage.js.map +1 -1
  13. package/build/module/ViewPager/ViewPagerNative.js +40 -34
  14. package/build/module/ViewPager/ViewPagerNative.js.map +1 -1
  15. package/build/module/ViewPager/ViewPagerProps.js.map +1 -1
  16. package/build/module/ViewPager/ViewPagerWeb.js +26 -23
  17. package/build/module/ViewPager/ViewPagerWeb.js.map +1 -1
  18. package/build/module/ViewPager/index.js.map +1 -1
  19. package/build/module/ViewPager/utils.js +1 -32
  20. package/build/module/ViewPager/utils.js.map +1 -1
  21. package/build/typescript/ViewPager/ChildrenMemoizedPage.d.ts +2 -1
  22. package/build/typescript/ViewPager/ViewPagerNative.d.ts +4 -1
  23. package/build/typescript/ViewPager/ViewPagerProps.d.ts +20 -19
  24. package/build/typescript/ViewPager/ViewPagerWeb.d.ts +4 -1
  25. package/build/typescript/ViewPager/index.d.ts +1 -1
  26. package/build/typescript/ViewPager/utils.d.ts +2 -19
  27. package/package.json +2 -2
  28. package/src/ViewPager/ChildrenMemoizedPage.tsx +91 -22
  29. package/src/ViewPager/ViewPagerNative.tsx +43 -38
  30. package/src/ViewPager/ViewPagerProps.ts +20 -19
  31. package/src/ViewPager/ViewPagerWeb.tsx +34 -29
  32. package/src/ViewPager/index.ts +1 -1
  33. package/src/ViewPager/utils.tsx +2 -55
@@ -1,73 +1,78 @@
1
- import React, { useCallback, useEffect, useRef } from 'react';
1
+ import React, { Children, forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
2
2
  import type { ViewPagerOnPageSelectedEvent } from 'react-native-pager-view';
3
3
  import RNViewPager from 'react-native-pager-view';
4
+ import { useSharedValue } from 'react-native-reanimated';
4
5
  import type ViewPagerProps from './ViewPagerProps';
5
- import {
6
- defaultEnableNeighborPageRerender,
7
- defaultEnableVisibleHint,
8
- defaultLoading,
9
- defaultPageComponent,
10
- usePageRenderer,
11
- } from './utils';
6
+ import type { ViewPagerInstance } from './ViewPagerProps';
7
+ import { defaultInitialPage, defaultLoading, defaultPageComponent } from './utils';
12
8
 
13
- export default function ViewPager(props: ViewPagerProps) {
9
+ const ViewPager = forwardRef<ViewPagerInstance, ViewPagerProps>(function ViewPager(props, ref) {
14
10
  const {
15
11
  children,
16
- enableVisibleHint = defaultEnableVisibleHint,
17
- index,
12
+ initialPage = defaultInitialPage,
18
13
  keyboardDismissMode = 'on-drag',
19
14
  loading = defaultLoading,
20
15
  onChange,
21
16
  pageComponent = defaultPageComponent,
22
- enableNeighborPageRerender = defaultEnableNeighborPageRerender,
23
17
  pageForceRerenderKey,
24
18
  scrollEnabled = true,
25
19
  style,
26
20
  } = props;
27
21
 
28
- const indexRef = useRef<number>(index);
22
+ const sharedIndex = useSharedValue<number>(initialPage);
29
23
  const pagerRef = useRef<RNViewPager | null>(null);
30
24
 
31
- const renderer = usePageRenderer({
32
- enableVisibleHint,
33
- index,
34
- loading,
35
- pageComponent,
36
- pageForceRerenderKey,
37
- enableNeighborPageRerender,
38
- });
25
+ const setPage = (newIndex: number) => {
26
+ requestAnimationFrame(() => {
27
+ pagerRef.current?.setPage(newIndex);
28
+ });
29
+ };
30
+
31
+ useImperativeHandle(
32
+ ref,
33
+ () => ({ setPage }),
34
+ [],
35
+ );
39
36
 
40
37
  const handlePageSelected = useCallback((e: ViewPagerOnPageSelectedEvent) => {
41
- indexRef.current = e.nativeEvent.position;
38
+ const currentIndex = sharedIndex.value;
39
+ const nextIndex = e.nativeEvent.position;
40
+
41
+ if (currentIndex === nextIndex) {
42
+ return;
43
+ }
44
+
45
+ sharedIndex.value = nextIndex;
42
46
 
43
47
  if (onChange) {
44
- onChange(indexRef.current);
48
+ onChange(nextIndex);
45
49
  }
46
50
  }, [onChange]);
47
51
 
48
- useEffect(() => {
49
- const handleNumber = index !== indexRef.current
50
- ? requestAnimationFrame(() => pagerRef.current?.setPage(index))
51
- : undefined;
52
-
53
- return () => {
54
- if (handleNumber) {
55
- cancelAnimationFrame(handleNumber);
56
- }
57
- };
58
- }, [index]);
52
+ const PageComponent = pageComponent;
59
53
 
60
54
  return (
61
55
  <RNViewPager
62
- initialPage={index}
56
+ ref={pagerRef}
57
+ initialPage={initialPage}
63
58
  keyboardDismissMode={keyboardDismissMode}
64
59
  onPageSelected={handlePageSelected}
65
60
  pageMargin={8}
66
- ref={pagerRef}
67
61
  scrollEnabled={scrollEnabled}
68
62
  style={style}
69
63
  >
70
- {renderer(children)}
64
+ {Children.map(children, (child, index) => (
65
+ <PageComponent
66
+ key={index}
67
+ children={child}
68
+ index={index}
69
+ loading={loading}
70
+ rerenderKey={pageForceRerenderKey}
71
+ sharedIndex={sharedIndex}
72
+ />
73
+ ))}
71
74
  </RNViewPager>
72
75
  );
73
- };
76
+ });
77
+
78
+ export default ViewPager;
@@ -1,41 +1,42 @@
1
- import React from 'react';
1
+ import type { ComponentType, PropsWithChildren, ReactNode, Ref } from 'react';
2
2
  import type { ViewProps } from 'react-native';
3
+ import type { SharedValue } from 'react-native-reanimated';
3
4
  import type { ComponentProps } from '@fountain-ui/core';
4
5
 
5
6
  export type KeyboardDismissMode = 'none' | 'on-drag';
6
7
 
7
- export type PageProps = React.PropsWithChildren<ViewProps> & {
8
- isNeighbor: boolean;
9
- isVisible: boolean;
10
- rerenderKey?: any;
8
+ export type PageProps = PropsWithChildren<ViewProps> & {
9
+ index: number;
10
+ loading: ViewPagerProps['loading'];
11
+ sharedIndex: SharedValue<number>;
12
+ rerenderKey?: ViewPagerProps['pageForceRerenderKey'];
11
13
  };
12
14
 
13
- export type PageComponent = React.ComponentType<PageProps>;
15
+ export type PageComponent = ComponentType<PageProps>;
14
16
 
15
17
  export type Loading = 'lazy' | 'eager';
16
18
 
17
- export default interface ViewPagerProps extends ComponentProps<{
19
+ export interface ViewPagerInstance {
18
20
  /**
19
- * Collection of ViewPager components.
21
+ * Function to scroll to a specific page in the ViewPager. Invalid index is ignored.
22
+ * @param index
20
23
  */
21
- children?: React.ReactNode;
24
+ setPage: (index: number) => void;
25
+ }
22
26
 
23
- /**
24
- * If `true`, enable page visible hint.
25
- * @default false
26
- */
27
- enableVisibleHint?: boolean;
27
+ export default interface ViewPagerProps extends ComponentProps<{
28
+ ref?: Ref<ViewPagerInstance>;
28
29
 
29
30
  /**
30
- * If `true`, Based on the current index, the pages on the left and right are also rerender.
31
- * @default false
31
+ * Collection of ViewPager components.
32
32
  */
33
- enableNeighborPageRerender?: boolean;
33
+ children?: ReactNode;
34
34
 
35
35
  /**
36
- * A number representing the index of the active page.
36
+ * Index of initial page that should be selected.
37
+ * @default 0
37
38
  */
38
- index: number,
39
+ initialPage?: number;
39
40
 
40
41
  /**
41
42
  * Whether to load the page immediately (`eager`) or on an as-needed basis (`lazy`).
@@ -1,13 +1,10 @@
1
- import React from 'react';
2
- import { StyleSheet, View } from 'react-native';
1
+ import React, { Children, forwardRef, useImperativeHandle } from 'react';
2
+ import { View } from 'react-native';
3
+ import { useSharedValue } from 'react-native-reanimated';
4
+ import { StyleSheet } from '@fountain-ui/core';
3
5
  import type ViewPagerProps from './ViewPagerProps';
4
- import {
5
- defaultEnableNeighborPageRerender,
6
- defaultEnableVisibleHint,
7
- defaultLoading,
8
- defaultPageComponent,
9
- usePageRenderer,
10
- } from './utils';
6
+ import type { ViewPagerInstance } from './ViewPagerProps';
7
+ import { defaultInitialPage, defaultLoading, defaultPageComponent } from './utils';
11
8
 
12
9
  const styles = StyleSheet.create({
13
10
  root: {
@@ -15,38 +12,46 @@ const styles = StyleSheet.create({
15
12
  overflow: 'auto',
16
13
  position: 'relative',
17
14
  },
18
- none: { display: 'none' },
19
15
  });
20
16
 
21
- export default function ViewPager(props: ViewPagerProps) {
17
+ const ViewPager = forwardRef<ViewPagerInstance, ViewPagerProps>(function ViewPager(props, ref) {
22
18
  const {
23
19
  children,
24
- enableVisibleHint = defaultEnableVisibleHint,
25
- enableNeighborPageRerender = defaultEnableNeighborPageRerender,
26
- index,
20
+ initialPage = defaultInitialPage,
27
21
  loading = defaultLoading,
28
22
  pageComponent = defaultPageComponent,
29
23
  pageForceRerenderKey,
30
24
  style,
31
25
  } = props;
32
26
 
33
- const renderer = usePageRenderer({
34
- enableVisibleHint,
35
- enableNeighborPageRerender,
36
- index,
37
- loading,
38
- pageComponent,
39
- pageForceRerenderKey,
40
- });
27
+ const sharedIndex = useSharedValue<number>(initialPage);
28
+
29
+ const setPage = (newIndex: number) => {
30
+ sharedIndex.value = newIndex;
31
+ };
32
+
33
+ useImperativeHandle(
34
+ ref,
35
+ () => ({ setPage }),
36
+ [],
37
+ );
38
+
39
+ const PageComponent = pageComponent;
41
40
 
42
41
  return (
43
42
  <View style={[styles.root, style]}>
44
- {React.Children.map(renderer(children), (child, i) =>
45
- React.cloneElement(child as React.ReactElement, {
46
- isVisible: i === index,
47
- style: i !== index ? styles.none : StyleSheet.absoluteFill,
48
- }),
49
- )}
43
+ {Children.map(children, (child, index) => (
44
+ <PageComponent
45
+ key={index}
46
+ children={child}
47
+ index={index}
48
+ loading={loading}
49
+ rerenderKey={pageForceRerenderKey}
50
+ sharedIndex={sharedIndex}
51
+ />
52
+ ))}
50
53
  </View>
51
54
  );
52
- };
55
+ });
56
+
57
+ export default ViewPager;
@@ -1,2 +1,2 @@
1
1
  export { default } from './ViewPagerWeb';
2
- export type { default as ViewPagerProps } from './ViewPagerProps';
2
+ export type { default as ViewPagerProps, ViewPagerInstance } from './ViewPagerProps';
@@ -1,62 +1,9 @@
1
1
  import React from 'react';
2
- import ViewPagerProps, { Loading, PageComponent } from './ViewPagerProps';
2
+ import { Loading, PageComponent } from './ViewPagerProps';
3
3
  import ChildrenMemoizedPage from './ChildrenMemoizedPage';
4
4
 
5
- export interface PageState {
6
- visited?: boolean;
7
- }
8
-
9
- export interface PageRenderOption {
10
- enableVisibleHint: boolean;
11
- enableNeighborPageRerender: boolean;
12
- index: number;
13
- loading: Loading;
14
- pageComponent: PageComponent;
15
- pageForceRerenderKey?: any;
16
- }
17
-
18
- export interface PageRenderer {
19
- (children: ViewPagerProps['children']): React.ReactNode;
20
- }
21
-
22
- export const defaultEnableVisibleHint: boolean = false;
23
-
24
- export const defaultEnableNeighborPageRerender: boolean = false;
5
+ export const defaultInitialPage: number = 0;
25
6
 
26
7
  export const defaultLoading: Loading = 'lazy';
27
8
 
28
9
  export const defaultPageComponent: PageComponent = ChildrenMemoizedPage;
29
-
30
- export const usePageRenderer = ({
31
- enableVisibleHint,
32
- index: currentIndex,
33
- loading,
34
- pageComponent: PageComponent,
35
- pageForceRerenderKey,
36
- enableNeighborPageRerender,
37
- }: PageRenderOption): PageRenderer => {
38
- const pagesStateRef = React.useRef<Array<PageState>>([]);
39
-
40
- pagesStateRef.current[currentIndex] = {
41
- ...pagesStateRef.current[currentIndex],
42
- visited: true,
43
- };
44
-
45
- return (children => React.Children.map(children, (child, index) => {
46
- const visited = pagesStateRef.current[index]?.visited ?? false;
47
- const content = (loading === 'eager' || visited) ? child : null;
48
-
49
- const isVisible = enableVisibleHint ? (index === currentIndex) : false;
50
- const isNeighborIndex = index === currentIndex - 1 || index === currentIndex + 1;
51
-
52
- return (
53
- <PageComponent
54
- key={index}
55
- children={content}
56
- isNeighbor={enableNeighborPageRerender && isNeighborIndex}
57
- isVisible={isVisible}
58
- rerenderKey={pageForceRerenderKey}
59
- />
60
- );
61
- }));
62
- };