luna-components-library 1.1.40 → 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 CHANGED
@@ -125,28 +125,35 @@ const isValid = validators.isEmail('user@luna.com'); // true
125
125
  All components use TypeScript with specific types for better type safety and IntelliSense. The library follows a minimal documentation approach with descriptive type names instead of extensive JSDoc comments.
126
126
 
127
127
  ### Button
128
- A versatile button component with multiple variants and sizes.
128
+ A versatile button component with multiple variants, sizes, icons, and rounded style.
129
129
 
130
130
  ```jsx
131
- <Button
132
- variant="primary"
133
- size="md"
134
- onClick={handleClick}
135
- disabled={false}
136
- >
137
- Button Text
138
- </Button>
131
+ // Basic
132
+ <Button variant="primary" size="md" onClick={handleClick}>Click Me</Button>
133
+
134
+ // Rounded (pill)
135
+ <Button variant="primary" rounded>Rounded</Button>
136
+
137
+ // With icon
138
+ <Button variant="primary" icon="🚀">Deploy</Button>
139
+ <Button variant="outline" icon="→" iconPosition="right">Next</Button>
140
+
141
+ // Icon only
142
+ <Button variant="danger" rounded icon="✕" />
139
143
  ```
140
144
 
141
145
  **Props:**
142
- - `children`: React.ReactNode - Button content
146
+ - `children?: React.ReactNode` - Button content (optional when using icon only)
143
147
  - `variant?: ButtonVariant` - Button style (default: 'primary')
144
- - `size?: ButtonSize` - Button size (default: 'md')
148
+ - `size?: ButtonSize` - Button size (default: 'sm')
149
+ - `rounded?: boolean` - Pill/circle shape (default: false)
150
+ - `icon?: React.ReactNode` - Icon element, emoji, or any ReactNode
151
+ - `iconPosition?: 'left' | 'right'` - Icon placement (default: 'left')
145
152
  - `onClick?: React.MouseEventHandler<HTMLButtonElement>` - Click handler
146
153
  - `disabled?: boolean` - Disable button (default: false)
147
154
  - `className?: string` - Additional CSS classes
148
155
  - `style?: React.CSSProperties` - Custom inline styles
149
- - `...props`: any - Additional HTML button attributes (spreads all native button props)
156
+ - `...props`: any - Additional HTML button attributes
150
157
 
151
158
  **Types:**
152
159
  ```typescript
@@ -155,15 +162,15 @@ type ButtonSize = 'sm' | 'md' | 'lg';
155
162
  ```
156
163
 
157
164
  **Variants:**
158
- - `primary` - Blue background button
159
- - `secondary` - Gray background button
165
+ - `primary` - Blue background
166
+ - `secondary` - Gray background
160
167
  - `outline` - Transparent with border
161
- - `success` - Green background button
162
- - `danger` - Red background button
163
- - `warning` - Yellow background button
164
- - `info` - Cyan background button
165
- - `dark` - Dark gray background button
166
- - `light` - Light gray background button
168
+ - `success` - Green background
169
+ - `danger` - Red background
170
+ - `warning` - Yellow background
171
+ - `info` - Cyan background
172
+ - `dark` - Dark gray background
173
+ - `light` - Light gray background
167
174
  - `link` - Blue text link with hover effects
168
175
 
169
176
  ### Card
