luna-components-library 1.1.40 → 1.1.41

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,67 +633,69 @@ 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
@@ -17,7 +17,23 @@ var colors = {
17
17
  bgSelected: "#eff6ff",
18
18
  bgSkeleton: "#f3f4f6",
19
19
  primary: "#2563eb",
20
- dark: "#111827"
20
+ primaryHover: "#1d4ed8",
21
+ secondary: "#4b5563",
22
+ secondaryHover: "#6d737c",
23
+ success: "#16a34a",
24
+ successHover: "#15803d",
25
+ danger: "#dc2626",
26
+ dangerHover: "#b91c1c",
27
+ warning: "#f59e0b",
28
+ warningHover: "#d97706",
29
+ info: "#0ea5e9",
30
+ infoHover: "#0891b2",
31
+ light: "#f9fafb",
32
+ lightHover: "#f3f4f6",
33
+ dark: "#111827",
34
+ darkHover: "#1f2937",
35
+ whatsapp: "#25D366",
36
+ whatsappHover: "#128C7E"
21
37
  };
22
38
  var radii = {
23
39
  sm: "0.375rem",
@@ -212,7 +228,9 @@ var commonStyles = {
212
228
  inputField: {
213
229
  width: "100%",
214
230
  borderRadius: radii.md,
215
- border: `1px solid ${colors.borderInput}`,
231
+ borderStyle: "solid",
232
+ borderWidth: "1px",
233
+ borderColor: colors.borderInput,
216
234
  transition: transitions.fast,
217
235
  outline: "none"
218
236
  },
@@ -380,52 +398,7 @@ var dropDownStyles = (styles, disabled, isOpen, hoverIndex, value) => ({
380
398
  ...styles?.arrow
381
399
  }
382
400
  });
383
- var variantStyles = {
384
- primary: {
385
- backgroundColor: "#2563eb",
386
- color: "#ffffff"
387
- },
388
- secondary: {
389
- backgroundColor: "#4b5563",
390
- color: "#ffffff"
391
- },
392
- outline: {
393
- backgroundColor: "transparent",
394
- color: "#374151",
395
- borderColor: "#d1d5db"
396
- },
397
- success: {
398
- backgroundColor: "#16a34a",
399
- color: "#ffffff"
400
- },
401
- danger: {
402
- backgroundColor: "#dc2626",
403
- color: "#ffffff"
404
- },
405
- warning: {
406
- backgroundColor: "#eab308",
407
- color: "#ffffff"
408
- },
409
- info: {
410
- backgroundColor: "#0891b2",
411
- color: "#ffffff"
412
- },
413
- dark: {
414
- backgroundColor: "#111827",
415
- color: "#ffffff"
416
- },
417
- light: {
418
- backgroundColor: "#f3f4f6",
419
- color: "#111827"
420
- },
421
- link: {
422
- backgroundColor: "transparent",
423
- color: "#2563eb",
424
- border: "none",
425
- padding: 0,
426
- textDecoration: "underline"
427
- }
428
- };
401
+ colors.primary, colors.white, colors.secondary, colors.white, colors.text, colors.borderInput, colors.success, colors.white, colors.danger, colors.white, colors.warning, colors.white, colors.info, colors.white, colors.dark, colors.white, colors.light, colors.dark, colors.primary;
429
402
  var inputStyles = (styles, extraStyle, inputSize, readOnly, disabled) => ({
430
403
  container: {
431
404
  ...commonStyles.inputWrapper,
@@ -448,25 +421,44 @@ var inputStyles = (styles, extraStyle, inputSize, readOnly, disabled) => ({
448
421
  variants: {
449
422
  none: {},
450
423
  primary: {
451
- ...variantStyles.primary,
452
- borderColor: "#2563eb"
424
+ borderColor: colors.primary,
425
+ color: colors.primary
453
426
  },
454
427
  secondary: {
455
- ...variantStyles.secondary,
456
- borderColor: "#4b5563"
428
+ borderColor: colors.secondary,
429
+ color: colors.secondary
457
430
  },
458
431
  outline: {
459
- ...variantStyles.outline,
460
- backgroundColor: "#ffffff",
461
- borderColor: "#d1d5db"
432
+ borderColor: colors.borderInput,
433
+ color: colors.text
462
434
  },
463
435
  danger: {
464
- ...variantStyles.danger,
465
- borderColor: "#dc2626"
436
+ borderColor: colors.danger,
437
+ color: colors.danger
466
438
  },
467
439
  success: {
468
- ...variantStyles.success,
469
- borderColor: "#16a34a"
440
+ borderColor: colors.success,
441
+ color: colors.success
442
+ },
443
+ warning: {
444
+ borderColor: colors.warning,
445
+ color: colors.warning
446
+ },
447
+ info: {
448
+ borderColor: colors.info,
449
+ color: colors.info
450
+ },
451
+ dark: {
452
+ borderColor: colors.dark,
453
+ color: colors.dark
454
+ },
455
+ light: {
456
+ borderColor: colors.light,
457
+ color: colors.light
458
+ },
459
+ link: {
460
+ borderColor: "transparent",
461
+ color: colors.primary
470
462
  }
471
463
  }
472
464
  });
@@ -625,34 +617,34 @@ var preloaderStyles = (zIndex, backgroundColor, size, borderWidth, styles) => ({
625
617
  });
626
618
  var progressBarVariantColors = {
627
619
  primary: {
628
- bg: "#2563eb",
629
- text: "#ffffff",
630
- track: "#e5e7eb"
620
+ bg: colors.primary,
621
+ text: colors.white,
622
+ track: colors.border
631
623
  },
632
624
  success: {
633
- bg: "#16a34a",
634
- text: "#ffffff",
635
- track: "#e5e7eb"
625
+ bg: colors.success,
626
+ text: colors.white,
627
+ track: colors.border
636
628
  },
637
629
  warning: {
638
- bg: "#eab308",
639
- text: "#111827",
640
- track: "#e5e7eb"
630
+ bg: colors.warning,
631
+ text: colors.dark,
632
+ track: colors.border
641
633
  },
642
634
  danger: {
643
- bg: "#dc2626",
644
- text: "#ffffff",
645
- track: "#e5e7eb"
635
+ bg: colors.danger,
636
+ text: colors.white,
637
+ track: colors.border
646
638
  },
647
639
  dark: {
648
- bg: "#1f2937",
649
- text: "#ffffff",
650
- track: "#d1d5db"
640
+ bg: colors.darkHover,
641
+ text: colors.white,
642
+ track: colors.borderInput
651
643
  },
652
644
  light: {
653
- bg: "#f3f4f6",
654
- text: "#111827",
655
- track: "#d1d5db"
645
+ bg: colors.light,
646
+ text: colors.dark,
647
+ track: colors.borderInput
656
648
  }
657
649
  };
658
650
  var progressBarStyles = (styles, percentage, variant) => {
@@ -958,7 +950,7 @@ var whatsAppStyles = (styles, position, size, isHovered, zIndex) => {
958
950
  ...cornerPositionStyles(pos),
959
951
  width: `${currentSize.button}px`,
960
952
  height: `${currentSize.button}px`,
961
- backgroundColor: isHovered ? "#128C7E" : "#25D366",
953
+ backgroundColor: isHovered ? colors.whatsappHover : colors.whatsapp,
962
954
  color: colors.white,
963
955
  zIndex: zIndex ?? 1e3,
964
956
  transition: transitions.normal,
@@ -1010,7 +1002,6 @@ var modalSizeClasses = {
1010
1002
  var modalOverlayClasses = (show, animation, className) => `fixed inset-0 z-60 flex items-center justify-center ${show ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"} ${animation ? "transition-opacity duration-300" : ""} ${className}`.trim();
1011
1003
  var modalDialogClasses = (size, centered, dialogClassName) => `relative w-full ${modalSizeClasses[size]} mx-auto ${centered ? "flex items-center justify-center min-h-screen" : "mt-8"} ${dialogClassName}`.trim();
1012
1004
  var anchorBaseStyles = (variant, isHovered, size) => ({
1013
- ...commonStyles.anchorBase,
1014
1005
  textDecoration: variant === "none" ? isHovered ? "underline" : "none" : "none",
1015
1006
  ...variant !== "none" ? sizeStyles[size] : {},
1016
1007
  borderRadius: variant === "none" ? "0" : radii.md
@@ -1045,40 +1036,40 @@ var accordionStyles = (isActive, styles) => ({
1045
1036
  var standardVariantStyles = (isHovered) => ({
1046
1037
  none: { color: colors.primary },
1047
1038
  primary: {
1048
- backgroundColor: isHovered ? "#1d4ed8" : "#2563eb",
1049
- color: isHovered ? "black" : "white"
1039
+ backgroundColor: isHovered ? colors.primaryHover : colors.primary,
1040
+ color: colors.white
1050
1041
  },
1051
1042
  secondary: {
1052
- backgroundColor: isHovered ? "#6d737c" : "#4b5563",
1053
- color: isHovered ? "black" : "white"
1043
+ backgroundColor: isHovered ? colors.secondaryHover : colors.secondary,
1044
+ color: colors.white
1054
1045
  },
1055
1046
  outline: {
1056
- backgroundColor: isHovered ? "lightgray" : "white",
1057
- color: isHovered ? "white" : "black"
1047
+ backgroundColor: isHovered ? colors.lightHover : colors.white,
1048
+ color: colors.dark
1058
1049
  },
1059
1050
  danger: {
1060
- backgroundColor: isHovered ? "#b91c1c" : "#dc2626",
1061
- color: isHovered ? "black" : "white"
1051
+ backgroundColor: isHovered ? colors.dangerHover : colors.danger,
1052
+ color: colors.white
1062
1053
  },
1063
1054
  success: {
1064
- backgroundColor: isHovered ? "#15803d" : "#16a34a",
1065
- color: isHovered ? "black" : "white"
1055
+ backgroundColor: isHovered ? colors.successHover : colors.success,
1056
+ color: colors.white
1066
1057
  },
1067
1058
  warning: {
1068
- backgroundColor: isHovered ? "#d97706" : "#f59e0b",
1069
- color: isHovered ? "black" : "white"
1059
+ backgroundColor: isHovered ? colors.warningHover : colors.warning,
1060
+ color: colors.white
1070
1061
  },
1071
1062
  info: {
1072
- backgroundColor: isHovered ? "#0891b2" : "#0ea5e9",
1073
- color: isHovered ? "black" : "white"
1063
+ backgroundColor: isHovered ? colors.infoHover : colors.info,
1064
+ color: colors.white
1074
1065
  },
1075
1066
  dark: {
1076
- backgroundColor: isHovered ? "#1f2937" : "#111827",
1077
- color: isHovered ? "black" : "white"
1067
+ backgroundColor: isHovered ? colors.darkHover : colors.dark,
1068
+ color: colors.white
1078
1069
  },
1079
1070
  light: {
1080
- backgroundColor: isHovered ? "#f3f4f6" : "#f9fafb",
1081
- color: isHovered ? "black" : "white"
1071
+ backgroundColor: isHovered ? colors.lightHover : colors.light,
1072
+ color: colors.dark
1082
1073
  },
1083
1074
  link: { color: isHovered ? colors.primary : colors.text }
1084
1075
  });
@@ -1121,7 +1112,7 @@ var require_react_jsx_runtime_production = /* @__PURE__ */ __commonJSMin(((expor
1121
1112
  var import_jsx_runtime = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
1122
1113
  module.exports = require_react_jsx_runtime_production();
1123
1114
  })))();
1124
- var Button = ({ children, variant = "primary", size = "sm", onClick, disabled = false, classNames = {}, styles = {}, className = "", style: extraStyle = {}, ...props }) => {
1115
+ var Button = ({ children, variant = "primary", size = "sm", onClick, disabled = false, rounded = false, icon, iconPosition = "left", classNames = {}, styles = {}, className = "", style: extraStyle = {}, ...props }) => {
1125
1116
  const [isHovered, setIsHovered] = useState(false);
1126
1117
  const finalClassNames = {
1127
1118
  container: "",
@@ -1134,6 +1125,13 @@ var Button = ({ children, variant = "primary", size = "sm", onClick, disabled =
1134
1125
  ...commonStyles.buttonBase,
1135
1126
  cursor: disabled ? "not-allowed" : "pointer",
1136
1127
  opacity: disabled ? .5 : 1,
1128
+ borderRadius: rounded ? "9999px" : "5px",
1129
+ boxShadow: "0 2px 4px rgba(0,0,0,0.15), 0 2px 3px rgba(0,0,0,0.08)",
1130
+ ...icon ? {
1131
+ display: "inline-flex",
1132
+ alignItems: "center",
1133
+ gap: "0.4em"
1134
+ } : {},
1137
1135
  ...sizeStyles[size]
1138
1136
  };
1139
1137
  const uiStyles = { variants: standardVariantStyles(isHovered) };
@@ -1142,7 +1140,7 @@ var Button = ({ children, variant = "primary", size = "sm", onClick, disabled =
1142
1140
  ...uiStyles.variants[variant],
1143
1141
  ...extraStyle
1144
1142
  };
1145
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
1143
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
1146
1144
  className: [
1147
1145
  finalClassNames.container,
1148
1146
  sizeClasses[size],
@@ -1156,7 +1154,11 @@ var Button = ({ children, variant = "primary", size = "sm", onClick, disabled =
1156
1154
  onMouseEnter: () => setIsHovered(true),
1157
1155
  onMouseLeave: () => setIsHovered(false),
1158
1156
  ...props,
1159
- children
1157
+ children: [
1158
+ icon && iconPosition === "left" && icon,
1159
+ children,
1160
+ icon && iconPosition === "right" && icon
1161
+ ]
1160
1162
  });
1161
1163
  };
1162
1164
  //#endregion
@@ -1180,7 +1182,7 @@ var Card = ({ children, title, className = "", padding = "md", shadow = "md", st
1180
1182
  };
1181
1183
  //#endregion
1182
1184
  //#region src/components/Anchor.tsx
1183
- var Anchor = ({ children, variant = "none", size = "md", href = "https://andreychaconresumereact.netlify.app/", className = "", target, rel, style = {}, ...props }) => {
1185
+ var Anchor = ({ children, variant = "none", size = "md", href = "https://andreychaconresumereact.netlify.app/", className = "", target = "_blank", rel = "noopener noreferrer", style = {}, ...props }) => {
1184
1186
  const [isHovered, setIsHovered] = useState(false);
1185
1187
  const combinedClassName = `luna-anchor ${className}`.trim();
1186
1188
  const uiStyles = {
@@ -1719,7 +1721,20 @@ var Modal = ({ show, onHide, size = "md", centered = false, backdrop = true, bac
1719
1721
  };
1720
1722
  //#endregion
1721
1723
  //#region src/components/Input.tsx
1722
- var Input = ({ children, variant = "none", type = "text", inputSize = "md", placeholder, value: controlledValue, onChange, onFocus, onBlur, disabled = false, required = false, readOnly = false, id, name, mask, maskChar = "_", useCurrency = false, currency = "USD", locale = "en-US", minFractionDigits = 0, maxFractionDigits = 2, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, classNames, styles, className: extraClassName = "", style: extraStyle = {}, ...props }) => {
1724
+ var variantBorderColors = {
1725
+ none: colors.borderInput,
1726
+ primary: colors.primary,
1727
+ secondary: colors.secondary,
1728
+ outline: colors.borderInput,
1729
+ danger: colors.danger,
1730
+ success: colors.success,
1731
+ warning: colors.warning,
1732
+ info: colors.info,
1733
+ dark: colors.dark,
1734
+ light: colors.light,
1735
+ link: "transparent"
1736
+ };
1737
+ var Input = ({ children, variant = "none", type = "text", inputSize = "md", placeholder, value: controlledValue, onChange, onFocus, onBlur, disabled = false, required = false, isRequired = false, icon, iconPosition = "left", readOnly = false, id, name, mask, maskChar = "_", useCurrency = false, currency = "USD", locale = "en-US", minFractionDigits = 0, maxFractionDigits = 2, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, classNames, styles, className: extraClassName = "", style: extraStyle = {}, ...props }) => {
1723
1738
  const inputId = id || name;
1724
1739
  const finalClassNames = {
1725
1740
  container: "luna-input",
@@ -1728,6 +1743,7 @@ var Input = ({ children, variant = "none", type = "text", inputSize = "md", plac
1728
1743
  classNames
1729
1744
  };
1730
1745
  const [internalValue, setInternalValue] = useState(controlledValue || "");
1746
+ const [isFocused, setIsFocused] = useState(false);
1731
1747
  useEffect(() => {
1732
1748
  if (controlledValue !== void 0) setInternalValue(controlledValue);
1733
1749
  }, [controlledValue]);
@@ -1789,6 +1805,7 @@ var Input = ({ children, variant = "none", type = "text", inputSize = "md", plac
1789
1805
  onChange?.(newVal);
1790
1806
  };
1791
1807
  const handleBlur = () => {
1808
+ setIsFocused(false);
1792
1809
  if (useCurrency && internalValue) {
1793
1810
  const formatted = formatCurrency(internalValue);
1794
1811
  if (controlledValue === void 0) setInternalValue(formatted);
@@ -1797,35 +1814,81 @@ var Input = ({ children, variant = "none", type = "text", inputSize = "md", plac
1797
1814
  onBlur?.();
1798
1815
  };
1799
1816
  const uiStyles = inputStyles(styles, extraStyle, inputSize, readOnly, disabled);
1817
+ const iconPadding = icon ? "2.25rem" : void 0;
1800
1818
  const finalInputStyle = {
1801
1819
  ...uiStyles.input,
1802
- ...uiStyles.variants[variant]
1820
+ ...uiStyles.variants[variant],
1821
+ borderStyle: "solid",
1822
+ borderWidth: isFocused ? "2px" : "1px",
1823
+ borderColor: variantBorderColors[variant],
1824
+ ...icon && iconPosition === "left" ? { paddingLeft: iconPadding } : {},
1825
+ ...icon && iconPosition === "right" ? { paddingRight: iconPadding } : {}
1803
1826
  };
1804
1827
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1805
1828
  className: `${finalClassNames.container || ""} ${extraClassName}`.trim(),
1806
1829
  style: uiStyles.container,
1807
- children: [children && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
1830
+ children: [children && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
1808
1831
  htmlFor: inputId,
1809
1832
  className: finalClassNames.label,
1810
1833
  style: uiStyles.label,
1811
- children
1812
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
1813
- id: inputId,
1814
- name,
1815
- type: useCurrency ? "text" : type,
1816
- placeholder: placeholder || (mask ? mask.replace(/[9a*]/g, maskChar) : ""),
1817
- value: internalValue,
1818
- onChange: handleChange,
1819
- onFocus,
1820
- onBlur: handleBlur,
1821
- disabled,
1822
- required,
1823
- readOnly,
1824
- "aria-label": ariaLabel,
1825
- "aria-labelledby": ariaLabelledby,
1826
- style: finalInputStyle,
1827
- className: `${finalClassNames.input || ""} rounded-xl`.trim(),
1828
- ...props
1834
+ children: [children, isRequired && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1835
+ style: {
1836
+ color: colors.danger,
1837
+ marginLeft: "0.25rem"
1838
+ },
1839
+ children: "*"
1840
+ })]
1841
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1842
+ style: {
1843
+ position: "relative",
1844
+ display: "flex",
1845
+ alignItems: "center"
1846
+ },
1847
+ children: [
1848
+ icon && iconPosition === "left" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1849
+ style: {
1850
+ position: "absolute",
1851
+ left: "0.65rem",
1852
+ display: "flex",
1853
+ alignItems: "center",
1854
+ color: colors.textDisabled,
1855
+ pointerEvents: "none"
1856
+ },
1857
+ children: icon
1858
+ }),
1859
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
1860
+ id: inputId,
1861
+ name,
1862
+ type: useCurrency ? "text" : type,
1863
+ placeholder: placeholder || (mask ? mask.replace(/[9a*]/g, maskChar) : ""),
1864
+ value: internalValue,
1865
+ onChange: handleChange,
1866
+ onFocus: () => {
1867
+ setIsFocused(true);
1868
+ onFocus?.();
1869
+ },
1870
+ onBlur: handleBlur,
1871
+ disabled,
1872
+ required,
1873
+ readOnly,
1874
+ "aria-label": ariaLabel,
1875
+ "aria-labelledby": ariaLabelledby,
1876
+ style: finalInputStyle,
1877
+ className: `${finalClassNames.input || ""} rounded-xl`.trim(),
1878
+ ...props
1879
+ }),
1880
+ icon && iconPosition === "right" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1881
+ style: {
1882
+ position: "absolute",
1883
+ right: "0.65rem",
1884
+ display: "flex",
1885
+ alignItems: "center",
1886
+ color: colors.textDisabled,
1887
+ pointerEvents: "none"
1888
+ },
1889
+ children: icon
1890
+ })
1891
+ ]
1829
1892
  })]
1830
1893
  });
1831
1894
  };