react-native-better-html 1.0.11 → 1.0.12

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/index.mjs CHANGED
@@ -145,7 +145,7 @@ var pressStrength = () => {
145
145
  };
146
146
 
147
147
  // src/utils/hooks.ts
148
- import { useEffect as useEffect2, useMemo as useMemo2, useState } from "react";
148
+ import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useRef, useState } from "react";
149
149
  import { Dimensions, Keyboard } from "react-native";
150
150
  import { useSafeAreaInsets } from "react-native-safe-area-context";
151
151
  import { useBooleanState as useBooleanState2, useTheme } from "react-better-core";
@@ -420,6 +420,128 @@ function useComponentPropsGrouper(props, prefix) {
420
420
  };
421
421
  }, [props, prefix]);
422
422
  }
423
+ function useForm(options) {
424
+ const { defaultValues, requiredFields, additional, onSubmit, validate } = options;
425
+ const inputFieldRefs = useRef(
426
+ {}
427
+ );
428
+ const [values, setValues] = useState(defaultValues);
429
+ const [errors, setErrors] = useState({});
430
+ const [isSubmitting, setIsSubmitting] = useBooleanState2();
431
+ const numberOfInputFields = Object.keys(defaultValues).length;
432
+ const setFieldValue = useCallback(
433
+ (field, value) => {
434
+ setValues((oldValue) => ({
435
+ ...oldValue,
436
+ [field]: value
437
+ }));
438
+ setErrors((oldValue) => ({
439
+ ...oldValue,
440
+ [field]: void 0
441
+ }));
442
+ },
443
+ []
444
+ );
445
+ const setFieldsValue = useCallback((values2) => {
446
+ setValues((oldValue) => ({
447
+ ...oldValue,
448
+ ...values2
449
+ }));
450
+ setErrors((oldValue) => {
451
+ const newErrors = {};
452
+ for (const key in values2) newErrors[key] = void 0;
453
+ return newErrors;
454
+ });
455
+ }, []);
456
+ const focusField = useCallback((field) => {
457
+ inputFieldRefs.current[field]?.focus();
458
+ }, []);
459
+ const validateForm = useCallback(() => {
460
+ const validationErrors = validate?.(values) || {};
461
+ setErrors(validationErrors);
462
+ return validationErrors;
463
+ }, [validate, values]);
464
+ const onSubmitFunction = useCallback(
465
+ async (event) => {
466
+ event?.preventDefault();
467
+ setIsSubmitting.setTrue();
468
+ try {
469
+ const validationErrors = validateForm();
470
+ if (Object.keys(validationErrors).length === 0) {
471
+ await onSubmit?.(values);
472
+ } else {
473
+ const firstErrorField = Object.keys(validationErrors)[0];
474
+ focusField(firstErrorField);
475
+ }
476
+ } finally {
477
+ setIsSubmitting.setFalse();
478
+ }
479
+ },
480
+ [values, validateForm, onSubmit, focusField]
481
+ );
482
+ const getInputFieldProps = useCallback(
483
+ (field) => {
484
+ const thisInputFieldIndex = Object.keys(values).findIndex((key) => key === field);
485
+ const isLastInputField = thisInputFieldIndex === numberOfInputFields - 1;
486
+ return {
487
+ required: requiredFields?.includes(field),
488
+ value: values[field]?.toString() ?? "",
489
+ errorMessage: errors[field],
490
+ returnKeyLabel: isLastInputField ? additional?.lastInputFieldReturnKeyLabel ?? "done" : "next",
491
+ onPressEnter: () => {
492
+ if (isLastInputField) onSubmitFunction();
493
+ else inputFieldRefs.current[Object.keys(values)[thisInputFieldIndex + 1]]?.focus();
494
+ },
495
+ onChange: (value) => {
496
+ setFieldValue(field, value);
497
+ },
498
+ ref: (element) => {
499
+ if (!element) return;
500
+ inputFieldRefs.current[field] = element;
501
+ }
502
+ };
503
+ },
504
+ [values, setFieldValue, errors, requiredFields, additional, onSubmitFunction]
505
+ );
506
+ const reset = useCallback(() => {
507
+ setValues(defaultValues);
508
+ setErrors({});
509
+ }, [defaultValues]);
510
+ const isDirty = useMemo2(
511
+ () => Object.keys(defaultValues).some((key) => defaultValues[key] !== values[key]),
512
+ [defaultValues, values]
513
+ );
514
+ const isValid = useMemo2(() => {
515
+ const validationErrors = validate?.(values) || {};
516
+ return Object.keys(validationErrors).length === 0;
517
+ }, [validate, values]);
518
+ const canSubmit = useMemo2(() => {
519
+ const requiredFieldsHaveValues = requiredFields?.every((field) => values[field] !== void 0 && values[field] !== "") ?? true;
520
+ return isValid && requiredFieldsHaveValues;
521
+ }, [isValid, requiredFields]);
522
+ return {
523
+ values,
524
+ errors,
525
+ isSubmitting,
526
+ setFieldValue,
527
+ setFieldsValue,
528
+ getInputFieldProps,
529
+ focusField,
530
+ inputFieldRefs: inputFieldRefs.current,
531
+ validate: validateForm,
532
+ onSubmit: onSubmitFunction,
533
+ reset,
534
+ requiredFields,
535
+ isDirty,
536
+ isValid,
537
+ canSubmit
538
+ };
539
+ }
540
+
541
+ // src/utils/functions.ts
542
+ var getFormErrorObject = (formValues) => {
543
+ return {};
544
+ };
423
545
 
