@fountain-ui/core 2.0.0-beta.14 → 2.0.0-beta.15

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 (38) hide show
  1. package/build/commonjs/ButtonBase/ButtonBase.js +3 -2
  2. package/build/commonjs/ButtonBase/ButtonBase.js.map +1 -1
  3. package/build/commonjs/CircularProgress/CircularProgress.js +15 -11
  4. package/build/commonjs/CircularProgress/CircularProgress.js.map +1 -1
  5. package/build/commonjs/Tabs/TabIndicator.js +16 -8
  6. package/build/commonjs/Tabs/TabIndicator.js.map +1 -1
  7. package/build/commonjs/Tabs/Tabs.js +7 -6
  8. package/build/commonjs/Tabs/Tabs.js.map +1 -1
  9. package/build/commonjs/Tabs/TabsProps.js.map +1 -1
  10. package/build/commonjs/Tabs/useScrollViewReaction.js +23 -12
  11. package/build/commonjs/Tabs/useScrollViewReaction.js.map +1 -1
  12. package/build/commonjs/hooks/useValidWindowDimensions/index.ios.js +14 -7
  13. package/build/commonjs/hooks/useValidWindowDimensions/index.ios.js.map +1 -1
  14. package/build/module/ButtonBase/ButtonBase.js +3 -2
  15. package/build/module/ButtonBase/ButtonBase.js.map +1 -1
  16. package/build/module/CircularProgress/CircularProgress.js +15 -11
  17. package/build/module/CircularProgress/CircularProgress.js.map +1 -1
  18. package/build/module/Tabs/TabIndicator.js +15 -8
  19. package/build/module/Tabs/TabIndicator.js.map +1 -1
  20. package/build/module/Tabs/Tabs.js +7 -5
  21. package/build/module/Tabs/Tabs.js.map +1 -1
  22. package/build/module/Tabs/TabsProps.js.map +1 -1
  23. package/build/module/Tabs/useScrollViewReaction.js +23 -13
  24. package/build/module/Tabs/useScrollViewReaction.js.map +1 -1
  25. package/build/module/hooks/useValidWindowDimensions/index.ios.js +15 -9
  26. package/build/module/hooks/useValidWindowDimensions/index.ios.js.map +1 -1
  27. package/build/typescript/CircularProgress/CircularProgress.d.ts +1 -1
  28. package/build/typescript/Tabs/Tabs.d.ts +1 -1
  29. package/build/typescript/Tabs/TabsProps.d.ts +5 -0
  30. package/build/typescript/hooks/useValidWindowDimensions/index.ios.d.ts +2 -1
  31. package/package.json +2 -2
  32. package/src/ButtonBase/ButtonBase.tsx +6 -2
  33. package/src/CircularProgress/CircularProgress.tsx +23 -15
  34. package/src/Tabs/TabIndicator.tsx +16 -8
  35. package/src/Tabs/Tabs.tsx +7 -4
  36. package/src/Tabs/TabsProps.ts +6 -0
  37. package/src/Tabs/useScrollViewReaction.ts +21 -13
  38. package/src/hooks/useValidWindowDimensions/index.ios.ts +16 -10
@@ -4,14 +4,19 @@ import React from 'react';
4
4
  import Animated, { Easing, useAnimatedStyle, withTiming } from 'react-native-reanimated';
5
5
  import { useTheme } from '../styles';
6
6
  import { defaultCoordinate } from './TabCoordinate';
7
+ const INDICATOR_WIDTH = 10;
8
+ const INDICATOR_HEIGHT = 4;
9
+ const SCROLLABLE_TABS_INSET = 12 * 2;
7
10
 
