luna-components-library 1.1.41 → 1.1.43

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.
Files changed (48) hide show
  1. package/README.md +163 -6
  2. package/dist/components/Form.d.ts +48 -0
  3. package/dist/{src/components → components}/index.d.ts +1 -0
  4. package/dist/hooks/index.d.ts +5 -0
  5. package/dist/hooks/useForm.hook.d.ts +36 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/luna-components-library.js +228 -39
  8. package/dist/luna-components-library.js.map +1 -1
  9. package/dist/src/index.d.ts +2 -3
  10. package/dist/{src/types.d.ts → types.d.ts} +2 -0
  11. package/dist/utilities/validators.util.d.ts +15 -0
  12. package/package.json +1 -1
  13. package/dist/src/hooks/index.d.ts +0 -3
  14. package/dist/src/utilities/validators.util.d.ts +0 -28
  15. /package/dist/{src/components → components}/Accordion.d.ts +0 -0
  16. /package/dist/{src/components → components}/Anchor.d.ts +0 -0
  17. /package/dist/{src/components → components}/Anchor.test.d.ts +0 -0
  18. /package/dist/{src/components → components}/Button.d.ts +0 -0
  19. /package/dist/{src/components → components}/Card.d.ts +0 -0
  20. /package/dist/{src/components → components}/DataTable.d.ts +0 -0
  21. /package/dist/{src/components → components}/DropDown.d.ts +0 -0
  22. /package/dist/{src/components → components}/Input.d.ts +0 -0
  23. /package/dist/{src/components → components}/Modal.d.ts +0 -0
  24. /package/dist/{src/components → components}/MultiSelect.d.ts +0 -0
  25. /package/dist/{src/components → components}/Popconfirm.d.ts +0 -0
  26. /package/dist/{src/components → components}/Preloader.d.ts +0 -0
  27. /package/dist/{src/components → components}/ProgressBar.d.ts +0 -0
  28. /package/dist/{src/components → components}/QRCode.d.ts +0 -0
  29. /package/dist/{src/components → components}/ScrollTop.d.ts +0 -0
  30. /package/dist/{src/components → components}/Spinner.d.ts +0 -0
  31. /package/dist/{src/components → components}/Toast.d.ts +0 -0
  32. /package/dist/{src/components → components}/Typed.d.ts +0 -0
  33. /package/dist/{src/components → components}/WhatsApp.d.ts +0 -0
  34. /package/dist/{src/components → components}/apiExamples/ApiExamples.d.ts +0 -0
  35. /package/dist/{src/components → components}/modalExamples/ModalDataExample.d.ts +0 -0
  36. /package/dist/{src/components → components}/utilExamples/UtilExamples.d.ts +0 -0
  37. /package/dist/{src/demo.d.ts → demo.d.ts} +0 -0
  38. /package/dist/{src/hooks → hooks}/useDebounce.hook.d.ts +0 -0
  39. /package/dist/{src/hooks → hooks}/useFetch.hook.d.ts +0 -0
  40. /package/dist/{src/hooks → hooks}/useLocalStorage.hook.d.ts +0 -0
  41. /package/dist/{src/setupTests.d.ts → setupTests.d.ts} +0 -0
  42. /package/dist/{src/styles.d.ts → styles.d.ts} +0 -0
  43. /package/dist/{src/utilities → utilities}/apiFetch.util.d.ts +0 -0
  44. /package/dist/{src/utilities → utilities}/formatters.util.d.ts +0 -0
  45. /package/dist/{src/utilities → utilities}/httpClient.util.d.ts +0 -0
  46. /package/dist/{src/utilities → utilities}/index.d.ts +0 -0
  47. /package/dist/{src/utilities → utilities}/logger.util.d.ts +0 -0
  48. /package/dist/{src/utilities → utilities}/storage.util.d.ts +0 -0
package/README.md CHANGED
@@ -702,7 +702,81 @@ 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
+ ### 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
+
705
778
  ### DataTable
779
+
706
780
  A powerful and customizable data grid with support for filtering, sorting, pagination, selection, and search.
707
781
 
708
782
  ```jsx
@@ -833,15 +907,25 @@ const text = formatters.truncate('Long text here...', 10); // "Long text..."
833
907
  ```
834
908
 
835
909
  ### validators
836
- Common validation rules.
910
+ Common validation functions, shared internally with `useForm`.
837
911
 
