luna-components-library 1.1.31 → 1.1.33

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
@@ -801,6 +801,9 @@ A versatile input component with multiple variants, sizes, and types.
801
801
  - `variantClassName?: string` - CSS classes for variant styling
802
802
  - `sizeClassName?: string` - CSS classes for size styling
803
803
  - `style?: React.CSSProperties` - Custom inline styles
804
+ - `id?: string` - HTML id attribute for label association
805
+ - `aria-label?: string` - ARIA label for accessibility
806
+ - `aria-labelledby?: string` - ARIA labelledby for accessibility
804
807
  - `...props`: any - Additional HTML input attributes (spreads all native input props)
805
808
 
806
809
  **Types:**
@@ -824,8 +827,132 @@ type InputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'sea
824
827
  - `lg` - Large padding and text
825
828
  - `xl` - Extra large padding and text
826
829
 
830
+
831
+ ## 🛠️ Utilities & Hooks
832
+
833
+ Luna Library now includes generic utilities and hooks to streamline API communication and state management.
834
+
835
+ ### httpClient
836
+ A generic HTTP client wrapper for the Fetch API with support for standard HTTP methods. It automatically handles JSON stringification for POST/PUT requests and sets appropriate headers.
837
+
838
+ ```javascript
839
+ import { httpClient } from 'luna-components-library';
840
+
841
+ // GET request
842
+ const data = await httpClient.get('https://api.example.com/data');
843
+
844
+ // POST request
845
+ const response = await httpClient.post('https://api.example.com/posts', {
846
+ title: 'New Post',
847
+ body: 'Content here'
848
+ });
849
+
850
+ // httpClient.delete(url, options)
851
+ ```
852
+
853
+ ### storage
854
+ A wrapper for `localStorage` with safety checks and automatic JSON parsing.
855
+
856
+ ```javascript
857
+ import { storage } from 'luna-components-library';
858
+
859
+ storage.set('user-theme', 'dark');
860
+ const theme = storage.get('user-theme', 'light'); // 'dark'
861
+ storage.remove('user-theme');
862
+ storage.clear();
863
+ ```
864
+
865
+ ### formatters
866
+ Useful functions for data presentation.
867
+
868
+ ```javascript
869
+ import { formatters } from 'luna-components-library';
870
+
871
+ const price = formatters.currency(1500.50); // "$1,500.50"
872
+ const date = formatters.date(new Date()); // "May 14, 2026"
873
+ const text = formatters.truncate('Long text here...', 10); // "Long text..."
874
+ ```
875
+
876
+ ### validators
877
+ Common validation rules.
878
+
879
+ ```javascript
880
+ import { validators } from 'luna-components-library';
881
+
882
+ validators.isEmail('test@example.com'); // true
883
+ validators.isEmpty(' '); // true
884
+ validators.isStrongPassword('Pass1234'); // true
885
+ ```
886
+
887
+ ### logger
888
+ Styled console logging for better debugging in development.
889
+
890
+ ```javascript
891
+ import { logger } from 'luna-components-library';
892
+
893
+ logger.info('App started');
894
+ logger.success('User logged in');
895
+ logger.warn('Low disk space');
896
+ logger.error('API failed', error);
897
+ ```
898
+ ```
899
+
900
+ ### useFetch
901
+ A powerful custom hook for performing data fetching. It manages `data`, `error`, and `loading` states automatically and includes built-in `AbortController` support to prevent memory leaks and race conditions.
902
+
903
+ **Options:**
904
+ - `delay?: number` - Optional delay in milliseconds before performing the fetch (useful for simulating slow networks or testing loading states).
905
+
906
+ ```javascript
907
+ import { useFetch, Spinner } from 'luna-components-library';
908
+
909
+ function UserList() {
910
+ // Fetch with an artificial delay of 2 seconds
911
+ const { data, error, loading } = useFetch('https://api.example.com/users', { delay: 2000 });
912
+
913
+ if (loading) return <Spinner />;
914
+ if (error) return <div>Error: {error}</div>;
915
+
916
+ return (
917
+ <ul>
918
+ {data?.map(user => (
919
+ <li key={user.id}>{user.name}</li>
920
+ ))}
921
+ </ul>
922
+ );
923
+ }
924
+
925
+ ### useLocalStorage
926
+ Syncs state with `localStorage` automatically.
927
+
928
+ ```javascript
929
+ import { useLocalStorage } from 'luna-components-library';
930
+
931
+ function ThemeToggle() {
932
+ const [theme, setTheme] = useLocalStorage('theme', 'light');
933
+ return <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>{theme}</button>;
934
+ }
935
+ ```
936
+
937
+ ### useDebounce
938
+ Delays value updates until a specified time has passed.
939
+
940
+ ```javascript
941
+ import { useDebounce } from 'luna-components-library';
942
+
943
+ function Search() {
944
+ const [query, setQuery] = useState('');
945
+ const debouncedQuery = useDebounce(query, 500);
946
+
947
+ useEffect(() => {
948
+ // Perform search only when user stops typing
949
+ }, [debouncedQuery]);
950
+ }
951
+ ```
952
+
827
953
  ## 🛠️ Development
828
954
 
955
+
829
956
  ### Prerequisites
830
957
  - Node.js 16+
831
958
  - npm, yarn, or pnpm
@@ -856,17 +983,14 @@ npm run clean
856
983
  luna-library/
857
984
  ├── src/
858
985
  │ ├── components/
859
- │ │ ├── Button.tsx
860
- │ │ ├── Card.tsx
861
- ├── Anchor.tsx
862
- │ │ ├── Accordion.tsx
863
- │ │ ├── Spinner.tsx
864
- ├── DropDown.tsx
865
- │ │ ├── ProgressBar.tsx
866
- │ │ ├── Preloader.tsx
867
- │ │ ├── ScrollTop.tsx
868
- │ │ ├── WhatsApp.tsx
869
- │ │ ├── Input.tsx
986
+ │ │ ├── ... (Button, Card, Modal, etc.)
987
+ │ │ └── index.ts
988
+ │ ├── hooks/
989
+ │ │ ├── useFetch.hook.ts
990
+ │ │ └── index.ts
991
+ │ ├── utilities/
992
+ │ │ ├── apiFetch.util.ts
993
+ │ │ ├── httpClient.util.ts
870
994
  │ │ └── index.ts
871
995
  │ └── index.ts
872
996
  ├── dist/ # Build output