8
11
  const useStyles = function () {
9
12
  const theme = useTheme();
10
13
  return {
11
14
  root: {
12
15
  backgroundColor: theme.palette.secondary.main,
16
+ left: 0,
13
17
  bottom: 0,
14
- height: 4,
18
+ width: INDICATOR_WIDTH,
19
+ height: INDICATOR_HEIGHT,
15
20
  position: 'absolute'
16
21
  },
17
22
  disabled: {
@@ -21,7 +26,7 @@ const useStyles = function () {
21
26
  };
22
27
 
23
28
  const ANIMATION_CONFIG = {
24
- duration: 200,
29
+ duration: 300,
25
30
  easing: Easing.out(Easing.exp)
26
31
  };
27
32
  export default function TabIndicator(props) {
@@ -41,13 +46,15 @@ export default function TabIndicator(props) {
41
46
  x2
42
47
  } = coordinates[index] ?? defaultCoordinate;
43
48
  const tabWidth = x2 - x1;
44
- const singleInset = scrollable ? 12 : 0;
45
- const totalInset = singleInset * 2;
46
- const nextLeft = x1 + singleInset;
47
- const nextWidth = tabWidth - totalInset;
49
+ const translateX = x1 + (tabWidth - INDICATOR_WIDTH) / 2;
50
+ const inset = scrollable ? SCROLLABLE_TABS_INSET : 0;
51
+ const scaleX = (tabWidth - inset) / INDICATOR_WIDTH;
48
52
  return {
49
- left: withTiming(nextLeft, ANIMATION_CONFIG),
50
- width: withTiming(nextWidth, ANIMATION_CONFIG)
53
+ transform: [{
54
+ translateX: withTiming(translateX, ANIMATION_CONFIG)
55
+ }, {
56
+ scaleX: withTiming(scaleX, ANIMATION_CONFIG)
57
+ }]
51
58
  };
52
59
  }, [coordinates, scrollable]);
53
60
  return /*#__PURE__*/React.createElement(Animated.View, _extends({
@@ -1 +1 @@
1
- {"version":3,"names":["React","Animated","Easing","useAnimatedStyle","withTiming","useTheme","defaultCoordinate","useStyles","theme","root","backgroundColor","palette","secondary","main","bottom","height","position","disabled","ANIMATION_CONFIG","duration","easing","out","exp","TabIndicator","props","coordinates","scrollable","sharedIndex","style","otherProps","styles","animatedStyle","index","value","x1","x2","tabWidth","singleInset","totalInset","nextLeft","nextWidth","left","width","undefined"],"sources":["TabIndicator.tsx"],"sourcesContent":["import React from 'react';\nimport type { WithTimingConfig } from 'react-native-reanimated';\nimport Animated, { Easing, useAnimatedStyle, withTiming } from 'react-native-reanimated';\nimport { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';\nimport { useTheme } from '../styles';\nimport type TabIndicatorProps from './TabIndicatorProps';\nimport { defaultCoordinate } from './TabCoordinate';\n\ntype TabIndicatorStyles = NamedStylesStringUnion<'root' | 'disabled'>;\n\nconst useStyles: UseStyles<TabIndicatorStyles> = function (): TabIndicatorStyles {\n const theme = useTheme();\n\n return {\n root: {\n backgroundColor: theme.palette.secondary.main,\n bottom: 0,\n height: 4,\n position: 'absolute',\n },\n disabled: {\n height: 0,\n },\n };\n};\n\nconst ANIMATION_CONFIG: Readonly<WithTimingConfig> = {\n duration: 200,\n easing: Easing.out(Easing.exp),\n};\n\nexport default function TabIndicator(props: TabIndicatorProps) {\n const {\n coordinates,\n disabled,\n scrollable,\n sharedIndex,\n style,\n ...otherProps\n } = props;\n\n const styles = useStyles();\n\n const animatedStyle = useAnimatedStyle(() => {\n const index = sharedIndex.value;\n\n const { x1, x2 } = coordinates[index] ?? defaultCoordinate;\n\n const tabWidth = x2 - x1;\n\n const singleInset = scrollable ? 12 : 0;\n const totalInset = singleInset * 2;\n\n const nextLeft = x1 + singleInset;\n const nextWidth = tabWidth - totalInset;\n\n return {\n left: withTiming(nextLeft, ANIMATION_CONFIG),\n width: withTiming(nextWidth, ANIMATION_CONFIG),\n };\n }, [coordinates, scrollable]);\n\n return (\n <Animated.View\n style={[\n styles.root,\n disabled ? styles.disabled : undefined,\n animatedStyle,\n style,\n ]}\n {...otherProps}\n />\n );\n};\n"],"mappings":";;AAAA,OAAOA,KAAP,MAAkB,OAAlB;AAEA,OAAOC,QAAP,IAAmBC,MAAnB,EAA2BC,gBAA3B,EAA6CC,UAA7C,QAA+D,yBAA/D;AAEA,SAASC,QAAT,QAAyB,WAAzB;AAEA,SAASC,iBAAT,QAAkC,iBAAlC;;AAIA,MAAMC,SAAwC,GAAG,YAAgC;EAC7E,MAAMC,KAAK,GAAGH,QAAQ,EAAtB;EAEA,OAAO;IACHI,IAAI,EAAE;MACFC,eAAe,EAAEF,KAAK,CAACG,OAAN,CAAcC,SAAd,CAAwBC,IADvC;MAEFC,MAAM,EAAE,CAFN;MAGFC,MAAM,EAAE,CAHN;MAIFC,QAAQ,EAAE;IAJR,CADH;IAOHC,QAAQ,EAAE;MACNF,MAAM,EAAE;IADF;EAPP,CAAP;AAWH,CAdD;;AAgBA,MAAMG,gBAA4C,GAAG;EACjDC,QAAQ,EAAE,GADuC;EAEjDC,MAAM,EAAElB,MAAM,CAACmB,GAAP,CAAWnB,MAAM,CAACoB,GAAlB;AAFyC,CAArD;AAKA,eAAe,SAASC,YAAT,CAAsBC,KAAtB,EAAgD;EAC3D,MAAM;IACFC,WADE;IAEFR,QAFE;IAGFS,UAHE;IAIFC,WAJE;IAKFC,KALE;IAMF,GAAGC;EAND,IAOFL,KAPJ;EASA,MAAMM,MAAM,GAAGvB,SAAS,EAAxB;EAEA,MAAMwB,aAAa,GAAG5B,gBAAgB,CAAC,MAAM;IACzC,MAAM6B,KAAK,GAAGL,WAAW,CAACM,KAA1B;IAEA,MAAM;MAAEC,EAAF;MAAMC;IAAN,IAAaV,WAAW,CAACO,KAAD,CAAX,IAAsB1B,iBAAzC;IAEA,MAAM8B,QAAQ,GAAGD,EAAE,GAAGD,EAAtB;IAEA,MAAMG,WAAW,GAAGX,UAAU,GAAG,EAAH,GAAQ,CAAtC;IACA,MAAMY,UAAU,GAAGD,WAAW,GAAG,CAAjC;IAEA,MAAME,QAAQ,GAAGL,EAAE,GAAGG,WAAtB;IACA,MAAMG,SAAS,GAAGJ,QAAQ,GAAGE,UAA7B;IAEA,OAAO;MACHG,IAAI,EAAErC,UAAU,CAACmC,QAAD,EAAWrB,gBAAX,CADb;MAEHwB,KAAK,EAAEtC,UAAU,CAACoC,SAAD,EAAYtB,gBAAZ;IAFd,CAAP;EAIH,CAjBqC,EAiBnC,CAACO,WAAD,EAAcC,UAAd,CAjBmC,CAAtC;EAmBA,oBACI,oBAAC,QAAD,CAAU,IAAV;IACI,KAAK,EAAE,CACHI,MAAM,CAACrB,IADJ,EAEHQ,QAAQ,GAAGa,MAAM,CAACb,QAAV,GAAqB0B,SAF1B,EAGHZ,aAHG,EAIHH,KAJG;EADX,GAOQC,UAPR,EADJ;AAWH;AAAA"}
1
+ {"version":3,"names":["React","Animated","Easing","useAnimatedStyle","withTiming","useTheme","defaultCoordinate","INDICATOR_WIDTH","INDICATOR_HEIGHT","SCROLLABLE_TABS_INSET","useStyles","theme","root","backgroundColor","palette","secondary","main","left","bottom","width","height","position","disabled","ANIMATION_CONFIG","duration","easing","out","exp","TabIndicator","props","coordinates","scrollable","sharedIndex","style","otherProps","styles","animatedStyle","index","value","x1","x2","tabWidth","translateX","inset","scaleX","transform","undefined"],"sources":["TabIndicator.tsx"],"sourcesContent":["import React from 'react';\nimport type { WithTimingConfig } from 'react-native-reanimated';\nimport Animated, { Easing, useAnimatedStyle, withTiming } from 'react-native-reanimated';\nimport { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';\nimport { useTheme } from '../styles';\nimport type TabIndicatorProps from './TabIndicatorProps';\nimport { defaultCoordinate } from './TabCoordinate';\n\ntype TabIndicatorStyles = NamedStylesStringUnion<'root' | 'disabled'>;\n\nconst INDICATOR_WIDTH = 10;\nconst INDICATOR_HEIGHT = 4;\n\nconst SCROLLABLE_TABS_INSET = 12 * 2;\n\nconst useStyles: UseStyles<TabIndicatorStyles> = function (): TabIndicatorStyles {\n const theme = useTheme();\n\n return {\n root: {\n backgroundColor: theme.palette.secondary.main,\n left: 0,\n bottom: 0,\n width: INDICATOR_WIDTH,\n height: INDICATOR_HEIGHT,\n position: 'absolute',\n },\n disabled: {\n height: 0,\n },\n };\n};\n\nconst ANIMATION_CONFIG: Readonly<WithTimingConfig> = {\n duration: 300,\n easing: Easing.out(Easing.exp),\n};\n\nexport default function TabIndicator(props: TabIndicatorProps) {\n const {\n coordinates,\n disabled,\n scrollable,\n sharedIndex,\n style,\n ...otherProps\n } = props;\n\n const styles = useStyles();\n\n const animatedStyle = useAnimatedStyle(() => {\n const index = sharedIndex.value;\n\n const { x1, x2 } = coordinates[index] ?? defaultCoordinate;\n\n const tabWidth = x2 - x1;\n\n const translateX = x1 + (tabWidth - INDICATOR_WIDTH) / 2;\n\n const inset = scrollable ? SCROLLABLE_TABS_INSET : 0;\n const scaleX = (tabWidth - inset) / INDICATOR_WIDTH;\n\n return {\n transform: [\n { translateX: withTiming(translateX, ANIMATION_CONFIG) },\n { scaleX: withTiming(scaleX, ANIMATION_CONFIG) },\n ],\n };\n }, [coordinates, scrollable]);\n\n return (\n <Animated.View\n style={[\n styles.root,\n disabled ? styles.disabled : undefined,\n animatedStyle,\n style,\n ]}\n {...otherProps}\n />\n );\n};\n"],"mappings":";;AAAA,OAAOA,KAAP,MAAkB,OAAlB;AAEA,OAAOC,QAAP,IAAmBC,MAAnB,EAA2BC,gBAA3B,EAA6CC,UAA7C,QAA+D,yBAA/D;AAEA,SAASC,QAAT,QAAyB,WAAzB;AAEA,SAASC,iBAAT,QAAkC,iBAAlC;AAIA,MAAMC,eAAe,GAAG,EAAxB;AACA,MAAMC,gBAAgB,GAAG,CAAzB;AAEA,MAAMC,qBAAqB,GAAG,KAAK,CAAnC;;AAEA,MAAMC,SAAwC,GAAG,YAAgC;EAC7E,MAAMC,KAAK,GAAGN,QAAQ,EAAtB;EAEA,OAAO;IACHO,IAAI,EAAE;MACFC,eAAe,EAAEF,KAAK,CAACG,OAAN,CAAcC,SAAd,CAAwBC,IADvC;MAEFC,IAAI,EAAE,CAFJ;MAGFC,MAAM,EAAE,CAHN;MAIFC,KAAK,EAAEZ,eAJL;MAKFa,MAAM,EAAEZ,gBALN;MAMFa,QAAQ,EAAE;IANR,CADH;IASHC,QAAQ,EAAE;MACNF,MAAM,EAAE;IADF;EATP,CAAP;AAaH,CAhBD;;AAkBA,MAAMG,gBAA4C,GAAG;EACjDC,QAAQ,EAAE,GADuC;EAEjDC,MAAM,EAAEvB,MAAM,CAACwB,GAAP,CAAWxB,MAAM,CAACyB,GAAlB;AAFyC,CAArD;AAKA,eAAe,SAASC,YAAT,CAAsBC,KAAtB,EAAgD;EAC3D,MAAM;IACFC,WADE;IAEFR,QAFE;IAGFS,UAHE;IAIFC,WAJE;IAKFC,KALE;IAMF,GAAGC;EAND,IAOFL,KAPJ;EASA,MAAMM,MAAM,GAAGzB,SAAS,EAAxB;EAEA,MAAM0B,aAAa,GAAGjC,gBAAgB,CAAC,MAAM;IACzC,MAAMkC,KAAK,GAAGL,WAAW,CAACM,KAA1B;IAEA,MAAM;MAAEC,EAAF;MAAMC;IAAN,IAAaV,WAAW,CAACO,KAAD,CAAX,IAAsB/B,iBAAzC;IAEA,MAAMmC,QAAQ,GAAGD,EAAE,GAAGD,EAAtB;IAEA,MAAMG,UAAU,GAAGH,EAAE,GAAG,CAACE,QAAQ,GAAGlC,eAAZ,IAA+B,CAAvD;IAEA,MAAMoC,KAAK,GAAGZ,UAAU,GAAGtB,qBAAH,GAA2B,CAAnD;IACA,MAAMmC,MAAM,GAAG,CAACH,QAAQ,GAAGE,KAAZ,IAAqBpC,eAApC;IAEA,OAAO;MACHsC,SAAS,EAAE,CACP;QAAEH,UAAU,EAAEtC,UAAU,CAACsC,UAAD,EAAanB,gBAAb;MAAxB,CADO,EAEP;QAAEqB,MAAM,EAAExC,UAAU,CAACwC,MAAD,EAASrB,gBAAT;MAApB,CAFO;IADR,CAAP;EAMH,CAlBqC,EAkBnC,CAACO,WAAD,EAAcC,UAAd,CAlBmC,CAAtC;EAoBA,oBACI,oBAAC,QAAD,CAAU,IAAV;IACI,KAAK,EAAE,CACHI,MAAM,CAACvB,IADJ,EAEHU,QAAQ,GAAGa,MAAM,CAACb,QAAV,GAAqBwB,SAF1B,EAGHV,aAHG,EAIHH,KAJG;EADX,GAOQC,UAPR,EADJ;AAWH;AAAA"}
@@ -33,9 +33,11 @@ const Tabs = /*#__PURE__*/forwardRef(function Tabs(props, ref) {
33
33
  onChange,
34
34
  scrollable = false,
35
35
  style,
36
- variant = 'primary'
36
+ variant = 'primary',
37
+ UNSTABLE_sharedIndex
37
38
  } = props;
38
- const sharedIndex = useSharedValue(initialIndex);
39
+ const fallbackSharedIndex = useSharedValue(initialIndex);
40
+ const sharedIndex = UNSTABLE_sharedIndex ?? fallbackSharedIndex;
39
41
 
40
42
  const getCurrentIndex = () => sharedIndex.value;
41
43
 
@@ -83,7 +85,7 @@ const Tabs = /*#__PURE__*/forwardRef(function Tabs(props, ref) {
83
85
 
84
86
 
85
87
  const tabElement = /*#__PURE__*/cloneElement(child, {
86
- enableIndicator: !disableIndicator && isReadyToRenderIndicator,
88
+ enableIndicator: !disableIndicator && !isReadyToRenderIndicator,
87
89
  onLayout,
88
90
  onPress,
89
91
  onMouseDown,
@@ -96,12 +98,12 @@ const Tabs = /*#__PURE__*/forwardRef(function Tabs(props, ref) {
96
98
  sharedIndex: sharedIndex
97
99
  });
98
100
  });
99
- const tabIndicator = /*#__PURE__*/React.createElement(TabIndicator, {
101
+ const tabIndicator = isReadyToRenderIndicator ? /*#__PURE__*/React.createElement(TabIndicator, {
100
102
  coordinates: coordinates,
101
103
  disabled: disableIndicator,
102
104
  scrollable: scrollable,
103
105
  sharedIndex: sharedIndex
104
- });
106
+ }) : null;
105
107
  return /*#__PURE__*/React.createElement(View, {
106
108
  onLayout: onLayout,
107
109
  style: css([styles.root, scrollable ? undefined : styles.fixedRoot, style])
@@ -1 +1 @@
1
- {"version":3,"names":["React","cloneElement","forwardRef","useImperativeHandle","ScrollView","View","useSharedValue","css","useTheme","TabIndicator","IndexAwareTab","useTabCoordinates","useScrollViewReaction","useStyles","theme","root","fixedRoot","flexDirection","fixedTab","flex","scrollableContainer","paddingHorizontal","spacing","Tabs","props","ref","children","initialIndex","disableIndicator","keyboardDismissMode","keyboardShouldPersistTaps","onChange","scrollable","style","variant","sharedIndex","getCurrentIndex","value","setTab","newIndex","styles","coordinates","updateCoordinate","scrollViewRef","onLayout","isReadyToRenderIndicator","length","tabElements","Children","map","child","index","event","x","width","nativeEvent","layout","onMouseDown","e","preventDefault","onPress","tabElement","enableIndicator","undefined","tabIndicator"],"sources":["Tabs.tsx"],"sourcesContent":["import React, { cloneElement, forwardRef, useImperativeHandle } from 'react';\nimport type { GestureResponderEvent, LayoutChangeEvent } from 'react-native';\nimport { ScrollView, View } from 'react-native';\nimport { useSharedValue } from 'react-native-reanimated';\nimport { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';\nimport { css, useTheme } from '../styles';\nimport type TabsProps from './TabsProps';\nimport type { TabsInstance } from './TabsProps';\nimport TabIndicator from './TabIndicator';\nimport IndexAwareTab from './IndexAwareTab';\nimport useTabCoordinates from './useTabCoordinates';\nimport useScrollViewReaction from './useScrollViewReaction';\n\ntype TabsStyleKeys =\n | 'root'\n | 'fixedRoot'\n | 'fixedTab'\n | 'scrollableContainer';\n\ntype TabsStyles = NamedStylesStringUnion<TabsStyleKeys>;\n\nconst useStyles: UseStyles<TabsStyles> = function (): TabsStyles {\n const theme = useTheme();\n\n return {\n root: {},\n fixedRoot: {\n flexDirection: 'row',\n },\n fixedTab: {\n flex: 1,\n },\n scrollableContainer: {\n paddingHorizontal: theme.spacing(1),\n },\n };\n};\n\nconst Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {\n const {\n children,\n initialIndex = 0,\n disableIndicator = false,\n keyboardDismissMode = 'none',\n keyboardShouldPersistTaps = 'never',\n onChange,\n scrollable = false,\n style,\n variant = 'primary',\n } = props;\n\n const sharedIndex = useSharedValue<number>(initialIndex);\n\n const getCurrentIndex = (): number => sharedIndex.value;\n\n const setTab = (newIndex: number) => {\n sharedIndex.value = newIndex;\n };\n\n useImperativeHandle(\n ref,\n () => ({\n getCurrentIndex,\n setTab,\n }),\n [sharedIndex],\n );\n\n const styles = useStyles();\n\n const { coordinates, updateCoordinate } = useTabCoordinates(children);\n\n const { scrollViewRef, onLayout } = useScrollViewReaction(sharedIndex, coordinates);\n\n const isReadyToRenderIndicator = coordinates.length > 0;\n\n const tabElements = React.Children.map(children, (child, index) => {\n const onLayout = (event: LayoutChangeEvent) => {\n const { x, width } = event.nativeEvent.layout;\n\n updateCoordinate(index, x, width);\n };\n\n const onMouseDown = (e: GestureResponderEvent) => {\n if (keyboardShouldPersistTaps === 'always') {\n e.preventDefault();\n }\n };\n\n const onPress = () => {\n setTab(index);\n\n onChange?.(index);\n // @ts-ignore\n child.props.onPress?.();\n };\n\n // @ts-ignore\n const tabElement = cloneElement(child, {\n enableIndicator: !disableIndicator && isReadyToRenderIndicator,\n onLayout,\n onPress,\n onMouseDown,\n variant,\n style: scrollable ? undefined : styles.fixedTab,\n });\n\n return (\n <IndexAwareTab\n children={tabElement}\n index={index}\n sharedIndex={sharedIndex}\n />\n );\n });\n\n const tabIndicator = (\n <TabIndicator\n coordinates={coordinates}\n disabled={disableIndicator}\n scrollable={scrollable}\n sharedIndex={sharedIndex}\n />\n );\n\n return (\n <View\n onLayout={onLayout}\n style={css([\n styles.root,\n scrollable ? undefined : styles.fixedRoot,\n style,\n ])}\n >\n {scrollable ? (\n <ScrollView\n automaticallyAdjustContentInsets={false}\n bounces={false}\n contentContainerStyle={styles.scrollableContainer}\n directionalLockEnabled={true}\n horizontal={true}\n ref={scrollViewRef}\n scrollsToTop={false}\n showsHorizontalScrollIndicator={false}\n showsVerticalScrollIndicator={false}\n keyboardDismissMode={keyboardDismissMode}\n keyboardShouldPersistTaps={keyboardShouldPersistTaps}\n >\n {tabElements}\n {tabIndicator}\n </ScrollView>\n ) : (\n <React.Fragment>\n {tabElements}\n {tabIndicator}\n </React.Fragment>\n )}\n </View>\n );\n});\n\nexport default Tabs;\n"],"mappings":"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"}
1
+ {"version":3,"names":["React","cloneElement","forwardRef","useImperativeHandle","ScrollView","View","useSharedValue","css","useTheme","TabIndicator","IndexAwareTab","useTabCoordinates","useScrollViewReaction","useStyles","theme","root","fixedRoot","flexDirection","fixedTab","flex","scrollableContainer","paddingHorizontal","spacing","Tabs","props","ref","children","initialIndex","disableIndicator","keyboardDismissMode","keyboardShouldPersistTaps","onChange","scrollable","style","variant","UNSTABLE_sharedIndex","fallbackSharedIndex","sharedIndex","getCurrentIndex","value","setTab","newIndex","styles","coordinates","updateCoordinate","scrollViewRef","onLayout","isReadyToRenderIndicator","length","tabElements","Children","map","child","index","event","x","width","nativeEvent","layout","onMouseDown","e","preventDefault","onPress","tabElement","enableIndicator","undefined","tabIndicator"],"sources":["Tabs.tsx"],"sourcesContent":["import React, { cloneElement, forwardRef, useImperativeHandle } from 'react';\nimport type { GestureResponderEvent, LayoutChangeEvent } from 'react-native';\nimport { ScrollView, View } from 'react-native';\nimport { useSharedValue } from 'react-native-reanimated';\nimport { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';\nimport { css, useTheme } from '../styles';\nimport type TabsProps from './TabsProps';\nimport type { TabsInstance } from './TabsProps';\nimport TabIndicator from './TabIndicator';\nimport IndexAwareTab from './IndexAwareTab';\nimport useTabCoordinates from './useTabCoordinates';\nimport useScrollViewReaction from './useScrollViewReaction';\n\ntype TabsStyleKeys =\n | 'root'\n | 'fixedRoot'\n | 'fixedTab'\n | 'scrollableContainer';\n\ntype TabsStyles = NamedStylesStringUnion<TabsStyleKeys>;\n\nconst useStyles: UseStyles<TabsStyles> = function (): TabsStyles {\n const theme = useTheme();\n\n return {\n root: {},\n fixedRoot: {\n flexDirection: 'row',\n },\n fixedTab: {\n flex: 1,\n },\n scrollableContainer: {\n paddingHorizontal: theme.spacing(1),\n },\n };\n};\n\nconst Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {\n const {\n children,\n initialIndex = 0,\n disableIndicator = false,\n keyboardDismissMode = 'none',\n keyboardShouldPersistTaps = 'never',\n onChange,\n scrollable = false,\n style,\n variant = 'primary',\n UNSTABLE_sharedIndex,\n } = props;\n\n const fallbackSharedIndex = useSharedValue<number>(initialIndex);\n\n const sharedIndex = UNSTABLE_sharedIndex ?? fallbackSharedIndex;\n\n const getCurrentIndex = (): number => sharedIndex.value;\n\n const setTab = (newIndex: number) => {\n sharedIndex.value = newIndex;\n };\n\n useImperativeHandle(\n ref,\n () => ({\n getCurrentIndex,\n setTab,\n }),\n [sharedIndex],\n );\n\n const styles = useStyles();\n\n const { coordinates, updateCoordinate } = useTabCoordinates(children);\n\n const { scrollViewRef, onLayout } = useScrollViewReaction(sharedIndex, coordinates);\n\n const isReadyToRenderIndicator = coordinates.length > 0;\n\n const tabElements = React.Children.map(children, (child, index) => {\n const onLayout = (event: LayoutChangeEvent) => {\n const { x, width } = event.nativeEvent.layout;\n\n updateCoordinate(index, x, width);\n };\n\n const onMouseDown = (e: GestureResponderEvent) => {\n if (keyboardShouldPersistTaps === 'always') {\n e.preventDefault();\n }\n };\n\n const onPress = () => {\n setTab(index);\n\n onChange?.(index);\n // @ts-ignore\n child.props.onPress?.();\n };\n\n // @ts-ignore\n const tabElement = cloneElement(child, {\n enableIndicator: !disableIndicator && !isReadyToRenderIndicator,\n onLayout,\n onPress,\n onMouseDown,\n variant,\n style: scrollable ? undefined : styles.fixedTab,\n });\n\n return (\n <IndexAwareTab\n children={tabElement}\n index={index}\n sharedIndex={sharedIndex}\n />\n );\n });\n\n const tabIndicator = isReadyToRenderIndicator ? (\n <TabIndicator\n coordinates={coordinates}\n disabled={disableIndicator}\n scrollable={scrollable}\n sharedIndex={sharedIndex}\n />\n ) : null;\n\n return (\n <View\n onLayout={onLayout}\n style={css([\n styles.root,\n scrollable ? undefined : styles.fixedRoot,\n style,\n ])}\n >\n {scrollable ? (\n <ScrollView\n automaticallyAdjustContentInsets={false}\n bounces={false}\n contentContainerStyle={styles.scrollableContainer}\n directionalLockEnabled={true}\n horizontal={true}\n ref={scrollViewRef}\n scrollsToTop={false}\n showsHorizontalScrollIndicator={false}\n showsVerticalScrollIndicator={false}\n keyboardDismissMode={keyboardDismissMode}\n keyboardShouldPersistTaps={keyboardShouldPersistTaps}\n >\n {tabElements}\n {tabIndicator}\n </ScrollView>\n ) : (\n <React.Fragment>\n {tabElements}\n {tabIndicator}\n </React.Fragment>\n )}\n </View>\n );\n});\n\nexport default Tabs;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["TabsProps.ts"],"sourcesContent":["import type { ReactNode, Ref } from 'react';\nimport type { ViewProps } from 'react-native';\nimport type { TabVariant } from '../Tab';\nimport type { OverridableComponentProps } from '../types';\n\nexport type KeyboardDismissMode =\n 'none'\n | 'on-drag'\n | 'interactive'; // ios only\n\nexport type KeyboardShouldPersistTaps =\n 'never'\n | boolean\n | 'always'\n | 'handled'; // app only\n\nexport interface TabsInstance {\n /**\n * Get current tab index.\n */\n getCurrentIndex: () => number;\n\n /**\n * Function to scroll to a specific tab. Invalid index is ignored.\n * @param index\n */\n setTab: (index: number) => void;\n}\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 * 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 * 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 { SharedValue } from 'react-native-reanimated';\nimport type { TabVariant } from '../Tab';\nimport type { OverridableComponentProps } from '../types';\n\nexport type KeyboardDismissMode =\n 'none'\n | 'on-drag'\n | 'interactive'; // ios only\n\nexport type KeyboardShouldPersistTaps =\n 'never'\n | boolean\n | 'always'\n | 'handled'; // app only\n\nexport interface TabsInstance {\n /**\n * Get current tab index.\n */\n getCurrentIndex: () => number;\n\n /**\n * Function to scroll to a specific tab. Invalid index is ignored.\n * @param index\n */\n setTab: (index: number) => void;\n}\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 * 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?: SharedValue<number>;\n\n /**\n * The variant to use.\n * @default 'primary'\n */\n variant?: TabVariant;\n}> {}\n"],"mappings":""}
@@ -1,24 +1,29 @@
1
1
  import { useCallback, useRef } from 'react';
2
- import { runOnJS, useAnimatedReaction } from 'react-native-reanimated';
2
+ import { Platform } from 'react-native';
3
+ import { runOnJS, scrollTo, useAnimatedReaction, useAnimatedRef } from 'react-native-reanimated';
3
4
  export default function useScrollViewReaction(sharedIndex, coordinates) {
4
- const scrollViewRef = useRef(null);
5
- const lastScrolledPositionRef = useRef(NaN);
5
+ const scrollViewRef = useAnimatedRef();
6
+ const lastScrolledXRef = useRef(NaN);
6
7
 
7
- const scrollTo = scrollPosition => {
8
- const scrollView = scrollViewRef.current;
8
+ const scrollToX = x => {
9
+ if (Number.isFinite(x)) {
10
+ var _scrollViewRef$curren;
9
11
 
10
- if (scrollView && Number.isFinite(scrollPosition)) {
11
- scrollView.scrollTo({
12
- x: scrollPosition,
12
+ (_scrollViewRef$curren = scrollViewRef.current) === null || _scrollViewRef$curren === void 0 ? void 0 : _scrollViewRef$curren.scrollTo({
13
+ x,
13
14
  y: 0,
14
15
  animated: true
15
16
  });
16
- lastScrolledPositionRef.current = scrollPosition;
17
+ didScrollToX(x);
17
18
  }
18
19
  };
19
20
 
21
+ const didScrollToX = x => {
22
+ lastScrolledXRef.current = x;
23
+ };
24
+
20
25
  const onLayout = useCallback(() => {
21
- scrollTo(lastScrolledPositionRef.current);
26
+ scrollToX(lastScrolledXRef.current);
22
27
  }, []);
23
28
  useAnimatedReaction(() => {
24
29
  const prevIndex = sharedIndex.value - 1;
@@ -30,9 +35,14 @@ export default function useScrollViewReaction(sharedIndex, coordinates) {
30
35
  }
31
36
 
32
37
  return 0;
33
- }, (scrollPosition, prevScrollPosition) => {
34
- if (scrollPosition !== prevScrollPosition) {
35
- runOnJS(scrollTo)(scrollPosition);
38
+ }, (x, prevX) => {
39
+ if (x !== prevX) {
40
+ if (Platform.OS === 'web') {
41
+ runOnJS(scrollToX)(x);
42
+ } else {
43
+ scrollTo(scrollViewRef, x, 0, true);
44
+ runOnJS(didScrollToX)(x);
45
+ }
36
46
  }
37
47
  }, [coordinates]);
38
48
  return {
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useRef","runOnJS","useAnimatedReaction","useScrollViewReaction","sharedIndex","coordinates","scrollViewRef","lastScrolledPositionRef","NaN","scrollTo","scrollPosition","scrollView","current","Number","isFinite","x","y","animated","onLayout","prevIndex","value","prevCoordinate","prevTabWidth","x2","x1","Math","floor","prevScrollPosition"],"sources":["useScrollViewReaction.ts"],"sourcesContent":["import type { MutableRefObject } from 'react';\nimport { useCallback, useRef } from 'react';\nimport type { ScrollView, ViewProps } from 'react-native';\nimport { runOnJS, SharedValue, useAnimatedReaction } from 'react-native-reanimated';\nimport type TabCoordinate from './TabCoordinate';\n\nexport interface UseScrollViewReaction {\n scrollViewRef: MutableRefObject<ScrollView | null>;\n onLayout: ViewProps['onLayout'];\n}\n\nexport default function useScrollViewReaction(\n sharedIndex: SharedValue<number>,\n coordinates: TabCoordinate[],\n): UseScrollViewReaction {\n const scrollViewRef = useRef<ScrollView | null>(null);\n\n const lastScrolledPositionRef = useRef<number>(NaN);\n\n const scrollTo = (scrollPosition: number) => {\n const scrollView = scrollViewRef.current;\n\n if (scrollView && Number.isFinite(scrollPosition)) {\n scrollView.scrollTo({ x: scrollPosition, y: 0, animated: true });\n\n lastScrolledPositionRef.current = scrollPosition;\n }\n };\n\n const onLayout = useCallback(() => {\n scrollTo(lastScrolledPositionRef.current);\n }, []);\n\n useAnimatedReaction(\n () => {\n const prevIndex = sharedIndex.value - 1;\n const prevCoordinate = coordinates[prevIndex];\n\n if (prevCoordinate) {\n const prevTabWidth = prevCoordinate.x2 - prevCoordinate.x1;\n return Math.floor(prevCoordinate.x1 + prevTabWidth / 2);\n }\n\n return 0;\n },\n (scrollPosition, prevScrollPosition) => {\n if (scrollPosition !== prevScrollPosition) {\n runOnJS(scrollTo)(scrollPosition);\n }\n },\n [coordinates],\n );\n\n return { scrollViewRef, onLayout };\n};\n"],"mappings":"AACA,SAASA,WAAT,EAAsBC,MAAtB,QAAoC,OAApC;AAEA,SAASC,OAAT,EAA+BC,mBAA/B,QAA0D,yBAA1D;AAQA,eAAe,SAASC,qBAAT,CACXC,WADW,EAEXC,WAFW,EAGU;EACrB,MAAMC,aAAa,GAAGN,MAAM,CAAoB,IAApB,CAA5B;EAEA,MAAMO,uBAAuB,GAAGP,MAAM,CAASQ,GAAT,CAAtC;;EAEA,MAAMC,QAAQ,GAAIC,cAAD,IAA4B;IACzC,MAAMC,UAAU,GAAGL,aAAa,CAACM,OAAjC;;IAEA,IAAID,UAAU,IAAIE,MAAM,CAACC,QAAP,CAAgBJ,cAAhB,CAAlB,EAAmD;MAC/CC,UAAU,CAACF,QAAX,CAAoB;QAAEM,CAAC,EAAEL,cAAL;QAAqBM,CAAC,EAAE,CAAxB;QAA2BC,QAAQ,EAAE;MAArC,CAApB;MAEAV,uBAAuB,CAACK,OAAxB,GAAkCF,cAAlC;IACH;EACJ,CARD;;EAUA,MAAMQ,QAAQ,GAAGnB,WAAW,CAAC,MAAM;IAC/BU,QAAQ,CAACF,uBAAuB,CAACK,OAAzB,CAAR;EACH,CAF2B,EAEzB,EAFyB,CAA5B;EAIAV,mBAAmB,CACf,MAAM;IACF,MAAMiB,SAAS,GAAGf,WAAW,CAACgB,KAAZ,GAAoB,CAAtC;IACA,MAAMC,cAAc,GAAGhB,WAAW,CAACc,SAAD,CAAlC;;IAEA,IAAIE,cAAJ,EAAoB;MAChB,MAAMC,YAAY,GAAGD,cAAc,CAACE,EAAf,GAAoBF,cAAc,CAACG,EAAxD;MACA,OAAOC,IAAI,CAACC,KAAL,CAAWL,cAAc,CAACG,EAAf,GAAoBF,YAAY,GAAG,CAA9C,CAAP;IACH;;IAED,OAAO,CAAP;EACH,CAXc,EAYf,CAACZ,cAAD,EAAiBiB,kBAAjB,KAAwC;IACpC,IAAIjB,cAAc,KAAKiB,kBAAvB,EAA2C;MACvC1B,OAAO,CAACQ,QAAD,CAAP,CAAkBC,cAAlB;IACH;EACJ,CAhBc,EAiBf,CAACL,WAAD,CAjBe,CAAnB;EAoBA,OAAO;IAAEC,aAAF;IAAiBY;EAAjB,CAAP;AACH;AAAA"}
1
+ {"version":3,"names":["useCallback","useRef","Platform","runOnJS","scrollTo","useAnimatedReaction","useAnimatedRef","useScrollViewReaction","sharedIndex","coordinates","scrollViewRef","lastScrolledXRef","NaN","scrollToX","x","Number","isFinite","current","y","animated","didScrollToX","onLayout","prevIndex","value","prevCoordinate","prevTabWidth","x2","x1","Math","floor","prevX","OS"],"sources":["useScrollViewReaction.ts"],"sourcesContent":["import type { MutableRefObject } from 'react';\nimport { useCallback, useRef } from 'react';\nimport type { ScrollView, ViewProps } from 'react-native';\nimport { Platform } from 'react-native';\nimport { runOnJS, scrollTo, SharedValue, useAnimatedReaction, useAnimatedRef } from 'react-native-reanimated';\nimport type TabCoordinate from './TabCoordinate';\n\nexport interface UseScrollViewReaction {\n scrollViewRef: MutableRefObject<ScrollView | null>;\n onLayout: ViewProps['onLayout'];\n}\n\nexport default function useScrollViewReaction(\n sharedIndex: SharedValue<number>,\n coordinates: TabCoordinate[],\n): UseScrollViewReaction {\n const scrollViewRef = useAnimatedRef<ScrollView>();\n\n const lastScrolledXRef = useRef<number>(NaN);\n\n const scrollToX = (x: number) => {\n if (Number.isFinite(x)) {\n scrollViewRef.current?.scrollTo({ x, y: 0, animated: true });\n\n didScrollToX(x);\n }\n };\n\n const didScrollToX = (x: number) => {\n lastScrolledXRef.current = x;\n };\n\n const onLayout = useCallback(() => {\n scrollToX(lastScrolledXRef.current);\n }, []);\n\n useAnimatedReaction(\n () => {\n const prevIndex = sharedIndex.value - 1;\n const prevCoordinate = coordinates[prevIndex];\n\n if (prevCoordinate) {\n const prevTabWidth = prevCoordinate.x2 - prevCoordinate.x1;\n return Math.floor(prevCoordinate.x1 + prevTabWidth / 2);\n }\n\n return 0;\n },\n (x, prevX) => {\n if (x !== prevX) {\n if (Platform.OS === 'web') {\n runOnJS(scrollToX)(x);\n } else {\n scrollTo(scrollViewRef, x, 0, true);\n runOnJS(didScrollToX)(x);\n }\n }\n },\n [coordinates],\n );\n\n return { scrollViewRef, onLayout };\n};\n"],"mappings":"AACA,SAASA,WAAT,EAAsBC,MAAtB,QAAoC,OAApC;AAEA,SAASC,QAAT,QAAyB,cAAzB;AACA,SAASC,OAAT,EAAkBC,QAAlB,EAAyCC,mBAAzC,EAA8DC,cAA9D,QAAoF,yBAApF;AAQA,eAAe,SAASC,qBAAT,CACXC,WADW,EAEXC,WAFW,EAGU;EACrB,MAAMC,aAAa,GAAGJ,cAAc,EAApC;EAEA,MAAMK,gBAAgB,GAAGV,MAAM,CAASW,GAAT,CAA/B;;EAEA,MAAMC,SAAS,GAAIC,CAAD,IAAe;IAC7B,IAAIC,MAAM,CAACC,QAAP,CAAgBF,CAAhB,CAAJ,EAAwB;MAAA;;MACpB,yBAAAJ,aAAa,CAACO,OAAd,gFAAuBb,QAAvB,CAAgC;QAAEU,CAAF;QAAKI,CAAC,EAAE,CAAR;QAAWC,QAAQ,EAAE;MAArB,CAAhC;MAEAC,YAAY,CAACN,CAAD,CAAZ;IACH;EACJ,CAND;;EAQA,MAAMM,YAAY,GAAIN,CAAD,IAAe;IAChCH,gBAAgB,CAACM,OAAjB,GAA2BH,CAA3B;EACH,CAFD;;EAIA,MAAMO,QAAQ,GAAGrB,WAAW,CAAC,MAAM;IAC/Ba,SAAS,CAACF,gBAAgB,CAACM,OAAlB,CAAT;EACH,CAF2B,EAEzB,EAFyB,CAA5B;EAIAZ,mBAAmB,CACf,MAAM;IACF,MAAMiB,SAAS,GAAGd,WAAW,CAACe,KAAZ,GAAoB,CAAtC;IACA,MAAMC,cAAc,GAAGf,WAAW,CAACa,SAAD,CAAlC;;IAEA,IAAIE,cAAJ,EAAoB;MAChB,MAAMC,YAAY,GAAGD,cAAc,CAACE,EAAf,GAAoBF,cAAc,CAACG,EAAxD;MACA,OAAOC,IAAI,CAACC,KAAL,CAAWL,cAAc,CAACG,EAAf,GAAoBF,YAAY,GAAG,CAA9C,CAAP;IACH;;IAED,OAAO,CAAP;EACH,CAXc,EAYf,CAACX,CAAD,EAAIgB,KAAJ,KAAc;IACV,IAAIhB,CAAC,KAAKgB,KAAV,EAAiB;MACb,IAAI5B,QAAQ,CAAC6B,EAAT,KAAgB,KAApB,EAA2B;QACvB5B,OAAO,CAACU,SAAD,CAAP,CAAmBC,CAAnB;MACH,CAFD,MAEO;QACHV,QAAQ,CAACM,aAAD,EAAgBI,CAAhB,EAAmB,CAAnB,EAAsB,IAAtB,CAAR;QACAX,OAAO,CAACiB,YAAD,CAAP,CAAsBN,CAAtB;MACH;IACJ;EACJ,CArBc,EAsBf,CAACL,WAAD,CAtBe,CAAnB;EAyBA,OAAO;IAAEC,aAAF;IAAiBW;EAAjB,CAAP;AACH;AAAA"}
@@ -1,13 +1,19 @@
1
- import { useState, useEffect } from 'react';
2
- import { useWindowDimensions } from 'react-native';
1
+ import { useEffect, useState } from 'react';
2
+ import { Dimensions } from 'react-native';
3
3
  export default function useValidWindowDimensions() {
4
- const window = useWindowDimensions();
5
- const [validWindow, setValidWindow] = useState(window);
4
+ const [window, setWindow] = useState(() => Dimensions.get('window'));
6
5
  useEffect(() => {
7
- if (window.width !== 0 && window.height !== 0) {
8
- setValidWindow(window);
9
- }
10
- }, [window]);
11
- return validWindow;
6
+ const subscription = Dimensions.addEventListener('change', newDimension => {
7
+ const {
8
+ window: newWindow
9
+ } = newDimension;
10
+
11
+ if (newWindow.width !== 0 && newWindow.height !== 0) {
12
+ setWindow(newWindow);
13
+ }
14
+ });
15
+ return subscription.remove;
16
+ }, []);
17
+ return window;
12
18
  }
13
19
  //# sourceMappingURL=index.ios.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["useState","useEffect","useWindowDimensions","useValidWindowDimensions","window","validWindow","setValidWindow","width","height"],"sources":["index.ios.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { useWindowDimensions } from 'react-native';\n\nexport default function useValidWindowDimensions() {\n const window = useWindowDimensions();\n const [validWindow, setValidWindow] = useState(window);\n\n useEffect(() => {\n if (window.width !== 0 && window.height !== 0) {\n setValidWindow(window);\n }\n }, [window]);\n\n return validWindow;\n}\n"],"mappings":"AAAA,SAASA,QAAT,EAAmBC,SAAnB,QAAoC,OAApC;AACA,SAASC,mBAAT,QAAoC,cAApC;AAEA,eAAe,SAASC,wBAAT,GAAoC;EAC/C,MAAMC,MAAM,GAAGF,mBAAmB,EAAlC;EACA,MAAM,CAACG,WAAD,EAAcC,cAAd,IAAgCN,QAAQ,CAACI,MAAD,CAA9C;EAEAH,SAAS,CAAC,MAAM;IACZ,IAAIG,MAAM,CAACG,KAAP,KAAiB,CAAjB,IAAsBH,MAAM,CAACI,MAAP,KAAkB,CAA5C,EAA+C;MAC3CF,cAAc,CAACF,MAAD,CAAd;IACH;EACJ,CAJQ,EAIN,CAACA,MAAD,CAJM,CAAT;EAMA,OAAOC,WAAP;AACH"}
1
+ {"version":3,"names":["useEffect","useState","Dimensions","useValidWindowDimensions","window","setWindow","get","subscription","addEventListener","newDimension","newWindow","width","height","remove"],"sources":["index.ios.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { ScaledSize } from 'react-native';\nimport { Dimensions } from 'react-native';\n\nexport default function useValidWindowDimensions(): ScaledSize {\n const [window, setWindow] = useState<ScaledSize>(() => Dimensions.get('window'));\n\n useEffect(() => {\n const subscription = Dimensions.addEventListener('change', (newDimension) => {\n const { window: newWindow } = newDimension;\n\n if (newWindow.width !== 0 && newWindow.height !== 0) {\n setWindow(newWindow);\n }\n });\n\n return subscription.remove;\n }, []);\n\n return window;\n}\n"],"mappings":"AAAA,SAASA,SAAT,EAAoBC,QAApB,QAAoC,OAApC;AAEA,SAASC,UAAT,QAA2B,cAA3B;AAEA,eAAe,SAASC,wBAAT,GAAgD;EAC3D,MAAM,CAACC,MAAD,EAASC,SAAT,IAAsBJ,QAAQ,CAAa,MAAMC,UAAU,CAACI,GAAX,CAAe,QAAf,CAAnB,CAApC;EAEAN,SAAS,CAAC,MAAM;IACZ,MAAMO,YAAY,GAAGL,UAAU,CAACM,gBAAX,CAA4B,QAA5B,EAAuCC,YAAD,IAAkB;MACzE,MAAM;QAAEL,MAAM,EAAEM;MAAV,IAAwBD,YAA9B;;MAEA,IAAIC,SAAS,CAACC,KAAV,KAAoB,CAApB,IAAyBD,SAAS,CAACE,MAAV,KAAqB,CAAlD,EAAqD;QACjDP,SAAS,CAACK,SAAD,CAAT;MACH;IACJ,CANoB,CAArB;IAQA,OAAOH,YAAY,CAACM,MAApB;EACH,CAVQ,EAUN,EAVM,CAAT;EAYA,OAAOT,MAAP;AACH"}
@@ -1,3 +1,3 @@
1
- import { ViewProps } from 'react-native';
1
+ import type { ViewProps } from 'react-native';
2
2
  import { OverridableComponentProps } from '../types';
3
3
  export default function CircularProgress(props: OverridableComponentProps<ViewProps>): JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
2
  import type TabsProps from './TabsProps';
3
3
  import type { TabsInstance } from './TabsProps';
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" | "hitSlop" | "removeClippedSubviews" | "nativeID" | "collapsable" | "needsOffscreenAlphaCompositing" | "renderToHardwareTextureAndroid" | "focusable" | "shouldRasterizeIOS" | "isTVSelectable" | "hasTVPreferredFocus" | "tvParallaxProperties" | "tvParallaxShiftDistanceX" | "tvParallaxShiftDistanceY" | "tvParallaxTiltAngle" | "tvParallaxMagnification" | "onTouchStart" | "onTouchMove" | "onTouchEnd" | "onTouchCancel" | "onTouchEndCapture" | "accessible" | "accessibilityActions" | "accessibilityLabel" | "accessibilityRole" | "accessibilityState" | "accessibilityHint" | "accessibilityValue" | "onAccessibilityAction" | "accessibilityLiveRegion" | "importantForAccessibility" | "accessibilityElementsHidden" | "accessibilityViewIsModal" | "onAccessibilityEscape" | "onAccessibilityTap" | "onMagicTap" | "accessibilityIgnoresInvertColors" | "variant" | "keyboardShouldPersistTaps" | "onChange" | "disableIndicator" | "initialIndex" | "scrollable"> & 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" | "hitSlop" | "removeClippedSubviews" | "nativeID" | "collapsable" | "needsOffscreenAlphaCompositing" | "renderToHardwareTextureAndroid" | "focusable" | "shouldRasterizeIOS" | "isTVSelectable" | "hasTVPreferredFocus" | "tvParallaxProperties" | "tvParallaxShiftDistanceX" | "tvParallaxShiftDistanceY" | "tvParallaxTiltAngle" | "tvParallaxMagnification" | "onTouchStart" | "onTouchMove" | "onTouchEnd" | "onTouchCancel" | "onTouchEndCapture" | "accessible" | "accessibilityActions" | "accessibilityLabel" | "accessibilityRole" | "accessibilityState" | "accessibilityHint" | "accessibilityValue" | "onAccessibilityAction" | "accessibilityLiveRegion" | "importantForAccessibility" | "accessibilityElementsHidden" | "accessibilityViewIsModal" | "onAccessibilityEscape" | "onAccessibilityTap" | "onMagicTap" | "accessibilityIgnoresInvertColors" | "variant" | "keyboardShouldPersistTaps" | "onChange" | "disableIndicator" | "initialIndex" | "scrollable" | "UNSTABLE_sharedIndex"> & React.RefAttributes<TabsInstance>>;
5
5
  export default Tabs;
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode, Ref } from 'react';
2
2
  import type { ViewProps } from 'react-native';
3
+ import type { SharedValue } from 'react-native-reanimated';
3
4
  import type { TabVariant } from '../Tab';
4
5
  import type { OverridableComponentProps } from '../types';
5
6
  export declare type KeyboardDismissMode = 'none' | 'on-drag' | 'interactive';
@@ -50,6 +51,10 @@ export default interface TabsProps extends OverridableComponentProps<ViewProps,
50
51
  * @default false
51
52
  */
52
53
  scrollable?: boolean;
54
+ /**
55
+ * Unstable API.
56
+ */
57
+ UNSTABLE_sharedIndex?: SharedValue<number>;
53
58
  /**
54
59
  * The variant to use.
55
60
  * @default 'primary'
@@ -1 +1,2 @@
1
- export default function useValidWindowDimensions(): import("react-native").ScaledSize;
1
+ import type { ScaledSize } from 'react-native';
2
+ export default function useValidWindowDimensions(): ScaledSize;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fountain-ui/core",
3
- "version": "2.0.0-beta.14",
3
+ "version": "2.0.0-beta.15",
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": "80988af3fdfd671fcb655d4b36a017ce13baf29e"
70
+ "gitHead": "efb9e6cc7cc65e52d1084a0c3664f419dc171240"
71
71
  }
@@ -17,7 +17,7 @@ const MINIFIED_SCALE = .96;
17
17
 
18
18
  // at "node_modules/react-native/Libraries/Pressability.js"
19
19
  // const DEFAULT_MIN_PRESS_DURATION = 130;
20
- const PRESS_IN_DELAY = 130;
20
+ const SCALE_EFFECT_PRESS_IN_DELAY = 130;
21
21
 
22
22
  type TimingAnimationValue = Animated.Value | Animated.ValueXY;
23
23
  type TimingAnimationToValue = Animated.TimingAnimationConfig['toValue'];
@@ -95,6 +95,10 @@ export default function ButtonBase(props: ButtonBaseProps) {
95
95
  transform: [{ scale }],
96
96
  };
97
97
 
98
+ const pressDelay = pressEffect === 'scale'
99
+ ? SCALE_EFFECT_PRESS_IN_DELAY
100
+ : 0;
101
+
98
102
  return (
99
103
  <AnimatedPressable
100
104
  disabled={disabled}
@@ -105,7 +109,7 @@ export default function ButtonBase(props: ButtonBaseProps) {
105
109
  animatedStyle,
106
110
  styleProp,
107
111
  ]}
108
- unstable_pressDelay={PRESS_IN_DELAY}
112
+ unstable_pressDelay={pressDelay}
109
113
  {...otherProps}
110
114
  >
111
115
  {typeof children !== 'function' ? (
@@ -1,15 +1,12 @@
1
- import React, { useEffect } from 'react';
2
- import { ViewProps } from 'react-native';
1
+ import React, { useEffect, useRef } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+ import { Animated, Easing } from 'react-native';
3
4
  import { NamedStylesStringUnion, UseStyles } from '@fountain-ui/styles';
4
- import type { WithTimingConfig } from 'react-native-reanimated';
5
- import Animated, { Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming } from 'react-native-reanimated';
6
5
  import { CircularProgress as CircularProgressIcon } from '../internal/icons';
7
6
  import { OverridableComponentProps } from '../types';
8
7
 
9
8
  type CircularProgressStyles = NamedStylesStringUnion<'root'>;
10
9
 
11
- const ANIMATION_CONFIG: Readonly<WithTimingConfig> = { duration: 900, easing: Easing.linear };
12
-
13
10
  const MIN_ROTATE_DEGREE = 0;
14
11
  const MAX_ROTATE_DEGREE = 360;
15
12
 
@@ -27,18 +24,29 @@ export default function CircularProgress(props: OverridableComponentProps<ViewPr
27
24
 
28
25
  const styles = useStyles();
29
26
 
30
- const rotate = useSharedValue(MIN_ROTATE_DEGREE);
27
+ const rotate = useRef(new Animated.Value(MIN_ROTATE_DEGREE)).current;
31
28
 
32
- const animatedStyle = useAnimatedStyle(() => ({
33
- transform: [{ rotate: `${rotate.value}deg` }],
34
- }), []);
29
+ const animatedStyle = {
30
+ transform: [{
31
+ rotate: rotate.interpolate({
32
+ inputRange: [MIN_ROTATE_DEGREE, MAX_ROTATE_DEGREE],
33
+ outputRange: [`${MIN_ROTATE_DEGREE}deg`, `${MAX_ROTATE_DEGREE}deg`],
34
+ }),
35
+ }],
36
+ };
35
37
 
36
38
  useEffect(() => {
37
- rotate.value = withRepeat(
38
- withTiming(MAX_ROTATE_DEGREE, ANIMATION_CONFIG),
39
- -1,
40
- false,
41
- );
39
+ Animated.loop(
40
+ Animated.timing(
41
+ rotate,
42
+ {
43
+ toValue: MAX_ROTATE_DEGREE,
44
+ duration: 900,
45
+ easing: Easing.linear,
46
+ useNativeDriver: true,
47
+ },
48
+ ),
49
+ ).start();
42
50
  }, []);
43
51
 
44
52
  return (
@@ -8,14 +8,21 @@ import { defaultCoordinate } from './TabCoordinate';
8
8
 
9
9
  type TabIndicatorStyles = NamedStylesStringUnion<'root' | 'disabled'>;
10
10
 
11
+ const INDICATOR_WIDTH = 10;
12
+ const INDICATOR_HEIGHT = 4;
13
+
14
+ const SCROLLABLE_TABS_INSET = 12 * 2;
15
+
11
16
  const useStyles: UseStyles<TabIndicatorStyles> = function (): TabIndicatorStyles {
12
17
  const theme = useTheme();
13
18
 
14
19
  return {
15
20
  root: {
16
21
  backgroundColor: theme.palette.secondary.main,
22
+ left: 0,
17
23
  bottom: 0,
18
- height: 4,
24
+ width: INDICATOR_WIDTH,
25
+ height: INDICATOR_HEIGHT,
19
26
  position: 'absolute',
20
27
  },
21
28
  disabled: {
@@ -25,7 +32,7 @@ const useStyles: UseStyles<TabIndicatorStyles> = function (): TabIndicatorStyles
25
32
  };
26
33
 
27
34
  const ANIMATION_CONFIG: Readonly<WithTimingConfig> = {
28
- duration: 200,
35
+ duration: 300,
29
36
  easing: Easing.out(Easing.exp),
30
37
  };
31
38
 
@@ -48,15 +55,16 @@ export default function TabIndicator(props: TabIndicatorProps) {
48
55
 
49
56
  const tabWidth = x2 - x1;
50
57
 
51
- const singleInset = scrollable ? 12 : 0;
52
- const totalInset = singleInset * 2;
58
+ const translateX = x1 + (tabWidth - INDICATOR_WIDTH) / 2;
53
59
 
54
- const nextLeft = x1 + singleInset;
55
- const nextWidth = tabWidth - totalInset;
60
+ const inset = scrollable ? SCROLLABLE_TABS_INSET : 0;
61
+ const scaleX = (tabWidth - inset) / INDICATOR_WIDTH;
56
62
 
57
63
  return {
58
- left: withTiming(nextLeft, ANIMATION_CONFIG),
59
- width: withTiming(nextWidth, ANIMATION_CONFIG),
64
+ transform: [
65
+ { translateX: withTiming(translateX, ANIMATION_CONFIG) },
66
+ { scaleX: withTiming(scaleX, ANIMATION_CONFIG) },
67
+ ],
60
68
  };
61
69
  }, [coordinates, scrollable]);
62
70
 
package/src/Tabs/Tabs.tsx CHANGED
@@ -47,9 +47,12 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
47
47
  scrollable = false,
48
48
  style,
49
49
  variant = 'primary',
50
+ UNSTABLE_sharedIndex,
50
51
  } = props;
51
52
 
52
- const sharedIndex = useSharedValue<number>(initialIndex);
53
+ const fallbackSharedIndex = useSharedValue<number>(initialIndex);
54
+
55
+ const sharedIndex = UNSTABLE_sharedIndex ?? fallbackSharedIndex;
53
56
 
54
57
  const getCurrentIndex = (): number => sharedIndex.value;
55
58
 
@@ -97,7 +100,7 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
97
100
 
98
101
  // @ts-ignore
99
102
  const tabElement = cloneElement(child, {
100
- enableIndicator: !disableIndicator && isReadyToRenderIndicator,
103
+ enableIndicator: !disableIndicator && !isReadyToRenderIndicator,
101
104
  onLayout,
102
105
  onPress,
103
106
  onMouseDown,
@@ -114,14 +117,14 @@ const Tabs = forwardRef<TabsInstance, TabsProps>(function Tabs(props, ref) {
114
117
  );
115
118
  });
116
119
 
117
- const tabIndicator = (
120
+ const tabIndicator = isReadyToRenderIndicator ? (
118
121
  <TabIndicator
119
122
  coordinates={coordinates}
120
123
  disabled={disableIndicator}
121
124
  scrollable={scrollable}
122
125
  sharedIndex={sharedIndex}
123
126
  />
124
- );
127
+ ) : null;
125
128
 
126
129
  return (
127
130
  <View
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode, Ref } from 'react';
2
2
  import type { ViewProps } from 'react-native';
3
+ import type { SharedValue } from 'react-native-reanimated';
3
4
  import type { TabVariant } from '../Tab';
4
5
  import type { OverridableComponentProps } from '../types';
5
6
 
@@ -70,6 +71,11 @@ export default interface TabsProps extends OverridableComponentProps<ViewProps,
70
71
  */
71
72
  scrollable?: boolean;
72
73
 
74
+ /**
75
+ * Unstable API.
76
+ */
77
+ UNSTABLE_sharedIndex?: SharedValue<number>;
78
+
73
79
  /**
74
80
  * The variant to use.
75
81
  * @default 'primary'
@@ -1,7 +1,8 @@
1
1
  import type { MutableRefObject } from 'react';
2
2
  import { useCallback, useRef } from 'react';
3
3
  import type { ScrollView, ViewProps } from 'react-native';
4
- import { runOnJS, SharedValue, useAnimatedReaction } from 'react-native-reanimated';
4
+ import { Platform } from 'react-native';
5
+ import { runOnJS, scrollTo, SharedValue, useAnimatedReaction, useAnimatedRef } from 'react-native-reanimated';
5
6
  import type TabCoordinate from './TabCoordinate';
6
7
 
7
8
  export interface UseScrollViewReaction {
@@ -13,22 +14,24 @@ export default function useScrollViewReaction(
13
14
  sharedIndex: SharedValue<number>,
14
15
  coordinates: TabCoordinate[],
15
16
  ): UseScrollViewReaction {
16
- const scrollViewRef = useRef<ScrollView | null>(null);
17
+ const scrollViewRef = useAnimatedRef<ScrollView>();
17
18
 
18
- const lastScrolledPositionRef = useRef<number>(NaN);
19
+ const lastScrolledXRef = useRef<number>(NaN);
19
20
 
20
- const scrollTo = (scrollPosition: number) => {
21
- const scrollView = scrollViewRef.current;
21
+ const scrollToX = (x: number) => {
22
+ if (Number.isFinite(x)) {
23
+ scrollViewRef.current?.scrollTo({ x, y: 0, animated: true });
22
24
 
23
- if (scrollView && Number.isFinite(scrollPosition)) {
24
- scrollView.scrollTo({ x: scrollPosition, y: 0, animated: true });
25
-
26
- lastScrolledPositionRef.current = scrollPosition;
25
+ didScrollToX(x);
27
26
  }
28
27
  };
29
28
 
29
+ const didScrollToX = (x: number) => {
30
+ lastScrolledXRef.current = x;
31
+ };
32
+
30
33
  const onLayout = useCallback(() => {
31
- scrollTo(lastScrolledPositionRef.current);
34
+ scrollToX(lastScrolledXRef.current);
32
35
  }, []);
33
36
 
34
37
  useAnimatedReaction(
@@ -43,9 +46,14 @@ export default function useScrollViewReaction(
43
46
 
44
47
  return 0;
45
48
  },
46
- (scrollPosition, prevScrollPosition) => {
47
- if (scrollPosition !== prevScrollPosition) {
48
- runOnJS(scrollTo)(scrollPosition);
49
+ (x, prevX) => {
50
+ if (x !== prevX) {
51
+ if (Platform.OS === 'web') {
52
+ runOnJS(scrollToX)(x);
53
+ } else {
54
+ scrollTo(scrollViewRef, x, 0, true);
55
+ runOnJS(didScrollToX)(x);
56
+ }
49
57
  }
50
58
  },
51
59
  [coordinates],