react-native-ui-lib 8.3.4 → 8.4.0-snapshot.7832
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/lib/android/build.gradle +3 -3
- package/package.json +1 -2
- package/screenFooter.d.ts +2 -0
- package/screenFooter.js +1 -0
- package/scripts/release/prReleaseNotesCommon.js +2 -1
- package/src/assets/internal/images/bottomGradient.png +0 -0
- package/src/assets/internal/images/bottomGradient@1.5x.png +0 -0
- package/src/assets/internal/images/bottomGradient@2x.png +0 -0
- package/src/assets/internal/images/bottomGradient@3x.png +0 -0
- package/src/assets/internal/images/bottomGradient@4x.png +0 -0
- package/src/assets/internal/images/index.js +3 -0
- package/src/components/screenFooter/index.d.ts +8 -0
- package/src/components/screenFooter/index.js +211 -0
- package/src/components/screenFooter/screenFooter.api.json +262 -0
- package/src/components/screenFooter/types.d.ts +100 -0
- package/src/components/screenFooter/types.js +39 -0
- package/src/components/slider/Thumb.d.ts +5 -1
- package/src/components/slider/Thumb.js +2 -1
- package/src/components/slider/index.d.ts +1 -0
- package/src/components/slider/index.js +69 -11
- package/src/components/slider/slider.api.json +6 -0
- package/src/components/slider/types.d.ts +5 -0
- package/src/hooks/index.d.ts +1 -0
- package/src/hooks/index.js +1 -0
- package/src/hooks/useScrollToHide/index.d.ts +24 -0
- package/src/hooks/useScrollToHide/index.js +48 -0
- package/src/incubator/expandableOverlay/index.js +7 -3
- package/src/incubator/slider/Thumb.d.ts +1 -0
- package/src/incubator/slider/Thumb.js +9 -6
- package/src/incubator/slider/index.d.ts +5 -0
- package/src/incubator/slider/index.js +47 -4
- package/src/index.d.ts +1 -0
- package/src/index.js +70 -0
- package/src/style/colors.d.ts +12 -13
- package/src/style/colors.js +40 -39
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export let ScreenFooterLayouts = /*#__PURE__*/function (ScreenFooterLayouts) {
|
|
2
|
+
ScreenFooterLayouts["HORIZONTAL"] = "horizontal";
|
|
3
|
+
ScreenFooterLayouts["VERTICAL"] = "vertical";
|
|
4
|
+
return ScreenFooterLayouts;
|
|
5
|
+
}({});
|
|
6
|
+
export let ScreenFooterBackgrounds = /*#__PURE__*/function (ScreenFooterBackgrounds) {
|
|
7
|
+
ScreenFooterBackgrounds["FADING"] = "fading";
|
|
8
|
+
ScreenFooterBackgrounds["SOLID"] = "solid";
|
|
9
|
+
ScreenFooterBackgrounds["TRANSPARENT"] = "transparent";
|
|
10
|
+
return ScreenFooterBackgrounds;
|
|
11
|
+
}({});
|
|
12
|
+
export let FooterAlignment = /*#__PURE__*/function (FooterAlignment) {
|
|
13
|
+
FooterAlignment["START"] = "start";
|
|
14
|
+
FooterAlignment["CENTER"] = "center";
|
|
15
|
+
FooterAlignment["END"] = "end";
|
|
16
|
+
return FooterAlignment;
|
|
17
|
+
}({});
|
|
18
|
+
export let HorizontalItemsDistribution = /*#__PURE__*/function (HorizontalItemsDistribution) {
|
|
19
|
+
HorizontalItemsDistribution["STACK"] = "stack";
|
|
20
|
+
HorizontalItemsDistribution["SPREAD"] = "spread";
|
|
21
|
+
return HorizontalItemsDistribution;
|
|
22
|
+
}({});
|
|
23
|
+
export let ItemsFit = /*#__PURE__*/function (ItemsFit) {
|
|
24
|
+
ItemsFit["FIT"] = "fit";
|
|
25
|
+
ItemsFit["STRETCH"] = "stretch";
|
|
26
|
+
ItemsFit["FIXED"] = "fixed";
|
|
27
|
+
return ItemsFit;
|
|
28
|
+
}({});
|
|
29
|
+
export let KeyboardBehavior = /*#__PURE__*/function (KeyboardBehavior) {
|
|
30
|
+
KeyboardBehavior["STICKY"] = "sticky";
|
|
31
|
+
KeyboardBehavior["HOISTED"] = "hoisted";
|
|
32
|
+
return KeyboardBehavior;
|
|
33
|
+
}({});
|
|
34
|
+
export let ScreenFooterShadow = /*#__PURE__*/function (ScreenFooterShadow) {
|
|
35
|
+
ScreenFooterShadow["SH10"] = "sh10";
|
|
36
|
+
ScreenFooterShadow["SH20"] = "sh20";
|
|
37
|
+
ScreenFooterShadow["SH30"] = "sh30";
|
|
38
|
+
return ScreenFooterShadow;
|
|
39
|
+
}({});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ViewStyle, ViewProps, View as RNView } from 'react-native';
|
|
2
|
+
import { ViewStyle, ViewProps, View as RNView, Animated } from 'react-native';
|
|
3
3
|
export interface ThumbProps extends ViewProps {
|
|
4
4
|
/**
|
|
5
5
|
* The thumb style
|
|
@@ -27,6 +27,10 @@ export interface ThumbProps extends ViewProps {
|
|
|
27
27
|
disabled?: boolean;
|
|
28
28
|
/** ref to thumb component */
|
|
29
29
|
ref?: React.RefObject<RNView>;
|
|
30
|
+
/**
|
|
31
|
+
* External scale animation value (used by useRelativeDrag)
|
|
32
|
+
*/
|
|
33
|
+
scaleAnimation?: Animated.Value;
|
|
30
34
|
}
|
|
31
35
|
declare const Thumb: React.ForwardRefExoticComponent<Omit<ThumbProps, "ref"> & React.RefAttributes<unknown>>;
|
|
32
36
|
export default Thumb;
|
|
@@ -18,6 +18,7 @@ const Thumb = forwardRef((props, ref) => {
|
|
|
18
18
|
thumbHitSlop,
|
|
19
19
|
onTouchStart,
|
|
20
20
|
onTouchEnd,
|
|
21
|
+
scaleAnimation,
|
|
21
22
|
...others
|
|
22
23
|
} = props;
|
|
23
24
|
const thumbRef = useCombinedRefs(ref);
|
|
@@ -82,7 +83,7 @@ const Thumb = forwardRef((props, ref) => {
|
|
|
82
83
|
backgroundColor: disabled ? DEFAULT_COLOR : thumbTintColor || ACTIVE_COLOR
|
|
83
84
|
}, {
|
|
84
85
|
transform: [{
|
|
85
|
-
scale: thumbScaleAnimation.current
|
|
86
|
+
scale: scaleAnimation ?? thumbScaleAnimation.current
|
|
86
87
|
}]
|
|
87
88
|
}]} />;
|
|
88
89
|
});
|
|
@@ -23,6 +23,7 @@ declare const _default: React.ForwardRefExoticComponent<Omit<import("./Thumb").T
|
|
|
23
23
|
disableRTL?: boolean | undefined;
|
|
24
24
|
accessible?: boolean | undefined;
|
|
25
25
|
testID?: string | undefined;
|
|
26
|
+
useRelativeDrag?: boolean | undefined;
|
|
26
27
|
migrate?: boolean | undefined;
|
|
27
28
|
enableThumbShadow?: boolean | undefined;
|
|
28
29
|
disabledThumbTintColor?: string | undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import _isFunction from "lodash/isFunction";
|
|
2
|
+
import _clamp from "lodash/clamp";
|
|
2
3
|
import React, { PureComponent } from 'react';
|
|
3
4
|
import { StyleSheet, PanResponder, AccessibilityInfo, Animated } from 'react-native';
|
|
4
5
|
import { Constants, asBaseComponent } from "../../commons/new";
|
|
@@ -55,6 +56,8 @@ class Slider extends PureComponent {
|
|
|
55
56
|
width: THUMB_SIZE,
|
|
56
57
|
height: THUMB_SIZE
|
|
57
58
|
};
|
|
59
|
+
_containerDragInitialValue = 0;
|
|
60
|
+
_containerThumbScale = new Animated.Value(1);
|
|
58
61
|
constructor(props) {
|
|
59
62
|
super(props);
|
|
60
63
|
this.activeThumbRef = this.thumb;
|
|
@@ -85,6 +88,15 @@ class Slider extends PureComponent {
|
|
|
85
88
|
onPanResponderEnd: () => true,
|
|
86
89
|
onPanResponderTerminationRequest: () => false
|
|
87
90
|
});
|
|
91
|
+
this.containerPanResponder = PanResponder.create({
|
|
92
|
+
onMoveShouldSetPanResponder: this.handleMoveShouldSetPanResponder,
|
|
93
|
+
onPanResponderGrant: this.handleContainerGrant,
|
|
94
|
+
onPanResponderMove: this.handleContainerMove,
|
|
95
|
+
onPanResponderRelease: this.handleContainerEnd,
|
|
96
|
+
onStartShouldSetPanResponder: () => true,
|
|
97
|
+
onPanResponderEnd: () => true,
|
|
98
|
+
onPanResponderTerminationRequest: () => false
|
|
99
|
+
});
|
|
88
100
|
}
|
|
89
101
|
reset() {
|
|
90
102
|
// NOTE: used with ref
|
|
@@ -203,6 +215,41 @@ class Slider extends PureComponent {
|
|
|
203
215
|
this.bounceToStep();
|
|
204
216
|
this.onSeekEnd();
|
|
205
217
|
};
|
|
218
|
+
handleContainerGrant = () => {
|
|
219
|
+
this._containerDragInitialValue = this.getValueForX(this._x);
|
|
220
|
+
this.animateContainerThumbScale(1.5);
|
|
221
|
+
this.onSeekStart();
|
|
222
|
+
};
|
|
223
|
+
handleContainerMove = (_e, gestureState) => {
|
|
224
|
+
if (this.props.disabled) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const {
|
|
228
|
+
minimumValue,
|
|
229
|
+
maximumValue
|
|
230
|
+
} = this.props;
|
|
231
|
+
const range = this.getRange();
|
|
232
|
+
const containerWidth = this.state.containerSize.width;
|
|
233
|
+
const dx = gestureState.dx * (Constants.isRTL && !this.disableRTL ? -1 : 1);
|
|
234
|
+
const deltaValue = dx / containerWidth * range;
|
|
235
|
+
const newValue = _clamp(this._containerDragInitialValue + deltaValue, minimumValue, maximumValue);
|
|
236
|
+
const newX = this.getXForValue(newValue);
|
|
237
|
+
this.set_x(newX);
|
|
238
|
+
this.moveTo(newX);
|
|
239
|
+
this.updateValue(newX);
|
|
240
|
+
};
|
|
241
|
+
handleContainerEnd = () => {
|
|
242
|
+
this.bounceToStep();
|
|
243
|
+
this.animateContainerThumbScale(1);
|
|
244
|
+
this.onSeekEnd();
|
|
245
|
+
};
|
|
246
|
+
animateContainerThumbScale = toValue => {
|
|
247
|
+
Animated.timing(this._containerThumbScale, {
|
|
248
|
+
toValue,
|
|
249
|
+
duration: 100,
|
|
250
|
+
useNativeDriver: true
|
|
251
|
+
}).start();
|
|
252
|
+
};
|
|
206
253
|
|
|
207
254
|
/* Actions */
|
|
208
255
|
|
|
@@ -463,6 +510,9 @@ class Slider extends PureComponent {
|
|
|
463
510
|
onThumbLayout = nativeEvent => {
|
|
464
511
|
this.handleMeasure('thumbSize', nativeEvent);
|
|
465
512
|
};
|
|
513
|
+
handleContainerShouldSetResponder = () => {
|
|
514
|
+
return !this.props.disabled;
|
|
515
|
+
};
|
|
466
516
|
handleTrackPress = event => {
|
|
467
517
|
if (this.props.disabled) {
|
|
468
518
|
return;
|
|
@@ -536,7 +586,8 @@ class Slider extends PureComponent {
|
|
|
536
586
|
disableActiveStyling,
|
|
537
587
|
disabled,
|
|
538
588
|
thumbTintColor,
|
|
539
|
-
thumbHitSlop
|
|
589
|
+
thumbHitSlop,
|
|
590
|
+
useRelativeDrag
|
|
540
591
|
} = this.props;
|
|
541
592
|
const {
|
|
542
593
|
thumbSize
|
|
@@ -556,16 +607,23 @@ class Slider extends PureComponent {
|
|
|
556
607
|
activeThumbStyle,
|
|
557
608
|
disableActiveStyling,
|
|
558
609
|
thumbHitSlop: thumbHitSlop ?? calculatedHitSlop,
|
|
559
|
-
onLayout: this.onThumbLayout
|
|
610
|
+
onLayout: this.onThumbLayout,
|
|
611
|
+
scaleAnimation: useRelativeDrag ? this._containerThumbScale : undefined
|
|
560
612
|
};
|
|
561
613
|
};
|
|
562
614
|
|
|
563
615
|
/* Renders */
|
|
564
616
|
renderMinThumb = () => {
|
|
565
|
-
|
|
617
|
+
const {
|
|
618
|
+
useRelativeDrag
|
|
619
|
+
} = this.props;
|
|
620
|
+
return <Thumb {...this.getThumbProps()} ref={this.minThumb} onTouchStart={useRelativeDrag ? undefined : this.onMinTouchStart} pointerEvents={useRelativeDrag ? 'none' : undefined} {...useRelativeDrag ? {} : this.panResponder.panHandlers} />;
|
|
566
621
|
};
|
|
567
622
|
renderThumb = () => {
|
|
568
|
-
|
|
623
|
+
const {
|
|
624
|
+
useRelativeDrag
|
|
625
|
+
} = this.props;
|
|
626
|
+
return <Thumb {...this.getThumbProps()} ref={this.thumb} onTouchStart={useRelativeDrag ? undefined : this.onTouchStart} pointerEvents={useRelativeDrag ? 'none' : undefined} {...useRelativeDrag ? {} : this.panResponder.panHandlers} />;
|
|
569
627
|
};
|
|
570
628
|
renderTrack() {
|
|
571
629
|
const {
|
|
@@ -607,14 +665,18 @@ class Slider extends PureComponent {
|
|
|
607
665
|
const {
|
|
608
666
|
containerStyle,
|
|
609
667
|
testID,
|
|
610
|
-
migrate
|
|
668
|
+
migrate,
|
|
669
|
+
useRelativeDrag
|
|
611
670
|
} = this.props;
|
|
612
671
|
if (migrate) {
|
|
613
672
|
return <IncubatorSlider {...this.props} />;
|
|
614
673
|
}
|
|
615
|
-
|
|
674
|
+
const containerGestureProps = useRelativeDrag ? this.containerPanResponder.panHandlers : {
|
|
675
|
+
onStartShouldSetResponder: this.handleContainerShouldSetResponder,
|
|
676
|
+
onResponderRelease: this.handleTrackPress
|
|
677
|
+
};
|
|
678
|
+
return <View style={[styles.container, containerStyle]} onLayout={this.onContainerLayout} onAccessibilityAction={this.onAccessibilityAction} testID={testID} {...containerGestureProps} {...this.getAccessibilityProps()}>
|
|
616
679
|
{this.renderTrack()}
|
|
617
|
-
<View style={styles.touchArea} onTouchEnd={this.handleTrackPress} />
|
|
618
680
|
{this.renderRangeThumb()}
|
|
619
681
|
{this.renderThumb()}
|
|
620
682
|
</View>;
|
|
@@ -636,9 +698,5 @@ const styles = StyleSheet.create({
|
|
|
636
698
|
},
|
|
637
699
|
trackDisableRTL: {
|
|
638
700
|
right: 0
|
|
639
|
-
},
|
|
640
|
-
touchArea: {
|
|
641
|
-
...StyleSheet.absoluteFillObject,
|
|
642
|
-
backgroundColor: 'transparent'
|
|
643
701
|
}
|
|
644
702
|
});
|
|
@@ -140,6 +140,12 @@
|
|
|
140
140
|
"type": "string",
|
|
141
141
|
"description": "The component test id"
|
|
142
142
|
},
|
|
143
|
+
{
|
|
144
|
+
"name": "useRelativeDrag",
|
|
145
|
+
"type": "boolean",
|
|
146
|
+
"description": "If true, dragging anywhere on the slider moves the thumb relative to its current position instead of snapping to the touch point. Designed for single-thumb mode.",
|
|
147
|
+
"default": "false"
|
|
148
|
+
},
|
|
143
149
|
{
|
|
144
150
|
"name": "migrate",
|
|
145
151
|
"type": "boolean",
|
|
@@ -97,6 +97,11 @@ export type SliderProps = Omit<ThumbProps, 'ref'> & {
|
|
|
97
97
|
* The slider's test identifier
|
|
98
98
|
*/
|
|
99
99
|
testID?: string;
|
|
100
|
+
/**
|
|
101
|
+
* If true, dragging anywhere on the slider moves the thumb relative to its current position
|
|
102
|
+
* instead of snapping to the touch point. Designed for single-thumb mode.
|
|
103
|
+
*/
|
|
104
|
+
useRelativeDrag?: boolean;
|
|
100
105
|
/**
|
|
101
106
|
* Whether to use the new Slider implementation using Reanimated
|
|
102
107
|
*/
|
package/src/hooks/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { default as useModifiers } from './useModifiers';
|
|
|
6
6
|
export { default as useOrientation } from './useOrientation';
|
|
7
7
|
export { default as useScrollEnabler } from './useScrollEnabler';
|
|
8
8
|
export { default as useScrollReached } from './useScrollReached';
|
|
9
|
+
export { default as useScrollToHide } from './useScrollToHide';
|
|
9
10
|
export { default as useScrollToItem } from './useScrollToItem';
|
|
10
11
|
export { default as useScrollTo } from './useScrollTo';
|
|
11
12
|
export { default as useThemeProps } from './useThemeProps';
|
package/src/hooks/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { default as useModifiers } from "./useModifiers";
|
|
|
6
6
|
export { default as useOrientation } from "./useOrientation";
|
|
7
7
|
export { default as useScrollEnabler } from "./useScrollEnabler";
|
|
8
8
|
export { default as useScrollReached } from "./useScrollReached";
|
|
9
|
+
export { default as useScrollToHide } from "./useScrollToHide";
|
|
9
10
|
export { default as useScrollToItem } from "./useScrollToItem";
|
|
10
11
|
export { default as useScrollTo } from "./useScrollTo";
|
|
11
12
|
export { default as useThemeProps } from "./useThemeProps";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
|
|
2
|
+
export type ScrollToHideProps = {
|
|
3
|
+
/**
|
|
4
|
+
* The threshold (in pixels) to ignore small scroll movements before updating visibility.
|
|
5
|
+
* Defaults to 0 (immediate reaction).
|
|
6
|
+
*/
|
|
7
|
+
scrollingThreshold?: number;
|
|
8
|
+
};
|
|
9
|
+
export type ScrollToHideResult = {
|
|
10
|
+
/**
|
|
11
|
+
* onScroll callback (should be passed to your ScrollView/FlatList onScroll prop).
|
|
12
|
+
*/
|
|
13
|
+
onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the footer should be visible based on scroll direction.
|
|
16
|
+
*/
|
|
17
|
+
visible: boolean;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* @description: A hook that tracks scroll direction to toggle visibility (e.g., for hiding a footer on scroll).
|
|
21
|
+
* @example: const {onScroll, visible} = useScrollToHide();
|
|
22
|
+
*/
|
|
23
|
+
declare const useScrollToHide: (props?: ScrollToHideProps) => ScrollToHideResult;
|
|
24
|
+
export default useScrollToHide;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useState, useCallback, useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* @description: A hook that tracks scroll direction to toggle visibility (e.g., for hiding a footer on scroll).
|
|
4
|
+
* @example: const {onScroll, visible} = useScrollToHide();
|
|
5
|
+
*/
|
|
6
|
+
const useScrollToHide = (props = {}) => {
|
|
7
|
+
const {
|
|
8
|
+
scrollingThreshold = 0
|
|
9
|
+
} = props;
|
|
10
|
+
const [visible, setVisible] = useState(true);
|
|
11
|
+
const prevContentOffset = useRef(0);
|
|
12
|
+
const onScroll = useCallback(event => {
|
|
13
|
+
const {
|
|
14
|
+
nativeEvent: {
|
|
15
|
+
contentOffset: {
|
|
16
|
+
y: currentOffset
|
|
17
|
+
},
|
|
18
|
+
contentSize: {
|
|
19
|
+
height: contentHeight
|
|
20
|
+
},
|
|
21
|
+
layoutMeasurement: {
|
|
22
|
+
height: layoutHeight
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
} = event;
|
|
26
|
+
|
|
27
|
+
// Ignore bounces (iOS)
|
|
28
|
+
if (currentOffset < 0 || currentOffset > contentHeight - layoutHeight) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const diff = currentOffset - prevContentOffset.current;
|
|
32
|
+
if (Math.abs(diff) > scrollingThreshold) {
|
|
33
|
+
if (diff > 0 && visible) {
|
|
34
|
+
// Scrolling Down -> Hide
|
|
35
|
+
setVisible(false);
|
|
36
|
+
} else if (diff < 0 && !visible) {
|
|
37
|
+
// Scrolling Up -> Show
|
|
38
|
+
setVisible(true);
|
|
39
|
+
}
|
|
40
|
+
prevContentOffset.current = currentOffset;
|
|
41
|
+
}
|
|
42
|
+
}, [visible, scrollingThreshold]);
|
|
43
|
+
return {
|
|
44
|
+
onScroll,
|
|
45
|
+
visible
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export default useScrollToHide;
|
|
@@ -33,11 +33,15 @@ const ExpandableOverlay = (props, ref) => {
|
|
|
33
33
|
setExpandableVisible(true);
|
|
34
34
|
onPress?.(props);
|
|
35
35
|
}, [onPress, customValue]);
|
|
36
|
-
const
|
|
36
|
+
const dismissOverlay = useCallback(() => {
|
|
37
37
|
setExpandableVisible(false);
|
|
38
38
|
focusAccessibility();
|
|
39
|
+
}, [focusAccessibility]);
|
|
40
|
+
const closeExpandable = useCallback(() => {
|
|
41
|
+
dismissOverlay();
|
|
39
42
|
useDialog ? dialogProps?.onDismiss?.() : modalProps?.onDismiss?.();
|
|
40
|
-
|
|
43
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
44
|
+
}, [dismissOverlay, dialogProps?.onDismiss, modalProps?.onDismiss]);
|
|
41
45
|
const toggleExpandable = useCallback(() => visible ? closeExpandable() : openExpandable(), [visible, openExpandable, closeExpandable]);
|
|
42
46
|
useImperativeHandle(ref, () => ({
|
|
43
47
|
openExpandable,
|
|
@@ -45,7 +49,7 @@ const ExpandableOverlay = (props, ref) => {
|
|
|
45
49
|
toggleExpandable
|
|
46
50
|
}));
|
|
47
51
|
const renderModal = () => {
|
|
48
|
-
return <Modal testID={`${testID}.overlay`} overlayBackgroundColor={Colors.$backgroundDefault} {...modalProps} visible={visible} onDismiss={
|
|
52
|
+
return <Modal testID={`${testID}.overlay`} overlayBackgroundColor={Colors.$backgroundDefault} {...modalProps} visible={visible} onDismiss={dismissOverlay} onRequestClose={closeExpandable} onBackgroundPress={closeExpandable}>
|
|
49
53
|
{showTopBar && <Modal.TopBar onDone={closeExpandable} {...topBarProps} />}
|
|
50
54
|
{expandableContent}
|
|
51
55
|
</Modal>;
|
|
@@ -31,7 +31,9 @@ const Thumb = props => {
|
|
|
31
31
|
stepInterpolatedValue,
|
|
32
32
|
gap = 0,
|
|
33
33
|
secondary,
|
|
34
|
-
enableShadow
|
|
34
|
+
enableShadow,
|
|
35
|
+
pointerEvents,
|
|
36
|
+
isActive
|
|
35
37
|
} = props;
|
|
36
38
|
const rtlFix = Constants.isRTL ? -1 : 1;
|
|
37
39
|
const isPressed = useSharedValue(false);
|
|
@@ -40,7 +42,7 @@ const Thumb = props => {
|
|
|
40
42
|
height: THUMB_SIZE
|
|
41
43
|
});
|
|
42
44
|
const lastOffset = useSharedValue(0);
|
|
43
|
-
const gesture = Gesture.Pan().onBegin(() => {
|
|
45
|
+
const gesture = Gesture.Pan().hitSlop(hitSlop ?? DEFAULT_THUMB_HIT_SLOP).onBegin(() => {
|
|
44
46
|
onSeekStart?.();
|
|
45
47
|
isPressed.value = true;
|
|
46
48
|
lastOffset.value = offset.value;
|
|
@@ -64,15 +66,16 @@ const Thumb = props => {
|
|
|
64
66
|
offset.value = Math.round(offset.value / stepInterpolatedValue.value) * stepInterpolatedValue.value;
|
|
65
67
|
}
|
|
66
68
|
});
|
|
67
|
-
gesture.enabled(!disabled);
|
|
69
|
+
gesture.enabled(!disabled && pointerEvents !== 'none');
|
|
68
70
|
const animatedStyle = useAnimatedStyle(() => {
|
|
69
|
-
const
|
|
71
|
+
const active = isPressed.value || isActive?.value;
|
|
72
|
+
const customStyle = active ? activeStyle?.value : defaultStyle?.value;
|
|
70
73
|
return {
|
|
71
74
|
...customStyle,
|
|
72
75
|
transform: [{
|
|
73
76
|
translateX: (offset.value - thumbSize.value.width / 2) * rtlFix
|
|
74
77
|
}, {
|
|
75
|
-
scale: withSpring(!disableActiveStyling &&
|
|
78
|
+
scale: withSpring(!disableActiveStyling && active ? 1.3 : 1)
|
|
76
79
|
}]
|
|
77
80
|
};
|
|
78
81
|
});
|
|
@@ -86,7 +89,7 @@ const Thumb = props => {
|
|
|
86
89
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
87
90
|
}, []);
|
|
88
91
|
return <GestureDetector gesture={gesture}>
|
|
89
|
-
<View reanimated style={[styles.thumbPosition, enableShadow && styles.thumbShadow, animatedStyle]} hitSlop={hitSlop} onLayout={onThumbLayout} />
|
|
92
|
+
<View reanimated pointerEvents={pointerEvents} style={[styles.thumbPosition, enableShadow && styles.thumbShadow, animatedStyle]} hitSlop={hitSlop} onLayout={onThumbLayout} />
|
|
90
93
|
</GestureDetector>;
|
|
91
94
|
};
|
|
92
95
|
const styles = StyleSheet.create({
|
|
@@ -126,6 +126,11 @@ export interface SliderProps extends AccessibilityProps {
|
|
|
126
126
|
* Whether to use the new Slider implementation using Reanimated
|
|
127
127
|
*/
|
|
128
128
|
migrate?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* If true, dragging anywhere on the slider moves the thumb relative to its current position
|
|
131
|
+
* instead of snapping to the touch point. Designed for single-thumb mode.
|
|
132
|
+
*/
|
|
133
|
+
useRelativeDrag?: boolean;
|
|
129
134
|
/**
|
|
130
135
|
* Control the throttle time of the onValueChange and onRangeChange callbacks
|
|
131
136
|
*/
|
|
@@ -2,13 +2,14 @@ import _throttle from "lodash/throttle";
|
|
|
2
2
|
import React, { useImperativeHandle, useCallback, useMemo, useEffect, useRef } from 'react';
|
|
3
3
|
import { StyleSheet } from 'react-native';
|
|
4
4
|
import { useSharedValue, useAnimatedStyle, runOnJS, useAnimatedReaction, withTiming } from 'react-native-reanimated';
|
|
5
|
-
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
5
|
+
import { GestureHandlerRootView, GestureDetector, Gesture } from 'react-native-gesture-handler';
|
|
6
6
|
import { forwardRef, Constants } from "../../commons/new";
|
|
7
7
|
import { extractAccessibilityProps } from "../../commons/modifiers";
|
|
8
8
|
import { Colors, Spacings } from "../../style";
|
|
9
9
|
import { StyleUtils } from "../../utils";
|
|
10
10
|
import { useThemeProps, useDidUpdate } from "../../hooks";
|
|
11
11
|
import { validateValues, getOffsetForValue, getValueForOffset, getStepInterpolated } from "./SliderPresenter";
|
|
12
|
+
import View from "../../components/view";
|
|
12
13
|
import Thumb from "./Thumb";
|
|
13
14
|
import Track from "./Track";
|
|
14
15
|
var ThumbType = /*#__PURE__*/function (ThumbType) {
|
|
@@ -54,6 +55,7 @@ const Slider = React.memo(props => {
|
|
|
54
55
|
accessible = true,
|
|
55
56
|
testID,
|
|
56
57
|
enableThumbShadow = true,
|
|
58
|
+
useRelativeDrag,
|
|
57
59
|
throttleTime = 200
|
|
58
60
|
} = themeProps;
|
|
59
61
|
const accessibilityProps = useMemo(() => {
|
|
@@ -215,6 +217,36 @@ const Slider = React.memo(props => {
|
|
|
215
217
|
runOnJS(onSeekEnd)();
|
|
216
218
|
}
|
|
217
219
|
};
|
|
220
|
+
const containerDragStartOffset = useSharedValue(0);
|
|
221
|
+
const isContainerDragging = useSharedValue(false);
|
|
222
|
+
const clampOffset = offset => {
|
|
223
|
+
'worklet';
|
|
224
|
+
|
|
225
|
+
return Math.max(0, Math.min(trackSize.value.width, offset));
|
|
226
|
+
};
|
|
227
|
+
const snapToStep = () => {
|
|
228
|
+
'worklet';
|
|
229
|
+
|
|
230
|
+
if (shouldBounceToStep) {
|
|
231
|
+
const step = stepInterpolatedValue.value;
|
|
232
|
+
defaultThumbOffset.value = Math.round(defaultThumbOffset.value / step) * step;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const containerGesture = Gesture.Pan().onBegin(() => {
|
|
236
|
+
containerDragStartOffset.value = defaultThumbOffset.value;
|
|
237
|
+
isContainerDragging.value = true;
|
|
238
|
+
_onSeekStart();
|
|
239
|
+
}).onUpdate(e => {
|
|
240
|
+
if (trackSize.value.width === 0) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const dx = e.translationX * (shouldDisableRTL ? 1 : rtlFix);
|
|
244
|
+
defaultThumbOffset.value = clampOffset(containerDragStartOffset.value + dx);
|
|
245
|
+
}).onEnd(() => _onSeekEnd()).onFinalize(() => {
|
|
246
|
+
isContainerDragging.value = false;
|
|
247
|
+
snapToStep();
|
|
248
|
+
});
|
|
249
|
+
containerGesture.enabled(!disabled && !!useRelativeDrag);
|
|
218
250
|
const trackAnimatedStyles = useAnimatedStyle(() => {
|
|
219
251
|
if (useRange) {
|
|
220
252
|
return {
|
|
@@ -236,15 +268,22 @@ const Slider = React.memo(props => {
|
|
|
236
268
|
|
|
237
269
|
/** renders */
|
|
238
270
|
const renderThumb = type => {
|
|
239
|
-
return <Thumb start={type === ThumbType.DEFAULT ? start : defaultThumbOffset} end={type === ThumbType.DEFAULT ? rangeThumbOffset : end} offset={type === ThumbType.DEFAULT ? defaultThumbOffset : rangeThumbOffset} gap={rangeGap} secondary={type !== ThumbType.DEFAULT} onSeekStart={_onSeekStart} onSeekEnd={_onSeekEnd} shouldDisableRTL={shouldDisableRTL} disabled={disabled} disableActiveStyling={disableActiveStyling} defaultStyle={_thumbStyle} activeStyle={_activeThumbStyle} hitSlop={thumbHitSlop} shouldBounceToStep={shouldBounceToStep} stepInterpolatedValue={stepInterpolatedValue} enableShadow={enableThumbShadow} />;
|
|
271
|
+
return <Thumb start={type === ThumbType.DEFAULT ? start : defaultThumbOffset} end={type === ThumbType.DEFAULT ? rangeThumbOffset : end} offset={type === ThumbType.DEFAULT ? defaultThumbOffset : rangeThumbOffset} gap={rangeGap} secondary={type !== ThumbType.DEFAULT} onSeekStart={_onSeekStart} onSeekEnd={_onSeekEnd} shouldDisableRTL={shouldDisableRTL} disabled={disabled} pointerEvents={useRelativeDrag ? 'none' : undefined} isActive={useRelativeDrag ? isContainerDragging : undefined} disableActiveStyling={disableActiveStyling} defaultStyle={_thumbStyle} activeStyle={_activeThumbStyle} hitSlop={thumbHitSlop} shouldBounceToStep={shouldBounceToStep} stepInterpolatedValue={stepInterpolatedValue} enableShadow={enableThumbShadow} />;
|
|
240
272
|
};
|
|
241
273
|
const _renderTrack = () => {
|
|
242
|
-
return <Track renderTrack={renderTrack} onLayout={onTrackLayout} onPress={onTrackPress} animatedStyle={trackAnimatedStyles} disabled={disabled} maximumTrackTintColor={maximumTrackTintColor} minimumTrackTintColor={minimumTrackTintColor} trackStyle={trackStyle} />;
|
|
274
|
+
return <Track renderTrack={renderTrack} onLayout={onTrackLayout} onPress={useRelativeDrag ? undefined : onTrackPress} animatedStyle={trackAnimatedStyles} disabled={disabled} maximumTrackTintColor={maximumTrackTintColor} minimumTrackTintColor={minimumTrackTintColor} trackStyle={trackStyle} />;
|
|
243
275
|
};
|
|
244
|
-
|
|
276
|
+
const renderSliderContent = () => <>
|
|
245
277
|
{_renderTrack()}
|
|
246
278
|
{renderThumb(ThumbType.DEFAULT)}
|
|
247
279
|
{useRange && renderThumb(ThumbType.RANGE)}
|
|
280
|
+
</>;
|
|
281
|
+
return <GestureHandlerRootView style={[styles.container, containerStyle, shouldDisableRTL && styles.disableRTL]} testID={testID} {...accessibilityProps}>
|
|
282
|
+
{useRelativeDrag ? <GestureDetector gesture={containerGesture}>
|
|
283
|
+
<View style={styles.gestureContainer}>
|
|
284
|
+
{renderSliderContent()}
|
|
285
|
+
</View>
|
|
286
|
+
</GestureDetector> : renderSliderContent()}
|
|
248
287
|
</GestureHandlerRootView>;
|
|
249
288
|
});
|
|
250
289
|
Slider.displayName = 'Incubator.Slider';
|
|
@@ -254,6 +293,10 @@ const styles = StyleSheet.create({
|
|
|
254
293
|
height: THUMB_SIZE + SHADOW_RADIUS,
|
|
255
294
|
justifyContent: 'center'
|
|
256
295
|
},
|
|
296
|
+
gestureContainer: {
|
|
297
|
+
flex: 1,
|
|
298
|
+
justifyContent: 'center'
|
|
299
|
+
},
|
|
257
300
|
disableRTL: {
|
|
258
301
|
transform: [{
|
|
259
302
|
scaleX: -1
|
package/src/index.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export { default as ExpandableSection, ExpandableSectionProps } from './componen
|
|
|
38
38
|
export { default as Fader, FaderProps, FaderPosition } from './components/fader';
|
|
39
39
|
export { default as FeatureHighlight, FeatureHighlightProps } from './components/featureHighlight';
|
|
40
40
|
export { default as FloatingButton, FloatingButtonProps, FloatingButtonLayouts } from './components/floatingButton';
|
|
41
|
+
export { default as ScreenFooter, ScreenFooterProps, ScreenFooterLayouts, ScreenFooterBackgrounds, FooterAlignment, HorizontalItemsDistribution, ItemsFit, KeyboardBehavior, ScreenFooterShadow } from './components/screenFooter';
|
|
41
42
|
export { default as Gradient, GradientProps, GradientTypes } from './components/gradient';
|
|
42
43
|
export { default as Slider } from './components/slider';
|
|
43
44
|
export { default as GradientSlider } from './components/slider/GradientSlider';
|