@vellira-ui/react-native 2.24.0 → 2.25.1
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/README.md +29 -0
- package/dist/index.js +123 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,6 +82,35 @@ Use `description` for settings-style helper text when the checkbox is not
|
|
|
82
82
|
wrapped in `FormField`. For checkbox rows without a visible label, provide
|
|
83
83
|
`accessibilityLabel`.
|
|
84
84
|
|
|
85
|
+
### Select Notes
|
|
86
|
+
|
|
87
|
+
Use `Select` for one form value from a compact list, `RadioGroup` for a few
|
|
88
|
+
visible choices, and `Dropdown` for action menus. Native `Select` commits the
|
|
89
|
+
picker draft only when the user presses `Done`; `Cancel` and the backdrop close
|
|
90
|
+
without changing the selected value.
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import { Select } from '@vellira-ui/react-native';
|
|
94
|
+
import { useState } from 'react';
|
|
95
|
+
|
|
96
|
+
export function RoleSelect() {
|
|
97
|
+
const [role, setRole] = useState('editor');
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<Select
|
|
101
|
+
label='Role'
|
|
102
|
+
value={role}
|
|
103
|
+
onChange={setRole}
|
|
104
|
+
options={[
|
|
105
|
+
{ label: 'Admin', value: 'admin' },
|
|
106
|
+
{ label: 'Editor', value: 'editor' },
|
|
107
|
+
{ label: 'Viewer', value: 'viewer' },
|
|
108
|
+
]}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
85
114
|
### FormField Notes
|
|
86
115
|
|
|
87
116
|
`FormField` is a presentational wrapper for custom native controls. Do not wrap
|
package/dist/index.js
CHANGED
|
@@ -1013,7 +1013,7 @@ var RadioGroup = forwardRef(
|
|
|
1013
1013
|
RadioGroup.displayName = "RadioGroup";
|
|
1014
1014
|
|
|
1015
1015
|
// src/components/Select/Select.tsx
|
|
1016
|
-
import { useState as useState3 } from "react";
|
|
1016
|
+
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
1017
1017
|
import { Picker } from "@react-native-picker/picker";
|
|
1018
1018
|
import { useControllableState as useControllableState3 } from "@vellira-ui/core";
|
|
1019
1019
|
import { Modal as Modal3, Pressable as Pressable7, Text as Text8, View as View13 } from "react-native";
|
|
@@ -1038,6 +1038,33 @@ var createStyles14 = (theme) => StyleSheet14.create({
|
|
|
1038
1038
|
borderRadius: theme.tokens.radius.md,
|
|
1039
1039
|
borderWidth: 1
|
|
1040
1040
|
},
|
|
1041
|
+
sm: {
|
|
1042
|
+
minHeight: 36,
|
|
1043
|
+
paddingHorizontal: theme.tokens.spacing[3],
|
|
1044
|
+
paddingVertical: theme.tokens.spacing[2]
|
|
1045
|
+
},
|
|
1046
|
+
md: {
|
|
1047
|
+
minHeight: 44,
|
|
1048
|
+
paddingHorizontal: theme.tokens.spacing[4],
|
|
1049
|
+
paddingVertical: theme.tokens.spacing[3]
|
|
1050
|
+
},
|
|
1051
|
+
lg: {
|
|
1052
|
+
minHeight: 52,
|
|
1053
|
+
paddingHorizontal: theme.tokens.spacing[5],
|
|
1054
|
+
paddingVertical: theme.tokens.spacing[4]
|
|
1055
|
+
},
|
|
1056
|
+
textSm: {
|
|
1057
|
+
fontSize: theme.tokens.typography.size.sm,
|
|
1058
|
+
lineHeight: theme.tokens.typography.lineHeight.sm
|
|
1059
|
+
},
|
|
1060
|
+
textMd: {
|
|
1061
|
+
fontSize: theme.tokens.typography.size.md,
|
|
1062
|
+
lineHeight: theme.tokens.typography.lineHeight.md
|
|
1063
|
+
},
|
|
1064
|
+
textLg: {
|
|
1065
|
+
fontSize: theme.tokens.typography.size.lg,
|
|
1066
|
+
lineHeight: theme.tokens.typography.lineHeight.lg
|
|
1067
|
+
},
|
|
1041
1068
|
triggerOpen: {
|
|
1042
1069
|
borderColor: theme.components.select.trigger.focus.border
|
|
1043
1070
|
},
|
|
@@ -1052,9 +1079,7 @@ var createStyles14 = (theme) => StyleSheet14.create({
|
|
|
1052
1079
|
flex: 1,
|
|
1053
1080
|
minWidth: 0,
|
|
1054
1081
|
color: theme.components.select.trigger.default.fg,
|
|
1055
|
-
fontFamily: theme.tokens.typography.family.regular
|
|
1056
|
-
fontSize: theme.tokens.typography.size.md,
|
|
1057
|
-
lineHeight: theme.tokens.typography.lineHeight.md
|
|
1082
|
+
fontFamily: theme.tokens.typography.family.regular
|
|
1058
1083
|
},
|
|
1059
1084
|
textDisabled: {
|
|
1060
1085
|
color: theme.components.select.trigger.disabled.fg
|
|
@@ -1080,22 +1105,31 @@ function SelectTrigger({
|
|
|
1080
1105
|
displayText,
|
|
1081
1106
|
isPlaceholder,
|
|
1082
1107
|
isOpen,
|
|
1108
|
+
size = "md",
|
|
1083
1109
|
disabled = false,
|
|
1110
|
+
required = false,
|
|
1084
1111
|
hasError = false,
|
|
1085
1112
|
accessibilityLabel,
|
|
1113
|
+
accessibilityHint,
|
|
1086
1114
|
triggerStyle,
|
|
1087
1115
|
textStyle,
|
|
1088
1116
|
onPress
|
|
1089
1117
|
}) {
|
|
1090
1118
|
const { theme } = useTheme();
|
|
1091
1119
|
const styles = useThemeStyles(createStyles14);
|
|
1120
|
+
const textSizeStyle = {
|
|
1121
|
+
sm: styles.textSm,
|
|
1122
|
+
md: styles.textMd,
|
|
1123
|
+
lg: styles.textLg
|
|
1124
|
+
}[size];
|
|
1125
|
+
const resolvedAccessibilityHint = accessibilityHint ?? (hasError ? "Invalid selection. Opens a picker" : required ? "Required. Opens a picker" : "Opens a picker");
|
|
1092
1126
|
return /* @__PURE__ */ jsxs8(
|
|
1093
1127
|
Pressable6,
|
|
1094
1128
|
{
|
|
1095
1129
|
disabled,
|
|
1096
1130
|
accessibilityRole: "button",
|
|
1097
1131
|
accessibilityLabel,
|
|
1098
|
-
accessibilityHint:
|
|
1132
|
+
accessibilityHint: resolvedAccessibilityHint,
|
|
1099
1133
|
accessibilityState: {
|
|
1100
1134
|
expanded: isOpen,
|
|
1101
1135
|
disabled
|
|
@@ -1103,6 +1137,7 @@ function SelectTrigger({
|
|
|
1103
1137
|
onPress,
|
|
1104
1138
|
style: [
|
|
1105
1139
|
styles.trigger,
|
|
1140
|
+
styles[size],
|
|
1106
1141
|
isOpen && styles.triggerOpen,
|
|
1107
1142
|
hasError && styles.triggerError,
|
|
1108
1143
|
disabled && styles.triggerDisabled,
|
|
@@ -1115,6 +1150,7 @@ function SelectTrigger({
|
|
|
1115
1150
|
numberOfLines: 1,
|
|
1116
1151
|
style: [
|
|
1117
1152
|
styles.text,
|
|
1153
|
+
textSizeStyle,
|
|
1118
1154
|
isPlaceholder && styles.placeholder,
|
|
1119
1155
|
disabled && styles.textDisabled,
|
|
1120
1156
|
textStyle
|
|
@@ -1163,6 +1199,7 @@ var createStyles15 = (theme) => StyleSheet15.create({
|
|
|
1163
1199
|
borderTopLeftRadius: theme.tokens.radius.lg,
|
|
1164
1200
|
borderTopRightRadius: theme.tokens.radius.lg,
|
|
1165
1201
|
borderWidth: 1,
|
|
1202
|
+
borderBottomWidth: 0,
|
|
1166
1203
|
overflow: "hidden"
|
|
1167
1204
|
},
|
|
1168
1205
|
toolbar: {
|
|
@@ -1175,19 +1212,31 @@ var createStyles15 = (theme) => StyleSheet15.create({
|
|
|
1175
1212
|
borderBottomWidth: 1
|
|
1176
1213
|
},
|
|
1177
1214
|
title: {
|
|
1215
|
+
flex: 1,
|
|
1216
|
+
marginHorizontal: theme.tokens.spacing[3],
|
|
1178
1217
|
color: theme.components.select.dropdown.fg,
|
|
1179
1218
|
fontFamily: theme.tokens.typography.family.medium,
|
|
1180
|
-
fontSize: theme.tokens.typography.size.md
|
|
1219
|
+
fontSize: theme.tokens.typography.size.md,
|
|
1220
|
+
lineHeight: theme.tokens.typography.lineHeight.md,
|
|
1221
|
+
textAlign: "center"
|
|
1222
|
+
},
|
|
1223
|
+
toolbarAction: {
|
|
1224
|
+
minWidth: 64,
|
|
1225
|
+
minHeight: 44,
|
|
1226
|
+
alignItems: "center",
|
|
1227
|
+
justifyContent: "center"
|
|
1181
1228
|
},
|
|
1182
1229
|
cancelText: {
|
|
1183
1230
|
color: theme.components.select.trigger.placeholder.fg,
|
|
1184
1231
|
fontFamily: theme.tokens.typography.family.medium,
|
|
1185
|
-
fontSize: theme.tokens.typography.size.md
|
|
1232
|
+
fontSize: theme.tokens.typography.size.md,
|
|
1233
|
+
lineHeight: theme.tokens.typography.lineHeight.md
|
|
1186
1234
|
},
|
|
1187
1235
|
doneText: {
|
|
1188
1236
|
color: theme.components.select.option.selected.bg,
|
|
1189
1237
|
fontFamily: theme.tokens.typography.family.medium,
|
|
1190
|
-
fontSize: theme.tokens.typography.size.md
|
|
1238
|
+
fontSize: theme.tokens.typography.size.md,
|
|
1239
|
+
lineHeight: theme.tokens.typography.lineHeight.md
|
|
1191
1240
|
},
|
|
1192
1241
|
picker: {
|
|
1193
1242
|
width: "100%"
|
|
@@ -1204,6 +1253,7 @@ function Select({
|
|
|
1204
1253
|
onChange,
|
|
1205
1254
|
options,
|
|
1206
1255
|
placeholder = "Select...",
|
|
1256
|
+
size = "md",
|
|
1207
1257
|
required = false,
|
|
1208
1258
|
disabled = false,
|
|
1209
1259
|
error,
|
|
@@ -1211,7 +1261,8 @@ function Select({
|
|
|
1211
1261
|
triggerStyle,
|
|
1212
1262
|
textStyle,
|
|
1213
1263
|
pickerStyle,
|
|
1214
|
-
accessibilityLabel
|
|
1264
|
+
accessibilityLabel,
|
|
1265
|
+
accessibilityHint
|
|
1215
1266
|
}) {
|
|
1216
1267
|
const { theme } = useTheme();
|
|
1217
1268
|
const styles = useThemeStyles(createStyles15);
|
|
@@ -1223,6 +1274,12 @@ function Select({
|
|
|
1223
1274
|
});
|
|
1224
1275
|
const [draftValue, setDraftValue] = useState3(selectedValue);
|
|
1225
1276
|
const selectedOption = options.find((o) => o.value === selectedValue);
|
|
1277
|
+
const hasError = !!error;
|
|
1278
|
+
useEffect2(() => {
|
|
1279
|
+
if (isOpen) {
|
|
1280
|
+
setDraftValue(selectedValue);
|
|
1281
|
+
}
|
|
1282
|
+
}, [isOpen, selectedValue]);
|
|
1226
1283
|
const resolvedLabel = accessibilityLabel ?? label ?? selectedOption?.label ?? placeholder ?? "Select";
|
|
1227
1284
|
const openPicker = () => {
|
|
1228
1285
|
if (disabled) return;
|
|
@@ -1240,6 +1297,11 @@ function Select({
|
|
|
1240
1297
|
setDraftValue(nextValue);
|
|
1241
1298
|
};
|
|
1242
1299
|
const confirmPicker = () => {
|
|
1300
|
+
const nextOption = options.find((option) => option.value === draftValue);
|
|
1301
|
+
if (!nextOption || nextOption.disabled) {
|
|
1302
|
+
setIsOpen(false);
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1243
1305
|
setSelectedValue(draftValue);
|
|
1244
1306
|
setIsOpen(false);
|
|
1245
1307
|
};
|
|
@@ -1259,9 +1321,12 @@ function Select({
|
|
|
1259
1321
|
displayText: selectedOption?.label ?? placeholder,
|
|
1260
1322
|
isPlaceholder: !selectedOption,
|
|
1261
1323
|
isOpen,
|
|
1324
|
+
size,
|
|
1262
1325
|
disabled,
|
|
1263
|
-
|
|
1326
|
+
required,
|
|
1327
|
+
hasError,
|
|
1264
1328
|
accessibilityLabel: resolvedLabel,
|
|
1329
|
+
accessibilityHint,
|
|
1265
1330
|
triggerStyle,
|
|
1266
1331
|
textStyle,
|
|
1267
1332
|
onPress: openPicker
|
|
@@ -1277,11 +1342,50 @@ function Select({
|
|
|
1277
1342
|
children: /* @__PURE__ */ jsxs9(View13, { style: styles.modalRoot, children: [
|
|
1278
1343
|
/* @__PURE__ */ jsx17(Pressable7, { style: styles.backdrop, onPress: closePicker }),
|
|
1279
1344
|
/* @__PURE__ */ jsxs9(View13, { style: styles.sheet, children: [
|
|
1280
|
-
/* @__PURE__ */ jsxs9(
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1345
|
+
/* @__PURE__ */ jsxs9(
|
|
1346
|
+
View13,
|
|
1347
|
+
{
|
|
1348
|
+
style: styles.toolbar,
|
|
1349
|
+
accessible: true,
|
|
1350
|
+
accessibilityRole: "toolbar",
|
|
1351
|
+
accessibilityLabel: `${resolvedLabel} picker actions`,
|
|
1352
|
+
children: [
|
|
1353
|
+
/* @__PURE__ */ jsx17(
|
|
1354
|
+
Pressable7,
|
|
1355
|
+
{
|
|
1356
|
+
onPress: closePicker,
|
|
1357
|
+
hitSlop: 8,
|
|
1358
|
+
style: styles.toolbarAction,
|
|
1359
|
+
accessibilityRole: "button",
|
|
1360
|
+
accessibilityLabel: "Cancel selection",
|
|
1361
|
+
accessibilityHint: "Closes the picker without changing the selected value",
|
|
1362
|
+
children: /* @__PURE__ */ jsx17(Text8, { style: styles.cancelText, children: "Cancel" })
|
|
1363
|
+
}
|
|
1364
|
+
),
|
|
1365
|
+
/* @__PURE__ */ jsx17(
|
|
1366
|
+
Text8,
|
|
1367
|
+
{
|
|
1368
|
+
style: styles.title,
|
|
1369
|
+
numberOfLines: 1,
|
|
1370
|
+
accessibilityRole: "header",
|
|
1371
|
+
children: resolvedLabel
|
|
1372
|
+
}
|
|
1373
|
+
),
|
|
1374
|
+
/* @__PURE__ */ jsx17(
|
|
1375
|
+
Pressable7,
|
|
1376
|
+
{
|
|
1377
|
+
onPress: confirmPicker,
|
|
1378
|
+
hitSlop: 8,
|
|
1379
|
+
style: styles.toolbarAction,
|
|
1380
|
+
accessibilityRole: "button",
|
|
1381
|
+
accessibilityLabel: "Confirm selection",
|
|
1382
|
+
accessibilityHint: "Applies the highlighted picker value",
|
|
1383
|
+
children: /* @__PURE__ */ jsx17(Text8, { style: styles.doneText, children: "Done" })
|
|
1384
|
+
}
|
|
1385
|
+
)
|
|
1386
|
+
]
|
|
1387
|
+
}
|
|
1388
|
+
),
|
|
1285
1389
|
/* @__PURE__ */ jsxs9(
|
|
1286
1390
|
Picker,
|
|
1287
1391
|
{
|
|
@@ -1620,7 +1724,7 @@ var Tabs = Object.assign(TabsRoot, {
|
|
|
1620
1724
|
});
|
|
1621
1725
|
|
|
1622
1726
|
// src/components/Tooltip/Tooltip.tsx
|
|
1623
|
-
import { useEffect as
|
|
1727
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState6 } from "react";
|
|
1624
1728
|
import { Modal as Modal4, Pressable as Pressable9, Text as Text10, View as View18 } from "react-native";
|
|
1625
1729
|
|
|
1626
1730
|
// src/hooks/useNativeFloatingPosition.ts
|
|
@@ -1760,7 +1864,7 @@ function Tooltip({
|
|
|
1760
1864
|
closeTimerRef.current = null;
|
|
1761
1865
|
}, hideDelay);
|
|
1762
1866
|
};
|
|
1763
|
-
|
|
1867
|
+
useEffect3(() => {
|
|
1764
1868
|
return clearCloseTimer;
|
|
1765
1869
|
}, []);
|
|
1766
1870
|
return /* @__PURE__ */ jsxs11(View18, { style: [styles.root, style], children: [
|
|
@@ -2556,7 +2660,7 @@ var Input = forwardRef3(
|
|
|
2556
2660
|
Input.displayName = "Input";
|
|
2557
2661
|
|
|
2558
2662
|
// src/primitives/Radio/Radio.tsx
|
|
2559
|
-
import { forwardRef as forwardRef4, useEffect as
|
|
2663
|
+
import { forwardRef as forwardRef4, useEffect as useEffect4 } from "react";
|
|
2560
2664
|
import { useControllableState as useControllableState5 } from "@vellira-ui/core";
|
|
2561
2665
|
import {
|
|
2562
2666
|
Pressable as Pressable13,
|
|
@@ -2707,7 +2811,7 @@ var Radio = forwardRef4(
|
|
|
2707
2811
|
};
|
|
2708
2812
|
const typographySize = typographySizeByRadioSize[resolvedSize];
|
|
2709
2813
|
const controlMarginTop = (typographySize.labelLineHeight - controlSize) / 2;
|
|
2710
|
-
|
|
2814
|
+
useEffect4(() => {
|
|
2711
2815
|
if (typeof __DEV__ !== "undefined" && __DEV__ && !label && !accessibilityLabel) {
|
|
2712
2816
|
console.warn(
|
|
2713
2817
|
"Radio requires either a visible label or accessibilityLabel."
|