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.
Files changed (35) hide show
  1. package/lib/android/build.gradle +3 -3
  2. package/package.json +1 -2
  3. package/screenFooter.d.ts +2 -0
  4. package/screenFooter.js +1 -0
  5. package/scripts/release/prReleaseNotesCommon.js +2 -1
  6. package/src/assets/internal/images/bottomGradient.png +0 -0
  7. package/src/assets/internal/images/bottomGradient@1.5x.png +0 -0
  8. package/src/assets/internal/images/bottomGradient@2x.png +0 -0
  9. package/src/assets/internal/images/bottomGradient@3x.png +0 -0
  10. package/src/assets/internal/images/bottomGradient@4x.png +0 -0
  11. package/src/assets/internal/images/index.js +3 -0
  12. package/src/components/screenFooter/index.d.ts +8 -0
  13. package/src/components/screenFooter/index.js +211 -0
  14. package/src/components/screenFooter/screenFooter.api.json +262 -0
  15. package/src/components/screenFooter/types.d.ts +100 -0
  16. package/src/components/screenFooter/types.js +39 -0
  17. package/src/components/slider/Thumb.d.ts +5 -1
  18. package/src/components/slider/Thumb.js +2 -1
  19. package/src/components/slider/index.d.ts +1 -0
  20. package/src/components/slider/index.js +69 -11
  21. package/src/components/slider/slider.api.json +6 -0
  22. package/src/components/slider/types.d.ts +5 -0
  23. package/src/hooks/index.d.ts +1 -0
  24. package/src/hooks/index.js +1 -0
  25. package/src/hooks/useScrollToHide/index.d.ts +24 -0
  26. package/src/hooks/useScrollToHide/index.js +48 -0
  27. package/src/incubator/expandableOverlay/index.js +7 -3
  28. package/src/incubator/slider/Thumb.d.ts +1 -0
  29. package/src/incubator/slider/Thumb.js +9 -6
  30. package/src/incubator/slider/index.d.ts +5 -0
  31. package/src/incubator/slider/index.js +47 -4
  32. package/src/index.d.ts +1 -0
  33. package/src/index.js +70 -0
  34. package/src/style/colors.d.ts +12 -13
  35. 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
- return <Thumb {...this.getThumbProps()} ref={this.minThumb} onTouchStart={this.onMinTouchStart} {...this.panResponder.panHandlers} />;
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
- return <Thumb {...this.getThumbProps()} ref={this.thumb} onTouchStart={this.onTouchStart} {...this.panResponder.panHandlers} />;
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
- return <View style={[styles.container, containerStyle]} onLayout={this.onContainerLayout} onAccessibilityAction={this.onAccessibilityAction} testID={testID} {...this.getAccessibilityProps()}>
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
  */
@@ -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';
@@ -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 closeExpandable = useCallback(() => {
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
- }, [useDialog, dialogProps?.onDismiss, modalProps?.onDismiss, focusAccessibility]);
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={closeExpandable} onRequestClose={closeExpandable} onBackgroundPress={closeExpandable}>
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>;
@@ -19,6 +19,7 @@ interface ThumbProps extends ViewProps {
19
19
  onSeekStart?: () => void;
20
20
  onSeekEnd?: () => void;
21
21
  enableShadow?: boolean;
22
+ isActive?: SharedValue<boolean>;
22
23
  }
23
24
  declare const Thumb: (props: ThumbProps) => React.JSX.Element;
24
25
  export default Thumb;
@@ -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 customStyle = isPressed.value ? activeStyle?.value : defaultStyle?.value;
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 && isPressed.value ? 1.3 : 1)
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
- return <GestureHandlerRootView style={[styles.container, containerStyle, shouldDisableRTL && styles.disableRTL]} testID={testID} {...accessibilityProps}>
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';