@processhub-lib/react 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/README.md +6 -0
- package/dist/context/ThemeContext.d.ts +22 -0
- package/dist/index-B6iFW4Kz.js +1171 -0
- package/dist/index-fvSAulfO.cjs +30 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.es.js +10 -0
- package/dist/style.css +1 -0
- package/dist/ui/Button/colors.d.ts +162 -0
- package/dist/ui/Button/index.d.ts +3 -0
- package/dist/ui/Button/types/button.d.ts +16 -0
- package/dist/ui/Input/index.d.ts +3 -0
- package/dist/ui/Input/types/input.d.ts +10 -0
- package/dist/ui/Select/index.d.ts +3 -0
- package/dist/ui/Select/types/select.d.ts +16 -0
- package/dist/ui/index.d.ts +3 -0
- package/dist/ui.cjs.js +1 -0
- package/dist/ui.d.ts +1 -0
- package/dist/ui.es.js +6 -0
- package/dist/utils/classnames.d.ts +16 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils.cjs.js +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.es.js +29 -0
- package/package.json +56 -0
- package/postcss.config.js +5 -0
- package/src/context/ThemeContext.tsx +45 -0
- package/src/index.ts +4 -0
- package/src/style.css +1 -0
- package/src/ui/Button/colors.tsx +163 -0
- package/src/ui/Button/index.tsx +46 -0
- package/src/ui/Button/types/button.ts +17 -0
- package/src/ui/Input/index.tsx +74 -0
- package/src/ui/Input/types/input.ts +11 -0
- package/src/ui/Select/index.tsx +149 -0
- package/src/ui/Select/types/select.ts +16 -0
- package/src/ui/index.ts +3 -0
- package/src/utils/classnames.tsx +89 -0
- package/src/utils/index.ts +1 -0
- package/tailwind.config.js +13 -0
- package/tsconfig.json +26 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +31 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
export const COLORS = {
|
|
2
|
+
green: {
|
|
3
|
+
solid: {
|
|
4
|
+
bg: "bg-green-500",
|
|
5
|
+
hover: "hover:bg-green-600 hover:text-white",
|
|
6
|
+
text: "text-white",
|
|
7
|
+
border: "border-green-500",
|
|
8
|
+
},
|
|
9
|
+
outline: {
|
|
10
|
+
bg: "bg-transparent",
|
|
11
|
+
hover: "hover:bg-green-600 hover:text-white",
|
|
12
|
+
text: "text-green-600",
|
|
13
|
+
border: "border-green-500",
|
|
14
|
+
},
|
|
15
|
+
ghost: {
|
|
16
|
+
bg: "bg-transparent",
|
|
17
|
+
hover: "hover:bg-green-600 hover:text-white",
|
|
18
|
+
text: "text-green-600",
|
|
19
|
+
border: "border-transparent",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
blue: {
|
|
23
|
+
solid: {
|
|
24
|
+
bg: "bg-blue-500",
|
|
25
|
+
hover: "hover:bg-blue-600 hover:text-white",
|
|
26
|
+
text: "text-white",
|
|
27
|
+
border: "border-blue-500",
|
|
28
|
+
},
|
|
29
|
+
outline: {
|
|
30
|
+
bg: "bg-transparent",
|
|
31
|
+
hover: "hover:bg-blue-500 hover:text-white",
|
|
32
|
+
text: "text-blue-600",
|
|
33
|
+
border: "border-blue-500",
|
|
34
|
+
},
|
|
35
|
+
ghost: {
|
|
36
|
+
bg: "bg-transparent",
|
|
37
|
+
hover: "hover:bg-blue-500 hover:text-white",
|
|
38
|
+
text: "text-blue-600",
|
|
39
|
+
border: "border-transparent",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
red: {
|
|
43
|
+
solid: {
|
|
44
|
+
bg: "bg-red-500",
|
|
45
|
+
hover: "hover:bg-red-600 hover:text-white",
|
|
46
|
+
text: "text-white",
|
|
47
|
+
border: "border-red-500",
|
|
48
|
+
},
|
|
49
|
+
outline: {
|
|
50
|
+
bg: "bg-transparent",
|
|
51
|
+
hover: "hover:bg-red-500 hover:text-white",
|
|
52
|
+
text: "text-red-600",
|
|
53
|
+
border: "border-red-500",
|
|
54
|
+
},
|
|
55
|
+
ghost: {
|
|
56
|
+
bg: "bg-transparent",
|
|
57
|
+
hover: "hover:bg-red-500 hover:text-white",
|
|
58
|
+
text: "text-red-600",
|
|
59
|
+
border: "border-transparent",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
yellow: {
|
|
63
|
+
solid: {
|
|
64
|
+
bg: "bg-yellow-500",
|
|
65
|
+
hover: "hover:bg-yellow-600 hover:text-black",
|
|
66
|
+
text: "text-black",
|
|
67
|
+
border: "border-yellow-500",
|
|
68
|
+
},
|
|
69
|
+
outline: {
|
|
70
|
+
bg: "bg-transparent",
|
|
71
|
+
hover: "hover:bg-yellow-500 hover:text-white",
|
|
72
|
+
text: "text-yellow-600",
|
|
73
|
+
border: "border-yellow-500",
|
|
74
|
+
},
|
|
75
|
+
ghost: {
|
|
76
|
+
bg: "bg-transparent",
|
|
77
|
+
hover: "hover:bg-yellow-500 hover:text-white",
|
|
78
|
+
text: "text-yellow-600",
|
|
79
|
+
border: "border-transparent",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
primary: {
|
|
84
|
+
solid: {
|
|
85
|
+
bg: "bg-primary",
|
|
86
|
+
hover: "hover:bg-primary-hover hover:text-foreground",
|
|
87
|
+
text: "text-foreground",
|
|
88
|
+
border: "border-primary",
|
|
89
|
+
},
|
|
90
|
+
outline: {
|
|
91
|
+
bg: "bg-transparent",
|
|
92
|
+
hover: "hover:bg-primary hover:text-foreground",
|
|
93
|
+
text: "text-primary",
|
|
94
|
+
border: "border-primary",
|
|
95
|
+
},
|
|
96
|
+
ghost: {
|
|
97
|
+
bg: "bg-transparent",
|
|
98
|
+
hover: "hover:bg-primary hover:text-foreground",
|
|
99
|
+
text: "text-primary",
|
|
100
|
+
border: "border-transparent",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
secondary: {
|
|
104
|
+
solid: {
|
|
105
|
+
bg: "bg-secondary",
|
|
106
|
+
hover: "hover:bg-secondary/80 hover:text-secondary-foreground",
|
|
107
|
+
text: "text-secondary-foreground",
|
|
108
|
+
border: "border-secondary",
|
|
109
|
+
},
|
|
110
|
+
outline: {
|
|
111
|
+
bg: "bg-transparent",
|
|
112
|
+
hover: "hover:bg-secondary hover:text-secondary-foreground",
|
|
113
|
+
text: "text-secondary",
|
|
114
|
+
border: "border-secondary",
|
|
115
|
+
},
|
|
116
|
+
ghost: {
|
|
117
|
+
bg: "bg-transparent",
|
|
118
|
+
hover: "hover:bg-secondary hover:text-secondary-foreground",
|
|
119
|
+
text: "text-secondary",
|
|
120
|
+
border: "border-transparent",
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
destructive: {
|
|
124
|
+
solid: {
|
|
125
|
+
bg: "bg-destructive",
|
|
126
|
+
hover: "hover:bg-destructive/90 hover:text-destructive-foreground",
|
|
127
|
+
text: "text-destructive-foreground",
|
|
128
|
+
border: "border-destructive",
|
|
129
|
+
},
|
|
130
|
+
outline: {
|
|
131
|
+
bg: "bg-transparent",
|
|
132
|
+
hover: "hover:bg-destructive hover:text-destructive-foreground",
|
|
133
|
+
text: "text-destructive",
|
|
134
|
+
border: "border-destructive",
|
|
135
|
+
},
|
|
136
|
+
ghost: {
|
|
137
|
+
bg: "bg-transparent",
|
|
138
|
+
hover: "hover:bg-destructive hover:text-destructive-foreground",
|
|
139
|
+
text: "text-destructive",
|
|
140
|
+
border: "border-transparent",
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
card: {
|
|
144
|
+
solid: {
|
|
145
|
+
bg: "bg-card",
|
|
146
|
+
hover: "hover:bg-accent hover:text-card-foreground",
|
|
147
|
+
text: "text-card-foreground",
|
|
148
|
+
border: "border-border",
|
|
149
|
+
},
|
|
150
|
+
outline: {
|
|
151
|
+
bg: "bg-transparent",
|
|
152
|
+
hover: "hover:bg-accent hover:text-card-foreground",
|
|
153
|
+
text: "text-card-foreground",
|
|
154
|
+
border: "border-border",
|
|
155
|
+
},
|
|
156
|
+
ghost: {
|
|
157
|
+
bg: "bg-transparent",
|
|
158
|
+
hover: "hover:bg-accent hover:text-card-foreground",
|
|
159
|
+
text: "text-card-foreground",
|
|
160
|
+
border: "border-transparent",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export * from "./types/button";
|
|
2
|
+
import { ButtonProps } from "./types/button";
|
|
3
|
+
import { useTheme } from "../../context/ThemeContext";
|
|
4
|
+
import { classnames } from "../../utils";
|
|
5
|
+
|
|
6
|
+
export function Button({
|
|
7
|
+
title,
|
|
8
|
+
icon,
|
|
9
|
+
positionIcon,
|
|
10
|
+
color = "green",
|
|
11
|
+
variant = "solid",
|
|
12
|
+
disable = false,
|
|
13
|
+
className,
|
|
14
|
+
type = "button",
|
|
15
|
+
onClick = () => {},
|
|
16
|
+
}: ButtonProps) {
|
|
17
|
+
const theme = useTheme();
|
|
18
|
+
const colorSet = theme[color]?.[variant] || theme.green.solid;
|
|
19
|
+
|
|
20
|
+
const classes = classnames(
|
|
21
|
+
// "cursor-pointer flex items-center justify-center gap-2 text-md font-bold rounded-lg p-2.5 w-auto border transition-colors duration-150",
|
|
22
|
+
"cursor-pointer flex items-center justify-center gap-2 px-4 py-2 rounded-lg transition-colors shadow-sm font-medium text-sm border",
|
|
23
|
+
colorSet.bg,
|
|
24
|
+
colorSet.hover,
|
|
25
|
+
colorSet.text,
|
|
26
|
+
colorSet.border,
|
|
27
|
+
{
|
|
28
|
+
"opacity-50 cursor-not-allowed": disable,
|
|
29
|
+
},
|
|
30
|
+
className
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<button
|
|
35
|
+
type={type}
|
|
36
|
+
onClick={(e) => !disable && onClick?.(e)}
|
|
37
|
+
disabled={disable}
|
|
38
|
+
className={classes}
|
|
39
|
+
>
|
|
40
|
+
{positionIcon === "left" && icon}
|
|
41
|
+
testedsad
|
|
42
|
+
{title && <span className="first-letter:uppercase">{title}</span>}
|
|
43
|
+
{positionIcon === "right" && icon}
|
|
44
|
+
</button>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { COLORS } from "../colors";
|
|
2
|
+
|
|
3
|
+
export interface ButtonProps {
|
|
4
|
+
onClick?: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
5
|
+
title: string;
|
|
6
|
+
icon?: JSX.Element;
|
|
7
|
+
positionIcon?: "left" | "right";
|
|
8
|
+
disable?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
type?: "button" | "submit" | "reset";
|
|
11
|
+
color: Color;
|
|
12
|
+
variant?: "solid" | "outline" | "ghost";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface CustomColors {}
|
|
16
|
+
|
|
17
|
+
export type Color = keyof typeof COLORS | keyof CustomColors | (string & {});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { classnames } from "../../utils";
|
|
2
|
+
import { InputProps } from "./types/input";
|
|
3
|
+
|
|
4
|
+
export function Input<T>({
|
|
5
|
+
field,
|
|
6
|
+
title,
|
|
7
|
+
id,
|
|
8
|
+
type,
|
|
9
|
+
placeholder,
|
|
10
|
+
className,
|
|
11
|
+
value,
|
|
12
|
+
iconPosition,
|
|
13
|
+
icon,
|
|
14
|
+
error,
|
|
15
|
+
...rest
|
|
16
|
+
}: InputProps<T>) {
|
|
17
|
+
return (
|
|
18
|
+
<div className="flex flex-col gap-2">
|
|
19
|
+
{title && (
|
|
20
|
+
<label
|
|
21
|
+
htmlFor={id}
|
|
22
|
+
className={classnames("block text-md font-medium text-foreground", {
|
|
23
|
+
"text-red-700": error,
|
|
24
|
+
})}
|
|
25
|
+
>
|
|
26
|
+
{rest.required && <span className="text-red-500">* </span>}
|
|
27
|
+
{title}:
|
|
28
|
+
</label>
|
|
29
|
+
)}
|
|
30
|
+
<div
|
|
31
|
+
className={classnames(
|
|
32
|
+
"flex items-center justify-between border border-border text-foreground text-sm rounded-lg w-full",
|
|
33
|
+
{
|
|
34
|
+
"text-red-500 placeholder-red-500 border-red-500 focus:ring-red-500 focus:border-red-500":
|
|
35
|
+
error,
|
|
36
|
+
"p-2.5": icon,
|
|
37
|
+
"bg-input cursor-pointer": !rest.disabled,
|
|
38
|
+
"bg-gray-400/40 cursor-not-allowed": rest.disabled,
|
|
39
|
+
}
|
|
40
|
+
)}
|
|
41
|
+
>
|
|
42
|
+
{iconPosition === "left" && icon}
|
|
43
|
+
<input
|
|
44
|
+
type={type}
|
|
45
|
+
id={id}
|
|
46
|
+
name={String(field)}
|
|
47
|
+
value={value}
|
|
48
|
+
placeholder={placeholder}
|
|
49
|
+
className={classnames(
|
|
50
|
+
"bg-transparent border-none text-foreground text-sm focus:outline-none w-full",
|
|
51
|
+
{
|
|
52
|
+
"ml-3": iconPosition === "left",
|
|
53
|
+
"p-2.5": !icon,
|
|
54
|
+
"cursor-pointer": !rest.disabled,
|
|
55
|
+
"cursor-not-allowed": rest.disabled,
|
|
56
|
+
className: className,
|
|
57
|
+
}
|
|
58
|
+
)}
|
|
59
|
+
// className={`bg-transparent border-none text-foreground text-sm focus:outline-none w-full p-2.5 cursor-not-allowed ${
|
|
60
|
+
// iconPosition === "left" && "ml-3"
|
|
61
|
+
// } ${className}`}
|
|
62
|
+
onChange={rest.onChange}
|
|
63
|
+
{...rest}
|
|
64
|
+
/>
|
|
65
|
+
{iconPosition === "right" && icon}
|
|
66
|
+
</div>
|
|
67
|
+
{error && (
|
|
68
|
+
<div className="mt-2">
|
|
69
|
+
<p className="text-sm text-red-600 dark:text-red-500">{error}</p>
|
|
70
|
+
</div>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type inputElementsType<T> = {
|
|
2
|
+
mask?: string;
|
|
3
|
+
field: keyof T;
|
|
4
|
+
title: string;
|
|
5
|
+
iconPosition?: "left" | "right";
|
|
6
|
+
icon?: React.JSX.Element;
|
|
7
|
+
} & React.InputHTMLAttributes<HTMLInputElement | HTMLSelectElement>;
|
|
8
|
+
|
|
9
|
+
export type InputProps<T> = {
|
|
10
|
+
error?: string;
|
|
11
|
+
} & inputElementsType<T>;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { SelectProps } from "./types/select";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { classnames } from "../../utils";
|
|
4
|
+
import { Input } from "../Input";
|
|
5
|
+
import { Button } from "../Button";
|
|
6
|
+
import { v4 } from "uuid";
|
|
7
|
+
import { BroomIcon, MagnifyingGlassIcon } from "@phosphor-icons/react";
|
|
8
|
+
|
|
9
|
+
function getNested(obj: any, path: string) {
|
|
10
|
+
return path.split(".").reduce((o, key) => (o ? o[key] : undefined), obj);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function Select<T>({
|
|
14
|
+
field,
|
|
15
|
+
title,
|
|
16
|
+
id,
|
|
17
|
+
placeholder,
|
|
18
|
+
value,
|
|
19
|
+
options,
|
|
20
|
+
icon,
|
|
21
|
+
iconPosition = "right",
|
|
22
|
+
error,
|
|
23
|
+
required,
|
|
24
|
+
onChange,
|
|
25
|
+
selected,
|
|
26
|
+
isClearable,
|
|
27
|
+
searchable = false,
|
|
28
|
+
}: SelectProps<T>) {
|
|
29
|
+
const [search, setSearch] = useState<string>("");
|
|
30
|
+
const [open, setOpen] = useState<boolean>(false);
|
|
31
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
32
|
+
const [label, setLabel] = useState<string>("");
|
|
33
|
+
|
|
34
|
+
// Fecha o dropdown ao clicar fora
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
function handleClickOutside(event: MouseEvent) {
|
|
37
|
+
if (
|
|
38
|
+
containerRef.current &&
|
|
39
|
+
!containerRef.current.contains(event.target as Node)
|
|
40
|
+
) {
|
|
41
|
+
setOpen(false);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
45
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
const handleSelect = (val: T) => {
|
|
49
|
+
onChange?.(val);
|
|
50
|
+
setOpen(false);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
setLabel(value);
|
|
55
|
+
}, [value]);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className="flex flex-col gap-2 w-full relative" ref={containerRef}>
|
|
59
|
+
{title && (
|
|
60
|
+
<label
|
|
61
|
+
htmlFor={id}
|
|
62
|
+
className={classnames("block text-md font-medium text-foreground", {
|
|
63
|
+
"text-red-700": error,
|
|
64
|
+
})}
|
|
65
|
+
>
|
|
66
|
+
{required && <span className="text-red-500">*</span>}{" "}
|
|
67
|
+
{title ? `${title}:` : ""}
|
|
68
|
+
</label>
|
|
69
|
+
)}
|
|
70
|
+
|
|
71
|
+
<div
|
|
72
|
+
className={classnames(
|
|
73
|
+
"cursor-pointer flex items-center justify-between bg-input border border-border text-foreground text-sm rounded-lg w-full p-2.5 pr-8",
|
|
74
|
+
{ "border-red-500": error }
|
|
75
|
+
)}
|
|
76
|
+
onClick={() => setOpen(!open)}
|
|
77
|
+
>
|
|
78
|
+
{iconPosition === "left" && icon}
|
|
79
|
+
<span className={`flex-1 ${iconPosition === "left" && "ml-3"}`}>
|
|
80
|
+
{label ? label : placeholder || "Selecione..."}
|
|
81
|
+
</span>
|
|
82
|
+
{iconPosition === "right" && icon}
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{open && (
|
|
86
|
+
<ul className="absolute top-full left-0 w-[600px] max-h-72 overflow-y-auto border border-border rounded-lg bg-card z-10 shadow-lg mt-1">
|
|
87
|
+
<li
|
|
88
|
+
key={v4()}
|
|
89
|
+
className="p-3 cursor-pointer text-foreground"
|
|
90
|
+
onClick={() => isClearable && handleSelect({} as T)}
|
|
91
|
+
>
|
|
92
|
+
{placeholder || "Selecione..."}
|
|
93
|
+
</li>
|
|
94
|
+
{searchable && (
|
|
95
|
+
<li className="px-2 cursor-pointer text-foreground flex gap-2 items-center justify-start">
|
|
96
|
+
<div className="flex-1">
|
|
97
|
+
<Input
|
|
98
|
+
field=""
|
|
99
|
+
title=""
|
|
100
|
+
placeholder="Buscar"
|
|
101
|
+
icon={<MagnifyingGlassIcon />}
|
|
102
|
+
iconPosition="left"
|
|
103
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
104
|
+
value={search}
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
<Button
|
|
108
|
+
color="blue"
|
|
109
|
+
title=""
|
|
110
|
+
icon={<BroomIcon />}
|
|
111
|
+
positionIcon="left"
|
|
112
|
+
onClick={() => {
|
|
113
|
+
handleSelect({} as T);
|
|
114
|
+
setSearch("");
|
|
115
|
+
}}
|
|
116
|
+
className="m-0"
|
|
117
|
+
/>
|
|
118
|
+
</li>
|
|
119
|
+
)}
|
|
120
|
+
{options
|
|
121
|
+
.filter((opt) => {
|
|
122
|
+
const fieldValue = getNested(opt, field);
|
|
123
|
+
return String(fieldValue)
|
|
124
|
+
.toLowerCase()
|
|
125
|
+
.includes(search.toLowerCase());
|
|
126
|
+
})
|
|
127
|
+
.map((opt) => (
|
|
128
|
+
<li
|
|
129
|
+
key={v4()}
|
|
130
|
+
className={classnames(
|
|
131
|
+
"p-2 hover:bg-primary cursor-pointer text-foreground",
|
|
132
|
+
{
|
|
133
|
+
"bg-primary":
|
|
134
|
+
getNested(opt, field) === value ||
|
|
135
|
+
selected?.[field] === opt[field],
|
|
136
|
+
}
|
|
137
|
+
)}
|
|
138
|
+
onClick={() => handleSelect(opt)}
|
|
139
|
+
>
|
|
140
|
+
{getNested(opt, field)}
|
|
141
|
+
</li>
|
|
142
|
+
))}
|
|
143
|
+
</ul>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
{error && <p className="text-sm text-red-600 mt-1">{error}</p>}
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type SelectProps<T> = {
|
|
2
|
+
title: string;
|
|
3
|
+
id: string;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
field: keyof T & string;
|
|
6
|
+
value: string;
|
|
7
|
+
options: T[];
|
|
8
|
+
icon?: React.ReactNode;
|
|
9
|
+
iconPosition?: "left" | "right";
|
|
10
|
+
error?: string;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
onChange?: (value: T) => void;
|
|
13
|
+
selected?: T;
|
|
14
|
+
isClearable?: boolean;
|
|
15
|
+
searchable?: boolean;
|
|
16
|
+
};
|
package/src/ui/index.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const hasOwn = {}.hasOwnProperty;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Constrói uma string de classes CSS dinamicamente.
|
|
5
|
+
*
|
|
6
|
+
* Aceita qualquer número de argumentos do tipo string, boolean, null, undefined ou objeto:
|
|
7
|
+
* - Strings são adicionadas diretamente.
|
|
8
|
+
* - Objetos: chaves com valor truthy são incluídas como classes.
|
|
9
|
+
* - Arrays são processados recursivamente.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* classnames("btn", { active: isActive, disabled: false }, ["px-2", null])
|
|
13
|
+
* // Resultado: "btn active px-2"
|
|
14
|
+
*
|
|
15
|
+
* @param {...(string | object | undefined | null | boolean)[]} args - Lista de classes e condições.
|
|
16
|
+
* @returns {string} String final de classes separadas por espaço.
|
|
17
|
+
*/
|
|
18
|
+
export function classnames(
|
|
19
|
+
...args: (string | object | undefined | null | boolean)[]
|
|
20
|
+
): string {
|
|
21
|
+
let classes = "";
|
|
22
|
+
for (let i = 0; i < args.length; i++) {
|
|
23
|
+
const arg = args[i];
|
|
24
|
+
if (arg) {
|
|
25
|
+
classes = appendClass(classes, parseValue(arg));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return classes;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Converte um argumento em string de classes.
|
|
34
|
+
*
|
|
35
|
+
* - Strings são retornadas como estão.
|
|
36
|
+
* - Objetos: inclui chaves com valor truthy.
|
|
37
|
+
* - Arrays: processados recursivamente.
|
|
38
|
+
*
|
|
39
|
+
* @param arg - Argumento a ser processado.
|
|
40
|
+
* @returns String de classes ou vazia.
|
|
41
|
+
*/
|
|
42
|
+
function parseValue(arg: string | object | undefined | null | boolean): string {
|
|
43
|
+
if (typeof arg === "string") {
|
|
44
|
+
return arg;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (typeof arg !== "object" || arg === null) {
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Processa array de classes
|
|
52
|
+
if (Array.isArray(arg)) {
|
|
53
|
+
return classnames(...arg);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Suporte a objetos com `toString()` customizados (não nativos)
|
|
57
|
+
if (
|
|
58
|
+
arg.toString !== Object.prototype.toString &&
|
|
59
|
+
!arg.toString.toString().includes("[native code]")
|
|
60
|
+
) {
|
|
61
|
+
return arg.toString();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Processa objetos: adiciona chave se valor for truthy
|
|
65
|
+
let classes = "";
|
|
66
|
+
|
|
67
|
+
for (const key in arg) {
|
|
68
|
+
if (hasOwn.call(arg, key) && (arg as Record<string, unknown>)[key]) {
|
|
69
|
+
classes = appendClass(classes, key);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return classes;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Concatena duas strings de classe com espaço.
|
|
78
|
+
*
|
|
79
|
+
* @param value - String atual.
|
|
80
|
+
* @param newClass - Nova classe a ser adicionada.
|
|
81
|
+
* @returns String combinada.
|
|
82
|
+
*/
|
|
83
|
+
function appendClass(value: string, newClass: string): string {
|
|
84
|
+
if (!newClass) {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return value ? `${value} ${newClass}` : newClass;
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./classnames";
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
/* Bundler mode */
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
"declaration": true
|
|
23
|
+
},
|
|
24
|
+
"include": ["src"],
|
|
25
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
26
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
4
|
+
import dts from "vite-plugin-dts";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
build: {
|
|
8
|
+
lib: {
|
|
9
|
+
entry: {
|
|
10
|
+
index: path.resolve(__dirname, "src/index.ts"),
|
|
11
|
+
ui: path.resolve(__dirname, "src/ui/index.ts"),
|
|
12
|
+
utils: path.resolve(__dirname, "src/utils/index.ts"),
|
|
13
|
+
},
|
|
14
|
+
name: "ProcessHubLib",
|
|
15
|
+
formats: ["es", "cjs"],
|
|
16
|
+
fileName: (format, entryName) => `${entryName}.${format}.js`,
|
|
17
|
+
},
|
|
18
|
+
rollupOptions: {
|
|
19
|
+
external: ["react", "react-dom"],
|
|
20
|
+
output: {
|
|
21
|
+
globals: { react: "React" },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
plugins: [
|
|
26
|
+
tailwindcss(),
|
|
27
|
+
dts({
|
|
28
|
+
insertTypesEntry: true,
|
|
29
|
+
}),
|
|
30
|
+
],
|
|
31
|
+
});
|