next-recomponents 1.0.0
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 +122 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +50922 -0
- package/dist/index.mjs +50909 -0
- package/package.json +18 -0
- package/src/alert/index.tsx +24 -0
- package/src/button/colors.tsx +9 -0
- package/src/button/index.tsx +42 -0
- package/src/container/icons.tsx +65 -0
- package/src/container/index.tsx +175 -0
- package/src/form/index.tsx +95 -0
- package/src/index.tsx +10 -0
- package/src/input/index.tsx +43 -0
- package/src/regular-expresions/index.ts +11 -0
- package/src/select/close.tsx +15 -0
- package/src/select/icon.tsx +15 -0
- package/src/select/index.tsx +212 -0
- package/src/table/export.tsx +29 -0
- package/src/table/filter.menu.tsx +188 -0
- package/src/table/filters.tsx +77 -0
- package/src/table/h.tsx +185 -0
- package/src/table/index.tsx +55 -0
- package/src/table/td.tsx +75 -0
- package/src/text-area/index.tsx +52 -0
- package/src/use-resources/encode.decode.tsx +25 -0
- package/src/use-resources/functions.ts +0 -0
- package/src/use-resources/get.token.tsx +15 -0
- package/src/use-resources/http.codes.ts +77 -0
- package/src/use-resources/index.ts +189 -0
- package/src/use-resources/types.ts +41 -0
- package/tsconfig.json +15 -0
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "next-recomponents",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsup src/index.tsx --format cjs,esm --dts --clean",
|
|
8
|
+
"prepublishOnly": "npm run build",
|
|
9
|
+
"publish:public": "npm publish --access public"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [],
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"tsup": "^8.5.0",
|
|
16
|
+
"typescript": "^5.8.3"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { catColor } from "../button/colors";
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
color?:
|
|
5
|
+
| "white"
|
|
6
|
+
| "primary"
|
|
7
|
+
| "secondary"
|
|
8
|
+
| "info"
|
|
9
|
+
| "danger"
|
|
10
|
+
| "warning"
|
|
11
|
+
| "success";
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
export default function Alert({
|
|
15
|
+
color = "primary",
|
|
16
|
+
children,
|
|
17
|
+
...props
|
|
18
|
+
}: Props) {
|
|
19
|
+
return (
|
|
20
|
+
<div className={[catColor[color], "p-2 rounded shadow border"].join(" ")}>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const catColor = {
|
|
2
|
+
white: "bg-white text-black",
|
|
3
|
+
primary: "bg-blue-500 text-white",
|
|
4
|
+
secondary: "bg-blue-800 text-white",
|
|
5
|
+
info: "bg-blue-200 text-black",
|
|
6
|
+
danger: "bg-red-500 text-white",
|
|
7
|
+
warning: "bg-yellow-500 text-white",
|
|
8
|
+
success: "bg-green-500 text-white",
|
|
9
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ButtonHTMLAttributes, DetailedHTMLProps } from "react";
|
|
2
|
+
import { catColor } from "./colors";
|
|
3
|
+
|
|
4
|
+
interface Props
|
|
5
|
+
extends DetailedHTMLProps<
|
|
6
|
+
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
7
|
+
HTMLButtonElement
|
|
8
|
+
> {
|
|
9
|
+
icon?: React.ReactNode;
|
|
10
|
+
size?: "full" | "small";
|
|
11
|
+
color?:
|
|
12
|
+
| "white"
|
|
13
|
+
| "primary"
|
|
14
|
+
| "secondary"
|
|
15
|
+
| "info"
|
|
16
|
+
| "danger"
|
|
17
|
+
| "warning"
|
|
18
|
+
| "success";
|
|
19
|
+
}
|
|
20
|
+
export default function Button({
|
|
21
|
+
className,
|
|
22
|
+
size = "small",
|
|
23
|
+
color = "primary",
|
|
24
|
+
children,
|
|
25
|
+
icon,
|
|
26
|
+
...props
|
|
27
|
+
}: Props) {
|
|
28
|
+
return (
|
|
29
|
+
<button
|
|
30
|
+
{...props}
|
|
31
|
+
className={[
|
|
32
|
+
className,
|
|
33
|
+
"p-2 border shadow rounded flex gap-1 justify-center items-center",
|
|
34
|
+
size == "full" ? "w-full" : "",
|
|
35
|
+
catColor[color],
|
|
36
|
+
].join(" ")}
|
|
37
|
+
>
|
|
38
|
+
{icon}
|
|
39
|
+
{children}
|
|
40
|
+
</button>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export default function MenuIcon() {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
stroke="currentColor"
|
|
5
|
+
fill="currentColor"
|
|
6
|
+
strokeWidth="0"
|
|
7
|
+
viewBox="0 0 512 512"
|
|
8
|
+
height="20px"
|
|
9
|
+
width="20px"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
>
|
|
12
|
+
<path d="M32 96v64h448V96H32zm0 128v64h448v-64H32zm0 128v64h448v-64H32z"></path>
|
|
13
|
+
</svg>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
export function HomeIcon() {
|
|
17
|
+
return (
|
|
18
|
+
<svg
|
|
19
|
+
stroke="currentColor"
|
|
20
|
+
fill="currentColor"
|
|
21
|
+
strokeWidth="0"
|
|
22
|
+
viewBox="0 0 576 512"
|
|
23
|
+
height="20px"
|
|
24
|
+
width="20px"
|
|
25
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
26
|
+
>
|
|
27
|
+
<path d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"></path>
|
|
28
|
+
</svg>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
export function ArrowUpIcon() {
|
|
32
|
+
return (
|
|
33
|
+
<svg
|
|
34
|
+
stroke="currentColor"
|
|
35
|
+
fill="currentColor"
|
|
36
|
+
strokeWidth="0"
|
|
37
|
+
version="1.2"
|
|
38
|
+
baseProfile="tiny"
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
height="20px"
|
|
41
|
+
width="20px"
|
|
42
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
43
|
+
>
|
|
44
|
+
<path d="M18.2 13.3l-6.2-6.3-6.2 6.3c-.2.2-.3.5-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7z"></path>
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function ArrowDownIcon() {
|
|
50
|
+
return (
|
|
51
|
+
<svg
|
|
52
|
+
stroke="currentColor"
|
|
53
|
+
fill="currentColor"
|
|
54
|
+
strokeWidth="0"
|
|
55
|
+
version="1.2"
|
|
56
|
+
baseProfile="tiny"
|
|
57
|
+
viewBox="0 0 24 24"
|
|
58
|
+
height="20px"
|
|
59
|
+
width="20px"
|
|
60
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
61
|
+
>
|
|
62
|
+
<path d="M5.8 9.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.4-.3.7s.1.5.3.7z"></path>
|
|
63
|
+
</svg>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState } from "react";
|
|
4
|
+
import { motion } from "framer-motion";
|
|
5
|
+
import MenuIcon, { ArrowDownIcon, ArrowUpIcon, HomeIcon } from "./icons";
|
|
6
|
+
import Link from "next/link";
|
|
7
|
+
type LocationItem = {
|
|
8
|
+
location: string;
|
|
9
|
+
name: React.ReactNode;
|
|
10
|
+
icon?: React.ReactNode;
|
|
11
|
+
};
|
|
12
|
+
export default function Container({
|
|
13
|
+
children,
|
|
14
|
+
appName,
|
|
15
|
+
menuList,
|
|
16
|
+
navItems,
|
|
17
|
+
leftPanel,
|
|
18
|
+
footPanel,
|
|
19
|
+
expandedFooter = false,
|
|
20
|
+
expandedMenu = false,
|
|
21
|
+
}: {
|
|
22
|
+
appName?: React.ReactNode;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
menuList?: Array<LocationItem>;
|
|
25
|
+
navItems?: Array<LocationItem>;
|
|
26
|
+
leftPanel?: React.ReactNode;
|
|
27
|
+
footPanel?: React.ReactNode;
|
|
28
|
+
expandedMenu?: boolean;
|
|
29
|
+
expandedFooter?: boolean;
|
|
30
|
+
}) {
|
|
31
|
+
const [isSidebarOpen, setIsSidebarOpen] = useState(expandedMenu);
|
|
32
|
+
const [isFooterOpen, setIsFooterOpen] = useState(expandedFooter);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="flex flex-col h-screen">
|
|
36
|
+
{/* Header */}
|
|
37
|
+
<header className="z-40">
|
|
38
|
+
<div className="bg-blue-600 text-white p-4 flex justify-between items-center shadow-md">
|
|
39
|
+
<button
|
|
40
|
+
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
|
|
41
|
+
className="bg-blue-600 text-white px-2 py-1 rounded-r"
|
|
42
|
+
>
|
|
43
|
+
<MenuIcon />
|
|
44
|
+
</button>
|
|
45
|
+
<h1 className="text-xl font-bold">{appName}</h1>
|
|
46
|
+
</div>
|
|
47
|
+
<div
|
|
48
|
+
className={` gap-2 bg-gray-800 text-white ${
|
|
49
|
+
isSidebarOpen ? "px-[270px]" : "px-[60px]"
|
|
50
|
+
} hidden sm:flex`}
|
|
51
|
+
>
|
|
52
|
+
{[{ location: "/", name: "Home", icon: <HomeIcon /> }, navItems]
|
|
53
|
+
.flat()
|
|
54
|
+
.map((li, k) => {
|
|
55
|
+
return (
|
|
56
|
+
li && (
|
|
57
|
+
<>
|
|
58
|
+
{"/ "}
|
|
59
|
+
<Link href={li.location} key={k} className="flex gap-1 p-1">
|
|
60
|
+
{li.icon}
|
|
61
|
+
{li.name}
|
|
62
|
+
</Link>
|
|
63
|
+
</>
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
})}
|
|
67
|
+
</div>
|
|
68
|
+
</header>
|
|
69
|
+
|
|
70
|
+
<div className="flex flex-1 overflow-hidden relative">
|
|
71
|
+
{/* Sidebar como drawer en mobile */}
|
|
72
|
+
<motion.aside
|
|
73
|
+
animate={{
|
|
74
|
+
width:
|
|
75
|
+
typeof window !== "undefined" && window.innerWidth < 768
|
|
76
|
+
? isSidebarOpen
|
|
77
|
+
? "100%"
|
|
78
|
+
: 0
|
|
79
|
+
: isSidebarOpen
|
|
80
|
+
? 250
|
|
81
|
+
: 60,
|
|
82
|
+
}}
|
|
83
|
+
className="bg-gray-800 text-white overflow-y-auto fixed md:static top-0 left-0 h-full z-50 md:z-auto transition-all duration-300 ease-in-out"
|
|
84
|
+
>
|
|
85
|
+
<div className="p-4 ">
|
|
86
|
+
{menuList && (
|
|
87
|
+
<ul className="space-y-3">
|
|
88
|
+
<li
|
|
89
|
+
key={"menu"}
|
|
90
|
+
className={"p-2 md:hidden "}
|
|
91
|
+
onClick={(e) => {
|
|
92
|
+
setIsSidebarOpen(false);
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
<MenuIcon />
|
|
96
|
+
</li>
|
|
97
|
+
{menuList.map((itemMenu, k) => {
|
|
98
|
+
const letra = `${itemMenu?.name}`.split("")[0];
|
|
99
|
+
return (
|
|
100
|
+
<li key={k}>
|
|
101
|
+
<Link
|
|
102
|
+
href={itemMenu.location}
|
|
103
|
+
onClick={() => setIsSidebarOpen(false)}
|
|
104
|
+
>
|
|
105
|
+
{isSidebarOpen && (
|
|
106
|
+
<div className="flex gap-2 items-center hover:bg-gray-700 p-2 rounded">
|
|
107
|
+
{itemMenu.icon}
|
|
108
|
+
<div>{itemMenu.name}</div>
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
{!isSidebarOpen && (
|
|
112
|
+
<div className="hover:bg-gray-200 hover:text-black rounded p-1">
|
|
113
|
+
{itemMenu?.icon || (
|
|
114
|
+
<span className="px-1 border shadow rounded">
|
|
115
|
+
{letra}
|
|
116
|
+
</span>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
)}
|
|
120
|
+
</Link>
|
|
121
|
+
</li>
|
|
122
|
+
);
|
|
123
|
+
})}
|
|
124
|
+
</ul>
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
127
|
+
</motion.aside>
|
|
128
|
+
|
|
129
|
+
{/* Overlay para cerrar en móviles */}
|
|
130
|
+
{isSidebarOpen &&
|
|
131
|
+
typeof window !== "undefined" &&
|
|
132
|
+
window.innerWidth < 768 && (
|
|
133
|
+
<div
|
|
134
|
+
className="fixed inset-0 bg-black bg-opacity-40 z-40"
|
|
135
|
+
onClick={() => setIsSidebarOpen(false)}
|
|
136
|
+
/>
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
{/* Main */}
|
|
140
|
+
<main className="flex-1 overflow-auto p-4 bg-gray-100 z-10 md:ml-0 ">
|
|
141
|
+
<div className="flex md:flex-row flex-col h-full gap-4 ">
|
|
142
|
+
{leftPanel && (
|
|
143
|
+
<aside className="w-full md:w-64 flex-shrink-0 text-xs text-gray-600 bg-gray-200 p-5 border rounded">
|
|
144
|
+
{leftPanel}
|
|
145
|
+
</aside>
|
|
146
|
+
)}
|
|
147
|
+
<section className="flex-1">{children}</section>
|
|
148
|
+
</div>
|
|
149
|
+
</main>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
{/* Footer */}
|
|
153
|
+
{footPanel && (
|
|
154
|
+
<motion.footer
|
|
155
|
+
className="bg-blue-100 overflow-hidden"
|
|
156
|
+
initial={isFooterOpen}
|
|
157
|
+
animate={{ height: isFooterOpen ? 120 : 40 }}
|
|
158
|
+
transition={{ duration: 0.3 }}
|
|
159
|
+
>
|
|
160
|
+
<div className="flex justify-center items-center p-2">
|
|
161
|
+
<button
|
|
162
|
+
onClick={() => setIsFooterOpen(!isFooterOpen)}
|
|
163
|
+
className="text-blue-700"
|
|
164
|
+
>
|
|
165
|
+
{isFooterOpen ? <ArrowDownIcon /> : <ArrowUpIcon />}
|
|
166
|
+
</button>
|
|
167
|
+
</div>
|
|
168
|
+
{isFooterOpen && (
|
|
169
|
+
<div className="px-4 pb-4 text-sm text-gray-600">{footPanel}</div>
|
|
170
|
+
)}
|
|
171
|
+
</motion.footer>
|
|
172
|
+
)}
|
|
173
|
+
</div>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
Dispatch,
|
|
3
|
+
Reducer,
|
|
4
|
+
SetStateAction,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useReducer,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from "react";
|
|
11
|
+
interface OnSubmitProps extends React.FormEvent<HTMLFormElement> {
|
|
12
|
+
values: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
interface Props {
|
|
15
|
+
onSubmit: (e: OnSubmitProps) => void;
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
state?: [any, Dispatch<SetStateAction<any>>];
|
|
18
|
+
invalidMessage?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useFormValues<T>(initial: Partial<T>) {
|
|
22
|
+
function reducer(st: Partial<T>, action: Partial<T>) {
|
|
23
|
+
const newSt = { ...st, ...action };
|
|
24
|
+
return newSt;
|
|
25
|
+
}
|
|
26
|
+
const state = useReducer<Reducer<Partial<T>, Partial<T>>>(
|
|
27
|
+
reducer,
|
|
28
|
+
initial as Partial<T>
|
|
29
|
+
);
|
|
30
|
+
return state;
|
|
31
|
+
}
|
|
32
|
+
export default function Form({
|
|
33
|
+
onSubmit,
|
|
34
|
+
state,
|
|
35
|
+
invalidMessage = "Existen valores inválidos en el formulario",
|
|
36
|
+
children,
|
|
37
|
+
}: Props) {
|
|
38
|
+
const ref = useRef<HTMLFormElement>(null);
|
|
39
|
+
const [hvalues, setHValues] = state || useState({});
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (ref?.current) {
|
|
42
|
+
const newValues = { ...hvalues };
|
|
43
|
+
const formData = new FormData(ref.current);
|
|
44
|
+
for (let [k, v] of formData.entries()) {
|
|
45
|
+
if (!newValues[k]) {
|
|
46
|
+
newValues[k] = "";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (Object.keys(hvalues).length != Object.keys(newValues).length) {
|
|
50
|
+
setHValues(newValues);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}, [state, ref]);
|
|
54
|
+
|
|
55
|
+
function hasErrors() {
|
|
56
|
+
const hasInvalids = ref?.current?.querySelectorAll(".invalid");
|
|
57
|
+
if (hasInvalids && hasInvalids?.length > 0) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<form
|
|
65
|
+
ref={ref}
|
|
66
|
+
onSubmit={(e) => {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
if (hasErrors()) {
|
|
69
|
+
alert(invalidMessage);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
onSubmit && onSubmit({ ...e, values: hvalues });
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
{React.Children.map(children, (child: any) => {
|
|
76
|
+
if (React.isValidElement(child)) {
|
|
77
|
+
const props: any = child?.props ? { ...child.props } : {};
|
|
78
|
+
try {
|
|
79
|
+
return React.cloneElement(child as any, {
|
|
80
|
+
value: hvalues[props.name] || "",
|
|
81
|
+
onChange: (e: any) => {
|
|
82
|
+
setHValues({ ...hvalues, [props.name]: e.target.value });
|
|
83
|
+
props?.onChange && props.onChange(e);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return child;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return child; // no modificar si no es input válido
|
|
92
|
+
})}
|
|
93
|
+
</form>
|
|
94
|
+
);
|
|
95
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { default as Alert } from "./alert";
|
|
2
|
+
export { default as Button } from "./button";
|
|
3
|
+
export { default as Container } from "./container";
|
|
4
|
+
export { default as Form } from "./form";
|
|
5
|
+
export { default as Input } from "./input";
|
|
6
|
+
export { default as regularExpresions } from "./regular-expresions";
|
|
7
|
+
export { default as Table } from "./table";
|
|
8
|
+
export { default as TextArea } from "./text-area";
|
|
9
|
+
export { default as useResources } from "./use-resources";
|
|
10
|
+
export { default as Select } from "./select";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DetailedHTMLProps, InputHTMLAttributes } from "react";
|
|
2
|
+
|
|
3
|
+
interface InputProps
|
|
4
|
+
extends DetailedHTMLProps<
|
|
5
|
+
InputHTMLAttributes<HTMLInputElement>,
|
|
6
|
+
HTMLInputElement
|
|
7
|
+
> {
|
|
8
|
+
label: React.ReactNode;
|
|
9
|
+
regex?: RegExp;
|
|
10
|
+
invalidMessage?: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
export default function Input({
|
|
13
|
+
label,
|
|
14
|
+
className,
|
|
15
|
+
regex,
|
|
16
|
+
invalidMessage = "Valor no válido",
|
|
17
|
+
...props
|
|
18
|
+
}: InputProps) {
|
|
19
|
+
const value = `${props?.value || ""}`;
|
|
20
|
+
const isValid = !regex ? true : regex.test(value);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="w-full">
|
|
24
|
+
<label className="flex flex-col gap-1">
|
|
25
|
+
<div className="font-bold ">{label}</div>
|
|
26
|
+
<div>
|
|
27
|
+
<input
|
|
28
|
+
{...props}
|
|
29
|
+
className={[
|
|
30
|
+
"p-2 w-full rounded border shadow",
|
|
31
|
+
value != "" && !isValid && "bg-red-200 text-black",
|
|
32
|
+
value != "" && isValid && "bg-green-200 text-black",
|
|
33
|
+
className,
|
|
34
|
+
].join(" ")}
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
</label>
|
|
38
|
+
{!isValid && value != "" && (
|
|
39
|
+
<div className="text-red-800 invalid">{invalidMessage}</div>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function CloseIcon() {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
stroke="currentColor"
|
|
5
|
+
fill="currentColor"
|
|
6
|
+
strokeWidth="0"
|
|
7
|
+
viewBox="0 0 512 512"
|
|
8
|
+
height="20px"
|
|
9
|
+
width="20px"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
>
|
|
12
|
+
<path d="M256 48C141.31 48 48 141.31 48 256s93.31 208 208 208 208-93.31 208-208S370.69 48 256 48zm86.63 272L320 342.63l-64-64-64 64L169.37 320l64-64-64-64L192 169.37l64 64 64-64L342.63 192l-64 64z"></path>
|
|
13
|
+
</svg>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function SelectIcon() {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
stroke="currentColor"
|
|
5
|
+
fill="currentColor"
|
|
6
|
+
strokeWidth="0"
|
|
7
|
+
viewBox="0 0 320 512"
|
|
8
|
+
height="20px"
|
|
9
|
+
width="20px"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
>
|
|
12
|
+
<path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"></path>
|
|
13
|
+
</svg>
|
|
14
|
+
);
|
|
15
|
+
}
|