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
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
|
+
import DropIcon, {
|
|
4
|
+
ArrowDownIcon,
|
|
5
|
+
ArrowUPIcon,
|
|
6
|
+
FilterIcon,
|
|
7
|
+
OrderAscIcon,
|
|
8
|
+
OrderDesIcon,
|
|
9
|
+
} from "./icons";
|
|
10
|
+
import { MenuItem } from "./menu.item";
|
|
11
|
+
import Form from "../form";
|
|
12
|
+
import { ContextType } from "./context";
|
|
13
|
+
import { TableProps } from "./types";
|
|
14
|
+
|
|
15
|
+
export function Header({
|
|
16
|
+
header,
|
|
17
|
+
context,
|
|
18
|
+
drag,
|
|
19
|
+
padding,
|
|
20
|
+
}: {
|
|
21
|
+
header: string;
|
|
22
|
+
context: ContextType & Omit<TableProps, "data">;
|
|
23
|
+
drag: {
|
|
24
|
+
widths: Record<string, number>;
|
|
25
|
+
gridTemplateColumns: string;
|
|
26
|
+
startDrag: (e: React.MouseEvent, headerKey: string) => void;
|
|
27
|
+
};
|
|
28
|
+
padding: string;
|
|
29
|
+
}) {
|
|
30
|
+
const [visible, setVisible] = useState(false);
|
|
31
|
+
|
|
32
|
+
const sortIndex = context.sort.findIndex((b) => b.field == header);
|
|
33
|
+
const [text, setText] = useState("");
|
|
34
|
+
const cellRef = useRef<HTMLDivElement>(null);
|
|
35
|
+
const [coords, setCoords] = useState<{ top: number; left: number } | null>(
|
|
36
|
+
null,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const filteredValues = [
|
|
40
|
+
...new Set(context.data.map((d) => d[header])),
|
|
41
|
+
].filter((d) => {
|
|
42
|
+
if (text == "") return true;
|
|
43
|
+
return `${d}`.toLowerCase().includes(text.toLowerCase());
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const [selected, setSelected] = useState([...filteredValues]);
|
|
47
|
+
|
|
48
|
+
const hasFilter = context.filters.find((b) => b.field == header);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (visible) {
|
|
52
|
+
context.setCurrentHeader(header);
|
|
53
|
+
}
|
|
54
|
+
setText("");
|
|
55
|
+
}, [visible]);
|
|
56
|
+
|
|
57
|
+
// Calcula la posición del popup relativa al viewport cada vez que se abre,
|
|
58
|
+
// y la vuelve a calcular en scroll/resize mientras está abierto.
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!visible || !cellRef.current) {
|
|
61
|
+
setCoords(null);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const updateCoords = () => {
|
|
66
|
+
if (!cellRef.current) return;
|
|
67
|
+
const rect = cellRef.current.getBoundingClientRect();
|
|
68
|
+
const POPUP_WIDTH = 300;
|
|
69
|
+
setCoords({
|
|
70
|
+
top: rect.bottom,
|
|
71
|
+
left: Math.max(8, rect.right - POPUP_WIDTH),
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
updateCoords();
|
|
76
|
+
window.addEventListener("scroll", updateCoords, true);
|
|
77
|
+
window.addEventListener("resize", updateCoords);
|
|
78
|
+
return () => {
|
|
79
|
+
window.removeEventListener("scroll", updateCoords, true);
|
|
80
|
+
window.removeEventListener("resize", updateCoords);
|
|
81
|
+
};
|
|
82
|
+
}, [visible]);
|
|
83
|
+
|
|
84
|
+
if (header == "__modal__") {
|
|
85
|
+
return (
|
|
86
|
+
<div className="">
|
|
87
|
+
<div
|
|
88
|
+
className={"break-words border text-center relative "}
|
|
89
|
+
style={{ zIndex: 10, height: `${padding}px`, maxHeight: "100px" }}
|
|
90
|
+
></div>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
} else if (header == "__select__") {
|
|
94
|
+
return (
|
|
95
|
+
<div className="">
|
|
96
|
+
<div
|
|
97
|
+
className={
|
|
98
|
+
"border flex items-center justify-center text-center relative "
|
|
99
|
+
}
|
|
100
|
+
style={{ zIndex: 10, height: `${padding}px`, maxHeight: "100px" }}
|
|
101
|
+
>
|
|
102
|
+
<input
|
|
103
|
+
type="checkbox"
|
|
104
|
+
className="w-5 h-5 accent-blue-600 transition-all duration-300 checked:scale-110"
|
|
105
|
+
checked={Boolean(
|
|
106
|
+
context.selected.length == context.filteredData.length,
|
|
107
|
+
)}
|
|
108
|
+
onChange={(e) => {
|
|
109
|
+
if (context.selected.length < context.filteredData.length) {
|
|
110
|
+
context.setSelected(context.filteredData.map((d) => d.id));
|
|
111
|
+
} else {
|
|
112
|
+
context.setSelected([]);
|
|
113
|
+
}
|
|
114
|
+
}}
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
} else
|
|
120
|
+
return (
|
|
121
|
+
<div className="">
|
|
122
|
+
<div
|
|
123
|
+
ref={cellRef}
|
|
124
|
+
className={"break-words border text-center relative "}
|
|
125
|
+
style={{ zIndex: 10, height: `${padding}px`, maxHeight: "100px" }}
|
|
126
|
+
>
|
|
127
|
+
<div className="flex gap-5 items-center justify-between h-full cursor-pointer">
|
|
128
|
+
<div className="flex gap-2">
|
|
129
|
+
{hasFilter ? (
|
|
130
|
+
<div
|
|
131
|
+
className="cursor-pointer"
|
|
132
|
+
onClick={(e) => {
|
|
133
|
+
context.setFilters(null);
|
|
134
|
+
setSelected(filteredValues);
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
<FilterIcon />
|
|
138
|
+
</div>
|
|
139
|
+
) : (
|
|
140
|
+
<div></div>
|
|
141
|
+
)}
|
|
142
|
+
<div
|
|
143
|
+
onClick={(e) => {
|
|
144
|
+
context.setSort({
|
|
145
|
+
[header]: "TOGGLE",
|
|
146
|
+
});
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{sortIndex >= 0 ? (
|
|
150
|
+
context.sort[sortIndex].type === "ASC" ? (
|
|
151
|
+
<ArrowUPIcon />
|
|
152
|
+
) : (
|
|
153
|
+
<ArrowDownIcon />
|
|
154
|
+
)
|
|
155
|
+
) : (
|
|
156
|
+
<div></div>
|
|
157
|
+
)}
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
<div
|
|
161
|
+
className="w-full"
|
|
162
|
+
onClick={(e) => {
|
|
163
|
+
context.setSort({
|
|
164
|
+
[header]: "TOGGLE",
|
|
165
|
+
});
|
|
166
|
+
}}
|
|
167
|
+
>
|
|
168
|
+
{header}
|
|
169
|
+
</div>
|
|
170
|
+
<div
|
|
171
|
+
style={{ zIndex: 10 }}
|
|
172
|
+
className="icon font-bold p-1 cursor-pointer pr-3 "
|
|
173
|
+
onClick={(e) => setVisible(true)}
|
|
174
|
+
>
|
|
175
|
+
<DropIcon />
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
{context.currentHeader != header && (
|
|
180
|
+
<div
|
|
181
|
+
onMouseDown={(e) => drag.startDrag(e, header)}
|
|
182
|
+
className="absolute top-0 h-full cursor-col-resize hover:bg-blue-300 p-2"
|
|
183
|
+
style={{ right: -2, width: 6, zIndex: 20 }}
|
|
184
|
+
/>
|
|
185
|
+
)}
|
|
186
|
+
|
|
187
|
+
{visible &&
|
|
188
|
+
context.currentHeader == header &&
|
|
189
|
+
createPortal(
|
|
190
|
+
<>
|
|
191
|
+
{/* Overlay a pantalla completa para cerrar al hacer click fuera */}
|
|
192
|
+
<div
|
|
193
|
+
className="w-screen h-screen fixed top-0 left-0"
|
|
194
|
+
onClick={(e) => {
|
|
195
|
+
setVisible(false);
|
|
196
|
+
}}
|
|
197
|
+
style={{ zIndex: 9998 }}
|
|
198
|
+
/>
|
|
199
|
+
|
|
200
|
+
{/* Popup posicionado con fixed + coordenadas calculadas del header real */}
|
|
201
|
+
{coords && (
|
|
202
|
+
<div
|
|
203
|
+
className="fixed"
|
|
204
|
+
style={{ top: coords.top, left: coords.left, zIndex: 9999 }}
|
|
205
|
+
>
|
|
206
|
+
<div className="bg-gray-100 w-[300px] flex flex-col border shadow rounded ">
|
|
207
|
+
{hasFilter && (
|
|
208
|
+
<MenuItem
|
|
209
|
+
label="Quitar filtro"
|
|
210
|
+
icon={<FilterIcon />}
|
|
211
|
+
onClick={(e) => {
|
|
212
|
+
setVisible(false);
|
|
213
|
+
context.setFilters(null);
|
|
214
|
+
setSelected(filteredValues);
|
|
215
|
+
}}
|
|
216
|
+
/>
|
|
217
|
+
)}
|
|
218
|
+
<MenuItem
|
|
219
|
+
label="Orenar de A a Z"
|
|
220
|
+
icon={<OrderAscIcon />}
|
|
221
|
+
onClick={(e) => {
|
|
222
|
+
setVisible(false);
|
|
223
|
+
context.setSort({ [header]: "ASC" });
|
|
224
|
+
context.setCurrentHeader(null);
|
|
225
|
+
}}
|
|
226
|
+
/>
|
|
227
|
+
<MenuItem
|
|
228
|
+
label="Orenar de Z a A"
|
|
229
|
+
icon={<OrderDesIcon />}
|
|
230
|
+
onClick={(e) => {
|
|
231
|
+
setVisible(false);
|
|
232
|
+
context.setSort({ [header]: "DESC" });
|
|
233
|
+
context.setCurrentHeader(null);
|
|
234
|
+
}}
|
|
235
|
+
/>
|
|
236
|
+
<Form
|
|
237
|
+
onSubmit={(e) => {
|
|
238
|
+
if (text != "") {
|
|
239
|
+
setSelected(filteredValues);
|
|
240
|
+
}
|
|
241
|
+
context.setFilters({
|
|
242
|
+
field: header,
|
|
243
|
+
values: text != "" ? filteredValues : selected,
|
|
244
|
+
});
|
|
245
|
+
setVisible(false);
|
|
246
|
+
context.setCurrentHeader(null);
|
|
247
|
+
}}
|
|
248
|
+
>
|
|
249
|
+
<div className="m-2 bg-white gap-2 flex flex-col">
|
|
250
|
+
<input
|
|
251
|
+
placeholder="Buscar..."
|
|
252
|
+
className="border shadow rounded p-2 w-full"
|
|
253
|
+
value={text}
|
|
254
|
+
onChange={(e) => setText(e.target.value)}
|
|
255
|
+
/>
|
|
256
|
+
<div
|
|
257
|
+
className="flex items-start flex-col p-2 border shadow rounded max-w-[600px] h-[300px] bg-white "
|
|
258
|
+
style={{ overflow: "auto" }}
|
|
259
|
+
>
|
|
260
|
+
<div
|
|
261
|
+
className={
|
|
262
|
+
"flex gap-2 " +
|
|
263
|
+
(text != "" ? "text-gray-400" : "")
|
|
264
|
+
}
|
|
265
|
+
>
|
|
266
|
+
<input
|
|
267
|
+
type="checkbox"
|
|
268
|
+
checked={
|
|
269
|
+
text != "" ||
|
|
270
|
+
selected.length == filteredValues.length
|
|
271
|
+
}
|
|
272
|
+
onClick={(e) => {
|
|
273
|
+
if (
|
|
274
|
+
selected.length == filteredValues.length
|
|
275
|
+
) {
|
|
276
|
+
setSelected([]);
|
|
277
|
+
} else {
|
|
278
|
+
setSelected(filteredValues);
|
|
279
|
+
}
|
|
280
|
+
}}
|
|
281
|
+
disabled={text != ""}
|
|
282
|
+
/>
|
|
283
|
+
<div className="truncate">Seleccionar todo</div>
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
{filteredValues.map((d) => {
|
|
287
|
+
return (
|
|
288
|
+
<div key={d} className="flex gap-2 ">
|
|
289
|
+
<input
|
|
290
|
+
disabled={text != ""}
|
|
291
|
+
type="checkbox"
|
|
292
|
+
onChange={(e) => {
|
|
293
|
+
const ns = [...selected];
|
|
294
|
+
const index = ns.findIndex((n) => n == d);
|
|
295
|
+
if (index >= 0) {
|
|
296
|
+
ns.splice(index, 1);
|
|
297
|
+
} else {
|
|
298
|
+
ns.push(d);
|
|
299
|
+
}
|
|
300
|
+
setSelected(ns);
|
|
301
|
+
}}
|
|
302
|
+
checked={text != "" || selected.includes(d)}
|
|
303
|
+
/>
|
|
304
|
+
<div className="truncate">{d}</div>
|
|
305
|
+
</div>
|
|
306
|
+
);
|
|
307
|
+
})}
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
<div className="flex justify-between p-2">
|
|
311
|
+
<button
|
|
312
|
+
onClick={(e) => {
|
|
313
|
+
setVisible(false);
|
|
314
|
+
context.setCurrentHeader(null);
|
|
315
|
+
}}
|
|
316
|
+
className="p-2 border shadow rounded bg-gray-300 text-black"
|
|
317
|
+
type="button"
|
|
318
|
+
>
|
|
319
|
+
Cancelar
|
|
320
|
+
</button>
|
|
321
|
+
<button className="p-2 border shadow rounded bg-blue-500 text-white">
|
|
322
|
+
Aceptar
|
|
323
|
+
</button>
|
|
324
|
+
</div>
|
|
325
|
+
</Form>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
)}
|
|
329
|
+
</>,
|
|
330
|
+
document.body,
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
);
|
|
335
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export default function DropIcon() {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
stroke="currentColor"
|
|
5
|
+
fill="currentColor"
|
|
6
|
+
strokeWidth="0"
|
|
7
|
+
viewBox="0 0 512 512"
|
|
8
|
+
height="1em"
|
|
9
|
+
className="text-blue-500"
|
|
10
|
+
width="1em"
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
>
|
|
13
|
+
<circle cx="256" cy="256" r="48"></circle>
|
|
14
|
+
<circle cx="256" cy="416" r="48"></circle>
|
|
15
|
+
<circle cx="256" cy="96" r="48"></circle>
|
|
16
|
+
</svg>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
export function EditIcon() {
|
|
20
|
+
return (
|
|
21
|
+
<svg
|
|
22
|
+
className="text-white"
|
|
23
|
+
stroke="currentColor"
|
|
24
|
+
fill="currentColor"
|
|
25
|
+
strokeWidth="0"
|
|
26
|
+
viewBox="0 0 24 24"
|
|
27
|
+
height="1em"
|
|
28
|
+
width="1em"
|
|
29
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
30
|
+
>
|
|
31
|
+
<path d="M16.7574 2.99678L14.7574 4.99678H5V18.9968H19V9.23943L21 7.23943V19.9968C21 20.5491 20.5523 20.9968 20 20.9968H4C3.44772 20.9968 3 20.5491 3 19.9968V3.99678C3 3.4445 3.44772 2.99678 4 2.99678H16.7574ZM20.4853 2.09729L21.8995 3.5115L12.7071 12.7039L11.2954 12.7064L11.2929 11.2897L20.4853 2.09729Z"></path>
|
|
32
|
+
</svg>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
export function ExcelIcon() {
|
|
36
|
+
return (
|
|
37
|
+
<svg
|
|
38
|
+
stroke="currentColor"
|
|
39
|
+
fill="currentColor"
|
|
40
|
+
strokeWidth="0"
|
|
41
|
+
viewBox="0 0 24 24"
|
|
42
|
+
height="1em"
|
|
43
|
+
width="1em"
|
|
44
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
45
|
+
>
|
|
46
|
+
<path d="M2.85858 2.87732L15.4293 1.0815C15.7027 1.04245 15.9559 1.2324 15.995 1.50577C15.9983 1.52919 16 1.55282 16 1.57648V22.4235C16 22.6996 15.7761 22.9235 15.5 22.9235C15.4763 22.9235 15.4527 22.9218 15.4293 22.9184L2.85858 21.1226C2.36593 21.0522 2 20.6303 2 20.1327V3.86727C2 3.36962 2.36593 2.9477 2.85858 2.87732ZM4 4.73457V19.2654L14 20.694V3.30599L4 4.73457ZM17 19H20V4.99997H17V2.99997H21C21.5523 2.99997 22 3.44769 22 3.99997V20C22 20.5523 21.5523 21 21 21H17V19ZM10.2 12L13 16H10.6L9 13.7143L7.39999 16H5L7.8 12L5 7.99997H7.39999L9 10.2857L10.6 7.99997H13L10.2 12Z"></path>
|
|
47
|
+
</svg>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
export function OrderAscIcon() {
|
|
51
|
+
return (
|
|
52
|
+
<svg
|
|
53
|
+
stroke="currentColor"
|
|
54
|
+
fill="currentColor"
|
|
55
|
+
strokeWidth="0"
|
|
56
|
+
version="1"
|
|
57
|
+
viewBox="0 0 48 48"
|
|
58
|
+
enableBackground="new 0 0 48 48"
|
|
59
|
+
height="1em"
|
|
60
|
+
width="1em"
|
|
61
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
62
|
+
>
|
|
63
|
+
<polygon
|
|
64
|
+
fill="#546E7A"
|
|
65
|
+
points="38,33 38,5 34,5 34,33 28,33 36,43 44,33"
|
|
66
|
+
></polygon>
|
|
67
|
+
<g fill="#2196F3">
|
|
68
|
+
<path d="M16.8,17.2h-5.3l-1.1,3H6.9L12.6,5h2.9l5.7,15.2h-3.2L16.8,17.2z M12.2,14.5H16l-1.9-5.7L12.2,14.5z"></path>
|
|
69
|
+
<path d="M12.4,40.5H20V43H8.4v-1.9L16,30.3H8.4v-2.5h11.4v1.7L12.4,40.5z"></path>
|
|
70
|
+
</g>
|
|
71
|
+
</svg>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function OrderDesIcon() {
|
|
76
|
+
return (
|
|
77
|
+
<svg
|
|
78
|
+
stroke="currentColor"
|
|
79
|
+
fill="currentColor"
|
|
80
|
+
strokeWidth="0"
|
|
81
|
+
version="1"
|
|
82
|
+
viewBox="0 0 48 48"
|
|
83
|
+
enableBackground="new 0 0 48 48"
|
|
84
|
+
height="1em"
|
|
85
|
+
width="1em"
|
|
86
|
+
className="text-blue-500"
|
|
87
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
88
|
+
>
|
|
89
|
+
<g fill="#2196F3">
|
|
90
|
+
<path d="M16.8,40h-5.3l-1.1,3H6.9l5.7-15.2h2.9L21.1,43h-3.2L16.8,40z M12.2,37.3H16l-1.9-5.7L12.2,37.3z"></path>
|
|
91
|
+
<path d="M12.4,17.7H20v2.5H8.4v-1.9L16,7.5H8.4V5h11.4v1.7L12.4,17.7z"></path>
|
|
92
|
+
</g>
|
|
93
|
+
<polygon
|
|
94
|
+
fill="#546E7A"
|
|
95
|
+
points="38,33 38,5 34,5 34,33 28,33 36,43 44,33"
|
|
96
|
+
></polygon>
|
|
97
|
+
</svg>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function ArrowDownIcon() {
|
|
102
|
+
return (
|
|
103
|
+
<svg
|
|
104
|
+
stroke="currentColor"
|
|
105
|
+
fill="currentColor"
|
|
106
|
+
strokeWidth="0"
|
|
107
|
+
className="text-blue-500"
|
|
108
|
+
viewBox="0 0 512 512"
|
|
109
|
+
height="1em"
|
|
110
|
+
width="1em"
|
|
111
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
112
|
+
>
|
|
113
|
+
<path d="M348.3 295.6c-5-5.1-13.3-5.1-18.4-.1L269 356.2V124.9c0-7.1-5.8-12.9-13-12.9s-13 5.8-13 12.9v231.3l-60.9-60.8c-5.1-5-13.3-4.9-18.4.1-5 5.1-5 13.2.1 18.3l83 82.4c1.2 1.1 2.5 2 4.1 2.7 1.6.7 3.3 1 5 1 3.4 0 6.6-1.3 9.1-3.7l83-82.4c5.2-4.9 5.3-13.1.3-18.2z"></path>
|
|
114
|
+
</svg>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function ArrowUPIcon() {
|
|
119
|
+
return (
|
|
120
|
+
<svg
|
|
121
|
+
stroke="currentColor"
|
|
122
|
+
fill="currentColor"
|
|
123
|
+
strokeWidth="0"
|
|
124
|
+
className="text-blue-500"
|
|
125
|
+
viewBox="0 0 512 512"
|
|
126
|
+
height="1em"
|
|
127
|
+
width="1em"
|
|
128
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
129
|
+
>
|
|
130
|
+
<path d="M348.3 216.4c-5 5.1-13.3 5.1-18.4.1L269 155.8v231.3c0 7.1-5.8 12.9-13 12.9s-13-5.8-13-12.9V155.8l-60.9 60.8c-5.1 5-13.3 4.9-18.4-.1-5-5.1-5-13.2.1-18.3l83-82.4c1.2-1.1 2.5-2 4.1-2.7 1.6-.7 3.3-1 5-1 3.4 0 6.6 1.3 9.1 3.7l83 82.4c5.2 4.9 5.3 13.1.3 18.2z"></path>
|
|
131
|
+
</svg>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
export function SearchIcon() {
|
|
135
|
+
return (
|
|
136
|
+
<svg
|
|
137
|
+
stroke="currentColor"
|
|
138
|
+
fill="currentColor"
|
|
139
|
+
strokeWidth="0"
|
|
140
|
+
viewBox="0 0 24 24"
|
|
141
|
+
className="text-blue-500"
|
|
142
|
+
height="1em"
|
|
143
|
+
width="1em"
|
|
144
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
145
|
+
>
|
|
146
|
+
<path fill="none" d="M0 0h24v24H0z"></path>
|
|
147
|
+
<path d="M7 9H2V7h5zm0 3H2v2h5zm13.59 7-3.83-3.83c-.8.52-1.74.83-2.76.83-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5c0 1.02-.31 1.96-.83 2.75L22 17.59zM17 11c0-1.65-1.35-3-3-3s-3 1.35-3 3 1.35 3 3 3 3-1.35 3-3M2 19h10v-2H2z"></path>
|
|
148
|
+
</svg>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
export function FilterIcon() {
|
|
152
|
+
return (
|
|
153
|
+
<svg
|
|
154
|
+
className="text-red-300"
|
|
155
|
+
stroke="currentColor"
|
|
156
|
+
fill="currentColor"
|
|
157
|
+
strokeWidth="0"
|
|
158
|
+
viewBox="0 0 24 24"
|
|
159
|
+
height="1em"
|
|
160
|
+
width="1em"
|
|
161
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
162
|
+
>
|
|
163
|
+
<path fill="none" d="M0 0h24v24H0zM0 0h24m0 24H0"></path>
|
|
164
|
+
<path d="M4.25 5.66c.1.13 5.74 7.33 5.74 7.33V19c0 .55.45 1 1.01 1h2.01c.55 0 1.01-.45 1.01-1v-6.02s5.49-7.02 5.75-7.34S20 5 20 5c0-.55-.45-1-1.01-1H5.01C4.4 4 4 4.48 4 5c0 .2.06.44.25.66"></path>
|
|
165
|
+
</svg>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ReactNode, useEffect, useMemo, useReducer, useState } from "react";
|
|
2
|
+
import regularExpresions, { FiltersType, SortType, TableProps } from "./types";
|
|
3
|
+
import HTable from "./h.table";
|
|
4
|
+
import useContext from "./context";
|
|
5
|
+
|
|
6
|
+
export default function TableAdvanced(tableProps: TableProps) {
|
|
7
|
+
const { data, ...props } = tableProps;
|
|
8
|
+
|
|
9
|
+
const context = useContext({ onSelect: Boolean(tableProps.onSelect) });
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
context.setData(
|
|
13
|
+
Array.isArray(data)
|
|
14
|
+
? data.map((d) => {
|
|
15
|
+
return {
|
|
16
|
+
...(tableProps?.modal ? { __modal__: "" } : {}),
|
|
17
|
+
...(tableProps?.onSelect ? { __select__: "" } : {}),
|
|
18
|
+
...d,
|
|
19
|
+
};
|
|
20
|
+
})
|
|
21
|
+
: data,
|
|
22
|
+
);
|
|
23
|
+
}, [data]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (tableProps?.sortBy) {
|
|
27
|
+
const [k, v] = Object.entries(tableProps.sortBy)[0];
|
|
28
|
+
|
|
29
|
+
context.setSort({ [k]: v });
|
|
30
|
+
}
|
|
31
|
+
}, [tableProps?.sortBy]);
|
|
32
|
+
|
|
33
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
34
|
+
if (data.every((d) => !d?.id)) {
|
|
35
|
+
throw new Error("All data rows must have an 'id' property.");
|
|
36
|
+
}
|
|
37
|
+
if (props?.onSave && props.onSelect) {
|
|
38
|
+
throw new Error("Must have only one of onSave or onSelect");
|
|
39
|
+
}
|
|
40
|
+
return <HTable context={{ ...context, ...props }} />;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export function MenuItem({
|
|
4
|
+
label,
|
|
5
|
+
icon,
|
|
6
|
+
onClick,
|
|
7
|
+
}: {
|
|
8
|
+
label: string;
|
|
9
|
+
icon: ReactNode;
|
|
10
|
+
onClick: React.MouseEventHandler<HTMLDivElement>;
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
className="cursor-pointer border p-2 hover:bg-gray-100 flex items-center bg-white gap-2"
|
|
15
|
+
onClick={onClick}
|
|
16
|
+
>
|
|
17
|
+
{icon}
|
|
18
|
+
{label}
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TableProps } from "@mui/material";
|
|
2
|
+
import { ContextType } from "./context";
|
|
3
|
+
import { SearchIcon } from "./icons";
|
|
4
|
+
|
|
5
|
+
export default function Searchable({
|
|
6
|
+
context,
|
|
7
|
+
}: {
|
|
8
|
+
context: ContextType & Omit<TableProps, "data">;
|
|
9
|
+
}) {
|
|
10
|
+
return (
|
|
11
|
+
<div className="flex justify-start p-2 relative w-96">
|
|
12
|
+
<input
|
|
13
|
+
type="search"
|
|
14
|
+
placeholder="Buscar por ...."
|
|
15
|
+
className="border shadow rounded p-2 w-full pr-10"
|
|
16
|
+
value={context.searchBy}
|
|
17
|
+
onChange={(e) => context.setSearchBy(e.target.value)}
|
|
18
|
+
/>
|
|
19
|
+
<div className="absolute top-0 right-0 p-5">
|
|
20
|
+
<SearchIcon />
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SortType } from "./types";
|
|
2
|
+
|
|
3
|
+
export const sortReducer = (
|
|
4
|
+
acc: SortType[],
|
|
5
|
+
b: Record<string, "ASC" | "DESC" | "TOGGLE">,
|
|
6
|
+
) => {
|
|
7
|
+
const oldAcc = [...acc];
|
|
8
|
+
const newAcc = [];
|
|
9
|
+
const keys = Object.keys(b);
|
|
10
|
+
const k = keys[0];
|
|
11
|
+
|
|
12
|
+
let newType = b[k];
|
|
13
|
+
if (b[k] == "TOGGLE") {
|
|
14
|
+
newType = oldAcc?.[0]?.type == "ASC" ? "DESC" : "ASC";
|
|
15
|
+
}
|
|
16
|
+
newAcc.push({ field: k, type: newType });
|
|
17
|
+
|
|
18
|
+
// const newAcc = [...acc];
|
|
19
|
+
// const keys = Object.keys(b);
|
|
20
|
+
// for (let k of keys) {
|
|
21
|
+
// const index = newAcc.findIndex((aa) => aa.field == k);
|
|
22
|
+
// if (index >= 0) {
|
|
23
|
+
// newAcc.splice(index, 1);
|
|
24
|
+
// }
|
|
25
|
+
|
|
26
|
+
// newAcc.push({ field: k, type: b[k] });
|
|
27
|
+
// }
|
|
28
|
+
return newAcc;
|
|
29
|
+
};
|