@@ -103,7 +103,7 @@ var Card = ({ children, title, className = "", containerClassName = "bg-white ro
103
103
  };
104
104
  //#endregion
105
105
  //#region src/components/Anchor.tsx
106
- var Anchor = ({ children = "Pablo Andrey Chacon Luna", variant = "none", size = "sm", href = "https://andreychaconresumereact.netlify.app/", className, containerClassName = "font-medium rounded-lg transition-colors focus:outline-none focus:ring-2", variantClassName = "bg-blue-600 text-white hover:bg-blue-700", sizeClassName = "px-3 py-1.5 text-sm", target = "_blank", rel = "noopener noreferrer", style, ...props }) => {
106
+ var Anchor = ({ children = "Pablo Andrey Chacon Luna", variant = "none", size = "sm", href = "https://andreychaconresumereact.netlify.app/", className, containerClassName = variant === "none" ? "font-medium transition-colors focus:outline-none" : "font-medium rounded-lg transition-colors focus:outline-none focus:ring-2", variantClassName = "bg-blue-600 text-white hover:bg-blue-700", sizeClassName = "px-3 py-1.5 text-sm", target = "_blank", rel = "noopener noreferrer", style, ...props }) => {
107
107
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
108
108
  href,
109
109
  target,
@@ -744,7 +744,7 @@ var Modal = ({ show, onHide, size = "md", centered = false, backdrop = true, bac
744
744
  };
745
745
  //#endregion
746
746
  //#region src/components/Input.tsx
747
- var Input = ({ children, variant = "none", type = "text", inputSize = "md", placeholder, value, onChange, onFocus, onBlur, disabled = false, required = false, readOnly = false, className = "", containerClassName = "relative inline-block", inputClassName = "border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500", variantClassName = "", sizeClassName = "", style, ...props }) => {
747
+ var Input = ({ children, variant = "none", type = "text", inputSize = "md", placeholder, value, onChange, onFocus, onBlur, disabled = false, required = false, readOnly = false, className = "", containerClassName = "relative inline-block", inputClassName = "border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500", variantClassName = "", sizeClassName = "", style, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...props }) => {
748
748
  const baseClasses = containerClassName;
749
749
  const variantClasses = {
750
750
  none: "",
@@ -773,9 +773,11 @@ var Input = ({ children, variant = "none", type = "text", inputSize = "md", plac
773
773
  `.trim(),
774
774
  style,
775
775
  children: [children && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
776
+ htmlFor: id,
776
777
  className: "block text-sm font-medium text-gray-700 mb-1",
777
778
  children
778
779
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
780
+ id,
779
781
  type,
780
782
  placeholder,
781
783
  value,
@@ -785,6 +787,8 @@ var Input = ({ children, variant = "none", type = "text", inputSize = "md", plac
785
787
  disabled,
786
788
  required,
787
789
  readOnly,
790
+ "aria-label": ariaLabel,
791
+ "aria-labelledby": ariaLabelledby,
788
792
  className: `
789
793
  ${inputClassName}
790
794
  ${variantClasses[variant]}
@@ -797,6 +801,322 @@ var Input = ({ children, variant = "none", type = "text", inputSize = "md", plac
797
801
  });
798
802
  };
799
803
  //#endregion
800
- export { Accordion, Anchor, Button, Card, DropDown, Input, Modal, Preloader, ProgressBar, ScrollTop, Spinner, Typed, WhatsApp };
804
+ //#region src/utilities/apiFetch.util.ts
805
+ /**
806
+ * A generic wrapper for the fetch API with error handling and response parsing.
807
+ * @param url - The URL to fetch
808
+ * @param options - Fetch options (method, headers, body, signal, etc.)
809
+ * @returns Parsed JSON response
810
+ */
811
+ var apiFetch = async (url, options) => {
812
+ try {
813
+ const response = await fetch(url, options);
814
+ if (!response.ok) throw new Error(`HTTP error! Status: ${response.status} - ${response.statusText}`);
815
+ return await response.json();
816
+ } catch (error) {
817
+ if (error instanceof DOMException && error.name === "AbortError") {
818
+ console.log("Fetch aborted");
819
+ return;
820
+ }
821
+ if (error instanceof Error) throw new Error(error.message);
822
+ throw new Error(String(error));
823
+ }
824
+ };
825
+ //#endregion
826
+ //#region src/utilities/httpClient.util.ts
827
+ /**
828
+ * Generic GET request
829
+ * @template T - Expected return type
830
+ * @param url - Full URL or endpoint
831
+ * @param options - Additional fetch options
832
+ */
833
+ var get = async (url, options) => {
834
+ return apiFetch(url, {
835
+ ...options,
836
+ method: "GET"
837
+ });
838
+ };
839
+ /**
840
+ * Generic POST request
841
+ * @template T - Expected return type
842
+ * @param url - Full URL or endpoint
843
+ * @param body - Data to send
844
+ * @param options - Additional fetch options
845
+ */
846
+ var post = async (url, body, options) => {
847
+ return apiFetch(url, {
848
+ ...options,
849
+ method: "POST",
850
+ body: JSON.stringify(body),
851
+ headers: {
852
+ "Content-Type": "application/json",
853
+ ...options?.headers
854
+ }
855
+ });
856
+ };
857
+ /**
858
+ * Generic PUT request
859
+ * @template T - Expected return type
860
+ * @param url - Full URL or endpoint
861
+ * @param body - Data to send
862
+ * @param options - Additional fetch options
863
+ */
864
+ var put = async (url, body, options) => {
865
+ return apiFetch(url, {
866
+ ...options,
867
+ method: "PUT",
868
+ body: JSON.stringify(body),
869
+ headers: {
870
+ "Content-Type": "application/json",
871
+ ...options?.headers
872
+ }
873
+ });
874
+ };
875
+ /**
876
+ * Generic DELETE request
877
+ * @template T - Expected return type
878
+ * @param url - Full URL or endpoint
879
+ * @param options - Additional fetch options
880
+ */
881
+ var del = async (url, options) => {
882
+ return apiFetch(url, {
883
+ ...options,
884
+ method: "DELETE"
885
+ });
886
+ };
887
+ var httpClient = {
888
+ get,
889
+ post,
890
+ put,
891
+ delete: del
892
+ };
893
+ //#endregion
894
+ //#region src/utilities/storage.util.ts
895
+ /**
896
+ * Utility for interacting with localStorage safely.
897
+ */
898
+ var storage = {
899
+ get: (key, defaultValue) => {
900
+ try {
901
+ const item = window.localStorage.getItem(key);
902
+ return item ? JSON.parse(item) : defaultValue;
903
+ } catch (error) {
904
+ console.error(`Error reading key "${key}" from storage:`, error);
905
+ return defaultValue;
906
+ }
907
+ },
908
+ set: (key, value) => {
909
+ try {
910
+ window.localStorage.setItem(key, JSON.stringify(value));
911
+ } catch (error) {
912
+ console.error(`Error writing key "${key}" to storage:`, error);
913
+ }
914
+ },
915
+ remove: (key) => {
916
+ try {
917
+ window.localStorage.removeItem(key);
918
+ } catch (error) {
919
+ console.error(`Error removing key "${key}" from storage:`, error);
920
+ }
921
+ },
922
+ clear: () => {
923
+ try {
924
+ window.localStorage.clear();
925
+ } catch (error) {
926
+ console.error("Error clearing storage:", error);
927
+ }
928
+ }
929
+ };
930
+ //#endregion
931
+ //#region src/utilities/formatters.util.ts
932
+ /**
933
+ * Utility functions for formatting data.
934
+ */
935
+ var formatters = {
936
+ /**
937
+ * Formats a number as currency.
938
+ * @param value - Number to format
939
+ * @param locale - Locale (default: 'en-US')
940
+ * @param currency - Currency code (default: 'USD')
941
+ */
942
+ currency: (value, locale = "en-US", currency = "USD") => {
943
+ return new Intl.NumberFormat(locale, {
944
+ style: "currency",
945
+ currency
946
+ }).format(value);
947
+ },
948
+ /**
949
+ * Formats a date to a readable string.
950
+ * @param date - Date to format
951
+ * @param locale - Locale (default: 'en-US')
952
+ * @param options - Intl.DateTimeFormatOptions
953
+ */
954
+ date: (date, locale = "en-US", options) => {
955
+ const d = new Date(date);
956
+ const defaultOptions = options || {
957
+ year: "numeric",
958
+ month: "long",
959
+ day: "numeric"
960
+ };
961
+ return new Intl.DateTimeFormat(locale, defaultOptions).format(d);
962
+ },
963
+ /**
964
+ * Truncates a string to a specific length.
965
+ */
966
+ truncate: (str, length) => {
967
+ if (str.length <= length) return str;
968
+ return str.slice(0, length) + "...";
969
+ }
970
+ };
971
+ //#endregion
972
+ //#region src/utilities/validators.util.ts
973
+ /**
974
+ * Utility functions for common validations.
975
+ */
976
+ var validators = {
977
+ /**
978
+ * Checks if a string is a valid email.
979
+ */
980
+ isEmail: (email) => {
981
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
982
+ },
983
+ /**
984
+ * Checks if a string is empty or only whitespace.
985
+ */
986
+ isEmpty: (str) => {
987
+ return !str || str.trim().length === 0;
988
+ },
989
+ /**
990
+ * Checks if a value is a number.
991
+ */
992
+ isNumber: (value) => {
993
+ return !isNaN(parseFloat(value)) && isFinite(value);
994
+ },
995
+ /**
996
+ * Validates if a password meets minimum complexity.
997
+ * (At least 8 chars, 1 letter, 1 number)
998
+ */
999
+ isStrongPassword: (password) => {
1000
+ return password.length >= 8 && /[A-Za-z]/.test(password) && /[0-9]/.test(password);
1001
+ },
1002
+ /**
1003
+ * Validates a phone number.
1004
+ * @param phone - Phone number string
1005
+ * @param locale - Optional locale (default: 'generic')
1006
+ */
1007
+ isPhone: (phone, locale = "generic") => {
1008
+ const cleanPhone = phone.replace(/\s|-/g, "");
1009
+ if (locale === "es-CR") return /^[245678]\d{7}$/.test(cleanPhone);
1010
+ return /^\+?[\d\s-]{7,}$/.test(cleanPhone);
1011
+ }
1012
+ };
1013
+ //#endregion
1014
+ //#region src/utilities/logger.util.ts
1015
+ var logStyles = {
1016
+ info: "color: #3b82f6; font-weight: bold;",
1017
+ warn: "color: #f59e0b; font-weight: bold;",
1018
+ error: "color: #ef4444; font-weight: bold;",
1019
+ success: "color: #10b981; font-weight: bold;"
1020
+ };
1021
+ var logger = {
1022
+ info: (message, ...data) => {},
1023
+ warn: (message, ...data) => {},
1024
+ error: (message, ...data) => {
1025
+ console.error(`%c[ERROR] ${message}`, logStyles.error, ...data);
1026
+ },
1027
+ success: (message, ...data) => {}
1028
+ };
1029
+ //#endregion
1030
+ //#region src/hooks/useFetch.hook.ts
1031
+ /**
1032
+ * Custom hook to perform a fetch request and manage its state (data, error, loading).
1033
+ * @template T - Expected return type
1034
+ * @param url - URL to fetch
1035
+ * @param options - Optional configuration (e.g., delay for testing)
1036
+ * @returns Object with data, error, and loading state
1037
+ */
1038
+ var useFetch = (url, options) => {
1039
+ const [data, setData] = useState(null);
1040
+ const [error, setError] = useState(null);
1041
+ const [loading, setLoading] = useState(true);
1042
+ useEffect(() => {
1043
+ const controller = new AbortController();
1044
+ const { signal } = controller;
1045
+ const fetchData = async () => {
1046
+ setLoading(true);
1047
+ if (options?.delay) await new Promise((resolve) => setTimeout(resolve, options.delay));
1048
+ try {
1049
+ setData(await apiFetch(url, { signal }));
1050
+ } catch (err) {
1051
+ if (err instanceof DOMException && err.name === "AbortError") console.log("Fetch aborted");
1052
+ else if (err instanceof Error) setError(err.message);
1053
+ else setError(String(err));
1054
+ } finally {
1055
+ setLoading(false);
1056
+ }
1057
+ };
1058
+ fetchData();
1059
+ return () => {
1060
+ controller.abort();
1061
+ };
1062
+ }, [url]);
1063
+ return {
1064
+ data,
1065
+ error,
1066
+ loading
1067
+ };
1068
+ };
1069
+ //#endregion
1070
+ //#region src/hooks/useLocalStorage.hook.ts
1071
+ /**
1072
+ * Custom hook to manage localStorage with React state.
1073
+ * @param key - The key to store in localStorage
1074
+ * @param initialValue - Initial value if none exists
1075
+ * @returns [storedValue, setValue] - State and setter function
1076
+ */
1077
+ function useLocalStorage(key, initialValue) {
1078
+ const [storedValue, setStoredValue] = useState(() => {
1079
+ if (typeof window === "undefined") return initialValue;
1080
+ try {
1081
+ const item = window.localStorage.getItem(key);
1082
+ return item ? JSON.parse(item) : initialValue;
1083
+ } catch (error) {
1084
+ console.error(`Error reading localStorage key "${key}":`, error);
1085
+ return initialValue;
1086
+ }
1087
+ });
1088
+ const setValue = (value) => {
1089
+ try {
1090
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
1091
+ setStoredValue(valueToStore);
1092
+ if (typeof window !== "undefined") window.localStorage.setItem(key, JSON.stringify(valueToStore));
1093
+ } catch (error) {
1094
+ console.error(`Error setting localStorage key "${key}":`, error);
1095
+ }
1096
+ };
1097
+ return [storedValue, setValue];
1098
+ }
1099
+ //#endregion
1100
+ //#region src/hooks/useDebounce.hook.ts
1101
+ /**
1102
+ * Custom hook to debounce a value.
1103
+ * @param value - The value to debounce
1104
+ * @param delay - Delay in milliseconds (default 500)
1105
+ * @returns The debounced value
1106
+ */
1107
+ function useDebounce(value, delay = 500) {
1108
+ const [debouncedValue, setDebouncedValue] = useState(value);
1109
+ useEffect(() => {
1110
+ const handler = setTimeout(() => {
1111
+ setDebouncedValue(value);
1112
+ }, delay);
1113
+ return () => {
1114
+ clearTimeout(handler);
1115
+ };
1116
+ }, [value, delay]);
1117
+ return debouncedValue;
1118
+ }
1119
+ //#endregion
1120
+ export { Accordion, Anchor, Button, Card, DropDown, Input, Modal, Preloader, ProgressBar, ScrollTop, Spinner, Typed, WhatsApp, apiFetch, del, formatters, get, httpClient, logger, post, put, storage, useDebounce, useFetch, useLocalStorage, validators };
801
1121
 
802
1122
  //# sourceMappingURL=luna-components-library.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"luna-components-library.js","names":[],"sources":["../node_modules/react/cjs/react-jsx-runtime.production.js","../node_modules/react/jsx-runtime.js","../src/components/Button.tsx","../src/components/Card.tsx","../src/components/Anchor.tsx","../src/components/Accordion.tsx","../src/components/Spinner.tsx","../src/components/DropDown.tsx","../src/components/ProgressBar.tsx","../src/components/Typed.tsx","../src/components/Preloader.tsx","../src/components/ScrollTop.tsx","../src/components/WhatsApp.tsx","../src/components/Modal.tsx","../src/components/Input.tsx"],"sourcesContent":["/**\n * @license React\n * react-jsx-runtime.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\");\nfunction jsxProd(type, config, maybeKey) {\n var key = null;\n void 0 !== maybeKey && (key = \"\" + maybeKey);\n void 0 !== config.key && (key = \"\" + config.key);\n if (\"key\" in config) {\n maybeKey = {};\n for (var propName in config)\n \"key\" !== propName && (maybeKey[propName] = config[propName]);\n } else maybeKey = config;\n config = maybeKey.ref;\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n ref: void 0 !== config ? config : null,\n props: maybeKey\n };\n}\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.jsx = jsxProd;\nexports.jsxs = jsxProd;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","import React from 'react';\n\n// Button variants and sizes\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light' | 'link';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type AllButtonProps = React.ComponentPropsWithoutRef<'button'>;\n\nexport type ButtonProps = {\n children: React.ReactNode;\n variant?: ButtonVariant;\n size?: ButtonSize;\n onClick?: () => void;\n disabled?: boolean;\n className?: string;\n containerClassName?: string;\n variantClassName?: string;\n sizeClassName?: string;\n style?: React.CSSProperties;\n}\n\n{/* onCLick default should open window.open('https://andreychaconresumereact.netlify.app/', '_blank') */ }\n\nconst Button = ({\n children,\n variant = 'primary',\n size = 'sm',\n onClick = () =>\n void 0,\n disabled = false,\n className = '',\n containerClassName = 'font-medium rounded-lg transition-colors focus:outline-none focus:ring-2',\n variantClassName = 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',\n sizeClassName = 'px-3 py-1.5 text-sm',\n style,\n ...props\n}: AllButtonProps & ButtonProps) => {\n const baseClasses = containerClassName;\n\n const variantClasses = {\n primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',\n secondary: 'bg-gray-600 text-white hover:bg-gray-700 focus:ring-gray-500',\n outline: 'border border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-gray-500',\n success: 'bg-green-600 text-white hover:bg-green-700 focus:ring-green-500',\n danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',\n warning: 'bg-yellow-500 text-white hover:bg-yellow-600 focus:ring-yellow-500',\n info: 'bg-cyan-600 text-white hover:bg-cyan-700 focus:ring-cyan-500',\n dark: 'bg-gray-900 text-white hover:bg-gray-800 focus:ring-gray-900',\n light: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-300',\n link: 'text-blue-600 hover:text-blue-800 hover:underline focus:outline-none focus:ring-2 focus:ring-blue-500',\n };\n\n const sizeClasses = {\n sm: 'px-3 py-1.5 text-sm',\n md: 'px-4 py-2 text-base',\n lg: 'px-6 py-3 text-lg',\n };\n\n const classes = `\n ${baseClasses}\n ${variantClasses[variant]}\n ${sizeClasses[size]}\n ${disabled ? 'opacity-50 cursor-not-allowed' : ''}\n ${className}\n `.trim();\n\n return (\n <button\n className={classes}\n onClick={onClick}\n disabled={disabled}\n style={style}\n {...props}\n >\n {children}\n </button>\n );\n};\n\nexport default Button;\n","import React from 'react';\n\n// Card padding and shadow variants\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg';\nexport type CardShadow = 'none' | 'sm' | 'md' | 'lg';\n\nexport type CardProps = {\n children: React.ReactNode;\n title?: string;\n className?: string;\n containerClassName?: string;\n titleClassName?: string;\n padding?: CardPadding;\n shadow?: CardShadow;\n style?: React.CSSProperties;\n}\n\nconst Card = ({\n children,\n title,\n className = '',\n containerClassName = 'bg-white rounded-lg border border-gray-200',\n titleClassName = 'text-lg font-semibold text-gray-900',\n padding = 'md',\n shadow = 'md',\n style,\n}: CardProps) => {\n const paddingClasses = {\n none: '',\n sm: 'p-3',\n md: 'p-4',\n lg: 'p-6',\n };\n\n const shadowClasses = {\n none: '',\n sm: 'shadow-sm',\n md: 'shadow-md',\n lg: 'shadow-lg',\n };\n\n const classes = `\n ${containerClassName}\n ${paddingClasses[padding]}\n ${shadowClasses[shadow]}\n ${className}\n `.trim();\n\n return (\n <div className={classes} style={style}>\n {title && (\n <div className=\"mb-4\">\n <h3 className={titleClassName}>{title}</h3>\n </div>\n )}\n {children}\n </div>\n );\n};\n\nexport default Card;\n","import React from 'react';\r\n\r\n// Anchor link variants and sizes\r\nexport type AnchorVariant = 'none' | 'primary' | 'secondary' | 'outline';\r\nexport type AnchorSize = 'sm' | 'md' | 'lg';\r\nexport type AllAnchorProps = React.ComponentPropsWithoutRef<'a'>;\r\n\r\nexport type AnchorProps = {\r\n children?: React.ReactNode;\r\n variant?: AnchorVariant;\r\n size?: AnchorSize;\r\n href?: string;\r\n className?: string;\r\n containerClassName?: string;\r\n variantClassName?: string;\r\n sizeClassName?: string;\r\n target?: string;\r\n rel?: string;\r\n style?: React.CSSProperties;\r\n};\r\n\r\nconst Anchor = ({\r\n children = \"Pablo Andrey Chacon Luna\",\r\n variant = 'none',\r\n size = 'sm',\r\n href = 'https://andreychaconresumereact.netlify.app/',\r\n className,\r\n containerClassName = 'font-medium rounded-lg transition-colors focus:outline-none focus:ring-2',\r\n variantClassName = 'bg-blue-600 text-white hover:bg-blue-700',\r\n sizeClassName = 'px-3 py-1.5 text-sm',\r\n target = '_blank',\r\n rel = 'noopener noreferrer',\r\n style,\r\n ...props\r\n}: AnchorProps & AllAnchorProps) => {\r\n\r\n const baseClasses = containerClassName;\r\n\r\n const variantClasses = {\r\n none: '',\r\n primary: 'bg-blue-600 text-white hover:bg-blue-700',\r\n secondary: 'bg-gray-600 text-white hover:bg-gray-700',\r\n outline: 'border border-gray-300 text-gray-700 hover:bg-gray-50',\r\n };\r\n\r\n const sizeClasses = {\r\n sm: 'px-3 py-1.5 text-sm',\r\n md: 'px-4 py-2 text-base',\r\n lg: 'px-6 py-3 text-lg',\r\n };\r\n\r\n const classes = `\r\n ${baseClasses}\r\n ${variantClasses[variant]}\r\n ${sizeClasses[size]}\r\n ${variantClassName}\r\n ${sizeClassName}\r\n ${className}\r\n `.trim();\r\n\r\n return (\r\n <a href={href} target={target} rel={rel} className={classes} style={style} {...props}>\r\n {children}\r\n </a>\r\n );\r\n};\r\n\r\nexport default Anchor;","\r\nimport React from 'react';\r\n\r\n// Accordion component for collapsible content\r\nexport type AccordionProps = {\r\n active: boolean;\r\n onClick: () => void;\r\n header: React.ReactNode;\r\n content: React.ReactNode;\r\n className?: string;\r\n containerClassName?: string;\r\n headerClassName?: string;\r\n contentClassName?: string;\r\n style?: React.CSSProperties;\r\n}\r\n\r\nconst Accordion = ({ active,\r\n onClick,\r\n header,\r\n content,\r\n className = '',\r\n containerClassName = 'border border-gray-200 rounded-lg overflow-hidden',\r\n headerClassName = 'w-full px-4 py-3 text-left bg-gray-50 hover:bg-gray-100 focus:bg-gray-100 focus:outline-none transition-colors duration-200 flex justify-between items-center',\r\n contentClassName = 'transition-all duration-300 ease-in-out',\r\n style\r\n}: AccordionProps) => {\r\n return (\r\n <div className={`${containerClassName} ${className}`} style={style}>\r\n <button\r\n onClick={onClick}\r\n className={headerClassName}\r\n aria-expanded={active}\r\n >\r\n {header}\r\n <svg\r\n className={`w-5 h-5 text-gray-500 transition-transform duration-200 ${active ? 'transform rotate-180' : ''\r\n }`}\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n strokeWidth={2}\r\n d=\"M19 9l-7 7-7-7\"\r\n />\r\n </svg>\r\n </button>\r\n\r\n <div\r\n className={`${contentClassName} ${active ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'\r\n } overflow-hidden`}\r\n >\r\n <div className={`p-4 bg-white border-t border-gray-200 ${contentClassName}`}>\r\n {content}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\nexport default Accordion;","import React from 'react';\n\nexport type SpinnerSize = 'sm' | 'md' | 'lg';\nexport type SpinnerType = 'circle' | 'dots' | 'pulse' | 'bars';\n\nexport type SpinnerProps = {\n className?: string;\n containerClassName?: string;\n dotClassName?: string;\n barClassName?: string;\n size?: SpinnerSize;\n type?: SpinnerType;\n style?: React.CSSProperties;\n};\n\nconst Spinner = ({\n className,\n containerClassName = 'flex gap-1',\n dotClassName = 'bg-blue-600 rounded-full animate-bounce',\n barClassName = 'bg-blue-600 animate-pulse',\n size = 'md',\n type = 'circle',\n style\n}: SpinnerProps) => {\n const sizeClasses = {\n sm: 'w-4 h-4',\n md: 'w-6 h-6',\n lg: 'w-8 h-8'\n };\n\n const dotSizeClasses = {\n sm: 'w-1 h-1',\n md: 'w-2 h-2',\n lg: 'w-3 h-3'\n };\n\n const barSizeClasses = {\n sm: 'w-1 h-4',\n md: 'w-1 h-6',\n lg: 'w-1 h-8'\n };\n\n if (type === 'dots') {\n return (\n <div role=\"status\" className={`${containerClassName} ${className || ''}`}>\n <span className=\"sr-only\">Loading...</span>\n <div className={`${dotSizeClasses[size]} ${dotClassName}`} style={{ animationDelay: '0ms' }}></div>\n <div className={`${dotSizeClasses[size]} ${dotClassName}`} style={{ animationDelay: '150ms' }}></div>\n <div className={`${dotSizeClasses[size]} ${dotClassName}`} style={{ animationDelay: '300ms' }}></div>\n </div>\n );\n }\n\n if (type === 'pulse') {\n return (\n <div role=\"status\" className={`${sizeClasses[size]} ${className || ''}`}>\n <span className=\"sr-only\">Loading...</span>\n <div className={`${sizeClasses[size]} ${dotClassName}`}></div>\n </div>\n );\n }\n\n if (type === 'bars') {\n return (\n <div role=\"status\" className={`flex gap-1 items-center ${containerClassName} ${className || ''}`}>\n <span className=\"sr-only\">Loading...</span>\n <div className={`${barSizeClasses[size]} ${barClassName}`} style={{ animationDelay: '0ms' }}></div>\n <div className={`${barSizeClasses[size]} ${barClassName}`} style={{ animationDelay: '200ms' }}></div>\n <div className={`${barSizeClasses[size]} ${barClassName}`} style={{ animationDelay: '400ms' }}></div>\n <div className={`${barSizeClasses[size]} ${barClassName}`} style={{ animationDelay: '600ms' }}></div>\n </div>\n );\n }\n\n // Default circle spinner\n return (\n <div\n role=\"status\"\n className={`inline-block animate-spin rounded-full border-2 border-gray-300 border-t-blue-600 ${sizeClasses[size]} ${className || ''}`}\n style={style}\n >\n <span className=\"sr-only\">Loading...</span>\n </div>\n );\n};\n\nexport default Spinner;\n","{/* Dropdown component for selecting options */ }\r\nimport React, { useState } from 'react';\r\n\r\ntype DropDownOption = {\r\n value: string;\r\n label: React.ReactNode;\r\n};\r\n\r\nexport type DropDownProps = {\r\n toggle: React.ReactNode;\r\n options: React.ReactNode[] | DropDownOption[];\r\n selected: React.ReactNode;\r\n onChange: (value: React.ReactNode) => void;\r\n className?: string;\r\n containerClassName?: string;\r\n dropdownClassName?: string;\r\n optionsContainerClassName?: string;\r\n optionClassName?: string;\r\n style?: React.CSSProperties;\r\n};\r\n\r\nconst DropDown = ({\r\n toggle,\r\n options,\r\n selected,\r\n onChange,\r\n className = '',\r\n containerClassName = 'relative inline-block text-left',\r\n dropdownClassName = 'absolute z-50 mt-2 w-48 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none',\r\n optionsContainerClassName = 'py-1 flex flex-col',\r\n optionClassName = 'block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900',\r\n style\r\n}: DropDownProps) => {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const handleToggle = () => {\r\n setIsOpen(!isOpen);\r\n };\r\n\r\n const handleOptionClick = (option: React.ReactNode) => {\r\n onChange(option);\r\n setIsOpen(false);\r\n };\r\n\r\n return (\r\n <div className={`${containerClassName} ${className}`} style={style}>\r\n <div onClick={handleToggle} className=\"cursor-pointer\">\r\n {toggle}\r\n </div>\r\n\r\n {isOpen && (\r\n <div className={dropdownClassName}>\r\n <div className={optionsContainerClassName}>\r\n {options.map((option, index) => {\r\n const isOptionObject = typeof option === 'object' && option !== null && 'value' in option;\r\n const optionValue = isOptionObject ? (option as DropDownOption).value : option;\r\n const optionLabel = isOptionObject ? (option as DropDownOption).label : option;\r\n\r\n return (\r\n <button\r\n key={index}\r\n onClick={() => handleOptionClick(optionValue)}\r\n className={optionClassName}\r\n >\r\n {optionLabel}\r\n </button>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\nexport default DropDown;","import React from 'react';\r\n\r\n// Progress bar color variants\r\nexport type ProgressBarVariant = 'primary' | 'success' | 'warning' | 'danger' | 'dark' | 'light';\r\n\r\n// Core progress bar props\r\nexport type ProgressBarProps = {\r\n progress: number;\r\n max: number;\r\n min: number;\r\n 'aria-label': string;\r\n}\r\n\r\n// Extended props with styling options\r\nexport type ProgressBarPropsWithClassName = ProgressBarProps & {\r\n className?: React.CSSProperties;\r\n style?: React.CSSProperties;\r\n containerClassName?: string;\r\n barClassName?: string;\r\n variant?: ProgressBarVariant;\r\n};\r\n\r\nconst ProgressBar = ({\r\n progress,\r\n max,\r\n min,\r\n 'aria-label': ariaLabel,\r\n className,\r\n style,\r\n containerClassName = 'w-full bg-gray-200 rounded-full h-4 overflow-hidden',\r\n barClassName = 'h-full rounded-full transition-all duration-300 flex items-center justify-center text-xs font-medium',\r\n variant = 'primary'\r\n}: ProgressBarPropsWithClassName) => {\r\n const variantClasses = {\r\n primary: {\r\n bg: 'bg-blue-600',\r\n text: 'text-white',\r\n containerBg: 'bg-gray-200'\r\n },\r\n success: {\r\n bg: 'bg-green-600',\r\n text: 'text-white',\r\n containerBg: 'bg-gray-200'\r\n },\r\n warning: {\r\n bg: 'bg-yellow-500',\r\n text: 'text-gray-900',\r\n containerBg: 'bg-gray-200'\r\n },\r\n danger: {\r\n bg: 'bg-red-600',\r\n text: 'text-white',\r\n containerBg: 'bg-gray-200'\r\n },\r\n dark: {\r\n bg: 'bg-gray-800',\r\n text: 'text-white',\r\n containerBg: 'bg-gray-300'\r\n },\r\n light: {\r\n bg: 'bg-gray-100',\r\n text: 'text-gray-900',\r\n containerBg: 'bg-gray-300'\r\n }\r\n };\r\n\r\n const currentVariant = variantClasses[variant];\r\n const barClasses = `${currentVariant.bg} ${barClassName} ${currentVariant.text}`;\r\n\r\n return (\r\n <div className={containerClassName} style={style}>\r\n <div\r\n role=\"progressbar\"\r\n className={barClasses}\r\n aria-valuenow={progress}\r\n aria-valuemin={min}\r\n aria-valuemax={max}\r\n style={{ width: `${progress}%`, ...className }}\r\n >\r\n {progress > 10 && `${progress}%`}\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\nexport default ProgressBar;","import React, { useState, useEffect, CSSProperties } from 'react';\r\n\r\n// Typing animation configuration\r\ntype TypedStyle = CSSProperties & {\r\n animation?: string;\r\n animationDelay?: string;\r\n};\r\n\r\ntype TypedProps = {\r\n strings: string[];\r\n typeSpeed?: number;\r\n backSpeed?: number;\r\n backDelay?: number;\r\n startDelay?: number;\r\n loop?: boolean;\r\n showCursor?: boolean;\r\n className?: string;\r\n containerClassName?: string;\r\n typedClassName?: string;\r\n cursorClassName?: string;\r\n style?: TypedStyle;\r\n};\r\n\r\nconst Typed = ({\r\n strings,\r\n typeSpeed = 50,\r\n backSpeed = 30,\r\n backDelay = 500,\r\n startDelay = 0,\r\n loop = true,\r\n showCursor = true,\r\n className = '',\r\n containerClassName = 'inline-block',\r\n typedClassName = 'typed',\r\n cursorClassName = 'typed-cursor ml-1 inline-block w-0.5 h-5 bg-current',\r\n style = {},\r\n}: TypedProps) => {\r\n const [currentStringIndex, setCurrentStringIndex] = useState(0);\r\n const [currentText, setCurrentText] = useState('');\r\n const [isDeleting, setIsDeleting] = useState(false);\r\n const [isPaused, setIsPaused] = useState(false);\r\n const [cursorOpacity, setCursorOpacity] = useState(1);\r\n\r\n useEffect(() => {\r\n const timer = setTimeout(() => {\r\n setIsPaused(false);\r\n }, startDelay);\r\n\r\n return () => clearTimeout(timer);\r\n }, [startDelay]);\r\n\r\n useEffect(() => {\r\n if (isPaused) return;\r\n\r\n const currentString = strings[currentStringIndex] || '';\r\n\r\n const timer = setTimeout(() => {\r\n if (!isDeleting) {\r\n // Typing\r\n if (currentText.length < currentString.length) {\r\n setCurrentText(currentText + currentString[currentText.length]);\r\n } else {\r\n // Finished typing, wait before deleting\r\n if (loop) {\r\n setIsPaused(true);\r\n setTimeout(() => {\r\n setIsPaused(false);\r\n setIsDeleting(true);\r\n }, backDelay);\r\n }\r\n }\r\n } else {\r\n // Deleting\r\n if (currentText.length > 0) {\r\n setCurrentText(currentText.slice(0, -1));\r\n } else {\r\n // Finished deleting, move to next string\r\n setIsDeleting(false);\r\n setCurrentStringIndex((prevIndex) =>\r\n prevIndex === strings.length - 1 ? 0 : prevIndex + 1\r\n );\r\n }\r\n }\r\n }, isDeleting ? backSpeed : typeSpeed);\r\n\r\n return () => clearTimeout(timer);\r\n }, [currentText, isDeleting, currentStringIndex, strings, typeSpeed, backSpeed, backDelay, loop, isPaused]);\r\n\r\n // Cursor fade effect\r\n useEffect(() => {\r\n const fadeTimer = setInterval(() => {\r\n setCursorOpacity(prev => {\r\n if (prev === 1) return 0;\r\n return 1;\r\n });\r\n }, 750);\r\n\r\n return () => clearInterval(fadeTimer);\r\n }, []);\r\n\r\n return (\r\n <span className={`${containerClassName} ${className}`} style={style}>\r\n <span className={typedClassName}>{currentText}</span>\r\n {showCursor && (\r\n <span\r\n className={cursorClassName}\r\n aria-hidden=\"true\"\r\n style={{\r\n opacity: cursorOpacity\r\n }}\r\n >\r\n\r\n </span>\r\n )}\r\n </span>\r\n );\r\n};\r\n\r\nexport default Typed;","import React, { useEffect, useState } from 'react';\n\nexport type PreloaderProps = {\n /** Loading state - if true, preloader is shown */\n isLoading?: boolean;\n /** Duration in milliseconds before auto-hide */\n duration?: number;\n /** Background color overlay */\n backgroundColor?: string;\n /** Accent color for the spinner */\n accentColor?: string;\n /** Size of the spinner in pixels */\n size?: number;\n /** Border width of the spinner */\n borderWidth?: number;\n /** Custom className for the preloader */\n className?: string;\n /** Custom className for the spinner */\n spinnerClassName?: string;\n /** Custom z-index */\n zIndex?: number;\n /** Callback when preloader finishes */\n onComplete?: () => void;\n /** Custom inline styles */\n style?: React.CSSProperties;\n}\n\nconst Preloader = ({\n isLoading: externalLoading,\n duration = 1000,\n backgroundColor,\n accentColor,\n size = 90,\n borderWidth = 6,\n className = '',\n spinnerClassName = '',\n zIndex = 999999,\n onComplete,\n style\n}: PreloaderProps) => {\n const [internalLoading, setInternalLoading] = useState(true);\n\n // Use external loading state if provided, otherwise use internal state\n const isLoading = externalLoading !== undefined ? externalLoading : internalLoading;\n\n useEffect(() => {\n // Only auto-hide if we're using internal loading state\n if (externalLoading === undefined) {\n const timer = setTimeout(() => {\n setInternalLoading(false);\n onComplete?.();\n }, duration);\n\n return () => clearTimeout(timer);\n }\n }, [duration, externalLoading, onComplete]);\n\n // Handle external loading state changes - auto-hide when externalLoading is true\n useEffect(() => {\n if (externalLoading === true) {\n const timer = setTimeout(() => {\n onComplete?.();\n }, duration);\n\n return () => clearTimeout(timer);\n }\n }, [externalLoading, duration, onComplete]);\n\n const preloaderStyle: React.CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex,\n overflow: 'hidden',\n background: backgroundColor || 'var(--background-color, #00000018)',\n transition: 'all 0.6s ease-out',\n display: isLoading ? 'block' : 'none'\n };\n\n const spinnerStyle: React.CSSProperties = {\n position: 'fixed',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n border: `${borderWidth}px solid #ffffff`,\n borderColor: `${accentColor || 'var(--accent-color, #007bff)'} transparent transparent transparent`,\n borderRadius: '50%',\n width: `${size}px`,\n height: `${size}px`,\n animation: 'animate-preloader 1.5s linear infinite'\n };\n\n return (\n <>\n <div\n className={`preloader-overlay ${className}`}\n style={{ ...preloaderStyle, ...style }}\n >\n <div\n className={`preloader-spinner ${spinnerClassName}`}\n style={spinnerStyle}\n />\n </div>\n\n {/* Global styles for animation */}\n <style>{`\n @keyframes animate-preloader {\n 0% {\n transform: translate(-50%, -50%) rotate(0deg);\n }\n 100% {\n transform: translate(-50%, -50%) rotate(360deg);\n }\n }\n `}</style>\n </>\n );\n};\n\nexport default Preloader;\n","import React, { useEffect, useState, useRef } from 'react';\r\n\r\nexport type ScrollTopProps = {\r\n /** Scroll position threshold to show the button (in pixels) */\r\n threshold?: number;\r\n /** Custom className for the button */\r\n className?: string;\r\n /** Custom icon/content for the button */\r\n children?: React.ReactNode;\r\n /** Position of the button */\r\n position?: 'bottom-right' | 'bottom-left' | 'bottom-center' | 'top-right' | 'top-left' | 'top-center';\r\n /** Size of the button */\r\n size?: 'sm' | 'md' | 'lg';\r\n /** Shape of the button */\r\n shape?: 'circle' | 'square' | 'rounded';\r\n /** Whether to show the button initially */\r\n showInitially?: boolean;\r\n /** Custom scroll behavior */\r\n scrollBehavior?: 'auto' | 'smooth';\r\n /** Custom styles */\r\n style?: React.CSSProperties;\r\n /** Callback when button is clicked */\r\n onClick?: () => void;\r\n /** Callback when visibility changes */\r\n onVisibilityChange?: (isVisible: boolean) => void;\r\n /** Element ID or selector to check visibility for showing the button */\r\n targetElement?: string;\r\n /** Percentage of page scroll to show the button (0-100) */\r\n scrollPercentage?: number;\r\n}\r\n\r\nconst ScrollTop = ({\r\n threshold = 100,\r\n className = '',\r\n children,\r\n position = 'bottom-right',\r\n size = 'md',\r\n shape = 'circle',\r\n showInitially = false,\r\n scrollBehavior = 'smooth',\r\n style,\r\n onClick,\r\n onVisibilityChange,\r\n targetElement,\r\n scrollPercentage,\r\n}: ScrollTopProps) => {\r\n const [isVisible, setIsVisible] = useState(showInitially);\r\n\r\n useEffect(() => {\r\n // Show/hide scroll to top button based on scroll position\r\n const toggleVisibility = () => {\r\n let shouldBeVisible = false;\r\n\r\n // Check scroll percentage first\r\n if (scrollPercentage !== undefined) {\r\n const maxScroll = document.documentElement.scrollHeight - window.innerHeight;\r\n const currentScroll = window.scrollY;\r\n const percentage = (currentScroll / maxScroll) * 100;\r\n shouldBeVisible = percentage >= scrollPercentage;\r\n }\r\n // Check target element visibility\r\n else if (targetElement) {\r\n const element = document.querySelector(targetElement);\r\n if (element) {\r\n const rect = element.getBoundingClientRect();\r\n const isInViewport = rect.top < window.innerHeight && rect.bottom > 0;\r\n shouldBeVisible = isInViewport; // Show when element is visible\r\n } else {\r\n // If element doesn't exist, fall back to threshold behavior\r\n shouldBeVisible = window.scrollY > threshold;\r\n }\r\n }\r\n // Default threshold behavior\r\n else {\r\n shouldBeVisible = window.scrollY > threshold;\r\n }\r\n\r\n // Set visibility immediately\r\n setIsVisible(shouldBeVisible);\r\n onVisibilityChange?.(shouldBeVisible);\r\n };\r\n\r\n window.addEventListener('scroll', toggleVisibility, { passive: true });\r\n // Initial check\r\n toggleVisibility();\r\n\r\n return () => {\r\n window.removeEventListener('scroll', toggleVisibility);\r\n };\r\n }, [threshold, onVisibilityChange, targetElement, scrollPercentage]);\r\n\r\n const scrollToTop = () => {\r\n window.scrollTo({\r\n top: 0,\r\n behavior: scrollBehavior\r\n });\r\n onClick?.();\r\n };\r\n\r\n const handleClick = (e: React.MouseEvent) => {\r\n e.preventDefault();\r\n scrollToTop();\r\n };\r\n\r\n // Position classes\r\n const positionClasses = {\r\n 'bottom-right': 'fixed bottom-8 right-8',\r\n 'bottom-left': 'fixed bottom-8 left-8',\r\n 'bottom-center': 'fixed bottom-8 left-1/2 transform -translate-x-1/2',\r\n 'top-right': 'fixed top-8 right-8',\r\n 'top-left': 'fixed top-8 left-8',\r\n 'top-center': 'fixed top-8 left-1/2 transform -translate-x-1/2'\r\n };\r\n\r\n // Size classes\r\n const sizeClasses = {\r\n sm: 'w-8 h-8 text-sm',\r\n md: 'w-12 h-12 text-base',\r\n lg: 'w-16 h-16 text-lg'\r\n };\r\n\r\n // Shape classes\r\n const shapeClasses = {\r\n circle: 'rounded-full',\r\n square: 'rounded-none',\r\n rounded: 'rounded-lg'\r\n };\r\n\r\n const buttonClasses = `\r\n ${positionClasses[position]}\r\n ${sizeClasses[size]}\r\n ${shapeClasses[shape]}\r\n ${className}\r\n flex items-center justify-center\r\n bg-blue-600 hover:bg-blue-700\r\n text-white\r\n shadow-lg hover:shadow-xl\r\n transition-all duration-300 ease-in-out\r\n cursor-pointer\r\n z-50\r\n ${isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4 pointer-events-none'}\r\n `;\r\n\r\n const defaultContent = (\r\n <svg\r\n className=\"w-4 h-4\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n strokeWidth={2}\r\n d=\"M5 10l7-7m0 0l7 7m-7-7v18\"\r\n />\r\n </svg>\r\n );\r\n\r\n return (\r\n <button\r\n className={buttonClasses}\r\n onClick={handleClick}\r\n style={style}\r\n aria-label=\"Scroll to top\"\r\n title=\"Scroll to top\"\r\n >\r\n {children || defaultContent}\r\n </button>\r\n );\r\n};\r\n\r\nexport default ScrollTop;\r\n","import React, { useState, useEffect } from 'react';\n\nexport interface WhatsAppProps {\n /** Phone number for WhatsApp (with country code, without + or spaces) */\n phone?: string;\n /** Default message to send */\n message?: string;\n /** Position of the button */\n position?: 'bottom-right' | 'bottom-left' | 'bottom-center' | 'top-right' | 'top-left' | 'top-center';\n /** Size of the button */\n size?: 'sm' | 'md' | 'lg';\n /** Show tooltip on hover */\n showTooltip?: boolean;\n /** Tooltip text */\n tooltipText?: string;\n /** Custom className for the button */\n className?: string;\n /** Custom className for the tooltip */\n tooltipClassName?: string;\n /** Custom styles */\n style?: React.CSSProperties;\n /** Callback when button is clicked */\n onClick?: () => void;\n /** Z-index for the button */\n zIndex?: number;\n /** Whether to open in new tab */\n openInNewTab?: boolean;\n}\n\nconst WhatsApp = ({\n phone = '',\n message = '¡Hola! Me gustaría obtener más información.',\n position = 'bottom-right',\n size = 'md',\n showTooltip = true,\n tooltipText = '¿En qué podemos ayudarte?',\n className = '',\n tooltipClassName = '',\n style,\n onClick,\n zIndex = 50,\n openInNewTab = true\n}: WhatsAppProps) => {\n const [isHovered, setIsHovered] = useState(false);\n\n // Don't render if no phone number provided\n if (!phone) return null;\n\n const handleWhatsAppClick = () => {\n const cleanPhone = phone.replace(/[^\\d]/g, '');\n const encodedMessage = encodeURIComponent(message);\n const whatsappUrl = `https://wa.me/${cleanPhone}?text=${encodedMessage}`;\n\n if (openInNewTab) {\n window.open(whatsappUrl, '_blank');\n } else {\n window.location.href = whatsappUrl;\n }\n\n onClick?.();\n };\n\n // Position classes\n const positionClasses = {\n 'bottom-right': 'bottom-6 right-6',\n 'bottom-left': 'bottom-6 left-6',\n 'bottom-center': 'bottom-6 left-1/2 transform -translate-x-1/2',\n 'top-right': 'top-6 right-6',\n 'top-left': 'top-6 left-6',\n 'top-center': 'top-6 left-1/2 transform -translate-x-1/2'\n };\n\n // Size classes\n const sizeClasses = {\n sm: 'w-10 h-10',\n md: 'w-14 h-14',\n lg: 'w-16 h-16'\n };\n\n // Icon size based on button size\n const iconSizes = {\n sm: 20,\n md: 32,\n lg: 40\n };\n\n const buttonClasses = `\n fixed ${positionClasses[position]}\n ${sizeClasses[size]}\n bg-[#25D366] hover:bg-[#128C7E]\n text-white rounded-full shadow-2xl\n flex items-center justify-center\n transition-all duration-300 hover:scale-110\n z-${zIndex}\n group\n ${className}\n `;\n\n return (\n <>\n <button\n className={buttonClasses}\n onClick={handleWhatsAppClick}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n style={style}\n aria-label=\"Chatear por WhatsApp\"\n title=\"Chatear por WhatsApp\"\n >\n <svg\n width={iconSizes[size]}\n height={iconSizes[size]}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\" />\n </svg>\n </button>\n\n {showTooltip && (\n <div\n className={`\n fixed ${positionClasses[position].replace('6', '20').replace('bottom-6', 'bottom-20').replace('top-6', 'top-20')}\n bg-white text-gray-800 px-3 py-1 rounded-md text-sm font-semibold\n shadow-md whitespace-nowrap pointer-events-none\n transition-opacity duration-300\n ${isHovered ? 'opacity-100' : 'opacity-0'}\n ${position.includes('right') ? 'right-20' : position.includes('left') ? 'left-20' : 'left-1/2 transform -translate-x-1/2'}\n ${tooltipClassName}\n `}\n style={{\n [position.includes('bottom') ? 'bottom' : 'top']: position.includes('bottom') ? '5.5rem' : '5.5rem'\n }}\n >\n {tooltipText}\n </div>\n )}\n </>\n );\n};\n\nexport default WhatsApp;\n","import React, { useEffect, useRef } from 'react';\n\n// Modal size variants\ntype ModalSize = 'sm' | 'md' | 'lg' | 'xl';\n\nexport interface ModalProps {\n /** Whether the modal is visible */\n show: boolean;\n /** Callback when modal is closed */\n onHide: () => void;\n /** Modal size variant */\n size?: ModalSize;\n /** Whether to center the modal vertically */\n centered?: boolean;\n /** Whether to show backdrop overlay */\n backdrop?: boolean | 'static';\n /** Whether to close modal when backdrop is clicked */\n backdropClose?: boolean;\n /** Whether to close modal when ESC key is pressed */\n keyboard?: boolean;\n /** Whether to show modal with animation */\n animation?: boolean;\n /** Custom className for the modal */\n className?: string;\n /** Custom className for the modal dialog */\n dialogClassName?: string;\n /** Custom className for the modal content */\n contentClassName?: string;\n /** Custom className for the modal header */\n headerClassName?: string;\n /** Custom className for the modal body */\n bodyClassName?: string;\n /** Custom className for the modal footer */\n footerClassName?: string;\n /** Modal title */\n title?: React.ReactNode;\n /** Modal header content */\n header?: React.ReactNode;\n /** Modal body content */\n children: React.ReactNode;\n /** Modal footer content */\n footer?: React.ReactNode;\n /** Whether to show close button in header */\n closeButton?: boolean;\n /** Custom inline styles */\n style?: React.CSSProperties;\n}\n\nconst Modal = ({\n show,\n onHide,\n size = 'md',\n centered = false,\n backdrop = true,\n backdropClose = true,\n keyboard = true,\n animation = true,\n className = '',\n dialogClassName = '',\n contentClassName = '',\n headerClassName = '',\n bodyClassName = '',\n footerClassName = '',\n title,\n header,\n children,\n footer,\n closeButton = true,\n style\n}: ModalProps) => {\n const modalRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n // Handle ESC key press\n useEffect(() => {\n if (!show || !keyboard) return;\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n onHide();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [show, keyboard, onHide]);\n\n // Handle backdrop click\n const handleBackdropClick = (event: React.MouseEvent) => {\n if (backdropClose) {\n // Check if click is on backdrop using data attribute\n const target = event.target as HTMLElement;\n if (target.getAttribute('data-backdrop') === 'true' || target.closest('[data-backdrop=\"true\"]')) {\n onHide();\n }\n }\n };\n\n // Focus management\n useEffect(() => {\n if (show) {\n previousActiveElement.current = document.activeElement as HTMLElement;\n // Focus the modal dialog\n if (modalRef.current) {\n modalRef.current.focus();\n }\n // Prevent body scroll\n document.body.style.overflow = 'hidden';\n } else {\n // Restore focus and body scroll\n if (previousActiveElement.current) {\n previousActiveElement.current.focus();\n }\n document.body.style.overflow = '';\n }\n\n return () => {\n document.body.style.overflow = '';\n };\n }, [show]);\n\n // Size classes\n const sizeClasses = {\n sm: 'max-w-sm',\n md: 'max-w-md',\n lg: 'max-w-lg',\n xl: 'max-w-xl'\n };\n\n const modalClasses = `\n fixed inset-0 z-60 flex items-center justify-center\n ${show ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}\n ${animation ? 'transition-opacity duration-300' : ''}\n ${className}\n `.trim();\n\n const dialogClasses = `\n relative w-full ${sizeClasses[size]} mx-auto\n ${centered ? 'flex items-center justify-center min-h-screen' : 'mt-8'}\n ${dialogClassName}\n `.trim();\n\n if (!show) return null;\n\n return (\n <div\n ref={modalRef}\n className={modalClasses}\n tabIndex={-1}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'modal-title' : undefined}\n style={style}\n >\n {/* Backdrop */}\n {backdrop && (\n <div\n data-backdrop=\"true\"\n className={`absolute inset-0 bg-black bg-opacity-50 ${animation ? 'transition-opacity duration-300' : ''} ${show ? 'opacity-100' : 'opacity-0'}`}\n onClick={handleBackdropClick}\n />\n )}\n\n {/* Modal Content */}\n <div className={dialogClasses}>\n <div className={`bg-white rounded-lg shadow-xl relative z-10 ${contentClassName}`}>\n {/* Modal Header */}\n {(header || title || closeButton) && (\n <div className={`flex items-center justify-between p-4 border-b border-gray-200 ${headerClassName}`}>\n {header || (title && (\n <h3 className=\"text-lg font-semibold text-gray-900\" id=\"modal-title\">\n {title}\n </h3>\n ))}\n {closeButton && (\n <button\n type=\"button\"\n className=\"text-gray-400 hover:text-gray-600 transition-colors\"\n onClick={onHide}\n aria-label=\"Close\"\n >\n <svg className=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </div>\n )}\n\n {/* Modal Body */}\n <div className={`p-4 ${bodyClassName}`}>\n {children}\n </div>\n\n {/* Modal Footer */}\n {footer && (\n <div className={`flex items-center justify-end p-4 border-t border-gray-200 space-x-2 ${footerClassName}`}>\n {footer}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n};\n\nexport default Modal;\n","import React from 'react';\n\n// Input variants and sizes\nexport type InputVariant = 'none' | 'primary' | 'secondary' | 'outline' | 'danger' | 'success';\nexport type InputSize = 'sm' | 'md' | 'lg' | 'xl';\nexport type InputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'date' | 'time' | 'datetime-local' | 'month' | 'week' | 'color' | 'file' | 'hidden' | 'image' | 'range' | 'reset' | 'submit';\n\nexport type AllInputProps = Omit<React.ComponentPropsWithoutRef<'input'>, 'onChange'>;\n\nexport type InputProps = {\n children?: React.ReactNode;\n variant?: InputVariant;\n inputSize?: InputSize;\n type?: InputType;\n placeholder?: string;\n value?: string;\n onChange?: (value: string) => void;\n onFocus?: () => void;\n onBlur?: () => void;\n disabled?: boolean;\n required?: boolean;\n readOnly?: boolean;\n className?: string;\n containerClassName?: string;\n inputClassName?: string;\n variantClassName?: string;\n sizeClassName?: string;\n style?: React.CSSProperties;\n};\n\nconst Input = ({\n children,\n variant = 'none',\n type = 'text',\n inputSize = 'md',\n placeholder,\n value,\n onChange,\n onFocus,\n onBlur,\n disabled = false,\n required = false,\n readOnly = false,\n className = '',\n containerClassName = 'relative inline-block',\n inputClassName = 'border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500',\n variantClassName = '',\n sizeClassName = '',\n style,\n ...props\n}: InputProps & AllInputProps) => {\n const baseClasses = containerClassName;\n\n const variantClasses: Record<InputVariant, string> = {\n none: '',\n primary: 'bg-blue-600 text-white border-blue-600 focus:ring-blue-500 focus:border-blue-500',\n secondary: 'bg-gray-600 text-white border-gray-600 focus:ring-gray-500 focus:border-gray-500',\n outline: 'border-gray-300 text-gray-700 bg-white focus:ring-blue-500 focus:border-blue-500',\n danger: 'bg-red-600 text-white border-red-600 focus:ring-red-500 focus:border-red-500',\n success: 'bg-green-600 text-white border-green-600 focus:ring-green-500 focus:border-green-500'\n };\n\n const sizeClasses: Record<InputSize, string> = {\n sm: 'px-2 py-1 text-sm',\n md: 'px-3 py-2 text-base',\n lg: 'px-4 py-3 text-lg',\n xl: 'px-6 py-4 text-xl'\n };\n\n const classes = `\n ${baseClasses}\n ${variantClasses[variant]}\n ${sizeClasses[inputSize]}\n ${variantClassName}\n ${sizeClassName}\n ${className}\n ${disabled ? 'opacity-50 cursor-not-allowed' : ''}\n ${readOnly ? 'bg-gray-100 cursor-not-allowed' : ''}\n `.trim();\n\n return (\n <div className={classes} style={style}>\n {children && (\n <label className=\"block text-sm font-medium text-gray-700 mb-1\">\n {children}\n </label>\n )}\n <input\n type={type}\n placeholder={placeholder}\n value={value}\n onChange={(e) => onChange?.(e.target.value)}\n onFocus={onFocus}\n onBlur={onBlur}\n disabled={disabled}\n required={required}\n readOnly={readOnly}\n className={`\n ${inputClassName}\n ${variantClasses[variant]}\n ${sizeClasses[inputSize]}\n ${variantClassName}\n ${sizeClassName}\n `.trim()}\n {...props}\n />\n </div>\n );\n};\n\nexport default Input;\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
1
+ {"version":3,"file":"luna-components-library.js","names":[],"sources":["../node_modules/react/cjs/react-jsx-runtime.production.js","../node_modules/react/jsx-runtime.js","../src/components/Button.tsx","../src/components/Card.tsx","../src/components/Anchor.tsx","../src/components/Accordion.tsx","../src/components/Spinner.tsx","../src/components/DropDown.tsx","../src/components/ProgressBar.tsx","../src/components/Typed.tsx","../src/components/Preloader.tsx","../src/components/ScrollTop.tsx","../src/components/WhatsApp.tsx","../src/components/Modal.tsx","../src/components/Input.tsx","../src/utilities/apiFetch.util.ts","../src/utilities/httpClient.util.ts","../src/utilities/storage.util.ts","../src/utilities/formatters.util.ts","../src/utilities/validators.util.ts","../src/utilities/logger.util.ts","../src/hooks/useFetch.hook.ts","../src/hooks/useLocalStorage.hook.ts","../src/hooks/useDebounce.hook.ts"],"sourcesContent":["/**\n * @license React\n * react-jsx-runtime.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\");\nfunction jsxProd(type, config, maybeKey) {\n var key = null;\n void 0 !== maybeKey && (key = \"\" + maybeKey);\n void 0 !== config.key && (key = \"\" + config.key);\n if (\"key\" in config) {\n maybeKey = {};\n for (var propName in config)\n \"key\" !== propName && (maybeKey[propName] = config[propName]);\n } else maybeKey = config;\n config = maybeKey.ref;\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n ref: void 0 !== config ? config : null,\n props: maybeKey\n };\n}\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.jsx = jsxProd;\nexports.jsxs = jsxProd;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","import React from 'react';\n\n// Button variants and sizes\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light' | 'link';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type AllButtonProps = React.ComponentPropsWithoutRef<'button'>;\n\nexport type ButtonProps = {\n children: React.ReactNode;\n variant?: ButtonVariant;\n size?: ButtonSize;\n onClick?: () => void;\n disabled?: boolean;\n className?: string;\n containerClassName?: string;\n variantClassName?: string;\n sizeClassName?: string;\n style?: React.CSSProperties;\n}\n\n{/* onCLick default should open window.open('https://andreychaconresumereact.netlify.app/', '_blank') */ }\n\nconst Button = ({\n children,\n variant = 'primary',\n size = 'sm',\n onClick = () =>\n void 0,\n disabled = false,\n className = '',\n containerClassName = 'font-medium rounded-lg transition-colors focus:outline-none focus:ring-2',\n variantClassName = 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',\n sizeClassName = 'px-3 py-1.5 text-sm',\n style,\n ...props\n}: AllButtonProps & ButtonProps) => {\n const baseClasses = containerClassName;\n\n const variantClasses = {\n primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',\n secondary: 'bg-gray-600 text-white hover:bg-gray-700 focus:ring-gray-500',\n outline: 'border border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-gray-500',\n success: 'bg-green-600 text-white hover:bg-green-700 focus:ring-green-500',\n danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',\n warning: 'bg-yellow-500 text-white hover:bg-yellow-600 focus:ring-yellow-500',\n info: 'bg-cyan-600 text-white hover:bg-cyan-700 focus:ring-cyan-500',\n dark: 'bg-gray-900 text-white hover:bg-gray-800 focus:ring-gray-900',\n light: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-300',\n link: 'text-blue-600 hover:text-blue-800 hover:underline focus:outline-none focus:ring-2 focus:ring-blue-500',\n };\n\n const sizeClasses = {\n sm: 'px-3 py-1.5 text-sm',\n md: 'px-4 py-2 text-base',\n lg: 'px-6 py-3 text-lg',\n };\n\n const classes = `\n ${baseClasses}\n ${variantClasses[variant]}\n ${sizeClasses[size]}\n ${disabled ? 'opacity-50 cursor-not-allowed' : ''}\n ${className}\n `.trim();\n\n return (\n <button\n className={classes}\n onClick={onClick}\n disabled={disabled}\n style={style}\n {...props}\n >\n {children}\n </button>\n );\n};\n\nexport default Button;\n","import React from 'react';\n\n// Card padding and shadow variants\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg';\nexport type CardShadow = 'none' | 'sm' | 'md' | 'lg';\n\nexport type CardProps = {\n children: React.ReactNode;\n title?: string;\n className?: string;\n containerClassName?: string;\n titleClassName?: string;\n padding?: CardPadding;\n shadow?: CardShadow;\n style?: React.CSSProperties;\n}\n\nconst Card = ({\n children,\n title,\n className = '',\n containerClassName = 'bg-white rounded-lg border border-gray-200',\n titleClassName = 'text-lg font-semibold text-gray-900',\n padding = 'md',\n shadow = 'md',\n style,\n}: CardProps) => {\n const paddingClasses = {\n none: '',\n sm: 'p-3',\n md: 'p-4',\n lg: 'p-6',\n };\n\n const shadowClasses = {\n none: '',\n sm: 'shadow-sm',\n md: 'shadow-md',\n lg: 'shadow-lg',\n };\n\n const classes = `\n ${containerClassName}\n ${paddingClasses[padding]}\n ${shadowClasses[shadow]}\n ${className}\n `.trim();\n\n return (\n <div className={classes} style={style}>\n {title && (\n <div className=\"mb-4\">\n <h3 className={titleClassName}>{title}</h3>\n </div>\n )}\n {children}\n </div>\n );\n};\n\nexport default Card;\n","import React from 'react';\r\n\r\n// Anchor link variants and sizes\r\nexport type AnchorVariant = 'none' | 'primary' | 'secondary' | 'outline';\r\nexport type AnchorSize = 'sm' | 'md' | 'lg';\r\nexport type AllAnchorProps = React.ComponentPropsWithoutRef<'a'>;\r\n\r\nexport type AnchorProps = {\r\n children?: React.ReactNode;\r\n variant?: AnchorVariant;\r\n size?: AnchorSize;\r\n href?: string;\r\n className?: string;\r\n containerClassName?: string;\r\n variantClassName?: string;\r\n sizeClassName?: string;\r\n target?: string;\r\n rel?: string;\r\n style?: React.CSSProperties;\r\n};\r\n\r\nconst Anchor = ({\r\n children = \"Pablo Andrey Chacon Luna\",\r\n variant = 'none',\r\n size = 'sm',\r\n href = 'https://andreychaconresumereact.netlify.app/',\r\n className,\r\n containerClassName = variant === 'none' ? 'font-medium transition-colors focus:outline-none' : 'font-medium rounded-lg transition-colors focus:outline-none focus:ring-2',\r\n variantClassName = 'bg-blue-600 text-white hover:bg-blue-700',\r\n sizeClassName = 'px-3 py-1.5 text-sm',\r\n target = '_blank',\r\n rel = 'noopener noreferrer',\r\n style,\r\n ...props\r\n}: AnchorProps & AllAnchorProps) => {\r\n\r\n const baseClasses = containerClassName;\r\n\r\n const variantClasses = {\r\n none: '',\r\n primary: 'bg-blue-600 text-white hover:bg-blue-700',\r\n secondary: 'bg-gray-600 text-white hover:bg-gray-700',\r\n outline: 'border border-gray-300 text-gray-700 hover:bg-gray-50',\r\n };\r\n\r\n const sizeClasses = {\r\n sm: 'px-3 py-1.5 text-sm',\r\n md: 'px-4 py-2 text-base',\r\n lg: 'px-6 py-3 text-lg',\r\n };\r\n\r\n const classes = `\r\n ${baseClasses}\r\n ${variantClasses[variant]}\r\n ${sizeClasses[size]}\r\n ${variantClassName}\r\n ${sizeClassName}\r\n ${className}\r\n `.trim();\r\n\r\n return (\r\n <a href={href} target={target} rel={rel} className={classes} style={style} {...props}>\r\n {children}\r\n </a>\r\n );\r\n};\r\n\r\nexport default Anchor;","\r\nimport React from 'react';\r\n\r\n// Accordion component for collapsible content\r\nexport type AccordionProps = {\r\n active: boolean;\r\n onClick: () => void;\r\n header: React.ReactNode;\r\n content: React.ReactNode;\r\n className?: string;\r\n containerClassName?: string;\r\n headerClassName?: string;\r\n contentClassName?: string;\r\n style?: React.CSSProperties;\r\n}\r\n\r\nconst Accordion = ({ active,\r\n onClick,\r\n header,\r\n content,\r\n className = '',\r\n containerClassName = 'border border-gray-200 rounded-lg overflow-hidden',\r\n headerClassName = 'w-full px-4 py-3 text-left bg-gray-50 hover:bg-gray-100 focus:bg-gray-100 focus:outline-none transition-colors duration-200 flex justify-between items-center',\r\n contentClassName = 'transition-all duration-300 ease-in-out',\r\n style\r\n}: AccordionProps) => {\r\n return (\r\n <div className={`${containerClassName} ${className}`} style={style}>\r\n <button\r\n onClick={onClick}\r\n className={headerClassName}\r\n aria-expanded={active}\r\n >\r\n {header}\r\n <svg\r\n className={`w-5 h-5 text-gray-500 transition-transform duration-200 ${active ? 'transform rotate-180' : ''\r\n }`}\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n strokeWidth={2}\r\n d=\"M19 9l-7 7-7-7\"\r\n />\r\n </svg>\r\n </button>\r\n\r\n <div\r\n className={`${contentClassName} ${active ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'\r\n } overflow-hidden`}\r\n >\r\n <div className={`p-4 bg-white border-t border-gray-200 ${contentClassName}`}>\r\n {content}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\nexport default Accordion;","import React from 'react';\n\nexport type SpinnerSize = 'sm' | 'md' | 'lg';\nexport type SpinnerType = 'circle' | 'dots' | 'pulse' | 'bars';\n\nexport type SpinnerProps = {\n className?: string;\n containerClassName?: string;\n dotClassName?: string;\n barClassName?: string;\n size?: SpinnerSize;\n type?: SpinnerType;\n style?: React.CSSProperties;\n};\n\nconst Spinner = ({\n className,\n containerClassName = 'flex gap-1',\n dotClassName = 'bg-blue-600 rounded-full animate-bounce',\n barClassName = 'bg-blue-600 animate-pulse',\n size = 'md',\n type = 'circle',\n style\n}: SpinnerProps) => {\n const sizeClasses = {\n sm: 'w-4 h-4',\n md: 'w-6 h-6',\n lg: 'w-8 h-8'\n };\n\n const dotSizeClasses = {\n sm: 'w-1 h-1',\n md: 'w-2 h-2',\n lg: 'w-3 h-3'\n };\n\n const barSizeClasses = {\n sm: 'w-1 h-4',\n md: 'w-1 h-6',\n lg: 'w-1 h-8'\n };\n\n if (type === 'dots') {\n return (\n <div role=\"status\" className={`${containerClassName} ${className || ''}`}>\n <span className=\"sr-only\">Loading...</span>\n <div className={`${dotSizeClasses[size]} ${dotClassName}`} style={{ animationDelay: '0ms' }}></div>\n <div className={`${dotSizeClasses[size]} ${dotClassName}`} style={{ animationDelay: '150ms' }}></div>\n <div className={`${dotSizeClasses[size]} ${dotClassName}`} style={{ animationDelay: '300ms' }}></div>\n </div>\n );\n }\n\n if (type === 'pulse') {\n return (\n <div role=\"status\" className={`${sizeClasses[size]} ${className || ''}`}>\n <span className=\"sr-only\">Loading...</span>\n <div className={`${sizeClasses[size]} ${dotClassName}`}></div>\n </div>\n );\n }\n\n if (type === 'bars') {\n return (\n <div role=\"status\" className={`flex gap-1 items-center ${containerClassName} ${className || ''}`}>\n <span className=\"sr-only\">Loading...</span>\n <div className={`${barSizeClasses[size]} ${barClassName}`} style={{ animationDelay: '0ms' }}></div>\n <div className={`${barSizeClasses[size]} ${barClassName}`} style={{ animationDelay: '200ms' }}></div>\n <div className={`${barSizeClasses[size]} ${barClassName}`} style={{ animationDelay: '400ms' }}></div>\n <div className={`${barSizeClasses[size]} ${barClassName}`} style={{ animationDelay: '600ms' }}></div>\n </div>\n );\n }\n\n // Default circle spinner\n return (\n <div\n role=\"status\"\n className={`inline-block animate-spin rounded-full border-2 border-gray-300 border-t-blue-600 ${sizeClasses[size]} ${className || ''}`}\n style={style}\n >\n <span className=\"sr-only\">Loading...</span>\n </div>\n );\n};\n\nexport default Spinner;\n","{/* Dropdown component for selecting options */ }\r\nimport React, { useState } from 'react';\r\n\r\ntype DropDownOption = {\r\n value: string;\r\n label: React.ReactNode;\r\n};\r\n\r\nexport type DropDownProps = {\r\n toggle: React.ReactNode;\r\n options: React.ReactNode[] | DropDownOption[];\r\n selected: React.ReactNode;\r\n onChange: (value: React.ReactNode) => void;\r\n className?: string;\r\n containerClassName?: string;\r\n dropdownClassName?: string;\r\n optionsContainerClassName?: string;\r\n optionClassName?: string;\r\n style?: React.CSSProperties;\r\n};\r\n\r\nconst DropDown = ({\r\n toggle,\r\n options,\r\n selected,\r\n onChange,\r\n className = '',\r\n containerClassName = 'relative inline-block text-left',\r\n dropdownClassName = 'absolute z-50 mt-2 w-48 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none',\r\n optionsContainerClassName = 'py-1 flex flex-col',\r\n optionClassName = 'block w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900',\r\n style\r\n}: DropDownProps) => {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const handleToggle = () => {\r\n setIsOpen(!isOpen);\r\n };\r\n\r\n const handleOptionClick = (option: React.ReactNode) => {\r\n onChange(option);\r\n setIsOpen(false);\r\n };\r\n\r\n return (\r\n <div className={`${containerClassName} ${className}`} style={style}>\r\n <div onClick={handleToggle} className=\"cursor-pointer\">\r\n {toggle}\r\n </div>\r\n\r\n {isOpen && (\r\n <div className={dropdownClassName}>\r\n <div className={optionsContainerClassName}>\r\n {options.map((option, index) => {\r\n const isOptionObject = typeof option === 'object' && option !== null && 'value' in option;\r\n const optionValue = isOptionObject ? (option as DropDownOption).value : option;\r\n const optionLabel = isOptionObject ? (option as DropDownOption).label : option;\r\n\r\n return (\r\n <button\r\n key={index}\r\n onClick={() => handleOptionClick(optionValue)}\r\n className={optionClassName}\r\n >\r\n {optionLabel}\r\n </button>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\nexport default DropDown;","import React from 'react';\r\n\r\n// Progress bar color variants\r\nexport type ProgressBarVariant = 'primary' | 'success' | 'warning' | 'danger' | 'dark' | 'light';\r\n\r\n// Core progress bar props\r\nexport type ProgressBarProps = {\r\n progress: number;\r\n max: number;\r\n min: number;\r\n 'aria-label': string;\r\n}\r\n\r\n// Extended props with styling options\r\nexport type ProgressBarPropsWithClassName = ProgressBarProps & {\r\n className?: React.CSSProperties;\r\n style?: React.CSSProperties;\r\n containerClassName?: string;\r\n barClassName?: string;\r\n variant?: ProgressBarVariant;\r\n};\r\n\r\nconst ProgressBar = ({\r\n progress,\r\n max,\r\n min,\r\n 'aria-label': ariaLabel,\r\n className,\r\n style,\r\n containerClassName = 'w-full bg-gray-200 rounded-full h-4 overflow-hidden',\r\n barClassName = 'h-full rounded-full transition-all duration-300 flex items-center justify-center text-xs font-medium',\r\n variant = 'primary'\r\n}: ProgressBarPropsWithClassName) => {\r\n const variantClasses = {\r\n primary: {\r\n bg: 'bg-blue-600',\r\n text: 'text-white',\r\n containerBg: 'bg-gray-200'\r\n },\r\n success: {\r\n bg: 'bg-green-600',\r\n text: 'text-white',\r\n containerBg: 'bg-gray-200'\r\n },\r\n warning: {\r\n bg: 'bg-yellow-500',\r\n text: 'text-gray-900',\r\n containerBg: 'bg-gray-200'\r\n },\r\n danger: {\r\n bg: 'bg-red-600',\r\n text: 'text-white',\r\n containerBg: 'bg-gray-200'\r\n },\r\n dark: {\r\n bg: 'bg-gray-800',\r\n text: 'text-white',\r\n containerBg: 'bg-gray-300'\r\n },\r\n light: {\r\n bg: 'bg-gray-100',\r\n text: 'text-gray-900',\r\n containerBg: 'bg-gray-300'\r\n }\r\n };\r\n\r\n const currentVariant = variantClasses[variant];\r\n const barClasses = `${currentVariant.bg} ${barClassName} ${currentVariant.text}`;\r\n\r\n return (\r\n <div className={containerClassName} style={style}>\r\n <div\r\n role=\"progressbar\"\r\n className={barClasses}\r\n aria-valuenow={progress}\r\n aria-valuemin={min}\r\n aria-valuemax={max}\r\n style={{ width: `${progress}%`, ...className }}\r\n >\r\n {progress > 10 && `${progress}%`}\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\nexport default ProgressBar;","import React, { useState, useEffect, CSSProperties } from 'react';\r\n\r\n// Typing animation configuration\r\ntype TypedStyle = CSSProperties & {\r\n animation?: string;\r\n animationDelay?: string;\r\n};\r\n\r\ntype TypedProps = {\r\n strings: string[];\r\n typeSpeed?: number;\r\n backSpeed?: number;\r\n backDelay?: number;\r\n startDelay?: number;\r\n loop?: boolean;\r\n showCursor?: boolean;\r\n className?: string;\r\n containerClassName?: string;\r\n typedClassName?: string;\r\n cursorClassName?: string;\r\n style?: TypedStyle;\r\n};\r\n\r\nconst Typed = ({\r\n strings,\r\n typeSpeed = 50,\r\n backSpeed = 30,\r\n backDelay = 500,\r\n startDelay = 0,\r\n loop = true,\r\n showCursor = true,\r\n className = '',\r\n containerClassName = 'inline-block',\r\n typedClassName = 'typed',\r\n cursorClassName = 'typed-cursor ml-1 inline-block w-0.5 h-5 bg-current',\r\n style = {},\r\n}: TypedProps) => {\r\n const [currentStringIndex, setCurrentStringIndex] = useState(0);\r\n const [currentText, setCurrentText] = useState('');\r\n const [isDeleting, setIsDeleting] = useState(false);\r\n const [isPaused, setIsPaused] = useState(false);\r\n const [cursorOpacity, setCursorOpacity] = useState(1);\r\n\r\n useEffect(() => {\r\n const timer = setTimeout(() => {\r\n setIsPaused(false);\r\n }, startDelay);\r\n\r\n return () => clearTimeout(timer);\r\n }, [startDelay]);\r\n\r\n useEffect(() => {\r\n if (isPaused) return;\r\n\r\n const currentString = strings[currentStringIndex] || '';\r\n\r\n const timer = setTimeout(() => {\r\n if (!isDeleting) {\r\n // Typing\r\n if (currentText.length < currentString.length) {\r\n setCurrentText(currentText + currentString[currentText.length]);\r\n } else {\r\n // Finished typing, wait before deleting\r\n if (loop) {\r\n setIsPaused(true);\r\n setTimeout(() => {\r\n setIsPaused(false);\r\n setIsDeleting(true);\r\n }, backDelay);\r\n }\r\n }\r\n } else {\r\n // Deleting\r\n if (currentText.length > 0) {\r\n setCurrentText(currentText.slice(0, -1));\r\n } else {\r\n // Finished deleting, move to next string\r\n setIsDeleting(false);\r\n setCurrentStringIndex((prevIndex) =>\r\n prevIndex === strings.length - 1 ? 0 : prevIndex + 1\r\n );\r\n }\r\n }\r\n }, isDeleting ? backSpeed : typeSpeed);\r\n\r\n return () => clearTimeout(timer);\r\n }, [currentText, isDeleting, currentStringIndex, strings, typeSpeed, backSpeed, backDelay, loop, isPaused]);\r\n\r\n // Cursor fade effect\r\n useEffect(() => {\r\n const fadeTimer = setInterval(() => {\r\n setCursorOpacity(prev => {\r\n if (prev === 1) return 0;\r\n return 1;\r\n });\r\n }, 750);\r\n\r\n return () => clearInterval(fadeTimer);\r\n }, []);\r\n\r\n return (\r\n <span className={`${containerClassName} ${className}`} style={style}>\r\n <span className={typedClassName}>{currentText}</span>\r\n {showCursor && (\r\n <span\r\n className={cursorClassName}\r\n aria-hidden=\"true\"\r\n style={{\r\n opacity: cursorOpacity\r\n }}\r\n >\r\n\r\n </span>\r\n )}\r\n </span>\r\n );\r\n};\r\n\r\nexport default Typed;","import React, { useEffect, useState } from 'react';\n\nexport type PreloaderProps = {\n /** Loading state - if true, preloader is shown */\n isLoading?: boolean;\n /** Duration in milliseconds before auto-hide */\n duration?: number;\n /** Background color overlay */\n backgroundColor?: string;\n /** Accent color for the spinner */\n accentColor?: string;\n /** Size of the spinner in pixels */\n size?: number;\n /** Border width of the spinner */\n borderWidth?: number;\n /** Custom className for the preloader */\n className?: string;\n /** Custom className for the spinner */\n spinnerClassName?: string;\n /** Custom z-index */\n zIndex?: number;\n /** Callback when preloader finishes */\n onComplete?: () => void;\n /** Custom inline styles */\n style?: React.CSSProperties;\n}\n\nconst Preloader = ({\n isLoading: externalLoading,\n duration = 1000,\n backgroundColor,\n accentColor,\n size = 90,\n borderWidth = 6,\n className = '',\n spinnerClassName = '',\n zIndex = 999999,\n onComplete,\n style\n}: PreloaderProps) => {\n const [internalLoading, setInternalLoading] = useState(true);\n\n // Use external loading state if provided, otherwise use internal state\n const isLoading = externalLoading !== undefined ? externalLoading : internalLoading;\n\n useEffect(() => {\n // Only auto-hide if we're using internal loading state\n if (externalLoading === undefined) {\n const timer = setTimeout(() => {\n setInternalLoading(false);\n onComplete?.();\n }, duration);\n\n return () => clearTimeout(timer);\n }\n }, [duration, externalLoading, onComplete]);\n\n // Handle external loading state changes - auto-hide when externalLoading is true\n useEffect(() => {\n if (externalLoading === true) {\n const timer = setTimeout(() => {\n onComplete?.();\n }, duration);\n\n return () => clearTimeout(timer);\n }\n }, [externalLoading, duration, onComplete]);\n\n const preloaderStyle: React.CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex,\n overflow: 'hidden',\n background: backgroundColor || 'var(--background-color, #00000018)',\n transition: 'all 0.6s ease-out',\n display: isLoading ? 'block' : 'none'\n };\n\n const spinnerStyle: React.CSSProperties = {\n position: 'fixed',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n border: `${borderWidth}px solid #ffffff`,\n borderColor: `${accentColor || 'var(--accent-color, #007bff)'} transparent transparent transparent`,\n borderRadius: '50%',\n width: `${size}px`,\n height: `${size}px`,\n animation: 'animate-preloader 1.5s linear infinite'\n };\n\n return (\n <>\n <div\n className={`preloader-overlay ${className}`}\n style={{ ...preloaderStyle, ...style }}\n >\n <div\n className={`preloader-spinner ${spinnerClassName}`}\n style={spinnerStyle}\n />\n </div>\n\n {/* Global styles for animation */}\n <style>{`\n @keyframes animate-preloader {\n 0% {\n transform: translate(-50%, -50%) rotate(0deg);\n }\n 100% {\n transform: translate(-50%, -50%) rotate(360deg);\n }\n }\n `}</style>\n </>\n );\n};\n\nexport default Preloader;\n","import React, { useEffect, useState, useRef } from 'react';\r\n\r\nexport type ScrollTopProps = {\r\n /** Scroll position threshold to show the button (in pixels) */\r\n threshold?: number;\r\n /** Custom className for the button */\r\n className?: string;\r\n /** Custom icon/content for the button */\r\n children?: React.ReactNode;\r\n /** Position of the button */\r\n position?: 'bottom-right' | 'bottom-left' | 'bottom-center' | 'top-right' | 'top-left' | 'top-center';\r\n /** Size of the button */\r\n size?: 'sm' | 'md' | 'lg';\r\n /** Shape of the button */\r\n shape?: 'circle' | 'square' | 'rounded';\r\n /** Whether to show the button initially */\r\n showInitially?: boolean;\r\n /** Custom scroll behavior */\r\n scrollBehavior?: 'auto' | 'smooth';\r\n /** Custom styles */\r\n style?: React.CSSProperties;\r\n /** Callback when button is clicked */\r\n onClick?: () => void;\r\n /** Callback when visibility changes */\r\n onVisibilityChange?: (isVisible: boolean) => void;\r\n /** Element ID or selector to check visibility for showing the button */\r\n targetElement?: string;\r\n /** Percentage of page scroll to show the button (0-100) */\r\n scrollPercentage?: number;\r\n}\r\n\r\nconst ScrollTop = ({\r\n threshold = 100,\r\n className = '',\r\n children,\r\n position = 'bottom-right',\r\n size = 'md',\r\n shape = 'circle',\r\n showInitially = false,\r\n scrollBehavior = 'smooth',\r\n style,\r\n onClick,\r\n onVisibilityChange,\r\n targetElement,\r\n scrollPercentage,\r\n}: ScrollTopProps) => {\r\n const [isVisible, setIsVisible] = useState(showInitially);\r\n\r\n useEffect(() => {\r\n // Show/hide scroll to top button based on scroll position\r\n const toggleVisibility = () => {\r\n let shouldBeVisible = false;\r\n\r\n // Check scroll percentage first\r\n if (scrollPercentage !== undefined) {\r\n const maxScroll = document.documentElement.scrollHeight - window.innerHeight;\r\n const currentScroll = window.scrollY;\r\n const percentage = (currentScroll / maxScroll) * 100;\r\n shouldBeVisible = percentage >= scrollPercentage;\r\n }\r\n // Check target element visibility\r\n else if (targetElement) {\r\n const element = document.querySelector(targetElement);\r\n if (element) {\r\n const rect = element.getBoundingClientRect();\r\n const isInViewport = rect.top < window.innerHeight && rect.bottom > 0;\r\n shouldBeVisible = isInViewport; // Show when element is visible\r\n } else {\r\n // If element doesn't exist, fall back to threshold behavior\r\n shouldBeVisible = window.scrollY > threshold;\r\n }\r\n }\r\n // Default threshold behavior\r\n else {\r\n shouldBeVisible = window.scrollY > threshold;\r\n }\r\n\r\n // Set visibility immediately\r\n setIsVisible(shouldBeVisible);\r\n onVisibilityChange?.(shouldBeVisible);\r\n };\r\n\r\n window.addEventListener('scroll', toggleVisibility, { passive: true });\r\n // Initial check\r\n toggleVisibility();\r\n\r\n return () => {\r\n window.removeEventListener('scroll', toggleVisibility);\r\n };\r\n }, [threshold, onVisibilityChange, targetElement, scrollPercentage]);\r\n\r\n const scrollToTop = () => {\r\n window.scrollTo({\r\n top: 0,\r\n behavior: scrollBehavior\r\n });\r\n onClick?.();\r\n };\r\n\r\n const handleClick = (e: React.MouseEvent) => {\r\n e.preventDefault();\r\n scrollToTop();\r\n };\r\n\r\n // Position classes\r\n const positionClasses = {\r\n 'bottom-right': 'fixed bottom-8 right-8',\r\n 'bottom-left': 'fixed bottom-8 left-8',\r\n 'bottom-center': 'fixed bottom-8 left-1/2 transform -translate-x-1/2',\r\n 'top-right': 'fixed top-8 right-8',\r\n 'top-left': 'fixed top-8 left-8',\r\n 'top-center': 'fixed top-8 left-1/2 transform -translate-x-1/2'\r\n };\r\n\r\n // Size classes\r\n const sizeClasses = {\r\n sm: 'w-8 h-8 text-sm',\r\n md: 'w-12 h-12 text-base',\r\n lg: 'w-16 h-16 text-lg'\r\n };\r\n\r\n // Shape classes\r\n const shapeClasses = {\r\n circle: 'rounded-full',\r\n square: 'rounded-none',\r\n rounded: 'rounded-lg'\r\n };\r\n\r\n const buttonClasses = `\r\n ${positionClasses[position]}\r\n ${sizeClasses[size]}\r\n ${shapeClasses[shape]}\r\n ${className}\r\n flex items-center justify-center\r\n bg-blue-600 hover:bg-blue-700\r\n text-white\r\n shadow-lg hover:shadow-xl\r\n transition-all duration-300 ease-in-out\r\n cursor-pointer\r\n z-50\r\n ${isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4 pointer-events-none'}\r\n `;\r\n\r\n const defaultContent = (\r\n <svg\r\n className=\"w-4 h-4\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n strokeWidth={2}\r\n d=\"M5 10l7-7m0 0l7 7m-7-7v18\"\r\n />\r\n </svg>\r\n );\r\n\r\n return (\r\n <button\r\n className={buttonClasses}\r\n onClick={handleClick}\r\n style={style}\r\n aria-label=\"Scroll to top\"\r\n title=\"Scroll to top\"\r\n >\r\n {children || defaultContent}\r\n </button>\r\n );\r\n};\r\n\r\nexport default ScrollTop;\r\n","import React, { useState, useEffect } from 'react';\n\nexport interface WhatsAppProps {\n /** Phone number for WhatsApp (with country code, without + or spaces) */\n phone?: string;\n /** Default message to send */\n message?: string;\n /** Position of the button */\n position?: 'bottom-right' | 'bottom-left' | 'bottom-center' | 'top-right' | 'top-left' | 'top-center';\n /** Size of the button */\n size?: 'sm' | 'md' | 'lg';\n /** Show tooltip on hover */\n showTooltip?: boolean;\n /** Tooltip text */\n tooltipText?: string;\n /** Custom className for the button */\n className?: string;\n /** Custom className for the tooltip */\n tooltipClassName?: string;\n /** Custom styles */\n style?: React.CSSProperties;\n /** Callback when button is clicked */\n onClick?: () => void;\n /** Z-index for the button */\n zIndex?: number;\n /** Whether to open in new tab */\n openInNewTab?: boolean;\n}\n\nconst WhatsApp = ({\n phone = '',\n message = '¡Hola! Me gustaría obtener más información.',\n position = 'bottom-right',\n size = 'md',\n showTooltip = true,\n tooltipText = '¿En qué podemos ayudarte?',\n className = '',\n tooltipClassName = '',\n style,\n onClick,\n zIndex = 50,\n openInNewTab = true\n}: WhatsAppProps) => {\n const [isHovered, setIsHovered] = useState(false);\n\n // Don't render if no phone number provided\n if (!phone) return null;\n\n const handleWhatsAppClick = () => {\n const cleanPhone = phone.replace(/[^\\d]/g, '');\n const encodedMessage = encodeURIComponent(message);\n const whatsappUrl = `https://wa.me/${cleanPhone}?text=${encodedMessage}`;\n\n if (openInNewTab) {\n window.open(whatsappUrl, '_blank');\n } else {\n window.location.href = whatsappUrl;\n }\n\n onClick?.();\n };\n\n // Position classes\n const positionClasses = {\n 'bottom-right': 'bottom-6 right-6',\n 'bottom-left': 'bottom-6 left-6',\n 'bottom-center': 'bottom-6 left-1/2 transform -translate-x-1/2',\n 'top-right': 'top-6 right-6',\n 'top-left': 'top-6 left-6',\n 'top-center': 'top-6 left-1/2 transform -translate-x-1/2'\n };\n\n // Size classes\n const sizeClasses = {\n sm: 'w-10 h-10',\n md: 'w-14 h-14',\n lg: 'w-16 h-16'\n };\n\n // Icon size based on button size\n const iconSizes = {\n sm: 20,\n md: 32,\n lg: 40\n };\n\n const buttonClasses = `\n fixed ${positionClasses[position]}\n ${sizeClasses[size]}\n bg-[#25D366] hover:bg-[#128C7E]\n text-white rounded-full shadow-2xl\n flex items-center justify-center\n transition-all duration-300 hover:scale-110\n z-${zIndex}\n group\n ${className}\n `;\n\n return (\n <>\n <button\n className={buttonClasses}\n onClick={handleWhatsAppClick}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n style={style}\n aria-label=\"Chatear por WhatsApp\"\n title=\"Chatear por WhatsApp\"\n >\n <svg\n width={iconSizes[size]}\n height={iconSizes[size]}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\" />\n </svg>\n </button>\n\n {showTooltip && (\n <div\n className={`\n fixed ${positionClasses[position].replace('6', '20').replace('bottom-6', 'bottom-20').replace('top-6', 'top-20')}\n bg-white text-gray-800 px-3 py-1 rounded-md text-sm font-semibold\n shadow-md whitespace-nowrap pointer-events-none\n transition-opacity duration-300\n ${isHovered ? 'opacity-100' : 'opacity-0'}\n ${position.includes('right') ? 'right-20' : position.includes('left') ? 'left-20' : 'left-1/2 transform -translate-x-1/2'}\n ${tooltipClassName}\n `}\n style={{\n [position.includes('bottom') ? 'bottom' : 'top']: position.includes('bottom') ? '5.5rem' : '5.5rem'\n }}\n >\n {tooltipText}\n </div>\n )}\n </>\n );\n};\n\nexport default WhatsApp;\n","import React, { useEffect, useRef } from 'react';\n\n// Modal size variants\ntype ModalSize = 'sm' | 'md' | 'lg' | 'xl';\n\nexport interface ModalProps {\n /** Whether the modal is visible */\n show: boolean;\n /** Callback when modal is closed */\n onHide: () => void;\n /** Modal size variant */\n size?: ModalSize;\n /** Whether to center the modal vertically */\n centered?: boolean;\n /** Whether to show backdrop overlay */\n backdrop?: boolean | 'static';\n /** Whether to close modal when backdrop is clicked */\n backdropClose?: boolean;\n /** Whether to close modal when ESC key is pressed */\n keyboard?: boolean;\n /** Whether to show modal with animation */\n animation?: boolean;\n /** Custom className for the modal */\n className?: string;\n /** Custom className for the modal dialog */\n dialogClassName?: string;\n /** Custom className for the modal content */\n contentClassName?: string;\n /** Custom className for the modal header */\n headerClassName?: string;\n /** Custom className for the modal body */\n bodyClassName?: string;\n /** Custom className for the modal footer */\n footerClassName?: string;\n /** Modal title */\n title?: React.ReactNode;\n /** Modal header content */\n header?: React.ReactNode;\n /** Modal body content */\n children: React.ReactNode;\n /** Modal footer content */\n footer?: React.ReactNode;\n /** Whether to show close button in header */\n closeButton?: boolean;\n /** Custom inline styles */\n style?: React.CSSProperties;\n}\n\nconst Modal = ({\n show,\n onHide,\n size = 'md',\n centered = false,\n backdrop = true,\n backdropClose = true,\n keyboard = true,\n animation = true,\n className = '',\n dialogClassName = '',\n contentClassName = '',\n headerClassName = '',\n bodyClassName = '',\n footerClassName = '',\n title,\n header,\n children,\n footer,\n closeButton = true,\n style\n}: ModalProps) => {\n const modalRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n // Handle ESC key press\n useEffect(() => {\n if (!show || !keyboard) return;\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n onHide();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [show, keyboard, onHide]);\n\n // Handle backdrop click\n const handleBackdropClick = (event: React.MouseEvent) => {\n if (backdropClose) {\n // Check if click is on backdrop using data attribute\n const target = event.target as HTMLElement;\n if (target.getAttribute('data-backdrop') === 'true' || target.closest('[data-backdrop=\"true\"]')) {\n onHide();\n }\n }\n };\n\n // Focus management\n useEffect(() => {\n if (show) {\n previousActiveElement.current = document.activeElement as HTMLElement;\n // Focus the modal dialog\n if (modalRef.current) {\n modalRef.current.focus();\n }\n // Prevent body scroll\n document.body.style.overflow = 'hidden';\n } else {\n // Restore focus and body scroll\n if (previousActiveElement.current) {\n previousActiveElement.current.focus();\n }\n document.body.style.overflow = '';\n }\n\n return () => {\n document.body.style.overflow = '';\n };\n }, [show]);\n\n // Size classes\n const sizeClasses = {\n sm: 'max-w-sm',\n md: 'max-w-md',\n lg: 'max-w-lg',\n xl: 'max-w-xl'\n };\n\n const modalClasses = `\n fixed inset-0 z-60 flex items-center justify-center\n ${show ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}\n ${animation ? 'transition-opacity duration-300' : ''}\n ${className}\n `.trim();\n\n const dialogClasses = `\n relative w-full ${sizeClasses[size]} mx-auto\n ${centered ? 'flex items-center justify-center min-h-screen' : 'mt-8'}\n ${dialogClassName}\n `.trim();\n\n if (!show) return null;\n\n return (\n <div\n ref={modalRef}\n className={modalClasses}\n tabIndex={-1}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'modal-title' : undefined}\n style={style}\n >\n {/* Backdrop */}\n {backdrop && (\n <div\n data-backdrop=\"true\"\n className={`absolute inset-0 bg-black bg-opacity-50 ${animation ? 'transition-opacity duration-300' : ''} ${show ? 'opacity-100' : 'opacity-0'}`}\n onClick={handleBackdropClick}\n />\n )}\n\n {/* Modal Content */}\n <div className={dialogClasses}>\n <div className={`bg-white rounded-lg shadow-xl relative z-10 ${contentClassName}`}>\n {/* Modal Header */}\n {(header || title || closeButton) && (\n <div className={`flex items-center justify-between p-4 border-b border-gray-200 ${headerClassName}`}>\n {header || (title && (\n <h3 className=\"text-lg font-semibold text-gray-900\" id=\"modal-title\">\n {title}\n </h3>\n ))}\n {closeButton && (\n <button\n type=\"button\"\n className=\"text-gray-400 hover:text-gray-600 transition-colors\"\n onClick={onHide}\n aria-label=\"Close\"\n >\n <svg className=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </div>\n )}\n\n {/* Modal Body */}\n <div className={`p-4 ${bodyClassName}`}>\n {children}\n </div>\n\n {/* Modal Footer */}\n {footer && (\n <div className={`flex items-center justify-end p-4 border-t border-gray-200 space-x-2 ${footerClassName}`}>\n {footer}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n};\n\nexport default Modal;\n","import React from 'react';\n\n// Input variants and sizes\nexport type InputVariant = 'none' | 'primary' | 'secondary' | 'outline' | 'danger' | 'success';\nexport type InputSize = 'sm' | 'md' | 'lg' | 'xl';\nexport type InputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'date' | 'time' | 'datetime-local' | 'month' | 'week' | 'color' | 'file' | 'hidden' | 'image' | 'range' | 'reset' | 'submit';\n\nexport type AllInputProps = Omit<React.ComponentPropsWithoutRef<'input'>, 'onChange'>;\n\nexport type InputProps = {\n children?: React.ReactNode;\n variant?: InputVariant;\n inputSize?: InputSize;\n type?: InputType;\n placeholder?: string;\n value?: string;\n onChange?: (value: string) => void;\n onFocus?: () => void;\n onBlur?: () => void;\n disabled?: boolean;\n required?: boolean;\n readOnly?: boolean;\n className?: string;\n containerClassName?: string;\n inputClassName?: string;\n variantClassName?: string;\n sizeClassName?: string;\n style?: React.CSSProperties;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n};\n\nconst Input = ({\n children,\n variant = 'none',\n type = 'text',\n inputSize = 'md',\n placeholder,\n value,\n onChange,\n onFocus,\n onBlur,\n disabled = false,\n required = false,\n readOnly = false,\n className = '',\n containerClassName = 'relative inline-block',\n inputClassName = 'border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500',\n variantClassName = '',\n sizeClassName = '',\n style,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n ...props\n}: InputProps & AllInputProps) => {\n const baseClasses = containerClassName;\n\n const variantClasses: Record<InputVariant, string> = {\n none: '',\n primary: 'bg-blue-600 text-white border-blue-600 focus:ring-blue-500 focus:border-blue-500',\n secondary: 'bg-gray-600 text-white border-gray-600 focus:ring-gray-500 focus:border-gray-500',\n outline: 'border-gray-300 text-gray-700 bg-white focus:ring-blue-500 focus:border-blue-500',\n danger: 'bg-red-600 text-white border-red-600 focus:ring-red-500 focus:border-red-500',\n success: 'bg-green-600 text-white border-green-600 focus:ring-green-500 focus:border-green-500'\n };\n\n const sizeClasses: Record<InputSize, string> = {\n sm: 'px-2 py-1 text-sm',\n md: 'px-3 py-2 text-base',\n lg: 'px-4 py-3 text-lg',\n xl: 'px-6 py-4 text-xl'\n };\n\n const classes = `\n ${baseClasses}\n ${variantClasses[variant]}\n ${sizeClasses[inputSize]}\n ${variantClassName}\n ${sizeClassName}\n ${className}\n ${disabled ? 'opacity-50 cursor-not-allowed' : ''}\n ${readOnly ? 'bg-gray-100 cursor-not-allowed' : ''}\n `.trim();\n\n return (\n <div className={classes} style={style}>\n {children && (\n <label\n htmlFor={id}\n className=\"block text-sm font-medium text-gray-700 mb-1\"\n >\n {children}\n </label>\n )}\n <input\n id={id}\n type={type}\n placeholder={placeholder}\n value={value}\n onChange={(e) => onChange?.(e.target.value)}\n onFocus={onFocus}\n onBlur={onBlur}\n disabled={disabled}\n required={required}\n readOnly={readOnly}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n className={`\n ${inputClassName}\n ${variantClasses[variant]}\n ${sizeClasses[inputSize]}\n ${variantClassName}\n ${sizeClassName}\n `.trim()}\n {...props}\n />\n </div>\n );\n};\n\nexport default Input;\n","/**\n * A generic wrapper for the fetch API with error handling and response parsing.\n * @param url - The URL to fetch\n * @param options - Fetch options (method, headers, body, signal, etc.)\n * @returns Parsed JSON response\n */\nexport const apiFetch = async (url: string, options?: RequestInit) => {\n try {\n const response = await fetch(url, options);\n\n if (!response.ok) {\n // Handles 4xx and 5xx errors\n throw new Error(`HTTP error! Status: ${response.status} - ${response.statusText}`);\n }\n\n // Return the parsed JSON data\n return await response.json();\n } catch (error) {\n // Handle fetch cancellation\n if (error instanceof DOMException && error.name === 'AbortError') {\n console.log('Fetch aborted');\n return;\n }\n \n // Handle other errors\n if (error instanceof Error) {\n throw new Error(error.message);\n }\n \n throw new Error(String(error));\n }\n};\n","import { apiFetch } from \"./apiFetch.util\";\n\n/**\n * Generic GET request\n * @template T - Expected return type\n * @param url - Full URL or endpoint\n * @param options - Additional fetch options\n */\nexport const get = async <T>(url: string, options?: RequestInit): Promise<T> => {\n return apiFetch(url, { ...options, method: 'GET' });\n};\n\n/**\n * Generic POST request\n * @template T - Expected return type\n * @param url - Full URL or endpoint\n * @param body - Data to send\n * @param options - Additional fetch options\n */\nexport const post = async <T>(url: string, body: any, options?: RequestInit): Promise<T> => {\n return apiFetch(url, {\n ...options,\n method: 'POST',\n body: JSON.stringify(body),\n headers: {\n 'Content-Type': 'application/json',\n ...options?.headers,\n },\n });\n};\n\n/**\n * Generic PUT request\n * @template T - Expected return type\n * @param url - Full URL or endpoint\n * @param body - Data to send\n * @param options - Additional fetch options\n */\nexport const put = async <T>(url: string, body: any, options?: RequestInit): Promise<T> => {\n return apiFetch(url, {\n ...options,\n method: 'PUT',\n body: JSON.stringify(body),\n headers: {\n 'Content-Type': 'application/json',\n ...options?.headers,\n },\n });\n};\n\n/**\n * Generic DELETE request\n * @template T - Expected return type\n * @param url - Full URL or endpoint\n * @param options - Additional fetch options\n */\nexport const del = async <T>(url: string, options?: RequestInit): Promise<T> => {\n return apiFetch(url, { ...options, method: 'DELETE' });\n};\n\n// Generic HTTP Client Object\nexport const httpClient = {\n get,\n post,\n put,\n delete: del,\n};\n","/**\n * Utility for interacting with localStorage safely.\n */\nexport const storage = {\n get: <T>(key: string, defaultValue: T): T => {\n try {\n const item = window.localStorage.getItem(key);\n return item ? JSON.parse(item) : defaultValue;\n } catch (error) {\n console.error(`Error reading key \"${key}\" from storage:`, error);\n return defaultValue;\n }\n },\n\n set: <T>(key: string, value: T): void => {\n try {\n window.localStorage.setItem(key, JSON.stringify(value));\n } catch (error) {\n console.error(`Error writing key \"${key}\" to storage:`, error);\n }\n },\n\n remove: (key: string): void => {\n try {\n window.localStorage.removeItem(key);\n } catch (error) {\n console.error(`Error removing key \"${key}\" from storage:`, error);\n }\n },\n\n clear: (): void => {\n try {\n window.localStorage.clear();\n } catch (error) {\n console.error('Error clearing storage:', error);\n }\n }\n};\n","/**\n * Utility functions for formatting data.\n */\nexport const formatters = {\n /**\n * Formats a number as currency.\n * @param value - Number to format\n * @param locale - Locale (default: 'en-US')\n * @param currency - Currency code (default: 'USD')\n */\n currency: (value: number, locale: string = 'en-US', currency: string = 'USD'): string => {\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: currency,\n }).format(value);\n },\n\n /**\n * Formats a date to a readable string.\n * @param date - Date to format\n * @param locale - Locale (default: 'en-US')\n * @param options - Intl.DateTimeFormatOptions\n */\n date: (date: Date | string | number, locale: string = 'en-US', options?: Intl.DateTimeFormatOptions): string => {\n const d = new Date(date);\n const defaultOptions: Intl.DateTimeFormatOptions = options || {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n };\n return new Intl.DateTimeFormat(locale, defaultOptions).format(d);\n },\n\n /**\n * Truncates a string to a specific length.\n */\n truncate: (str: string, length: number): string => {\n if (str.length <= length) return str;\n return str.slice(0, length) + '...';\n }\n};\n","/**\n * Utility functions for common validations.\n */\nexport const validators = {\n /**\n * Checks if a string is a valid email.\n */\n isEmail: (email: string): boolean => {\n const re = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return re.test(email);\n },\n\n /**\n * Checks if a string is empty or only whitespace.\n */\n isEmpty: (str: string | null | undefined): boolean => {\n return !str || str.trim().length === 0;\n },\n\n /**\n * Checks if a value is a number.\n */\n isNumber: (value: any): boolean => {\n return !isNaN(parseFloat(value)) && isFinite(value);\n },\n\n /**\n * Validates if a password meets minimum complexity.\n * (At least 8 chars, 1 letter, 1 number)\n */\n isStrongPassword: (password: string): boolean => {\n return password.length >= 8 && /[A-Za-z]/.test(password) && /[0-9]/.test(password);\n },\n\n /**\n * Validates a phone number.\n * @param phone - Phone number string\n * @param locale - Optional locale (default: 'generic')\n */\n isPhone: (phone: string, locale: string = 'generic'): boolean => {\n const cleanPhone = phone.replace(/\\s|-/g, '');\n if (locale === 'es-CR') {\n // Costa Rica: 8 digits, starts with 2, 4, 5, 6, 7 or 8\n return /^[245678]\\d{7}$/.test(cleanPhone);\n }\n // Generic: at least 7 digits\n return /^\\+?[\\d\\s-]{7,}$/.test(cleanPhone);\n }\n};\n","/**\n * Utility for logging messages with different levels and optional environment check.\n */\nconst isDev = process.env.NODE_ENV === 'development';\n\nconst logStyles = {\n info: 'color: #3b82f6; font-weight: bold;',\n warn: 'color: #f59e0b; font-weight: bold;',\n error: 'color: #ef4444; font-weight: bold;',\n success: 'color: #10b981; font-weight: bold;'\n};\n\nexport const logger = {\n info: (message: string, ...data: any[]): void => {\n if (isDev) {\n console.log(`%c[INFO] ${message}`, logStyles.info, ...data);\n }\n },\n\n warn: (message: string, ...data: any[]): void => {\n if (isDev) {\n console.warn(`%c[WARN] ${message}`, logStyles.warn, ...data);\n }\n },\n\n error: (message: string, ...data: any[]): void => {\n // Errors are usually logged even in production for debugging\n console.error(`%c[ERROR] ${message}`, logStyles.error, ...data);\n },\n\n success: (message: string, ...data: any[]): void => {\n if (isDev) {\n console.log(`%c[SUCCESS] ${message}`, logStyles.success, ...data);\n }\n }\n};\n","import { useState, useEffect } from 'react';\nimport { apiFetch } from '../utilities/apiFetch.util';\n\ntype Data<T> = T | null;\ntype ErrorType = Error | null | string | null;\n\ntype Params<T> = {\n data: Data<T>;\n error: ErrorType;\n loading: boolean;\n};\n\n/**\n * Custom hook to perform a fetch request and manage its state (data, error, loading).\n * @template T - Expected return type\n * @param url - URL to fetch\n * @param options - Optional configuration (e.g., delay for testing)\n * @returns Object with data, error, and loading state\n */\nexport const useFetch = <T>(url: string, options?: { delay?: number }): Params<T> => {\n const [data, setData] = useState<Data<T>>(null);\n const [error, setError] = useState<ErrorType>(null);\n const [loading, setLoading] = useState<boolean>(true);\n\n useEffect(() => {\n const controller = new AbortController();\n const { signal } = controller;\n\n const fetchData = async () => {\n setLoading(true);\n \n if (options?.delay) {\n await new Promise(resolve => setTimeout(resolve, options.delay));\n }\n\n try {\n const result: T = await apiFetch(url, { signal });\n setData(result);\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') {\n console.log('Fetch aborted');\n } else if (err instanceof Error) {\n setError(err.message);\n } else {\n setError(String(err));\n }\n } finally {\n setLoading(false);\n }\n };\n\n fetchData();\n\n return () => {\n controller.abort();\n };\n }, [url]);\n\n return { data, error, loading };\n};\n","import { useState, useEffect } from 'react';\n\n/**\n * Custom hook to manage localStorage with React state.\n * @param key - The key to store in localStorage\n * @param initialValue - Initial value if none exists\n * @returns [storedValue, setValue] - State and setter function\n */\nexport function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void] {\n // State to store our value\n // Pass initial state function to useState so logic is only executed once\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === 'undefined') {\n return initialValue;\n }\n try {\n const item = window.localStorage.getItem(key);\n return item ? JSON.parse(item) : initialValue;\n } catch (error) {\n console.error(`Error reading localStorage key \"${key}\":`, error);\n return initialValue;\n }\n });\n\n // Return a wrapped version of useState's setter function that\n // persists the new value to localStorage.\n const setValue = (value: T | ((val: T) => T)) => {\n try {\n // Allow value to be a function so we have same API as useState\n const valueToStore = value instanceof Function ? value(storedValue) : value;\n setStoredValue(valueToStore);\n if (typeof window !== 'undefined') {\n window.localStorage.setItem(key, JSON.stringify(valueToStore));\n }\n } catch (error) {\n console.error(`Error setting localStorage key \"${key}\":`, error);\n }\n };\n\n return [storedValue, setValue];\n}\n","import { useState, useEffect } from 'react';\n\n/**\n * Custom hook to debounce a value.\n * @param value - The value to debounce\n * @param delay - Delay in milliseconds (default 500)\n * @returns The debounced value\n */\nexport function useDebounce<T>(value: T, delay: number = 500): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n useEffect(() => {\n // Update debounced value after delay\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n // Cancel the timeout if value changes (or on unmount)\n return () => {\n clearTimeout(handler);\n };\n }, [value, delay]);\n\n return debouncedValue;\n}\n"],"x_google_ignoreList":[0,1],"mappings":"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"}
@@ -22,6 +22,9 @@ export type InputProps = {
22
22
  variantClassName?: string;
23
23
  sizeClassName?: string;
24
24
  style?: React.CSSProperties;
25
+ id?: string;
26
+ 'aria-label'?: string;
27
+ 'aria-labelledby'?: string;
25
28
  };
26
- declare const Input: ({ children, variant, type, inputSize, placeholder, value, onChange, onFocus, onBlur, disabled, required, readOnly, className, containerClassName, inputClassName, variantClassName, sizeClassName, style, ...props }: InputProps & AllInputProps) => import("react/jsx-runtime").JSX.Element;
29
+ declare const Input: ({ children, variant, type, inputSize, placeholder, value, onChange, onFocus, onBlur, disabled, required, readOnly, className, containerClassName, inputClassName, variantClassName, sizeClassName, style, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...props }: InputProps & AllInputProps) => import("react/jsx-runtime").JSX.Element;
27
30
  export default Input;
@@ -0,0 +1,2 @@
1
+ declare const ApiExamples: () => import("react/jsx-runtime").JSX.Element;
2
+ export default ApiExamples;
@@ -0,0 +1,2 @@
1
+ declare const UtilExamples: () => import("react/jsx-runtime").JSX.Element;
2
+ export default UtilExamples;
@@ -0,0 +1,3 @@
1
+ export * from './useFetch.hook';
2
+ export * from './useLocalStorage.hook';
3
+ export * from './useDebounce.hook';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Custom hook to debounce a value.
3
+ * @param value - The value to debounce
4
+ * @param delay - Delay in milliseconds (default 500)
5
+ * @returns The debounced value
6
+ */
7
+ export declare function useDebounce<T>(value: T, delay?: number): T;
@@ -0,0 +1,18 @@
1
+ type Data<T> = T | null;
2
+ type ErrorType = Error | null | string | null;
3
+ type Params<T> = {
4
+ data: Data<T>;
5
+ error: ErrorType;
6
+ loading: boolean;
7
+ };
8
+ /**
9
+ * Custom hook to perform a fetch request and manage its state (data, error, loading).
10
+ * @template T - Expected return type
11
+ * @param url - URL to fetch
12
+ * @param options - Optional configuration (e.g., delay for testing)
13
+ * @returns Object with data, error, and loading state
14
+ */
15
+ export declare const useFetch: <T>(url: string, options?: {
16
+ delay?: number;
17
+ }) => Params<T>;
18
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Custom hook to manage localStorage with React state.
3
+ * @param key - The key to store in localStorage
4
+ * @param initialValue - Initial value if none exists
5
+ * @returns [storedValue, setValue] - State and setter function
6
+ */
7
+ export declare function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void];
@@ -1 +1,3 @@
1
1
  export * from './components';
2
+ export * from './utilities';
3
+ export * from './hooks';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A generic wrapper for the fetch API with error handling and response parsing.
3
+ * @param url - The URL to fetch
4
+ * @param options - Fetch options (method, headers, body, signal, etc.)
5
+ * @returns Parsed JSON response
6
+ */
7
+ export declare const apiFetch: (url: string, options?: RequestInit) => Promise<any>;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Utility functions for formatting data.
3
+ */
4
+ export declare const formatters: {
5
+ /**
6
+ * Formats a number as currency.
7
+ * @param value - Number to format
8
+ * @param locale - Locale (default: 'en-US')
9
+ * @param currency - Currency code (default: 'USD')
10
+ */
11
+ currency: (value: number, locale?: string, currency?: string) => string;
12
+ /**
13
+ * Formats a date to a readable string.
14
+ * @param date - Date to format
15
+ * @param locale - Locale (default: 'en-US')
16
+ * @param options - Intl.DateTimeFormatOptions
17
+ */
18
+ date: (date: Date | string | number, locale?: string, options?: Intl.DateTimeFormatOptions) => string;
19
+ /**
20
+ * Truncates a string to a specific length.
21
+ */
22
+ truncate: (str: string, length: number) => string;
23
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Generic GET request
3
+ * @template T - Expected return type
4
+ * @param url - Full URL or endpoint
5
+ * @param options - Additional fetch options
6
+ */
7
+ export declare const get: <T>(url: string, options?: RequestInit) => Promise<T>;
8
+ /**
9
+ * Generic POST request
10
+ * @template T - Expected return type
11
+ * @param url - Full URL or endpoint
12
+ * @param body - Data to send
13
+ * @param options - Additional fetch options
14
+ */
15
+ export declare const post: <T>(url: string, body: any, options?: RequestInit) => Promise<T>;
16
+ /**
17
+ * Generic PUT request
18
+ * @template T - Expected return type
19
+ * @param url - Full URL or endpoint
20
+ * @param body - Data to send
21
+ * @param options - Additional fetch options
22
+ */
23
+ export declare const put: <T>(url: string, body: any, options?: RequestInit) => Promise<T>;
24
+ /**
25
+ * Generic DELETE request
26
+ * @template T - Expected return type
27
+ * @param url - Full URL or endpoint
28
+ * @param options - Additional fetch options
29
+ */
30
+ export declare const del: <T>(url: string, options?: RequestInit) => Promise<T>;
31
+ export declare const httpClient: {
32
+ get: <T>(url: string, options?: RequestInit) => Promise<T>;
33
+ post: <T>(url: string, body: any, options?: RequestInit) => Promise<T>;
34
+ put: <T>(url: string, body: any, options?: RequestInit) => Promise<T>;
35
+ delete: <T>(url: string, options?: RequestInit) => Promise<T>;
36
+ };
@@ -0,0 +1,6 @@
1
+ export * from './apiFetch.util';
2
+ export * from './httpClient.util';
3
+ export * from './storage.util';
4
+ export * from './formatters.util';
5
+ export * from './validators.util';
6
+ export * from './logger.util';
@@ -0,0 +1,6 @@
1
+ export declare const logger: {
2
+ info: (message: string, ...data: any[]) => void;
3
+ warn: (message: string, ...data: any[]) => void;
4
+ error: (message: string, ...data: any[]) => void;
5
+ success: (message: string, ...data: any[]) => void;
6
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Utility for interacting with localStorage safely.
3
+ */
4
+ export declare const storage: {
5
+ get: <T>(key: string, defaultValue: T) => T;
6
+ set: <T>(key: string, value: T) => void;
7
+ remove: (key: string) => void;
8
+ clear: () => void;
9
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Utility functions for common validations.
3
+ */
4
+ export declare const validators: {
5
+ /**
6
+ * Checks if a string is a valid email.
7
+ */
8
+ isEmail: (email: string) => boolean;
9
+ /**
10
+ * Checks if a string is empty or only whitespace.
11
+ */
12
+ isEmpty: (str: string | null | undefined) => boolean;
13
+ /**
14
+ * Checks if a value is a number.
15
+ */
16
+ isNumber: (value: any) => boolean;
17
+ /**
18
+ * Validates if a password meets minimum complexity.
19
+ * (At least 8 chars, 1 letter, 1 number)
20
+ */
21
+ isStrongPassword: (password: string) => boolean;
22
+ /**
23
+ * Validates a phone number.
24
+ * @param phone - Phone number string
25
+ * @param locale - Optional locale (default: 'generic')
26
+ */
27
+ isPhone: (phone: string, locale?: string) => boolean;
28
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luna-components-library",
3
- "version": "1.1.31",
3
+ "version": "1.1.33",
4
4
  "description": "A React component library with TypeScript support",
5
5
  "main": "dist/luna-components-library.js",
6
6
  "module": "dist/luna-components-library.js",