@vellira-ui/react-native 2.28.0 → 2.30.0

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +342 -152
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -47,7 +47,7 @@ export function Example() {
47
47
 
48
48
  return (
49
49
  <View style={{ gap: 16 }}>
50
- <Input label='Email' value={email} onChange={setEmail} />
50
+ <Input label='Email' value={email} onValueChange={setEmail} />
51
51
  <Checkbox
52
52
  label='Accept terms'
53
53
  description='Required to create an account.'
package/dist/index.js CHANGED
@@ -944,6 +944,7 @@ import { useControllableState as useControllableState2 } from "@vellira-ui/core"
944
944
  import { View as View11 } from "react-native";
945
945
 
946
946
  // src/patterns/FormField/FormField.tsx
947
+ import { useId } from "react";
947
948
  import { Text as Text6, View as View10 } from "react-native";
948
949
 
949
950
  // src/patterns/FormField/FormField.styles.ts
@@ -954,14 +955,14 @@ var createStyles12 = (theme) => StyleSheet12.create({
954
955
  width: "100%",
955
956
  minWidth: 0,
956
957
  alignSelf: "stretch",
957
- gap: theme.tokens.spacing[2]
958
+ gap: theme.components.formField.size.md.gap
958
959
  },
959
960
  label: {
960
961
  color: theme.components.formField.label.fg,
961
962
  fontFamily: theme.tokens.typography.family.medium,
962
- fontSize: theme.tokens.typography.size.md,
963
+ fontSize: theme.components.formField.size.md.labelFontSize,
963
964
  fontWeight: fontWeight(theme.tokens.typography.weight.medium),
964
- lineHeight: theme.tokens.typography.lineHeight.md
965
+ lineHeight: theme.components.formField.size.md.labelLineHeight
965
966
  },
966
967
  labelDisabled: {
967
968
  color: theme.components.formField.disabled.labelFg
@@ -970,11 +971,25 @@ var createStyles12 = (theme) => StyleSheet12.create({
970
971
  marginLeft: theme.tokens.spacing[1],
971
972
  color: theme.components.formField.requiredMark.fg
972
973
  },
974
+ optional: {
975
+ marginLeft: theme.components.formField.size.md.gap,
976
+ color: theme.components.formField.optional.fg,
977
+ fontFamily: theme.tokens.typography.family.regular,
978
+ fontSize: theme.components.formField.size.md.optionalFontSize,
979
+ lineHeight: theme.components.formField.size.md.optionalLineHeight
980
+ },
981
+ labelInfo: {
982
+ marginLeft: theme.components.formField.size.md.gap,
983
+ color: theme.components.formField.labelInfo.fg,
984
+ fontFamily: theme.tokens.typography.family.medium,
985
+ fontSize: theme.components.formField.size.md.labelInfoFontSize,
986
+ lineHeight: theme.components.formField.size.md.labelInfoSize
987
+ },
973
988
  description: {
974
989
  color: theme.components.formField.description.fg,
975
990
  fontFamily: theme.tokens.typography.family.regular,
976
- fontSize: theme.tokens.typography.size.sm,
977
- lineHeight: theme.tokens.typography.lineHeight.sm
991
+ fontSize: theme.components.formField.size.md.descriptionFontSize,
992
+ lineHeight: theme.components.formField.size.md.descriptionLineHeight
978
993
  },
979
994
  descriptionDisabled: {
980
995
  color: theme.components.formField.disabled.descriptionFg
@@ -992,22 +1007,32 @@ var createStyles12 = (theme) => StyleSheet12.create({
992
1007
  error: {
993
1008
  color: theme.components.formField.helperText.error.fg,
994
1009
  fontFamily: theme.tokens.typography.family.regular,
995
- fontSize: theme.tokens.typography.size.sm,
996
- lineHeight: theme.tokens.typography.lineHeight.sm
1010
+ fontSize: theme.components.formField.size.md.helperTextFontSize,
1011
+ lineHeight: theme.components.formField.size.md.helperTextLineHeight
997
1012
  },
998
1013
  helperTextDisabled: {
999
1014
  color: theme.components.formField.disabled.helperTextFg
1000
1015
  }
1001
1016
  });
1002
1017
 
1018
+ // src/patterns/FormField/FormFieldContext.ts
1019
+ import { createContext as createContext3, useContext as useContext3 } from "react";
1020
+ var FormFieldContext = createContext3(null);
1021
+ var useFormFieldContext = () => useContext3(FormFieldContext);
1022
+
1003
1023
  // src/patterns/FormField/FormField.tsx
1004
1024
  import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
1005
1025
  function FormField({
1026
+ id,
1006
1027
  label,
1007
1028
  description,
1008
1029
  error,
1009
1030
  required = false,
1010
1031
  disabled = false,
1032
+ invalid = false,
1033
+ size = "md",
1034
+ labelInfo,
1035
+ optionalText,
1011
1036
  children,
1012
1037
  style,
1013
1038
  controlStyle,
@@ -1016,18 +1041,46 @@ function FormField({
1016
1041
  errorStyle,
1017
1042
  ...rest
1018
1043
  }) {
1044
+ const { theme } = useTheme();
1019
1045
  const styles = useThemeStyles(createStyles12);
1046
+ const sizeTokens = theme.components.formField.size[size];
1047
+ const generatedId = useId();
1048
+ const controlId = id ?? generatedId;
1049
+ const labelId = label ? `${controlId}-label` : void 0;
1050
+ const descriptionId = description ? `${controlId}-description` : void 0;
1051
+ const errorId = error ? `${controlId}-error` : void 0;
1052
+ const isInvalid = invalid || Boolean(error);
1053
+ const contextValue = {
1054
+ controlId,
1055
+ labelId,
1056
+ descriptionId,
1057
+ errorId,
1058
+ required,
1059
+ disabled,
1060
+ invalid: isInvalid,
1061
+ size,
1062
+ ariaLabelledBy: labelId,
1063
+ ariaDescribedBy: [descriptionId, errorId].filter(Boolean).join(" ") || void 0
1064
+ };
1020
1065
  return /* @__PURE__ */ jsxs7(
1021
1066
  View10,
1022
1067
  {
1023
1068
  ...rest,
1024
1069
  accessibilityState: disabled ? { disabled: true } : void 0,
1025
- style: [styles.root, style],
1070
+ style: [styles.root, { gap: sizeTokens.gap }, style],
1026
1071
  children: [
1027
1072
  label && (typeof label === "string" || typeof label === "number" ? /* @__PURE__ */ jsxs7(
1028
1073
  Text6,
1029
1074
  {
1030
- style: [styles.label, disabled && styles.labelDisabled, labelStyle],
1075
+ style: [
1076
+ styles.label,
1077
+ {
1078
+ fontSize: sizeTokens.labelFontSize,
1079
+ lineHeight: sizeTokens.labelLineHeight
1080
+ },
1081
+ disabled && styles.labelDisabled,
1082
+ labelStyle
1083
+ ],
1031
1084
  children: [
1032
1085
  label,
1033
1086
  required && /* @__PURE__ */ jsx14(
@@ -1038,6 +1091,32 @@ function FormField({
1038
1091
  importantForAccessibility: "no",
1039
1092
  children: " *"
1040
1093
  }
1094
+ ),
1095
+ !required && optionalText && /* @__PURE__ */ jsx14(
1096
+ Text6,
1097
+ {
1098
+ style: [
1099
+ styles.optional,
1100
+ {
1101
+ fontSize: sizeTokens.optionalFontSize,
1102
+ lineHeight: sizeTokens.optionalLineHeight
1103
+ }
1104
+ ],
1105
+ children: optionalText
1106
+ }
1107
+ ),
1108
+ labelInfo && /* @__PURE__ */ jsx14(
1109
+ Text6,
1110
+ {
1111
+ style: [
1112
+ styles.labelInfo,
1113
+ {
1114
+ fontSize: sizeTokens.labelInfoFontSize,
1115
+ lineHeight: sizeTokens.labelInfoSize
1116
+ }
1117
+ ],
1118
+ children: labelInfo
1119
+ }
1041
1120
  )
1042
1121
  ]
1043
1122
  }
@@ -1051,26 +1130,36 @@ function FormField({
1051
1130
  importantForAccessibility: "no",
1052
1131
  children: "*"
1053
1132
  }
1054
- )
1133
+ ),
1134
+ !required && optionalText,
1135
+ labelInfo
1055
1136
  ] })),
1056
1137
  description && (typeof description === "string" || typeof description === "number" ? /* @__PURE__ */ jsx14(
1057
1138
  Text6,
1058
1139
  {
1059
1140
  style: [
1060
1141
  styles.description,
1142
+ {
1143
+ fontSize: sizeTokens.descriptionFontSize,
1144
+ lineHeight: sizeTokens.descriptionLineHeight
1145
+ },
1061
1146
  disabled && styles.descriptionDisabled,
1062
1147
  descriptionStyle
1063
1148
  ],
1064
1149
  children: description
1065
1150
  }
1066
1151
  ) : description),
1067
- /* @__PURE__ */ jsx14(View10, { style: [styles.control, controlStyle], children }),
1152
+ /* @__PURE__ */ jsx14(FormFieldContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx14(View10, { style: [styles.control, { gap: sizeTokens.gap }, controlStyle], children }) }),
1068
1153
  error && (typeof error === "string" || typeof error === "number" ? /* @__PURE__ */ jsx14(
1069
1154
  Text6,
1070
1155
  {
1071
1156
  accessibilityLiveRegion: "polite",
1072
1157
  style: [
1073
1158
  styles.error,
1159
+ {
1160
+ fontSize: sizeTokens.helperTextFontSize,
1161
+ lineHeight: sizeTokens.helperTextLineHeight
1162
+ },
1074
1163
  disabled && styles.helperTextDisabled,
1075
1164
  errorStyle
1076
1165
  ],
@@ -1101,11 +1190,11 @@ var createStyles13 = (theme) => StyleSheet13.create({
1101
1190
  });
1102
1191
 
1103
1192
  // src/components/RadioGroup/RadioGroupContext.tsx
1104
- import { createContext as createContext3, useContext as useContext3 } from "react";
1105
- var RadioGroupContext = createContext3(null);
1193
+ import { createContext as createContext4, useContext as useContext4 } from "react";
1194
+ var RadioGroupContext = createContext4(null);
1106
1195
  var RadioGroupProvider = RadioGroupContext.Provider;
1107
1196
  function useRadioGroupContext() {
1108
- return useContext3(RadioGroupContext);
1197
+ return useContext4(RadioGroupContext);
1109
1198
  }
1110
1199
 
1111
1200
  // src/components/RadioGroup/RadioGroup.tsx
@@ -1118,6 +1207,7 @@ var RadioGroup = forwardRef(
1118
1207
  disabled = false,
1119
1208
  required = false,
1120
1209
  size = "md",
1210
+ color = "primary",
1121
1211
  orientation = "vertical",
1122
1212
  label,
1123
1213
  description,
@@ -1166,6 +1256,7 @@ var RadioGroup = forwardRef(
1166
1256
  required,
1167
1257
  invalid,
1168
1258
  size,
1259
+ color,
1169
1260
  onValueChange: setSelectedValue
1170
1261
  },
1171
1262
  children: /* @__PURE__ */ jsx15(
@@ -1640,11 +1731,11 @@ Select.displayName = "Select";
1640
1731
  import { View as View14 } from "react-native";
1641
1732
 
1642
1733
  // src/components/Tabs/TabsContext.tsx
1643
- import { createContext as createContext4, useContext as useContext4 } from "react";
1644
- var TabsContext = createContext4(null);
1734
+ import { createContext as createContext5, useContext as useContext5 } from "react";
1735
+ var TabsContext = createContext5(null);
1645
1736
  var TabsProvider = TabsContext.Provider;
1646
1737
  var useTabs = () => {
1647
- const context = useContext4(TabsContext);
1738
+ const context = useContext5(TabsContext);
1648
1739
  if (!context) {
1649
1740
  throw new Error("Tabs components must be used inside <Tabs>.");
1650
1741
  }
@@ -2803,7 +2894,6 @@ import { Pressable as Pressable12, Text as Text13, TextInput, View as View21 } f
2803
2894
 
2804
2895
  // src/primitives/Input/Input.styles.ts
2805
2896
  import { StyleSheet as StyleSheet23 } from "react-native";
2806
- var getPlaceholderTextColor = (theme) => theme.components.input.default.placeholder;
2807
2897
  var getDisabledPlaceholderTextColor = (theme) => theme.components.input.disabled.placeholder;
2808
2898
  var resolveRingColor = (ring) => typeof ring === "string" ? ring : ring.color;
2809
2899
  var createStyles23 = (theme) => StyleSheet23.create({
@@ -2865,6 +2955,24 @@ var createStyles23 = (theme) => StyleSheet23.create({
2865
2955
  alignItems: "center",
2866
2956
  justifyContent: "center"
2867
2957
  },
2958
+ revealButton: {
2959
+ position: "absolute",
2960
+ right: theme.tokens.spacing[3],
2961
+ top: "50%",
2962
+ zIndex: 1,
2963
+ minWidth: 40,
2964
+ height: 28,
2965
+ marginTop: -14,
2966
+ borderRadius: theme.tokens.radius.sm,
2967
+ alignItems: "center",
2968
+ justifyContent: "center"
2969
+ },
2970
+ revealButtonText: {
2971
+ color: theme.components.input.icon.muted,
2972
+ fontFamily: theme.tokens.typography.family.medium,
2973
+ fontSize: theme.tokens.typography.size.xs,
2974
+ lineHeight: theme.tokens.typography.lineHeight.xs
2975
+ },
2868
2976
  clearButtonText: {
2869
2977
  color: theme.components.input.clearButton.fg,
2870
2978
  fontSize: 16,
@@ -2918,20 +3026,6 @@ var createStyles23 = (theme) => StyleSheet23.create({
2918
3026
  shadowRadius: 6,
2919
3027
  elevation: 1
2920
3028
  },
2921
- success: {
2922
- borderColor: theme.components.input.success.border
2923
- },
2924
- successFocused: {
2925
- borderColor: theme.components.input.success.border,
2926
- shadowColor: resolveRingColor(theme.components.input.success.ring),
2927
- shadowOffset: {
2928
- width: 0,
2929
- height: 0
2930
- },
2931
- shadowOpacity: 0.18,
2932
- shadowRadius: 6,
2933
- elevation: 1
2934
- },
2935
3029
  readOnly: {
2936
3030
  color: theme.components.input.readOnly.fg,
2937
3031
  borderColor: theme.components.input.readOnly.border,
@@ -2974,24 +3068,48 @@ var autoCorrectByInputType = {
2974
3068
  url: false,
2975
3069
  search: false
2976
3070
  };
3071
+ var applyPatternMask = (value, pattern) => {
3072
+ const digits = value.replace(/\D/g, "");
3073
+ let digitIndex = 0;
3074
+ let maskedValue = "";
3075
+ for (const character of pattern) {
3076
+ if (character === "#") {
3077
+ const nextDigit = digits[digitIndex];
3078
+ if (!nextDigit) break;
3079
+ maskedValue += nextDigit;
3080
+ digitIndex += 1;
3081
+ continue;
3082
+ }
3083
+ if (digitIndex < digits.length) {
3084
+ maskedValue += character;
3085
+ }
3086
+ }
3087
+ return maskedValue;
3088
+ };
3089
+ var applyMask = (value, mask) => {
3090
+ if (!mask) return value;
3091
+ return typeof mask === "function" ? mask(value) : applyPatternMask(value, mask);
3092
+ };
2977
3093
  var Input = forwardRef3(
2978
3094
  ({
2979
3095
  label,
2980
3096
  description,
2981
3097
  value,
2982
3098
  defaultValue,
2983
- onChange,
3099
+ onValueChange,
2984
3100
  placeholder,
2985
- size = "md",
3101
+ size,
2986
3102
  error,
3103
+ invalid = false,
2987
3104
  disabled = false,
2988
3105
  required = false,
3106
+ loading = false,
2989
3107
  readOnly = false,
2990
3108
  type = "text",
2991
- leftIcon,
2992
- rightIcon,
2993
- leftIconTone = "default",
2994
- rightIconTone = "default",
3109
+ startIcon,
3110
+ endIcon,
3111
+ startIconTone = "default",
3112
+ endIconTone = "default",
2995
3113
  clearIcon,
2996
3114
  clearIconTone = "danger",
2997
3115
  iconSize,
@@ -3010,19 +3128,38 @@ var Input = forwardRef3(
3010
3128
  maxLength,
3011
3129
  clearable,
3012
3130
  onClear,
3131
+ color = "primary",
3132
+ variant = "outline",
3133
+ revealPassword = false,
3134
+ showCounter: _showCounter,
3135
+ mask,
3136
+ format,
3137
+ parse,
3013
3138
  ...props
3014
3139
  }, ref) => {
3015
3140
  const { theme } = useTheme();
3016
3141
  const styles = useThemeStyles(createStyles23);
3142
+ const field = useFormFieldContext();
3143
+ const hasOwnField = Boolean(label || description || error);
3017
3144
  const [isFocused, setIsFocused] = useState6(false);
3018
3145
  const [uncontrolledValue, setUncontrolledValue] = useState6(
3019
3146
  defaultValue ?? ""
3020
3147
  );
3021
3148
  const isControlled = value !== void 0;
3022
3149
  const currentValue = isControlled ? value : uncontrolledValue;
3150
+ const displayValue = format ? format(currentValue) : currentValue;
3023
3151
  const hasValue = currentValue !== "";
3024
- const placeholderTextColor = disabled ? getDisabledPlaceholderTextColor(theme) : readOnly ? theme.components.input.readOnly.placeholder : getPlaceholderTextColor(theme);
3152
+ const resolvedSize = size ?? field?.size ?? "md";
3153
+ const inputColorPalette = theme.components.input[color];
3154
+ const inputPalette = inputColorPalette[variant];
3155
+ const inputState = isFocused ? inputPalette.focus : inputPalette.default;
3156
+ const isInvalid = invalid || Boolean(error) || !hasOwnField && Boolean(field?.invalid);
3157
+ const isDisabled = disabled || !hasOwnField && Boolean(field?.disabled);
3158
+ const isRequired = required || !hasOwnField && Boolean(field?.required);
3159
+ const isReadOnly = readOnly || loading;
3160
+ const placeholderTextColor = isDisabled ? getDisabledPlaceholderTextColor(theme) : readOnly ? theme.components.input.readOnly.placeholder : inputState.placeholder;
3025
3161
  const isPassword = type === "password";
3162
+ const [isPasswordRevealed, setIsPasswordRevealed] = useState6(false);
3026
3163
  const resolvedIconSize = iconSize ?? 16;
3027
3164
  const handleFocus = (event) => {
3028
3165
  setIsFocused(true);
@@ -3033,114 +3170,148 @@ var Input = forwardRef3(
3033
3170
  onBlur?.(event);
3034
3171
  };
3035
3172
  const handleChangeText = (nextValue) => {
3173
+ const parsedValue = parse ? parse(nextValue) : nextValue;
3174
+ const maskedValue = applyMask(parsedValue, mask);
3036
3175
  if (!isControlled) {
3037
- setUncontrolledValue(nextValue);
3176
+ setUncontrolledValue(maskedValue);
3038
3177
  }
3039
- onChange?.(nextValue);
3178
+ onValueChange?.(maskedValue);
3040
3179
  };
3041
3180
  const handleClear = () => {
3042
3181
  if (!isControlled) {
3043
3182
  setUncontrolledValue("");
3044
3183
  }
3184
+ onValueChange?.("");
3045
3185
  onClear?.();
3046
3186
  };
3047
- const showClearButton = clearable && hasValue && !disabled && !readOnly;
3048
- const showRightIcon = !showClearButton && Boolean(rightIcon);
3049
- const leftIconColor = disabled ? theme.components.input.disabled.icon : theme.components.input.icon[leftIconTone];
3050
- const rightIconColor = disabled ? theme.components.input.disabled.icon : theme.components.input.icon[rightIconTone];
3051
- const clearIconColor = disabled ? theme.components.input.disabled.icon : theme.components.input.icon[clearIconTone];
3187
+ const showClearButton = clearable && hasValue && !isDisabled && !isReadOnly;
3188
+ const showRevealButton = revealPassword && isPassword && !isDisabled;
3189
+ const showRightIcon = !showClearButton && !showRevealButton && Boolean(endIcon);
3190
+ const startIconColor = isDisabled ? theme.components.input.disabled.icon : theme.components.input.icon[startIconTone];
3191
+ const endIconColor = isDisabled ? theme.components.input.disabled.icon : theme.components.input.icon[endIconTone];
3192
+ const clearIconColor = isDisabled ? theme.components.input.disabled.icon : theme.components.input.icon[clearIconTone];
3193
+ const control = /* @__PURE__ */ jsxs14(View21, { style: styles.inputWrapper, children: [
3194
+ startIcon && /* @__PURE__ */ jsx25(
3195
+ View21,
3196
+ {
3197
+ pointerEvents: "none",
3198
+ style: styles.leftIcon,
3199
+ accessibilityElementsHidden: true,
3200
+ importantForAccessibility: "no",
3201
+ children: cloneElement6(startIcon, {
3202
+ color: startIconColor,
3203
+ size: resolvedIconSize
3204
+ })
3205
+ }
3206
+ ),
3207
+ /* @__PURE__ */ jsx25(
3208
+ TextInput,
3209
+ {
3210
+ ...props,
3211
+ ref,
3212
+ nativeID: props.nativeID ?? (!hasOwnField ? field?.controlId : void 0),
3213
+ value: displayValue,
3214
+ onChangeText: handleChangeText,
3215
+ editable: !isDisabled && !isReadOnly,
3216
+ placeholder,
3217
+ keyboardType: keyboardType ?? keyboardTypeByInputType[type],
3218
+ secureTextEntry: secureTextEntry ?? (isPassword && !isPasswordRevealed),
3219
+ autoCapitalize: autoCapitalize ?? autoCapitalizeByInputType[type],
3220
+ autoCorrect: autoCorrect ?? autoCorrectByInputType[type],
3221
+ onBlur: handleBlur,
3222
+ onFocus: handleFocus,
3223
+ autoFocus,
3224
+ maxLength,
3225
+ testID,
3226
+ placeholderTextColor,
3227
+ accessibilityLabel: accessibilityLabel ?? label,
3228
+ accessibilityHint,
3229
+ accessibilityState: { disabled: isDisabled },
3230
+ accessibilityLabelledBy: !hasOwnField ? field?.labelId : void 0,
3231
+ "aria-describedby": !hasOwnField ? field?.ariaDescribedBy : void 0,
3232
+ style: [
3233
+ styles.input,
3234
+ {
3235
+ color: inputState.fg,
3236
+ backgroundColor: inputState.bg,
3237
+ borderColor: inputState.border
3238
+ },
3239
+ styles[resolvedSize],
3240
+ inputStyle,
3241
+ startIcon && styles.inputWithLeftAdornment,
3242
+ (showRightIcon || showClearButton || showRevealButton) && styles.inputWithRightAdornment,
3243
+ isFocused && !isDisabled && !isReadOnly && {
3244
+ color: inputPalette.focus.fg,
3245
+ backgroundColor: inputPalette.focus.bg,
3246
+ borderColor: inputPalette.focus.border,
3247
+ shadowColor: inputColorPalette.ring,
3248
+ shadowOffset: {
3249
+ width: 0,
3250
+ height: 0
3251
+ },
3252
+ shadowOpacity: 0.18,
3253
+ shadowRadius: 6,
3254
+ elevation: 1
3255
+ },
3256
+ isInvalid && styles.error,
3257
+ isFocused && isInvalid && !isDisabled && !isReadOnly && styles.errorFocused,
3258
+ isReadOnly && !isDisabled && styles.readOnly,
3259
+ isDisabled && styles.disabled
3260
+ ]
3261
+ }
3262
+ ),
3263
+ showClearButton ? /* @__PURE__ */ jsx25(
3264
+ Pressable12,
3265
+ {
3266
+ accessibilityRole: "button",
3267
+ accessibilityLabel: "Clear input",
3268
+ hitSlop: 8,
3269
+ onPress: handleClear,
3270
+ style: styles.clearButton,
3271
+ children: clearIcon ? cloneElement6(clearIcon, {
3272
+ color: clearIconColor,
3273
+ size: resolvedIconSize
3274
+ }) : /* @__PURE__ */ jsx25(Text13, { style: [styles.clearButtonText, { color: clearIconColor }], children: "\xD7" })
3275
+ }
3276
+ ) : showRevealButton ? /* @__PURE__ */ jsx25(
3277
+ Pressable12,
3278
+ {
3279
+ accessibilityRole: "button",
3280
+ accessibilityLabel: isPasswordRevealed ? "Hide password" : "Show password",
3281
+ hitSlop: 8,
3282
+ onPress: () => setIsPasswordRevealed((revealed) => !revealed),
3283
+ style: styles.revealButton,
3284
+ children: /* @__PURE__ */ jsx25(Text13, { style: styles.revealButtonText, children: isPasswordRevealed ? "Hide" : "Show" })
3285
+ }
3286
+ ) : showRightIcon && endIcon && /* @__PURE__ */ jsx25(
3287
+ View21,
3288
+ {
3289
+ pointerEvents: "none",
3290
+ style: styles.rightIcon,
3291
+ accessibilityElementsHidden: true,
3292
+ importantForAccessibility: "no",
3293
+ children: cloneElement6(endIcon, {
3294
+ color: endIconColor,
3295
+ size: resolvedIconSize
3296
+ })
3297
+ }
3298
+ )
3299
+ ] });
3300
+ if (!hasOwnField && field) {
3301
+ return control;
3302
+ }
3052
3303
  return /* @__PURE__ */ jsx25(
3053
3304
  FormField,
3054
3305
  {
3055
3306
  label,
3056
3307
  description,
3057
3308
  error,
3058
- required,
3059
- disabled,
3309
+ required: isRequired,
3310
+ disabled: isDisabled,
3311
+ invalid: isInvalid,
3312
+ size: resolvedSize,
3060
3313
  style: containerStyle,
3061
- children: /* @__PURE__ */ jsxs14(View21, { style: styles.inputWrapper, children: [
3062
- leftIcon && /* @__PURE__ */ jsx25(
3063
- View21,
3064
- {
3065
- pointerEvents: "none",
3066
- style: styles.leftIcon,
3067
- accessibilityElementsHidden: true,
3068
- importantForAccessibility: "no",
3069
- children: cloneElement6(leftIcon, {
3070
- color: leftIconColor,
3071
- size: resolvedIconSize
3072
- })
3073
- }
3074
- ),
3075
- /* @__PURE__ */ jsx25(
3076
- TextInput,
3077
- {
3078
- ...props,
3079
- ref,
3080
- value: currentValue,
3081
- onChangeText: handleChangeText,
3082
- editable: !disabled && !readOnly,
3083
- placeholder,
3084
- keyboardType: keyboardType ?? keyboardTypeByInputType[type],
3085
- secureTextEntry: secureTextEntry ?? isPassword,
3086
- autoCapitalize: autoCapitalize ?? autoCapitalizeByInputType[type],
3087
- autoCorrect: autoCorrect ?? autoCorrectByInputType[type],
3088
- onBlur: handleBlur,
3089
- onFocus: handleFocus,
3090
- autoFocus,
3091
- maxLength,
3092
- testID,
3093
- placeholderTextColor,
3094
- accessibilityLabel: accessibilityLabel ?? label,
3095
- accessibilityHint,
3096
- accessibilityState: { disabled },
3097
- style: [
3098
- styles.input,
3099
- styles[size],
3100
- inputStyle,
3101
- leftIcon && styles.inputWithLeftAdornment,
3102
- (showRightIcon || showClearButton) && styles.inputWithRightAdornment,
3103
- isFocused && !disabled && !readOnly && styles.focused,
3104
- error && styles.error,
3105
- isFocused && error && !disabled && !readOnly && styles.errorFocused,
3106
- readOnly && !disabled && styles.readOnly,
3107
- disabled && styles.disabled
3108
- ]
3109
- }
3110
- ),
3111
- showClearButton ? /* @__PURE__ */ jsx25(
3112
- Pressable12,
3113
- {
3114
- accessibilityRole: "button",
3115
- accessibilityLabel: "Clear input",
3116
- hitSlop: 8,
3117
- onPress: handleClear,
3118
- style: styles.clearButton,
3119
- children: clearIcon ? cloneElement6(clearIcon, {
3120
- color: clearIconColor,
3121
- size: resolvedIconSize
3122
- }) : /* @__PURE__ */ jsx25(
3123
- Text13,
3124
- {
3125
- style: [styles.clearButtonText, { color: clearIconColor }],
3126
- children: "\xD7"
3127
- }
3128
- )
3129
- }
3130
- ) : showRightIcon && rightIcon && /* @__PURE__ */ jsx25(
3131
- View21,
3132
- {
3133
- pointerEvents: "none",
3134
- style: styles.rightIcon,
3135
- accessibilityElementsHidden: true,
3136
- importantForAccessibility: "no",
3137
- children: cloneElement6(rightIcon, {
3138
- color: rightIconColor,
3139
- size: resolvedIconSize
3140
- })
3141
- }
3142
- )
3143
- ] })
3314
+ children: control
3144
3315
  }
3145
3316
  );
3146
3317
  }
@@ -3184,12 +3355,12 @@ var createStyles24 = (theme) => StyleSheet24.create({
3184
3355
  backgroundColor: theme.components.radio.default.bg
3185
3356
  },
3186
3357
  controlChecked: {
3187
- borderColor: theme.components.radio.checked.default.border,
3188
- backgroundColor: theme.components.radio.checked.default.bg
3358
+ borderColor: theme.components.radio.primary.default.border,
3359
+ backgroundColor: theme.components.radio.primary.default.bg
3189
3360
  },
3190
3361
  controlCheckedPressed: {
3191
- borderColor: theme.components.radio.checked.pressed.border,
3192
- backgroundColor: theme.components.radio.checked.pressed.bg
3362
+ borderColor: theme.components.radio.primary.pressed.border,
3363
+ backgroundColor: theme.components.radio.primary.pressed.bg
3193
3364
  },
3194
3365
  controlInvalid: {
3195
3366
  borderColor: theme.components.radio.invalid.border
@@ -3199,18 +3370,18 @@ var createStyles24 = (theme) => StyleSheet24.create({
3199
3370
  backgroundColor: theme.components.radio.disabled.bg
3200
3371
  },
3201
3372
  controlCheckedDisabled: {
3202
- borderColor: theme.components.radio.checked.disabled.border,
3203
- backgroundColor: theme.components.radio.checked.disabled.bg
3373
+ borderColor: theme.components.radio.selectedDisabled.border,
3374
+ backgroundColor: theme.components.radio.selectedDisabled.bg
3204
3375
  },
3205
3376
  indicator: {
3206
3377
  borderRadius: theme.tokens.radius.full,
3207
- backgroundColor: theme.components.radio.checked.default.fg
3378
+ backgroundColor: theme.components.radio.primary.default.fg
3208
3379
  },
3209
3380
  indicatorPressed: {
3210
- backgroundColor: theme.components.radio.checked.pressed.fg
3381
+ backgroundColor: theme.components.radio.primary.pressed.fg
3211
3382
  },
3212
3383
  indicatorDisabled: {
3213
- backgroundColor: theme.components.radio.checked.disabled.fg
3384
+ backgroundColor: theme.components.radio.selectedDisabled.fg
3214
3385
  },
3215
3386
  content: {
3216
3387
  flexShrink: 1,
@@ -3221,10 +3392,10 @@ var createStyles24 = (theme) => StyleSheet24.create({
3221
3392
  fontFamily: theme.tokens.typography.family.regular
3222
3393
  },
3223
3394
  labelChecked: {
3224
- color: theme.components.radio.checked.default.labelFg
3395
+ color: theme.components.radio.primary.default.labelFg
3225
3396
  },
3226
3397
  labelCheckedPressed: {
3227
- color: theme.components.radio.checked.pressed.labelFg
3398
+ color: theme.components.radio.primary.pressed.labelFg
3228
3399
  },
3229
3400
  labelInvalid: {
3230
3401
  color: theme.components.radio.invalid.fg
@@ -3243,7 +3414,7 @@ var createStyles24 = (theme) => StyleSheet24.create({
3243
3414
  });
3244
3415
 
3245
3416
  // src/primitives/Radio/Radio.tsx
3246
- import { jsx as jsx26, jsxs as jsxs15 } from "react/jsx-runtime";
3417
+ import { Fragment as Fragment5, jsx as jsx26, jsxs as jsxs15 } from "react/jsx-runtime";
3247
3418
  var controlSizeBySize = {
3248
3419
  sm: 14,
3249
3420
  md: 16,
@@ -3262,9 +3433,11 @@ var Radio = forwardRef4(
3262
3433
  disabled: disabledProp = false,
3263
3434
  required: requiredProp = false,
3264
3435
  size: sizeProp,
3436
+ color: colorProp,
3265
3437
  onCheckedChange,
3266
3438
  label,
3267
3439
  description,
3440
+ icon,
3268
3441
  error,
3269
3442
  accessibilityLabel,
3270
3443
  accessibilityHint,
@@ -3289,6 +3462,8 @@ var Radio = forwardRef4(
3289
3462
  const resolvedRequired = Boolean(group?.required) || requiredProp;
3290
3463
  const resolvedInvalid = Boolean(group?.invalid) || Boolean(error);
3291
3464
  const resolvedSize = sizeProp ?? group?.size ?? "md";
3465
+ const resolvedColor = colorProp ?? group?.color ?? "primary";
3466
+ const radioColor = theme.components.radio[resolvedColor];
3292
3467
  const controlSize = controlSizeBySize[resolvedSize];
3293
3468
  const indicatorSize = indicatorSizeBySize[resolvedSize];
3294
3469
  const typographySizeByRadioSize = {
@@ -3343,7 +3518,7 @@ var Radio = forwardRef4(
3343
3518
  typeof style === "function" ? style(state) : style
3344
3519
  ];
3345
3520
  return /* @__PURE__ */ jsxs15(View22, { style: [styles.root, containerStyle], children: [
3346
- /* @__PURE__ */ jsxs15(
3521
+ /* @__PURE__ */ jsx26(
3347
3522
  Pressable13,
3348
3523
  {
3349
3524
  ...rest,
@@ -3358,7 +3533,7 @@ var Radio = forwardRef4(
3358
3533
  disabled: resolvedDisabled,
3359
3534
  onPress: handlePress,
3360
3535
  style: resolvePressableStyle,
3361
- children: [
3536
+ children: (state) => /* @__PURE__ */ jsxs15(Fragment5, { children: [
3362
3537
  /* @__PURE__ */ jsx26(
3363
3538
  View22,
3364
3539
  {
@@ -3370,23 +3545,33 @@ var Radio = forwardRef4(
3370
3545
  height: controlSize,
3371
3546
  marginTop: controlMarginTop
3372
3547
  },
3373
- resolvedChecked && styles.controlChecked,
3548
+ resolvedChecked && {
3549
+ backgroundColor: radioColor.default.bg,
3550
+ borderColor: radioColor.default.border
3551
+ },
3552
+ resolvedChecked && state.pressed && !resolvedDisabled && {
3553
+ backgroundColor: radioColor.pressed.bg,
3554
+ borderColor: radioColor.pressed.border,
3555
+ transform: [{ scale: 0.96 }]
3556
+ },
3374
3557
  resolvedInvalid && styles.controlInvalid,
3375
- resolvedDisabled && styles.controlDisabled
3558
+ resolvedDisabled && styles.controlDisabled,
3559
+ resolvedChecked && resolvedDisabled && styles.controlCheckedDisabled
3376
3560
  ],
3377
- children: resolvedChecked && /* @__PURE__ */ jsx26(
3561
+ children: resolvedChecked && (icon ?? /* @__PURE__ */ jsx26(
3378
3562
  View22,
3379
3563
  {
3380
3564
  style: [
3381
3565
  styles.indicator,
3382
3566
  {
3383
3567
  width: indicatorSize,
3384
- height: indicatorSize
3568
+ height: indicatorSize,
3569
+ backgroundColor: state.pressed && !resolvedDisabled ? radioColor.pressed.fg : radioColor.default.fg
3385
3570
  },
3386
3571
  resolvedDisabled && styles.indicatorDisabled
3387
3572
  ]
3388
3573
  }
3389
- )
3574
+ ))
3390
3575
  }
3391
3576
  ),
3392
3577
  (label || description) && /* @__PURE__ */ jsxs15(View22, { pointerEvents: "none", style: styles.content, children: [
@@ -3399,7 +3584,12 @@ var Radio = forwardRef4(
3399
3584
  fontSize: typographySize.labelSize,
3400
3585
  lineHeight: typographySize.labelLineHeight
3401
3586
  },
3402
- resolvedChecked && styles.labelChecked,
3587
+ resolvedChecked && {
3588
+ color: radioColor.default.labelFg
3589
+ },
3590
+ resolvedChecked && state.pressed && !resolvedDisabled && {
3591
+ color: radioColor.pressed.labelFg
3592
+ },
3403
3593
  resolvedInvalid && styles.labelInvalid,
3404
3594
  resolvedDisabled && styles.textDisabled,
3405
3595
  labelStyle
@@ -3423,7 +3613,7 @@ var Radio = forwardRef4(
3423
3613
  }
3424
3614
  ) : description)
3425
3615
  ] })
3426
- ]
3616
+ ] })
3427
3617
  }
3428
3618
  ),
3429
3619
  error && /* @__PURE__ */ jsx26(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellira-ui/react-native",
3
- "version": "2.28.0",
3
+ "version": "2.30.0",
4
4
  "description": "React Native components for Vellira Design System",
5
5
  "author": "Roman Bakurov",
6
6
  "license": "MIT",