Skip to content

Commit c0a81b8

Browse files
authored
[IOPLT-968] Add dark mode version to NumberPad component (#425)
> [!note] > To avoid confusion between light and dark mode, this PR introduces a breaking change: > `dark` → `primary` > `light` → `neutral` ## Short description This PR adds the dark mode version to the `NumberPad` component ## List of changes proposed in this pull request - Change `variant` accepted values of the `NumberPad` and `CodeInput` components - Add dark mode version to the `NumberButton` component - Add haptic feedback when the citizen presses the `NumberButton` ### Preview https://github.com/user-attachments/assets/2005aed5-b4d9-4492-a30c-48b9163d1212 ## How to test 1. Launch the example app 2. Go to the **Number Pad** page. 3. Enable/Disable primary background. Enable/Disable dark mode.
1 parent e34f313 commit c0a81b8

File tree

5 files changed

+47
-31
lines changed

5 files changed

+47
-31
lines changed

example/src/pages/NumberPad.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const NumberPadScreen = () => {
8080
<CodeInput
8181
value={value}
8282
length={PIN_LENGTH}
83-
variant={blueBackground ? "light" : "dark"}
83+
variant={blueBackground ? "primary" : "neutral"}
8484
onValueChange={setValue}
8585
onValidate={v => v === "123456"}
8686
/>
@@ -89,7 +89,7 @@ export const NumberPadScreen = () => {
8989
deleteAccessibilityLabel="Delete"
9090
onNumberPress={onNumberPress}
9191
onDeletePress={onDeletePress}
92-
variant={blueBackground ? "dark" : "light"}
92+
variant={blueBackground ? "primary" : "neutral"}
9393
biometricType="FACE_ID"
9494
biometricAccessibilityLabel="Face ID"
9595
onBiometricPress={onBiometricPress}

src/components/codeInput/CodeInput.tsx

+11-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type CodeInputProps = {
1111
onValueChange: (value: string) => void;
1212
length: number;
1313
onValidate: (value: string) => boolean;
14-
variant?: "light" | "dark";
14+
variant?: "primary" | "neutral";
1515
};
1616

1717
const DOT_SIZE = 16;
@@ -47,35 +47,39 @@ export const CodeInput = ({
4747
length,
4848
value,
4949
onValueChange,
50-
variant = "light",
50+
variant = "primary",
5151
onValidate
5252
}: CodeInputProps) => {
5353
const [status, setStatus] = useState<"default" | "error">("default");
54-
const { themeType } = useIOThemeContext();
54+
const { themeType, theme } = useIOThemeContext();
5555

5656
const { translate, animatedStyle, shakeAnimation } = useErrorShakeAnimation();
5757

5858
/* Empty Dot
5959
- Right color depending on both theme and variant */
6060
const emptyDotColorLightBg = IOColors["grey-650"];
61-
const emptyDotColorDarkBg = hexToRgba(IOColors.white, 0.75);
61+
const emptyDotColorDarkBg = IOColors["grey-300"];
62+
const emptyDotColorAccentBg = hexToRgba(IOColors.white, 0.75);
63+
6264
const emptyDotColorThemeBased =
6365
themeType === "light" ? emptyDotColorLightBg : emptyDotColorDarkBg;
6466

6567
const emptyDotColor =
66-
variant === "light" ? emptyDotColorDarkBg : emptyDotColorThemeBased;
68+
variant === "primary" ? emptyDotColorAccentBg : emptyDotColorThemeBased;
6769

6870
/* Filled Dot
6971
- Right color depending on theme, variant and status */
7072
const filledDotColorLightBg = IOColors.black;
7173
const filledDotColorDarkBg = IOColors.white;
74+
const filledDotColorError =
75+
variant === "primary" ? IOColors["error-400"] : IOColors[theme.errorText];
7276
const filledDotColorThemeBased =
7377
themeType === "light" ? filledDotColorLightBg : filledDotColorDarkBg;
7478

7579
const filledDotColor =
7680
status === "error"
77-
? IOColors["error-600"]
78-
: variant === "light"
81+
? filledDotColorError
82+
: variant === "primary"
7983
? filledDotColorDarkBg
8084
: filledDotColorThemeBased;
8185

src/components/numberpad/NumberButton.tsx

+30-18
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1-
import React, { memo, useCallback } from "react";
1+
import React, { memo, useCallback, useMemo } from "react";
22
import { Pressable } from "react-native";
3+
import ReactNativeHapticFeedback from "react-native-haptic-feedback";
34
import Animated, {
45
interpolateColor,
56
useAnimatedStyle,
67
useReducedMotion
78
} from "react-native-reanimated";
89
import {
10+
hexToRgba,
911
IOColors,
1012
IONumberPadButtonStyles,
11-
useIONewTypeface
13+
useIONewTypeface,
14+
useIOTheme
1215
} from "../../core";
1316
import { useScaleAnimation } from "../../hooks";
1417
import { IOText } from "../typography";
1518

16-
type NumberButtonVariantType = "light" | "dark";
19+
type NumberButtonVariantType = "neutral" | "primary";
1720

1821
type NumberButtonProps = {
1922
/**
20-
* Used to choose the component color variant between `dark` and `light`.
23+
* Used to choose the component color variant between `neutral` and `primary`.
2124
*/
2225
variant: NumberButtonVariantType;
2326
/**
@@ -38,31 +41,39 @@ type ColorMapVariant = {
3841
foreground: IOColors;
3942
};
4043

41-
const colorMap: Record<NumberButtonVariantType, ColorMapVariant> = {
42-
light: {
43-
background: IOColors["grey-50"],
44-
pressed: IOColors["grey-200"],
45-
foreground: "blueIO-500"
46-
},
47-
dark: {
48-
background: IOColors["blueIO-400"],
49-
pressed: IOColors["blueIO-200"],
50-
foreground: "white"
51-
}
52-
};
53-
5444
/**
5545
* Based on a `Pressable` element, it displays a number button with animations on press In and Out.
5646
*
5747
* @returns {JSX.Element} The rendered `NumberButton`
5848
*/
5949
export const NumberButton = memo(
6050
({ number, variant, onPress }: NumberButtonProps) => {
51+
const theme = useIOTheme();
52+
6153
const { progress, onPressIn, onPressOut, scaleAnimatedStyle } =
62-
useScaleAnimation("slight");
54+
useScaleAnimation("medium");
6355
const reducedMotion = useReducedMotion();
6456
const { newTypefaceEnabled } = useIONewTypeface();
6557

58+
const colorMap: Record<NumberButtonVariantType, ColorMapVariant> = useMemo(
59+
() => ({
60+
neutral: {
61+
background: hexToRgba(
62+
IOColors[theme["interactiveElem-default"]],
63+
0.1
64+
),
65+
pressed: hexToRgba(IOColors[theme["interactiveElem-default"]], 0.35),
66+
foreground: theme["interactiveElem-default"]
67+
},
68+
primary: {
69+
background: hexToRgba(IOColors.white, 0.15),
70+
pressed: hexToRgba(IOColors.white, 0.5),
71+
foreground: "white"
72+
}
73+
}),
74+
[theme]
75+
);
76+
6677
// Interpolate animation values from `isPressed` values
6778
const pressedAnimationStyle = useAnimatedStyle(() => ({
6879
backgroundColor: interpolateColor(
@@ -73,6 +84,7 @@ export const NumberButton = memo(
7384
}));
7485

7586
const handleOnPress = useCallback(() => {
87+
ReactNativeHapticFeedback.trigger("impactLight");
7688
onPress(number);
7789
}, [number, onPress]);
7890

src/components/numberpad/NumberPad.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const mapIconSpecByBiometric: Record<
7070
* @returns {JSX.Element} The rendered numeric keyboard component.
7171
*/
7272
export const NumberPad = ({
73-
variant = "dark",
73+
variant = "primary",
7474
biometricType,
7575
biometricAccessibilityLabel,
7676
deleteAccessibilityLabel,
@@ -101,7 +101,7 @@ export const NumberPad = ({
101101
<ButtonWrapper key={item}>
102102
<IconButton
103103
icon="cancel"
104-
color={variant === "dark" ? "contrast" : "primary"}
104+
color={variant === "primary" ? "contrast" : "primary"}
105105
onPress={onDeletePress}
106106
accessibilityLabel={deleteAccessibilityLabel}
107107
/>
@@ -114,7 +114,7 @@ export const NumberPad = ({
114114
<IconButton
115115
icon={mapIconSpecByBiometric[biometricType].icon}
116116
iconSize={mapIconSpecByBiometric[biometricType].size}
117-
color={variant === "dark" ? "contrast" : "primary"}
117+
color={variant === "primary" ? "contrast" : "primary"}
118118
onPress={onBiometricPress}
119119
accessibilityLabel={biometricAccessibilityLabel}
120120
/>

stories/components/numberpad/NumberPad.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ type Story = StoryObj<typeof meta>;
2222
export const Light: Story = {
2323
args: {
2424
deleteAccessibilityLabel: "Delete",
25-
variant: "light"
25+
variant: "neutral"
2626
}
2727
};

0 commit comments

Comments
 (0)