flysoft-react-ui 0.4.0 → 0.5.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.
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +20 -4
- package/dist/components/form-controls/AutocompleteInput.d.ts +11 -3
- package/dist/components/form-controls/AutocompleteInput.d.ts.map +1 -1
- package/dist/components/form-controls/AutocompleteInput.js +410 -31
- package/dist/components/form-controls/Button.js +1 -1
- package/dist/components/form-controls/Checkbox.d.ts +14 -0
- package/dist/components/form-controls/Checkbox.d.ts.map +1 -0
- package/dist/components/form-controls/Checkbox.js +77 -0
- package/dist/components/form-controls/DateInput.d.ts +20 -4
- package/dist/components/form-controls/DateInput.d.ts.map +1 -1
- package/dist/components/form-controls/DateInput.js +425 -70
- package/dist/components/form-controls/DatePicker.d.ts +4 -3
- package/dist/components/form-controls/DatePicker.d.ts.map +1 -1
- package/dist/components/form-controls/DatePicker.js +26 -30
- package/dist/components/form-controls/Input.d.ts +10 -1
- package/dist/components/form-controls/Input.d.ts.map +1 -1
- package/dist/components/form-controls/Input.js +16 -10
- package/dist/components/form-controls/Pagination.d.ts +1 -0
- package/dist/components/form-controls/Pagination.d.ts.map +1 -1
- package/dist/components/form-controls/Pagination.js +3 -40
- package/dist/components/form-controls/RadioButtonGroup.d.ts +62 -0
- package/dist/components/form-controls/RadioButtonGroup.d.ts.map +1 -0
- package/dist/components/form-controls/RadioButtonGroup.js +220 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.d.ts +68 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.d.ts.map +1 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.js +962 -0
- package/dist/components/form-controls/SearchSelectInput.d.ts +70 -0
- package/dist/components/form-controls/SearchSelectInput.d.ts.map +1 -0
- package/dist/components/form-controls/SearchSelectInput.js +335 -0
- package/dist/components/form-controls/index.d.ts +7 -1
- package/dist/components/form-controls/index.d.ts.map +1 -1
- package/dist/components/form-controls/index.js +3 -0
- package/dist/components/layout/AppLayout.d.ts +3 -2
- package/dist/components/layout/AppLayout.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.js +104 -31
- package/dist/components/layout/Card.d.ts +4 -1
- package/dist/components/layout/Card.d.ts.map +1 -1
- package/dist/components/layout/Card.js +30 -1
- package/dist/components/layout/Collection.js +1 -1
- package/dist/components/layout/DataTable.d.ts +29 -0
- package/dist/components/layout/DataTable.d.ts.map +1 -0
- package/dist/components/layout/DataTable.js +165 -0
- package/dist/components/layout/index.d.ts +2 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +1 -0
- package/dist/components/utils/Avatar.d.ts +49 -0
- package/dist/components/utils/Avatar.d.ts.map +1 -0
- package/dist/components/utils/Avatar.js +93 -0
- package/dist/components/utils/Badge.d.ts +3 -0
- package/dist/components/utils/Badge.d.ts.map +1 -1
- package/dist/components/utils/Badge.js +130 -26
- package/dist/components/utils/Dialog.d.ts.map +1 -1
- package/dist/components/utils/Dialog.js +5 -1
- package/dist/components/utils/DropdownMenu.d.ts +25 -0
- package/dist/components/utils/DropdownMenu.d.ts.map +1 -0
- package/dist/components/utils/DropdownMenu.js +145 -0
- package/dist/components/utils/Filter.d.ts +57 -0
- package/dist/components/utils/Filter.d.ts.map +1 -0
- package/dist/components/utils/Filter.js +580 -0
- package/dist/components/utils/FiltersDialog.d.ts +21 -0
- package/dist/components/utils/FiltersDialog.d.ts.map +1 -0
- package/dist/components/utils/FiltersDialog.js +104 -0
- package/dist/components/utils/Loader.js +1 -1
- package/dist/components/utils/RoadMap.d.ts +59 -0
- package/dist/components/utils/RoadMap.d.ts.map +1 -0
- package/dist/components/utils/RoadMap.js +138 -0
- package/dist/components/utils/Snackbar.d.ts +13 -0
- package/dist/components/utils/Snackbar.d.ts.map +1 -0
- package/dist/components/utils/Snackbar.js +121 -0
- package/dist/components/utils/SnackbarContainer.d.ts +7 -0
- package/dist/components/utils/SnackbarContainer.d.ts.map +1 -0
- package/dist/components/utils/SnackbarContainer.js +25 -0
- package/dist/components/utils/index.d.ts +12 -0
- package/dist/components/utils/index.d.ts.map +1 -1
- package/dist/components/utils/index.js +6 -0
- package/dist/contexts/AppLayoutContext.d.ts +40 -0
- package/dist/contexts/AppLayoutContext.d.ts.map +1 -0
- package/dist/contexts/AppLayoutContext.js +98 -0
- package/dist/contexts/ListCrudContext.d.ts +29 -0
- package/dist/contexts/ListCrudContext.d.ts.map +1 -0
- package/dist/contexts/ListCrudContext.js +209 -0
- package/dist/contexts/SnackbarContext.d.ts +26 -0
- package/dist/contexts/SnackbarContext.d.ts.map +1 -0
- package/dist/contexts/SnackbarContext.js +34 -0
- package/dist/contexts/index.d.ts +6 -0
- package/dist/contexts/index.d.ts.map +1 -1
- package/dist/contexts/index.js +6 -0
- package/dist/contexts/presets.js +6 -6
- package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +3 -1
- package/dist/docs/AvatarDocs.d.ts +4 -0
- package/dist/docs/AvatarDocs.d.ts.map +1 -0
- package/dist/docs/AvatarDocs.js +7 -0
- package/dist/docs/BadgeDocs.d.ts.map +1 -1
- package/dist/docs/BadgeDocs.js +4 -2
- package/dist/docs/CardDocs.d.ts.map +1 -1
- package/dist/docs/CardDocs.js +7 -1
- package/dist/docs/CheckboxDocs.d.ts +4 -0
- package/dist/docs/CheckboxDocs.d.ts.map +1 -0
- package/dist/docs/CheckboxDocs.js +7 -0
- package/dist/docs/DataTableDocs.d.ts +4 -0
- package/dist/docs/DataTableDocs.d.ts.map +1 -0
- package/dist/docs/DataTableDocs.js +244 -0
- package/dist/docs/DateInputDocs.d.ts +1 -0
- package/dist/docs/DateInputDocs.d.ts.map +1 -1
- package/dist/docs/DateInputDocs.js +7 -9
- package/dist/docs/DatePickerDocs.d.ts +1 -0
- package/dist/docs/DatePickerDocs.d.ts.map +1 -1
- package/dist/docs/DatePickerDocs.js +6 -8
- package/dist/docs/DocAdmin.d.ts +4 -0
- package/dist/docs/DocAdmin.d.ts.map +1 -0
- package/dist/docs/DocAdmin.js +68 -0
- package/dist/docs/DocsMenu.d.ts.map +1 -1
- package/dist/docs/DocsMenu.js +1 -1
- package/dist/docs/DocsRouter.d.ts.map +1 -1
- package/dist/docs/DocsRouter.js +13 -1
- package/dist/docs/DropdownMenuDocs.d.ts +4 -0
- package/dist/docs/DropdownMenuDocs.d.ts.map +1 -0
- package/dist/docs/DropdownMenuDocs.js +66 -0
- package/dist/docs/ExampleFormDocs.d.ts +4 -0
- package/dist/docs/ExampleFormDocs.d.ts.map +1 -0
- package/dist/docs/ExampleFormDocs.js +148 -0
- package/dist/docs/FilterDocs.d.ts +4 -0
- package/dist/docs/FilterDocs.d.ts.map +1 -0
- package/dist/docs/FilterDocs.js +112 -0
- package/dist/docs/InputDocs.d.ts.map +1 -1
- package/dist/docs/InputDocs.js +11 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts +11 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.js +25 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts +2 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.js +51 -0
- package/dist/docs/PaginationDocs.js +6 -6
- package/dist/docs/RadioButtonGroupDocs.d.ts +4 -0
- package/dist/docs/RadioButtonGroupDocs.d.ts.map +1 -0
- package/dist/docs/RadioButtonGroupDocs.js +46 -0
- package/dist/docs/RoadMapDocs.d.ts +4 -0
- package/dist/docs/RoadMapDocs.d.ts.map +1 -0
- package/dist/docs/RoadMapDocs.js +171 -0
- package/dist/docs/SearchSelectInputDocs.d.ts +4 -0
- package/dist/docs/SearchSelectInputDocs.d.ts.map +1 -0
- package/dist/docs/SearchSelectInputDocs.js +168 -0
- package/dist/docs/SnackbarDocs.d.ts +4 -0
- package/dist/docs/SnackbarDocs.d.ts.map +1 -0
- package/dist/docs/SnackbarDocs.js +50 -0
- package/dist/docs/TabsGroupDocs.d.ts.map +1 -1
- package/dist/docs/TabsGroupDocs.js +12 -1
- package/dist/docs/docMockServices/empresaService.d.ts +38 -0
- package/dist/docs/docMockServices/empresaService.d.ts.map +1 -0
- package/dist/docs/docMockServices/empresaService.js +116 -0
- package/dist/docs/docMockServices/index.d.ts +9 -0
- package/dist/docs/docMockServices/index.d.ts.map +1 -0
- package/dist/docs/docMockServices/index.js +8 -0
- package/dist/docs/docMockServices/initialData.d.ts +6 -0
- package/dist/docs/docMockServices/initialData.d.ts.map +1 -0
- package/dist/docs/docMockServices/initialData.js +132 -0
- package/dist/docs/docMockServices/interfaces.d.ts +26 -0
- package/dist/docs/docMockServices/interfaces.d.ts.map +1 -0
- package/dist/docs/docMockServices/interfaces.js +1 -0
- package/dist/docs/docMockServices/personaEmpresaService.d.ts +43 -0
- package/dist/docs/docMockServices/personaEmpresaService.d.ts.map +1 -0
- package/dist/docs/docMockServices/personaEmpresaService.js +113 -0
- package/dist/docs/docMockServices/personaService.d.ts +39 -0
- package/dist/docs/docMockServices/personaService.d.ts.map +1 -0
- package/dist/docs/docMockServices/personaService.js +180 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useAsyncRequest.d.ts +17 -0
- package/dist/hooks/useAsyncRequest.d.ts.map +1 -0
- package/dist/hooks/useAsyncRequest.js +70 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
+
import dayjs, {} from "dayjs";
|
|
3
4
|
import { Button } from "./Button";
|
|
4
5
|
const createDateAtMidnight = (date) => {
|
|
5
|
-
const d =
|
|
6
|
-
d.
|
|
7
|
-
return d;
|
|
6
|
+
const d = dayjs(date);
|
|
7
|
+
return d.startOf("day");
|
|
8
8
|
};
|
|
9
9
|
const isSameDay = (a, b) => {
|
|
10
|
-
return
|
|
11
|
-
a.getMonth() === b.getMonth() &&
|
|
12
|
-
a.getDate() === b.getDate());
|
|
10
|
+
return a.isSame(b, "day");
|
|
13
11
|
};
|
|
14
12
|
const getDaysInMonth = (year, month) => {
|
|
15
|
-
return
|
|
13
|
+
return dayjs().year(year).month(month).daysInMonth();
|
|
16
14
|
};
|
|
17
15
|
const getWeekdayLabels = (startWeekOn) => {
|
|
18
16
|
const base = ["D", "L", "M", "X", "J", "V", "S"];
|
|
@@ -22,23 +20,19 @@ const getWeekdayLabels = (startWeekOn) => {
|
|
|
22
20
|
return [...base.slice(1), base[0]];
|
|
23
21
|
};
|
|
24
22
|
export const DatePicker = ({ value, onChange, initialViewDate, startWeekOn = "sunday", className = "", }) => {
|
|
25
|
-
const today = React.useMemo(() =>
|
|
26
|
-
const
|
|
27
|
-
const base =
|
|
23
|
+
const today = React.useMemo(() => dayjs().startOf("day"), []);
|
|
24
|
+
const getViewFromValue = React.useCallback((val) => {
|
|
25
|
+
const base = val ?? initialViewDate ?? today;
|
|
28
26
|
return {
|
|
29
|
-
month: base.
|
|
30
|
-
year: base.
|
|
27
|
+
month: base.month(),
|
|
28
|
+
year: base.year(),
|
|
31
29
|
};
|
|
32
|
-
}, [
|
|
33
|
-
const [view, setView] = React.useState(
|
|
30
|
+
}, [initialViewDate, today]);
|
|
31
|
+
const [view, setView] = React.useState(() => getViewFromValue(value));
|
|
34
32
|
React.useEffect(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
year: value.getFullYear(),
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}, [value]);
|
|
33
|
+
// Sincronizar la vista con el valor actual, incluso si es null/undefined
|
|
34
|
+
setView(getViewFromValue(value));
|
|
35
|
+
}, [value, getViewFromValue]);
|
|
42
36
|
const handlePrevMonth = () => {
|
|
43
37
|
setView((prev) => {
|
|
44
38
|
const month = prev.month === 0 ? 11 : prev.month - 1;
|
|
@@ -62,7 +56,7 @@ export const DatePicker = ({ value, onChange, initialViewDate, startWeekOn = "su
|
|
|
62
56
|
const handleSelectDay = (day, month, year) => {
|
|
63
57
|
const targetMonth = month !== undefined ? month : view.month;
|
|
64
58
|
const targetYear = year !== undefined ? year : view.year;
|
|
65
|
-
const date =
|
|
59
|
+
const date = dayjs().year(targetYear).month(targetMonth).date(day);
|
|
66
60
|
onChange?.(createDateAtMidnight(date));
|
|
67
61
|
// Si el día es de otro mes, cambiar la vista
|
|
68
62
|
if (month !== undefined && month !== view.month) {
|
|
@@ -72,8 +66,8 @@ export const DatePicker = ({ value, onChange, initialViewDate, startWeekOn = "su
|
|
|
72
66
|
setView({ month: targetMonth, year: targetYear });
|
|
73
67
|
}
|
|
74
68
|
};
|
|
75
|
-
const firstDayOfMonth =
|
|
76
|
-
const firstWeekday = firstDayOfMonth.
|
|
69
|
+
const firstDayOfMonth = dayjs().year(view.year).month(view.month).date(1);
|
|
70
|
+
const firstWeekday = firstDayOfMonth.day(); // 0-6, Sunday=0
|
|
77
71
|
const daysInMonth = getDaysInMonth(view.year, view.month);
|
|
78
72
|
const weekdayLabels = getWeekdayLabels(startWeekOn);
|
|
79
73
|
const offset = startWeekOn === "sunday"
|
|
@@ -115,15 +109,17 @@ export const DatePicker = ({ value, onChange, initialViewDate, startWeekOn = "su
|
|
|
115
109
|
}
|
|
116
110
|
weeks.push(currentWeek);
|
|
117
111
|
}
|
|
118
|
-
const selectedDate = value &&
|
|
119
|
-
const monthName =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
112
|
+
const selectedDate = value && value.isValid() ? createDateAtMidnight(value) : null;
|
|
113
|
+
const monthName = dayjs()
|
|
114
|
+
.year(view.year)
|
|
115
|
+
.month(view.month)
|
|
116
|
+
.date(1)
|
|
117
|
+
.format("MMMM");
|
|
118
|
+
return (_jsxs("div", { className: `inline-flex flex-col rounded-lg border border-[var(--color-border-default)]
|
|
123
119
|
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, { type: "button", size: "sm", variant: "ghost", icon: "fa-angle-double-left", onClick: handlePrevYear, "aria-label": "A\u00F1o anterior" }), _jsx(Button, { type: "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, { type: "button", size: "sm", variant: "ghost", icon: "fa-angle-right", onClick: handleNextMonth, "aria-label": "Mes siguiente" }), _jsx(Button, { type: "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
120
|
const { day, month, year } = dayInfo;
|
|
125
121
|
const isCurrentMonth = month === view.month && year === view.year;
|
|
126
|
-
const date =
|
|
122
|
+
const date = dayjs().year(year).month(month).date(day);
|
|
127
123
|
const isToday = isSameDay(date, today);
|
|
128
124
|
const isSelected = selectedDate !== null && isSameDay(date, selectedDate);
|
|
129
125
|
let dayClasses = "w-8 h-8 flex items-center justify-center rounded-full cursor-pointer text-xs";
|
|
@@ -6,6 +6,15 @@ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElem
|
|
|
6
6
|
iconPosition?: "left" | "right";
|
|
7
7
|
size?: "sm" | "md" | "lg";
|
|
8
8
|
children?: React.ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* Callback cuando se hace click en el ícono. Si está definido, el ícono será clickeable.
|
|
11
|
+
*/
|
|
12
|
+
onIconClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Si es true, el input será de solo lectura. No se podrá modificar pero no se verá como disabled.
|
|
15
|
+
* Por defecto es false.
|
|
16
|
+
*/
|
|
17
|
+
readOnly?: boolean;
|
|
9
18
|
}
|
|
10
|
-
export declare const Input: React.
|
|
19
|
+
export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
11
20
|
//# sourceMappingURL=Input.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/Input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,UACf,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/Input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,UACf,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAC7D;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,KAAK,qFA4FjB,CAAC"}
|
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
-
export const Input = ({ label, error, icon, iconPosition = "left", size = "md", className = "", id, ...props }) => {
|
|
3
|
+
export const Input = React.forwardRef(({ label, error, icon, iconPosition = "left", size = "md", className = "", id, onIconClick, readOnly = false, ...props }, ref) => {
|
|
4
4
|
const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`;
|
|
5
5
|
const baseClasses = `
|
|
6
|
-
w-full border rounded-lg transition-colors focus:outline-none
|
|
6
|
+
w-full border rounded-lg transition-colors focus:outline-none
|
|
7
7
|
disabled:opacity-50 disabled:cursor-not-allowed
|
|
8
8
|
font-[var(--font-default)] text-[var(--color-text-primary)]
|
|
9
|
-
bg-[var(--color-bg-default)]
|
|
10
9
|
`;
|
|
10
|
+
const readOnlyClasses = readOnly
|
|
11
|
+
? `border-transparent bg-transparent focus:ring-0`
|
|
12
|
+
: `focus:ring-1 bg-[var(--color-bg-default)]`;
|
|
11
13
|
const sizeClasses = {
|
|
12
14
|
sm: "px-3 py-1.5 text-sm",
|
|
13
15
|
md: "px-4 py-2 text-base",
|
|
14
16
|
lg: "px-6 py-3 text-lg",
|
|
15
17
|
};
|
|
16
|
-
const stateClasses =
|
|
17
|
-
?
|
|
18
|
-
:
|
|
19
|
-
|
|
18
|
+
const stateClasses = readOnly
|
|
19
|
+
? ""
|
|
20
|
+
: error
|
|
21
|
+
? `border-[var(--color-border-error)] focus:border-[var(--color-border-error)] focus:ring-[var(--color-border-error)]`
|
|
22
|
+
: `border-[var(--color-border-default)] focus:border-[var(--color-border-focus)] focus:ring-[var(--color-border-focus)]`;
|
|
23
|
+
const inputClasses = `${baseClasses} ${readOnlyClasses} ${sizeClasses[size]} ${stateClasses} ${className}`;
|
|
20
24
|
const iconClasses = size === "sm" ? "w-4 h-4" : size === "md" ? "w-5 h-5" : "w-6 h-6";
|
|
21
25
|
const renderIcon = () => {
|
|
22
26
|
if (!icon)
|
|
23
27
|
return null;
|
|
24
|
-
|
|
28
|
+
const iconElement = (_jsx("i", { className: `fa ${icon} ${iconClasses} text-[var(--color-text-muted)] absolute top-1/2 transform -translate-y-1/2 ${iconPosition === "left" ? "left-3" : "right-3"} ${onIconClick && !readOnly ? "cursor-pointer hover:text-[var(--color-primary)] transition-colors" : ""}`, onClick: readOnly ? undefined : onIconClick }));
|
|
29
|
+
return iconElement;
|
|
25
30
|
};
|
|
26
|
-
return (_jsxs("div", { className: "w-full", children: [label && (_jsx("label", { htmlFor: inputId, className: "block text-sm text-[var(--color-primary)] mb-1 font-[var(--font-default)]", children: label })), _jsxs("div", { className: "relative", children: [icon && iconPosition === "left" && renderIcon(), _jsx("input", { id: inputId, className: `${inputClasses} ${icon && iconPosition === "left" ? "pl-10" : ""} ${icon && iconPosition === "right" ? "pr-10" : ""}`, ...props }), icon && iconPosition === "right" && renderIcon()] }), error && (_jsx("p", { className: "mt-1 text-sm text-[var(--color-danger)] font-[var(--font-default)]", children: error }))] }));
|
|
27
|
-
};
|
|
31
|
+
return (_jsxs("div", { className: "w-full", children: [label && (_jsx("label", { htmlFor: inputId, className: "block text-sm text-[var(--color-primary)] mb-1 font-[var(--font-default)]", children: label })), _jsxs("div", { className: "relative", children: [icon && iconPosition === "left" && renderIcon(), _jsx("input", { ref: ref, id: inputId, className: `${inputClasses} ${icon && iconPosition === "left" ? "pl-10" : ""} ${icon && iconPosition === "right" ? "pr-10" : ""}`, autoComplete: "off", readOnly: readOnly, ...props }), icon && iconPosition === "right" && renderIcon()] }), error && (_jsx("p", { className: "mt-1 text-sm text-[var(--color-danger)] font-[var(--font-default)]", children: error }))] }));
|
|
32
|
+
});
|
|
33
|
+
Input.displayName = "Input";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/Pagination.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/Pagination.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA0FhD,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { useSearchParams } from "react-router-dom";
|
|
4
4
|
import { Button } from "./Button";
|
|
5
|
-
export const Pagination = ({ fieldName = "pagina", page = 1, pages = 1, total = 0, }) => {
|
|
5
|
+
export const Pagination = ({ fieldName = "pagina", page = 1, pages = 1, total = 0, isLoading = false, }) => {
|
|
6
6
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
7
7
|
const navigateToPage = (newPage) => {
|
|
8
8
|
if (newPage < 1 || newPage > pages || newPage === page) {
|
|
@@ -19,42 +19,5 @@ export const Pagination = ({ fieldName = "pagina", page = 1, pages = 1, total =
|
|
|
19
19
|
const isFirstPage = page <= 1;
|
|
20
20
|
const isLastPage = page >= pages;
|
|
21
21
|
const hasPages = pages > 1;
|
|
22
|
-
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-double-left", onClick: goToFirstPage, disabled: isFirstPage || !hasPages, "aria-label": "Primera p\u00E1gina" }) }), _jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-left", onClick: goToPreviousPage, disabled: isFirstPage || !hasPages, "aria-label": "P\u00E1gina anterior" }) }),
|
|
23
|
-
// <div className="flex flex-col items-center gap-0 font-[var(--font-default)]">
|
|
24
|
-
// {/* Botones de navegación */}
|
|
25
|
-
// <div className="flex items-center gap-2">
|
|
26
|
-
//
|
|
27
|
-
// {/* Texto de página */}
|
|
28
|
-
// <span
|
|
29
|
-
// className="text-xs px-3 leading-none"
|
|
30
|
-
// style={{ color: "var(--color-text-primary)" }}
|
|
31
|
-
// >
|
|
32
|
-
// Página {page} de {pages}
|
|
33
|
-
// </span>
|
|
34
|
-
// <Button
|
|
35
|
-
// variant="ghost"
|
|
36
|
-
// size="sm"
|
|
37
|
-
// icon="fa-angle-right"
|
|
38
|
-
// onClick={goToNextPage}
|
|
39
|
-
// disabled={isLastPage || !hasPages}
|
|
40
|
-
// aria-label="Página siguiente"
|
|
41
|
-
// />
|
|
42
|
-
// <Button
|
|
43
|
-
// variant="ghost"
|
|
44
|
-
// size="sm"
|
|
45
|
-
// icon="fa-angle-double-right"
|
|
46
|
-
// onClick={goToLastPage}
|
|
47
|
-
// disabled={isLastPage || !hasPages}
|
|
48
|
-
// aria-label="Última página"
|
|
49
|
-
// />
|
|
50
|
-
// </div>
|
|
51
|
-
// {/* Texto de elementos */}
|
|
52
|
-
// <span
|
|
53
|
-
// className="text-xs leading-none"
|
|
54
|
-
// style={{ color: "var(--color-text-secondary)" }}
|
|
55
|
-
// >
|
|
56
|
-
// {total} elemento{total !== 1 ? "s" : ""}
|
|
57
|
-
// </span>
|
|
58
|
-
// </div>
|
|
59
|
-
);
|
|
22
|
+
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-double-left", onClick: goToFirstPage, disabled: isFirstPage || !hasPages || isLoading, "aria-label": "Primera p\u00E1gina" }) }), _jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-left", onClick: goToPreviousPage, disabled: isFirstPage || !hasPages || isLoading, "aria-label": "P\u00E1gina anterior" }) }), _jsx("div", { className: `text-xs h-[32px] min-w-[100px] text-center flex flex-col justify-center`, children: isLoading ? (_jsx(_Fragment, { children: _jsx("span", { className: "block", children: "Cargando..." }) })) : (_jsxs(_Fragment, { children: [_jsxs("span", { className: "block", children: ["P\u00E1gina ", page, " de ", pages] }), _jsxs("span", { className: "block", children: [total, " elemento", total !== 1 ? "s" : ""] })] })) }), _jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-right", onClick: goToNextPage, disabled: isLastPage || !hasPages || isLoading, "aria-label": "P\u00E1gina siguiente" }) }), _jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-double-right", onClick: goToLastPage, disabled: isLastPage || !hasPages || isLoading, "aria-label": "\u00DAltima p\u00E1gina" }) })] }));
|
|
60
23
|
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface RadioOption {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string | number;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface RadioButtonGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "children"> {
|
|
8
|
+
/**
|
|
9
|
+
* Array de opciones para renderizar radios automáticamente
|
|
10
|
+
*/
|
|
11
|
+
options: RadioOption[];
|
|
12
|
+
/**
|
|
13
|
+
* Valor seleccionado (controlado)
|
|
14
|
+
*/
|
|
15
|
+
value?: string | number;
|
|
16
|
+
/**
|
|
17
|
+
* Callback cuando cambia la selección
|
|
18
|
+
* Puede recibir un valor directo o un evento (para compatibilidad con react-hook-form)
|
|
19
|
+
*/
|
|
20
|
+
onChange?: ((value: string | number) => void) | React.ChangeEventHandler<HTMLInputElement>;
|
|
21
|
+
/**
|
|
22
|
+
* Posición del label para todas las opciones
|
|
23
|
+
*/
|
|
24
|
+
labelPosition?: "left" | "right";
|
|
25
|
+
/**
|
|
26
|
+
* Tamaño de los radio buttons
|
|
27
|
+
*/
|
|
28
|
+
size?: "sm" | "md" | "lg";
|
|
29
|
+
/**
|
|
30
|
+
* Mensaje de error a mostrar
|
|
31
|
+
*/
|
|
32
|
+
error?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Dirección del layout: vertical (columna) o horizontal (fila)
|
|
35
|
+
*/
|
|
36
|
+
direction?: "vertical" | "horizontal";
|
|
37
|
+
/**
|
|
38
|
+
* Espaciado entre opciones
|
|
39
|
+
*/
|
|
40
|
+
gap?: "sm" | "md" | "lg";
|
|
41
|
+
/**
|
|
42
|
+
* Nombre del campo (para react-hook-form)
|
|
43
|
+
*/
|
|
44
|
+
name?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Estado deshabilitado
|
|
47
|
+
*/
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Callback cuando pierde el foco
|
|
51
|
+
* Puede recibir un evento (para compatibilidad con react-hook-form) o ser una función sin parámetros
|
|
52
|
+
*/
|
|
53
|
+
onBlur?: (() => void) | React.FocusEventHandler<HTMLInputElement>;
|
|
54
|
+
/**
|
|
55
|
+
* Si es true, el radio group será de solo lectura. Las opciones no seleccionadas se verán deshabilitadas
|
|
56
|
+
* y la seleccionada se verá igual, pero no se podrá cambiar el valor.
|
|
57
|
+
* Por defecto es false.
|
|
58
|
+
*/
|
|
59
|
+
readOnly?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export declare const RadioButtonGroup: React.ForwardRefExoticComponent<RadioButtonGroupProps & React.RefAttributes<HTMLInputElement>>;
|
|
62
|
+
//# sourceMappingURL=RadioButtonGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadioButtonGroup.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/RadioButtonGroup.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,qBACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IAC3E;;OAEG;IACH,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,EACL,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC,GAClC,KAAK,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IAC/C;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC;;OAEG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,SAAS,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACtC;;OAEG;IACH,GAAG,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACzB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,KAAK,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;IAClE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,gGAmV5B,CAAC"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useFormContext } from "react-hook-form";
|
|
4
|
+
export const RadioButtonGroup = React.forwardRef(({ options, value, onChange, labelPosition = "right", size = "md", error, direction = "vertical", gap = "md", className = "", name, disabled, onBlur, readOnly = false, ...props }, ref) => {
|
|
5
|
+
// useFormContext debe llamarse incondicionalmente (regla de hooks)
|
|
6
|
+
// Si no hay FormProvider, esto lanzará un error
|
|
7
|
+
// Para modo controlado sin FormProvider, necesitamos envolver los ejemplos
|
|
8
|
+
// en un FormProvider mínimo o usar una detección diferente
|
|
9
|
+
// Por ahora, llamamos useFormContext incondicionalmente
|
|
10
|
+
const formContext = useFormContext();
|
|
11
|
+
const setValue = formContext?.setValue;
|
|
12
|
+
// Detectar si estamos en modo register
|
|
13
|
+
// Modo register: formContext existe (hay FormProvider) Y name está definido
|
|
14
|
+
// Modo controlado: no hay formContext (no hay FormProvider) o no hay name
|
|
15
|
+
// Nota: Si no hay FormProvider, useFormContext lanzará error, pero esto es esperado
|
|
16
|
+
// cuando se usa register. Para modo controlado, el componente puede funcionar
|
|
17
|
+
// si simplemente no usamos el contexto cuando no está disponible
|
|
18
|
+
const isRegisterMode = React.useMemo(() => {
|
|
19
|
+
// Si no hay formContext, definitivamente es modo controlado
|
|
20
|
+
if (!formContext)
|
|
21
|
+
return false;
|
|
22
|
+
// Si hay formContext pero no hay name, es modo controlado
|
|
23
|
+
if (!name)
|
|
24
|
+
return false;
|
|
25
|
+
// Si hay formContext y name, es modo register
|
|
26
|
+
return true;
|
|
27
|
+
}, [formContext, name]);
|
|
28
|
+
// Usar el valor proporcionado solo en modo controlado (no register)
|
|
29
|
+
// En modo register, react-hook-form maneja el valor automáticamente
|
|
30
|
+
const currentValue = !isRegisterMode ? value : undefined;
|
|
31
|
+
// Input hidden para compatibilidad con react-hook-form
|
|
32
|
+
const hiddenInputRef = React.useRef(null);
|
|
33
|
+
// Combinar el ref del forwardRef (de register) con el ref interno del input hidden
|
|
34
|
+
const combinedRef = React.useCallback((node) => {
|
|
35
|
+
hiddenInputRef.current = node;
|
|
36
|
+
if (typeof ref === "function") {
|
|
37
|
+
ref(node);
|
|
38
|
+
}
|
|
39
|
+
else if (ref) {
|
|
40
|
+
ref.current = node;
|
|
41
|
+
}
|
|
42
|
+
}, [ref]);
|
|
43
|
+
// Obtener el valor actual desde react-hook-form en modo register
|
|
44
|
+
const watchValue = formContext?.watch(name || "");
|
|
45
|
+
const actualValue = isRegisterMode
|
|
46
|
+
? watchValue !== undefined
|
|
47
|
+
? watchValue
|
|
48
|
+
: currentValue
|
|
49
|
+
: currentValue;
|
|
50
|
+
const gapClasses = {
|
|
51
|
+
sm: "gap-2",
|
|
52
|
+
md: "gap-3",
|
|
53
|
+
lg: "gap-4",
|
|
54
|
+
};
|
|
55
|
+
const directionClasses = {
|
|
56
|
+
vertical: "flex-col",
|
|
57
|
+
horizontal: "flex-row flex-wrap",
|
|
58
|
+
};
|
|
59
|
+
const containerClasses = `
|
|
60
|
+
flex ${directionClasses[direction]} ${gapClasses[gap]}
|
|
61
|
+
${className}
|
|
62
|
+
`;
|
|
63
|
+
const sizeClasses = {
|
|
64
|
+
sm: "w-4 h-4",
|
|
65
|
+
md: "w-5 h-5",
|
|
66
|
+
lg: "w-6 h-6",
|
|
67
|
+
};
|
|
68
|
+
const labelSizeClasses = {
|
|
69
|
+
sm: "text-sm",
|
|
70
|
+
md: "text-base",
|
|
71
|
+
lg: "text-lg",
|
|
72
|
+
};
|
|
73
|
+
const handleOptionClick = (optionValue) => {
|
|
74
|
+
if (disabled || readOnly)
|
|
75
|
+
return;
|
|
76
|
+
const valueString = String(optionValue);
|
|
77
|
+
if (isRegisterMode && name) {
|
|
78
|
+
// En modo register, actualizar el input hidden y usar setValue
|
|
79
|
+
if (hiddenInputRef.current) {
|
|
80
|
+
const nativeInput = hiddenInputRef.current;
|
|
81
|
+
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
82
|
+
if (nativeInputValueSetter) {
|
|
83
|
+
nativeInputValueSetter.call(nativeInput, valueString);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
nativeInput.value = valueString;
|
|
87
|
+
}
|
|
88
|
+
// Disparar evento change para react-hook-form
|
|
89
|
+
const changeEvent = {
|
|
90
|
+
target: nativeInput,
|
|
91
|
+
currentTarget: nativeInput,
|
|
92
|
+
};
|
|
93
|
+
if (onChange) {
|
|
94
|
+
// En modo register, siempre usar formato de evento
|
|
95
|
+
onChange(changeEvent);
|
|
96
|
+
}
|
|
97
|
+
// Disparar eventos nativos
|
|
98
|
+
const inputEvent = new Event("input", {
|
|
99
|
+
bubbles: true,
|
|
100
|
+
cancelable: true,
|
|
101
|
+
});
|
|
102
|
+
nativeInput.dispatchEvent(inputEvent);
|
|
103
|
+
const changeEventNative = new Event("change", {
|
|
104
|
+
bubbles: true,
|
|
105
|
+
cancelable: true,
|
|
106
|
+
});
|
|
107
|
+
nativeInput.dispatchEvent(changeEventNative);
|
|
108
|
+
}
|
|
109
|
+
// También usar setValue directamente como respaldo
|
|
110
|
+
if (setValue) {
|
|
111
|
+
setValue(name, optionValue, {
|
|
112
|
+
shouldValidate: true,
|
|
113
|
+
shouldDirty: true,
|
|
114
|
+
shouldTouch: true,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Modo controlado - llamar onChange con valor directo
|
|
120
|
+
if (onChange) {
|
|
121
|
+
// Verificar si onChange es una función de valor directo o ChangeEventHandler
|
|
122
|
+
// Intentar primero como función de valor directo (modo controlado típico)
|
|
123
|
+
const onChangeAsValueFn = onChange;
|
|
124
|
+
// Si la función tiene la firma correcta, llamarla directamente
|
|
125
|
+
if (typeof onChangeAsValueFn === "function") {
|
|
126
|
+
onChangeAsValueFn(optionValue);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Si no, intentar como ChangeEventHandler (por compatibilidad)
|
|
130
|
+
const syntheticEvent = {
|
|
131
|
+
target: { value: String(optionValue) },
|
|
132
|
+
currentTarget: { value: String(optionValue) },
|
|
133
|
+
};
|
|
134
|
+
onChange(syntheticEvent);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const handleBlur = (event) => {
|
|
140
|
+
if (onBlur) {
|
|
141
|
+
if (isRegisterMode && hiddenInputRef.current) {
|
|
142
|
+
// En modo register, crear un evento de blur para react-hook-form
|
|
143
|
+
const blurEvent = event ||
|
|
144
|
+
{
|
|
145
|
+
target: hiddenInputRef.current,
|
|
146
|
+
currentTarget: hiddenInputRef.current,
|
|
147
|
+
};
|
|
148
|
+
onBlur(blurEvent);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// En modo controlado, llamar como función sin parámetros
|
|
152
|
+
onBlur();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// También disparar blur en el input hidden si existe
|
|
156
|
+
if (hiddenInputRef.current) {
|
|
157
|
+
hiddenInputRef.current.blur();
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
return (_jsxs("div", { className: "w-full", children: [_jsx("input", { ref: combinedRef, type: "hidden", name: name, value: actualValue !== undefined ? String(actualValue) : "", readOnly: true, tabIndex: -1, "aria-hidden": "true" }), _jsx("div", { className: containerClasses, role: "radiogroup", "aria-label": name, "aria-invalid": error ? "true" : "false", "aria-errormessage": error ? `${name}-error` : undefined, ...props, children: options.map((option, index) => {
|
|
161
|
+
const optionValue = String(option.value);
|
|
162
|
+
const isSelected = actualValue !== undefined && String(actualValue) === optionValue;
|
|
163
|
+
// En modo readOnly, las opciones no seleccionadas se ven deshabilitadas
|
|
164
|
+
const isDisabled = disabled || option.disabled || (readOnly && !isSelected);
|
|
165
|
+
const radioId = `${name || "radio"}-${index}-${option.value}`;
|
|
166
|
+
const radioClasses = `
|
|
167
|
+
${sizeClasses[size]}
|
|
168
|
+
rounded-full border-2 transition-all duration-200
|
|
169
|
+
flex items-center justify-center
|
|
170
|
+
${isSelected
|
|
171
|
+
? "border-[var(--color-primary)]"
|
|
172
|
+
: "border-[var(--color-border-default)]"}
|
|
173
|
+
${isSelected ? "bg-[var(--color-primary)]" : "bg-transparent"}
|
|
174
|
+
${isDisabled
|
|
175
|
+
? "opacity-50 cursor-not-allowed"
|
|
176
|
+
: "cursor-pointer hover:border-[var(--color-primary)]"}
|
|
177
|
+
focus:outline-none focus:ring-2 focus:ring-offset-0 focus:ring-[var(--color-primary)]
|
|
178
|
+
`;
|
|
179
|
+
const labelClasses = `
|
|
180
|
+
${labelSizeClasses[size]}
|
|
181
|
+
font-[var(--font-default)] select-none
|
|
182
|
+
${isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
|
|
183
|
+
text-[var(--color-text-primary)]
|
|
184
|
+
`;
|
|
185
|
+
const optionContainerClasses = `
|
|
186
|
+
flex items-center
|
|
187
|
+
${labelPosition === "left" ? "flex-row-reverse" : "flex-row"}
|
|
188
|
+
${gapClasses[gap]}
|
|
189
|
+
`;
|
|
190
|
+
return (_jsxs("div", { className: optionContainerClasses, onClick: () => !isDisabled && handleOptionClick(option.value), onBlur: index === options.length - 1
|
|
191
|
+
? () => {
|
|
192
|
+
handleBlur();
|
|
193
|
+
}
|
|
194
|
+
: undefined, role: "radio", "aria-checked": isSelected, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : 0, onKeyDown: (e) => {
|
|
195
|
+
if (isDisabled || readOnly)
|
|
196
|
+
return;
|
|
197
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
198
|
+
e.preventDefault();
|
|
199
|
+
handleOptionClick(option.value);
|
|
200
|
+
}
|
|
201
|
+
}, children: [_jsx("div", { className: radioClasses, children: isSelected && (_jsx("div", { className: "rounded-full bg-white", style: {
|
|
202
|
+
width: size === "sm"
|
|
203
|
+
? "8px"
|
|
204
|
+
: size === "md"
|
|
205
|
+
? "10px"
|
|
206
|
+
: "12px",
|
|
207
|
+
height: size === "sm"
|
|
208
|
+
? "8px"
|
|
209
|
+
: size === "md"
|
|
210
|
+
? "10px"
|
|
211
|
+
: "12px",
|
|
212
|
+
} })) }), option.label && (_jsx("label", { htmlFor: radioId, className: labelClasses, onClick: (e) => {
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
if (!isDisabled) {
|
|
215
|
+
handleOptionClick(option.value);
|
|
216
|
+
}
|
|
217
|
+
}, children: option.label }))] }, radioId));
|
|
218
|
+
}) }), error && (_jsx("p", { id: name ? `${name}-error` : undefined, className: "mt-1 text-sm text-[var(--color-danger)] font-[var(--font-default)]", children: error }))] }));
|
|
219
|
+
});
|
|
220
|
+
RadioButtonGroup.displayName = "RadioButtonGroup";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { InputProps } from "./Input";
|
|
3
|
+
import type { PaginationInterface } from "./Pagination";
|
|
4
|
+
export interface SearchSelectOption {
|
|
5
|
+
label: string;
|
|
6
|
+
value?: string;
|
|
7
|
+
description?: string | number;
|
|
8
|
+
icon?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SearchSelectInputProps<T = SearchSelectOption, K = string> extends Omit<InputProps, "onChange" | "value" | "ref"> {
|
|
11
|
+
/**
|
|
12
|
+
* Valor del input (controlado).
|
|
13
|
+
* Puede ser la opción completa (T), el valor extraído (K si hay getOptionValue), o un string (para compatibilidad con react-hook-form).
|
|
14
|
+
*/
|
|
15
|
+
value?: T | K | string;
|
|
16
|
+
/**
|
|
17
|
+
* Callback cuando cambia el valor del input.
|
|
18
|
+
* Recibe la opción completa (T) si no hay getOptionValue, o el valor extraído (K) si hay getOptionValue.
|
|
19
|
+
* También es compatible con react-hook-form: acepta el onChange estándar de HTML.
|
|
20
|
+
*/
|
|
21
|
+
onChange?: ((value: T | K) => void) | React.ChangeEventHandler<HTMLInputElement>;
|
|
22
|
+
/**
|
|
23
|
+
* Función que realiza la búsqueda y devuelve un Promise con los resultados
|
|
24
|
+
*/
|
|
25
|
+
onSearchPromiseFn: (text: string) => Promise<Array<T> | PaginationInterface<T>>;
|
|
26
|
+
/**
|
|
27
|
+
* Función que busca un elemento individual usando su valor (K).
|
|
28
|
+
* Se usa cuando hay un valor por defecto que no está presente en las opciones cargadas.
|
|
29
|
+
* Recibe el valor (K) y devuelve una Promise con el objeto completo (T) o undefined si no se encuentra.
|
|
30
|
+
*/
|
|
31
|
+
onSingleSearchPromiseFn: (value: K) => Promise<T | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* Callback al seleccionar una opción. Devuelve el item completo (T) y el valor mapeado (K)
|
|
34
|
+
*/
|
|
35
|
+
onSelectOption?: (option: T, value: K) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Título del dialog de selección. Por defecto "Seleccione una opción"
|
|
38
|
+
*/
|
|
39
|
+
dialogTitle?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Posición del botón de búsqueda. Por defecto "right"
|
|
42
|
+
*/
|
|
43
|
+
searchButtonPosition?: "left" | "right";
|
|
44
|
+
/**
|
|
45
|
+
* Texto a mostrar cuando no hay resultados
|
|
46
|
+
*/
|
|
47
|
+
noResultsText?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Obtiene el label que se muestra para cada opción. Por defecto usa la propiedad "label".
|
|
50
|
+
*/
|
|
51
|
+
getOptionLabel?: (item: T) => string;
|
|
52
|
+
/**
|
|
53
|
+
* Obtiene el valor que se devuelve al seleccionar una opción. Por defecto usa la propiedad "value".
|
|
54
|
+
*/
|
|
55
|
+
getOptionValue?: (item: T) => K;
|
|
56
|
+
/**
|
|
57
|
+
* Obtiene la descripción opcional para cada opción. Por defecto usa la propiedad "description".
|
|
58
|
+
*/
|
|
59
|
+
getOptionDescription?: (item: T) => string | number | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Renderizado personalizado de cada opción. Si se define, se ignora el render por defecto.
|
|
62
|
+
*/
|
|
63
|
+
renderOption?: (item: T) => React.ReactNode;
|
|
64
|
+
}
|
|
65
|
+
export declare const SearchSelectInput: <T = SearchSelectOption, K = string>(props: SearchSelectInputProps<T, K> & {
|
|
66
|
+
ref?: React.ForwardedRef<HTMLInputElement>;
|
|
67
|
+
}) => React.ReactElement;
|
|
68
|
+
//# sourceMappingURL=SearchSelectInput-OLD.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchSelectInput-OLD.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/SearchSelectInput-OLD.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,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,GAAG,KAAK,CAAC;IACtD;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EACL,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,GACxB,KAAK,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IAC/C;;OAEG;IACH,iBAAiB,EAAE,CACjB,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD;;;;OAIG;IACH,uBAAuB,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC9D;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC/C;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxC;;OAEG;IACH,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;AA6sCD,eAAO,MAAM,iBAAiB,EAAiC,CAC7D,CAAC,GAAG,kBAAkB,EACtB,CAAC,GAAG,MAAM,EAEV,KAAK,EAAE,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IACpC,GAAG,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC;CAC5C,KACE,KAAK,CAAC,YAAY,CAAC"}
|