next-recomponents 2.0.40 → 2.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +53 -13
- package/dist/index.d.ts +53 -13
- package/dist/index.js +1157 -26
- package/dist/index.mjs +1162 -27
- package/package.json +1 -1
- package/src/index.tsx +1 -0
- package/src/input/index.tsx +4 -4
- package/src/modal/index.tsx +8 -4
- package/src/pop/color.tsx +1 -1
- package/src/pop/index.tsx +22 -9
- package/src/pop/overlay.tsx +5 -3
- package/src/pop/types.ts +1 -1
- package/src/select/index.tsx +2 -2
- package/src/table/index.tsx +6 -8
- package/src/table-advanced/context.ts +88 -0
- package/src/table-advanced/filter.reducer.ts +19 -0
- package/src/table-advanced/h.table.tsx +579 -0
- package/src/table-advanced/header.tsx +335 -0
- package/src/table-advanced/icons.tsx +167 -0
- package/src/table-advanced/index.tsx +43 -0
- package/src/table-advanced/menu.item.tsx +21 -0
- package/src/table-advanced/searchable.tsx +24 -0
- package/src/table-advanced/sort.reducer.ts +29 -0
- package/src/table-advanced/types.ts +82 -0
- package/src/text-area/index.tsx +2 -2
package/package.json
CHANGED
package/src/index.tsx
CHANGED
package/src/input/index.tsx
CHANGED
|
@@ -21,13 +21,13 @@ export default function Input({
|
|
|
21
21
|
const [showPassword, setShowPassword] = useState(false);
|
|
22
22
|
const svg = icon;
|
|
23
23
|
return (
|
|
24
|
-
<div className="w-full relative">
|
|
25
|
-
<div className="relative flex items-center border rounded bg-white pr-1 mb-5">
|
|
24
|
+
<div className="w-full relative my-3">
|
|
25
|
+
<div className="relative flex items-center border rounded bg-white pr-1 mb-5 w-full">
|
|
26
26
|
<input
|
|
27
27
|
{...props}
|
|
28
28
|
type={isPassword && showPassword ? "text" : props.type}
|
|
29
29
|
className={[
|
|
30
|
-
"
|
|
30
|
+
"block p-2 w-full bg-transparent min-w-0",
|
|
31
31
|
isPassword && "pr-10",
|
|
32
32
|
value !== "" && !isValid && "bg-red-200 text-black",
|
|
33
33
|
value !== "" && isValid && "bg-green-200 text-black",
|
|
@@ -36,7 +36,7 @@ export default function Input({
|
|
|
36
36
|
.filter(Boolean)
|
|
37
37
|
.join(" ")}
|
|
38
38
|
/>
|
|
39
|
-
<label className="absolute -top-2.5 left-2 text-xs font-bold
|
|
39
|
+
<label className="absolute -top-2.5 left-2 text-xs font-bold px-1">
|
|
40
40
|
{label}
|
|
41
41
|
{props?.required && <span className="text-red-500 ml-1">*</span>}
|
|
42
42
|
</label>
|
package/src/modal/index.tsx
CHANGED
|
@@ -15,6 +15,7 @@ export default function Modal({
|
|
|
15
15
|
children,
|
|
16
16
|
color = "primary",
|
|
17
17
|
title,
|
|
18
|
+
onClose,
|
|
18
19
|
}: {
|
|
19
20
|
button: ReactElement<React.ComponentPropsWithRef<"button">>;
|
|
20
21
|
children: ReactElement<
|
|
@@ -22,9 +23,12 @@ export default function Modal({
|
|
|
22
23
|
>;
|
|
23
24
|
color?: PopupColor;
|
|
24
25
|
title?: string;
|
|
26
|
+
onClose?: () => Promise<boolean>;
|
|
25
27
|
}) {
|
|
26
28
|
const pop = usePopup();
|
|
27
|
-
const hide = () =>
|
|
29
|
+
const hide = () => {
|
|
30
|
+
pop.close(false);
|
|
31
|
+
};
|
|
28
32
|
|
|
29
33
|
const childrenWithHide = useMemo(
|
|
30
34
|
() => cloneElement(children, { hide }),
|
|
@@ -39,14 +43,14 @@ export default function Modal({
|
|
|
39
43
|
const onClick = props?.onClick;
|
|
40
44
|
|
|
41
45
|
return (
|
|
42
|
-
|
|
46
|
+
<div className="">
|
|
43
47
|
{cloneElement(button, {
|
|
44
48
|
onClick: (e) => {
|
|
45
49
|
onClick?.(e as any);
|
|
46
|
-
pop.modal(childrenWithHide, color, false, true);
|
|
50
|
+
pop.modal(childrenWithHide, color, false, true, onClose);
|
|
47
51
|
},
|
|
48
52
|
})}
|
|
49
53
|
{pop.PopupComponent}
|
|
50
|
-
|
|
54
|
+
</div>
|
|
51
55
|
);
|
|
52
56
|
}
|
package/src/pop/color.tsx
CHANGED
package/src/pop/index.tsx
CHANGED
|
@@ -30,13 +30,21 @@ export default function usePopup() {
|
|
|
30
30
|
[],
|
|
31
31
|
);
|
|
32
32
|
|
|
33
|
-
const close = useCallback(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
const close = useCallback(
|
|
34
|
+
async (confirmed: boolean, value?: string) => {
|
|
35
|
+
const prev = { ...popup };
|
|
36
|
+
let visible = false;
|
|
37
|
+
if (confirmed) {
|
|
38
|
+
prev.onConfirm?.(value);
|
|
39
|
+
} else if (prev?.onCancel) {
|
|
40
|
+
visible = !Boolean(await prev.onCancel?.());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const data = { ...prev, visible, inputValue: "" };
|
|
44
|
+
setPopup(data);
|
|
45
|
+
},
|
|
46
|
+
[popup],
|
|
47
|
+
);
|
|
40
48
|
|
|
41
49
|
const alert = useCallback(
|
|
42
50
|
(message: string, color: PopupColor = "primary"): Promise<void> =>
|
|
@@ -58,14 +66,19 @@ export default function usePopup() {
|
|
|
58
66
|
color: PopupColor = "primary",
|
|
59
67
|
icons: boolean = false,
|
|
60
68
|
full: boolean = false,
|
|
69
|
+
onClose?: () => Promise<boolean>,
|
|
61
70
|
): Promise<void> =>
|
|
62
71
|
new Promise((resolve) =>
|
|
63
72
|
open({
|
|
64
73
|
type: "modal",
|
|
65
74
|
message,
|
|
66
75
|
color,
|
|
67
|
-
onConfirm: () =>
|
|
68
|
-
|
|
76
|
+
// onConfirm: () => {
|
|
77
|
+
// resolve();
|
|
78
|
+
// },
|
|
79
|
+
onCancel: async () => {
|
|
80
|
+
return await onClose?.();
|
|
81
|
+
},
|
|
69
82
|
icons,
|
|
70
83
|
full,
|
|
71
84
|
}),
|
package/src/pop/overlay.tsx
CHANGED
|
@@ -16,7 +16,7 @@ export function PopupOverlay({
|
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
18
|
<div
|
|
19
|
-
className=
|
|
19
|
+
className="fixed inset-0 flex items-center justify-center z-[1000] overflow-auto p-1"
|
|
20
20
|
style={{ background: "rgba(15,23,42,0.45)", backdropFilter: "blur(2px)" }}
|
|
21
21
|
// onClick={(e) => {
|
|
22
22
|
// if (e.target === e.currentTarget) {
|
|
@@ -33,7 +33,7 @@ export function PopupOverlay({
|
|
|
33
33
|
`}</style>
|
|
34
34
|
|
|
35
35
|
<div
|
|
36
|
-
className={`bg-gradient-to-br ${c.bg} border ${c.border} ${popup.full == true ? " w-
|
|
36
|
+
className={`bg-gradient-to-br ${c.bg} border ${c.border} ${popup.full == true ? " w-screen h-screen mt-10 " : " max-w-sm "} rounded shadow m-2`}
|
|
37
37
|
style={{ animation: "fadeInScale 0.18s ease-out", overflow: "auto" }}
|
|
38
38
|
>
|
|
39
39
|
{/* Header */}
|
|
@@ -77,7 +77,9 @@ export function PopupOverlay({
|
|
|
77
77
|
)}
|
|
78
78
|
|
|
79
79
|
{/* Divider */}
|
|
80
|
-
|
|
80
|
+
{popup.type !== "modal" && (
|
|
81
|
+
<div className={`border-t ${c.border} mt-4`} />
|
|
82
|
+
)}
|
|
81
83
|
|
|
82
84
|
{/* Actions */}
|
|
83
85
|
{popup.type !== "modal" && (
|
package/src/pop/types.ts
CHANGED
package/src/select/index.tsx
CHANGED
|
@@ -119,7 +119,7 @@ export default function Select({
|
|
|
119
119
|
}
|
|
120
120
|
}, [isOpen]);
|
|
121
121
|
return (
|
|
122
|
-
<div ref={containerRef} className="w-full relative
|
|
122
|
+
<div ref={containerRef} className="w-full relative my-3">
|
|
123
123
|
<div className="relative flex items-center border rounded bg-white">
|
|
124
124
|
<input
|
|
125
125
|
autoComplete="off"
|
|
@@ -163,7 +163,7 @@ export default function Select({
|
|
|
163
163
|
onKeyDown={handleKeyDown}
|
|
164
164
|
/>
|
|
165
165
|
{label && (
|
|
166
|
-
<label className="absolute -top-2.5 left-2 text-xs font-bold
|
|
166
|
+
<label className="absolute -top-2.5 left-2 text-xs font-bold px-1">
|
|
167
167
|
{label} {props?.required && <span className="text-red-500">*</span>}
|
|
168
168
|
</label>
|
|
169
169
|
)}
|
package/src/table/index.tsx
CHANGED
|
@@ -9,13 +9,6 @@ import regularExpresions from "../regular-expresions";
|
|
|
9
9
|
|
|
10
10
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
11
11
|
|
|
12
|
-
export interface TableButtonProps extends React.MouseEvent<
|
|
13
|
-
HTMLButtonElement,
|
|
14
|
-
MouseEvent
|
|
15
|
-
> {
|
|
16
|
-
row: Record<string, any>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
12
|
type FooterAggregation = "sum" | "avg" | "count";
|
|
20
13
|
|
|
21
14
|
interface FooterType {
|
|
@@ -23,7 +16,12 @@ interface FooterType {
|
|
|
23
16
|
}
|
|
24
17
|
|
|
25
18
|
type GridDensity = "compact" | "standard" | "comfortable";
|
|
26
|
-
|
|
19
|
+
export interface TableButtonProps extends React.MouseEvent<
|
|
20
|
+
HTMLButtonElement,
|
|
21
|
+
MouseEvent
|
|
22
|
+
> {
|
|
23
|
+
row: Record<string, any>;
|
|
24
|
+
}
|
|
27
25
|
interface TableProps {
|
|
28
26
|
data: any;
|
|
29
27
|
searchable?: boolean;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useEffect, useMemo, useReducer, useState } from "react";
|
|
2
|
+
import { filterReducer } from "./filter.reducer";
|
|
3
|
+
import { FiltersType, SortType } from "./types";
|
|
4
|
+
import { sortReducer } from "./sort.reducer";
|
|
5
|
+
import useExcel from "../use-excel";
|
|
6
|
+
|
|
7
|
+
export default function useContext({ onSelect }: { onSelect?: boolean }) {
|
|
8
|
+
const excel = useExcel();
|
|
9
|
+
const [editions, setEditions] = useState<any[]>([]);
|
|
10
|
+
const [selected, setSelected] = useState<any[]>([]);
|
|
11
|
+
const [data, setData] = useState<any[]>([]);
|
|
12
|
+
const headers = [...new Set(data.map((b) => Object.keys(b)).flat())];
|
|
13
|
+
|
|
14
|
+
const [currentHeader, setCurrentHeader] = useState<string | null>(null);
|
|
15
|
+
const cols = headers.length;
|
|
16
|
+
const [filters, setFilters] = useReducer(
|
|
17
|
+
filterReducer,
|
|
18
|
+
new Array<FiltersType>(),
|
|
19
|
+
);
|
|
20
|
+
const [searchBy, setSearchBy] = useState<string>("");
|
|
21
|
+
const [sort, setSort] = useReducer(sortReducer, new Array<SortType>());
|
|
22
|
+
|
|
23
|
+
const sortedData: any[] = useMemo(() => {
|
|
24
|
+
return [...data].sort((a, b) => {
|
|
25
|
+
for (const { field, type } of sort.reverse()) {
|
|
26
|
+
const av = a[field];
|
|
27
|
+
const bv = b[field];
|
|
28
|
+
|
|
29
|
+
// nulls/undefined van siempre al final, independiente de ASC/DESC
|
|
30
|
+
if (av == null && bv == null) continue;
|
|
31
|
+
if (av == null) return 1;
|
|
32
|
+
if (bv == null) return -1;
|
|
33
|
+
|
|
34
|
+
let cmp: number;
|
|
35
|
+
if (typeof av === "number" && typeof bv === "number") {
|
|
36
|
+
cmp = av - bv;
|
|
37
|
+
} else {
|
|
38
|
+
cmp = `${av}`.localeCompare(`${bv}`, undefined, { numeric: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (cmp !== 0) {
|
|
42
|
+
return type === "DESC" ? -cmp : cmp;
|
|
43
|
+
}
|
|
44
|
+
// empate: sigue al siguiente campo en context.sort
|
|
45
|
+
}
|
|
46
|
+
return 0;
|
|
47
|
+
});
|
|
48
|
+
}, [data, sort]);
|
|
49
|
+
const filteredData = [...sortedData].filter((d) => {
|
|
50
|
+
if (editions.includes(d?.id)) return true;
|
|
51
|
+
|
|
52
|
+
for (const { field, values } of filters) {
|
|
53
|
+
if (!values.includes(d[field])) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (filters.length == 0) {
|
|
62
|
+
setEditions([]);
|
|
63
|
+
}
|
|
64
|
+
}, [filters]);
|
|
65
|
+
return {
|
|
66
|
+
currentHeader,
|
|
67
|
+
setCurrentHeader,
|
|
68
|
+
sort,
|
|
69
|
+
setSort,
|
|
70
|
+
cols,
|
|
71
|
+
headers,
|
|
72
|
+
data,
|
|
73
|
+
sortedData,
|
|
74
|
+
filters,
|
|
75
|
+
setFilters,
|
|
76
|
+
filteredData,
|
|
77
|
+
setData,
|
|
78
|
+
searchBy,
|
|
79
|
+
setSearchBy,
|
|
80
|
+
selected,
|
|
81
|
+
setSelected,
|
|
82
|
+
editions,
|
|
83
|
+
excel,
|
|
84
|
+
setEditions,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type ContextType = ReturnType<typeof useContext>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FiltersType } from "./types";
|
|
2
|
+
|
|
3
|
+
export const filterReducer = (acc: FiltersType[], b: FiltersType | null) => {
|
|
4
|
+
if (b == null) return [];
|
|
5
|
+
const newAcc: FiltersType[] = [];
|
|
6
|
+
newAcc.push(b);
|
|
7
|
+
return newAcc;
|
|
8
|
+
|
|
9
|
+
// const newAcc = [...acc];
|
|
10
|
+
|
|
11
|
+
// const index = newAcc.findIndex((aa) => aa.field == b.field);
|
|
12
|
+
// if (index >= 0) {
|
|
13
|
+
// newAcc[index].values = b.values;
|
|
14
|
+
// } else {
|
|
15
|
+
// newAcc.push(b);
|
|
16
|
+
// }
|
|
17
|
+
|
|
18
|
+
// return newAcc;
|
|
19
|
+
};
|