react-native-ui-lib 8.4.0-snapshot.7832 → 8.4.0-snapshot.7841
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/package.json +1 -1
- package/src/components/floatingButton/floatingButton.api.json +10 -15
- package/src/components/floatingButton/index.d.ts +13 -39
- package/src/components/floatingButton/index.js +58 -165
- package/src/components/screenFooter/index.js +10 -7
- package/src/components/screenFooter/screenFooter.api.json +9 -21
- package/src/components/screenFooter/types.d.ts +6 -1
- package/src/components/slider/Thumb.d.ts +1 -5
- package/src/components/slider/Thumb.js +1 -2
- package/src/components/slider/index.d.ts +0 -1
- package/src/components/slider/index.js +11 -69
- package/src/components/slider/slider.api.json +0 -6
- package/src/components/slider/types.d.ts +0 -5
- package/src/components/textField/TextField.driver.new.js +12 -7
- package/src/components/textField/index.js +31 -9
- package/src/incubator/slider/Thumb.d.ts +0 -1
- package/src/incubator/slider/Thumb.js +6 -9
- package/src/incubator/slider/index.d.ts +0 -5
- package/src/incubator/slider/index.js +4 -47
package/package.json
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "FloatingButton",
|
|
3
3
|
"category": "overlays",
|
|
4
|
-
"description": "Hovering button with gradient background",
|
|
5
|
-
"modifiers": [
|
|
6
|
-
"margin",
|
|
7
|
-
"background",
|
|
8
|
-
"color"
|
|
9
|
-
],
|
|
4
|
+
"description": "Hovering button with gradient background, backed by ScreenFooter",
|
|
5
|
+
"modifiers": ["margin", "background", "color"],
|
|
10
6
|
"example": "https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/FloatingButtonScreen.tsx",
|
|
11
7
|
"images": [
|
|
12
8
|
"https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/FloatingButton/FloatingButton.gif?raw=true"
|
|
@@ -58,6 +54,12 @@
|
|
|
58
54
|
"type": "boolean",
|
|
59
55
|
"description": "Whether to show background overlay"
|
|
60
56
|
},
|
|
57
|
+
{
|
|
58
|
+
"name": "hoisted",
|
|
59
|
+
"type": "boolean",
|
|
60
|
+
"description": "Whether the footer should be hoisted above the keyboard. When true, uses KeyboardAccessoryView for keyboard-aware positioning. When false, uses sticky positioning.",
|
|
61
|
+
"default": "true"
|
|
62
|
+
},
|
|
61
63
|
{
|
|
62
64
|
"name": "testID",
|
|
63
65
|
"type": "string",
|
|
@@ -96,11 +98,7 @@
|
|
|
96
98
|
},
|
|
97
99
|
{
|
|
98
100
|
"type": "table",
|
|
99
|
-
"columns": [
|
|
100
|
-
"Property",
|
|
101
|
-
"Default",
|
|
102
|
-
"Full Width"
|
|
103
|
-
],
|
|
101
|
+
"columns": ["Property", "Default", "Full Width"],
|
|
104
102
|
"items": [
|
|
105
103
|
{
|
|
106
104
|
"title": "Main button",
|
|
@@ -130,10 +128,7 @@
|
|
|
130
128
|
},
|
|
131
129
|
{
|
|
132
130
|
"type": "table",
|
|
133
|
-
"columns": [
|
|
134
|
-
"Layout",
|
|
135
|
-
"Component"
|
|
136
|
-
],
|
|
131
|
+
"columns": ["Layout", "Component"],
|
|
137
132
|
"items": [
|
|
138
133
|
{
|
|
139
134
|
"title": "Horizontal",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import React, { PropsWithChildren
|
|
2
|
-
import { Animated } from 'react-native';
|
|
1
|
+
import React, { PropsWithChildren } from 'react';
|
|
3
2
|
import { ButtonProps } from '../button';
|
|
4
3
|
export declare enum FloatingButtonLayouts {
|
|
5
4
|
VERTICAL = "Vertical",
|
|
@@ -23,7 +22,7 @@ export interface FloatingButtonProps {
|
|
|
23
22
|
*/
|
|
24
23
|
bottomMargin?: number;
|
|
25
24
|
/**
|
|
26
|
-
* Whether the buttons get the container's full
|
|
25
|
+
* Whether the buttons get the container's full width (vertical layout only)
|
|
27
26
|
*/
|
|
28
27
|
fullWidth?: boolean;
|
|
29
28
|
/**
|
|
@@ -42,6 +41,12 @@ export interface FloatingButtonProps {
|
|
|
42
41
|
* Whether to show background overlay
|
|
43
42
|
*/
|
|
44
43
|
hideBackgroundOverlay?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the footer should be hoisted above the keyboard.
|
|
46
|
+
* When true (default), uses KeyboardAccessoryView for keyboard-aware positioning.
|
|
47
|
+
* When false, uses sticky positioning.
|
|
48
|
+
*/
|
|
49
|
+
hoisted?: boolean;
|
|
45
50
|
/**
|
|
46
51
|
* Used as testing identifier
|
|
47
52
|
* <TestID> - the floatingButton container
|
|
@@ -50,40 +55,9 @@ export interface FloatingButtonProps {
|
|
|
50
55
|
*/
|
|
51
56
|
testID?: string;
|
|
52
57
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
*/
|
|
59
|
-
declare class FloatingButton extends PureComponent<FloatingButtonProps> {
|
|
60
|
-
static displayName: string;
|
|
61
|
-
static floatingButtonLayouts: typeof FloatingButtonLayouts;
|
|
62
|
-
static defaultProps: {
|
|
63
|
-
duration: number;
|
|
64
|
-
buttonLayout: FloatingButtonLayouts;
|
|
65
|
-
};
|
|
66
|
-
initialVisibility?: boolean;
|
|
67
|
-
firstLoad: boolean;
|
|
68
|
-
visibleAnimated: Animated.Value;
|
|
69
|
-
constructor(props: FloatingButtonProps);
|
|
70
|
-
componentDidUpdate(prevProps: FloatingButtonProps): void;
|
|
71
|
-
getAnimatedStyle: () => {
|
|
72
|
-
opacity: Animated.Value;
|
|
73
|
-
transform: {
|
|
74
|
-
translateY: Animated.AnimatedInterpolation<string | number>;
|
|
75
|
-
}[];
|
|
76
|
-
};
|
|
77
|
-
get isSecondaryOnly(): boolean;
|
|
78
|
-
get isHorizontalLayout(): boolean;
|
|
79
|
-
get isSecondaryHorizontal(): boolean | undefined;
|
|
80
|
-
get isSecondaryVertical(): boolean | undefined;
|
|
81
|
-
renderButton(): React.JSX.Element | undefined;
|
|
82
|
-
renderOverlay: () => React.JSX.Element | undefined;
|
|
83
|
-
renderSecondaryButton(): React.JSX.Element | undefined;
|
|
84
|
-
renderHorizontalLayout(): React.JSX.Element;
|
|
85
|
-
renderVerticalLayout(): React.JSX.Element;
|
|
86
|
-
render(): false | React.JSX.Element | undefined;
|
|
87
|
-
}
|
|
88
|
-
declare const _default: React.ForwardRefExoticComponent<FloatingButtonProps & React.RefAttributes<any>> & typeof FloatingButton;
|
|
58
|
+
declare const _default: React.ForwardRefExoticComponent<FloatingButtonProps & React.RefAttributes<any>> & {
|
|
59
|
+
(props: FloatingButtonProps): React.JSX.Element | null;
|
|
60
|
+
displayName: string;
|
|
61
|
+
floatingButtonLayouts: typeof FloatingButtonLayouts;
|
|
62
|
+
};
|
|
89
63
|
export default _default;
|
|
@@ -1,190 +1,83 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { StyleSheet
|
|
3
|
-
import {
|
|
1
|
+
import React, { useEffect, useMemo } from 'react';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import { asBaseComponent } from "../../commons/new";
|
|
4
|
+
import { LogService } from "../../services";
|
|
4
5
|
import { Colors, Shadows, Spacings } from "../../style";
|
|
5
|
-
import View from "../view";
|
|
6
|
-
import Image from "../image";
|
|
7
6
|
import Button from "../button";
|
|
7
|
+
import ScreenFooter, { ScreenFooterLayouts, ScreenFooterBackgrounds, KeyboardBehavior, ItemsFit } from "../screenFooter";
|
|
8
8
|
export let FloatingButtonLayouts = /*#__PURE__*/function (FloatingButtonLayouts) {
|
|
9
9
|
FloatingButtonLayouts["VERTICAL"] = "Vertical";
|
|
10
10
|
FloatingButtonLayouts["HORIZONTAL"] = "Horizontal";
|
|
11
11
|
return FloatingButtonLayouts;
|
|
12
12
|
}({});
|
|
13
|
-
const gradientImage = () => require("./gradient.png");
|
|
14
|
-
|
|
15
13
|
/**
|
|
16
|
-
* @description: Hovering button with gradient background
|
|
14
|
+
* @description: Hovering button with gradient background, backed by ScreenFooter
|
|
17
15
|
* @modifiers: margin, background, color
|
|
18
16
|
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/FloatingButtonScreen.tsx
|
|
19
17
|
* @gif: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/FloatingButton/FloatingButton.gif?raw=true
|
|
20
18
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
19
|
+
const FloatingButton = props => {
|
|
20
|
+
const {
|
|
21
|
+
visible = false,
|
|
22
|
+
button,
|
|
23
|
+
secondaryButton,
|
|
24
|
+
bottomMargin,
|
|
25
|
+
fullWidth,
|
|
26
|
+
buttonLayout = FloatingButtonLayouts.VERTICAL,
|
|
27
|
+
duration = 300,
|
|
28
|
+
withoutAnimation,
|
|
29
|
+
hideBackgroundOverlay,
|
|
30
|
+
hoisted = true,
|
|
31
|
+
testID
|
|
32
|
+
} = props;
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
// eslint-disable-next-line max-len
|
|
35
|
+
LogService.warn('RNUILib FloatingButton now uses ScreenFooter internally, which requires a SafeAreaProvider. If you experience safe area issues, please wrap your app (or the relevant screen) with <SafeAreaProvider>.');
|
|
36
|
+
}, []);
|
|
37
|
+
const isSecondaryOnly = !!secondaryButton && !button;
|
|
38
|
+
const isHorizontal = buttonLayout === FloatingButtonLayouts.HORIZONTAL || isSecondaryOnly;
|
|
39
|
+
const footerContentContainerStyle = useMemo(() => {
|
|
40
|
+
if (bottomMargin !== undefined) {
|
|
41
|
+
return {
|
|
42
|
+
paddingBottom: bottomMargin
|
|
43
|
+
};
|
|
45
44
|
}
|
|
46
|
-
|
|
47
|
-
getAnimatedStyle = () => {
|
|
45
|
+
const isSecondaryAtBottom = !!secondaryButton && (isSecondaryOnly || !isHorizontal);
|
|
48
46
|
return {
|
|
49
|
-
|
|
50
|
-
transform: [{
|
|
51
|
-
translateY: this.visibleAnimated.interpolate({
|
|
52
|
-
inputRange: [0, 1],
|
|
53
|
-
outputRange: [Constants.screenHeight / 2, 0]
|
|
54
|
-
})
|
|
55
|
-
}]
|
|
47
|
+
paddingBottom: isSecondaryAtBottom ? Spacings.s7 : Spacings.s8
|
|
56
48
|
};
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
secondaryButton,
|
|
61
|
-
button
|
|
62
|
-
} = this.props;
|
|
63
|
-
return !!secondaryButton && !button;
|
|
64
|
-
}
|
|
65
|
-
get isHorizontalLayout() {
|
|
66
|
-
const {
|
|
67
|
-
buttonLayout
|
|
68
|
-
} = this.props;
|
|
69
|
-
return buttonLayout === FloatingButtonLayouts.HORIZONTAL || this.isSecondaryOnly;
|
|
70
|
-
}
|
|
71
|
-
get isSecondaryHorizontal() {
|
|
72
|
-
const {
|
|
73
|
-
secondaryButton
|
|
74
|
-
} = this.props;
|
|
75
|
-
return secondaryButton && this.isHorizontalLayout;
|
|
76
|
-
}
|
|
77
|
-
get isSecondaryVertical() {
|
|
78
|
-
const {
|
|
79
|
-
secondaryButton
|
|
80
|
-
} = this.props;
|
|
81
|
-
return secondaryButton && !this.isHorizontalLayout;
|
|
82
|
-
}
|
|
83
|
-
renderButton() {
|
|
84
|
-
const {
|
|
85
|
-
bottomMargin,
|
|
86
|
-
button,
|
|
87
|
-
fullWidth,
|
|
88
|
-
testID
|
|
89
|
-
} = this.props;
|
|
90
|
-
if (button) {
|
|
91
|
-
const shadowStyle = button && !button.outline && !button.link ? styles.shadow : undefined;
|
|
92
|
-
const marginStyle = {
|
|
93
|
-
marginTop: Spacings.s4,
|
|
94
|
-
marginBottom: this.isSecondaryVertical ? Spacings.s4 : bottomMargin || Spacings.s8,
|
|
95
|
-
marginLeft: this.isSecondaryHorizontal || fullWidth ? Spacings.s4 : undefined,
|
|
96
|
-
marginRight: this.isSecondaryHorizontal ? Spacings.s5 : fullWidth ? Spacings.s4 : undefined
|
|
97
|
-
};
|
|
98
|
-
const shouldFlex = this.isSecondaryHorizontal || fullWidth && this.isHorizontalLayout;
|
|
99
|
-
return <Button size={Button.sizes.large} flex={!!shouldFlex} style={[shadowStyle, marginStyle]} testID={`${testID}.button`} {...button} />;
|
|
100
|
-
}
|
|
49
|
+
}, [bottomMargin, secondaryButton, isSecondaryOnly, isHorizontal]);
|
|
50
|
+
if (!button && !secondaryButton) {
|
|
51
|
+
return null;
|
|
101
52
|
}
|
|
102
|
-
|
|
103
|
-
if (!
|
|
104
|
-
return
|
|
105
|
-
<Image style={styles.image} source={gradientImage()} resizeMode={'stretch'} tintColor={Colors.$backgroundDefault} />
|
|
106
|
-
</View>;
|
|
53
|
+
const renderPrimaryButton = () => {
|
|
54
|
+
if (!button) {
|
|
55
|
+
return null;
|
|
107
56
|
}
|
|
57
|
+
const shadowStyle = !button.outline && !button.link ? styles.shadow : undefined;
|
|
58
|
+
const shouldFlex = isHorizontal && !!secondaryButton || fullWidth && isHorizontal;
|
|
59
|
+
return <Button key="primary" size={Button.sizes.large} flex={!!shouldFlex} style={shadowStyle} testID={testID ? `${testID}.button` : undefined} {...button} />;
|
|
108
60
|
};
|
|
109
|
-
renderSecondaryButton() {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
bottomMargin,
|
|
113
|
-
testID,
|
|
114
|
-
fullWidth,
|
|
115
|
-
button
|
|
116
|
-
} = this.props;
|
|
117
|
-
if (secondaryButton) {
|
|
118
|
-
const bgColor = secondaryButton.backgroundColor || Colors.$backgroundDefault;
|
|
119
|
-
const shouldFlex = this.isHorizontalLayout && !!button || fullWidth && this.isSecondaryOnly;
|
|
120
|
-
const buttonStyle = this.isHorizontalLayout ? [styles.shadow, styles.horizontalSecondaryMargin, {
|
|
121
|
-
backgroundColor: bgColor
|
|
122
|
-
}] : {
|
|
123
|
-
marginBottom: bottomMargin || Spacings.s7
|
|
124
|
-
};
|
|
125
|
-
return <Button outline={this.isHorizontalLayout} flex={shouldFlex} link={!this.isHorizontalLayout} size={Button.sizes.large} testID={`${testID}.secondaryButton`} {...secondaryButton} style={buttonStyle} enableShadow={false} />;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
renderHorizontalLayout() {
|
|
129
|
-
return <>
|
|
130
|
-
{this.renderOverlay()}
|
|
131
|
-
{this.renderSecondaryButton()}
|
|
132
|
-
{this.renderButton()}
|
|
133
|
-
</>;
|
|
134
|
-
}
|
|
135
|
-
renderVerticalLayout() {
|
|
136
|
-
return <>
|
|
137
|
-
{this.renderOverlay()}
|
|
138
|
-
{this.renderButton()}
|
|
139
|
-
{this.renderSecondaryButton()}
|
|
140
|
-
</>;
|
|
141
|
-
}
|
|
142
|
-
render() {
|
|
143
|
-
// NOTE: keep this.firstLoad as true as long as the visibility changed to true
|
|
144
|
-
const {
|
|
145
|
-
withoutAnimation,
|
|
146
|
-
visible,
|
|
147
|
-
fullWidth,
|
|
148
|
-
testID,
|
|
149
|
-
button,
|
|
150
|
-
secondaryButton
|
|
151
|
-
} = this.props;
|
|
152
|
-
this.firstLoad && !visible ? this.firstLoad = true : this.firstLoad = false;
|
|
153
|
-
|
|
154
|
-
// NOTE: On first load, don't show if it should not be visible
|
|
155
|
-
if (this.firstLoad === true && !this.initialVisibility) {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
if (!visible && withoutAnimation) {
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
if (button || secondaryButton) {
|
|
162
|
-
const hasBothButtons = !!(button && secondaryButton);
|
|
163
|
-
const shouldCenter = !fullWidth || this.isHorizontalLayout && hasBothButtons;
|
|
164
|
-
return <View row={this.isHorizontalLayout} center={shouldCenter} pointerEvents="box-none" animated style={[styles.container, this.getAnimatedStyle()]} testID={testID}>
|
|
165
|
-
{this.isHorizontalLayout ? this.renderHorizontalLayout() : this.renderVerticalLayout()}
|
|
166
|
-
</View>;
|
|
61
|
+
const renderSecondaryButton = () => {
|
|
62
|
+
if (!secondaryButton) {
|
|
63
|
+
return null;
|
|
167
64
|
}
|
|
168
|
-
|
|
169
|
-
|
|
65
|
+
const shouldFlex = isHorizontal && !!button || fullWidth && isSecondaryOnly;
|
|
66
|
+
const bgColor = secondaryButton.backgroundColor || Colors.$backgroundDefault;
|
|
67
|
+
return <Button key="secondary" outline={isHorizontal} link={!isHorizontal} flex={shouldFlex} size={Button.sizes.large} testID={testID ? `${testID}.secondaryButton` : undefined} {...secondaryButton} style={isHorizontal ? [styles.shadow, {
|
|
68
|
+
backgroundColor: bgColor
|
|
69
|
+
}] : undefined} enableShadow={false} />;
|
|
70
|
+
};
|
|
71
|
+
const children = isHorizontal ? [renderSecondaryButton(), renderPrimaryButton()] : [renderPrimaryButton(), renderSecondaryButton()];
|
|
72
|
+
return <ScreenFooter visible={visible} layout={isHorizontal ? ScreenFooterLayouts.HORIZONTAL : ScreenFooterLayouts.VERTICAL} backgroundType={hideBackgroundOverlay ? ScreenFooterBackgrounds.TRANSPARENT : ScreenFooterBackgrounds.FADING} keyboardBehavior={hoisted ? KeyboardBehavior.HOISTED : KeyboardBehavior.STICKY} animationDuration={withoutAnimation ? 0 : duration} itemsFit={fullWidth ? ItemsFit.STRETCH : undefined} contentContainerStyle={footerContentContainerStyle} testID={testID}>
|
|
73
|
+
{children}
|
|
74
|
+
</ScreenFooter>;
|
|
75
|
+
};
|
|
76
|
+
FloatingButton.displayName = 'FloatingButton';
|
|
77
|
+
FloatingButton.floatingButtonLayouts = FloatingButtonLayouts;
|
|
170
78
|
const styles = StyleSheet.create({
|
|
171
|
-
container: {
|
|
172
|
-
// ...StyleSheet.absoluteFillObject, // TODO: this is breaking scenarios where the FloatingButton is inside a KeyboardTrackingView
|
|
173
|
-
top: undefined,
|
|
174
|
-
zIndex: 99
|
|
175
|
-
},
|
|
176
|
-
image: {
|
|
177
|
-
...StyleSheet.absoluteFillObject,
|
|
178
|
-
width: '100%',
|
|
179
|
-
height: '100%'
|
|
180
|
-
},
|
|
181
79
|
shadow: {
|
|
182
80
|
...Shadows.sh20.bottom
|
|
183
|
-
},
|
|
184
|
-
horizontalSecondaryMargin: {
|
|
185
|
-
marginTop: Spacings.s4,
|
|
186
|
-
marginBottom: Spacings.s7,
|
|
187
|
-
marginLeft: Spacings.s5
|
|
188
81
|
}
|
|
189
82
|
});
|
|
190
83
|
export default asBaseComponent(FloatingButton);
|
|
@@ -26,7 +26,8 @@ const ScreenFooter = props => {
|
|
|
26
26
|
visible = true,
|
|
27
27
|
animationDuration = 200,
|
|
28
28
|
shadow = ScreenFooterShadow.SH20,
|
|
29
|
-
hideDivider = false
|
|
29
|
+
hideDivider = false,
|
|
30
|
+
contentContainerStyle: contentContainerStyleOverride
|
|
30
31
|
} = props;
|
|
31
32
|
const keyboard = useAnimatedKeyboard();
|
|
32
33
|
const [height, setHeight] = useState(0);
|
|
@@ -112,13 +113,15 @@ const ScreenFooter = props => {
|
|
|
112
113
|
if (isSolid) {
|
|
113
114
|
const shadowStyle = Shadows[shadow]?.top;
|
|
114
115
|
const backgroundElevation = shadowStyle?.elevation || 0;
|
|
115
|
-
// When the background has a shadow (elevation on Android), it might render on top of the content
|
|
116
116
|
style.push({
|
|
117
117
|
elevation: backgroundElevation + 1
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
|
+
if (contentContainerStyleOverride) {
|
|
121
|
+
style.push(contentContainerStyleOverride);
|
|
122
|
+
}
|
|
120
123
|
return style;
|
|
121
|
-
}, [layout, alignItems, justifyContent, insets.bottom, isSolid, shadow, isKeyboardVisible]);
|
|
124
|
+
}, [layout, alignItems, justifyContent, insets.bottom, isSolid, shadow, isKeyboardVisible, contentContainerStyleOverride]);
|
|
122
125
|
const solidBackgroundStyle = useMemo(() => {
|
|
123
126
|
if (!isSolid) {
|
|
124
127
|
return undefined;
|
|
@@ -154,13 +157,13 @@ const ScreenFooter = props => {
|
|
|
154
157
|
maxWidth: '100%'
|
|
155
158
|
};
|
|
156
159
|
return <View key={index} style={fixedStyle}>
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
{child}
|
|
161
|
+
</View>;
|
|
159
162
|
}
|
|
160
163
|
if (isHorizontal && React.isValidElement(child) && itemsFit === ItemsFit.STRETCH) {
|
|
161
164
|
return <View flex row centerH key={index}>
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
{child}
|
|
166
|
+
</View>;
|
|
164
167
|
}
|
|
165
168
|
return child;
|
|
166
169
|
}, [itemsFit, itemWidth, isHorizontal]);
|
|
@@ -81,6 +81,11 @@
|
|
|
81
81
|
"type": "boolean",
|
|
82
82
|
"description": "If true, hides the top divider for solid background. Only applies when backgroundType is 'solid'",
|
|
83
83
|
"default": "false"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": "contentContainerStyle",
|
|
87
|
+
"type": "StyleProp<ViewStyle>",
|
|
88
|
+
"description": "Custom style for the content container that wraps the footer's children. Can be used to override default padding, gap, or other layout properties."
|
|
84
89
|
}
|
|
85
90
|
],
|
|
86
91
|
"snippet": [
|
|
@@ -118,11 +123,7 @@
|
|
|
118
123
|
},
|
|
119
124
|
{
|
|
120
125
|
"type": "table",
|
|
121
|
-
"columns": [
|
|
122
|
-
"Layout Type",
|
|
123
|
-
"Use Case",
|
|
124
|
-
"Example"
|
|
125
|
-
],
|
|
126
|
+
"columns": ["Layout Type", "Use Case", "Example"],
|
|
126
127
|
"items": [
|
|
127
128
|
{
|
|
128
129
|
"title": "Horizontal - Spread",
|
|
@@ -157,11 +158,7 @@
|
|
|
157
158
|
},
|
|
158
159
|
{
|
|
159
160
|
"type": "table",
|
|
160
|
-
"columns": [
|
|
161
|
-
"Layout Type",
|
|
162
|
-
"Use Case",
|
|
163
|
-
"Example"
|
|
164
|
-
],
|
|
161
|
+
"columns": ["Layout Type", "Use Case", "Example"],
|
|
165
162
|
"items": [
|
|
166
163
|
{
|
|
167
164
|
"title": "Vertical - Fit",
|
|
@@ -187,11 +184,7 @@
|
|
|
187
184
|
},
|
|
188
185
|
{
|
|
189
186
|
"type": "table",
|
|
190
|
-
"columns": [
|
|
191
|
-
"Background Type",
|
|
192
|
-
"Visual",
|
|
193
|
-
"When to Use"
|
|
194
|
-
],
|
|
187
|
+
"columns": ["Background Type", "Visual", "When to Use"],
|
|
195
188
|
"items": [
|
|
196
189
|
{
|
|
197
190
|
"title": "Solid",
|
|
@@ -226,11 +219,7 @@
|
|
|
226
219
|
},
|
|
227
220
|
{
|
|
228
221
|
"type": "table",
|
|
229
|
-
"columns": [
|
|
230
|
-
"Behavior",
|
|
231
|
-
"Description",
|
|
232
|
-
"Example"
|
|
233
|
-
],
|
|
222
|
+
"columns": ["Behavior", "Description", "Example"],
|
|
234
223
|
"items": [
|
|
235
224
|
{
|
|
236
225
|
"title": "Sticky",
|
|
@@ -259,4 +248,3 @@
|
|
|
259
248
|
]
|
|
260
249
|
}
|
|
261
250
|
}
|
|
262
|
-
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
|
2
|
-
import { DimensionValue } from 'react-native';
|
|
2
|
+
import { DimensionValue, StyleProp, ViewStyle } from 'react-native';
|
|
3
3
|
export declare enum ScreenFooterLayouts {
|
|
4
4
|
HORIZONTAL = "horizontal",
|
|
5
5
|
VERTICAL = "vertical"
|
|
@@ -97,4 +97,9 @@ export interface ScreenFooterProps extends PropsWithChildren<{}> {
|
|
|
97
97
|
* Only applies when backgroundType is 'solid'
|
|
98
98
|
*/
|
|
99
99
|
hideDivider?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Custom style for the content container that wraps the footer's children.
|
|
102
|
+
* Can be used to override default padding, gap, or other layout properties.
|
|
103
|
+
*/
|
|
104
|
+
contentContainerStyle?: StyleProp<ViewStyle>;
|
|
100
105
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ViewStyle, ViewProps, View as RNView
|
|
2
|
+
import { ViewStyle, ViewProps, View as RNView } from 'react-native';
|
|
3
3
|
export interface ThumbProps extends ViewProps {
|
|
4
4
|
/**
|
|
5
5
|
* The thumb style
|
|
@@ -27,10 +27,6 @@ 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;
|
|
34
30
|
}
|
|
35
31
|
declare const Thumb: React.ForwardRefExoticComponent<Omit<ThumbProps, "ref"> & React.RefAttributes<unknown>>;
|
|
36
32
|
export default Thumb;
|
|
@@ -18,7 +18,6 @@ const Thumb = forwardRef((props, ref) => {
|
|
|
18
18
|
thumbHitSlop,
|
|
19
19
|
onTouchStart,
|
|
20
20
|
onTouchEnd,
|
|
21
|
-
scaleAnimation,
|
|
22
21
|
...others
|
|
23
22
|
} = props;
|
|
24
23
|
const thumbRef = useCombinedRefs(ref);
|
|
@@ -83,7 +82,7 @@ const Thumb = forwardRef((props, ref) => {
|
|
|
83
82
|
backgroundColor: disabled ? DEFAULT_COLOR : thumbTintColor || ACTIVE_COLOR
|
|
84
83
|
}, {
|
|
85
84
|
transform: [{
|
|
86
|
-
scale:
|
|
85
|
+
scale: thumbScaleAnimation.current
|
|
87
86
|
}]
|
|
88
87
|
}]} />;
|
|
89
88
|
});
|
|
@@ -23,7 +23,6 @@ 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;
|
|
27
26
|
migrate?: boolean | undefined;
|
|
28
27
|
enableThumbShadow?: boolean | undefined;
|
|
29
28
|
disabledThumbTintColor?: string | undefined;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import _isFunction from "lodash/isFunction";
|
|
2
|
-
import _clamp from "lodash/clamp";
|
|
3
2
|
import React, { PureComponent } from 'react';
|
|
4
3
|
import { StyleSheet, PanResponder, AccessibilityInfo, Animated } from 'react-native';
|
|
5
4
|
import { Constants, asBaseComponent } from "../../commons/new";
|
|
@@ -56,8 +55,6 @@ class Slider extends PureComponent {
|
|
|
56
55
|
width: THUMB_SIZE,
|
|
57
56
|
height: THUMB_SIZE
|
|
58
57
|
};
|
|
59
|
-
_containerDragInitialValue = 0;
|
|
60
|
-
_containerThumbScale = new Animated.Value(1);
|
|
61
58
|
constructor(props) {
|
|
62
59
|
super(props);
|
|
63
60
|
this.activeThumbRef = this.thumb;
|
|
@@ -88,15 +85,6 @@ class Slider extends PureComponent {
|
|
|
88
85
|
onPanResponderEnd: () => true,
|
|
89
86
|
onPanResponderTerminationRequest: () => false
|
|
90
87
|
});
|
|
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
|
-
});
|
|
100
88
|
}
|
|
101
89
|
reset() {
|
|
102
90
|
// NOTE: used with ref
|
|
@@ -215,41 +203,6 @@ class Slider extends PureComponent {
|
|
|
215
203
|
this.bounceToStep();
|
|
216
204
|
this.onSeekEnd();
|
|
217
205
|
};
|
|
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
|
-
};
|
|
253
206
|
|
|
254
207
|
/* Actions */
|
|
255
208
|
|
|
@@ -510,9 +463,6 @@ class Slider extends PureComponent {
|
|
|
510
463
|
onThumbLayout = nativeEvent => {
|
|
511
464
|
this.handleMeasure('thumbSize', nativeEvent);
|
|
512
465
|
};
|
|
513
|
-
handleContainerShouldSetResponder = () => {
|
|
514
|
-
return !this.props.disabled;
|
|
515
|
-
};
|
|
516
466
|
handleTrackPress = event => {
|
|
517
467
|
if (this.props.disabled) {
|
|
518
468
|
return;
|
|
@@ -586,8 +536,7 @@ class Slider extends PureComponent {
|
|
|
586
536
|
disableActiveStyling,
|
|
587
537
|
disabled,
|
|
588
538
|
thumbTintColor,
|
|
589
|
-
thumbHitSlop
|
|
590
|
-
useRelativeDrag
|
|
539
|
+
thumbHitSlop
|
|
591
540
|
} = this.props;
|
|
592
541
|
const {
|
|
593
542
|
thumbSize
|
|
@@ -607,23 +556,16 @@ class Slider extends PureComponent {
|
|
|
607
556
|
activeThumbStyle,
|
|
608
557
|
disableActiveStyling,
|
|
609
558
|
thumbHitSlop: thumbHitSlop ?? calculatedHitSlop,
|
|
610
|
-
onLayout: this.onThumbLayout
|
|
611
|
-
scaleAnimation: useRelativeDrag ? this._containerThumbScale : undefined
|
|
559
|
+
onLayout: this.onThumbLayout
|
|
612
560
|
};
|
|
613
561
|
};
|
|
614
562
|
|
|
615
563
|
/* Renders */
|
|
616
564
|
renderMinThumb = () => {
|
|
617
|
-
|
|
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} />;
|
|
565
|
+
return <Thumb {...this.getThumbProps()} ref={this.minThumb} onTouchStart={this.onMinTouchStart} {...this.panResponder.panHandlers} />;
|
|
621
566
|
};
|
|
622
567
|
renderThumb = () => {
|
|
623
|
-
|
|
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} />;
|
|
568
|
+
return <Thumb {...this.getThumbProps()} ref={this.thumb} onTouchStart={this.onTouchStart} {...this.panResponder.panHandlers} />;
|
|
627
569
|
};
|
|
628
570
|
renderTrack() {
|
|
629
571
|
const {
|
|
@@ -665,18 +607,14 @@ class Slider extends PureComponent {
|
|
|
665
607
|
const {
|
|
666
608
|
containerStyle,
|
|
667
609
|
testID,
|
|
668
|
-
migrate
|
|
669
|
-
useRelativeDrag
|
|
610
|
+
migrate
|
|
670
611
|
} = this.props;
|
|
671
612
|
if (migrate) {
|
|
672
613
|
return <IncubatorSlider {...this.props} />;
|
|
673
614
|
}
|
|
674
|
-
|
|
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()}>
|
|
615
|
+
return <View style={[styles.container, containerStyle]} onLayout={this.onContainerLayout} onAccessibilityAction={this.onAccessibilityAction} testID={testID} {...this.getAccessibilityProps()}>
|
|
679
616
|
{this.renderTrack()}
|
|
617
|
+
<View style={styles.touchArea} onTouchEnd={this.handleTrackPress} />
|
|
680
618
|
{this.renderRangeThumb()}
|
|
681
619
|
{this.renderThumb()}
|
|
682
620
|
</View>;
|
|
@@ -698,5 +636,9 @@ const styles = StyleSheet.create({
|
|
|
698
636
|
},
|
|
699
637
|
trackDisableRTL: {
|
|
700
638
|
right: 0
|
|
639
|
+
},
|
|
640
|
+
touchArea: {
|
|
641
|
+
...StyleSheet.absoluteFillObject,
|
|
642
|
+
backgroundColor: 'transparent'
|
|
701
643
|
}
|
|
702
644
|
});
|
|
@@ -140,12 +140,6 @@
|
|
|
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
|
-
},
|
|
149
143
|
{
|
|
150
144
|
"name": "migrate",
|
|
151
145
|
"type": "boolean",
|
|
@@ -97,11 +97,6 @@ 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;
|
|
105
100
|
/**
|
|
106
101
|
* Whether to use the new Slider implementation using Reanimated
|
|
107
102
|
*/
|
|
@@ -9,6 +9,10 @@ import { ButtonDriver } from "../button/Button.driver.new";
|
|
|
9
9
|
import { ViewDriver } from "../view/View.driver.new";
|
|
10
10
|
export const TextFieldDriver = (props, options) => {
|
|
11
11
|
const driver = usePressableDriver(useComponentDriver(props, options));
|
|
12
|
+
const inputDriver = useComponentDriver({
|
|
13
|
+
renderTree: props.renderTree,
|
|
14
|
+
testID: `${props.testID}.input`
|
|
15
|
+
}, options);
|
|
12
16
|
const floatingPlaceholderDriver = TextDriver({
|
|
13
17
|
renderTree: props.renderTree,
|
|
14
18
|
testID: `${props.testID}.floatingPlaceholder`
|
|
@@ -41,30 +45,31 @@ export const TextFieldDriver = (props, options) => {
|
|
|
41
45
|
renderTree: props.renderTree,
|
|
42
46
|
testID: `${props.testID}.clearButton.container`
|
|
43
47
|
});
|
|
48
|
+
const getInputElement = () => inputDriver.queryElement() ?? driver.getElement();
|
|
44
49
|
const getValue = () => {
|
|
45
|
-
return
|
|
50
|
+
return getInputElement().props.value ?? getInputElement().props.defaultValue;
|
|
46
51
|
};
|
|
47
52
|
const changeText = text => {
|
|
48
|
-
fireEvent.changeText(
|
|
53
|
+
fireEvent.changeText(getInputElement(), text);
|
|
49
54
|
};
|
|
50
55
|
const focus = () => {
|
|
51
|
-
fireEvent(
|
|
56
|
+
fireEvent(getInputElement(), 'focus');
|
|
52
57
|
};
|
|
53
58
|
const blur = () => {
|
|
54
|
-
fireEvent(
|
|
59
|
+
fireEvent(getInputElement(), 'blur');
|
|
55
60
|
};
|
|
56
61
|
const isEnabled = () => {
|
|
57
|
-
return !
|
|
62
|
+
return !getInputElement().props.accessibilityState?.disabled;
|
|
58
63
|
};
|
|
59
64
|
const getPlaceholder = () => {
|
|
60
65
|
const exists = () => {
|
|
61
|
-
const hasPlaceholder = !!
|
|
66
|
+
const hasPlaceholder = !!getInputElement().props.placeholder;
|
|
62
67
|
const hasText = !!getValue();
|
|
63
68
|
return hasPlaceholder && (!hasText || hasText && floatingPlaceholderDriver.exists());
|
|
64
69
|
};
|
|
65
70
|
const getText = () => {
|
|
66
71
|
if (exists()) {
|
|
67
|
-
return
|
|
72
|
+
return getInputElement().props.placeholder;
|
|
68
73
|
}
|
|
69
74
|
};
|
|
70
75
|
return {
|
|
@@ -78,6 +78,8 @@ const TextField = props => {
|
|
|
78
78
|
readonly = false,
|
|
79
79
|
showMandatoryIndication,
|
|
80
80
|
clearButtonStyle,
|
|
81
|
+
testID,
|
|
82
|
+
accessibilityLabel: accessibilityLabelProp,
|
|
81
83
|
...others
|
|
82
84
|
} = usePreset(props);
|
|
83
85
|
const {
|
|
@@ -135,11 +137,31 @@ const TextField = props => {
|
|
|
135
137
|
const hasValue = fieldState.value !== undefined; // NOTE: not pressable if centered without a value (so can't center placeholder)
|
|
136
138
|
const inputStyle = useMemo(() => [typographyStyle, colorStyle, others.style, hasValue && centeredTextStyle], [typographyStyle, colorStyle, others.style, centeredTextStyle, hasValue]);
|
|
137
139
|
const dummyPlaceholderStyle = useMemo(() => [inputStyle, styles.dummyPlaceholder], [inputStyle]);
|
|
140
|
+
const defaultAccessibilityLabel = useMemo(() => {
|
|
141
|
+
const parts = [];
|
|
142
|
+
if (label) {
|
|
143
|
+
parts.push(label);
|
|
144
|
+
}
|
|
145
|
+
if (context.isMandatory) {
|
|
146
|
+
parts.push('required');
|
|
147
|
+
}
|
|
148
|
+
parts.push('textField');
|
|
149
|
+
if (helperText) {
|
|
150
|
+
parts.push(helperText);
|
|
151
|
+
} else if (placeholder) {
|
|
152
|
+
parts.push(placeholder);
|
|
153
|
+
}
|
|
154
|
+
if (showCharCounter && others.maxLength) {
|
|
155
|
+
parts.push(`you can enter up to ${others.maxLength} characters`);
|
|
156
|
+
}
|
|
157
|
+
return parts.join(', ');
|
|
158
|
+
}, [label, context.isMandatory, helperText, placeholder, showCharCounter, others.maxLength]);
|
|
159
|
+
const accessibilityLabel = accessibilityLabelProp ?? defaultAccessibilityLabel;
|
|
138
160
|
return <FieldContext.Provider value={context}>
|
|
139
|
-
<View {...containerProps} style={[margins, positionStyle, containerStyle, centeredContainerStyle]}>
|
|
161
|
+
<View {...containerProps} testID={testID} accessible accessibilityLabel={accessibilityLabel} style={[margins, positionStyle, containerStyle, centeredContainerStyle]}>
|
|
140
162
|
<View row spread style={centeredContainerStyle}>
|
|
141
|
-
<Label label={label} labelColor={labelColor} labelStyle={_labelStyle} labelProps={labelProps} floatingPlaceholder={floatingPlaceholder} validationMessagePosition={validationMessagePosition} testID={`${
|
|
142
|
-
{validationMessagePosition === ValidationMessagePosition.TOP && <ValidationMessage enableErrors={enableErrors} validate={others.validate} validationMessage={others.validationMessage} validationMessageStyle={_validationMessageStyle} retainValidationSpace={retainValidationSpace && retainTopMessageSpace} testID={`${
|
|
163
|
+
<Label label={label} labelColor={labelColor} labelStyle={_labelStyle} labelProps={labelProps} floatingPlaceholder={floatingPlaceholder} validationMessagePosition={validationMessagePosition} testID={`${testID}.label`} showMandatoryIndication={showMandatoryIndication} enableErrors={enableErrors} />
|
|
164
|
+
{validationMessagePosition === ValidationMessagePosition.TOP && <ValidationMessage enableErrors={enableErrors} validate={others.validate} validationMessage={others.validationMessage} validationMessageStyle={_validationMessageStyle} retainValidationSpace={retainValidationSpace && retainTopMessageSpace} testID={`${testID}.validationMessage`} />}
|
|
143
165
|
{topTrailingAccessory && <View>{topTrailingAccessory}</View>}
|
|
144
166
|
</View>
|
|
145
167
|
<View style={[paddings, fieldStyle]} row centerV centerH={centered}>
|
|
@@ -158,26 +180,26 @@ const TextField = props => {
|
|
|
158
180
|
{Constants.isAndroid && centered && <Text marginR-s1 style={dummyPlaceholderStyle}>
|
|
159
181
|
{placeholder}
|
|
160
182
|
</Text>}
|
|
161
|
-
{floatingPlaceholder && <FloatingPlaceholder defaultValue={others.defaultValue} placeholder={placeholder} floatingPlaceholderStyle={_floatingPlaceholderStyle} floatingPlaceholderColor={floatingPlaceholderColor} floatOnFocus={floatOnFocus} validationMessagePosition={validationMessagePosition} extraOffset={leadingAccessoryMeasurements?.width} testID={`${
|
|
183
|
+
{floatingPlaceholder && <FloatingPlaceholder defaultValue={others.defaultValue} placeholder={placeholder} floatingPlaceholderStyle={_floatingPlaceholderStyle} floatingPlaceholderColor={floatingPlaceholderColor} floatOnFocus={floatOnFocus} validationMessagePosition={validationMessagePosition} extraOffset={leadingAccessoryMeasurements?.width} testID={`${testID}.floatingPlaceholder`} showMandatoryIndication={showMandatoryIndication} />}
|
|
162
184
|
<Input hitSlop={{
|
|
163
185
|
top: 20,
|
|
164
186
|
bottom: 20
|
|
165
|
-
}} placeholderTextColor={hidePlaceholder ? 'transparent' : placeholderTextColor} value={fieldState.value} {...others} readonly={readonly} style={inputStyle} onFocus={onFocus} onBlur={onBlur} onChangeText={onChangeText} placeholder={placeholder} hint={hint} showMandatoryIndication={showMandatoryIndication && !label} />
|
|
187
|
+
}} placeholderTextColor={hidePlaceholder ? 'transparent' : placeholderTextColor} value={fieldState.value} {...others} testID={`${testID}.input`} readonly={readonly} style={inputStyle} onFocus={onFocus} onBlur={onBlur} onChangeText={onChangeText} placeholder={placeholder} hint={hint} showMandatoryIndication={showMandatoryIndication && !label} />
|
|
166
188
|
</View>}
|
|
167
|
-
{showClearButton && <ClearButton onClear={onClear} testID={`${
|
|
189
|
+
{showClearButton && <ClearButton onClear={onClear} testID={`${testID}.clearButton`} onChangeText={onChangeText} clearButtonStyle={clearButtonStyle} />}
|
|
168
190
|
{trailingAccessory}
|
|
169
191
|
{/* </View> */}
|
|
170
192
|
</View>
|
|
171
193
|
<View row spread center={centered}>
|
|
172
194
|
<View flex={!centered} flexG={centered} marginR-s4={showCharCounter}>
|
|
173
|
-
{validationMessagePosition === ValidationMessagePosition.BOTTOM && <ValidationMessage enableErrors={enableErrors} validate={others.validate} validationMessage={others.validationMessage} validationIcon={validationIcon} validationMessageStyle={_validationMessageStyle} retainValidationSpace={retainValidationSpace} testID={`${
|
|
174
|
-
{helperText && <Text $textNeutralHeavy subtext marginT-s1 testID={`${
|
|
195
|
+
{validationMessagePosition === ValidationMessagePosition.BOTTOM && <ValidationMessage enableErrors={enableErrors} validate={others.validate} validationMessage={others.validationMessage} validationIcon={validationIcon} validationMessageStyle={_validationMessageStyle} retainValidationSpace={retainValidationSpace} testID={`${testID}.validationMessage`} />}
|
|
196
|
+
{helperText && <Text $textNeutralHeavy subtext marginT-s1 testID={`${testID}.helperText`}>
|
|
175
197
|
{helperText}
|
|
176
198
|
</Text>}
|
|
177
199
|
{bottomAccessory}
|
|
178
200
|
</View>
|
|
179
201
|
<View>
|
|
180
|
-
{showCharCounter && <CharCounter maxLength={others.maxLength} charCounterStyle={charCounterStyle} testID={`${
|
|
202
|
+
{showCharCounter && <CharCounter maxLength={others.maxLength} charCounterStyle={charCounterStyle} testID={`${testID}.charCounter`} />}
|
|
181
203
|
</View>
|
|
182
204
|
</View>
|
|
183
205
|
</View>
|
|
@@ -31,9 +31,7 @@ const Thumb = props => {
|
|
|
31
31
|
stepInterpolatedValue,
|
|
32
32
|
gap = 0,
|
|
33
33
|
secondary,
|
|
34
|
-
enableShadow
|
|
35
|
-
pointerEvents,
|
|
36
|
-
isActive
|
|
34
|
+
enableShadow
|
|
37
35
|
} = props;
|
|
38
36
|
const rtlFix = Constants.isRTL ? -1 : 1;
|
|
39
37
|
const isPressed = useSharedValue(false);
|
|
@@ -42,7 +40,7 @@ const Thumb = props => {
|
|
|
42
40
|
height: THUMB_SIZE
|
|
43
41
|
});
|
|
44
42
|
const lastOffset = useSharedValue(0);
|
|
45
|
-
const gesture = Gesture.Pan().
|
|
43
|
+
const gesture = Gesture.Pan().onBegin(() => {
|
|
46
44
|
onSeekStart?.();
|
|
47
45
|
isPressed.value = true;
|
|
48
46
|
lastOffset.value = offset.value;
|
|
@@ -66,16 +64,15 @@ const Thumb = props => {
|
|
|
66
64
|
offset.value = Math.round(offset.value / stepInterpolatedValue.value) * stepInterpolatedValue.value;
|
|
67
65
|
}
|
|
68
66
|
});
|
|
69
|
-
gesture.enabled(!disabled
|
|
67
|
+
gesture.enabled(!disabled);
|
|
70
68
|
const animatedStyle = useAnimatedStyle(() => {
|
|
71
|
-
const
|
|
72
|
-
const customStyle = active ? activeStyle?.value : defaultStyle?.value;
|
|
69
|
+
const customStyle = isPressed.value ? activeStyle?.value : defaultStyle?.value;
|
|
73
70
|
return {
|
|
74
71
|
...customStyle,
|
|
75
72
|
transform: [{
|
|
76
73
|
translateX: (offset.value - thumbSize.value.width / 2) * rtlFix
|
|
77
74
|
}, {
|
|
78
|
-
scale: withSpring(!disableActiveStyling &&
|
|
75
|
+
scale: withSpring(!disableActiveStyling && isPressed.value ? 1.3 : 1)
|
|
79
76
|
}]
|
|
80
77
|
};
|
|
81
78
|
});
|
|
@@ -89,7 +86,7 @@ const Thumb = props => {
|
|
|
89
86
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
90
87
|
}, []);
|
|
91
88
|
return <GestureDetector gesture={gesture}>
|
|
92
|
-
<View reanimated
|
|
89
|
+
<View reanimated style={[styles.thumbPosition, enableShadow && styles.thumbShadow, animatedStyle]} hitSlop={hitSlop} onLayout={onThumbLayout} />
|
|
93
90
|
</GestureDetector>;
|
|
94
91
|
};
|
|
95
92
|
const styles = StyleSheet.create({
|
|
@@ -126,11 +126,6 @@ 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;
|
|
134
129
|
/**
|
|
135
130
|
* Control the throttle time of the onValueChange and onRangeChange callbacks
|
|
136
131
|
*/
|
|
@@ -2,14 +2,13 @@ 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
|
|
5
|
+
import { GestureHandlerRootView } 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";
|
|
13
12
|
import Thumb from "./Thumb";
|
|
14
13
|
import Track from "./Track";
|
|
15
14
|
var ThumbType = /*#__PURE__*/function (ThumbType) {
|
|
@@ -55,7 +54,6 @@ const Slider = React.memo(props => {
|
|
|
55
54
|
accessible = true,
|
|
56
55
|
testID,
|
|
57
56
|
enableThumbShadow = true,
|
|
58
|
-
useRelativeDrag,
|
|
59
57
|
throttleTime = 200
|
|
60
58
|
} = themeProps;
|
|
61
59
|
const accessibilityProps = useMemo(() => {
|
|
@@ -217,36 +215,6 @@ const Slider = React.memo(props => {
|
|
|
217
215
|
runOnJS(onSeekEnd)();
|
|
218
216
|
}
|
|
219
217
|
};
|
|
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);
|
|
250
218
|
const trackAnimatedStyles = useAnimatedStyle(() => {
|
|
251
219
|
if (useRange) {
|
|
252
220
|
return {
|
|
@@ -268,22 +236,15 @@ const Slider = React.memo(props => {
|
|
|
268
236
|
|
|
269
237
|
/** renders */
|
|
270
238
|
const renderThumb = type => {
|
|
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}
|
|
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} />;
|
|
272
240
|
};
|
|
273
241
|
const _renderTrack = () => {
|
|
274
|
-
return <Track renderTrack={renderTrack} onLayout={onTrackLayout} onPress={
|
|
242
|
+
return <Track renderTrack={renderTrack} onLayout={onTrackLayout} onPress={onTrackPress} animatedStyle={trackAnimatedStyles} disabled={disabled} maximumTrackTintColor={maximumTrackTintColor} minimumTrackTintColor={minimumTrackTintColor} trackStyle={trackStyle} />;
|
|
275
243
|
};
|
|
276
|
-
|
|
244
|
+
return <GestureHandlerRootView style={[styles.container, containerStyle, shouldDisableRTL && styles.disableRTL]} testID={testID} {...accessibilityProps}>
|
|
277
245
|
{_renderTrack()}
|
|
278
246
|
{renderThumb(ThumbType.DEFAULT)}
|
|
279
247
|
{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()}
|
|
287
248
|
</GestureHandlerRootView>;
|
|
288
249
|
});
|
|
289
250
|
Slider.displayName = 'Incubator.Slider';
|
|
@@ -293,10 +254,6 @@ const styles = StyleSheet.create({
|
|
|
293
254
|
height: THUMB_SIZE + SHADOW_RADIUS,
|
|
294
255
|
justifyContent: 'center'
|
|
295
256
|
},
|
|
296
|
-
gestureContainer: {
|
|
297
|
-
flex: 1,
|
|
298
|
-
justifyContent: 'center'
|
|
299
|
-
},
|
|
300
257
|
disableRTL: {
|
|
301
258
|
transform: [{
|
|
302
259
|
scaleX: -1
|