@boostdev/design-system-react-native 0.2.1 → 0.2.3
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/CHANGELOG.md +32 -0
- package/dist/index.cjs +225 -151
- package/dist/index.js +225 -151
- package/package.json +1 -1
- package/src/components/interaction/form/Combobox/Combobox.spec.tsx +28 -0
- package/src/components/interaction/form/Combobox/Combobox.tsx +31 -13
- package/src/components/interaction/form/FormInput/FormInput.spec.tsx +25 -8
- package/src/components/interaction/form/FormInput/FormInput.tsx +52 -31
- package/src/components/interaction/form/NumberInput/NumberInput.spec.tsx +24 -0
- package/src/components/interaction/form/NumberInput/NumberInput.tsx +45 -27
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +12 -2
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.spec.tsx +49 -7
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +46 -31
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
All notable changes to `@boostdev/design-system-react-native` are documented here.
|
|
4
|
+
## [0.2.3] - 2026-06-11
|
|
5
|
+
### Fixed
|
|
6
|
+
- Fix(segmented-control): proper track + thumb rendering
|
|
7
|
+
|
|
8
|
+
Replace per-segment outline/border approach with a single rounded track
|
|
9
|
+
(colorBgSubtle background, 2 px inner padding) containing equal-width
|
|
10
|
+
segments. Selected segment renders a contrasting thumb: colorBg + shadow
|
|
11
|
+
for the outline variant (white elevated surface), colorInteractive for
|
|
12
|
+
the filled variant. No per-segment borders; no stray focus rectangle.
|
|
13
|
+
|
|
14
|
+
Mirrors the web DS visual contract (track bg, thumb bg, thumb radius =
|
|
15
|
+
track radius minus padding). Dark mode via useTheme throughout.
|
|
16
|
+
All accessibilityRole/accessibilityState props preserved.
|
|
17
|
+
Spec updated to pin track-has-one-background and thumb structure. ([`2a83f88`](https://gitlab.com/b00stdev/boostdev-design-system-react-native/-/commit/2a83f8804553e390528fc85dd79c1541bf92343e))
|
|
18
|
+
|
|
19
|
+
## [0.2.2] - 2026-06-11
|
|
20
|
+
### Fixed
|
|
21
|
+
- Fix(form): vertically center text in single-line inputs
|
|
22
|
+
|
|
23
|
+
Replace minHeight + paddingVertical on TextInput with a wrapper View
|
|
24
|
+
(height: 44, justifyContent: center) + paddingVertical: 0 on the inner
|
|
25
|
+
TextInput. iOS auto-centers text within the wrapper height; paddingVertical
|
|
26
|
+
on the TextInput itself caused text to sit off-center after the 0.2.1
|
|
27
|
+
lineHeight removal. Added textAlignVertical: center and includeFontPadding:
|
|
28
|
+
false for Android parity.
|
|
29
|
+
|
|
30
|
+
Applied consistently to FormInput, NumberInput, and Combobox.
|
|
31
|
+
Textarea (multiline) is unaffected.
|
|
32
|
+
|
|
33
|
+
Spec invariants updated: paddingVertical === 0 on TextInput, wrapper
|
|
34
|
+
height >= 44 replace the previous minHeight >= 44 assertion. ([`437b16d`](https://gitlab.com/b00stdev/boostdev-design-system-react-native/-/commit/437b16dccb60b3b3948c84519990c18688e51f71))
|
|
35
|
+
|
|
4
36
|
## [0.2.1] - 2026-06-11
|
|
5
37
|
### Fixed
|
|
6
38
|
- Fix(form): prevent iOS text clipping in single-line inputs
|
package/dist/index.cjs
CHANGED
|
@@ -2682,24 +2682,36 @@ function FileInput({
|
|
|
2682
2682
|
var import_react9 = require("react");
|
|
2683
2683
|
var import_react_native37 = require("react-native");
|
|
2684
2684
|
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2685
|
+
var INPUT_HEIGHT = 44;
|
|
2685
2686
|
var MAX_DROPDOWN_HEIGHT = 200;
|
|
2686
2687
|
var styles33 = import_react_native37.StyleSheet.create({
|
|
2687
2688
|
container: {
|
|
2688
2689
|
gap: import_native.spacing.xxs
|
|
2689
2690
|
},
|
|
2691
|
+
/**
|
|
2692
|
+
* Draws the pill border and provides the fixed 44pt touch-target height.
|
|
2693
|
+
* justifyContent: 'center' vertically centers the inner TextInput.
|
|
2694
|
+
* position: 'relative' is required for the dropdown's absolute positioning.
|
|
2695
|
+
*/
|
|
2690
2696
|
inputWrapper: {
|
|
2691
|
-
position: "relative"
|
|
2697
|
+
position: "relative",
|
|
2698
|
+
height: INPUT_HEIGHT,
|
|
2699
|
+
justifyContent: "center",
|
|
2700
|
+
borderWidth: 1,
|
|
2701
|
+
borderRadius: import_native.border.radius.full
|
|
2692
2702
|
},
|
|
2703
|
+
/**
|
|
2704
|
+
* No vertical padding: iOS centers text automatically inside the wrapper height.
|
|
2705
|
+
* textAlignVertical + includeFontPadding provide Android parity.
|
|
2706
|
+
* No lineHeight: explicit lineHeight clips ascenders/descenders on iOS.
|
|
2707
|
+
*/
|
|
2693
2708
|
input: {
|
|
2694
2709
|
fontFamily: import_native.font.family.body,
|
|
2695
2710
|
fontSize: import_native.font.size.body,
|
|
2696
|
-
// No lineHeight on single-line TextInput: iOS clips descenders/ascenders when
|
|
2697
|
-
// lineHeight is set explicitly. minHeight: 44 satisfies the touch-target requirement.
|
|
2698
|
-
paddingVertical: import_native.spacing.xs,
|
|
2699
2711
|
paddingHorizontal: import_native.spacing.s,
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2712
|
+
paddingVertical: 0,
|
|
2713
|
+
textAlignVertical: "center",
|
|
2714
|
+
includeFontPadding: false
|
|
2703
2715
|
},
|
|
2704
2716
|
dropdown: {
|
|
2705
2717
|
position: "absolute",
|
|
@@ -2768,80 +2780,88 @@ function Combobox({
|
|
|
2768
2780
|
const borderColor = error ? colors2.colorError : colors2.colorOnBg;
|
|
2769
2781
|
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_react_native37.View, { style: [styles33.container, style], children: [
|
|
2770
2782
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Label, { label }),
|
|
2771
|
-
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
style: [
|
|
2786
|
-
styles33.input,
|
|
2787
|
-
{
|
|
2788
|
-
borderColor,
|
|
2789
|
-
color: colors2.colorOnBg,
|
|
2790
|
-
backgroundColor: colors2.colorBg
|
|
2791
|
-
},
|
|
2792
|
-
disabled && { opacity: 0.4 }
|
|
2793
|
-
]
|
|
2794
|
-
}
|
|
2795
|
-
),
|
|
2796
|
-
isOpen && filtered.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2797
|
-
import_react_native37.ScrollView,
|
|
2798
|
-
{
|
|
2799
|
-
style: [
|
|
2800
|
-
styles33.dropdown,
|
|
2783
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2784
|
+
import_react_native37.View,
|
|
2785
|
+
{
|
|
2786
|
+
style: [
|
|
2787
|
+
styles33.inputWrapper,
|
|
2788
|
+
{
|
|
2789
|
+
borderColor,
|
|
2790
|
+
backgroundColor: colors2.colorBg
|
|
2791
|
+
},
|
|
2792
|
+
disabled && { opacity: 0.4 }
|
|
2793
|
+
],
|
|
2794
|
+
children: [
|
|
2795
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2796
|
+
import_react_native37.TextInput,
|
|
2801
2797
|
{
|
|
2802
|
-
|
|
2803
|
-
|
|
2798
|
+
value: inputValue,
|
|
2799
|
+
onChangeText: handleChangeText,
|
|
2800
|
+
onFocus: handleFocus,
|
|
2801
|
+
onBlur: handleBlur,
|
|
2802
|
+
placeholder,
|
|
2803
|
+
placeholderTextColor: colors2.colorOnBgSubtle,
|
|
2804
|
+
editable: !disabled,
|
|
2805
|
+
accessibilityRole: "combobox",
|
|
2806
|
+
accessibilityLabel: typeof label === "string" ? label : name,
|
|
2807
|
+
accessibilityState: { disabled, expanded: isOpen },
|
|
2808
|
+
style: [
|
|
2809
|
+
styles33.input,
|
|
2810
|
+
{ color: colors2.colorOnBg }
|
|
2811
|
+
]
|
|
2804
2812
|
}
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
isDisabled && { opacity: 0.4 }
|
|
2824
|
-
],
|
|
2825
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2826
|
-
import_react_native37.Text,
|
|
2813
|
+
),
|
|
2814
|
+
isOpen && filtered.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2815
|
+
import_react_native37.ScrollView,
|
|
2816
|
+
{
|
|
2817
|
+
style: [
|
|
2818
|
+
styles33.dropdown,
|
|
2819
|
+
{
|
|
2820
|
+
borderColor: colors2.colorBgSubtle,
|
|
2821
|
+
backgroundColor: colors2.colorBg
|
|
2822
|
+
}
|
|
2823
|
+
],
|
|
2824
|
+
keyboardShouldPersistTaps: "handled",
|
|
2825
|
+
nestedScrollEnabled: true,
|
|
2826
|
+
children: filtered.map((option) => {
|
|
2827
|
+
const isSelected = option.value === value;
|
|
2828
|
+
const isDisabled = option.disabled;
|
|
2829
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2830
|
+
import_react_native37.Pressable,
|
|
2827
2831
|
{
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2832
|
+
onPress: () => !isDisabled && selectOption(option),
|
|
2833
|
+
disabled: isDisabled,
|
|
2834
|
+
accessibilityRole: "menuitem",
|
|
2835
|
+
accessibilityLabel: option.label,
|
|
2836
|
+
accessibilityState: { selected: isSelected, disabled: isDisabled },
|
|
2837
|
+
style: ({ pressed }) => [
|
|
2838
|
+
styles33.option,
|
|
2839
|
+
isSelected && { backgroundColor: colors2.colorBlueSubtle },
|
|
2840
|
+
pressed && !isDisabled && { backgroundColor: colors2.colorBgSubtle },
|
|
2841
|
+
isDisabled && { opacity: 0.4 }
|
|
2834
2842
|
],
|
|
2835
|
-
children:
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2843
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2844
|
+
import_react_native37.Text,
|
|
2845
|
+
{
|
|
2846
|
+
style: [
|
|
2847
|
+
styles33.optionText,
|
|
2848
|
+
{
|
|
2849
|
+
color: isSelected ? colors2.colorInteractive : colors2.colorOnBg
|
|
2850
|
+
},
|
|
2851
|
+
isSelected && { fontWeight: import_native.font.weight.semibold }
|
|
2852
|
+
],
|
|
2853
|
+
children: option.label
|
|
2854
|
+
}
|
|
2855
|
+
)
|
|
2856
|
+
},
|
|
2857
|
+
option.value
|
|
2858
|
+
);
|
|
2859
|
+
})
|
|
2860
|
+
}
|
|
2861
|
+
)
|
|
2862
|
+
]
|
|
2863
|
+
}
|
|
2864
|
+
),
|
|
2845
2865
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Message, { type: "error", message: error }),
|
|
2846
2866
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Message, { type: "hint", message: hint })
|
|
2847
2867
|
] });
|
|
@@ -2851,22 +2871,32 @@ function Combobox({
|
|
|
2851
2871
|
var import_react10 = require("react");
|
|
2852
2872
|
var import_react_native38 = require("react-native");
|
|
2853
2873
|
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2874
|
+
var TRACK_PADDING = import_native.spacing.xxxs;
|
|
2854
2875
|
var sizeTokens2 = {
|
|
2855
|
-
small: {
|
|
2856
|
-
medium: {
|
|
2857
|
-
large: {
|
|
2876
|
+
small: { minHeight: 32, fontSize: import_native.font.size.bodyS, px: import_native.spacing.s, py: import_native.spacing.xxs },
|
|
2877
|
+
medium: { minHeight: 44, fontSize: import_native.font.size.body, px: import_native.spacing.m, py: import_native.spacing.xs },
|
|
2878
|
+
large: { minHeight: 48, fontSize: import_native.font.size.body, px: import_native.spacing.m, py: import_native.spacing.s }
|
|
2879
|
+
};
|
|
2880
|
+
var thumbShadow = {
|
|
2881
|
+
shadowColor: "#000",
|
|
2882
|
+
shadowOffset: { width: 0, height: 1 },
|
|
2883
|
+
shadowOpacity: 0.12,
|
|
2884
|
+
shadowRadius: 2,
|
|
2885
|
+
elevation: 2
|
|
2858
2886
|
};
|
|
2859
2887
|
var styles34 = import_react_native38.StyleSheet.create({
|
|
2860
2888
|
track: {
|
|
2861
2889
|
flexDirection: "row",
|
|
2862
2890
|
borderRadius: import_native.border.radius.full,
|
|
2891
|
+
padding: TRACK_PADDING,
|
|
2863
2892
|
overflow: "hidden"
|
|
2864
2893
|
},
|
|
2865
|
-
|
|
2894
|
+
segment: {
|
|
2866
2895
|
flex: 1,
|
|
2867
2896
|
alignItems: "center",
|
|
2868
2897
|
justifyContent: "center",
|
|
2869
|
-
|
|
2898
|
+
// Thumb radius = track radius minus the padding gap (mirrors thumb-radius calc in web CSS)
|
|
2899
|
+
borderRadius: import_native.border.radius.full - TRACK_PADDING
|
|
2870
2900
|
},
|
|
2871
2901
|
label: {
|
|
2872
2902
|
fontFamily: import_native.font.family.body,
|
|
@@ -2885,21 +2915,26 @@ function SegmentedControl({
|
|
|
2885
2915
|
style
|
|
2886
2916
|
}) {
|
|
2887
2917
|
const { colors: colors2 } = useTheme();
|
|
2888
|
-
const {
|
|
2918
|
+
const { minHeight, fontSize, px, py } = sizeTokens2[size];
|
|
2889
2919
|
const items = import_react10.Children.toArray(children);
|
|
2890
|
-
const
|
|
2920
|
+
const trackBg = colors2.colorBgSubtle;
|
|
2921
|
+
const thumbBg = variant === "outline" ? colors2.colorBg : colors2.colorInteractive;
|
|
2891
2922
|
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2892
2923
|
import_react_native38.View,
|
|
2893
2924
|
{
|
|
2894
|
-
style: [styles34.track,
|
|
2925
|
+
style: [styles34.track, { backgroundColor: trackBg }, style],
|
|
2895
2926
|
accessibilityRole: "tablist",
|
|
2896
2927
|
accessibilityLabel,
|
|
2897
2928
|
children: items.map((child, index) => {
|
|
2898
2929
|
const isActive = index === selectedIndex;
|
|
2899
|
-
const
|
|
2900
|
-
const inactiveStyle = variant === "filled" ? {} : {};
|
|
2901
|
-
const textColor = variant === "filled" && isActive ? colors2.colorOnInteractive : isActive ? colors2.colorInteractive : colors2.colorOnBgSubtle;
|
|
2930
|
+
const labelColor = isActive ? variant === "outline" ? colors2.colorInteractiveOnBg : colors2.colorOnInteractive : colors2.colorOnBgSubtle;
|
|
2902
2931
|
const label = typeof child === "string" || typeof child === "number" ? String(child) : null;
|
|
2932
|
+
const segmentStyle = {
|
|
2933
|
+
minHeight,
|
|
2934
|
+
paddingHorizontal: px,
|
|
2935
|
+
paddingVertical: py,
|
|
2936
|
+
...isActive ? { backgroundColor: thumbBg, ...thumbShadow } : { backgroundColor: "transparent" }
|
|
2937
|
+
};
|
|
2903
2938
|
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2904
2939
|
import_react_native38.Pressable,
|
|
2905
2940
|
{
|
|
@@ -2909,14 +2944,12 @@ function SegmentedControl({
|
|
|
2909
2944
|
accessibilityLabel: label ?? void 0,
|
|
2910
2945
|
accessibilityState: { selected: isActive, disabled },
|
|
2911
2946
|
style: ({ pressed }) => [
|
|
2912
|
-
styles34.
|
|
2913
|
-
|
|
2914
|
-
isActive ? activeStyle : inactiveStyle,
|
|
2915
|
-
variant === "filled" && isActive && { borderRadius: import_native.border.radius.full },
|
|
2947
|
+
styles34.segment,
|
|
2948
|
+
segmentStyle,
|
|
2916
2949
|
pressed && !disabled && { opacity: 0.7 },
|
|
2917
2950
|
disabled && { opacity: 0.4 }
|
|
2918
2951
|
],
|
|
2919
|
-
children: label !== null ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native38.Text, { style: [styles34.label, { fontSize, color:
|
|
2952
|
+
children: label !== null ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native38.Text, { style: [styles34.label, { fontSize, color: labelColor }], children: label }) : child
|
|
2920
2953
|
},
|
|
2921
2954
|
index
|
|
2922
2955
|
);
|
|
@@ -2929,6 +2962,7 @@ function SegmentedControl({
|
|
|
2929
2962
|
var import_react11 = require("react");
|
|
2930
2963
|
var import_react_native39 = require("react-native");
|
|
2931
2964
|
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2965
|
+
var INPUT_HEIGHT2 = 44;
|
|
2932
2966
|
var keyboardTypeMap = {
|
|
2933
2967
|
text: "default",
|
|
2934
2968
|
email: "email-address",
|
|
@@ -2942,16 +2976,28 @@ var styles35 = import_react_native39.StyleSheet.create({
|
|
|
2942
2976
|
container: {
|
|
2943
2977
|
gap: import_native.spacing.xxs
|
|
2944
2978
|
},
|
|
2979
|
+
/**
|
|
2980
|
+
* The wrapper draws the pill border and provides the fixed 44pt touch-target height.
|
|
2981
|
+
* justifyContent: 'center' vertically centers the inner TextInput on both iOS and Android.
|
|
2982
|
+
*/
|
|
2983
|
+
inputWrapper: {
|
|
2984
|
+
height: INPUT_HEIGHT2,
|
|
2985
|
+
justifyContent: "center",
|
|
2986
|
+
borderWidth: 1,
|
|
2987
|
+
borderRadius: import_native.border.radius.full
|
|
2988
|
+
},
|
|
2989
|
+
/**
|
|
2990
|
+
* No vertical padding: iOS centers text automatically inside the wrapper height.
|
|
2991
|
+
* textAlignVertical + includeFontPadding are Android-specific but harmless on iOS.
|
|
2992
|
+
* No lineHeight: explicit lineHeight on a single-line TextInput clips ascenders/descenders on iOS.
|
|
2993
|
+
*/
|
|
2945
2994
|
input: {
|
|
2946
2995
|
fontFamily: import_native.font.family.body,
|
|
2947
2996
|
fontSize: import_native.font.size.body,
|
|
2948
|
-
// No lineHeight on single-line TextInput: iOS clips descenders/ascenders when
|
|
2949
|
-
// lineHeight is set explicitly. minHeight: 44 satisfies the touch-target requirement.
|
|
2950
2997
|
paddingHorizontal: import_native.spacing.s,
|
|
2951
|
-
paddingVertical:
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
minHeight: 44
|
|
2998
|
+
paddingVertical: 0,
|
|
2999
|
+
textAlignVertical: "center",
|
|
3000
|
+
includeFontPadding: false
|
|
2955
3001
|
}
|
|
2956
3002
|
});
|
|
2957
3003
|
function FormInput({
|
|
@@ -2975,38 +3021,46 @@ function FormInput({
|
|
|
2975
3021
|
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_react_native39.View, { style: [styles35.container, style], children: [
|
|
2976
3022
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Label, { label }),
|
|
2977
3023
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2978
|
-
import_react_native39.
|
|
3024
|
+
import_react_native39.View,
|
|
2979
3025
|
{
|
|
2980
|
-
accessibilityLabel: typeof label === "string" ? label : name,
|
|
2981
|
-
accessibilityState: { disabled },
|
|
2982
|
-
accessibilityHint: hint,
|
|
2983
|
-
value,
|
|
2984
|
-
defaultValue,
|
|
2985
|
-
placeholder,
|
|
2986
|
-
placeholderTextColor: colors2.colorOnBgSubtle,
|
|
2987
|
-
keyboardType: keyboardTypeMap[type],
|
|
2988
|
-
secureTextEntry: type === "password",
|
|
2989
|
-
autoCapitalize: type === "email" || type === "url" ? "none" : "sentences",
|
|
2990
|
-
autoComplete: type === "email" ? "email" : type === "tel" ? "tel" : void 0,
|
|
2991
|
-
editable: !disabled,
|
|
2992
|
-
onChangeText: onChange,
|
|
2993
|
-
onFocus: () => {
|
|
2994
|
-
setFocused(true);
|
|
2995
|
-
onFocus?.();
|
|
2996
|
-
},
|
|
2997
|
-
onBlur: () => {
|
|
2998
|
-
setFocused(false);
|
|
2999
|
-
onBlur?.();
|
|
3000
|
-
},
|
|
3001
3026
|
style: [
|
|
3002
|
-
styles35.
|
|
3027
|
+
styles35.inputWrapper,
|
|
3003
3028
|
{
|
|
3004
3029
|
borderColor,
|
|
3005
|
-
color: colors2.colorOnBg,
|
|
3006
3030
|
backgroundColor: colors2.colorBg
|
|
3007
3031
|
},
|
|
3008
3032
|
disabled && { opacity: 0.4 }
|
|
3009
|
-
]
|
|
3033
|
+
],
|
|
3034
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3035
|
+
import_react_native39.TextInput,
|
|
3036
|
+
{
|
|
3037
|
+
accessibilityLabel: typeof label === "string" ? label : name,
|
|
3038
|
+
accessibilityState: { disabled },
|
|
3039
|
+
accessibilityHint: hint,
|
|
3040
|
+
value,
|
|
3041
|
+
defaultValue,
|
|
3042
|
+
placeholder,
|
|
3043
|
+
placeholderTextColor: colors2.colorOnBgSubtle,
|
|
3044
|
+
keyboardType: keyboardTypeMap[type],
|
|
3045
|
+
secureTextEntry: type === "password",
|
|
3046
|
+
autoCapitalize: type === "email" || type === "url" ? "none" : "sentences",
|
|
3047
|
+
autoComplete: type === "email" ? "email" : type === "tel" ? "tel" : void 0,
|
|
3048
|
+
editable: !disabled,
|
|
3049
|
+
onChangeText: onChange,
|
|
3050
|
+
onFocus: () => {
|
|
3051
|
+
setFocused(true);
|
|
3052
|
+
onFocus?.();
|
|
3053
|
+
},
|
|
3054
|
+
onBlur: () => {
|
|
3055
|
+
setFocused(false);
|
|
3056
|
+
onBlur?.();
|
|
3057
|
+
},
|
|
3058
|
+
style: [
|
|
3059
|
+
styles35.input,
|
|
3060
|
+
{ color: colors2.colorOnBg }
|
|
3061
|
+
]
|
|
3062
|
+
}
|
|
3063
|
+
)
|
|
3010
3064
|
}
|
|
3011
3065
|
),
|
|
3012
3066
|
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Message, { type: "error", message: error }),
|
|
@@ -3502,18 +3556,30 @@ var styles39 = import_react_native43.StyleSheet.create({
|
|
|
3502
3556
|
fontWeight: import_native.font.weight.semibold,
|
|
3503
3557
|
lineHeight: 24
|
|
3504
3558
|
},
|
|
3505
|
-
|
|
3559
|
+
/**
|
|
3560
|
+
* The inputWrapper holds the border and fixed height so justifyContent: 'center'
|
|
3561
|
+
* vertically centers the TextInput. flex: 1 lets it fill the space between steppers.
|
|
3562
|
+
*/
|
|
3563
|
+
inputWrapper: {
|
|
3506
3564
|
flex: 1,
|
|
3565
|
+
height: STEPPER_SIZE,
|
|
3566
|
+
justifyContent: "center",
|
|
3567
|
+
borderTopWidth: 1,
|
|
3568
|
+
borderBottomWidth: 1
|
|
3569
|
+
},
|
|
3570
|
+
/**
|
|
3571
|
+
* No vertical padding: iOS auto-centers within the wrapper height.
|
|
3572
|
+
* textAlignVertical + includeFontPadding provide Android parity.
|
|
3573
|
+
* No lineHeight: explicit lineHeight clips ascenders/descenders on iOS.
|
|
3574
|
+
*/
|
|
3575
|
+
input: {
|
|
3507
3576
|
fontFamily: import_native.font.family.body,
|
|
3508
3577
|
fontSize: import_native.font.size.body,
|
|
3509
|
-
// No lineHeight on single-line TextInput: iOS clips descenders/ascenders when
|
|
3510
|
-
// lineHeight is set explicitly. minHeight: STEPPER_SIZE (44) covers the touch target.
|
|
3511
3578
|
textAlign: "center",
|
|
3512
3579
|
paddingHorizontal: import_native.spacing.xs,
|
|
3513
|
-
paddingVertical:
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
minHeight: STEPPER_SIZE
|
|
3580
|
+
paddingVertical: 0,
|
|
3581
|
+
textAlignVertical: "center",
|
|
3582
|
+
includeFontPadding: false
|
|
3517
3583
|
}
|
|
3518
3584
|
});
|
|
3519
3585
|
function NumberInput({
|
|
@@ -3579,32 +3645,40 @@ function NumberInput({
|
|
|
3579
3645
|
}
|
|
3580
3646
|
),
|
|
3581
3647
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3582
|
-
import_react_native43.
|
|
3648
|
+
import_react_native43.View,
|
|
3583
3649
|
{
|
|
3584
|
-
accessibilityLabel: label,
|
|
3585
|
-
accessibilityState: { disabled },
|
|
3586
|
-
accessibilityValue: {
|
|
3587
|
-
min,
|
|
3588
|
-
max,
|
|
3589
|
-
now: currentValue,
|
|
3590
|
-
text: String(currentValue)
|
|
3591
|
-
},
|
|
3592
|
-
keyboardType: "numeric",
|
|
3593
|
-
value: String(currentValue),
|
|
3594
|
-
editable: !disabled,
|
|
3595
|
-
onChangeText: (text) => {
|
|
3596
|
-
const parsed = parseFloat(text);
|
|
3597
|
-
const safe = isNaN(parsed) ? 0 : parsed;
|
|
3598
|
-
update(safe);
|
|
3599
|
-
},
|
|
3600
3650
|
style: [
|
|
3601
|
-
styles39.
|
|
3651
|
+
styles39.inputWrapper,
|
|
3602
3652
|
{
|
|
3603
3653
|
borderColor,
|
|
3604
|
-
color: colors2.colorOnBg,
|
|
3605
3654
|
backgroundColor: colors2.colorBg
|
|
3606
3655
|
}
|
|
3607
|
-
]
|
|
3656
|
+
],
|
|
3657
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3658
|
+
import_react_native43.TextInput,
|
|
3659
|
+
{
|
|
3660
|
+
accessibilityLabel: label,
|
|
3661
|
+
accessibilityState: { disabled },
|
|
3662
|
+
accessibilityValue: {
|
|
3663
|
+
min,
|
|
3664
|
+
max,
|
|
3665
|
+
now: currentValue,
|
|
3666
|
+
text: String(currentValue)
|
|
3667
|
+
},
|
|
3668
|
+
keyboardType: "numeric",
|
|
3669
|
+
value: String(currentValue),
|
|
3670
|
+
editable: !disabled,
|
|
3671
|
+
onChangeText: (text) => {
|
|
3672
|
+
const parsed = parseFloat(text);
|
|
3673
|
+
const safe = isNaN(parsed) ? 0 : parsed;
|
|
3674
|
+
update(safe);
|
|
3675
|
+
},
|
|
3676
|
+
style: [
|
|
3677
|
+
styles39.input,
|
|
3678
|
+
{ color: colors2.colorOnBg }
|
|
3679
|
+
]
|
|
3680
|
+
}
|
|
3681
|
+
)
|
|
3608
3682
|
}
|
|
3609
3683
|
),
|
|
3610
3684
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|