838
912
  ```javascript
839
913
  import { validators } from 'luna-components-library';
840
914
 
841
- validators.isEmail('test@example.com'); // true
842
- validators.isEmpty(' '); // true
843
- validators.isStrongPassword('Pass1234'); // true
844
- validators.isPhone('88888888', 'es-CR'); // true
915
+ validators.isEmail('test@example.com'); // true
916
+ validators.isUrl('https://example.com'); // true
917
+ validators.isEmpty(' '); // true
918
+ validators.isEmpty(null); // true
919
+ validators.isEmpty(false); // true
920
+ validators.isNumber('42'); // true
921
+ validators.isStrongPassword('Pass1234'); // true (8+ chars, letter + number)
922
+ validators.isPhone('88888888', 'es-CR'); // true
923
+ validators.minLength('hello', 3); // true
924
+ validators.maxLength('hello', 10); // true
925
+ validators.matchesPattern('ABC123', /^[A-Z]{3}\d{3}$/); // true
926
+ validators.isDate('2025-01-15'); // true
927
+ validators.isDateBefore('2024-01-01', '2025-01-01'); // true
928
+ validators.isDateAfter('2025-01-01', '2024-01-01'); // true
845
929
  ```
846
930
 
847
931
  ### logger
@@ -882,7 +966,80 @@ function UserList() {
882
966
  }
883
967
  ```
884
968
 
885
- ### useLocalStorage
969
+ ### useForm
970
+ Manages form state and validation. Returns a `FormInstance` used by the `Form` component.
971
+
972
+ ```javascript
973
+ import { useForm } from 'luna-components-library';
974
+
975
+ const form = useForm({
976
+ email: {
977
+ value: '',
978
+ rules: [
979
+ { required: true, message: 'Email is required' },
980
+ { type: 'email', message: 'Invalid email' },
981
+ ]
982
+ },
983
+ age: {
984
+ value: '',
985
+ rules: [
986
+ { type: 'number', message: 'Must be a number' },
987
+ ]
988
+ },
989
+ website: {
990
+ value: '',
991
+ rules: [{ type: 'url', message: 'Invalid URL' }]
992
+ },
993
+ birthdate: {
994
+ value: '',
995
+ rules: [
996
+ { type: 'date', message: 'Invalid date' },
997
+ { minDate: '1900-01-01', message: 'Too old' },
998
+ { maxDate: '2025-12-31', message: 'Cannot be in the future' },
999
+ ]
1000
+ },
1001
+ bio: {
1002
+ value: '',
1003
+ rules: [
1004
+ { minLength: 10, message: 'Min 10 characters' },
1005
+ { maxLength: 200, message: 'Max 200 characters' },
1006
+ ]
1007
+ },
1008
+ code: {
1009
+ value: '',
1010
+ rules: [{ pattern: /^[A-Z]{3}\d{3}$/, message: 'Format: ABC123' }]
1011
+ },
1012
+ custom: {
1013
+ value: '',
1014
+ rules: [{ validator: (v) => v !== 'forbidden' ? undefined : 'This value is not allowed' }]
1015
+ },
1016
+ });
1017
+
1018
+ // FormInstance API
1019
+ form.values; // { email: '', age: '', ... }
1020
+ form.errors; // { email: 'Email is required', ... }
1021
+ form.setValue('email', 'a@b.c'); // update + validate field
1022
+ form.validate(); // validate all, returns boolean
1023
+ form.reset(); // restore initial values
1024
+ form.setError('email', 'Taken'); // set error manually
1025
+ form.clearError('email'); // clear error manually
1026
+ ```
1027
+
1028
+ **FieldRule options:**
1029
+ ```typescript
1030
+ type FieldRule = {
1031
+ required?: boolean; // field must not be empty
1032
+ message?: string; // custom error message
1033
+ type?: 'email' | 'url' | 'number' | 'date'; // format validation
1034
+ minLength?: number; // minimum string length
1035
+ maxLength?: number; // maximum string length
1036
+ minDate?: string; // minimum date (ISO string)
1037
+ maxDate?: string; // maximum date (ISO string)
1038
+ pattern?: RegExp; // regex pattern
1039
+ validator?: (value: any) => string | undefined; // custom validator
1040
+ };
1041
+ ```
1042
+
886
1043
  Syncs state with `localStorage` automatically.
887
1044
 
888
1045
  ```javascript
