@fountain-ui/core 2.0.0-beta.75 → 2.0.0-beta.76
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/Dialog/Dialog.js +30 -2
- package/build/commonjs/Dialog/Dialog.js.map +1 -1
- package/build/commonjs/Modal/Modal.js +7 -1
- package/build/commonjs/Modal/Modal.js.map +1 -1
- package/build/commonjs/Modal/ModalProps.js.map +1 -1
- package/build/commonjs/Snackbar/Snackbar.js +30 -7
- package/build/commonjs/Snackbar/Snackbar.js.map +1 -1
- package/build/commonjs/Tab/Tab.js +23 -14
- package/build/commonjs/Tab/Tab.js.map +1 -1
- package/build/commonjs/Tab/TabIndicator.js +5 -4
- package/build/commonjs/Tab/TabIndicator.js.map +1 -1
- package/build/commonjs/Tab/TabProps.js.map +1 -1
- package/build/commonjs/Tab/index.js.map +1 -1
- package/build/commonjs/Tabs/Tabs.js +46 -6
- package/build/commonjs/Tabs/Tabs.js.map +1 -1
- package/build/commonjs/Tabs/TabsProps.js.map +1 -1
- package/build/module/Dialog/Dialog.js +25 -3
- package/build/module/Dialog/Dialog.js.map +1 -1
- package/build/module/Modal/Modal.js +6 -1
- package/build/module/Modal/Modal.js.map +1 -1
- package/build/module/Modal/ModalProps.js.map +1 -1
- package/build/module/Snackbar/Snackbar.js +21 -5
- package/build/module/Snackbar/Snackbar.js.map +1 -1
- package/build/module/Tab/Tab.js +24 -15
- package/build/module/Tab/Tab.js.map +1 -1
- package/build/module/Tab/TabIndicator.js +5 -4
- package/build/module/Tab/TabIndicator.js.map +1 -1
- package/build/module/Tab/TabProps.js.map +1 -1
- package/build/module/Tab/index.js.map +1 -1
- package/build/module/Tabs/Tabs.js +48 -6
- package/build/module/Tabs/Tabs.js.map +1 -1
- package/build/module/Tabs/TabsProps.js.map +1 -1
- package/build/typescript/Modal/ModalProps.d.ts +10 -0
- package/build/typescript/Tab/TabIndicator.d.ts +3 -2
- package/build/typescript/Tab/TabProps.d.ts +14 -1
- package/build/typescript/Tab/index.d.ts +1 -1
- package/build/typescript/Tabs/Tabs.d.ts +1 -1
- package/build/typescript/Tabs/TabsProps.d.ts +14 -1
- package/package.json +2 -2
- package/src/Dialog/Dialog.tsx +26 -4
- package/src/Modal/Modal.tsx +7 -1
- package/src/Modal/ModalProps.ts +12 -0
- package/src/Snackbar/Snackbar.tsx +29 -7
- package/src/Tab/Tab.tsx +43 -17
- package/src/Tab/TabIndicator.tsx +7 -7
- package/src/Tab/TabProps.ts +16 -1
- package/src/Tab/index.ts +1 -1
- package/src/Tabs/Tabs.tsx +42 -4
- package/src/Tabs/TabsProps.ts +16 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["TabsProps.ts"],"sourcesContent":["import type { ReactNode, Ref } from 'react';\nimport type { ViewProps } from 'react-native';\nimport type { TabIndicatorColor } from './TabIndicatorProps';\nimport type { TabVariant } from '../Tab';\nimport type { OverridableComponentProps, SyncAnimatedValue } from '../types';\nimport type { KeyboardDismissMode, KeyboardShouldPersistTaps, TabsInstance } from './types';\n\nexport default interface TabsProps extends OverridableComponentProps<ViewProps, {\n ref?: Ref<TabsInstance>;\n\n /**\n * Collection of Tab components.\n */\n children: ReactNode;\n\n /**\n * If `true`, the indicator is disabled.\n * @default false\n */\n disableIndicator?: boolean;\n\n /**\n * The color of tab indicator\n * @default 'primary'\n */\n indicatorColor?: TabIndicatorColor;\n\n /**\n * Index of initial tab that should be selected.\n * @default 0\n */\n initialIndex?: number;\n\n /**\n * keyboard dismissing condition of dragging.\n * @default 'none'\n */\n keyboardDismissMode?: KeyboardDismissMode,\n\n /**\n * keyboard persisting condition of tapping.\n * @default 'never'\n */\n keyboardShouldPersistTaps?: KeyboardShouldPersistTaps,\n\n /**\n * Callback fired when a tab is selected.\n */\n onChange?: (newIndex: number) => void;\n\n /**\n * If `true`, the component will be able to scroll.\n * @default false\n */\n scrollable?: boolean;\n\n /**\n * Unstable API.\n */\n UNSTABLE_sharedIndex?: SyncAnimatedValue;\n\n /**\n * The variant to use.\n * @default 'primary'\n */\n variant?: TabVariant;\n}> {}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"names":[],"sources":["TabsProps.ts"],"sourcesContent":["import type { ReactNode, Ref } from 'react';\nimport type { ViewProps } from 'react-native';\nimport type { TabIndicatorColor } from './TabIndicatorProps';\nimport type { TabVariant, TabIndicatorSize } from '../Tab';\nimport type { OverridableComponentProps, SyncAnimatedValue } from '../types';\nimport type { KeyboardDismissMode, KeyboardShouldPersistTaps, TabsInstance } from './types';\n\nexport default interface TabsProps extends OverridableComponentProps<ViewProps, {\n ref?: Ref<TabsInstance>;\n\n /**\n * Collection of Tab components.\n */\n children: ReactNode;\n\n /**\n * If `true`, the indicator is disabled.\n * @default false\n */\n disableIndicator?: boolean;\n\n /**\n * The color of tab indicator\n * @default 'primary'\n */\n indicatorColor?: TabIndicatorColor;\n\n /**\n * The size of tab indicator.\n * 'full' adjusts the indicator to the size of the Tab,\n * while 'fit-content' adjusts the indicator to the size of the content inside the Tab.\n * @default 'full'\n */\n indicatorSize?: TabIndicatorSize;\n\n /**\n * Index of initial tab that should be selected.\n * @default 0\n */\n initialIndex?: number;\n\n /**\n * keyboard dismissing condition of dragging.\n * @default 'none'\n */\n keyboardDismissMode?: KeyboardDismissMode,\n\n /**\n * keyboard persisting condition of tapping.\n * @default 'never'\n */\n keyboardShouldPersistTaps?: KeyboardShouldPersistTaps,\n\n /**\n * Callback fired when a tab is selected.\n */\n onChange?: (newIndex: number) => void;\n\n /**\n * If `true`, the component will be able to scroll.\n * @default false\n */\n scrollable?: boolean;\n\n /**\n * Unstable API.\n */\n UNSTABLE_sharedIndex?: SyncAnimatedValue;\n\n /**\n * The variant to use.\n * @default 'primary'\n */\n variant?: TabVariant;\n\n /**\n * Callback function executed when a Tab is selected.\n * Executed even if the index does not change when a Tab is pressed.\n * Receives the next tab index and the current tab index as parameters.\n */\n onTabSelected?: (newIndex: number, currentIndex: number) => void;\n}> {}\n"],"mappings":""}
|
|
@@ -21,6 +21,16 @@ export default interface ModalProps extends OverridableComponentProps<ViewProps,
|
|
|
21
21
|
* @default false
|
|
22
22
|
*/
|
|
23
23
|
disableAnimation?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The number of milliseconds to enter animation.
|
|
26
|
+
* @default 300
|
|
27
|
+
*/
|
|
28
|
+
enterDuration?: number;
|
|
29
|
+
/**
|
|
30
|
+
* The number of milliseconds to exit animation.
|
|
31
|
+
* @default 150
|
|
32
|
+
*/
|
|
33
|
+
exitDuration?: number;
|
|
24
34
|
/**
|
|
25
35
|
* If `true`, the backdrop is not rendered.
|
|
26
36
|
* @default false
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { TabIndicatorColor } from './TabProps';
|
|
3
|
-
declare const _default: React.MemoExoticComponent<({ color }: {
|
|
2
|
+
import type { TabIndicatorColor, TabIndicatorSize } from './TabProps';
|
|
3
|
+
declare const _default: React.MemoExoticComponent<({ indicatorSize, color }: {
|
|
4
|
+
indicatorSize: TabIndicatorSize;
|
|
4
5
|
color: TabIndicatorColor;
|
|
5
6
|
}) => JSX.Element>;
|
|
6
7
|
export default _default;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { LayoutChangeEvent } from 'react-native';
|
|
2
3
|
import type { TabBaseProps } from '../TabBase';
|
|
3
4
|
import type { OverridableComponentProps } from '../types';
|
|
4
5
|
export declare type TabVariant = 'primary' | 'secondary' | 'bottom-navigation';
|
|
5
6
|
export declare type TabIndicatorColor = 'primary' | 'secondary';
|
|
7
|
+
export declare type TabIndicatorSize = 'full' | 'fit-content';
|
|
6
8
|
export default interface TabProps extends OverridableComponentProps<TabBaseProps, {
|
|
7
9
|
/**
|
|
8
10
|
* If `true`, the badge is visible.
|
|
@@ -12,7 +14,7 @@ export default interface TabProps extends OverridableComponentProps<TabBaseProps
|
|
|
12
14
|
/**
|
|
13
15
|
* The label of the Tab.
|
|
14
16
|
*/
|
|
15
|
-
children: string;
|
|
17
|
+
children: string | React.ReactElement;
|
|
16
18
|
/**
|
|
17
19
|
* If `true`, the indicator is enabled.
|
|
18
20
|
* @default false
|
|
@@ -32,6 +34,13 @@ export default interface TabProps extends OverridableComponentProps<TabBaseProps
|
|
|
32
34
|
* @default 'primary'
|
|
33
35
|
*/
|
|
34
36
|
indicatorColor?: TabIndicatorColor;
|
|
37
|
+
/**
|
|
38
|
+
* The size of tab indicator.
|
|
39
|
+
* 'full' adjusts the indicator to the size of the Tab,
|
|
40
|
+
* while 'fit-content' adjusts the indicator to the size of the content inside the Tab.
|
|
41
|
+
* @default 'full'
|
|
42
|
+
*/
|
|
43
|
+
indicatorSize?: TabIndicatorSize;
|
|
35
44
|
/**
|
|
36
45
|
* If supplied, use this icon on selected state.
|
|
37
46
|
*/
|
|
@@ -41,5 +50,9 @@ export default interface TabProps extends OverridableComponentProps<TabBaseProps
|
|
|
41
50
|
* @default 'primary'
|
|
42
51
|
*/
|
|
43
52
|
variant?: TabVariant;
|
|
53
|
+
/**
|
|
54
|
+
* Function to be passed to the child component's onLayout prop.
|
|
55
|
+
*/
|
|
56
|
+
onTabInnerLayout?: (event: LayoutChangeEvent) => void;
|
|
44
57
|
}> {
|
|
45
58
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default } from './Tab';
|
|
2
|
-
export type { default as TabProps, TabVariant } from './TabProps';
|
|
2
|
+
export type { default as TabProps, TabVariant, TabIndicatorSize } from './TabProps';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type TabsProps from './TabsProps';
|
|
3
3
|
import type { TabsInstance } from './types';
|
|
4
|
-
declare const Tabs: React.ForwardRefExoticComponent<Pick<TabsProps, "testID" | "style" | "onLayout" | "keyboardDismissMode" | "children" | "pointerEvents" | "onStartShouldSetResponder" | "onMoveShouldSetResponder" | "onResponderEnd" | "onResponderGrant" | "onResponderReject" | "onResponderMove" | "onResponderRelease" | "onResponderStart" | "onResponderTerminationRequest" | "onResponderTerminate" | "onStartShouldSetResponderCapture" | "onMoveShouldSetResponderCapture" | "accessibilityLabel" | "accessible" | "hitSlop" | "removeClippedSubviews" | "nativeID" | "collapsable" | "needsOffscreenAlphaCompositing" | "renderToHardwareTextureAndroid" | "focusable" | "shouldRasterizeIOS" | "isTVSelectable" | "hasTVPreferredFocus" | "tvParallaxProperties" | "tvParallaxShiftDistanceX" | "tvParallaxShiftDistanceY" | "tvParallaxTiltAngle" | "tvParallaxMagnification" | "onTouchStart" | "onTouchMove" | "onTouchEnd" | "onTouchCancel" | "onTouchEndCapture" | "accessibilityActions" | "accessibilityRole" | "accessibilityState" | "accessibilityHint" | "accessibilityValue" | "onAccessibilityAction" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityViewIsModal" | "onAccessibilityEscape" | "onAccessibilityTap" | "onMagicTap" | "accessibilityIgnoresInvertColors" | "variant" | "keyboardShouldPersistTaps" | "onChange" | "indicatorColor" | "initialIndex" | "scrollable" | "disableIndicator" | "UNSTABLE_sharedIndex"> & React.RefAttributes<TabsInstance>>;
|
|
4
|
+
declare const Tabs: React.ForwardRefExoticComponent<Pick<TabsProps, "testID" | "style" | "onLayout" | "keyboardDismissMode" | "children" | "pointerEvents" | "onStartShouldSetResponder" | "onMoveShouldSetResponder" | "onResponderEnd" | "onResponderGrant" | "onResponderReject" | "onResponderMove" | "onResponderRelease" | "onResponderStart" | "onResponderTerminationRequest" | "onResponderTerminate" | "onStartShouldSetResponderCapture" | "onMoveShouldSetResponderCapture" | "accessibilityLabel" | "accessible" | "hitSlop" | "removeClippedSubviews" | "nativeID" | "collapsable" | "needsOffscreenAlphaCompositing" | "renderToHardwareTextureAndroid" | "focusable" | "shouldRasterizeIOS" | "isTVSelectable" | "hasTVPreferredFocus" | "tvParallaxProperties" | "tvParallaxShiftDistanceX" | "tvParallaxShiftDistanceY" | "tvParallaxTiltAngle" | "tvParallaxMagnification" | "onTouchStart" | "onTouchMove" | "onTouchEnd" | "onTouchCancel" | "onTouchEndCapture" | "accessibilityActions" | "accessibilityRole" | "accessibilityState" | "accessibilityHint" | "accessibilityValue" | "onAccessibilityAction" | "accessibilityLabelledBy" | "accessibilityLiveRegion" | "importantForAccessibility" | "accessibilityElementsHidden" | "accessibilityLanguage" | "accessibilityViewIsModal" | "onAccessibilityEscape" | "onAccessibilityTap" | "onMagicTap" | "accessibilityIgnoresInvertColors" | "variant" | "keyboardShouldPersistTaps" | "onChange" | "indicatorColor" | "indicatorSize" | "initialIndex" | "scrollable" | "disableIndicator" | "UNSTABLE_sharedIndex" | "onTabSelected"> & React.RefAttributes<TabsInstance>>;
|
|
5
5
|
export default Tabs;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactNode, Ref } from 'react';
|
|
2
2
|
import type { ViewProps } from 'react-native';
|
|
3
3
|
import type { TabIndicatorColor } from './TabIndicatorProps';
|
|
4
|
-
import type { TabVariant } from '../Tab';
|
|
4
|
+
import type { TabVariant, TabIndicatorSize } from '../Tab';
|
|
5
5
|
import type { OverridableComponentProps, SyncAnimatedValue } from '../types';
|
|
6
6
|
import type { KeyboardDismissMode, KeyboardShouldPersistTaps, TabsInstance } from './types';
|
|
7
7
|
export default interface TabsProps extends OverridableComponentProps<ViewProps, {
|
|
@@ -20,6 +20,13 @@ export default interface TabsProps extends OverridableComponentProps<ViewProps,
|
|
|
20
20
|
* @default 'primary'
|
|
21
21
|
*/
|
|
22
22
|
indicatorColor?: TabIndicatorColor;
|
|
23
|
+
/**
|
|
24
|
+
* The size of tab indicator.
|
|
25
|
+
* 'full' adjusts the indicator to the size of the Tab,
|
|
26
|
+
* while 'fit-content' adjusts the indicator to the size of the content inside the Tab.
|
|
27
|
+
* @default 'full'
|
|
28
|
+
*/
|
|
29
|
+
indicatorSize?: TabIndicatorSize;
|
|
23
30
|
/**
|
|
24
31
|
* Index of initial tab that should be selected.
|
|
25
32
|
* @default 0
|
|
@@ -53,5 +60,11 @@ export default interface TabsProps extends OverridableComponentProps<ViewProps,
|
|
|
53
60
|
* @default 'primary'
|
|
54
61
|
*/
|
|
55
62
|
variant?: TabVariant;
|
|
63
|
+
/**
|
|
64
|
+
* Callback function executed when a Tab is selected.
|
|
65
|
+
* Executed even if the index does not change when a Tab is pressed.
|
|
66
|
+
* Receives the next tab index and the current tab index as parameters.
|
|
67
|
+
*/
|
|
68
|
+
onTabSelected?: (newIndex: number, currentIndex: number) => void;
|
|
56
69
|
}> {
|
|
57
70
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fountain-ui/core",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.76",
|
|
4
4
|
"author": "Fountain-UI Team",
|
|
5
5
|
"description": "React components that implement Tappytoon's Fountain Design.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"publishConfig": {
|
|
68
68
|
"access": "public"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "cc48805efae8ceeab7e10b5e39dba30136d3a6e4"
|
|
71
71
|
}
|
package/src/Dialog/Dialog.tsx
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { useWindowDimensions } from 'react-native';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { useWindowDimensions, Animated } from 'react-native';
|
|
3
3
|
import { css, NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';
|
|
4
4
|
import Column from '../Column';
|
|
5
5
|
import Modal from '../Modal';
|
|
6
6
|
import Paper from '../Paper';
|
|
7
|
+
import { useAnimatedValue } from '../hooks';
|
|
8
|
+
import { isNotAndroid12 } from '../utils';
|
|
7
9
|
import { useTheme } from '../styles';
|
|
8
10
|
import type DialogProps from './DialogProps';
|
|
9
11
|
|
|
@@ -20,6 +22,10 @@ type DialogStyles = NamedStylesStringUnion<DialogStyleKeys>;
|
|
|
20
22
|
|
|
21
23
|
const DIALOG_MAX_WIDTH = 328;
|
|
22
24
|
|
|
25
|
+
const fadeInDuration = 300;
|
|
26
|
+
const fadeOutDuration = 100;
|
|
27
|
+
const fadeAnimationDelay = 50;
|
|
28
|
+
|
|
23
29
|
const useStyles: UseStyles<DialogStyles> = function (): DialogStyles {
|
|
24
30
|
const theme = useTheme();
|
|
25
31
|
|
|
@@ -74,16 +80,32 @@ export default function Dialog(props: DialogProps) {
|
|
|
74
80
|
|
|
75
81
|
const styles = useStyles();
|
|
76
82
|
const theme = useTheme();
|
|
83
|
+
const animatedOpacity = useAnimatedValue(fullScreen ? 1 : 0);
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if(!fullScreen){
|
|
87
|
+
Animated.timing(animatedOpacity, {
|
|
88
|
+
toValue: visible ? 1 : 0,
|
|
89
|
+
duration: visible ? fadeInDuration: fadeOutDuration,
|
|
90
|
+
delay: visible ? fadeAnimationDelay : 0,
|
|
91
|
+
useNativeDriver: isNotAndroid12,
|
|
92
|
+
}).start();
|
|
93
|
+
}
|
|
94
|
+
}, [fullScreen, visible]);
|
|
77
95
|
|
|
78
96
|
return (
|
|
79
97
|
<Modal
|
|
80
98
|
animationStyle={fullScreen ? styles.animationFullScreen : styles.animation}
|
|
81
99
|
onClose={onClose}
|
|
100
|
+
exitDuration={fullScreen ? 150 : 300}
|
|
82
101
|
visible={visible}
|
|
83
102
|
style={styles.root}
|
|
84
103
|
{...otherProps}
|
|
85
104
|
>
|
|
86
|
-
<
|
|
105
|
+
<Animated.View
|
|
106
|
+
needsOffscreenAlphaCompositing={true}
|
|
107
|
+
style={{ opacity: animatedOpacity }}
|
|
108
|
+
>
|
|
87
109
|
{topElement ? (
|
|
88
110
|
<Column style={fullScreen ? undefined : styles.topElementSize}>
|
|
89
111
|
<Column style={styles.topElementPosition}>
|
|
@@ -103,7 +125,7 @@ export default function Dialog(props: DialogProps) {
|
|
|
103
125
|
>
|
|
104
126
|
{children}
|
|
105
127
|
</Paper>
|
|
106
|
-
</
|
|
128
|
+
</Animated.View>
|
|
107
129
|
</Modal>
|
|
108
130
|
);
|
|
109
131
|
};
|
package/src/Modal/Modal.tsx
CHANGED
|
@@ -14,6 +14,9 @@ export interface ModalCloseEvent {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
const defaultEnterDuration = 300;
|
|
18
|
+
const defaultExitDuration = 150;
|
|
19
|
+
|
|
17
20
|
export const createModalCloseEvent = (reason: ModalCloseReasonType) => ({
|
|
18
21
|
metadata: {
|
|
19
22
|
reason,
|
|
@@ -26,6 +29,8 @@ export default function Modal(props: ModalProps) {
|
|
|
26
29
|
backdropOpacity = 0.5,
|
|
27
30
|
children,
|
|
28
31
|
disableAnimation = false,
|
|
32
|
+
enterDuration = defaultEnterDuration,
|
|
33
|
+
exitDuration = defaultExitDuration,
|
|
29
34
|
hideBackdrop = false,
|
|
30
35
|
onClose,
|
|
31
36
|
style,
|
|
@@ -68,7 +73,8 @@ export default function Modal(props: ModalProps) {
|
|
|
68
73
|
{!disableAnimation ? (
|
|
69
74
|
<Slide
|
|
70
75
|
appear={visible}
|
|
71
|
-
|
|
76
|
+
enterDuration={enterDuration}
|
|
77
|
+
exitDuration={exitDuration}
|
|
72
78
|
onEnter={() => setExited(false)}
|
|
73
79
|
onExited={() => setExited(true)}
|
|
74
80
|
style={animationStyle}
|
package/src/Modal/ModalProps.ts
CHANGED
|
@@ -26,6 +26,18 @@ export default interface ModalProps extends OverridableComponentProps<ViewProps,
|
|
|
26
26
|
*/
|
|
27
27
|
disableAnimation?: boolean;
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* The number of milliseconds to enter animation.
|
|
31
|
+
* @default 300
|
|
32
|
+
*/
|
|
33
|
+
enterDuration?: number;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The number of milliseconds to exit animation.
|
|
37
|
+
* @default 150
|
|
38
|
+
*/
|
|
39
|
+
exitDuration?: number;
|
|
40
|
+
|
|
29
41
|
/**
|
|
30
42
|
* If `true`, the backdrop is not rendered.
|
|
31
43
|
* @default false
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { Animated } from 'react-native';
|
|
3
3
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
4
|
-
import {
|
|
4
|
+
import { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';
|
|
5
5
|
import Slide from '../Slide';
|
|
6
6
|
import SnackbarContent from '../SnackbarContent';
|
|
7
|
+
import { useAnimatedValue } from '../hooks';
|
|
7
8
|
import { useTheme } from '../styles';
|
|
9
|
+
import { isNotAndroid12 } from '../utils';
|
|
8
10
|
import type SnackbarProps from './SnackbarProps';
|
|
9
11
|
|
|
10
12
|
type SnackBarStyles = NamedStylesStringUnion<'root'>;
|
|
11
13
|
|
|
14
|
+
const fadeInDuration = 300;
|
|
15
|
+
const fadeOutDuration = 110;
|
|
16
|
+
const fadeAnimationDelay = 100;
|
|
17
|
+
|
|
12
18
|
const useStyles: UseStyles<SnackBarStyles> = function (): SnackBarStyles {
|
|
13
19
|
const theme = useTheme();
|
|
14
20
|
const insets = useSafeAreaInsets();
|
|
@@ -75,16 +81,32 @@ export default function Snackbar(props: SnackbarProps) {
|
|
|
75
81
|
|
|
76
82
|
const [exited, setExited] = React.useState(true);
|
|
77
83
|
|
|
84
|
+
const animatedOpacity = useAnimatedValue(0);
|
|
85
|
+
|
|
86
|
+
const animatedStyle = {
|
|
87
|
+
opacity: animatedOpacity,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
Animated.timing(animatedOpacity, {
|
|
92
|
+
toValue: visible ? 1 : 0,
|
|
93
|
+
duration: visible ? fadeInDuration : fadeOutDuration,
|
|
94
|
+
delay: visible ? fadeAnimationDelay : 0,
|
|
95
|
+
useNativeDriver: isNotAndroid12,
|
|
96
|
+
}).start();
|
|
97
|
+
}, [visible]);
|
|
98
|
+
|
|
78
99
|
if (!visible && exited) {
|
|
79
100
|
return null;
|
|
80
101
|
}
|
|
81
102
|
|
|
82
103
|
return (
|
|
83
|
-
<View
|
|
84
|
-
style={
|
|
104
|
+
<Animated.View
|
|
105
|
+
style={[
|
|
106
|
+
animatedStyle,
|
|
85
107
|
styles.root,
|
|
86
108
|
style,
|
|
87
|
-
]
|
|
109
|
+
]}
|
|
88
110
|
{...otherProps}
|
|
89
111
|
>
|
|
90
112
|
<Slide
|
|
@@ -103,6 +125,6 @@ export default function Snackbar(props: SnackbarProps) {
|
|
|
103
125
|
/>
|
|
104
126
|
)}
|
|
105
127
|
</Slide>
|
|
106
|
-
</View>
|
|
128
|
+
</Animated.View>
|
|
107
129
|
);
|
|
108
130
|
};
|
package/src/Tab/Tab.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { cloneElement } from 'react';
|
|
2
|
-
import { Platform, Text } from 'react-native';
|
|
2
|
+
import { Platform, Text, View } from 'react-native';
|
|
3
3
|
import Badge from '../Badge';
|
|
4
4
|
import TabBase from '../TabBase';
|
|
5
5
|
import type TabProps from './TabProps';
|
|
@@ -10,16 +10,14 @@ const styles = StyleSheet.create({
|
|
|
10
10
|
root: {
|
|
11
11
|
// TODO: Remove redundant platform checking
|
|
12
12
|
...(Platform.OS === 'web' ? { minWidth: 'auto' } : {}),
|
|
13
|
-
},
|
|
14
|
-
primary: {
|
|
15
|
-
minHeight: 40,
|
|
16
|
-
},
|
|
17
|
-
secondary: {
|
|
18
13
|
minHeight: 40,
|
|
19
14
|
},
|
|
20
15
|
bottomNavigation: {
|
|
21
16
|
minHeight: 56,
|
|
22
17
|
},
|
|
18
|
+
filledInner: {
|
|
19
|
+
justifyContent: 'center',
|
|
20
|
+
}
|
|
23
21
|
});
|
|
24
22
|
|
|
25
23
|
export default function Tab(props: TabProps) {
|
|
@@ -29,10 +27,12 @@ export default function Tab(props: TabProps) {
|
|
|
29
27
|
enableIndicator = false,
|
|
30
28
|
icon: defaultIcon,
|
|
31
29
|
indicatorColor = 'primary',
|
|
30
|
+
indicatorSize = 'full',
|
|
32
31
|
selected = false,
|
|
33
32
|
selectedIcon,
|
|
34
33
|
variant = 'primary',
|
|
35
34
|
style,
|
|
35
|
+
onTabInnerLayout,
|
|
36
36
|
...otherProps
|
|
37
37
|
} = props;
|
|
38
38
|
|
|
@@ -44,12 +44,15 @@ export default function Tab(props: TabProps) {
|
|
|
44
44
|
|
|
45
45
|
const tabBaseStyle = css([
|
|
46
46
|
styles.root,
|
|
47
|
-
variant === '
|
|
48
|
-
? styles.primary
|
|
49
|
-
: (variant === 'secondary' ? styles.secondary : styles.bottomNavigation),
|
|
47
|
+
variant === 'bottom-navigation' && styles.bottomNavigation,
|
|
50
48
|
style,
|
|
51
49
|
]);
|
|
52
50
|
|
|
51
|
+
const tabInnerStyle = css([
|
|
52
|
+
styles.root,
|
|
53
|
+
styles.filledInner,
|
|
54
|
+
]);
|
|
55
|
+
|
|
53
56
|
const fontStyle = createFontStyle(theme, {
|
|
54
57
|
selector: (typo) => variant === 'primary'
|
|
55
58
|
? typo.h2
|
|
@@ -62,13 +65,12 @@ export default function Tab(props: TabProps) {
|
|
|
62
65
|
const icon = selected ? (selectedIcon || defaultIcon) : defaultIcon;
|
|
63
66
|
const iconElement = icon ? cloneElement(icon, { fill: color }) : null;
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
>
|
|
68
|
+
const tabElement = typeof children !== 'string' ? (
|
|
69
|
+
React.cloneElement(children, {
|
|
70
|
+
selected,
|
|
71
|
+
})
|
|
72
|
+
) : (
|
|
73
|
+
<React.Fragment>
|
|
72
74
|
<Badge
|
|
73
75
|
children={iconElement}
|
|
74
76
|
invisible={!badgeVisible}
|
|
@@ -78,8 +80,32 @@ export default function Tab(props: TabProps) {
|
|
|
78
80
|
children={children}
|
|
79
81
|
style={css(fontStyle)}
|
|
80
82
|
/>
|
|
83
|
+
</React.Fragment>
|
|
84
|
+
);
|
|
85
|
+
const tabIndicator = (enableIndicator && selected)
|
|
86
|
+
? <TabIndicator indicatorSize={indicatorSize} color={indicatorColor}/>
|
|
87
|
+
: null;
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<TabBase
|
|
91
|
+
pressEffect={pressEffect}
|
|
92
|
+
style={tabBaseStyle}
|
|
93
|
+
vertical={vertical}
|
|
94
|
+
{...otherProps}
|
|
95
|
+
>
|
|
96
|
+
{indicatorSize === 'fit-content' ? (
|
|
97
|
+
<View onLayout={onTabInnerLayout} style={tabInnerStyle}>
|
|
98
|
+
{tabElement}
|
|
99
|
+
|
|
100
|
+
{tabIndicator}
|
|
101
|
+
</View>
|
|
102
|
+
) : (
|
|
103
|
+
<React.Fragment>
|
|
104
|
+
{tabElement}
|
|
81
105
|
|
|
82
|
-
|
|
106
|
+
{tabIndicator}
|
|
107
|
+
</React.Fragment>
|
|
108
|
+
)}
|
|
83
109
|
</TabBase>
|
|
84
110
|
);
|
|
85
111
|
};
|
package/src/Tab/TabIndicator.tsx
CHANGED
|
@@ -2,12 +2,12 @@ import React from 'react';
|
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import { NamedStylesStringUnion } from '@fountain-ui/styles';
|
|
4
4
|
import { useTheme } from '../styles';
|
|
5
|
-
import type { TabIndicatorColor } from './TabProps';
|
|
5
|
+
import type { TabIndicatorColor, TabIndicatorSize } from './TabProps';
|
|
6
6
|
|
|
7
7
|
type TabIndicatorStyles = NamedStylesStringUnion<'root'>;
|
|
8
8
|
|
|
9
|
-
const useStyles: (color: TabIndicatorColor) => TabIndicatorStyles =
|
|
10
|
-
function (color: TabIndicatorColor): TabIndicatorStyles {
|
|
9
|
+
const useStyles: (color: TabIndicatorColor, indicatorSize: TabIndicatorSize) => TabIndicatorStyles =
|
|
10
|
+
function (color: TabIndicatorColor, indicatorSize): TabIndicatorStyles {
|
|
11
11
|
const theme = useTheme();
|
|
12
12
|
|
|
13
13
|
return {
|
|
@@ -15,15 +15,15 @@ const useStyles: (color: TabIndicatorColor) => TabIndicatorStyles =
|
|
|
15
15
|
backgroundColor: theme.palette[color].main,
|
|
16
16
|
bottom: 0,
|
|
17
17
|
height: 2,
|
|
18
|
-
left: 12,
|
|
19
|
-
right: 12,
|
|
18
|
+
left: indicatorSize === 'fit-content' ? 0 : 12,
|
|
19
|
+
right: indicatorSize === 'fit-content' ? 0 : 12,
|
|
20
20
|
position: 'absolute',
|
|
21
21
|
},
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
const TabIndicator = function TabIndicator({ color }: { color: TabIndicatorColor }) {
|
|
26
|
-
const styles = useStyles(color);
|
|
25
|
+
const TabIndicator = function TabIndicator({ indicatorSize, color }: { indicatorSize: TabIndicatorSize; color: TabIndicatorColor }) {
|
|
26
|
+
const styles = useStyles(color, indicatorSize);
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
29
|
<View
|
package/src/Tab/TabProps.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { LayoutChangeEvent } from 'react-native';
|
|
2
3
|
import type { TabBaseProps } from '../TabBase';
|
|
3
4
|
import type { OverridableComponentProps } from '../types';
|
|
4
5
|
|
|
5
6
|
export type TabVariant = 'primary' | 'secondary' | 'bottom-navigation';
|
|
6
7
|
export type TabIndicatorColor = 'primary' | 'secondary'
|
|
8
|
+
export type TabIndicatorSize = 'full' | 'fit-content';
|
|
7
9
|
|
|
8
10
|
export default interface TabProps extends OverridableComponentProps<TabBaseProps, {
|
|
9
11
|
/**
|
|
@@ -15,7 +17,7 @@ export default interface TabProps extends OverridableComponentProps<TabBaseProps
|
|
|
15
17
|
/**
|
|
16
18
|
* The label of the Tab.
|
|
17
19
|
*/
|
|
18
|
-
children: string;
|
|
20
|
+
children: string | React.ReactElement;
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* If `true`, the indicator is enabled.
|
|
@@ -40,6 +42,14 @@ export default interface TabProps extends OverridableComponentProps<TabBaseProps
|
|
|
40
42
|
*/
|
|
41
43
|
indicatorColor?: TabIndicatorColor;
|
|
42
44
|
|
|
45
|
+
/**
|
|
46
|
+
* The size of tab indicator.
|
|
47
|
+
* 'full' adjusts the indicator to the size of the Tab,
|
|
48
|
+
* while 'fit-content' adjusts the indicator to the size of the content inside the Tab.
|
|
49
|
+
* @default 'full'
|
|
50
|
+
*/
|
|
51
|
+
indicatorSize?: TabIndicatorSize;
|
|
52
|
+
|
|
43
53
|
/**
|
|
44
54
|
* If supplied, use this icon on selected state.
|
|
45
55
|
*/
|
|
@@ -50,4 +60,9 @@ export default interface TabProps extends OverridableComponentProps<TabBaseProps
|
|
|
50
60
|
* @default 'primary'
|
|
51
61
|
*/
|
|
52
62
|
variant?: TabVariant;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Function to be passed to the child component's onLayout prop.
|
|
66
|
+
*/
|
|
67
|
+
onTabInnerLayout?: (event: LayoutChangeEvent) => void;
|
|
53
68
|
}> {}
|
package/src/Tab/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default } from './Tab';
|
|
2
|
-
export type { default as TabProps, TabVariant } from './TabProps';
|
|
2
|
+
export type { default as TabProps, TabVariant, TabIndicatorSize } from './TabProps';
|
package/src/Tabs/Tabs.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { cloneElement, forwardRef, useEffect, useImperativeHandle } from 'react';
|
|
1
|
+
import React, { cloneElement, forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react';
|
|
2
2
|
import type { GestureResponderEvent, LayoutChangeEvent } from 'react-native';
|
|
3
3
|
import { View } from 'react-native';
|
|
4
4
|
import { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';
|
|
@@ -45,6 +45,7 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
|
|
|
45
45
|
indicatorColor = 'primary',
|
|
46
46
|
initialIndex = 0,
|
|
47
47
|
disableIndicator = false,
|
|
48
|
+
indicatorSize = 'full',
|
|
48
49
|
keyboardDismissMode = 'none',
|
|
49
50
|
keyboardShouldPersistTaps = 'never',
|
|
50
51
|
onChange,
|
|
@@ -52,13 +53,19 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
|
|
|
52
53
|
style,
|
|
53
54
|
variant = 'primary',
|
|
54
55
|
UNSTABLE_sharedIndex,
|
|
56
|
+
onTabSelected,
|
|
55
57
|
} = props;
|
|
56
58
|
|
|
57
59
|
const fallbackSharedIndex = useSyncAnimatedValue({ initialValue: initialIndex });
|
|
58
60
|
const sharedIndex = UNSTABLE_sharedIndex ?? fallbackSharedIndex;
|
|
59
61
|
const realInitialIndex = sharedIndex.initialValue;
|
|
60
62
|
|
|
63
|
+
const currentIndexRef = useRef(initialIndex);
|
|
64
|
+
|
|
61
65
|
const setTab = (newIndex: number) => {
|
|
66
|
+
const currentIndex = currentIndexRef.current;
|
|
67
|
+
onTabSelected?.(newIndex, currentIndex);
|
|
68
|
+
|
|
62
69
|
sharedIndex.animatedValue.setValue(newIndex);
|
|
63
70
|
};
|
|
64
71
|
|
|
@@ -72,19 +79,48 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
|
|
|
72
79
|
|
|
73
80
|
const styles = useStyles();
|
|
74
81
|
|
|
75
|
-
const [
|
|
82
|
+
const [outerCoordinates, updateCoordinate] = useTabCoordinates(children);
|
|
83
|
+
const [innerCoordinates, updateInnerCoordinate] = useTabCoordinates(children);
|
|
76
84
|
|
|
77
|
-
const canRenderIndicator =
|
|
85
|
+
const canRenderIndicator = indicatorSize === 'fit-content'
|
|
86
|
+
? isEveryTabCoordinatesDefined(innerCoordinates, children)
|
|
87
|
+
: isEveryTabCoordinatesDefined(outerCoordinates, children);
|
|
78
88
|
|
|
79
89
|
const indexStore = useIndexStore(sharedIndex);
|
|
80
90
|
|
|
91
|
+
const coordinates = useMemo(() => {
|
|
92
|
+
if (indicatorSize !== 'fit-content') {
|
|
93
|
+
return outerCoordinates;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return innerCoordinates.map((coordinate, idx) => {
|
|
97
|
+
const { x1, x2 } = coordinate;
|
|
98
|
+
const { x1: outerX1 } = outerCoordinates[idx];
|
|
99
|
+
return {
|
|
100
|
+
x1: x1 + outerX1,
|
|
101
|
+
x2: x2 + outerX1,
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}, [outerCoordinates, innerCoordinates, variant]);
|
|
105
|
+
|
|
81
106
|
useEffect(() => {
|
|
82
107
|
return indexStore.subscribe(newIndex => {
|
|
83
108
|
onChange?.(newIndex);
|
|
109
|
+
currentIndexRef.current = newIndex;
|
|
84
110
|
});
|
|
85
111
|
}, [indexStore, onChange]);
|
|
86
112
|
|
|
87
113
|
const tabElements = React.Children.map(children, (child, index) => {
|
|
114
|
+
if (!child) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const onTabInnerLayout = (event: LayoutChangeEvent) => {
|
|
119
|
+
const { x, width } = event.nativeEvent.layout;
|
|
120
|
+
|
|
121
|
+
updateInnerCoordinate(index, x, width);
|
|
122
|
+
};
|
|
123
|
+
|
|
88
124
|
const onLayout = (event: LayoutChangeEvent) => {
|
|
89
125
|
const { x, width } = event.nativeEvent.layout;
|
|
90
126
|
|
|
@@ -111,10 +147,12 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
|
|
|
111
147
|
const tabElement = cloneElement(child, {
|
|
112
148
|
enableIndicator: !disableIndicator && !canRenderIndicator,
|
|
113
149
|
indicatorColor,
|
|
150
|
+
onTabInnerLayout,
|
|
114
151
|
onLayout,
|
|
115
152
|
onPress,
|
|
116
153
|
onMouseDown,
|
|
117
154
|
variant,
|
|
155
|
+
indicatorSize,
|
|
118
156
|
style: scrollable ? undefined : styles.fixedTab,
|
|
119
157
|
});
|
|
120
158
|
|
|
@@ -125,7 +163,7 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
|
|
|
125
163
|
initialIndex={realInitialIndex}
|
|
126
164
|
/>
|
|
127
165
|
);
|
|
128
|
-
});
|
|
166
|
+
})?.filter(Boolean);
|
|
129
167
|
|
|
130
168
|
const tabIndicator = canRenderIndicator ? (
|
|
131
169
|
<TabIndicator
|