424
546
  // src/utils/asyncStorage.ts
425
547
  import NativeAsyncStorage from "@react-native-async-storage/async-storage";
@@ -975,7 +1097,7 @@ Button2.text = ButtonComponent.text;
975
1097
  var Button_default = Button2;
976
1098
 
977
1099
  // src/components/ScreenHolder.tsx
978
- import { memo as memo7, useCallback, useMemo as useMemo6 } from "react";
1100
+ import { memo as memo7, useCallback as useCallback2, useMemo as useMemo6 } from "react";
979
1101
  import { KeyboardAvoidingView, Platform as Platform3, RefreshControl, ScrollView } from "react-native";
980
1102
  import { useBooleanState as useBooleanState3, useTheme as useTheme6 } from "react-better-core";
981
1103
  import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -1005,7 +1127,7 @@ var ScreenHolderComponent = ({
1005
1127
  }),
1006
1128
  []
1007
1129
  );
1008
- const onRefreshElement = useCallback(() => {
1130
+ const onRefreshElement = useCallback2(() => {
1009
1131
  setIsRefreshing.setTrue();
1010
1132
  onRefresh?.();
1011
1133
  setTimeout(() => {
@@ -1141,15 +1263,15 @@ var Image_default = Image2;
1141
1263
  import {
1142
1264
  forwardRef,
1143
1265
  memo as memo9,
1144
- useCallback as useCallback2,
1266
+ useCallback as useCallback3,
1145
1267
  useEffect as useEffect4,
1146
1268
  useImperativeHandle,
1147
1269
  useMemo as useMemo8,
1148
- useRef,
1270
+ useRef as useRef2,
1149
1271
  useState as useState2
1150
1272
  } from "react";
1151
1273
  import {
1152
- TextInput
1274
+ TextInput as TextInput2
1153
1275
  } from "react-native";
1154
1276
  import {
1155
1277
  darkenColor,
@@ -1185,6 +1307,8 @@ var InputFieldComponent = forwardRef(
1185
1307
  fontWeight = 400,
1186
1308
  lineHeight = 20,
1187
1309
  textAlign,
1310
+ required,
1311
+ disabled,
1188
1312
  paddingHorizontal,
1189
1313
  paddingVertical,
1190
1314
  onFocus,
@@ -1195,22 +1319,22 @@ var InputFieldComponent = forwardRef(
1195
1319
  }, ref) => {
1196
1320
  const theme2 = useTheme8();
1197
1321
  const { colorTheme } = useBetterCoreContext3();
1198
- const textInputRef = useRef(null);
1322
+ const textInputRef = useRef2(null);
1199
1323
  const [internalValue, setInternalValue] = useState2(value?.toString() || defaultValue || "");
1200
1324
  const [isFocused, setIsFocused] = useBooleanState4();
1201
1325
  const borderWidth = 1;
1202
1326
  const readyPaddingHorizontal = paddingHorizontal ?? theme2.styles.space;
1203
1327
  const readyPaddingVertical = paddingVertical ? parseFloat(paddingVertical.toString()) : (theme2.styles.space + theme2.styles.gap) / 2;
1204
1328
  const readyHeight = height ?? borderWidth + readyPaddingVertical + lineHeight + readyPaddingVertical + borderWidth;
1205
- const onFocusElement = useCallback2((event) => {
1329
+ const onFocusElement = useCallback3((event) => {
1206
1330
  setIsFocused.setTrue();
1207
1331
  onFocus?.(event);
1208
1332
  }, []);
1209
- const onBlurElement = useCallback2((event) => {
1333
+ const onBlurElement = useCallback3((event) => {
1210
1334
  setIsFocused.setFalse();
1211
1335
  onBlur?.(event);
1212
1336
  }, []);
1213
- const onChangeText = useCallback2(
1337
+ const onChangeText = useCallback3(
1214
1338
  (text) => {
1215
1339
  setInternalValue(text);
1216
1340
  onChange?.(text);
@@ -1241,111 +1365,124 @@ var InputFieldComponent = forwardRef(
1241
1365
  []
1242
1366
  );
1243
1367
  const prefixSuffixBackgroundColor = colorTheme === "light" ? darkenColor(theme2.colors.backgroundContent, 0.03) : lightenColor(theme2.colors.backgroundContent, 0.1);
1244
- return /* @__PURE__ */ jsxs4(View_default, { flex: 1, gap: theme2.styles.gap / 3, children: [
1245
- label && /* @__PURE__ */ jsx9(Text_default, { fontSize: 14, color: theme2.colors.textSecondary, children: label }),
1246
- /* @__PURE__ */ jsxs4(View_default, { isRow: true, position: "relative", alignItems: "center", flex: 1, height: readyHeight, children: [
1247
- prefix && /* @__PURE__ */ jsx9(
1248
- View_default,
1249
- {
1250
- isRow: true,
1251
- height: "100%",
1252
- backgroundColor: prefixSuffixBackgroundColor,
1253
- alignItems: "center",
1254
- borderWidth,
1255
- borderRightWidth: 0,
1256
- borderTopLeftRadius: theme2.styles.borderRadius,
1257
- borderBottomLeftRadius: theme2.styles.borderRadius,
1258
- borderColor: theme2.colors.border,
1259
- paddingHorizontal: readyPaddingHorizontal,
1260
- children: typeof prefix === "string" ? /* @__PURE__ */ jsx9(Text_default, { fontWeight: 700, children: prefix }) : prefix
1261
- }
1262
- ),
1263
- /* @__PURE__ */ jsx9(
1264
- Animate_default.View,
1265
- {
1266
- flex: 1,
1267
- backgroundColor: theme2.colors.backgroundContent,
1268
- borderTopLeftRadius: prefix ? 0 : theme2.styles.borderRadius,
1269
- borderBottomLeftRadius: prefix ? 0 : theme2.styles.borderRadius,
1270
- borderTopRightRadius: suffix ? 0 : theme2.styles.borderRadius,
1271
- borderBottomRightRadius: suffix ? 0 : theme2.styles.borderRadius,
1272
- borderWidth,
1273
- initialBorderColor: theme2.colors.border,
1274
- animateBorderColor: isFocused ? theme2.colors.primary : isError ? theme2.colors.error : theme2.colors.border,
1275
- overflow: "hidden",
1276
- children: /* @__PURE__ */ jsx9(
1277
- TextInput,
1368
+ return /* @__PURE__ */ jsxs4(
1369
+ Animate_default.View,
1370
+ {
1371
+ flex: 1,
1372
+ gap: theme2.styles.gap / 3,
1373
+ initialOpacity: 1,
1374
+ animateOpacity: disabled ? 0.6 : 1,
1375
+ children: [
1376
+ label && /* @__PURE__ */ jsxs4(View_default, { isRow: true, width: "100%", alignItems: "center", gap: 2, children: [
1377
+ /* @__PURE__ */ jsx9(Text_default, { fontSize: 14, color: theme2.colors.textSecondary, children: label }),
1378
+ required && /* @__PURE__ */ jsx9(Text_default, { color: theme2.colors.error, children: "*" })
1379
+ ] }),
1380
+ /* @__PURE__ */ jsxs4(View_default, { isRow: true, position: "relative", alignItems: "center", flex: 1, height: readyHeight, children: [
1381
+ prefix && /* @__PURE__ */ jsx9(
1382
+ View_default,
1383
+ {
1384
+ isRow: true,
1385
+ height: "100%",
1386
+ backgroundColor: prefixSuffixBackgroundColor,
1387
+ alignItems: "center",
1388
+ borderWidth,
1389
+ borderRightWidth: 0,
1390
+ borderTopLeftRadius: theme2.styles.borderRadius,
1391
+ borderBottomLeftRadius: theme2.styles.borderRadius,
1392
+ borderColor: theme2.colors.border,
1393
+ paddingHorizontal: readyPaddingHorizontal,
1394
+ children: typeof prefix === "string" ? /* @__PURE__ */ jsx9(Text_default, { fontWeight: 700, children: prefix }) : prefix
1395
+ }
1396
+ ),
1397
+ /* @__PURE__ */ jsx9(
1398
+ Animate_default.View,
1399
+ {
1400
+ flex: 1,
1401
+ backgroundColor: theme2.colors.backgroundContent,
1402
+ borderTopLeftRadius: prefix ? 0 : theme2.styles.borderRadius,
1403
+ borderBottomLeftRadius: prefix ? 0 : theme2.styles.borderRadius,
1404
+ borderTopRightRadius: suffix ? 0 : theme2.styles.borderRadius,
1405
+ borderBottomRightRadius: suffix ? 0 : theme2.styles.borderRadius,
1406
+ borderWidth,
1407
+ initialBorderColor: theme2.colors.border,
1408
+ animateBorderColor: isFocused ? theme2.colors.primary : isError ? theme2.colors.error : theme2.colors.border,
1409
+ overflow: "hidden",
1410
+ children: /* @__PURE__ */ jsx9(
1411
+ TextInput2,
1412
+ {
1413
+ style: textInputStyle,
1414
+ value: internalValue,
1415
+ defaultValue,
1416
+ autoCapitalize,
1417
+ autoComplete,
1418
+ autoCorrect,
1419
+ autoFocus,
1420
+ placeholder,
1421
+ placeholderTextColor: theme2.colors.textSecondary + "80",
1422
+ enterKeyHint: returnKeyLabel,
1423
+ returnKeyType,
1424
+ onSubmitEditing: onPressEnter,
1425
+ readOnly: !editable || disabled,
1426
+ textAlign,
1427
+ editable: !disabled,
1428
+ keyboardAppearance,
1429
+ keyboardType,
1430
+ cursorColor: theme2.colors.primary,
1431
+ selectionColor: theme2.colors.primary,
1432
+ secureTextEntry,
1433
+ onFocus: onFocusElement,
1434
+ onBlur: onBlurElement,
1435
+ onChangeText,
1436
+ onPress,
1437
+ ref: textInputRef
1438
+ }
1439
+ )
1440
+ }
1441
+ ),
1442
+ suffix && /* @__PURE__ */ jsx9(
1443
+ View_default,
1278
1444
  {
1279
- style: textInputStyle,
1280
- value: internalValue,
1281
- defaultValue,
1282
- autoCapitalize,
1283
- autoComplete,
1284
- autoCorrect,
1285
- autoFocus,
1286
- placeholder,
1287
- placeholderTextColor: theme2.colors.textSecondary + "80",
1288
- enterKeyHint: returnKeyLabel,
1289
- returnKeyType,
1290
- onSubmitEditing: onPressEnter,
1291
- readOnly: !editable,
1292
- textAlign,
1293
- keyboardAppearance,
1294
- keyboardType,
1295
- cursorColor: theme2.colors.primary,
1296
- selectionColor: theme2.colors.primary,
1297
- secureTextEntry,
1298
- onFocus: onFocusElement,
1299
- onBlur: onBlurElement,
1300
- onChangeText,
1301
- onPress,
1302
- ref: textInputRef
1445
+ isRow: true,
1446
+ height: "100%",
1447
+ backgroundColor: prefixSuffixBackgroundColor,
1448
+ alignItems: "center",
1449
+ borderWidth,
1450
+ borderLeftWidth: 0,
1451
+ borderTopRightRadius: theme2.styles.borderRadius,
1452
+ borderBottomRightRadius: theme2.styles.borderRadius,
1453
+ borderColor: theme2.colors.border,
1454
+ paddingHorizontal: readyPaddingHorizontal,
1455
+ children: typeof suffix === "string" ? /* @__PURE__ */ jsx9(Text_default, { fontWeight: 700, children: suffix }) : suffix
1303
1456
  }
1304
1457
  )
1305
- }
1306
- ),
1307
- suffix && /* @__PURE__ */ jsx9(
1308
- View_default,
1309
- {
1310
- isRow: true,
1311
- height: "100%",
1312
- backgroundColor: prefixSuffixBackgroundColor,
1313
- alignItems: "center",
1314
- borderWidth,
1315
- borderLeftWidth: 0,
1316
- borderTopRightRadius: theme2.styles.borderRadius,
1317
- borderBottomRightRadius: theme2.styles.borderRadius,
1318
- borderColor: theme2.colors.border,
1319
- paddingHorizontal: readyPaddingHorizontal,
1320
- children: typeof suffix === "string" ? /* @__PURE__ */ jsx9(Text_default, { fontWeight: 700, children: suffix }) : suffix
1321
- }
1322
- )
1323
- ] }),
1324
- infoMessage && /* @__PURE__ */ jsx9(
1325
- Animate_default.Text,
1326
- {
1327
- fontSize: 14,
1328
- color: theme2.colors.textSecondary,
1329
- initialHeight: 0,
1330
- initialOpacity: 0,
1331
- animateHeight: 17,
1332
- animateOpacity: 1,
1333
- children: infoMessage
1334
- }
1335
- ),
1336
- errorMessage && /* @__PURE__ */ jsx9(
1337
- Animate_default.Text,
1338
- {
1339
- fontSize: 14,
1340
- color: theme2.colors.error,
1341
- initialHeight: 0,
1342
- initialOpacity: 0,
1343
- animateHeight: 17,
1344
- animateOpacity: 1,
1345
- children: errorMessage
1346
- }
1347
- )
1348
- ] });
1458
+ ] }),
1459
+ infoMessage && /* @__PURE__ */ jsx9(
1460
+ Animate_default.Text,
1461
+ {
1462
+ fontSize: 14,
1463
+ color: theme2.colors.textSecondary,
1464
+ initialHeight: 0,
1465
+ initialOpacity: 0,
1466
+ animateHeight: 17,
1467
+ animateOpacity: 1,
1468
+ children: infoMessage
1469
+ }
1470
+ ),
1471
+ errorMessage && /* @__PURE__ */ jsx9(
1472
+ Animate_default.Text,
1473
+ {
1474
+ fontSize: 14,
1475
+ color: theme2.colors.error,
1476
+ initialHeight: 0,
1477
+ initialOpacity: 0,
1478
+ animateHeight: 17,
1479
+ animateOpacity: 1,
1480
+ children: errorMessage
1481
+ }
1482
+ )
1483
+ ]
1484
+ }
1485
+ );
1349
1486
  }
1350
1487
  );
1351
1488
  InputFieldComponent.email = forwardRef(function Email(props, ref) {
@@ -1451,6 +1588,7 @@ export {
1451
1588
  formatPhoneNumber,
1452
1589
  generateAsyncStorage,
1453
1590
  generateRandomString,
1591
+ getFormErrorObject,
1454
1592
  getPluralWord,
1455
1593
  lightenColor2 as lightenColor,
1456
1594
  loaderControls,
@@ -1460,6 +1598,7 @@ export {
1460
1598
  useBooleanState5 as useBooleanState,
1461
1599
  useDebounceState,
1462
1600
  useDevice,
1601
+ useForm,
1463
1602
  useKeyboard,
1464
1603
  useLoader2 as useLoader,
1465
1604
  useLoaderControls,