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 +224 -60
- package/dist/luna-components-library.js +413 -161
- package/dist/luna-components-library.js.map +1 -1
- package/dist/src/components/Button.d.ts +5 -2
- package/dist/src/components/Form.d.ts +48 -0
- package/dist/src/components/Input.d.ts +4 -1
- 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/styles.d.ts +19 -15
- 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
|
@@ -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
|
|
128
|
+
A versatile button component with multiple variants, sizes, icons, and rounded style.
|
|
129
129
|
|
|
130
130
|
```jsx
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|
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: '
|
|
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
|
|
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
|
|
159
|
-
- `secondary` - Gray background
|
|
165
|
+
- `primary` - Blue background
|
|
166
|
+
- `secondary` - Gray background
|
|
160
167
|
- `outline` - Transparent with border
|
|
161
|
-
- `success` - Green background
|
|
162
|
-
- `danger` - Red background
|
|
163
|
-
- `warning` - Yellow background
|
|
164
|
-
- `info` - Cyan background
|
|
165
|
-
- `dark` - Dark gray background
|
|
166
|
-
- `light` - Light gray background
|
|
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
|
|
636
|
+
A versatile input component with multiple variants, sizes, masks, currency formatting, icons, and required indicator.
|
|
630
637
|
|
|
631
638
|
```jsx
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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
|
|
648
|
-
- `variant?: InputVariant` -
|
|
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` -
|
|
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` -
|
|
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
|
|
674
|
+
- `id?: string` - HTML id for label association
|
|
662
675
|
- `name?: string` - HTML name attribute
|
|
663
|
-
- `classNames?: InputClassNames` - Custom class names
|
|
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"
|
|
669
|
-
- `locale?: string` - Locale for formatting (e.g. "en-US"
|
|
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
|
|
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
|
-
**
|
|
684
|
-
|
|
685
|
-
|
|
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
|
-
###
|
|
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
|
|
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');
|
|
833
|
-
validators.
|
|
834
|
-
validators.
|
|
835
|
-
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
|
|
836
927
|
```
|
|
837
928
|
|
|
838
929
|
### logger
|
|
@@ -873,7 +964,80 @@ function UserList() {
|
|
|
873
964
|
}
|
|
874
965
|
```
|
|
875
966
|
|
|
876
|
-
###
|
|
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
|