@@ -626,74 +633,148 @@ const MyComponent = () => {
626
633
  ```
627
634
 
628
635
  ### Input
629
- A versatile input component with multiple variants, sizes, masks, currency formatting, and types.
636
+ A versatile input component with multiple variants, sizes, masks, currency formatting, icons, and required indicator.
630
637
 
631
638
  ```jsx
632
- <Input
633
- inputSize="md"
634
- variant="primary"
635
- type="text"
636
- placeholder="Enter your text here"
637
- value={inputValue}
638
- onChange={(value) => setInputValue(value)}
639
- className="custom-class"
640
- id="my-input"
641
- >
642
- Input Label
643
- </Input>
639
+ // Basic
640
+ <Input variant="primary" placeholder="Enter text">Label</Input>
641
+
642
+ // With icon
643
+ <Input icon="🔍" placeholder="Search...">Search</Input>
644
+ <Input icon="🔒" iconPosition="right" variant="danger" placeholder="Password">Password</Input>
645
+
646
+ // Required indicator
647
+ <Input isRequired variant="primary" placeholder="Email">Email</Input>
648
+
649
+ // Currency
650
+ <Input useCurrency currency="USD" locale="en-US" placeholder="$0.00">Price</Input>
651
+
652
+ // Mask
653
+ <Input mask="(999) 999-9999" placeholder="(555) 000-0000">Phone</Input>
644
654
  ```
645
655
 
646
656
  **Props:**
647
- - `children?: React.ReactNode` - Label content for the input
648
- - `variant?: InputVariant` - Input style variant (default: 'none')
657
+ - `children?: React.ReactNode` - Label content
658
+ - `variant?: InputVariant` - Border color variant (default: 'none')
649
659
  - `inputSize?: InputSize` - Input size (default: 'md')
650
660
  - `type?: InputType` - HTML input type (default: 'text')
651
661
  - `placeholder?: string` - Placeholder text
652
- - `value?: string` - Input value
662
+ - `value?: string` - Controlled value
653
663
  - `onChange?: (value: string) => void` - Change handler
654
664
  - `onFocus?: () => void` - Focus handler
655
665
  - `onBlur?: () => void` - Blur handler
656
666
  - `disabled?: boolean` - Disable input (default: false)
657
- - `required?: boolean` - Required field (default: false)
667
+ - `required?: boolean` - HTML required attribute (default: false)
668
+ - `isRequired?: boolean` - Shows a red `*` next to the label (default: false)
658
669
  - `readOnly?: boolean` - Read-only input (default: false)
670
+ - `icon?: React.ReactNode` - Icon inside the input (emoji, SVG, component)
671
+ - `iconPosition?: 'left' | 'right'` - Icon placement (default: 'left')
659
672
  - `className?: string` - Additional CSS classes
660
673
  - `style?: React.CSSProperties` - Custom inline styles
661
- - `id?: string` - HTML id attribute for label association
674
+ - `id?: string` - HTML id for label association
662
675
  - `name?: string` - HTML name attribute
663
- - `classNames?: InputClassNames` - Custom class names for sub-elements
664
- - `styles?: InputStyles` - Custom inline styles per element
665
- - `mask?: string` - Input mask pattern (e.g. "(999) 999-9999")
666
- - `maskChar?: string` - Mask placeholder character (default: '_')
676
+ - `classNames?: InputClassNames` - Custom class names per sub-element
677
+ - `styles?: InputStyles` - Custom inline styles per sub-element
678
+ - `mask?: string` - Input mask pattern (e.g. `"(999) 999-9999"`)
679
+ - `maskChar?: string` - Mask placeholder character (default: `'_'`)
667
680
  - `useCurrency?: boolean` - Enable currency formatting
668
- - `currency?: string` - Currency code (e.g. "USD", "CRC")
669
- - `locale?: string` - Locale for formatting (e.g. "en-US", "es-CR")
681
+ - `currency?: string` - Currency code (e.g. `"USD"`, `"CRC"`)
682
+ - `locale?: string` - Locale for formatting (e.g. `"en-US"`, `"es-CR"`)
670
683
  - `minFractionDigits?: number` - Minimum fraction digits (default: 0)
671
684
  - `maxFractionDigits?: number` - Maximum fraction digits (default: 2)
672
- - `aria-label?: string` - ARIA label for accessibility
685
+ - `aria-label?: string` - ARIA label
673
686
  - `aria-labelledby?: string` - ARIA labelledby
674
687
  - `...props`: any - Additional HTML input attributes
675
688
 
676
689
  **Types:**
677
690
  ```typescript
678
- type InputVariant = 'none' | 'primary' | 'secondary' | 'outline' | 'danger' | 'success';
691
+ type InputVariant = 'none' | 'primary' | 'secondary' | 'outline' | 'danger' | 'success' | 'warning' | 'info' | 'dark' | 'light' | 'link';
679
692
  type InputSize = 'sm' | 'md' | 'lg' | 'xl';
680
693
  type InputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'date' | 'time' | 'datetime-local' | 'month' | 'week' | 'color' | 'file' | 'hidden' | 'image' | 'range' | 'reset' | 'submit';
681
694
  ```
682
695
 
683
- **Variants:**
684
- - `none` - Default styling with border
685
- - `primary` - Blue focus ring
686
- - `secondary` - Gray focus ring
687
- - `outline` - Border only
688
- - `danger` - Red focus ring
689
- - `success` - Green focus ring
696
+ **Focus behavior:** Border increases from `1px` to `2px` on focus, using the variant color. On blur it returns to `1px` with the same variant color.
697
+
698
+ **Variants:** Each variant applies a distinct border color — `primary` (blue), `secondary` (gray), `danger` (red), `success` (green), `warning` (yellow), `info` (cyan), `dark`, `light`, `outline` (default gray), `none` (default gray).
690
699
 
691
700
  **Size Options:**
692
701
  - `sm` - Small padding and text
693
702
  - `md` - Medium padding and text
694
703
  - `lg` - Large padding and text
695
704
  - `xl` - Extra large padding and text
696
- ### DataTable
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
+
697
778
  A powerful and customizable data grid with support for filtering, sorting, pagination, selection, and search.
698
779
 
699
780
  ```jsx
@@ -824,15 +905,25 @@ const text = formatters.truncate('Long text here...', 10); // "Long text..."
824
905
  ```
825
906
 
826
907
  ### validators
827
- Common validation rules.
908
+ Common validation functions, shared internally with `useForm`.
828
909
 
829
910
  ```javascript
830
911
  import { validators } from 'luna-components-library';
831
912
 
832
- validators.isEmail('test@example.com'); // true
833
- validators.isEmpty(' '); // true
834
- validators.isStrongPassword('Pass1234'); // true
835
- validators.isPhone('88888888', 'es-CR'); // true
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
836
927
  ```
837
928
 
838
929
  ### logger
@@ -873,7 +964,80 @@ function UserList() {
873
964
  }
874
965
  ```
875
966
 
876
- ### useLocalStorage
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
+
877
1041
  Syncs state with `localStorage` automatically.
878
1042
 
879
1043
  ```javascript