flysoft-react-ui 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/components/form-controls/AutocompleteInput.d.ts +39 -0
  2. package/dist/components/form-controls/AutocompleteInput.d.ts.map +1 -0
  3. package/dist/components/form-controls/AutocompleteInput.js +113 -0
  4. package/dist/components/form-controls/DateInput.d.ts +12 -0
  5. package/dist/components/form-controls/DateInput.d.ts.map +1 -0
  6. package/dist/components/form-controls/DateInput.js +156 -0
  7. package/dist/components/form-controls/DatePicker.d.ts +14 -0
  8. package/dist/components/form-controls/DatePicker.d.ts.map +1 -0
  9. package/dist/components/form-controls/DatePicker.js +148 -0
  10. package/dist/components/form-controls/index.d.ts +6 -0
  11. package/dist/components/form-controls/index.d.ts.map +1 -1
  12. package/dist/components/form-controls/index.js +3 -0
  13. package/dist/components/layout/AppLayout.d.ts.map +1 -1
  14. package/dist/components/layout/AppLayout.js +39 -5
  15. package/dist/contexts/AuthContext.d.ts.map +1 -1
  16. package/dist/contexts/AuthContext.js +108 -14
  17. package/dist/docs/AuthDocs.tsx/AuthDocs.d.ts +11 -0
  18. package/dist/docs/AuthDocs.tsx/AuthDocs.d.ts.map +1 -1
  19. package/dist/docs/AuthDocs.tsx/AuthDocs.js +14 -33
  20. package/dist/docs/AuthDocs.tsx/AuthDocsContent.d.ts.map +1 -1
  21. package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +11 -3
  22. package/dist/docs/AuthDocs.tsx/mockAuthService.d.ts +24 -0
  23. package/dist/docs/AuthDocs.tsx/mockAuthService.d.ts.map +1 -0
  24. package/dist/docs/AuthDocs.tsx/mockAuthService.js +78 -0
  25. package/dist/docs/AutocompleteInputDocs.d.ts +4 -0
  26. package/dist/docs/AutocompleteInputDocs.d.ts.map +1 -0
  27. package/dist/docs/AutocompleteInputDocs.js +75 -0
  28. package/dist/docs/DateInputDocs.d.ts +4 -0
  29. package/dist/docs/DateInputDocs.d.ts.map +1 -0
  30. package/dist/docs/DateInputDocs.js +21 -0
  31. package/dist/docs/DatePickerDocs.d.ts +4 -0
  32. package/dist/docs/DatePickerDocs.d.ts.map +1 -0
  33. package/dist/docs/DatePickerDocs.js +18 -0
  34. package/dist/docs/DocsMenu.d.ts.map +1 -1
  35. package/dist/docs/DocsMenu.js +1 -1
  36. package/dist/docs/DocsRouter.d.ts.map +1 -1
  37. package/dist/docs/DocsRouter.js +5 -1
  38. package/dist/hooks/useElementScroll.d.ts.map +1 -1
  39. package/dist/hooks/useElementScroll.js +37 -15
  40. package/dist/index.css +1 -1
  41. package/dist/index.d.ts +6 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +3 -0
  44. package/dist/index.js.map +1 -1
  45. package/package.json +1 -1
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import type { InputProps } from "./Input";
3
+ export interface AutocompleteOption {
4
+ label: string;
5
+ value: string;
6
+ description?: string | number;
7
+ icon?: string;
8
+ }
9
+ export interface AutocompleteInputProps<T = AutocompleteOption, K = string> extends Omit<InputProps, "onChange" | "value"> {
10
+ options: T[];
11
+ value?: string;
12
+ /**
13
+ * Valor de texto del input (controlado)
14
+ */
15
+ onChange?: (value: string) => void;
16
+ /**
17
+ * Callback al seleccionar una opción. Devuelve el item completo (T) y el valor mapeado (K)
18
+ */
19
+ onSelectOption?: (option: T, value: K) => void;
20
+ noResultsText?: string;
21
+ /**
22
+ * Obtiene el label que se muestra para cada opción. Por defecto usa la propiedad "label".
23
+ */
24
+ getOptionLabel?: (item: T) => string;
25
+ /**
26
+ * Obtiene el valor que se devuelve al seleccionar una opción. Por defecto usa la propiedad "value".
27
+ */
28
+ getOptionValue?: (item: T) => K;
29
+ /**
30
+ * Obtiene la descripción opcional para cada opción. Por defecto usa la propiedad "description".
31
+ */
32
+ getOptionDescription?: (item: T) => string | number | undefined;
33
+ /**
34
+ * Renderizado personalizado de cada opción. Si se define, se ignora el render por defecto.
35
+ */
36
+ renderOption?: (item: T) => React.ReactNode;
37
+ }
38
+ export declare const AutocompleteInput: <T = AutocompleteOption, K = string>({ options, value, onChange, onSelectOption, noResultsText, className, getOptionLabel, getOptionValue, getOptionDescription, renderOption, ...inputProps }: AutocompleteInputProps<T, K>) => import("react/jsx-runtime").JSX.Element;
39
+ //# sourceMappingURL=AutocompleteInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AutocompleteInput.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/AutocompleteInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB,CAAC,CAAC,GAAG,kBAAkB,EAAE,CAAC,GAAG,MAAM,CACxE,SAAQ,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC;IAC9C,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IACrC;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;IAChC;;OAEG;IACH,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAChE;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;CAC7C;AAED,eAAO,MAAM,iBAAiB,GAAI,CAAC,GAAG,kBAAkB,EAAE,CAAC,GAAG,MAAM,EAAE,2JAYnE,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,4CAoM9B,CAAC"}
@@ -0,0 +1,113 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { Input } from "./Input";
4
+ export const AutocompleteInput = ({ options, value, onChange, onSelectOption, noResultsText = "Sin resultados", className = "", getOptionLabel, getOptionValue, getOptionDescription, renderOption, ...inputProps }) => {
5
+ const [internalValue, setInternalValue] = React.useState(value || "");
6
+ const [isOpen, setIsOpen] = React.useState(false);
7
+ const [highlightedIndex, setHighlightedIndex] = React.useState(-1);
8
+ const containerRef = React.useRef(null);
9
+ const inputValue = value !== undefined ? value : internalValue;
10
+ const labelGetter = React.useCallback((item) => {
11
+ if (getOptionLabel)
12
+ return getOptionLabel(item);
13
+ const anyItem = item;
14
+ return (anyItem.label ?? "").toString();
15
+ }, [getOptionLabel]);
16
+ const valueGetter = React.useCallback((item) => {
17
+ if (getOptionValue)
18
+ return getOptionValue(item);
19
+ const anyItem = item;
20
+ return anyItem.value ?? undefined;
21
+ }, [getOptionValue]);
22
+ const descriptionGetter = React.useCallback((item) => {
23
+ if (getOptionDescription)
24
+ return getOptionDescription(item);
25
+ const anyItem = item;
26
+ return anyItem.description;
27
+ }, [getOptionDescription]);
28
+ const filteredOptions = React.useMemo(() => {
29
+ const search = inputValue.trim().toLowerCase();
30
+ if (!search)
31
+ return options;
32
+ return options.filter((option) => {
33
+ const label = labelGetter(option).toLowerCase();
34
+ const optionValue = String(valueGetter(option) ?? "").toLowerCase();
35
+ return label.includes(search) || optionValue.includes(search);
36
+ });
37
+ }, [inputValue, options, labelGetter, valueGetter]);
38
+ const handleChange = (event) => {
39
+ const newValue = event.target.value;
40
+ if (value === undefined) {
41
+ setInternalValue(newValue);
42
+ }
43
+ onChange?.(newValue);
44
+ setIsOpen(true);
45
+ setHighlightedIndex(-1);
46
+ };
47
+ const handleSelect = (option) => {
48
+ const label = labelGetter(option);
49
+ const selectedValue = valueGetter(option);
50
+ if (value === undefined) {
51
+ setInternalValue(label);
52
+ }
53
+ onChange?.(label);
54
+ onSelectOption?.(option, selectedValue);
55
+ setIsOpen(false);
56
+ };
57
+ const handleKeyDown = (event) => {
58
+ if (!isOpen && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
59
+ setIsOpen(true);
60
+ return;
61
+ }
62
+ if (!filteredOptions.length)
63
+ return;
64
+ if (event.key === "ArrowDown") {
65
+ event.preventDefault();
66
+ setHighlightedIndex((prev) => prev < filteredOptions.length - 1 ? prev + 1 : 0);
67
+ }
68
+ else if (event.key === "ArrowUp") {
69
+ event.preventDefault();
70
+ setHighlightedIndex((prev) => prev > 0 ? prev - 1 : filteredOptions.length - 1);
71
+ }
72
+ else if (event.key === "Enter") {
73
+ if (highlightedIndex >= 0 && highlightedIndex < filteredOptions.length) {
74
+ event.preventDefault();
75
+ handleSelect(filteredOptions[highlightedIndex]);
76
+ }
77
+ }
78
+ else if (event.key === "Escape") {
79
+ setIsOpen(false);
80
+ }
81
+ };
82
+ React.useEffect(() => {
83
+ const handleClickOutside = (event) => {
84
+ if (containerRef.current &&
85
+ !containerRef.current.contains(event.target)) {
86
+ setIsOpen(false);
87
+ }
88
+ };
89
+ document.addEventListener("mousedown", handleClickOutside);
90
+ return () => {
91
+ document.removeEventListener("mousedown", handleClickOutside);
92
+ };
93
+ }, []);
94
+ React.useEffect(() => {
95
+ if (value !== undefined) {
96
+ setInternalValue(value);
97
+ }
98
+ }, [value]);
99
+ const showDropdown = isOpen && (filteredOptions.length > 0 || noResultsText);
100
+ return (_jsxs("div", { ref: containerRef, className: "relative w-full", children: [_jsx(Input, { ...inputProps, value: inputValue, onChange: handleChange, onFocus: () => setIsOpen(true), onKeyDown: handleKeyDown, className: className, autoComplete: "off" }), showDropdown && (_jsx("div", { className: "absolute z-20 mt-1 w-full rounded-md border border-[var(--color-border-default)] \r\n bg-[var(--color-bg-default)] shadow-[var(--shadow-lg)] max-h-60 overflow-auto", children: filteredOptions.length > 0 ? (_jsx("ul", { className: "py-1", children: filteredOptions.map((option, index) => {
101
+ const label = labelGetter(option);
102
+ const description = descriptionGetter(option);
103
+ const anyOption = option;
104
+ return (_jsx("li", { className: `px-3 py-2 cursor-pointer flex items-start gap-2 text-sm
105
+ ${index === highlightedIndex
106
+ ? "bg-[var(--color-primary-soft)] text-[var(--color-primary)]"
107
+ : "text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]"}`, onMouseDown: (event) => {
108
+ event.preventDefault();
109
+ handleSelect(option);
110
+ }, onMouseEnter: () => setHighlightedIndex(index), children: renderOption ? (renderOption(option)) : (_jsxs(_Fragment, { children: [anyOption.icon && (_jsx("i", { className: `fa ${anyOption.icon} mt-0.5 text-[var(--color-text-muted)]` })), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "font-[var(--font-default)]", children: label }), description !== undefined &&
111
+ description !== null && (_jsx("span", { className: "text-xs text-[var(--color-text-secondary)]", children: description }))] })] })) }, String(valueGetter(option) ?? label ?? index)));
112
+ }) })) : (_jsx("div", { className: "px-3 py-2 text-sm text-[var(--color-text-secondary)]", children: noResultsText })) }))] }));
113
+ };
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import type { InputProps } from "./Input";
3
+ import type { DatePickerProps } from "./DatePicker";
4
+ export type DateInputFormat = "dd/mm/yyyy" | "mm/dd/yyyy";
5
+ export interface DateInputProps extends Omit<InputProps, "type" | "value" | "onChange"> {
6
+ value?: Date | null;
7
+ onChange?: (date: Date | null) => void;
8
+ format?: DateInputFormat;
9
+ datePickerProps?: Omit<DatePickerProps, "value" | "onChange">;
10
+ }
11
+ export declare const DateInput: React.FC<DateInputProps>;
12
+ //# sourceMappingURL=DateInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DateInput.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/DateInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,YAAY,CAAC;AAE1D,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;IACvD,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IACvC,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,eAAe,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,UAAU,CAAC,CAAC;CAC/D;AAsFD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAuK9C,CAAC"}
@@ -0,0 +1,156 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { Input } from "./Input";
4
+ import { DatePicker } from "./DatePicker";
5
+ const pad = (value) => value.toString().padStart(2, "0");
6
+ const formatDateToString = (date, format) => {
7
+ if (!date)
8
+ return "";
9
+ const day = pad(date.getDate());
10
+ const month = pad(date.getMonth() + 1);
11
+ const year = date.getFullYear().toString();
12
+ if (format === "mm/dd/yyyy") {
13
+ return `${month}/${day}/${year}`;
14
+ }
15
+ return `${day}/${month}/${year}`;
16
+ };
17
+ const parseDateFromString = (value, format) => {
18
+ // Primero intentar parsear como números sin separadores (ej: 11102025)
19
+ const numbersOnly = value.replace(/\D/g, "");
20
+ if (numbersOnly.length === 8) {
21
+ // Formato: ddmmyyyy o mmddyyyy
22
+ const p1 = parseInt(numbersOnly.substring(0, 2), 10);
23
+ const p2 = parseInt(numbersOnly.substring(2, 4), 10);
24
+ const p3 = parseInt(numbersOnly.substring(4, 8), 10);
25
+ const day = format === "mm/dd/yyyy" ? p2 : p1;
26
+ const month = format === "mm/dd/yyyy" ? p1 : p2;
27
+ const year = p3;
28
+ if (!isNaN(day) &&
29
+ !isNaN(month) &&
30
+ !isNaN(year) &&
31
+ day >= 1 &&
32
+ month >= 1 &&
33
+ month <= 12 &&
34
+ year >= 1000 &&
35
+ year <= 9999) {
36
+ const date = new Date(year, month - 1, day);
37
+ if (date.getFullYear() === year &&
38
+ date.getMonth() === month - 1 &&
39
+ date.getDate() === day) {
40
+ return date;
41
+ }
42
+ }
43
+ }
44
+ // Si no funciona, intentar parsear con separadores
45
+ const parts = value.split(/[/\-.]/).map((p) => p.trim());
46
+ if (parts.length !== 3)
47
+ return null;
48
+ const [p1, p2, p3] = parts;
49
+ const day = format === "mm/dd/yyyy" ? parseInt(p2, 10) : parseInt(p1, 10);
50
+ const month = format === "mm/dd/yyyy" ? parseInt(p1, 10) : parseInt(p2, 10);
51
+ const year = parseInt(p3, 10);
52
+ if (isNaN(day) ||
53
+ isNaN(month) ||
54
+ isNaN(year) ||
55
+ day < 1 ||
56
+ month < 1 ||
57
+ month > 12) {
58
+ return null;
59
+ }
60
+ const date = new Date(year, month - 1, day);
61
+ if (date.getFullYear() !== year ||
62
+ date.getMonth() !== month - 1 ||
63
+ date.getDate() !== day) {
64
+ return null;
65
+ }
66
+ return date;
67
+ };
68
+ export const DateInput = ({ value, onChange, format = "dd/mm/yyyy", datePickerProps, icon = "fa-calendar-alt", iconPosition = "right", className = "", ...inputProps }) => {
69
+ const [internalDate, setInternalDate] = React.useState(value ?? null);
70
+ const [inputValue, setInputValue] = React.useState(formatDateToString(value ?? null, format));
71
+ const [isOpen, setIsOpen] = React.useState(false);
72
+ const containerRef = React.useRef(null);
73
+ const inputWrapperRef = React.useRef(null);
74
+ const iconRef = React.useRef(null);
75
+ React.useEffect(() => {
76
+ if (value !== undefined) {
77
+ setInternalDate(value);
78
+ setInputValue(formatDateToString(value, format));
79
+ }
80
+ }, [value, format]);
81
+ // Centrar el ícono verticalmente respecto al input real
82
+ React.useEffect(() => {
83
+ const updateIconPosition = () => {
84
+ if (iconPosition === "right" &&
85
+ inputWrapperRef.current &&
86
+ iconRef.current) {
87
+ const inputElement = inputWrapperRef.current.querySelector("input");
88
+ if (inputElement) {
89
+ const inputRect = inputElement.getBoundingClientRect();
90
+ const wrapperRect = inputWrapperRef.current.getBoundingClientRect();
91
+ const topOffset = inputRect.top - wrapperRect.top + inputRect.height / 2;
92
+ iconRef.current.style.top = `${topOffset}px`;
93
+ iconRef.current.style.transform = "translateY(-50%)";
94
+ }
95
+ }
96
+ };
97
+ // Ejecutar inmediatamente
98
+ updateIconPosition();
99
+ // Ejecutar cuando cambie el tamaño de la ventana
100
+ window.addEventListener("resize", updateIconPosition);
101
+ return () => {
102
+ window.removeEventListener("resize", updateIconPosition);
103
+ };
104
+ }, [iconPosition, inputValue, inputProps.label, inputProps.size]);
105
+ const handleDateChange = (date) => {
106
+ if (value === undefined) {
107
+ setInternalDate(date);
108
+ setInputValue(formatDateToString(date, format));
109
+ }
110
+ onChange?.(date);
111
+ setIsOpen(false);
112
+ };
113
+ const handleInputChange = (event) => {
114
+ const newValue = event.target.value;
115
+ setInputValue(newValue);
116
+ // No intentamos parsear en cada pulsación, solo actualizamos el texto.
117
+ };
118
+ const handleInputBlur = (event) => {
119
+ const newValue = event.target.value.trim();
120
+ if (!newValue) {
121
+ handleDateChange(null);
122
+ return;
123
+ }
124
+ const parsed = parseDateFromString(newValue, format);
125
+ if (parsed) {
126
+ handleDateChange(parsed);
127
+ }
128
+ else {
129
+ // Si no es válida, restauramos el valor anterior formateado.
130
+ setInputValue(formatDateToString(internalDate, format));
131
+ }
132
+ };
133
+ const handleIconClick = (event) => {
134
+ event.preventDefault();
135
+ setIsOpen((prev) => !prev);
136
+ };
137
+ React.useEffect(() => {
138
+ const handleClickOutside = (event) => {
139
+ if (containerRef.current &&
140
+ !containerRef.current.contains(event.target)) {
141
+ setIsOpen(false);
142
+ }
143
+ };
144
+ document.addEventListener("mousedown", handleClickOutside);
145
+ return () => {
146
+ document.removeEventListener("mousedown", handleClickOutside);
147
+ };
148
+ }, []);
149
+ const datePickerInitialViewDate = internalDate ?? datePickerProps?.initialViewDate ?? new Date();
150
+ return (_jsxs("div", { ref: containerRef, className: "relative w-full", children: [_jsxs("div", { ref: inputWrapperRef, className: "relative", children: [_jsx(Input, { ...inputProps, type: "text", value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, icon: iconPosition === "right" ? undefined : icon, iconPosition: iconPosition, placeholder: inputProps.placeholder ??
151
+ (format === "mm/dd/yyyy" ? "mm/dd/yyyy" : "dd/mm/yyyy"), className: `${className} ${iconPosition === "right" ? "pr-10" : ""}` }), iconPosition === "right" && (_jsx("div", { ref: iconRef, className: "absolute right-3 cursor-pointer", onMouseDown: handleIconClick, children: _jsx("i", { className: `fa ${icon} ${inputProps.size === "sm"
152
+ ? "w-4 h-4"
153
+ : inputProps.size === "lg"
154
+ ? "w-6 h-6"
155
+ : "w-5 h-5"} text-[var(--color-text-muted)]` }) }))] }), isOpen && (_jsx("div", { className: "absolute z-20 mt-1 right-0", children: _jsx(DatePicker, { ...datePickerProps, value: internalDate ?? datePickerInitialViewDate, onChange: (date) => handleDateChange(date) }) }))] }));
156
+ };
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ export type DatePickerView = {
3
+ month: number;
4
+ year: number;
5
+ };
6
+ export interface DatePickerProps {
7
+ value?: Date | null;
8
+ onChange?: (date: Date) => void;
9
+ initialViewDate?: Date;
10
+ startWeekOn?: "monday" | "sunday";
11
+ className?: string;
12
+ }
13
+ export declare const DatePicker: React.FC<DatePickerProps>;
14
+ //# sourceMappingURL=DatePicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/DatePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAChC,eAAe,CAAC,EAAE,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4BD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAuOhD,CAAC"}
@@ -0,0 +1,148 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { Button } from "./Button";
4
+ const createDateAtMidnight = (date) => {
5
+ const d = new Date(date);
6
+ d.setHours(0, 0, 0, 0);
7
+ return d;
8
+ };
9
+ const isSameDay = (a, b) => {
10
+ return (a.getFullYear() === b.getFullYear() &&
11
+ a.getMonth() === b.getMonth() &&
12
+ a.getDate() === b.getDate());
13
+ };
14
+ const getDaysInMonth = (year, month) => {
15
+ return new Date(year, month + 1, 0).getDate();
16
+ };
17
+ const getWeekdayLabels = (startWeekOn) => {
18
+ const base = ["D", "L", "M", "X", "J", "V", "S"];
19
+ if (startWeekOn === "sunday") {
20
+ return base;
21
+ }
22
+ return [...base.slice(1), base[0]];
23
+ };
24
+ export const DatePicker = ({ value, onChange, initialViewDate, startWeekOn = "sunday", className = "", }) => {
25
+ const today = React.useMemo(() => createDateAtMidnight(new Date()), []);
26
+ const initial = React.useMemo(() => {
27
+ const base = value ?? initialViewDate ?? today;
28
+ return {
29
+ month: base.getMonth(),
30
+ year: base.getFullYear(),
31
+ };
32
+ }, [value, initialViewDate, today]);
33
+ const [view, setView] = React.useState(initial);
34
+ React.useEffect(() => {
35
+ if (value) {
36
+ setView({
37
+ month: value.getMonth(),
38
+ year: value.getFullYear(),
39
+ });
40
+ }
41
+ }, [value]);
42
+ const handlePrevMonth = () => {
43
+ setView((prev) => {
44
+ const month = prev.month === 0 ? 11 : prev.month - 1;
45
+ const year = prev.month === 0 ? prev.year - 1 : prev.year;
46
+ return { month, year };
47
+ });
48
+ };
49
+ const handleNextMonth = () => {
50
+ setView((prev) => {
51
+ const month = prev.month === 11 ? 0 : prev.month + 1;
52
+ const year = prev.month === 11 ? prev.year + 1 : prev.year;
53
+ return { month, year };
54
+ });
55
+ };
56
+ const handlePrevYear = () => {
57
+ setView((prev) => ({ ...prev, year: prev.year - 1 }));
58
+ };
59
+ const handleNextYear = () => {
60
+ setView((prev) => ({ ...prev, year: prev.year + 1 }));
61
+ };
62
+ const handleSelectDay = (day, month, year) => {
63
+ const targetMonth = month !== undefined ? month : view.month;
64
+ const targetYear = year !== undefined ? year : view.year;
65
+ const date = new Date(targetYear, targetMonth, day);
66
+ onChange?.(createDateAtMidnight(date));
67
+ // Si el día es de otro mes, cambiar la vista
68
+ if (month !== undefined && month !== view.month) {
69
+ setView({ month: targetMonth, year: targetYear });
70
+ }
71
+ else if (year !== undefined && year !== view.year) {
72
+ setView({ month: targetMonth, year: targetYear });
73
+ }
74
+ };
75
+ const firstDayOfMonth = new Date(view.year, view.month, 1);
76
+ const firstWeekday = firstDayOfMonth.getDay(); // 0-6, Sunday=0
77
+ const daysInMonth = getDaysInMonth(view.year, view.month);
78
+ const weekdayLabels = getWeekdayLabels(startWeekOn);
79
+ const offset = startWeekOn === "sunday"
80
+ ? firstWeekday
81
+ : firstWeekday === 0
82
+ ? 6
83
+ : firstWeekday - 1;
84
+ // Calcular días del mes anterior y siguiente
85
+ const prevMonth = view.month === 0 ? 11 : view.month - 1;
86
+ const prevYear = view.month === 0 ? view.year - 1 : view.year;
87
+ const daysInPrevMonth = getDaysInMonth(prevYear, prevMonth);
88
+ const nextMonth = view.month === 11 ? 0 : view.month + 1;
89
+ const nextYear = view.month === 11 ? view.year + 1 : view.year;
90
+ const weeks = [];
91
+ // Construir todas las semanas (6 semanas = 42 días)
92
+ let dayCounter = 1 - offset; // Puede ser negativo para días del mes anterior
93
+ for (let week = 0; week < 6; week++) {
94
+ const currentWeek = [];
95
+ for (let dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {
96
+ if (dayCounter < 1) {
97
+ // Día del mes anterior
98
+ const day = daysInPrevMonth + dayCounter;
99
+ currentWeek.push({ day, month: prevMonth, year: prevYear });
100
+ }
101
+ else if (dayCounter > daysInMonth) {
102
+ // Día del mes siguiente
103
+ const day = dayCounter - daysInMonth;
104
+ currentWeek.push({ day, month: nextMonth, year: nextYear });
105
+ }
106
+ else {
107
+ // Día del mes actual
108
+ currentWeek.push({
109
+ day: dayCounter,
110
+ month: view.month,
111
+ year: view.year,
112
+ });
113
+ }
114
+ dayCounter++;
115
+ }
116
+ weeks.push(currentWeek);
117
+ }
118
+ const selectedDate = value && !isNaN(value.getTime()) ? createDateAtMidnight(value) : null;
119
+ const monthName = new Date(view.year, view.month, 1).toLocaleString("es-ES", {
120
+ month: "long",
121
+ });
122
+ return (_jsxs("div", { className: `inline-flex flex-col rounded-lg border border-[var(--color-border-default)]
123
+ bg-[var(--color-bg-default)] p-3 shadow-sm font-[var(--font-default)] text-sm ${className}`, children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { size: "sm", variant: "ghost", icon: "fa-angle-double-left", onClick: handlePrevYear, "aria-label": "A\u00F1o anterior" }), _jsx(Button, { size: "sm", variant: "ghost", icon: "fa-angle-left", onClick: handlePrevMonth, "aria-label": "Mes anterior" })] }), _jsx("div", { className: "text-center", children: _jsxs("div", { className: "text-[var(--color-text-primary)] font-medium capitalize", children: [monthName, " ", view.year] }) }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { size: "sm", variant: "ghost", icon: "fa-angle-right", onClick: handleNextMonth, "aria-label": "Mes siguiente" }), _jsx(Button, { size: "sm", variant: "ghost", icon: "fa-angle-double-right", onClick: handleNextYear, "aria-label": "A\u00F1o siguiente" })] })] }), _jsx("div", { className: "grid grid-cols-7 gap-1 mb-1", children: weekdayLabels.map((label) => (_jsx("div", { className: "text-xs text-center text-[var(--color-text-secondary)]", children: label }, label))) }), _jsx("div", { className: "grid grid-rows-6 gap-1", children: weeks.map((week, rowIndex) => (_jsx("div", { className: "grid grid-cols-7 gap-1", children: week.map((dayInfo, index) => {
124
+ const { day, month, year } = dayInfo;
125
+ const isCurrentMonth = month === view.month && year === view.year;
126
+ const date = new Date(year, month, day);
127
+ const isToday = isSameDay(date, today);
128
+ const isSelected = selectedDate !== null && isSameDay(date, selectedDate);
129
+ let dayClasses = "w-8 h-8 flex items-center justify-center rounded-full cursor-pointer text-xs";
130
+ if (isSelected) {
131
+ dayClasses +=
132
+ " bg-[var(--color-primary)] text-[var(--color-primary-contrast)]";
133
+ }
134
+ else if (isToday) {
135
+ dayClasses +=
136
+ " border border-[var(--color-primary)] text-[var(--color-primary)]";
137
+ }
138
+ else if (isCurrentMonth) {
139
+ dayClasses +=
140
+ " text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]";
141
+ }
142
+ else {
143
+ dayClasses +=
144
+ " text-[var(--color-text-muted)] opacity-50 hover:bg-[var(--color-bg-secondary)] hover:opacity-75";
145
+ }
146
+ return (_jsx("button", { type: "button", className: dayClasses, onClick: () => handleSelectDay(day, month, year), children: day }, index));
147
+ }) }, rowIndex))) })] }));
148
+ };
@@ -1,5 +1,11 @@
1
1
  export { Button } from "./Button";
