@hero-design/rn 8.59.0 → 8.60.0
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +6 -0
- package/es/index.js +183 -102
- package/lib/index.js +183 -102
- package/package.json +1 -1
- package/src/components/AnimatedScroller/AnimatedFAB.tsx +99 -49
- package/src/components/AnimatedScroller/AnimatedScrollable.tsx +18 -3
- package/src/components/AnimatedScroller/__tests__/ScrollablesWithFAB.spec.tsx +30 -9
- package/src/components/AnimatedScroller/__tests__/__snapshots__/ScrollablesWithFAB.spec.tsx.snap +474 -447
- package/src/components/FAB/ActionGroup/ActionItem.tsx +3 -1
- package/src/components/FAB/ActionGroup/__tests__/__snapshots__/index.spec.tsx.snap +216 -211
- package/src/components/FAB/ActionGroup/index.tsx +34 -28
- package/src/components/FAB/FAB.tsx +102 -41
- package/src/components/FAB/StyledFAB.tsx +10 -8
- package/src/components/FAB/__tests__/__snapshots__/StyledFAB.spec.tsx.snap +34 -38
- package/src/components/FAB/__tests__/__snapshots__/index.spec.tsx.snap +191 -170
- package/types/components/AnimatedScroller/AnimatedFAB.d.ts +3 -1
- package/types/components/AnimatedScroller/AnimatedScrollable.d.ts +1 -1
- package/types/components/FAB/StyledFAB.d.ts +4 -6
package/lib/index.js
CHANGED
|
@@ -14416,7 +14416,8 @@ var Error$1 = function Error(_ref2) {
|
|
|
14416
14416
|
}, nativeProps));
|
|
14417
14417
|
};
|
|
14418
14418
|
|
|
14419
|
-
var
|
|
14419
|
+
var AnimatedTouchableHighlight = reactNative.Animated.createAnimatedComponent(reactNative.TouchableHighlight);
|
|
14420
|
+
var StyledFAB$1 = index$9(AnimatedTouchableHighlight)(function (_ref) {
|
|
14420
14421
|
var theme = _ref.theme,
|
|
14421
14422
|
themeActive = _ref.themeActive;
|
|
14422
14423
|
return {
|
|
@@ -14431,7 +14432,8 @@ var StyledFAB$1 = index$9(reactNative.TouchableHighlight)(function (_ref) {
|
|
|
14431
14432
|
shadowColor: theme.__hd__.fab.shadows.color,
|
|
14432
14433
|
shadowOffset: theme.__hd__.fab.shadows.offset,
|
|
14433
14434
|
shadowRadius: theme.__hd__.fab.shadows.radius,
|
|
14434
|
-
shadowOpacity: theme.__hd__.fab.shadows.opacity
|
|
14435
|
+
shadowOpacity: theme.__hd__.fab.shadows.opacity,
|
|
14436
|
+
height: theme.__hd__.fab.sizes.height
|
|
14435
14437
|
};
|
|
14436
14438
|
});
|
|
14437
14439
|
var StyledFABIcon = index$9(Icon)(function (_ref2) {
|
|
@@ -14523,16 +14525,14 @@ var IconWithTextContent = function IconWithTextContent(_ref2) {
|
|
|
14523
14525
|
testID: "styled-fab-icon"
|
|
14524
14526
|
})), /*#__PURE__*/React__default["default"].createElement(StyledFABText, null, title));
|
|
14525
14527
|
};
|
|
14526
|
-
var
|
|
14527
|
-
|
|
14528
|
-
|
|
14529
|
-
|
|
14530
|
-
|
|
14531
|
-
|
|
14532
|
-
|
|
14533
|
-
|
|
14534
|
-
},
|
|
14535
|
-
duration: reactNative.Platform.OS === 'ios' ? 300 : 400
|
|
14528
|
+
var animateWidth = function animateWidth() {
|
|
14529
|
+
reactNative.LayoutAnimation.configureNext({
|
|
14530
|
+
duration: reactNative.Platform.OS === 'ios' ? 200 : 400,
|
|
14531
|
+
update: {
|
|
14532
|
+
type: 'spring',
|
|
14533
|
+
springDamping: reactNative.Platform.OS === 'ios' ? 1 : 1.5
|
|
14534
|
+
}
|
|
14535
|
+
});
|
|
14536
14536
|
};
|
|
14537
14537
|
var FAB$1 = /*#__PURE__*/React.forwardRef(function (_ref3, ref) {
|
|
14538
14538
|
var _StyleSheet$flatten, _StyleSheet$flatten2;
|
|
@@ -14544,61 +14544,99 @@ var FAB$1 = /*#__PURE__*/React.forwardRef(function (_ref3, ref) {
|
|
|
14544
14544
|
active = _ref3.active,
|
|
14545
14545
|
style = _ref3.style;
|
|
14546
14546
|
var theme = useTheme();
|
|
14547
|
-
var _React$useState = React__default["default"].useState(
|
|
14548
|
-
_React$useState2 = _slicedToArray(_React$useState, 2),
|
|
14549
|
-
canAnimate = _React$useState2[0],
|
|
14550
|
-
setCanAnimate = _React$useState2[1];
|
|
14551
|
-
var _React$useState3 = React__default["default"].useState({
|
|
14547
|
+
var _React$useState = React__default["default"].useState({
|
|
14552
14548
|
hideTitle: false,
|
|
14553
14549
|
hideButton: false
|
|
14554
14550
|
}),
|
|
14555
|
-
_React$
|
|
14556
|
-
displayState = _React$
|
|
14557
|
-
setDisplayState = _React$
|
|
14551
|
+
_React$useState2 = _slicedToArray(_React$useState, 2),
|
|
14552
|
+
displayState = _React$useState2[0],
|
|
14553
|
+
setDisplayState = _React$useState2[1];
|
|
14558
14554
|
var isIconOnly = displayState.hideTitle || active || !title;
|
|
14555
|
+
var animatedValues = {
|
|
14556
|
+
opacity: React__default["default"].useRef(new reactNative.Animated.Value(1)).current,
|
|
14557
|
+
width: React__default["default"].useRef(new reactNative.Animated.Value(1)).current,
|
|
14558
|
+
translateY: React__default["default"].useRef(new reactNative.Animated.Value(0)).current
|
|
14559
|
+
};
|
|
14560
|
+
var marginBottom = Number((_StyleSheet$flatten = reactNative.StyleSheet.flatten(style)) === null || _StyleSheet$flatten === void 0 ? void 0 : _StyleSheet$flatten.marginBottom) || 0;
|
|
14561
|
+
var _React$useState3 = React__default["default"].useState(0),
|
|
14562
|
+
_React$useState4 = _slicedToArray(_React$useState3, 2),
|
|
14563
|
+
buttonWidth = _React$useState4[0],
|
|
14564
|
+
setButtonWidth = _React$useState4[1];
|
|
14565
|
+
var hasSetButtonWidth = buttonWidth > 0;
|
|
14559
14566
|
React__default["default"].useImperativeHandle(ref, function () {
|
|
14560
14567
|
return {
|
|
14561
14568
|
show: function show() {
|
|
14569
|
+
reactNative.Animated.spring(animatedValues.translateY, {
|
|
14570
|
+
toValue: 0,
|
|
14571
|
+
useNativeDriver: true
|
|
14572
|
+
}).start();
|
|
14562
14573
|
setDisplayState({
|
|
14563
14574
|
hideButton: false,
|
|
14564
14575
|
hideTitle: false
|
|
14565
14576
|
});
|
|
14577
|
+
animateWidth();
|
|
14578
|
+
reactNative.Animated.spring(animatedValues.opacity, {
|
|
14579
|
+
toValue: 1,
|
|
14580
|
+
useNativeDriver: true
|
|
14581
|
+
}).start();
|
|
14566
14582
|
},
|
|
14567
14583
|
collapse: function collapse() {
|
|
14584
|
+
reactNative.Animated.parallel([reactNative.Animated.spring(animatedValues.opacity, {
|
|
14585
|
+
toValue: 1,
|
|
14586
|
+
useNativeDriver: true
|
|
14587
|
+
}), reactNative.Animated.spring(animatedValues.translateY, {
|
|
14588
|
+
toValue: 0,
|
|
14589
|
+
useNativeDriver: true
|
|
14590
|
+
})]).start();
|
|
14591
|
+
animateWidth();
|
|
14568
14592
|
setDisplayState({
|
|
14569
14593
|
hideButton: false,
|
|
14570
14594
|
hideTitle: true
|
|
14571
14595
|
});
|
|
14572
14596
|
},
|
|
14573
14597
|
hide: function hide() {
|
|
14574
|
-
|
|
14575
|
-
|
|
14576
|
-
|
|
14598
|
+
reactNative.Animated.stagger(20, [reactNative.Animated.spring(animatedValues.opacity, {
|
|
14599
|
+
toValue: 0,
|
|
14600
|
+
useNativeDriver: true
|
|
14601
|
+
}), reactNative.Animated.spring(animatedValues.translateY, {
|
|
14602
|
+
toValue: 1,
|
|
14603
|
+
useNativeDriver: true
|
|
14604
|
+
})]).start(function () {
|
|
14605
|
+
animateWidth();
|
|
14606
|
+
setDisplayState(function (previousState) {
|
|
14607
|
+
return _objectSpread2(_objectSpread2({}, previousState), {}, {
|
|
14608
|
+
hideButton: true
|
|
14609
|
+
});
|
|
14577
14610
|
});
|
|
14578
14611
|
});
|
|
14579
14612
|
}
|
|
14580
14613
|
};
|
|
14581
14614
|
}, []);
|
|
14582
|
-
React__default["default"].
|
|
14583
|
-
|
|
14584
|
-
|
|
14585
|
-
}
|
|
14586
|
-
}, [isIconOnly, displayState.hideButton, canAnimate]);
|
|
14587
|
-
var marginBottom = Number((_StyleSheet$flatten = reactNative.StyleSheet.flatten(style)) === null || _StyleSheet$flatten === void 0 ? void 0 : _StyleSheet$flatten.marginBottom) || 0;
|
|
14588
|
-
return /*#__PURE__*/React__default["default"].createElement(StyledFAB$1
|
|
14589
|
-
/** Add a small timeout before executing animation to prevent flakiness */, {
|
|
14590
|
-
onLayout: function onLayout() {
|
|
14591
|
-
return setTimeout(function () {
|
|
14592
|
-
return setCanAnimate(true);
|
|
14593
|
-
}, 500);
|
|
14615
|
+
return /*#__PURE__*/React__default["default"].createElement(StyledFAB$1, {
|
|
14616
|
+
onLayout: function onLayout(event) {
|
|
14617
|
+
return !hasSetButtonWidth && !active && setButtonWidth(event.nativeEvent.layout.width);
|
|
14594
14618
|
},
|
|
14595
14619
|
underlayColor: theme.__hd__.fab.colors.buttonPressedBackground,
|
|
14596
14620
|
onPress: onPress,
|
|
14597
14621
|
style: [style, {
|
|
14598
|
-
bottom: displayState.hideButton ? -(marginBottom + theme.__hd__.fab.sizes.height * 2) : (_StyleSheet$flatten2 = reactNative.StyleSheet.flatten(style)) === null || _StyleSheet$flatten2 === void 0 ? void 0 : _StyleSheet$flatten2.bottom
|
|
14622
|
+
bottom: displayState.hideButton ? -(marginBottom + theme.__hd__.fab.sizes.height * 2) : (_StyleSheet$flatten2 = reactNative.StyleSheet.flatten(style)) === null || _StyleSheet$flatten2 === void 0 ? void 0 : _StyleSheet$flatten2.bottom,
|
|
14623
|
+
transform: [{
|
|
14624
|
+
translateY: animatedValues.translateY.interpolate({
|
|
14625
|
+
inputRange: [0, 1],
|
|
14626
|
+
outputRange: [0, marginBottom + theme.__hd__.fab.sizes.height * 2]
|
|
14627
|
+
})
|
|
14628
|
+
}]
|
|
14599
14629
|
}],
|
|
14600
14630
|
testID: testID,
|
|
14601
14631
|
themeActive: active
|
|
14632
|
+
}, /*#__PURE__*/React__default["default"].createElement(reactNative.Animated.View, {
|
|
14633
|
+
style: {
|
|
14634
|
+
flexDirection: 'row',
|
|
14635
|
+
opacity: animatedValues.opacity.interpolate({
|
|
14636
|
+
inputRange: [0, 1],
|
|
14637
|
+
outputRange: [0, 1]
|
|
14638
|
+
})
|
|
14639
|
+
}
|
|
14602
14640
|
}, isIconOnly ? /*#__PURE__*/React__default["default"].createElement(IconOnlyContent, {
|
|
14603
14641
|
animated: animated,
|
|
14604
14642
|
active: active,
|
|
@@ -14606,7 +14644,7 @@ var FAB$1 = /*#__PURE__*/React.forwardRef(function (_ref3, ref) {
|
|
|
14606
14644
|
}) : /*#__PURE__*/React__default["default"].createElement(IconWithTextContent, {
|
|
14607
14645
|
icon: icon,
|
|
14608
14646
|
title: title
|
|
14609
|
-
}));
|
|
14647
|
+
})));
|
|
14610
14648
|
});
|
|
14611
14649
|
FAB$1.displayName = 'FAB';
|
|
14612
14650
|
|
|
@@ -14656,7 +14694,9 @@ var ActionItem = function ActionItem(_ref) {
|
|
|
14656
14694
|
reactNative.Animated.spring(animatedValue.current, {
|
|
14657
14695
|
toValue: active ? 1 : 0,
|
|
14658
14696
|
useNativeDriver: reactNative.Platform.OS !== 'web',
|
|
14659
|
-
delay: index *
|
|
14697
|
+
delay: index * 30,
|
|
14698
|
+
speed: 10,
|
|
14699
|
+
bounciness: 10
|
|
14660
14700
|
}).start();
|
|
14661
14701
|
}, [active, index]);
|
|
14662
14702
|
return /*#__PURE__*/React__default["default"].createElement(reactNative.Animated.View, {
|
|
@@ -14733,8 +14773,9 @@ var ActionGroup = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
|
14733
14773
|
_ref$fabIcon = _ref.fabIcon,
|
|
14734
14774
|
fabIcon = _ref$fabIcon === void 0 ? 'add' : _ref$fabIcon;
|
|
14735
14775
|
useDeprecation("FAB.ActionGroup's headerTitle prop will be removed in the next major release. Please remove it.", headerTitle !== undefined);
|
|
14776
|
+
var theme = useTheme();
|
|
14736
14777
|
var fabRef = React.useRef(null);
|
|
14737
|
-
var
|
|
14778
|
+
var animatedValue = React.useRef(new reactNative.Animated.Value(active ? 1 : 0));
|
|
14738
14779
|
React__default["default"].useImperativeHandle(ref, function () {
|
|
14739
14780
|
return {
|
|
14740
14781
|
showFAB: function showFAB() {
|
|
@@ -14752,28 +14793,28 @@ var ActionGroup = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
|
14752
14793
|
};
|
|
14753
14794
|
}, [fabRef]);
|
|
14754
14795
|
React__default["default"].useEffect(function () {
|
|
14755
|
-
reactNative.Animated.spring(
|
|
14796
|
+
reactNative.Animated.spring(animatedValue.current, {
|
|
14756
14797
|
toValue: active ? 1 : 0,
|
|
14798
|
+
delay: 100,
|
|
14757
14799
|
useNativeDriver: reactNative.Platform.OS !== 'web'
|
|
14758
14800
|
}).start();
|
|
14801
|
+
if (active) {
|
|
14802
|
+
var _fabRef$current4;
|
|
14803
|
+
(_fabRef$current4 = fabRef.current) === null || _fabRef$current4 === void 0 || _fabRef$current4.collapse();
|
|
14804
|
+
} else {
|
|
14805
|
+
var _fabRef$current5;
|
|
14806
|
+
(_fabRef$current5 = fabRef.current) === null || _fabRef$current5 === void 0 || _fabRef$current5.show();
|
|
14807
|
+
}
|
|
14759
14808
|
}, [active]);
|
|
14760
|
-
var
|
|
14809
|
+
var actionGroupOpacity = animatedValue.current.interpolate({
|
|
14761
14810
|
inputRange: [0, 1],
|
|
14762
14811
|
outputRange: [0, 1]
|
|
14763
14812
|
});
|
|
14764
|
-
var interpolatedFABOpacityAnimation = tranlateXAnimation.current.interpolate({
|
|
14765
|
-
inputRange: [0, 1],
|
|
14766
|
-
outputRange: [1, 0]
|
|
14767
|
-
});
|
|
14768
14813
|
return /*#__PURE__*/React__default["default"].createElement(StyledContainer$2, {
|
|
14769
14814
|
testID: testID,
|
|
14770
14815
|
pointerEvents: "box-none",
|
|
14771
14816
|
style: style
|
|
14772
|
-
}, /*#__PURE__*/React__default["default"].createElement(reactNative.Animated.View, {
|
|
14773
|
-
style: {
|
|
14774
|
-
opacity: interpolatedFABOpacityAnimation
|
|
14775
|
-
}
|
|
14776
|
-
}, /*#__PURE__*/React__default["default"].createElement(StyledFAB, {
|
|
14817
|
+
}, /*#__PURE__*/React__default["default"].createElement(reactNative.Animated.View, null, /*#__PURE__*/React__default["default"].createElement(StyledFAB, {
|
|
14777
14818
|
key: "fab",
|
|
14778
14819
|
testID: "fab",
|
|
14779
14820
|
icon: fabIcon,
|
|
@@ -14797,7 +14838,7 @@ var ActionGroup = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
|
14797
14838
|
testID: "action-group",
|
|
14798
14839
|
pointerEvents: "box-none",
|
|
14799
14840
|
style: {
|
|
14800
|
-
opacity:
|
|
14841
|
+
opacity: actionGroupOpacity
|
|
14801
14842
|
}
|
|
14802
14843
|
}, /*#__PURE__*/React__default["default"].createElement(Box, {
|
|
14803
14844
|
style: [style, {
|
|
@@ -14810,7 +14851,13 @@ var ActionGroup = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
|
14810
14851
|
index: active ? index : items.length - index,
|
|
14811
14852
|
active: active
|
|
14812
14853
|
}));
|
|
14813
|
-
}))), /*#__PURE__*/React__default["default"].createElement(StyledFAB
|
|
14854
|
+
}))), active && /*#__PURE__*/React__default["default"].createElement(StyledFAB
|
|
14855
|
+
// This FAB is moved up a bit compared to the original FAB,
|
|
14856
|
+
// set marginBottom to negative value to compensate for it
|
|
14857
|
+
, {
|
|
14858
|
+
style: {
|
|
14859
|
+
marginBottom: -theme.space.xxsmall
|
|
14860
|
+
},
|
|
14814
14861
|
key: "fab-in-portal",
|
|
14815
14862
|
testID: "fab-in-portal",
|
|
14816
14863
|
icon: fabIcon,
|
|
@@ -35584,8 +35631,9 @@ var index = Object.assign(RichTextEditorWithRef, {
|
|
|
35584
35631
|
Toolbar: EditorToolbar
|
|
35585
35632
|
});
|
|
35586
35633
|
|
|
35587
|
-
var
|
|
35588
|
-
var
|
|
35634
|
+
var LAST_BREAKPOINT = 100;
|
|
35635
|
+
var MIDDLE_BREAKPOINT = 250;
|
|
35636
|
+
var MAX_ANIMATABLE_SCROLL_DISTANCE = 400;
|
|
35589
35637
|
var REF_ACTIONS_BY_COMPONENT = {
|
|
35590
35638
|
FAB: {
|
|
35591
35639
|
show: 'show',
|
|
@@ -35600,27 +35648,23 @@ var REF_ACTIONS_BY_COMPONENT = {
|
|
|
35600
35648
|
};
|
|
35601
35649
|
var AnimatedFAB = function AnimatedFAB(_ref) {
|
|
35602
35650
|
var fabProps = _ref.fabProps,
|
|
35603
|
-
contentOffsetY = _ref.contentOffsetY
|
|
35651
|
+
contentOffsetY = _ref.contentOffsetY,
|
|
35652
|
+
contentHeight = _ref.contentHeight,
|
|
35653
|
+
layoutHeight = _ref.layoutHeight;
|
|
35604
35654
|
var component = 'items' in fabProps ? 'ActionGroup' : 'FAB';
|
|
35605
|
-
var _React$useState = React__default["default"].useState('down'),
|
|
35606
|
-
_React$useState2 = _slicedToArray(_React$useState, 2),
|
|
35607
|
-
currentScrollDirection = _React$useState2[0],
|
|
35608
|
-
setCurrentScrollDirection = _React$useState2[1];
|
|
35609
|
-
var _React$useState3 = React__default["default"].useState(0),
|
|
35610
|
-
_React$useState4 = _slicedToArray(_React$useState3, 2),
|
|
35611
|
-
lastScrollY = _React$useState4[0],
|
|
35612
|
-
setLastScrollY = _React$useState4[1];
|
|
35613
|
-
var _React$useState5 = React__default["default"].useState('show'),
|
|
35614
|
-
_React$useState6 = _slicedToArray(_React$useState5, 2),
|
|
35615
|
-
fabState = _React$useState6[0],
|
|
35616
|
-
setFabState = _React$useState6[1];
|
|
35617
|
-
var _React$useState7 = React__default["default"].useState(SHOW_AND_HIDE_BREAKPOINT),
|
|
35618
|
-
_React$useState8 = _slicedToArray(_React$useState7, 2),
|
|
35619
|
-
remainingScrollOffset = _React$useState8[0],
|
|
35620
|
-
setRemainingScrollOffset = _React$useState8[1];
|
|
35621
35655
|
var ref = React__default["default"].useRef(null);
|
|
35656
|
+
var currentContentHeight = React__default["default"].useRef(0);
|
|
35657
|
+
var currentLayoutHeight = React__default["default"].useRef(0);
|
|
35658
|
+
/** fabState is used to avoid calling duplicated animations. */
|
|
35659
|
+
var fabState = React__default["default"].useRef('show');
|
|
35660
|
+
/** remainingScrollOffset determines whether to animate the FAB. */
|
|
35661
|
+
var remainingScrollOffset = React__default["default"].useRef(MAX_ANIMATABLE_SCROLL_DISTANCE);
|
|
35662
|
+
/** currentScrollDirection is used to determine the scroll direction. */
|
|
35663
|
+
var currentScrollDirection = React__default["default"].useRef('down');
|
|
35664
|
+
/** lastScrollY is the scrollY from the preview scroll event. */
|
|
35665
|
+
var lastScrollY = React__default["default"].useRef(0);
|
|
35622
35666
|
var animateFab = React__default["default"].useCallback(function (newState) {
|
|
35623
|
-
if (fabState !== newState) {
|
|
35667
|
+
if (fabState.current !== newState) {
|
|
35624
35668
|
if (newState === 'show') {
|
|
35625
35669
|
var _ref$current;
|
|
35626
35670
|
(_ref$current = ref.current) === null || _ref$current === void 0 || _ref$current[REF_ACTIONS_BY_COMPONENT[component].show]();
|
|
@@ -35631,37 +35675,64 @@ var AnimatedFAB = function AnimatedFAB(_ref) {
|
|
|
35631
35675
|
var _ref$current3;
|
|
35632
35676
|
(_ref$current3 = ref.current) === null || _ref$current3 === void 0 || _ref$current3[REF_ACTIONS_BY_COMPONENT[component].collapse]();
|
|
35633
35677
|
}
|
|
35634
|
-
|
|
35635
|
-
}
|
|
35636
|
-
}, [fabState, component]);
|
|
35637
|
-
// Listen to ScrollView's contentOffsetY value
|
|
35638
|
-
contentOffsetY.addListener(function (_ref2) {
|
|
35639
|
-
var value = _ref2.value;
|
|
35640
|
-
if (value < 0) {
|
|
35641
|
-
return;
|
|
35642
|
-
}
|
|
35643
|
-
var newScrollDirection = value > lastScrollY ? 'down' : 'up';
|
|
35644
|
-
var isScrollingDown = newScrollDirection === 'down';
|
|
35645
|
-
if (newScrollDirection !== currentScrollDirection || lastScrollY === 0) {
|
|
35646
|
-
setLastScrollY(value);
|
|
35647
|
-
setCurrentScrollDirection(newScrollDirection);
|
|
35648
|
-
}
|
|
35649
|
-
var offsetFromLastDirection = Math.abs(value - lastScrollY);
|
|
35650
|
-
var offsetDiff = Math.round(Math.max(remainingScrollOffset - offsetFromLastDirection, 0));
|
|
35651
|
-
if (remainingScrollOffset > 0) {
|
|
35652
|
-
if (offsetDiff === SHOW_AND_HIDE_BREAKPOINT) {
|
|
35653
|
-
animateFab(isScrollingDown ? 'show' : 'hide');
|
|
35654
|
-
} else if (offsetDiff <= SHOW_AND_HIDE_BREAKPOINT && offsetDiff > COLLAPSE_BREAKPOINT) {
|
|
35655
|
-
animateFab('collapse');
|
|
35656
|
-
} else if (offsetDiff <= COLLAPSE_BREAKPOINT) {
|
|
35657
|
-
animateFab(isScrollingDown ? 'hide' : 'show');
|
|
35658
|
-
}
|
|
35659
|
-
setRemainingScrollOffset(offsetDiff);
|
|
35678
|
+
fabState.current = newState;
|
|
35660
35679
|
}
|
|
35661
|
-
});
|
|
35680
|
+
}, [component]);
|
|
35662
35681
|
React__default["default"].useEffect(function () {
|
|
35663
|
-
|
|
35664
|
-
|
|
35682
|
+
contentHeight.addListener(function (_ref2) {
|
|
35683
|
+
var value = _ref2.value;
|
|
35684
|
+
if (value > 0 && value !== currentContentHeight.current) {
|
|
35685
|
+
currentContentHeight.current = value;
|
|
35686
|
+
}
|
|
35687
|
+
});
|
|
35688
|
+
layoutHeight.addListener(function (_ref3) {
|
|
35689
|
+
var value = _ref3.value;
|
|
35690
|
+
if (value > 0 && value !== currentLayoutHeight.current) {
|
|
35691
|
+
currentLayoutHeight.current = value;
|
|
35692
|
+
}
|
|
35693
|
+
});
|
|
35694
|
+
// Listen to ScrollView's contentOffsetY value
|
|
35695
|
+
contentOffsetY.addListener(function (_ref4) {
|
|
35696
|
+
var value = _ref4.value;
|
|
35697
|
+
if (value < 0 ||
|
|
35698
|
+
// Prevent calling the function if the scroll is not significant
|
|
35699
|
+
value > 0 && Math.abs(value - lastScrollY.current) < 5) {
|
|
35700
|
+
return;
|
|
35701
|
+
}
|
|
35702
|
+
// Scroll up to top, bouncing included.
|
|
35703
|
+
if (value === 0 && lastScrollY.current !== 0) {
|
|
35704
|
+
animateFab('show');
|
|
35705
|
+
}
|
|
35706
|
+
var newScrollDirection = value >= lastScrollY.current ? 'down' : 'up';
|
|
35707
|
+
if (newScrollDirection !== currentScrollDirection.current) {
|
|
35708
|
+
// If scroll direction changes, reset all values
|
|
35709
|
+
currentScrollDirection.current = newScrollDirection;
|
|
35710
|
+
remainingScrollOffset.current = MAX_ANIMATABLE_SCROLL_DISTANCE;
|
|
35711
|
+
}
|
|
35712
|
+
var hasReachedBottom = value + currentLayoutHeight.current >= currentContentHeight.current;
|
|
35713
|
+
// Scroll down to bottom, bouncing included.
|
|
35714
|
+
if (hasReachedBottom) {
|
|
35715
|
+
animateFab('hide');
|
|
35716
|
+
return;
|
|
35717
|
+
}
|
|
35718
|
+
if (remainingScrollOffset.current) {
|
|
35719
|
+
var offsetDiff = Math.round(Math.max(Math.abs(value - lastScrollY.current), 0));
|
|
35720
|
+
var newRemainingScrollOffset = Math.max(remainingScrollOffset.current - offsetDiff, 0);
|
|
35721
|
+
if (newRemainingScrollOffset <= LAST_BREAKPOINT) {
|
|
35722
|
+
animateFab(currentScrollDirection.current === 'down' ? 'hide' : 'show');
|
|
35723
|
+
} else if (newRemainingScrollOffset <= MIDDLE_BREAKPOINT) {
|
|
35724
|
+
animateFab('collapse');
|
|
35725
|
+
}
|
|
35726
|
+
remainingScrollOffset.current = newRemainingScrollOffset;
|
|
35727
|
+
}
|
|
35728
|
+
lastScrollY.current = value;
|
|
35729
|
+
});
|
|
35730
|
+
return function () {
|
|
35731
|
+
contentOffsetY.removeAllListeners();
|
|
35732
|
+
contentHeight.removeAllListeners();
|
|
35733
|
+
layoutHeight.removeAllListeners();
|
|
35734
|
+
};
|
|
35735
|
+
}, [contentHeight, contentOffsetY, layoutHeight]);
|
|
35665
35736
|
return component === 'FAB' ? /*#__PURE__*/React__default["default"].createElement(FAB, _extends$1({
|
|
35666
35737
|
ref: ref
|
|
35667
35738
|
}, fabProps)) : /*#__PURE__*/React__default["default"].createElement(ActionGroup, _extends$1({
|
|
@@ -35673,6 +35744,8 @@ function AnimatedScroller(_ref) {
|
|
|
35673
35744
|
var ScrollComponent = _ref.ScrollComponent,
|
|
35674
35745
|
fabProps = _ref.fabProps;
|
|
35675
35746
|
var contentOffsetY = React__default["default"].useRef(new reactNative.Animated.Value(0)).current;
|
|
35747
|
+
var contentHeight = React__default["default"].useRef(new reactNative.Animated.Value(0)).current;
|
|
35748
|
+
var layoutHeight = React__default["default"].useRef(new reactNative.Animated.Value(0)).current;
|
|
35676
35749
|
// Common props for all ScrollView, FlatList and SectionList.
|
|
35677
35750
|
var _ScrollComponent$prop = ScrollComponent.props,
|
|
35678
35751
|
onScroll = _ScrollComponent$prop.onScroll,
|
|
@@ -35683,6 +35756,12 @@ function AnimatedScroller(_ref) {
|
|
|
35683
35756
|
nativeEvent: {
|
|
35684
35757
|
contentOffset: {
|
|
35685
35758
|
y: contentOffsetY
|
|
35759
|
+
},
|
|
35760
|
+
contentSize: {
|
|
35761
|
+
height: contentHeight
|
|
35762
|
+
},
|
|
35763
|
+
layoutMeasurement: {
|
|
35764
|
+
height: layoutHeight
|
|
35686
35765
|
}
|
|
35687
35766
|
}
|
|
35688
35767
|
}], {
|
|
@@ -35691,7 +35770,9 @@ function AnimatedScroller(_ref) {
|
|
|
35691
35770
|
})
|
|
35692
35771
|
})), !!fabProps && /*#__PURE__*/React__default["default"].createElement(AnimatedFAB, {
|
|
35693
35772
|
fabProps: fabProps,
|
|
35694
|
-
contentOffsetY: contentOffsetY
|
|
35773
|
+
contentOffsetY: contentOffsetY,
|
|
35774
|
+
contentHeight: contentHeight,
|
|
35775
|
+
layoutHeight: layoutHeight
|
|
35695
35776
|
}));
|
|
35696
35777
|
}
|
|
35697
35778
|
|
package/package.json
CHANGED
|
@@ -7,8 +7,9 @@ import ActionGroup, {
|
|
|
7
7
|
} from '../FAB/ActionGroup';
|
|
8
8
|
import { FABHandles, FABProps } from '../FAB/FAB';
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const LAST_BREAKPOINT = 100;
|
|
11
|
+
const MIDDLE_BREAKPOINT = 250;
|
|
12
|
+
const MAX_ANIMATABLE_SCROLL_DISTANCE = 400;
|
|
12
13
|
const REF_ACTIONS_BY_COMPONENT = {
|
|
13
14
|
FAB: {
|
|
14
15
|
show: 'show',
|
|
@@ -25,25 +26,36 @@ const REF_ACTIONS_BY_COMPONENT = {
|
|
|
25
26
|
interface AnimatedFABProps {
|
|
26
27
|
fabProps: FABProps | ActionGroupProps;
|
|
27
28
|
contentOffsetY: Animated.Value;
|
|
29
|
+
contentHeight: Animated.Value;
|
|
30
|
+
layoutHeight: Animated.Value;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
const AnimatedFAB = ({
|
|
33
|
+
const AnimatedFAB = ({
|
|
34
|
+
fabProps,
|
|
35
|
+
contentOffsetY,
|
|
36
|
+
contentHeight,
|
|
37
|
+
layoutHeight,
|
|
38
|
+
}: AnimatedFABProps) => {
|
|
31
39
|
const component = 'items' in fabProps ? 'ActionGroup' : 'FAB';
|
|
32
|
-
const [currentScrollDirection, setCurrentScrollDirection] = React.useState<
|
|
33
|
-
'up' | 'down'
|
|
34
|
-
>('down');
|
|
35
|
-
const [lastScrollY, setLastScrollY] = React.useState(0);
|
|
36
|
-
const [fabState, setFabState] = React.useState<'show' | 'hide' | 'collapse'>(
|
|
37
|
-
'show'
|
|
38
|
-
);
|
|
39
|
-
const [remainingScrollOffset, setRemainingScrollOffset] = React.useState(
|
|
40
|
-
SHOW_AND_HIDE_BREAKPOINT
|
|
41
|
-
);
|
|
42
40
|
const ref = React.useRef<FABHandles & ActionGroupHandles>(null);
|
|
41
|
+
const currentContentHeight = React.useRef(0);
|
|
42
|
+
const currentLayoutHeight = React.useRef(0);
|
|
43
|
+
|
|
44
|
+
/** fabState is used to avoid calling duplicated animations. */
|
|
45
|
+
const fabState = React.useRef<'show' | 'hide' | 'collapse'>('show');
|
|
46
|
+
|
|
47
|
+
/** remainingScrollOffset determines whether to animate the FAB. */
|
|
48
|
+
const remainingScrollOffset = React.useRef(MAX_ANIMATABLE_SCROLL_DISTANCE);
|
|
49
|
+
|
|
50
|
+
/** currentScrollDirection is used to determine the scroll direction. */
|
|
51
|
+
const currentScrollDirection = React.useRef<'up' | 'down'>('down');
|
|
52
|
+
|
|
53
|
+
/** lastScrollY is the scrollY from the preview scroll event. */
|
|
54
|
+
const lastScrollY = React.useRef(0);
|
|
43
55
|
|
|
44
56
|
const animateFab = React.useCallback(
|
|
45
57
|
(newState: 'show' | 'hide' | 'collapse') => {
|
|
46
|
-
if (fabState !== newState) {
|
|
58
|
+
if (fabState.current !== newState) {
|
|
47
59
|
if (newState === 'show') {
|
|
48
60
|
ref.current?.[REF_ACTIONS_BY_COMPONENT[component].show]();
|
|
49
61
|
} else if (newState === 'hide') {
|
|
@@ -51,49 +63,87 @@ const AnimatedFAB = ({ fabProps, contentOffsetY }: AnimatedFABProps) => {
|
|
|
51
63
|
} else {
|
|
52
64
|
ref.current?.[REF_ACTIONS_BY_COMPONENT[component].collapse]();
|
|
53
65
|
}
|
|
54
|
-
|
|
66
|
+
fabState.current = newState;
|
|
55
67
|
}
|
|
56
68
|
},
|
|
57
|
-
[
|
|
69
|
+
[component]
|
|
58
70
|
);
|
|
59
71
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (offsetDiff === SHOW_AND_HIDE_BREAKPOINT) {
|
|
80
|
-
animateFab(isScrollingDown ? 'show' : 'hide');
|
|
81
|
-
} else if (
|
|
82
|
-
offsetDiff <= SHOW_AND_HIDE_BREAKPOINT &&
|
|
83
|
-
offsetDiff > COLLAPSE_BREAKPOINT
|
|
72
|
+
React.useEffect(() => {
|
|
73
|
+
contentHeight.addListener(({ value }) => {
|
|
74
|
+
if (value > 0 && value !== currentContentHeight.current) {
|
|
75
|
+
currentContentHeight.current = value;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
layoutHeight.addListener(({ value }) => {
|
|
80
|
+
if (value > 0 && value !== currentLayoutHeight.current) {
|
|
81
|
+
currentLayoutHeight.current = value;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Listen to ScrollView's contentOffsetY value
|
|
86
|
+
contentOffsetY.addListener(({ value }) => {
|
|
87
|
+
if (
|
|
88
|
+
value < 0 ||
|
|
89
|
+
// Prevent calling the function if the scroll is not significant
|
|
90
|
+
(value > 0 && Math.abs(value - lastScrollY.current) < 5)
|
|
84
91
|
) {
|
|
85
|
-
|
|
86
|
-
} else if (offsetDiff <= COLLAPSE_BREAKPOINT) {
|
|
87
|
-
animateFab(isScrollingDown ? 'hide' : 'show');
|
|
92
|
+
return;
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
// Scroll up to top, bouncing included.
|
|
96
|
+
if (value === 0 && lastScrollY.current !== 0) {
|
|
97
|
+
animateFab('show');
|
|
98
|
+
}
|
|
93
99
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
const newScrollDirection = value >= lastScrollY.current ? 'down' : 'up';
|
|
101
|
+
|
|
102
|
+
if (newScrollDirection !== currentScrollDirection.current) {
|
|
103
|
+
// If scroll direction changes, reset all values
|
|
104
|
+
currentScrollDirection.current = newScrollDirection;
|
|
105
|
+
remainingScrollOffset.current = MAX_ANIMATABLE_SCROLL_DISTANCE;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const hasReachedBottom =
|
|
109
|
+
value + currentLayoutHeight.current >= currentContentHeight.current;
|
|
110
|
+
|
|
111
|
+
// Scroll down to bottom, bouncing included.
|
|
112
|
+
if (hasReachedBottom) {
|
|
113
|
+
animateFab('hide');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (remainingScrollOffset.current) {
|
|
118
|
+
const offsetDiff = Math.round(
|
|
119
|
+
Math.max(Math.abs(value - lastScrollY.current), 0)
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const newRemainingScrollOffset = Math.max(
|
|
123
|
+
remainingScrollOffset.current - offsetDiff,
|
|
124
|
+
0
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
if (newRemainingScrollOffset <= LAST_BREAKPOINT) {
|
|
128
|
+
animateFab(
|
|
129
|
+
currentScrollDirection.current === 'down' ? 'hide' : 'show'
|
|
130
|
+
);
|
|
131
|
+
} else if (newRemainingScrollOffset <= MIDDLE_BREAKPOINT) {
|
|
132
|
+
animateFab('collapse');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
remainingScrollOffset.current = newRemainingScrollOffset;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
lastScrollY.current = value;
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return () => {
|
|
142
|
+
contentOffsetY.removeAllListeners();
|
|
143
|
+
contentHeight.removeAllListeners();
|
|
144
|
+
layoutHeight.removeAllListeners();
|
|
145
|
+
};
|
|
146
|
+
}, [contentHeight, contentOffsetY, layoutHeight]);
|
|
97
147
|
|
|
98
148
|
return component === 'FAB' ? (
|
|
99
149
|
<FAB ref={ref} {...(fabProps as FABProps)} />
|