diff --git a/packages/pluggableWidgets/slider-native/package.json b/packages/pluggableWidgets/slider-native/package.json
index 88e05de9c..600fc1440 100644
--- a/packages/pluggableWidgets/slider-native/package.json
+++ b/packages/pluggableWidgets/slider-native/package.json
@@ -21,11 +21,9 @@
"dependencies": {
"@mendix/piw-native-utils-internal": "*",
"@mendix/piw-utils-internal": "*",
- "@ptomasroos/react-native-multi-slider": "^1.0.0",
- "prop-types": "^15.7.2"
+ "@react-native-community/slider": "^5.2.0"
},
"devDependencies": {
- "@mendix/pluggable-widgets-tools": "*",
- "@types/ptomasroos__react-native-multi-slider": "^0.0.1"
+ "@mendix/pluggable-widgets-tools": "*"
}
}
diff --git a/packages/pluggableWidgets/slider-native/src/Marker.tsx b/packages/pluggableWidgets/slider-native/src/Marker.tsx
deleted file mode 100644
index b2a02bfcf..000000000
--- a/packages/pluggableWidgets/slider-native/src/Marker.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { MarkerProps } from "@ptomasroos/react-native-multi-slider";
-import { ReactElement } from "react";
-import { Platform, StyleSheet, TouchableHighlight, View } from "react-native";
-
-export function Marker(props: MarkerProps & { testID: string }): ReactElement {
- return (
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- markerStyle: {
- ...Platform.select({
- ios: {
- height: 30,
- width: 30,
- borderRadius: 30,
- borderWidth: 1,
- borderColor: "#DDDDDD",
- backgroundColor: "#FFFFFF",
- shadowColor: "#000000",
- shadowOffset: {
- width: 0,
- height: 3
- },
- shadowRadius: 1,
- shadowOpacity: 0.2
- },
- android: {
- height: 12,
- width: 12,
- borderRadius: 12,
- backgroundColor: "#0D8675"
- }
- })
- },
- pressedMarkerStyle: {
- ...Platform.select({
- ios: {},
- android: {
- height: 20,
- width: 20,
- borderRadius: 20
- }
- })
- },
- disabled: {
- backgroundColor: "#d3d3d3"
- }
-});
diff --git a/packages/pluggableWidgets/slider-native/src/Slider.tsx b/packages/pluggableWidgets/slider-native/src/Slider.tsx
index 685b71d53..4aaa09c86 100644
--- a/packages/pluggableWidgets/slider-native/src/Slider.tsx
+++ b/packages/pluggableWidgets/slider-native/src/Slider.tsx
@@ -1,20 +1,17 @@
import { available, flattenStyles, toNumber, unavailable } from "@mendix/piw-native-utils-internal";
import { executeAction } from "@mendix/piw-utils-internal";
-import { ValueStatus, Option } from "mendix";
-import MultiSlider, { MarkerProps } from "@ptomasroos/react-native-multi-slider";
-import { ReactElement, useCallback, useRef, useState, JSX } from "react";
-import { LayoutChangeEvent, Text, View } from "react-native";
+import { ValueStatus } from "mendix";
+import RNSlider from "@react-native-community/slider";
+import { ReactElement, useCallback, useRef } from "react";
+import { Text, View } from "react-native";
import { Big } from "big.js";
import { SliderProps } from "../typings/SliderProps";
-import { Marker } from "./Marker";
import { defaultSliderStyle, SliderStyle } from "./ui/Styles";
export type Props = SliderProps;
export function Slider(props: Props): ReactElement {
- const [width, setWidth] = useState();
-
const lastValue = useRef(toNumber(props.valueAttribute));
const value = toNumber(props.valueAttribute);
@@ -22,43 +19,34 @@ export function Slider(props: Props): ReactElement {
const validProps = validationMessages.length === 0;
const editable = props.editable !== "never" && !props.valueAttribute.readOnly && validProps;
const styles = flattenStyles(defaultSliderStyle, props.style);
- // We have to fix the decimal count ourselves because of an unresolved bug in the library: https://github.com/ptomasroos/react-native-multi-slider/issues/211
+
const decimalCount = useCallback(
- (value: Option): number => value?.toString().split(".")?.[1]?.length || 0,
+ (val: Big | undefined): number => val?.toString().split(".")?.[1]?.length || 0,
[]
);
- const customMarker =
- () =>
- (markerProps: MarkerProps): JSX.Element =>
- ;
-
- const onLayout = useCallback((event: LayoutChangeEvent): void => {
- setWidth(event.nativeEvent.layout.width);
- }, []);
-
const onSlide = useCallback(
- (values: number[]): void => {
- if (values[0] === null) {
+ (newValue: number): void => {
+ if (newValue === null) {
return;
}
if (props.stepSize.status === ValueStatus.Available) {
- props.valueAttribute.setValue(new Big(values[0].toFixed(decimalCount(props.stepSize.value))));
+ props.valueAttribute.setValue(new Big(newValue.toFixed(decimalCount(props.stepSize.value))));
}
},
[props.valueAttribute, props.stepSize, decimalCount]
);
const onChange = useCallback(
- (values: number[]): void => {
- if (values[0] === null || lastValue.current === values[0]) {
+ (newValue: number): void => {
+ if (newValue === null || lastValue.current === newValue) {
return;
}
- lastValue.current = values[0];
+ lastValue.current = newValue;
if (props.stepSize.status === ValueStatus.Available) {
- props.valueAttribute.setValue(new Big(values[0].toFixed(decimalCount(props.stepSize.value))));
+ props.valueAttribute.setValue(new Big(newValue.toFixed(decimalCount(props.stepSize.value))));
}
executeAction(props.onChange);
@@ -67,22 +55,29 @@ export function Slider(props: Props): ReactElement {
);
return (
-
-
+
{!validProps && {validationMessages.join("\n")}}
{props.valueAttribute.validation && (
diff --git a/packages/pluggableWidgets/slider-native/src/__tests__/Slider.spec.tsx b/packages/pluggableWidgets/slider-native/src/__tests__/Slider.spec.tsx
index bf74fc3e6..ab0b93472 100644
--- a/packages/pluggableWidgets/slider-native/src/__tests__/Slider.spec.tsx
+++ b/packages/pluggableWidgets/slider-native/src/__tests__/Slider.spec.tsx
@@ -1,8 +1,6 @@
import { actionValue, dynamicValue, EditableValueBuilder } from "@mendix/piw-utils-internal";
import { Big } from "big.js";
-import { View } from "react-native";
-import { fireEvent, render, RenderAPI } from "@testing-library/react-native";
-import { ReactTestInstance } from "react-test-renderer";
+import { fireEvent, render } from "@testing-library/react-native";
import { ValueStatus, DynamicValue } from "mendix";
import { Props, Slider } from "../Slider";
@@ -93,29 +91,6 @@ describe("Slider", () => {
expect(component.queryByText("The current value can not be greater than the maximum value.")).not.toBeNull();
});
- it("renders with the width of the parent view", () => {
- const component = render(
-
- );
- fireEvent(component.getByTestId("slider-test"), "layout", { nativeEvent: { layout: { width: 100 } } });
- expect(component.getByTestId("slider-test").findByProps({ sliderLength: 100 })).not.toBeNull();
- });
-
it("renders a validation message", () => {
const value = new EditableValueBuilder().withValidation("Invalid").build();
const component = render();
@@ -123,61 +98,31 @@ describe("Slider", () => {
expect(component.queryByText("Invalid")).not.toBeNull();
});
- it.skip("handles an invalid step size", () => {
- const component = render();
- expect(component.getByTestId("slider-test").findByProps({ step: 1 })).not.toBeNull();
- });
-
- it("changes the value when swiping", () => {
+ it("changes the value when sliding completes", () => {
const onChangeAction = actionValue();
const component = render();
- fireEvent(getHandle(component), "responderGrant", { touchHistory: { touchBank: [] } });
- fireEvent(getHandle(component), "responderMove", responderMove(50));
-
- expect(onChangeAction.execute).not.toHaveBeenCalled();
-
- fireEvent(getHandle(component), "responderRelease", {});
+ const slider = component.getByTestId("slider-test$slider");
+ fireEvent(slider, "onSlidingComplete", 190);
expect(defaultProps.valueAttribute.setValue).toHaveBeenCalledWith(new Big(190));
expect(onChangeAction.execute).toHaveBeenCalledTimes(1);
});
+ it("calls onValueChange while sliding", () => {
+ const component = render();
+
+ const slider = component.getByTestId("slider-test$slider");
+ fireEvent(slider, "onValueChange", 150);
+
+ expect(defaultProps.valueAttribute.setValue).toHaveBeenCalledWith(new Big(150));
+ });
+
it("does not change the value when non editable", () => {
const onChangeAction = actionValue();
const component = render();
- fireEvent(getHandle(component), "responderGrant", { touchHistory: { touchBank: [] } });
- fireEvent(getHandle(component), "responderMove", responderMove(50));
- fireEvent(getHandle(component), "responderRelease", {});
-
- expect(onChangeAction.execute).not.toHaveBeenCalled();
- expect(defaultProps.valueAttribute.setValue).not.toHaveBeenCalled();
+ const slider = component.getByTestId("slider-test$slider");
+ expect(slider.props.disabled).toBe(true);
});
});
-
-function getHandle(component: RenderAPI): ReactTestInstance {
- return component
- .getByTestId("slider-test")
- .findAllByType(View)
- .filter(instance => instance.props.onMoveShouldSetResponder)[0];
-}
-
-function responderMove(dx: number): any {
- return {
- touchHistory: {
- numberActiveTouches: 1,
- indexOfSingleActiveTouch: 0,
- touchBank: [
- {
- touchActive: true,
- currentTimeStamp: Date.now(),
- currentPageX: dx,
- currentPageY: 0,
- previousPageX: 0,
- previousPageY: 0
- }
- ]
- }
- };
-}
diff --git a/packages/pluggableWidgets/slider-native/src/__tests__/__snapshots__/Slider.spec.tsx.snap b/packages/pluggableWidgets/slider-native/src/__tests__/__snapshots__/Slider.spec.tsx.snap
index 927cf2876..b5dc18283 100644
--- a/packages/pluggableWidgets/slider-native/src/__tests__/__snapshots__/Slider.spec.tsx.snap
+++ b/packages/pluggableWidgets/slider-native/src/__tests__/__snapshots__/Slider.spec.tsx.snap
@@ -2,162 +2,63 @@
exports[`Slider renders 1`] = `
-
-
-
-
-
-
-
-
-
-
-
+ tapToSeek={false}
+ testID="slider-test$slider"
+ thumbTintColor="rgb(98,0,238)"
+ upperLimit={9007199254740991}
+ value={140}
+ />
`;
diff --git a/packages/pluggableWidgets/slider-native/src/ui/Styles.ts b/packages/pluggableWidgets/slider-native/src/ui/Styles.ts
index 2edd936cd..bfc5eb88c 100644
--- a/packages/pluggableWidgets/slider-native/src/ui/Styles.ts
+++ b/packages/pluggableWidgets/slider-native/src/ui/Styles.ts
@@ -1,6 +1,15 @@
import { Style } from "@mendix/piw-native-utils-internal";
import { Platform, TextStyle, ViewStyle } from "react-native";
+interface SliderColors {
+ minimumTrackTintColor?: string;
+ maximumTrackTintColor?: string;
+ thumbTintColor?: string;
+ minimumTrackTintColorDisabled?: string;
+ maximumTrackTintColorDisabled?: string;
+ thumbTintColorDisabled?: string;
+}
+
export interface SliderStyle extends Style {
container: ViewStyle;
track: ViewStyle;
@@ -11,6 +20,8 @@ export interface SliderStyle extends Style {
markerActive: ViewStyle;
markerDisabled: ViewStyle;
validationMessage: TextStyle;
+ /** Color configuration for the native slider component. */
+ sliderColors: SliderColors;
}
const blue = "rgb(0,122,255)";
@@ -72,5 +83,13 @@ export const defaultSliderStyle: SliderStyle = {
},
validationMessage: {
color: "#ed1c24"
+ },
+ sliderColors: {
+ minimumTrackTintColor: Platform.select({ ios: blue, android: purple }),
+ maximumTrackTintColor: Platform.select({ ios: blueLighter, android: purpleLighter }),
+ thumbTintColor: Platform.select({ android: purple }),
+ minimumTrackTintColorDisabled: Platform.select({ ios: blue, android: "#AAA" }),
+ maximumTrackTintColorDisabled: Platform.select({ ios: blueLighter, android: "#EEE" }),
+ thumbTintColorDisabled: Platform.select({ android: "#AAA" })
}
};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4e4afb67d..dd049e45f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -858,19 +858,13 @@ importers:
'@mendix/piw-utils-internal':
specifier: '*'
version: link:../../tools/piw-utils-internal
- '@ptomasroos/react-native-multi-slider':
- specifier: ^1.0.0
- version: 1.0.0(patch_hash=b5e11465e4305f5284e90a78fc4575401f791921f34dbbafb9831f19ecae94da)(prop-types@15.8.1)(react-native@0.78.2(@babel/core@7.28.0)(@babel/preset-env@7.28.0(@babel/core@7.28.0))(@react-native-community/cli@14.1.0(typescript@5.8.3))(@types/react@19.0.14)(react@19.0.0))(react@19.0.0)
- prop-types:
- specifier: ^15.7.2
- version: 15.8.1
+ '@react-native-community/slider':
+ specifier: ^5.2.0
+ version: 5.2.0
devDependencies:
'@mendix/pluggable-widgets-tools':
specifier: 10.21.1
version: 10.21.1(patch_hash=d453a814eaf17b7f8d4239b31793af4ff954c6c420ad02ae92cce4efcb6c449f)(@jest/transform@29.7.0)(@jest/types@30.0.1)(@types/babel__core@7.20.5)(@types/node@20.19.9)(encoding@0.1.13)(jest-util@30.0.2)(picomatch@4.0.3)(react-dom@19.0.0(react@19.0.0))(react-native@0.78.2(@babel/core@7.28.0)(@babel/preset-env@7.28.0(@babel/core@7.28.0))(@react-native-community/cli@14.1.0(typescript@5.8.3))(@types/react@19.0.14)(react@19.0.0))(react@19.0.0)(tslib@2.8.1)
- '@types/ptomasroos__react-native-multi-slider':
- specifier: ^0.0.1
- version: 0.0.1(@babel/core@7.28.0)(@babel/preset-env@7.28.0(@babel/core@7.28.0))(@react-native-community/cli@14.1.0(typescript@5.8.3))(react@19.0.0)
packages/pluggableWidgets/switch-native:
dependencies:
@@ -2200,6 +2194,9 @@ packages:
peerDependencies:
react-native: 0.78.2
+ '@react-native-community/slider@5.2.0':
+ resolution: {integrity: sha512-484sH8aWEaSjxaZ7HT3YZ8CKDcNes2synko1vdEz5DFEdvKAduxKJTj22L/qBMD7rtIkfbX69DMzWDAGbOAV6w==}
+
'@react-native-firebase/app@20.1.0':
resolution: {integrity: sha512-FCcTtmfz/Bk2laOEKOiUrQUkAnzerkRml7d3kZzJSxaBWLFxpWJQnnXqGZmD8hNWio2QEauB8llUD71KiDk+sw==}
peerDependencies:
@@ -9655,6 +9652,8 @@ snapshots:
dependencies:
react-native: 0.78.2(@babel/core@7.28.0)(@babel/preset-env@7.28.0(@babel/core@7.28.0))(@react-native-community/cli@14.1.0(typescript@5.8.3))(@types/react@19.0.14)(react@19.0.0)
+ '@react-native-community/slider@5.2.0': {}
+
'@react-native-firebase/app@20.1.0(react-native@0.78.2(@babel/core@7.28.0)(@babel/preset-env@7.28.0(@babel/core@7.28.0))(@react-native-community/cli@14.1.0(typescript@5.8.3))(@types/react@19.0.14)(react@19.0.0))(react@19.0.0)':
dependencies:
opencollective-postinstall: 2.0.3