2
2
  export { Input } from "./Input";
3
+ export { AutocompleteInput } from "./AutocompleteInput";
4
+ export { DatePicker } from "./DatePicker";
5
+ export { DateInput } from "./DateInput";
3
6
  export type { ButtonProps } from "./Button";
4
7
  export type { InputProps } from "./Input";
8
+ export type { AutocompleteInputProps, AutocompleteOption, } from "./AutocompleteInput";
9
+ export type { DatePickerProps } from "./DatePicker";
10
+ export type { DateInputProps, DateInputFormat } from "./DateInput";
5
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,YAAY,EACV,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
@@ -1,2 +1,5 @@
1
1
  export { Button } from "./Button";
2
2
  export { Input } from "./Input";
3
+ export { AutocompleteInput } from "./AutocompleteInput";
4
+ export { DatePicker } from "./DatePicker";
5
+ export { DateInput } from "./DateInput";
@@ -1 +1 @@
1
- {"version":3,"file":"AppLayout.d.ts","sourceRoot":"","sources":["../../../src/components/layout/AppLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2B,MAAM,OAAO,CAAC;AAKhD,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAoL9C,CAAC"}
1
+ {"version":3,"file":"AppLayout.d.ts","sourceRoot":"","sources":["../../../src/components/layout/AppLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2B,MAAM,OAAO,CAAC;AAKhD,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAyN9C,CAAC"}
@@ -10,35 +10,69 @@ export const AppLayout = ({ navBarDrawer, leftDrawer, children, className = "",
10
10
  const [isMobileDrawerOpen, setIsMobileDrawerOpen] = useState(false);
11
11
  const [isNavbarVisible, setIsNavbarVisible] = useState(true);
12
12
  const isNavbarVisibleRef = useRef(isNavbarVisible);
13
+ const isTransitioningRef = useRef(false);
14
+ const lastScrollYRef = useRef(0);
13
15
  const shouldShowMobileDrawer = isMobile || isTablet;
14
16
  const shouldShowDesktopDrawer = !shouldShowMobileDrawer && leftDrawer;
15
17
  // Mantener el ref sincronizado con el estado
16
18
  React.useEffect(() => {
17
19
  isNavbarVisibleRef.current = isNavbarVisible;
20
+ // Marcar que estamos en transición por 350ms (duración de la transición + margen)
21
+ isTransitioningRef.current = true;
22
+ const timer = setTimeout(() => {
23
+ isTransitioningRef.current = false;
24
+ }, 350);
25
+ return () => clearTimeout(timer);
18
26
  }, [isNavbarVisible]);
19
- // Controlar visibilidad del navbar basado en scroll
27
+ // Controlar visibilidad del navbar basado en scroll con histeresis mejorada
20
28
  React.useEffect(() => {
29
+ // Ignorar cambios durante transiciones o cambios muy pequeños de scroll
30
+ if (isTransitioningRef.current) {
31
+ return;
32
+ }
33
+ const SCROLL_DELTA_THRESHOLD = 5; // Mínimo cambio de scroll para considerar
34
+ const scrollDelta = Math.abs(scrollY - lastScrollYRef.current);
35
+ // Ignorar cambios muy pequeños que pueden ser causados por el cambio de padding
36
+ if (scrollDelta < SCROLL_DELTA_THRESHOLD && lastScrollYRef.current > 0) {
37
+ return;
38
+ }
39
+ const SHOW_THRESHOLD = 80;
40
+ const HIDE_THRESHOLD = 120;
41
+ // Verificar si estamos cerca del final del scroll (margen de error de 10px)
42
+ const element = contentRef.current;
43
+ const isNearBottom = element
44
+ ? Math.abs(element.scrollHeight - element.clientHeight - element.scrollTop) < 10
45
+ : false;
21
46
  let shouldBeVisible;
22
- if (scrollY < 100) {
47
+ if (scrollY < SHOW_THRESHOLD) {
23
48
  // Siempre mostrar navbar cerca del top
24
49
  shouldBeVisible = true;
25
50
  }
26
- else if (scrollDirection === "down" && scrollY > 100) {
27
- // Ocultar navbar al hacer scroll hacia abajo
51
+ else if (scrollDirection === "down" && scrollY > HIDE_THRESHOLD && !isNearBottom) {
52
+ // Ocultar navbar al hacer scroll hacia abajo, excepto si estamos cerca del final
28
53
  shouldBeVisible = false;
29
54
  }
30
- else if (scrollDirection === "up" && scrollY > 100) {
55
+ else if (scrollDirection === "up" && scrollY > SHOW_THRESHOLD) {
31
56
  // Mostrar navbar al hacer scroll hacia arriba
32
57
  shouldBeVisible = true;
33
58
  }
59
+ else if (isNearBottom && scrollDirection === "down") {
60
+ // Si estamos en el final y scrolleamos hacia abajo, mantener el estado actual
61
+ return;
62
+ }
34
63
  else {
35
64
  // No cambiar el estado si scrollDirection es null o no se cumple ninguna condición
36
65
  return;
37
66
  }
38
67
  // Solo actualizar el estado si hay un cambio real
39
68
  if (shouldBeVisible !== isNavbarVisibleRef.current) {
69
+ lastScrollYRef.current = scrollY;
40
70
  setIsNavbarVisible(shouldBeVisible);
41
71
  }
72
+ else {
73
+ // Actualizar la referencia del scroll incluso si no cambiamos la visibilidad
74
+ lastScrollYRef.current = scrollY;
75
+ }
42
76
  }, [scrollDirection, scrollY]);
43
77
  const handleMobileDrawerToggle = () => {
44
78
  setIsMobileDrawerOpen(!isMobileDrawerOpen);
@@ -1 +1 @@
1
- {"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../../src/contexts/AuthContext.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,wBAAwB;IACvC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,GAAG,CAAC;IACpB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,wBAAwB,GAAG,IAAI,CAAC;IACtC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,0CAMtB,CAAC;AAiBH,UAAU,iBAAiB;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9E,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAClE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA4DpD,CAAC"}
1
+ {"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../../src/contexts/AuthContext.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,wBAAwB;IACvC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,GAAG,CAAC;IACpB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,wBAAwB,GAAG,IAAI,CAAC;IACtC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,0CAMtB,CAAC;AAiEH,UAAU,iBAAiB;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9E,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAClE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAmHpD,CAAC"}