velto-ui 1.0.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.
- package/dist/components/Button/Button.js +142 -0
- package/dist/components/Button/button.config.js +44 -0
- package/dist/components/Button/button.utils.js +48 -0
- package/dist/components/Input/Input.js +195 -0
- package/dist/components/Input/input.config.js +61 -0
- package/dist/components/Input/input.utils.js +16 -0
- package/dist/index.js +36 -0
- package/dist/theme/ThemeProvider.js +47 -0
- package/dist/theme/dark.js +20 -0
- package/dist/theme/index.js +16 -0
- package/dist/theme/light.js +20 -0
- package/package.json +33 -0
- package/src/components/Button/Button.js +165 -0
- package/src/components/Button/button.config.js +16 -0
- package/src/components/Button/button.utils.js +48 -0
- package/src/components/Input/Input.js +206 -0
- package/src/components/Input/input.config.js +28 -0
- package/src/components/Input/input.utils.js +10 -0
- package/src/index.js +5 -0
- package/src/theme/ThemeProvider.js +29 -0
- package/src/theme/dark.js +19 -0
- package/src/theme/index.js +1 -0
- package/src/theme/light.js +19 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports["default"] = void 0;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
var _theme = require("../../theme");
|
|
11
|
+
var _button = require("./button.config");
|
|
12
|
+
var _button2 = require("./button.utils");
|
|
13
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
14
|
+
var Button = function Button(_ref) {
|
|
15
|
+
var title = _ref.title,
|
|
16
|
+
_ref$variant = _ref.variant,
|
|
17
|
+
variant = _ref$variant === void 0 ? 'solid' : _ref$variant,
|
|
18
|
+
_ref$size = _ref.size,
|
|
19
|
+
size = _ref$size === void 0 ? 'md' : _ref$size,
|
|
20
|
+
_ref$intent = _ref.intent,
|
|
21
|
+
intent = _ref$intent === void 0 ? 'primary' : _ref$intent,
|
|
22
|
+
_ref$shape = _ref.shape,
|
|
23
|
+
shape = _ref$shape === void 0 ? 'rounded' : _ref$shape,
|
|
24
|
+
_ref$loading = _ref.loading,
|
|
25
|
+
loading = _ref$loading === void 0 ? false : _ref$loading,
|
|
26
|
+
loadingText = _ref.loadingText,
|
|
27
|
+
_ref$loadingPosition = _ref.loadingPosition,
|
|
28
|
+
loadingPosition = _ref$loadingPosition === void 0 ? 'center' : _ref$loadingPosition,
|
|
29
|
+
_ref$disabled = _ref.disabled,
|
|
30
|
+
disabled = _ref$disabled === void 0 ? false : _ref$disabled,
|
|
31
|
+
_ref$fullWidth = _ref.fullWidth,
|
|
32
|
+
fullWidth = _ref$fullWidth === void 0 ? false : _ref$fullWidth,
|
|
33
|
+
_ref$textTransform = _ref.textTransform,
|
|
34
|
+
textTransform = _ref$textTransform === void 0 ? 'none' : _ref$textTransform,
|
|
35
|
+
bg = _ref.bg,
|
|
36
|
+
textColor = _ref.textColor,
|
|
37
|
+
disabledBg = _ref.disabledBg,
|
|
38
|
+
disabledTextColor = _ref.disabledTextColor,
|
|
39
|
+
borderRadius = _ref.borderRadius,
|
|
40
|
+
paddingVertical = _ref.paddingVertical,
|
|
41
|
+
paddingHorizontal = _ref.paddingHorizontal,
|
|
42
|
+
leftIcon = _ref.leftIcon,
|
|
43
|
+
rightIcon = _ref.rightIcon,
|
|
44
|
+
_style = _ref.style,
|
|
45
|
+
textStyle = _ref.textStyle,
|
|
46
|
+
sx = _ref.sx,
|
|
47
|
+
onPress = _ref.onPress;
|
|
48
|
+
var _useTheme = (0, _theme.useTheme)(),
|
|
49
|
+
theme = _useTheme.theme;
|
|
50
|
+
var colors = theme.colors;
|
|
51
|
+
|
|
52
|
+
// resolve size, intent and variant styles based on props
|
|
53
|
+
var sizeConfig = _button.BUTTON_SIZES[size] || _button.BUTTON_SIZES.md;
|
|
54
|
+
var intentColor = (0, _button2.getIntentColor)(intent, colors);
|
|
55
|
+
var variantStyle = (0, _button2.getVariantStyle)(variant, intentColor);
|
|
56
|
+
|
|
57
|
+
// button becomes non-clickable when disabled or loading
|
|
58
|
+
var isDisabled = disabled || loading;
|
|
59
|
+
|
|
60
|
+
// only apply disabled styling when explicitly disabled (not during loading)
|
|
61
|
+
var showDisabledStyle = disabled;
|
|
62
|
+
|
|
63
|
+
// final background color with override priority
|
|
64
|
+
var finalBg = bg !== null && bg !== void 0 ? bg : showDisabledStyle ? disabledBg || colors.disabled : variantStyle.backgroundColor;
|
|
65
|
+
|
|
66
|
+
// final text color with override priority
|
|
67
|
+
var finalTextColor = textColor !== null && textColor !== void 0 ? textColor : showDisabledStyle ? disabledTextColor || '#888' : variantStyle.textColor;
|
|
68
|
+
|
|
69
|
+
// padding controls actual button size (not just text)
|
|
70
|
+
var finalPV = paddingVertical !== null && paddingVertical !== void 0 ? paddingVertical : sizeConfig.pv;
|
|
71
|
+
var finalPH = paddingHorizontal !== null && paddingHorizontal !== void 0 ? paddingHorizontal : sizeConfig.ph;
|
|
72
|
+
|
|
73
|
+
// pill shape only affects border radius, not height
|
|
74
|
+
var finalRadius = borderRadius !== null && borderRadius !== void 0 ? borderRadius : shape === 'pill' ? 999 : _button.BUTTON_SHAPES[shape] || _button.BUTTON_SHAPES.rounded;
|
|
75
|
+
|
|
76
|
+
// switch text when loadingText is provided
|
|
77
|
+
var finalText = loading && loadingText ? loadingText : title;
|
|
78
|
+
return /*#__PURE__*/_react["default"].createElement(_reactNative.Pressable, {
|
|
79
|
+
onPress: onPress,
|
|
80
|
+
disabled: isDisabled,
|
|
81
|
+
android_ripple: _reactNative.Platform.OS === 'android' ? {
|
|
82
|
+
color: '#ccc'
|
|
83
|
+
} : undefined,
|
|
84
|
+
style: function style(_ref2) {
|
|
85
|
+
var pressed = _ref2.pressed;
|
|
86
|
+
return [styles.base, {
|
|
87
|
+
backgroundColor: finalBg,
|
|
88
|
+
borderWidth: variantStyle.borderWidth || 0,
|
|
89
|
+
borderColor: variantStyle.borderColor || 'transparent',
|
|
90
|
+
paddingVertical: finalPV,
|
|
91
|
+
paddingHorizontal: finalPH,
|
|
92
|
+
borderRadius: finalRadius,
|
|
93
|
+
// handles both normal and full width layouts
|
|
94
|
+
alignSelf: fullWidth ? 'stretch' : 'flex-start',
|
|
95
|
+
width: fullWidth ? '100%' : undefined,
|
|
96
|
+
opacity: pressed ? 0.85 : 1
|
|
97
|
+
}, sx, _style];
|
|
98
|
+
}
|
|
99
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
100
|
+
style: styles.row
|
|
101
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
102
|
+
style: styles.side
|
|
103
|
+
}, loading && loadingPosition === 'left' ? /*#__PURE__*/_react["default"].createElement(_reactNative.ActivityIndicator, {
|
|
104
|
+
color: finalTextColor
|
|
105
|
+
}) : !loading && leftIcon), /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
106
|
+
style: styles.center
|
|
107
|
+
}, loading && loadingPosition === 'center' ? /*#__PURE__*/_react["default"].createElement(_reactNative.ActivityIndicator, {
|
|
108
|
+
color: finalTextColor
|
|
109
|
+
}) : /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
|
|
110
|
+
numberOfLines: 1,
|
|
111
|
+
style: [styles.text, {
|
|
112
|
+
color: finalTextColor,
|
|
113
|
+
fontSize: sizeConfig.fontSize,
|
|
114
|
+
textTransform: textTransform
|
|
115
|
+
}, textStyle]
|
|
116
|
+
}, finalText)), /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
117
|
+
style: styles.side
|
|
118
|
+
}, loading && loadingPosition === 'right' ? /*#__PURE__*/_react["default"].createElement(_reactNative.ActivityIndicator, {
|
|
119
|
+
color: finalTextColor
|
|
120
|
+
}) : !loading && rightIcon)));
|
|
121
|
+
};
|
|
122
|
+
var _default = exports["default"] = /*#__PURE__*/(0, _react.memo)(Button);
|
|
123
|
+
var styles = _reactNative.StyleSheet.create({
|
|
124
|
+
base: {
|
|
125
|
+
justifyContent: 'center',
|
|
126
|
+
alignItems: 'center'
|
|
127
|
+
},
|
|
128
|
+
row: {
|
|
129
|
+
flexDirection: 'row',
|
|
130
|
+
alignItems: 'center'
|
|
131
|
+
},
|
|
132
|
+
side: {
|
|
133
|
+
minWidth: 24,
|
|
134
|
+
alignItems: 'center'
|
|
135
|
+
},
|
|
136
|
+
center: {
|
|
137
|
+
marginHorizontal: 6
|
|
138
|
+
},
|
|
139
|
+
text: {
|
|
140
|
+
fontWeight: '600'
|
|
141
|
+
}
|
|
142
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.BUTTON_VARIANTS = exports.BUTTON_SIZES = exports.BUTTON_SHAPES = void 0;
|
|
7
|
+
var BUTTON_VARIANTS = exports.BUTTON_VARIANTS = ['solid', 'outline', 'ghost', 'soft', 'link'];
|
|
8
|
+
var BUTTON_SIZES = exports.BUTTON_SIZES = {
|
|
9
|
+
xs: {
|
|
10
|
+
pv: 6,
|
|
11
|
+
ph: 10,
|
|
12
|
+
fontSize: 10
|
|
13
|
+
},
|
|
14
|
+
sm: {
|
|
15
|
+
pv: 8,
|
|
16
|
+
ph: 12,
|
|
17
|
+
fontSize: 12
|
|
18
|
+
},
|
|
19
|
+
md: {
|
|
20
|
+
pv: 12,
|
|
21
|
+
ph: 16,
|
|
22
|
+
fontSize: 16
|
|
23
|
+
},
|
|
24
|
+
lg: {
|
|
25
|
+
pv: 16,
|
|
26
|
+
ph: 20,
|
|
27
|
+
fontSize: 18
|
|
28
|
+
},
|
|
29
|
+
xl: {
|
|
30
|
+
pv: 20,
|
|
31
|
+
ph: 24,
|
|
32
|
+
fontSize: 20
|
|
33
|
+
},
|
|
34
|
+
icon: {
|
|
35
|
+
pv: 10,
|
|
36
|
+
ph: 10,
|
|
37
|
+
fontSize: 16
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var BUTTON_SHAPES = exports.BUTTON_SHAPES = {
|
|
41
|
+
rounded: 10,
|
|
42
|
+
pill: 999,
|
|
43
|
+
square: 2
|
|
44
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getVariantStyle = exports.getIntentColor = void 0;
|
|
7
|
+
var getIntentColor = exports.getIntentColor = function getIntentColor(intent, colors) {
|
|
8
|
+
var map = {
|
|
9
|
+
primary: colors.primary,
|
|
10
|
+
success: colors.secondary,
|
|
11
|
+
error: colors.danger,
|
|
12
|
+
warning: '#FFA500',
|
|
13
|
+
info: '#3B82F6'
|
|
14
|
+
};
|
|
15
|
+
return map[intent] || colors.primary;
|
|
16
|
+
};
|
|
17
|
+
var getVariantStyle = exports.getVariantStyle = function getVariantStyle(variant, intentColor) {
|
|
18
|
+
switch (variant) {
|
|
19
|
+
case 'outline':
|
|
20
|
+
return {
|
|
21
|
+
backgroundColor: 'transparent',
|
|
22
|
+
borderColor: intentColor,
|
|
23
|
+
borderWidth: 1,
|
|
24
|
+
textColor: intentColor
|
|
25
|
+
};
|
|
26
|
+
case 'ghost':
|
|
27
|
+
return {
|
|
28
|
+
backgroundColor: 'transparent',
|
|
29
|
+
textColor: intentColor
|
|
30
|
+
};
|
|
31
|
+
case 'soft':
|
|
32
|
+
return {
|
|
33
|
+
backgroundColor: intentColor + '20',
|
|
34
|
+
textColor: intentColor
|
|
35
|
+
};
|
|
36
|
+
case 'link':
|
|
37
|
+
return {
|
|
38
|
+
backgroundColor: 'transparent',
|
|
39
|
+
textColor: intentColor
|
|
40
|
+
};
|
|
41
|
+
case 'solid':
|
|
42
|
+
default:
|
|
43
|
+
return {
|
|
44
|
+
backgroundColor: intentColor,
|
|
45
|
+
textColor: '#fff'
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports["default"] = void 0;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
var _theme = require("../../theme");
|
|
11
|
+
var _input = require("./input.config");
|
|
12
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
13
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
14
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
15
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
16
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
17
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
18
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
19
|
+
var Input = function Input(_ref) {
|
|
20
|
+
var value = _ref.value,
|
|
21
|
+
onChangeText = _ref.onChangeText,
|
|
22
|
+
label = _ref.label,
|
|
23
|
+
placeholder = _ref.placeholder,
|
|
24
|
+
helperText = _ref.helperText,
|
|
25
|
+
error = _ref.error,
|
|
26
|
+
success = _ref.success,
|
|
27
|
+
_ref$variant = _ref.variant,
|
|
28
|
+
variant = _ref$variant === void 0 ? 'outline' : _ref$variant,
|
|
29
|
+
_ref$size = _ref.size,
|
|
30
|
+
size = _ref$size === void 0 ? 'md' : _ref$size,
|
|
31
|
+
_ref$disabled = _ref.disabled,
|
|
32
|
+
disabled = _ref$disabled === void 0 ? false : _ref$disabled,
|
|
33
|
+
_ref$readOnly = _ref.readOnly,
|
|
34
|
+
readOnly = _ref$readOnly === void 0 ? false : _ref$readOnly,
|
|
35
|
+
_ref$secureTextEntry = _ref.secureTextEntry,
|
|
36
|
+
secureTextEntry = _ref$secureTextEntry === void 0 ? false : _ref$secureTextEntry,
|
|
37
|
+
_ref$multiline = _ref.multiline,
|
|
38
|
+
multiline = _ref$multiline === void 0 ? false : _ref$multiline,
|
|
39
|
+
_ref$clearable = _ref.clearable,
|
|
40
|
+
clearable = _ref$clearable === void 0 ? false : _ref$clearable,
|
|
41
|
+
bg = _ref.bg,
|
|
42
|
+
textColor = _ref.textColor,
|
|
43
|
+
borderRadius = _ref.borderRadius,
|
|
44
|
+
paddingVertical = _ref.paddingVertical,
|
|
45
|
+
paddingHorizontal = _ref.paddingHorizontal,
|
|
46
|
+
style = _ref.style,
|
|
47
|
+
inputStyle = _ref.inputStyle;
|
|
48
|
+
var _useTheme = (0, _theme.useTheme)(),
|
|
49
|
+
theme = _useTheme.theme;
|
|
50
|
+
var colors = theme.colors;
|
|
51
|
+
var _useState = (0, _react.useState)(false),
|
|
52
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
53
|
+
focused = _useState2[0],
|
|
54
|
+
setFocused = _useState2[1];
|
|
55
|
+
|
|
56
|
+
// password toggle state
|
|
57
|
+
var _useState3 = (0, _react.useState)(secureTextEntry),
|
|
58
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
59
|
+
secure = _useState4[0],
|
|
60
|
+
setSecure = _useState4[1];
|
|
61
|
+
|
|
62
|
+
// ✅ sync with prop change (IMPORTANT FIX)
|
|
63
|
+
(0, _react.useEffect)(function () {
|
|
64
|
+
setSecure(secureTextEntry);
|
|
65
|
+
}, [secureTextEntry]);
|
|
66
|
+
var sizeConfig = _input.INPUT_SIZES[size] || _input.INPUT_SIZES.md;
|
|
67
|
+
|
|
68
|
+
/* ---------------- STATE PRIORITY ---------------- */
|
|
69
|
+
var state = disabled ? 'disabled' : error ? 'error' : success ? 'success' : focused ? 'focus' : 'default';
|
|
70
|
+
|
|
71
|
+
/* ---------------- COLORS ---------------- */
|
|
72
|
+
|
|
73
|
+
var finalBorderColor = state === 'disabled' ? colors.border : state === 'error' ? colors.error : state === 'success' ? colors.success : state === 'focus' ? colors.primary : colors.border;
|
|
74
|
+
var backgroundColor = bg !== null && bg !== void 0 ? bg : variant === 'solid' ? colors.surface || colors.background : 'transparent';
|
|
75
|
+
var finalTextColor = textColor !== null && textColor !== void 0 ? textColor : disabled ? colors.textSecondary || '#999' : colors.text;
|
|
76
|
+
var finalPV = paddingVertical !== null && paddingVertical !== void 0 ? paddingVertical : sizeConfig.pv;
|
|
77
|
+
var finalPH = paddingHorizontal !== null && paddingHorizontal !== void 0 ? paddingHorizontal : sizeConfig.ph;
|
|
78
|
+
var showClear = clearable && (value === null || value === void 0 ? void 0 : value.length) > 0 && !disabled && !readOnly;
|
|
79
|
+
|
|
80
|
+
/* ---------------- VARIANT STYLE ---------------- */
|
|
81
|
+
|
|
82
|
+
var getVariantStyle = function getVariantStyle() {
|
|
83
|
+
switch (variant) {
|
|
84
|
+
case 'solid':
|
|
85
|
+
return {
|
|
86
|
+
borderWidth: 1
|
|
87
|
+
};
|
|
88
|
+
case 'outline':
|
|
89
|
+
return {
|
|
90
|
+
borderWidth: 1
|
|
91
|
+
};
|
|
92
|
+
case 'underline':
|
|
93
|
+
return {
|
|
94
|
+
borderBottomWidth: 1
|
|
95
|
+
};
|
|
96
|
+
case 'ghost':
|
|
97
|
+
return {
|
|
98
|
+
borderWidth: 0
|
|
99
|
+
};
|
|
100
|
+
default:
|
|
101
|
+
return {
|
|
102
|
+
borderWidth: 1
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/* ---------------- RENDER ---------------- */
|
|
108
|
+
|
|
109
|
+
return /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
110
|
+
style: {
|
|
111
|
+
width: '100%'
|
|
112
|
+
}
|
|
113
|
+
}, label && /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
|
|
114
|
+
style: [styles.label, {
|
|
115
|
+
color: colors.text
|
|
116
|
+
}]
|
|
117
|
+
}, label), /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
118
|
+
style: [styles.wrapper, getVariantStyle(), {
|
|
119
|
+
backgroundColor: backgroundColor,
|
|
120
|
+
borderColor: finalBorderColor,
|
|
121
|
+
borderRadius: borderRadius !== null && borderRadius !== void 0 ? borderRadius : 8,
|
|
122
|
+
paddingVertical: finalPV,
|
|
123
|
+
paddingHorizontal: finalPH,
|
|
124
|
+
minHeight: multiline ? 80 : 44,
|
|
125
|
+
opacity: disabled ? 0.5 : 1
|
|
126
|
+
}, style]
|
|
127
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.TextInput, {
|
|
128
|
+
value: value,
|
|
129
|
+
onChangeText: onChangeText,
|
|
130
|
+
placeholder: placeholder,
|
|
131
|
+
editable: !disabled && !readOnly,
|
|
132
|
+
secureTextEntry: secure // ✅ controlled by state
|
|
133
|
+
,
|
|
134
|
+
multiline: multiline,
|
|
135
|
+
placeholderTextColor: colors.border,
|
|
136
|
+
onFocus: function onFocus() {
|
|
137
|
+
return setFocused(true);
|
|
138
|
+
},
|
|
139
|
+
onBlur: function onBlur() {
|
|
140
|
+
return setFocused(false);
|
|
141
|
+
},
|
|
142
|
+
style: [styles.input, {
|
|
143
|
+
color: finalTextColor,
|
|
144
|
+
fontSize: sizeConfig.fontSize,
|
|
145
|
+
textAlignVertical: multiline ? 'top' : 'center'
|
|
146
|
+
}, inputStyle]
|
|
147
|
+
}), /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
148
|
+
style: styles.right
|
|
149
|
+
}, showClear && /*#__PURE__*/_react["default"].createElement(_reactNative.Pressable, {
|
|
150
|
+
onPress: function onPress() {
|
|
151
|
+
return onChangeText('');
|
|
152
|
+
}
|
|
153
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
|
|
154
|
+
style: {
|
|
155
|
+
color: colors.text
|
|
156
|
+
}
|
|
157
|
+
}, "\u2715")), secureTextEntry && /*#__PURE__*/_react["default"].createElement(_reactNative.Pressable, {
|
|
158
|
+
onPress: function onPress() {
|
|
159
|
+
return setSecure(function (prev) {
|
|
160
|
+
return !prev;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
|
|
164
|
+
style: {
|
|
165
|
+
color: colors.text,
|
|
166
|
+
fontSize: 16
|
|
167
|
+
}
|
|
168
|
+
}, secure ? '👁️' : '🙈')))), (helperText || error) && /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
|
|
169
|
+
style: {
|
|
170
|
+
marginTop: 4,
|
|
171
|
+
fontSize: 12,
|
|
172
|
+
color: error ? colors.error : success ? colors.success : colors.textSecondary || colors.text
|
|
173
|
+
}
|
|
174
|
+
}, error || helperText));
|
|
175
|
+
};
|
|
176
|
+
var _default = exports["default"] = /*#__PURE__*/(0, _react.memo)(Input);
|
|
177
|
+
/* ---------------- STYLES ---------------- */
|
|
178
|
+
var styles = _reactNative.StyleSheet.create({
|
|
179
|
+
label: {
|
|
180
|
+
marginBottom: 4,
|
|
181
|
+
fontWeight: '500'
|
|
182
|
+
},
|
|
183
|
+
wrapper: {
|
|
184
|
+
flexDirection: 'row',
|
|
185
|
+
alignItems: 'center'
|
|
186
|
+
},
|
|
187
|
+
input: {
|
|
188
|
+
flex: 1
|
|
189
|
+
},
|
|
190
|
+
right: {
|
|
191
|
+
flexDirection: 'row',
|
|
192
|
+
alignItems: 'center',
|
|
193
|
+
gap: 6
|
|
194
|
+
}
|
|
195
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.INPUT_VARIANTS = exports.INPUT_SIZES = void 0;
|
|
7
|
+
var INPUT_SIZES = exports.INPUT_SIZES = {
|
|
8
|
+
xs: {
|
|
9
|
+
fontSize: 12,
|
|
10
|
+
pv: 6,
|
|
11
|
+
ph: 8
|
|
12
|
+
},
|
|
13
|
+
sm: {
|
|
14
|
+
fontSize: 14,
|
|
15
|
+
pv: 8,
|
|
16
|
+
ph: 10
|
|
17
|
+
},
|
|
18
|
+
md: {
|
|
19
|
+
fontSize: 16,
|
|
20
|
+
pv: 10,
|
|
21
|
+
ph: 12
|
|
22
|
+
},
|
|
23
|
+
lg: {
|
|
24
|
+
fontSize: 18,
|
|
25
|
+
pv: 12,
|
|
26
|
+
ph: 14
|
|
27
|
+
},
|
|
28
|
+
xl: {
|
|
29
|
+
fontSize: 20,
|
|
30
|
+
pv: 14,
|
|
31
|
+
ph: 16
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var INPUT_VARIANTS = exports.INPUT_VARIANTS = {
|
|
35
|
+
solid: function solid(c) {
|
|
36
|
+
return {
|
|
37
|
+
backgroundColor: c.background,
|
|
38
|
+
borderWidth: 0
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
outline: function outline(c) {
|
|
42
|
+
return {
|
|
43
|
+
backgroundColor: 'transparent',
|
|
44
|
+
borderWidth: 1,
|
|
45
|
+
borderColor: c.border
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
underline: function underline(c) {
|
|
49
|
+
return {
|
|
50
|
+
backgroundColor: 'transparent',
|
|
51
|
+
borderBottomWidth: 1,
|
|
52
|
+
borderColor: c.border
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
ghost: function ghost() {
|
|
56
|
+
return {
|
|
57
|
+
backgroundColor: 'transparent',
|
|
58
|
+
borderWidth: 0
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getStateColor = void 0;
|
|
7
|
+
var getStateColor = exports.getStateColor = function getStateColor(state, colors) {
|
|
8
|
+
switch (state) {
|
|
9
|
+
case 'error':
|
|
10
|
+
return colors.error;
|
|
11
|
+
case 'success':
|
|
12
|
+
return colors.success;
|
|
13
|
+
default:
|
|
14
|
+
return colors.border;
|
|
15
|
+
}
|
|
16
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _exportNames = {
|
|
7
|
+
Button: true,
|
|
8
|
+
Input: true
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "Button", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function get() {
|
|
13
|
+
return _Button["default"];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(exports, "Input", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function get() {
|
|
19
|
+
return _Input["default"];
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
var _Button = _interopRequireDefault(require("./components/Button/Button"));
|
|
23
|
+
var _Input = _interopRequireDefault(require("./components/Input/Input"));
|
|
24
|
+
var _theme = require("./theme");
|
|
25
|
+
Object.keys(_theme).forEach(function (key) {
|
|
26
|
+
if (key === "default" || key === "__esModule") return;
|
|
27
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
28
|
+
if (key in exports && exports[key] === _theme[key]) return;
|
|
29
|
+
Object.defineProperty(exports, key, {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
get: function get() {
|
|
32
|
+
return _theme[key];
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.useTheme = exports.ThemeProvider = void 0;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _light = require("./light");
|
|
10
|
+
var _dark = require("./dark");
|
|
11
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
12
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
13
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
14
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
15
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
16
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
17
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
18
|
+
var ThemeContext = /*#__PURE__*/(0, _react.createContext)();
|
|
19
|
+
var ThemeProvider = exports.ThemeProvider = function ThemeProvider(_ref) {
|
|
20
|
+
var children = _ref.children;
|
|
21
|
+
var _useState = (0, _react.useState)('light'),
|
|
22
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
23
|
+
mode = _useState2[0],
|
|
24
|
+
setMode = _useState2[1];
|
|
25
|
+
var theme = (0, _react.useMemo)(function () {
|
|
26
|
+
return mode === 'light' ? _light.lightTheme : _dark.darkTheme;
|
|
27
|
+
}, [mode]);
|
|
28
|
+
var toggleTheme = function toggleTheme() {
|
|
29
|
+
setMode(function (prev) {
|
|
30
|
+
return prev === 'light' ? 'dark' : 'light';
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
return /*#__PURE__*/_react["default"].createElement(ThemeContext.Provider, {
|
|
34
|
+
value: {
|
|
35
|
+
theme: theme,
|
|
36
|
+
mode: mode,
|
|
37
|
+
toggleTheme: toggleTheme
|
|
38
|
+
}
|
|
39
|
+
}, children);
|
|
40
|
+
};
|
|
41
|
+
var useTheme = exports.useTheme = function useTheme() {
|
|
42
|
+
var context = (0, _react.useContext)(ThemeContext);
|
|
43
|
+
if (!context) {
|
|
44
|
+
throw new Error('useTheme must be used inside ThemeProvider');
|
|
45
|
+
}
|
|
46
|
+
return context;
|
|
47
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.darkTheme = void 0;
|
|
7
|
+
var darkTheme = exports.darkTheme = {
|
|
8
|
+
mode: 'dark',
|
|
9
|
+
colors: {
|
|
10
|
+
primary: '#3b82f6',
|
|
11
|
+
background: '#111827',
|
|
12
|
+
surface: '#1f2937',
|
|
13
|
+
text: '#f9fafb',
|
|
14
|
+
textSecondary: '#9ca3af',
|
|
15
|
+
border: '#374151',
|
|
16
|
+
error: '#ef4444',
|
|
17
|
+
success: '#22c55e',
|
|
18
|
+
disabled: '#374151'
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _ThemeProvider = require("./ThemeProvider");
|
|
7
|
+
Object.keys(_ThemeProvider).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _ThemeProvider[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function get() {
|
|
13
|
+
return _ThemeProvider[key];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.lightTheme = void 0;
|
|
7
|
+
var lightTheme = exports.lightTheme = {
|
|
8
|
+
mode: 'light',
|
|
9
|
+
colors: {
|
|
10
|
+
primary: '#2563eb',
|
|
11
|
+
background: '#ffffff',
|
|
12
|
+
surface: '#f9fafb',
|
|
13
|
+
text: '#111827',
|
|
14
|
+
textSecondary: '#6b7280',
|
|
15
|
+
border: '#d1d5db',
|
|
16
|
+
error: '#dc2626',
|
|
17
|
+
success: '#16a34a',
|
|
18
|
+
disabled: '#e5e7eb'
|
|
19
|
+
}
|
|
20
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "velto-ui",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "VeltoUI - React Native UI component library",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"react-native": "src/index.js",
|
|
7
|
+
"author": "Kanhu Charan Sahoo",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"react-native",
|
|
11
|
+
"ui",
|
|
12
|
+
"components",
|
|
13
|
+
"design-system",
|
|
14
|
+
"velto-ui"
|
|
15
|
+
],
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"react": ">=16.8.0",
|
|
18
|
+
"react-native": ">=0.60.0"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "babel src -d dist"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@babel/cli": "^7.29.7",
|
|
29
|
+
"@babel/core": "^7.29.7",
|
|
30
|
+
"@babel/preset-env": "^7.29.7",
|
|
31
|
+
"@babel/preset-react": "^7.29.7"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { Pressable, Text, ActivityIndicator, View, StyleSheet, Platform } from 'react-native';
|
|
3
|
+
import { useTheme } from '../../theme';
|
|
4
|
+
import { BUTTON_SIZES, BUTTON_SHAPES } from './button.config';
|
|
5
|
+
import { getIntentColor, getVariantStyle } from './button.utils';
|
|
6
|
+
|
|
7
|
+
const Button = ({
|
|
8
|
+
title,
|
|
9
|
+
variant = 'solid',
|
|
10
|
+
size = 'md',
|
|
11
|
+
intent = 'primary',
|
|
12
|
+
shape = 'rounded',
|
|
13
|
+
|
|
14
|
+
loading = false,
|
|
15
|
+
loadingText,
|
|
16
|
+
loadingPosition = 'center',
|
|
17
|
+
disabled = false,
|
|
18
|
+
|
|
19
|
+
fullWidth = false,
|
|
20
|
+
textTransform = 'none',
|
|
21
|
+
|
|
22
|
+
bg,
|
|
23
|
+
textColor,
|
|
24
|
+
disabledBg,
|
|
25
|
+
disabledTextColor,
|
|
26
|
+
borderRadius,
|
|
27
|
+
paddingVertical,
|
|
28
|
+
paddingHorizontal,
|
|
29
|
+
|
|
30
|
+
leftIcon,
|
|
31
|
+
rightIcon,
|
|
32
|
+
|
|
33
|
+
style,
|
|
34
|
+
textStyle,
|
|
35
|
+
sx,
|
|
36
|
+
|
|
37
|
+
onPress,
|
|
38
|
+
}) => {
|
|
39
|
+
const { theme } = useTheme();
|
|
40
|
+
const colors = theme.colors;
|
|
41
|
+
|
|
42
|
+
// resolve size, intent and variant styles based on props
|
|
43
|
+
const sizeConfig = BUTTON_SIZES[size] || BUTTON_SIZES.md;
|
|
44
|
+
const intentColor = getIntentColor(intent, colors);
|
|
45
|
+
const variantStyle = getVariantStyle(variant, intentColor);
|
|
46
|
+
|
|
47
|
+
// button becomes non-clickable when disabled or loading
|
|
48
|
+
const isDisabled = disabled || loading;
|
|
49
|
+
|
|
50
|
+
// only apply disabled styling when explicitly disabled (not during loading)
|
|
51
|
+
const showDisabledStyle = disabled;
|
|
52
|
+
|
|
53
|
+
// final background color with override priority
|
|
54
|
+
const finalBg =
|
|
55
|
+
bg ?? (showDisabledStyle ? disabledBg || colors.disabled : variantStyle.backgroundColor);
|
|
56
|
+
|
|
57
|
+
// final text color with override priority
|
|
58
|
+
const finalTextColor =
|
|
59
|
+
textColor ?? (showDisabledStyle ? disabledTextColor || '#888' : variantStyle.textColor);
|
|
60
|
+
|
|
61
|
+
// padding controls actual button size (not just text)
|
|
62
|
+
const finalPV = paddingVertical ?? sizeConfig.pv;
|
|
63
|
+
const finalPH = paddingHorizontal ?? sizeConfig.ph;
|
|
64
|
+
|
|
65
|
+
// pill shape only affects border radius, not height
|
|
66
|
+
const finalRadius =
|
|
67
|
+
borderRadius ?? (shape === 'pill' ? 999 : BUTTON_SHAPES[shape] || BUTTON_SHAPES.rounded);
|
|
68
|
+
|
|
69
|
+
// switch text when loadingText is provided
|
|
70
|
+
const finalText = loading && loadingText ? loadingText : title;
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<Pressable
|
|
74
|
+
onPress={onPress}
|
|
75
|
+
disabled={isDisabled}
|
|
76
|
+
android_ripple={Platform.OS === 'android' ? { color: '#ccc' } : undefined}
|
|
77
|
+
style={({ pressed }) => [
|
|
78
|
+
styles.base,
|
|
79
|
+
{
|
|
80
|
+
backgroundColor: finalBg,
|
|
81
|
+
borderWidth: variantStyle.borderWidth || 0,
|
|
82
|
+
borderColor: variantStyle.borderColor || 'transparent',
|
|
83
|
+
|
|
84
|
+
paddingVertical: finalPV,
|
|
85
|
+
paddingHorizontal: finalPH,
|
|
86
|
+
|
|
87
|
+
borderRadius: finalRadius,
|
|
88
|
+
|
|
89
|
+
// handles both normal and full width layouts
|
|
90
|
+
alignSelf: fullWidth ? 'stretch' : 'flex-start',
|
|
91
|
+
width: fullWidth ? '100%' : undefined,
|
|
92
|
+
|
|
93
|
+
opacity: pressed ? 0.85 : 1,
|
|
94
|
+
},
|
|
95
|
+
sx,
|
|
96
|
+
style,
|
|
97
|
+
]}
|
|
98
|
+
>
|
|
99
|
+
<View style={styles.row}>
|
|
100
|
+
{/* left side: icon or loader */}
|
|
101
|
+
<View style={styles.side}>
|
|
102
|
+
{loading && loadingPosition === 'left' ? (
|
|
103
|
+
<ActivityIndicator color={finalTextColor} />
|
|
104
|
+
) : (
|
|
105
|
+
!loading && leftIcon
|
|
106
|
+
)}
|
|
107
|
+
</View>
|
|
108
|
+
|
|
109
|
+
{/* center: text or loader */}
|
|
110
|
+
<View style={styles.center}>
|
|
111
|
+
{loading && loadingPosition === 'center' ? (
|
|
112
|
+
<ActivityIndicator color={finalTextColor} />
|
|
113
|
+
) : (
|
|
114
|
+
<Text
|
|
115
|
+
numberOfLines={1}
|
|
116
|
+
style={[
|
|
117
|
+
styles.text,
|
|
118
|
+
{
|
|
119
|
+
color: finalTextColor,
|
|
120
|
+
fontSize: sizeConfig.fontSize,
|
|
121
|
+
textTransform,
|
|
122
|
+
},
|
|
123
|
+
textStyle,
|
|
124
|
+
]}
|
|
125
|
+
>
|
|
126
|
+
{finalText}
|
|
127
|
+
</Text>
|
|
128
|
+
)}
|
|
129
|
+
</View>
|
|
130
|
+
|
|
131
|
+
{/* right side: icon or loader */}
|
|
132
|
+
<View style={styles.side}>
|
|
133
|
+
{loading && loadingPosition === 'right' ? (
|
|
134
|
+
<ActivityIndicator color={finalTextColor} />
|
|
135
|
+
) : (
|
|
136
|
+
!loading && rightIcon
|
|
137
|
+
)}
|
|
138
|
+
</View>
|
|
139
|
+
</View>
|
|
140
|
+
</Pressable>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export default memo(Button);
|
|
145
|
+
|
|
146
|
+
const styles = StyleSheet.create({
|
|
147
|
+
base: {
|
|
148
|
+
justifyContent: 'center',
|
|
149
|
+
alignItems: 'center',
|
|
150
|
+
},
|
|
151
|
+
row: {
|
|
152
|
+
flexDirection: 'row',
|
|
153
|
+
alignItems: 'center',
|
|
154
|
+
},
|
|
155
|
+
side: {
|
|
156
|
+
minWidth: 24,
|
|
157
|
+
alignItems: 'center',
|
|
158
|
+
},
|
|
159
|
+
center: {
|
|
160
|
+
marginHorizontal: 6,
|
|
161
|
+
},
|
|
162
|
+
text: {
|
|
163
|
+
fontWeight: '600',
|
|
164
|
+
},
|
|
165
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const BUTTON_VARIANTS = ['solid', 'outline', 'ghost', 'soft', 'link'];
|
|
2
|
+
|
|
3
|
+
export const BUTTON_SIZES = {
|
|
4
|
+
xs: { pv: 6, ph: 10, fontSize: 10 },
|
|
5
|
+
sm: { pv: 8, ph: 12, fontSize: 12 },
|
|
6
|
+
md: { pv: 12, ph: 16, fontSize: 16 },
|
|
7
|
+
lg: { pv: 16, ph: 20, fontSize: 18 },
|
|
8
|
+
xl: { pv: 20, ph: 24, fontSize: 20 },
|
|
9
|
+
icon: { pv: 10, ph: 10, fontSize: 16 },
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const BUTTON_SHAPES = {
|
|
13
|
+
rounded: 10,
|
|
14
|
+
pill: 999,
|
|
15
|
+
square: 2,
|
|
16
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const getIntentColor = (intent, colors) => {
|
|
2
|
+
const map = {
|
|
3
|
+
primary: colors.primary,
|
|
4
|
+
success: colors.secondary,
|
|
5
|
+
error: colors.danger,
|
|
6
|
+
warning: '#FFA500',
|
|
7
|
+
info: '#3B82F6',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
return map[intent] || colors.primary;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const getVariantStyle = (variant, intentColor) => {
|
|
14
|
+
switch (variant) {
|
|
15
|
+
case 'outline':
|
|
16
|
+
return {
|
|
17
|
+
backgroundColor: 'transparent',
|
|
18
|
+
borderColor: intentColor,
|
|
19
|
+
borderWidth: 1,
|
|
20
|
+
textColor: intentColor,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
case 'ghost':
|
|
24
|
+
return {
|
|
25
|
+
backgroundColor: 'transparent',
|
|
26
|
+
textColor: intentColor,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
case 'soft':
|
|
30
|
+
return {
|
|
31
|
+
backgroundColor: intentColor + '20',
|
|
32
|
+
textColor: intentColor,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
case 'link':
|
|
36
|
+
return {
|
|
37
|
+
backgroundColor: 'transparent',
|
|
38
|
+
textColor: intentColor,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
case 'solid':
|
|
42
|
+
default:
|
|
43
|
+
return {
|
|
44
|
+
backgroundColor: intentColor,
|
|
45
|
+
textColor: '#fff',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import React, { useState, useEffect, memo } from 'react';
|
|
2
|
+
import { View, Text, TextInput, StyleSheet, Pressable } from 'react-native';
|
|
3
|
+
import { useTheme } from '../../theme';
|
|
4
|
+
import { INPUT_SIZES } from './input.config';
|
|
5
|
+
|
|
6
|
+
const Input = ({
|
|
7
|
+
value,
|
|
8
|
+
onChangeText,
|
|
9
|
+
|
|
10
|
+
label,
|
|
11
|
+
placeholder,
|
|
12
|
+
helperText,
|
|
13
|
+
error,
|
|
14
|
+
success,
|
|
15
|
+
|
|
16
|
+
variant = 'outline',
|
|
17
|
+
size = 'md',
|
|
18
|
+
|
|
19
|
+
disabled = false,
|
|
20
|
+
readOnly = false,
|
|
21
|
+
|
|
22
|
+
secureTextEntry = false,
|
|
23
|
+
multiline = false,
|
|
24
|
+
|
|
25
|
+
clearable = false,
|
|
26
|
+
|
|
27
|
+
bg,
|
|
28
|
+
textColor,
|
|
29
|
+
borderRadius,
|
|
30
|
+
paddingVertical,
|
|
31
|
+
paddingHorizontal,
|
|
32
|
+
|
|
33
|
+
style,
|
|
34
|
+
inputStyle,
|
|
35
|
+
}) => {
|
|
36
|
+
const { theme } = useTheme();
|
|
37
|
+
const colors = theme.colors;
|
|
38
|
+
|
|
39
|
+
const [focused, setFocused] = useState(false);
|
|
40
|
+
|
|
41
|
+
// password toggle state
|
|
42
|
+
const [secure, setSecure] = useState(secureTextEntry);
|
|
43
|
+
|
|
44
|
+
// ✅ sync with prop change (IMPORTANT FIX)
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
setSecure(secureTextEntry);
|
|
47
|
+
}, [secureTextEntry]);
|
|
48
|
+
|
|
49
|
+
const sizeConfig = INPUT_SIZES[size] || INPUT_SIZES.md;
|
|
50
|
+
|
|
51
|
+
/* ---------------- STATE PRIORITY ---------------- */
|
|
52
|
+
const state = disabled
|
|
53
|
+
? 'disabled'
|
|
54
|
+
: error
|
|
55
|
+
? 'error'
|
|
56
|
+
: success
|
|
57
|
+
? 'success'
|
|
58
|
+
: focused
|
|
59
|
+
? 'focus'
|
|
60
|
+
: 'default';
|
|
61
|
+
|
|
62
|
+
/* ---------------- COLORS ---------------- */
|
|
63
|
+
|
|
64
|
+
const finalBorderColor =
|
|
65
|
+
state === 'disabled'
|
|
66
|
+
? colors.border
|
|
67
|
+
: state === 'error'
|
|
68
|
+
? colors.error
|
|
69
|
+
: state === 'success'
|
|
70
|
+
? colors.success
|
|
71
|
+
: state === 'focus'
|
|
72
|
+
? colors.primary
|
|
73
|
+
: colors.border;
|
|
74
|
+
|
|
75
|
+
const backgroundColor =
|
|
76
|
+
bg ?? (variant === 'solid' ? colors.surface || colors.background : 'transparent');
|
|
77
|
+
|
|
78
|
+
const finalTextColor = textColor ?? (disabled ? colors.textSecondary || '#999' : colors.text);
|
|
79
|
+
|
|
80
|
+
const finalPV = paddingVertical ?? sizeConfig.pv;
|
|
81
|
+
const finalPH = paddingHorizontal ?? sizeConfig.ph;
|
|
82
|
+
|
|
83
|
+
const showClear = clearable && value?.length > 0 && !disabled && !readOnly;
|
|
84
|
+
|
|
85
|
+
/* ---------------- VARIANT STYLE ---------------- */
|
|
86
|
+
|
|
87
|
+
const getVariantStyle = () => {
|
|
88
|
+
switch (variant) {
|
|
89
|
+
case 'solid':
|
|
90
|
+
return { borderWidth: 1 };
|
|
91
|
+
case 'outline':
|
|
92
|
+
return { borderWidth: 1 };
|
|
93
|
+
case 'underline':
|
|
94
|
+
return { borderBottomWidth: 1 };
|
|
95
|
+
case 'ghost':
|
|
96
|
+
return { borderWidth: 0 };
|
|
97
|
+
default:
|
|
98
|
+
return { borderWidth: 1 };
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/* ---------------- RENDER ---------------- */
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<View style={{ width: '100%' }}>
|
|
106
|
+
{/* LABEL */}
|
|
107
|
+
{label && <Text style={[styles.label, { color: colors.text }]}>{label}</Text>}
|
|
108
|
+
|
|
109
|
+
{/* INPUT */}
|
|
110
|
+
<View
|
|
111
|
+
style={[
|
|
112
|
+
styles.wrapper,
|
|
113
|
+
getVariantStyle(),
|
|
114
|
+
{
|
|
115
|
+
backgroundColor,
|
|
116
|
+
borderColor: finalBorderColor,
|
|
117
|
+
borderRadius: borderRadius ?? 8,
|
|
118
|
+
paddingVertical: finalPV,
|
|
119
|
+
paddingHorizontal: finalPH,
|
|
120
|
+
minHeight: multiline ? 80 : 44,
|
|
121
|
+
opacity: disabled ? 0.5 : 1,
|
|
122
|
+
},
|
|
123
|
+
style,
|
|
124
|
+
]}
|
|
125
|
+
>
|
|
126
|
+
<TextInput
|
|
127
|
+
value={value}
|
|
128
|
+
onChangeText={onChangeText}
|
|
129
|
+
placeholder={placeholder}
|
|
130
|
+
editable={!disabled && !readOnly}
|
|
131
|
+
secureTextEntry={secure} // ✅ controlled by state
|
|
132
|
+
multiline={multiline}
|
|
133
|
+
placeholderTextColor={colors.border}
|
|
134
|
+
onFocus={() => setFocused(true)}
|
|
135
|
+
onBlur={() => setFocused(false)}
|
|
136
|
+
style={[
|
|
137
|
+
styles.input,
|
|
138
|
+
{
|
|
139
|
+
color: finalTextColor,
|
|
140
|
+
fontSize: sizeConfig.fontSize,
|
|
141
|
+
textAlignVertical: multiline ? 'top' : 'center',
|
|
142
|
+
},
|
|
143
|
+
inputStyle,
|
|
144
|
+
]}
|
|
145
|
+
/>
|
|
146
|
+
|
|
147
|
+
{/* RIGHT SIDE ACTIONS */}
|
|
148
|
+
<View style={styles.right}>
|
|
149
|
+
{/* Clear button */}
|
|
150
|
+
{showClear && (
|
|
151
|
+
<Pressable onPress={() => onChangeText('')}>
|
|
152
|
+
<Text style={{ color: colors.text }}>✕</Text>
|
|
153
|
+
</Pressable>
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
{/* Password toggle */}
|
|
157
|
+
{secureTextEntry && (
|
|
158
|
+
<Pressable onPress={() => setSecure((prev) => !prev)}>
|
|
159
|
+
<Text style={{ color: colors.text, fontSize: 16 }}>{secure ? '👁️' : '🙈'}</Text>
|
|
160
|
+
</Pressable>
|
|
161
|
+
)}
|
|
162
|
+
</View>
|
|
163
|
+
</View>
|
|
164
|
+
|
|
165
|
+
{/* HELPER / ERROR */}
|
|
166
|
+
{(helperText || error) && (
|
|
167
|
+
<Text
|
|
168
|
+
style={{
|
|
169
|
+
marginTop: 4,
|
|
170
|
+
fontSize: 12,
|
|
171
|
+
color: error
|
|
172
|
+
? colors.error
|
|
173
|
+
: success
|
|
174
|
+
? colors.success
|
|
175
|
+
: colors.textSecondary || colors.text,
|
|
176
|
+
}}
|
|
177
|
+
>
|
|
178
|
+
{error || helperText}
|
|
179
|
+
</Text>
|
|
180
|
+
)}
|
|
181
|
+
</View>
|
|
182
|
+
);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
export default memo(Input);
|
|
186
|
+
|
|
187
|
+
/* ---------------- STYLES ---------------- */
|
|
188
|
+
|
|
189
|
+
const styles = StyleSheet.create({
|
|
190
|
+
label: {
|
|
191
|
+
marginBottom: 4,
|
|
192
|
+
fontWeight: '500',
|
|
193
|
+
},
|
|
194
|
+
wrapper: {
|
|
195
|
+
flexDirection: 'row',
|
|
196
|
+
alignItems: 'center',
|
|
197
|
+
},
|
|
198
|
+
input: {
|
|
199
|
+
flex: 1,
|
|
200
|
+
},
|
|
201
|
+
right: {
|
|
202
|
+
flexDirection: 'row',
|
|
203
|
+
alignItems: 'center',
|
|
204
|
+
gap: 6,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const INPUT_SIZES = {
|
|
2
|
+
xs: { fontSize: 12, pv: 6, ph: 8 },
|
|
3
|
+
sm: { fontSize: 14, pv: 8, ph: 10 },
|
|
4
|
+
md: { fontSize: 16, pv: 10, ph: 12 },
|
|
5
|
+
lg: { fontSize: 18, pv: 12, ph: 14 },
|
|
6
|
+
xl: { fontSize: 20, pv: 14, ph: 16 },
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const INPUT_VARIANTS = {
|
|
10
|
+
solid: (c) => ({
|
|
11
|
+
backgroundColor: c.background,
|
|
12
|
+
borderWidth: 0,
|
|
13
|
+
}),
|
|
14
|
+
outline: (c) => ({
|
|
15
|
+
backgroundColor: 'transparent',
|
|
16
|
+
borderWidth: 1,
|
|
17
|
+
borderColor: c.border,
|
|
18
|
+
}),
|
|
19
|
+
underline: (c) => ({
|
|
20
|
+
backgroundColor: 'transparent',
|
|
21
|
+
borderBottomWidth: 1,
|
|
22
|
+
borderColor: c.border,
|
|
23
|
+
}),
|
|
24
|
+
ghost: () => ({
|
|
25
|
+
backgroundColor: 'transparent',
|
|
26
|
+
borderWidth: 0,
|
|
27
|
+
}),
|
|
28
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React, { createContext, useContext, useMemo, useState } from 'react';
|
|
2
|
+
import { lightTheme } from './light';
|
|
3
|
+
import { darkTheme } from './dark';
|
|
4
|
+
|
|
5
|
+
const ThemeContext = createContext();
|
|
6
|
+
|
|
7
|
+
export const ThemeProvider = ({ children }) => {
|
|
8
|
+
const [mode, setMode] = useState('light');
|
|
9
|
+
|
|
10
|
+
const theme = useMemo(() => {
|
|
11
|
+
return mode === 'light' ? lightTheme : darkTheme;
|
|
12
|
+
}, [mode]);
|
|
13
|
+
|
|
14
|
+
const toggleTheme = () => {
|
|
15
|
+
setMode((prev) => (prev === 'light' ? 'dark' : 'light'));
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<ThemeContext.Provider value={{ theme, mode, toggleTheme }}>{children}</ThemeContext.Provider>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const useTheme = () => {
|
|
24
|
+
const context = useContext(ThemeContext);
|
|
25
|
+
if (!context) {
|
|
26
|
+
throw new Error('useTheme must be used inside ThemeProvider');
|
|
27
|
+
}
|
|
28
|
+
return context;
|
|
29
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const darkTheme = {
|
|
2
|
+
mode: 'dark',
|
|
3
|
+
colors: {
|
|
4
|
+
primary: '#3b82f6',
|
|
5
|
+
|
|
6
|
+
background: '#111827',
|
|
7
|
+
surface: '#1f2937',
|
|
8
|
+
|
|
9
|
+
text: '#f9fafb',
|
|
10
|
+
textSecondary: '#9ca3af',
|
|
11
|
+
|
|
12
|
+
border: '#374151',
|
|
13
|
+
|
|
14
|
+
error: '#ef4444',
|
|
15
|
+
success: '#22c55e',
|
|
16
|
+
|
|
17
|
+
disabled: '#374151',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ThemeProvider';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const lightTheme = {
|
|
2
|
+
mode: 'light',
|
|
3
|
+
colors: {
|
|
4
|
+
primary: '#2563eb',
|
|
5
|
+
|
|
6
|
+
background: '#ffffff',
|
|
7
|
+
surface: '#f9fafb',
|
|
8
|
+
|
|
9
|
+
text: '#111827',
|
|
10
|
+
textSecondary: '#6b7280',
|
|
11
|
+
|
|
12
|
+
border: '#d1d5db',
|
|
13
|
+
|
|
14
|
+
error: '#dc2626',
|
|
15
|
+
success: '#16a34a',
|
|
16
|
+
|
|
17
|
+
disabled: '#e5e7eb',
|
|
18
|
+
},
|
|
19
|
+
};
|