luna-components-library 1.1.41 → 1.1.42
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 +162 -7
- package/dist/luna-components-library.js +228 -39
- package/dist/luna-components-library.js.map +1 -1
- package/dist/src/components/Form.d.ts +48 -0
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/hooks/index.d.ts +2 -0
- package/dist/src/hooks/useForm.hook.d.ts +36 -0
- package/dist/src/types.d.ts +2 -0
- package/dist/src/utilities/validators.util.d.ts +11 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -702,7 +702,79 @@ type InputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'sea
|
|
|
702
702
|
- `md` - Medium padding and text
|
|
703
703
|
- `lg` - Large padding and text
|
|
704
704
|
- `xl` - Extra large padding and text
|
|
705
|
-
###
|
|
705
|
+
### Form
|
|
706
|
+
A form component with built-in state management, validation, and layout control. Works together with the `useForm` hook.
|
|
707
|
+
|
|
708
|
+
```jsx
|
|
709
|
+
import { Form, Input, Button, useForm } from 'luna-components-library';
|
|
710
|
+
|
|
711
|
+
const MyForm = () => {
|
|
712
|
+
const form = useForm({
|
|
713
|
+
name: { value: '', rules: [{ required: true, message: 'Name is required' }] },
|
|
714
|
+
email: { value: '', rules: [{ required: true }, { type: 'email', message: 'Invalid email' }] },
|
|
715
|
+
password: { value: '', rules: [
|
|
716
|
+
{ required: true },
|
|
717
|
+
{ minLength: 6, message: 'Min 6 characters' },
|
|
718
|
+
{ validator: (v) => !validators.isStrongPassword(v) ? 'Must have letters and numbers' : undefined }
|
|
719
|
+
]},
|
|
720
|
+
birthdate: { value: '', rules: [{ type: 'date' }, { maxDate: '2025-12-31' }] },
|
|
721
|
+
agree: { value: false, rules: [{ required: true, message: 'You must accept the terms' }] },
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
return (
|
|
725
|
+
<Form form={form} layout="vertical" onFinish={(values) => console.log(values)}>
|
|
726
|
+
<Form.Item name="name" label="Full Name" required>
|
|
727
|
+
<Input placeholder="John Doe" />
|
|
728
|
+
</Form.Item>
|
|
729
|
+
<Form.Item name="email" label="Email" required>
|
|
730
|
+
<Input type="email" placeholder="john@example.com" />
|
|
731
|
+
</Form.Item>
|
|
732
|
+
<Form.Item name="password" label="Password" required>
|
|
733
|
+
<Input type="password" placeholder="Min 6 chars" />
|
|
734
|
+
</Form.Item>
|
|
735
|
+
<Form.Item name="birthdate" label="Birth Date" required>
|
|
736
|
+
<Input type="date" />
|
|
737
|
+
</Form.Item>
|
|
738
|
+
<Form.Item name="agree">
|
|
739
|
+
<label>
|
|
740
|
+
<input type="checkbox" checked={form.values.agree} onChange={(e) => form.setValue('agree', e.target.checked)} />
|
|
741
|
+
I accept the terms
|
|
742
|
+
</label>
|
|
743
|
+
</Form.Item>
|
|
744
|
+
<Button type="submit">Submit</Button>
|
|
745
|
+
<Button type="button" variant="outline" onClick={form.reset}>Reset</Button>
|
|
746
|
+
</Form>
|
|
747
|
+
);
|
|
748
|
+
};
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
**Form Props:**
|
|
752
|
+
- `form: FormInstance` - Form instance from `useForm` hook
|
|
753
|
+
- `onFinish?: (values) => void` - Called on valid submit
|
|
754
|
+
- `onFinishFailed?: (errors) => void` - Called on invalid submit
|
|
755
|
+
- `layout?: FormLayout` - Layout mode (default: `'vertical'`)
|
|
756
|
+
- `children: React.ReactNode` - Form fields
|
|
757
|
+
- `className?: string` - Additional CSS classes
|
|
758
|
+
- `style?: React.CSSProperties` - Custom inline styles
|
|
759
|
+
|
|
760
|
+
**Form.Item Props:**
|
|
761
|
+
- `name?: string` - Field name, connects to form context
|
|
762
|
+
- `label?: React.ReactNode` - Field label
|
|
763
|
+
- `children: React.ReactElement` - Input component (auto-receives `value` and `onChange`)
|
|
764
|
+
- `required?: boolean` - Shows `*` on label
|
|
765
|
+
- `className?: string` - Additional CSS classes
|
|
766
|
+
- `style?: React.CSSProperties` - Custom inline styles
|
|
767
|
+
|
|
768
|
+
**Types:**
|
|
769
|
+
```typescript
|
|
770
|
+
type FormLayout = 'vertical' | 'horizontal' | 'inline';
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**Behavior:**
|
|
774
|
+
- `Form.Item` automatically injects `value`, `onChange`, and `variant="danger"` into the child when there's an error
|
|
775
|
+
- Supports Luna `Input` and native HTML inputs (checkbox, etc.)
|
|
776
|
+
- Error messages appear below the field in red
|
|
777
|
+
|
|
706
778
|
A powerful and customizable data grid with support for filtering, sorting, pagination, selection, and search.
|
|
707
779
|
|
|
708
780
|
```jsx
|
|
@@ -833,15 +905,25 @@ const text = formatters.truncate('Long text here...', 10); // "Long text..."
|
|
|
833
905
|
```
|
|
834
906
|
|
|
835
907
|
### validators
|
|
836
|
-
Common validation
|
|
908
|
+
Common validation functions, shared internally with `useForm`.
|
|
837
909
|
|
|
838
910
|
```javascript
|
|
839
911
|
import { validators } from 'luna-components-library';
|
|
840
912
|
|
|
841
|
-
validators.isEmail('test@example.com');
|
|
842
|
-
validators.
|
|
843
|
-
validators.
|
|
844
|
-
validators.
|
|
913
|
+
validators.isEmail('test@example.com'); // true
|
|
914
|
+
validators.isUrl('https://example.com'); // true
|
|
915
|
+
validators.isEmpty(' '); // true
|
|
916
|
+
validators.isEmpty(null); // true
|
|
917
|
+
validators.isEmpty(false); // true
|
|
918
|
+
validators.isNumber('42'); // true
|
|
919
|
+
validators.isStrongPassword('Pass1234'); // true (8+ chars, letter + number)
|
|
920
|
+
validators.isPhone('88888888', 'es-CR'); // true
|
|
921
|
+
validators.minLength('hello', 3); // true
|
|
922
|
+
validators.maxLength('hello', 10); // true
|
|
923
|
+
validators.matchesPattern('ABC123', /^[A-Z]{3}\d{3}$/); // true
|
|
924
|
+
validators.isDate('2025-01-15'); // true
|
|
925
|
+
validators.isDateBefore('2024-01-01', '2025-01-01'); // true
|
|
926
|
+
validators.isDateAfter('2025-01-01', '2024-01-01'); // true
|
|
845
927
|
```
|
|
846
928
|
|
|
847
929
|
### logger
|
|
@@ -882,7 +964,80 @@ function UserList() {
|
|
|
882
964
|
}
|
|
883
965
|
```
|
|
884
966
|
|
|
885
|
-
###
|
|
967
|
+
### useForm
|
|
968
|
+
Manages form state and validation. Returns a `FormInstance` used by the `Form` component.
|
|
969
|
+
|
|
970
|
+
```javascript
|
|
971
|
+
import { useForm } from 'luna-components-library';
|
|
972
|
+
|
|
973
|
+
const form = useForm({
|
|
974
|
+
email: {
|
|
975
|
+
value: '',
|
|
976
|
+
rules: [
|
|
977
|
+
{ required: true, message: 'Email is required' },
|
|
978
|
+
{ type: 'email', message: 'Invalid email' },
|
|
979
|
+
]
|
|
980
|
+
},
|
|
981
|
+
age: {
|
|
982
|
+
value: '',
|
|
983
|
+
rules: [
|
|
984
|
+
{ type: 'number', message: 'Must be a number' },
|
|
985
|
+
]
|
|
986
|
+
},
|
|
987
|
+
website: {
|
|
988
|
+
value: '',
|
|
989
|
+
rules: [{ type: 'url', message: 'Invalid URL' }]
|
|
990
|
+
},
|
|
991
|
+
birthdate: {
|
|
992
|
+
value: '',
|
|
993
|
+
rules: [
|
|
994
|
+
{ type: 'date', message: 'Invalid date' },
|
|
995
|
+
{ minDate: '1900-01-01', message: 'Too old' },
|
|
996
|
+
{ maxDate: '2025-12-31', message: 'Cannot be in the future' },
|
|
997
|
+
]
|
|
998
|
+
},
|
|
999
|
+
bio: {
|
|
1000
|
+
value: '',
|
|
1001
|
+
rules: [
|
|
1002
|
+
{ minLength: 10, message: 'Min 10 characters' },
|
|
1003
|
+
{ maxLength: 200, message: 'Max 200 characters' },
|
|
1004
|
+
]
|
|
1005
|
+
},
|
|
1006
|
+
code: {
|
|
1007
|
+
value: '',
|
|
1008
|
+
rules: [{ pattern: /^[A-Z]{3}\d{3}$/, message: 'Format: ABC123' }]
|
|
1009
|
+
},
|
|
1010
|
+
custom: {
|
|
1011
|
+
value: '',
|
|
1012
|
+
rules: [{ validator: (v) => v !== 'forbidden' ? undefined : 'This value is not allowed' }]
|
|
1013
|
+
},
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
// FormInstance API
|
|
1017
|
+
form.values; // { email: '', age: '', ... }
|
|
1018
|
+
form.errors; // { email: 'Email is required', ... }
|
|
1019
|
+
form.setValue('email', 'a@b.c'); // update + validate field
|
|
1020
|
+
form.validate(); // validate all, returns boolean
|
|
1021
|
+
form.reset(); // restore initial values
|
|
1022
|
+
form.setError('email', 'Taken'); // set error manually
|
|
1023
|
+
form.clearError('email'); // clear error manually
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
**FieldRule options:**
|
|
1027
|
+
```typescript
|
|
1028
|
+
type FieldRule = {
|
|
1029
|
+
required?: boolean; // field must not be empty
|
|
1030
|
+
message?: string; // custom error message
|
|
1031
|
+
type?: 'email' | 'url' | 'number' | 'date'; // format validation
|
|
1032
|
+
minLength?: number; // minimum string length
|
|
1033
|
+
maxLength?: number; // maximum string length
|
|
1034
|
+
minDate?: string; // minimum date (ISO string)
|
|
1035
|
+
maxDate?: string; // maximum date (ISO string)
|
|
1036
|
+
pattern?: RegExp; // regex pattern
|
|
1037
|
+
validator?: (value: any) => string | undefined; // custom validator
|
|
1038
|
+
};
|
|
1039
|
+
```
|
|
1040
|
+
|
|
886
1041
|
Syncs state with `localStorage` automatically.
|
|
887
1042
|
|
|
888
1043
|
```javascript
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
1
|
+
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
4
4
|
//#endregion
|
|
@@ -2478,6 +2478,115 @@ var QRCode = ({ value, size = 160, color = "000000", bgColor = "ffffff", bordere
|
|
|
2478
2478
|
});
|
|
2479
2479
|
};
|
|
2480
2480
|
//#endregion
|
|
2481
|
+
//#region src/components/Form.tsx
|
|
2482
|
+
var FormContext = createContext(null);
|
|
2483
|
+
var useFormContext = () => useContext(FormContext);
|
|
2484
|
+
/**
|
|
2485
|
+
* Form.Item component that represents an individual form field. It accepts a name prop to connect to the form context, a label for display, and children which should be a form input component. It automatically handles value changes and displays validation errors based on the form context. The required prop adds an asterisk to the label and indicates that the field is required.
|
|
2486
|
+
* @param name The name of the form field, used to connect to the form context for value and error handling.
|
|
2487
|
+
* @param label The label to display for the form field.
|
|
2488
|
+
* @param children The form input component (e.g., Input, Checkbox) that will be rendered and connected to the form context.
|
|
2489
|
+
* @param required If true, indicates that the field is required and adds an asterisk to the label.
|
|
2490
|
+
* @param className Optional additional class name for styling.
|
|
2491
|
+
* @param style Optional additional styles for the form item container.
|
|
2492
|
+
* @returns A Form.Item component that displays a label, the input field, and any validation error messages based on the form context.
|
|
2493
|
+
* @description Form.Item component that represents an individual form field. It accepts a name prop to connect to the form context, a label for display, and children which should be a form input component. It automatically handles value changes and displays validation errors based on the form context. The required prop adds an asterisk to the label and indicates that the field is required.
|
|
2494
|
+
*
|
|
2495
|
+
*/
|
|
2496
|
+
var FormItem = ({ name, label, children, required, className, style }) => {
|
|
2497
|
+
const ctx = useFormContext();
|
|
2498
|
+
const error = name && ctx ? ctx.errors[name] : void 0;
|
|
2499
|
+
const value = name && ctx ? ctx.values[name] : void 0;
|
|
2500
|
+
const child = name && ctx ? React.createElement(children.type, {
|
|
2501
|
+
...children.props,
|
|
2502
|
+
value: value ?? "",
|
|
2503
|
+
onChange: (val) => {
|
|
2504
|
+
if (val && typeof val === "object" && val.target) ctx.setValue(name, val.target.type === "checkbox" ? val.target.checked : val.target.value);
|
|
2505
|
+
else ctx.setValue(name, val);
|
|
2506
|
+
},
|
|
2507
|
+
variant: error ? "danger" : children.props.variant ?? void 0
|
|
2508
|
+
}) : children;
|
|
2509
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
2510
|
+
className,
|
|
2511
|
+
style: {
|
|
2512
|
+
marginBottom: "1.25rem",
|
|
2513
|
+
display: ctx?.layout === "horizontal" ? "flex" : "block",
|
|
2514
|
+
alignItems: ctx?.layout === "horizontal" ? "flex-start" : void 0,
|
|
2515
|
+
gap: ctx?.layout === "horizontal" ? "1rem" : void 0,
|
|
2516
|
+
...style
|
|
2517
|
+
},
|
|
2518
|
+
children: [label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
|
|
2519
|
+
style: {
|
|
2520
|
+
display: "block",
|
|
2521
|
+
fontSize: fontSizes.sm,
|
|
2522
|
+
fontWeight: fontWeights.medium,
|
|
2523
|
+
color: colors.text,
|
|
2524
|
+
marginBottom: ctx?.layout === "horizontal" ? 0 : "0.25rem",
|
|
2525
|
+
minWidth: ctx?.layout === "horizontal" ? "120px" : void 0,
|
|
2526
|
+
paddingTop: ctx?.layout === "horizontal" ? "0.5rem" : void 0
|
|
2527
|
+
},
|
|
2528
|
+
children: [required && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
2529
|
+
style: {
|
|
2530
|
+
color: colors.danger,
|
|
2531
|
+
marginRight: "0.25rem"
|
|
2532
|
+
},
|
|
2533
|
+
children: "*"
|
|
2534
|
+
}), label]
|
|
2535
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
2536
|
+
style: { flex: 1 },
|
|
2537
|
+
children: [child, error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
2538
|
+
style: {
|
|
2539
|
+
display: "block",
|
|
2540
|
+
marginTop: "0.25rem",
|
|
2541
|
+
fontSize: fontSizes.xs,
|
|
2542
|
+
color: colors.danger
|
|
2543
|
+
},
|
|
2544
|
+
children: error
|
|
2545
|
+
})]
|
|
2546
|
+
})]
|
|
2547
|
+
});
|
|
2548
|
+
};
|
|
2549
|
+
/**
|
|
2550
|
+
*
|
|
2551
|
+
* @param form The form instance created by the useForm hook, which manages form state and validation.
|
|
2552
|
+
* @param onFinish Callback function that is called when the form is successfully submitted and passes validation. It receives the form values as an argument.
|
|
2553
|
+
* @param onFinishFailed Callback function that is called when the form submission fails validation. It receives the form errors as an argument.
|
|
2554
|
+
* @param layout The layout of the form, which can be 'vertical', 'horizontal', or 'inline'. This affects how the form items are displayed.
|
|
2555
|
+
* @param children The form fields (Form.Item components) that will be rendered inside the form.
|
|
2556
|
+
* @param className Optional additional class name for styling the form.
|
|
2557
|
+
* @param style Optional additional styles for the form container.
|
|
2558
|
+
*
|
|
2559
|
+
* @returns A Form component that provides context for managing form state and validation. It includes a nested Form.Item component for individual form fields, which automatically connects to the form context for value and error handling. The Form component handles form submission and validation, while Form.Item displays labels, inputs, and error messages based on the provided rules.
|
|
2560
|
+
* @description Form component that provides context for managing form state and validation. It includes a nested Form.Item component for individual form fields, which automatically connects to the form context for value and error handling. The Form component handles form submission and validation, while Form.Item displays labels, inputs, and error messages based on the provided rules.
|
|
2561
|
+
*/
|
|
2562
|
+
var Form = ({ form, onFinish, onFinishFailed, layout = "vertical", children, className, style }) => {
|
|
2563
|
+
const handleSubmit = (e) => {
|
|
2564
|
+
e.preventDefault();
|
|
2565
|
+
if (form.validate()) onFinish?.(form.values);
|
|
2566
|
+
else onFinishFailed?.(form.errors);
|
|
2567
|
+
};
|
|
2568
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FormContext.Provider, {
|
|
2569
|
+
value: {
|
|
2570
|
+
...form,
|
|
2571
|
+
layout
|
|
2572
|
+
},
|
|
2573
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", {
|
|
2574
|
+
onSubmit: handleSubmit,
|
|
2575
|
+
className,
|
|
2576
|
+
style: {
|
|
2577
|
+
display: layout === "inline" ? "flex" : "block",
|
|
2578
|
+
flexWrap: layout === "inline" ? "wrap" : void 0,
|
|
2579
|
+
gap: layout === "inline" ? "1rem" : void 0,
|
|
2580
|
+
alignItems: layout === "inline" ? "flex-end" : void 0,
|
|
2581
|
+
...style
|
|
2582
|
+
},
|
|
2583
|
+
noValidate: true,
|
|
2584
|
+
children
|
|
2585
|
+
})
|
|
2586
|
+
});
|
|
2587
|
+
};
|
|
2588
|
+
Form.Item = FormItem;
|
|
2589
|
+
//#endregion
|
|
2481
2590
|
//#region src/utilities/apiFetch.util.ts
|
|
2482
2591
|
/**
|
|
2483
2592
|
* A generic wrapper for the fetch API with error handling and response parsing.
|
|
@@ -2646,46 +2755,33 @@ var formatters = {
|
|
|
2646
2755
|
}
|
|
2647
2756
|
};
|
|
2648
2757
|
//#endregion
|
|
2758
|
+
//#region src/types.ts
|
|
2759
|
+
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2760
|
+
var URL_REGEX = /^https?:\/\/.+/;
|
|
2761
|
+
//#endregion
|
|
2649
2762
|
//#region src/utilities/validators.util.ts
|
|
2650
|
-
/**
|
|
2651
|
-
* Utility functions for common validations.
|
|
2652
|
-
*/
|
|
2653
2763
|
var validators = {
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
/**
|
|
2661
|
-
* Checks if a string is empty or only whitespace.
|
|
2662
|
-
*/
|
|
2663
|
-
isEmpty: (str) => {
|
|
2664
|
-
return !str || str.trim().length === 0;
|
|
2665
|
-
},
|
|
2666
|
-
/**
|
|
2667
|
-
* Checks if a value is a number.
|
|
2668
|
-
*/
|
|
2669
|
-
isNumber: (value) => {
|
|
2670
|
-
return !isNaN(parseFloat(value)) && isFinite(value);
|
|
2671
|
-
},
|
|
2672
|
-
/**
|
|
2673
|
-
* Validates if a password meets minimum complexity.
|
|
2674
|
-
* (At least 8 chars, 1 letter, 1 number)
|
|
2675
|
-
*/
|
|
2676
|
-
isStrongPassword: (password) => {
|
|
2677
|
-
return password.length >= 8 && /[A-Za-z]/.test(password) && /[0-9]/.test(password);
|
|
2678
|
-
},
|
|
2679
|
-
/**
|
|
2680
|
-
* Validates a phone number.
|
|
2681
|
-
* @param phone - Phone number string
|
|
2682
|
-
* @param locale - Optional locale (default: 'generic')
|
|
2683
|
-
*/
|
|
2764
|
+
isEmail: (value) => EMAIL_REGEX.test(value),
|
|
2765
|
+
isUrl: (value) => URL_REGEX.test(value),
|
|
2766
|
+
isEmpty: (value) => value === void 0 || value === null || value === false || String(value).trim().length === 0,
|
|
2767
|
+
isNumber: (value) => !isNaN(parseFloat(value)) && isFinite(value),
|
|
2768
|
+
/** A strong password is defined as including both letters and numbers. */
|
|
2769
|
+
isStrongPassword: (password, minLength) => password.length >= minLength && /[A-Za-z]/.test(password) && /[0-9]/.test(password),
|
|
2684
2770
|
isPhone: (phone, locale = "generic") => {
|
|
2685
|
-
const
|
|
2686
|
-
if (locale === "es-CR") return /^[245678]\d{7}$/.test(
|
|
2687
|
-
return /^\+?[\d\s-]{7,}$/.test(
|
|
2688
|
-
}
|
|
2771
|
+
const clean = phone.replace(/\s|-/g, "");
|
|
2772
|
+
if (locale === "es-CR") return /^[245678]\d{7}$/.test(clean);
|
|
2773
|
+
return /^\+?[\d\s-]{7,}$/.test(clean);
|
|
2774
|
+
},
|
|
2775
|
+
minLength: (value, min) => min === void 0 || value.length >= min,
|
|
2776
|
+
maxLength: (value, max) => max === void 0 || value.length <= max,
|
|
2777
|
+
matchesPattern: (value, pattern) => pattern.test(value),
|
|
2778
|
+
isDate: (value) => {
|
|
2779
|
+
if (!value) return false;
|
|
2780
|
+
const d = new Date(value);
|
|
2781
|
+
return !isNaN(d.getTime());
|
|
2782
|
+
},
|
|
2783
|
+
isDateBefore: (value, max) => new Date(value) < new Date(max),
|
|
2784
|
+
isDateAfter: (value, min) => new Date(value) > new Date(min)
|
|
2689
2785
|
};
|
|
2690
2786
|
//#endregion
|
|
2691
2787
|
//#region src/utilities/logger.util.ts
|
|
@@ -2803,6 +2899,99 @@ function useDebounce(value, delay = 500) {
|
|
|
2803
2899
|
return debouncedValue;
|
|
2804
2900
|
}
|
|
2805
2901
|
//#endregion
|
|
2806
|
-
|
|
2902
|
+
//#region src/hooks/useForm.hook.ts
|
|
2903
|
+
/**
|
|
2904
|
+
* Validates a field value against its rules.
|
|
2905
|
+
* @param value
|
|
2906
|
+
* @param rules
|
|
2907
|
+
* @returns
|
|
2908
|
+
* Checks the provided value against an array of validation rules. It returns an error message if any rule fails, or undefined if the value is valid. The function supports various types of validation, including required fields, email and URL formats, number checks, length constraints, date validations, custom patterns, and custom validator functions.
|
|
2909
|
+
*/
|
|
2910
|
+
var validateField = (value, rules = []) => {
|
|
2911
|
+
for (const rule of rules) {
|
|
2912
|
+
if (rule.validator) {
|
|
2913
|
+
const msg = rule.validator(value);
|
|
2914
|
+
if (msg) return msg;
|
|
2915
|
+
}
|
|
2916
|
+
if (rule.required && validators.isEmpty(value)) return rule.message ?? "This field is required";
|
|
2917
|
+
if (rule.type === "email" && value && !validators.isEmail(value)) return rule.message ?? "Invalid email address";
|
|
2918
|
+
if (rule.type === "url" && value && !validators.isUrl(value)) return rule.message ?? "Invalid URL";
|
|
2919
|
+
if (rule.type === "number" && value && !validators.isNumber(value)) return rule.message ?? "Must be a number";
|
|
2920
|
+
if (rule.minLength && value && !validators.minLength(value, rule.minLength)) return rule.message ?? `Minimum ${rule.minLength} characters`;
|
|
2921
|
+
if (rule.maxLength && value && !validators.maxLength(value, rule.maxLength)) return rule.message ?? `Maximum ${rule.maxLength} characters`;
|
|
2922
|
+
if (rule.type === "date" && value && !validators.isDate(value)) return rule.message ?? "Invalid date";
|
|
2923
|
+
if (rule.minDate && value && !validators.isDateAfter(value, rule.minDate)) return rule.message ?? `Date must be after ${rule.minDate}`;
|
|
2924
|
+
if (rule.maxDate && value && !validators.isDateBefore(value, rule.maxDate)) return rule.message ?? `Date must be before ${rule.maxDate}`;
|
|
2925
|
+
if (rule.pattern && value && !validators.matchesPattern(value, rule.pattern)) return rule.message ?? "Invalid format";
|
|
2926
|
+
}
|
|
2927
|
+
};
|
|
2928
|
+
/**
|
|
2929
|
+
* Hook for managing form state and validation.
|
|
2930
|
+
* @param fields
|
|
2931
|
+
* @returns
|
|
2932
|
+
* Provides form state management and validation based on a configuration object. Each field can have its own validation rules, and the hook returns current values, errors, and helper functions to manage the form.
|
|
2933
|
+
* The `useForm` hook initializes form values and errors based on the provided field configurations. It offers a `setValue` function to update field values and perform validation, a `validate` function to check all fields against their rules, a `reset` function to restore initial values, and functions to set or clear specific error messages. This hook abstracts away the complexities of form handling, making it easier to implement forms in React components.
|
|
2934
|
+
*/
|
|
2935
|
+
var useForm = (fields) => {
|
|
2936
|
+
const initialValues = Object.fromEntries(Object.entries(fields).map(([k, v]) => [k, v.value]));
|
|
2937
|
+
const [values, setValues] = useState(initialValues);
|
|
2938
|
+
const [errors, setErrors] = useState({});
|
|
2939
|
+
const setValue = (name, value) => {
|
|
2940
|
+
setValues((prev) => ({
|
|
2941
|
+
...prev,
|
|
2942
|
+
[name]: value
|
|
2943
|
+
}));
|
|
2944
|
+
const error = validateField(value, fields[name]?.rules);
|
|
2945
|
+
setErrors((prev) => ({
|
|
2946
|
+
...prev,
|
|
2947
|
+
[name]: error
|
|
2948
|
+
}));
|
|
2949
|
+
};
|
|
2950
|
+
const validate = () => {
|
|
2951
|
+
const newErrors = {};
|
|
2952
|
+
let valid = true;
|
|
2953
|
+
for (const [name, config] of Object.entries(fields)) {
|
|
2954
|
+
const error = validateField(values[name], config.rules);
|
|
2955
|
+
if (error) {
|
|
2956
|
+
newErrors[name] = error;
|
|
2957
|
+
valid = false;
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
setErrors(newErrors);
|
|
2961
|
+
return valid;
|
|
2962
|
+
};
|
|
2963
|
+
const reset = () => {
|
|
2964
|
+
setValues(initialValues);
|
|
2965
|
+
setErrors({});
|
|
2966
|
+
};
|
|
2967
|
+
const setError = (name, message) => {
|
|
2968
|
+
setErrors((prev) => ({
|
|
2969
|
+
...prev,
|
|
2970
|
+
[name]: message
|
|
2971
|
+
}));
|
|
2972
|
+
};
|
|
2973
|
+
const clearError = (name) => {
|
|
2974
|
+
setErrors((prev) => ({
|
|
2975
|
+
...prev,
|
|
2976
|
+
[name]: void 0
|
|
2977
|
+
}));
|
|
2978
|
+
};
|
|
2979
|
+
/**
|
|
2980
|
+
* Returns the form instance with all its methods and state.
|
|
2981
|
+
* This includes the current values of the form fields, any validation errors, and functions to update field values, validate the form, reset it, and manage errors. The hook abstracts away the complexity of form state management and validation, providing a simple interface for use in components.
|
|
2982
|
+
* @returns {FormInstance} The form instance containing values, errors, and helper functions.
|
|
2983
|
+
*/
|
|
2984
|
+
return {
|
|
2985
|
+
values,
|
|
2986
|
+
errors,
|
|
2987
|
+
setValue,
|
|
2988
|
+
validate,
|
|
2989
|
+
reset,
|
|
2990
|
+
setError,
|
|
2991
|
+
clearError
|
|
2992
|
+
};
|
|
2993
|
+
};
|
|
2994
|
+
//#endregion
|
|
2995
|
+
export { Accordion, Anchor, Button, Card, DataTable, DropDown, Form, Input, Modal, MultiSelect, Popconfirm, Preloader, ProgressBar, QRCode, ScrollTop, Spinner, Toast, Typed, WhatsApp, apiFetch, del, formatters, get, httpClient, logger, post, put, storage, useDebounce, useFetch, useForm, useLocalStorage, validators };
|
|
2807
2996
|
|
|
2808
2997
|
//# sourceMappingURL=luna-components-library.js.map
|