@@ -0,0 +1,48 @@
1
+ import { default as React } from 'react';
2
+ import { FormInstance } from '../hooks/useForm.hook';
3
+ type FormContextValue = FormInstance & {
4
+ layout?: FormLayout;
5
+ };
6
+ export declare const useFormContext: () => FormContextValue | null;
7
+ export type FormLayout = 'vertical' | 'horizontal' | 'inline';
8
+ /**
9
+ * 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.
10
+ */
11
+ export type FormProps = {
12
+ form: FormInstance;
13
+ onFinish?: (values: Record<string, any>) => void;
14
+ onFinishFailed?: (errors: Record<string, string | undefined>) => void;
15
+ layout?: FormLayout;
16
+ children: React.ReactNode;
17
+ className?: string;
18
+ style?: React.CSSProperties;
19
+ };
20
+ /**
21
+ * 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.
22
+ */
23
+ export type FormItemProps = {
24
+ name?: string;
25
+ label?: React.ReactNode;
26
+ children: React.ReactElement<any>;
27
+ required?: boolean;
28
+ className?: string;
29
+ style?: React.CSSProperties;
30
+ };
31
+ /**
32
+ *
33
+ * @param form The form instance created by the useForm hook, which manages form state and validation.
34
+ * @param onFinish Callback function that is called when the form is successfully submitted and passes validation. It receives the form values as an argument.
35
+ * @param onFinishFailed Callback function that is called when the form submission fails validation. It receives the form errors as an argument.
36
+ * @param layout The layout of the form, which can be 'vertical', 'horizontal', or 'inline'. This affects how the form items are displayed.
37
+ * @param children The form fields (Form.Item components) that will be rendered inside the form.
38
+ * @param className Optional additional class name for styling the form.
39
+ * @param style Optional additional styles for the form container.
40
+ *
41
+ * @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.
42
+ * @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.
43
+ */
44
+ declare const Form: {
45
+ ({ form, onFinish, onFinishFailed, layout, children, className, style }: FormProps): import("react/jsx-runtime").JSX.Element;
46
+ Item: ({ name, label, children, required, className, style }: FormItemProps) => import("react/jsx-runtime").JSX.Element;
47
+ };
48
+ export default Form;
@@ -16,3 +16,4 @@ export { default as Toast } from './Toast';
16
16
  export { default as MultiSelect } from './MultiSelect';
17
17
  export { default as Popconfirm } from './Popconfirm';
18
18
  export { default as QRCode } from './QRCode';
19
+ export { default as Form } from './Form';
@@ -0,0 +1,5 @@
1
+ export * from './useFetch.hook';
2
+ export * from './useLocalStorage.hook';
3
+ export * from './useDebounce.hook';
4
+ export { default as useForm } from './useForm.hook';
5
+ export type { FormInstance, FieldRule, FieldConfig, FormFields } from './useForm.hook';
@@ -0,0 +1,36 @@
1
+ export type FieldRule = {
2
+ required?: boolean;
3
+ message?: string;
4
+ minLength?: number;
5
+ maxLength?: number;
6
+ pattern?: RegExp;
7
+ type?: 'email' | 'url' | 'number' | 'date';
8
+ minDate?: string;
9
+ maxDate?: string;
10
+ validator?: (value: any) => string | undefined;
11
+ };
12
+ export type FieldConfig = {
13
+ value: any;
14
+ rules?: FieldRule[];
15
+ };
16
+ export type FormFields = Record<string, FieldConfig>;
17
+ export type FormValues = Record<string, any>;
18
+ export type FormErrors = Record<string, string | undefined>;
19
+ export type FormInstance = {
20
+ values: FormValues;
21
+ errors: FormErrors;
22
+ setValue: (name: string, value: any) => void;
23
+ validate: () => boolean;
24
+ reset: () => void;
25
+ setError: (name: string, message: string) => void;
26
+ clearError: (name: string) => void;
27
+ };
28
+ /**
29
+ * Hook for managing form state and validation.
30
+ * @param fields
31
+ * @returns
32
+ * 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.
33
+ * 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.
34
+ */
35
+ declare const useForm: (fields: FormFields) => FormInstance;
36
+ export default useForm;
@@ -0,0 +1,3 @@
1
+ export * from './components';
2
+ export * from './utilities';
3
+ export * from './hooks';
@@ -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
- * Checks if a string is a valid email.
2656
- */
2657
- isEmail: (email) => {
2658
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
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 cleanPhone = phone.replace(/\s|-/g, "");
2686
- if (locale === "es-CR") return /^[245678]\d{7}$/.test(cleanPhone);
2687
- return /^\+?[\d\s-]{7,}$/.test(cleanPhone);
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
- export { Accordion, Anchor, Button, Card, DataTable, DropDown, Input, Modal, MultiSelect, Popconfirm, Preloader, ProgressBar, QRCode, ScrollTop, Spinner, Toast, Typed, WhatsApp, apiFetch, del, formatters, get, httpClient, logger, post, put, storage, useDebounce, useFetch, useLocalStorage, validators };
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