@zauru-sdk/components 2.1.1 → 2.3.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/CHANGELOG.md +19 -0
- package/dist/Buttons/Button.d.ts +7 -0
- package/dist/esm/Buttons/Button.js +37 -9
- package/package.json +6 -6
- package/src/Buttons/Button.tsx +132 -29
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [2.3.0](https://github.com/intuitiva/zauru-typescript-sdk/compare/v2.2.0...v2.3.0) (2025-06-27)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @zauru-sdk/components
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [2.2.0](https://github.com/intuitiva/zauru-typescript-sdk/compare/v2.1.1...v2.2.0) (2025-06-26)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* updating payee relations ([f133cbd](https://github.com/intuitiva/zauru-typescript-sdk/commit/f133cbdbd633330571fe99b1c775fbb900c1caba))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
## [2.1.1](https://github.com/intuitiva/zauru-typescript-sdk/compare/v2.1.0...v2.1.1) (2025-06-18)
|
|
7
26
|
|
|
8
27
|
**Note:** Version bump only for package @zauru-sdk/components
|
package/dist/Buttons/Button.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
type DropdownOption = {
|
|
2
|
+
label: string;
|
|
3
|
+
value: string;
|
|
4
|
+
onClick: () => void;
|
|
5
|
+
};
|
|
1
6
|
type Props = {
|
|
2
7
|
type?: "reset" | "button" | "submit" | undefined;
|
|
3
8
|
title?: string;
|
|
@@ -11,6 +16,8 @@ type Props = {
|
|
|
11
16
|
disabled?: boolean;
|
|
12
17
|
enableFormErrorsValidation?: boolean;
|
|
13
18
|
enableFormErrorsDescriptions?: boolean;
|
|
19
|
+
dropdownOptions?: DropdownOption[];
|
|
20
|
+
dropdownTitle?: string;
|
|
14
21
|
};
|
|
15
22
|
export declare const Button: (props: Props) => import("react/jsx-runtime").JSX.Element;
|
|
16
23
|
export {};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useFormContext } from "react-hook-form";
|
|
3
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
4
|
export const Button = (props) => {
|
|
4
|
-
const { type = "submit", loading = false, loadingText = "Guardando...", title = "Guardar", name = "save", onClickSave, selectedColor = "indigo", children, className = "", disabled = false, enableFormErrorsValidation = false, enableFormErrorsDescriptions = false, } = props;
|
|
5
|
+
const { type = "submit", loading = false, loadingText = "Guardando...", title = "Guardar", name = "save", onClickSave, selectedColor = "indigo", children, className = "", disabled = false, enableFormErrorsValidation = false, enableFormErrorsDescriptions = false, dropdownOptions = [], dropdownTitle, } = props;
|
|
6
|
+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
7
|
+
const dropdownRef = useRef(null);
|
|
5
8
|
const formContext = useFormContext();
|
|
6
9
|
const formHasErrors = formContext ? !formContext.formState.isValid : false;
|
|
7
10
|
const formErrors = formContext ? formContext.formState.errors : {};
|
|
@@ -58,13 +61,38 @@ export const Button = (props) => {
|
|
|
58
61
|
.map((error) => error?.message?.toString())
|
|
59
62
|
.join(", ")
|
|
60
63
|
: "";
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
// Manejar click fuera del dropdown
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const handleClickOutside = (event) => {
|
|
67
|
+
if (dropdownRef.current &&
|
|
68
|
+
!dropdownRef.current.contains(event.target)) {
|
|
69
|
+
setIsDropdownOpen(false);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
73
|
+
return () => {
|
|
74
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
75
|
+
};
|
|
76
|
+
}, []);
|
|
77
|
+
const isButtonDisabled = loading || disabled || (enableFormErrorsValidation && formHasErrors);
|
|
78
|
+
const hasDropdown = dropdownOptions.length > 0;
|
|
79
|
+
// Si no hay opciones de dropdown, comportamiento normal
|
|
80
|
+
if (!hasDropdown) {
|
|
81
|
+
const buttonContent = (_jsx("button", { type: type, name: "action", value: name, disabled: isButtonDisabled, onClick: onClickSave, className: `${isButtonDisabled ? " bg-opacity-25 " : ""} ${loading
|
|
82
|
+
? " cursor-progress"
|
|
83
|
+
: `${isButtonDisabled ? "" : `hover:${color.bg700}`}`} inline-flex justify-center rounded-md border border-transparent ${color.bg600} py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:${color.ring500} focus:ring-offset-2 ${className}`, children: loading ? children ?? loadingText : children ?? title }));
|
|
84
|
+
return (_jsxs(_Fragment, { children: [(enableFormErrorsValidation && formHasErrors && errorMessage) ||
|
|
85
|
+
(enableFormErrorsDescriptions && errorMessage) ? (_jsx("div", { className: "flex flex-col items-end mb-2", children: _jsx("div", { className: "p-2 bg-red-100 border border-red-400 text-red-700 rounded-md shadow-sm", children: _jsx("p", { className: "text-sm", children: errorMessage }) }) })) : null, buttonContent] }));
|
|
86
|
+
}
|
|
87
|
+
// Comportamiento con dropdown
|
|
88
|
+
const dropdownContent = (_jsxs("div", { className: "relative inline-block text-left", ref: dropdownRef, children: [_jsx("div", { children: _jsxs("button", { type: "button", disabled: isButtonDisabled, onClick: () => setIsDropdownOpen(!isDropdownOpen), className: `${isButtonDisabled ? " bg-opacity-25 " : ""} ${loading
|
|
89
|
+
? " cursor-progress"
|
|
90
|
+
: `${isButtonDisabled ? "" : `hover:${color.bg700}`}`} inline-flex justify-center items-center rounded-md border border-transparent ${color.bg600} py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:${color.ring500} focus:ring-offset-2 ${className}`, children: [loading
|
|
91
|
+
? children ?? loadingText
|
|
92
|
+
: children ?? dropdownTitle ?? title, _jsx("svg", { className: `ml-2 -mr-1 h-4 w-4 transition-transform ${isDropdownOpen ? "rotate-180" : ""}`, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", children: _jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) })] }) }), isDropdownOpen && (_jsx("div", { className: "absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none", children: _jsx("div", { className: "py-1", children: dropdownOptions.map((option, index) => (_jsx("button", { type: "button", onClick: () => {
|
|
93
|
+
option.onClick();
|
|
94
|
+
setIsDropdownOpen(false);
|
|
95
|
+
}, className: "block w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 text-left", children: option.label }, index))) }) }))] }));
|
|
68
96
|
return (_jsxs(_Fragment, { children: [(enableFormErrorsValidation && formHasErrors && errorMessage) ||
|
|
69
|
-
(enableFormErrorsDescriptions && errorMessage) ? (_jsx("div", { className: "flex flex-col items-end mb-2", children: _jsx("div", { className: "p-2 bg-red-100 border border-red-400 text-red-700 rounded-md shadow-sm", children: _jsx("p", { className: "text-sm", children: errorMessage }) }) })) : null,
|
|
97
|
+
(enableFormErrorsDescriptions && errorMessage) ? (_jsx("div", { className: "flex flex-col items-end mb-2", children: _jsx("div", { className: "p-2 bg-red-100 border border-red-400 text-red-700 rounded-md shadow-sm", children: _jsx("p", { className: "text-sm", children: errorMessage }) }) })) : null, dropdownContent] }));
|
|
70
98
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zauru-sdk/components",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Componentes reutilizables en las WebApps de Zauru.",
|
|
5
5
|
"main": "./dist/esm/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"@rails/activestorage": "^8.0.200",
|
|
36
36
|
"@reduxjs/toolkit": "^2.2.1",
|
|
37
37
|
"@remix-run/react": "^2.8.1",
|
|
38
|
-
"@zauru-sdk/common": "^2.
|
|
39
|
-
"@zauru-sdk/hooks": "^2.
|
|
38
|
+
"@zauru-sdk/common": "^2.2.0",
|
|
39
|
+
"@zauru-sdk/hooks": "^2.2.0",
|
|
40
40
|
"@zauru-sdk/icons": "^2.0.188",
|
|
41
|
-
"@zauru-sdk/types": "^2.
|
|
42
|
-
"@zauru-sdk/utils": "^2.
|
|
41
|
+
"@zauru-sdk/types": "^2.2.0",
|
|
42
|
+
"@zauru-sdk/utils": "^2.3.0",
|
|
43
43
|
"browser-image-compression": "^2.0.2",
|
|
44
44
|
"framer-motion": "^11.7.0",
|
|
45
45
|
"jsonwebtoken": "^9.0.2",
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"styled-components": "^5.3.5",
|
|
53
53
|
"zod": "^3.23.8"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "c0a2ddf3c3c2b5ba8da9e7c7a3a40043a64688d3"
|
|
56
56
|
}
|
package/src/Buttons/Button.tsx
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { ColorInterface } from "../NavBar/NavBar.types.js";
|
|
2
2
|
import { useFormContext } from "react-hook-form";
|
|
3
|
+
import { useState, useRef, useEffect } from "react";
|
|
4
|
+
|
|
5
|
+
type DropdownOption = {
|
|
6
|
+
label: string;
|
|
7
|
+
value: string;
|
|
8
|
+
onClick: () => void;
|
|
9
|
+
};
|
|
3
10
|
|
|
4
11
|
type Props = {
|
|
5
12
|
type?: "reset" | "button" | "submit" | undefined;
|
|
@@ -15,6 +22,9 @@ type Props = {
|
|
|
15
22
|
disabled?: boolean;
|
|
16
23
|
enableFormErrorsValidation?: boolean;
|
|
17
24
|
enableFormErrorsDescriptions?: boolean;
|
|
25
|
+
// Nuevas props para dropdown
|
|
26
|
+
dropdownOptions?: DropdownOption[];
|
|
27
|
+
dropdownTitle?: string;
|
|
18
28
|
};
|
|
19
29
|
|
|
20
30
|
export const Button = (props: Props) => {
|
|
@@ -31,8 +41,13 @@ export const Button = (props: Props) => {
|
|
|
31
41
|
disabled = false,
|
|
32
42
|
enableFormErrorsValidation = false,
|
|
33
43
|
enableFormErrorsDescriptions = false,
|
|
44
|
+
dropdownOptions = [],
|
|
45
|
+
dropdownTitle,
|
|
34
46
|
} = props;
|
|
35
47
|
|
|
48
|
+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
49
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
50
|
+
|
|
36
51
|
const formContext = useFormContext();
|
|
37
52
|
const formHasErrors = formContext ? !formContext.formState.isValid : false;
|
|
38
53
|
const formErrors = formContext ? formContext.formState.errors : {};
|
|
@@ -93,35 +108,123 @@ export const Button = (props: Props) => {
|
|
|
93
108
|
.join(", ")
|
|
94
109
|
: "";
|
|
95
110
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
111
|
+
// Manejar click fuera del dropdown
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
114
|
+
if (
|
|
115
|
+
dropdownRef.current &&
|
|
116
|
+
!dropdownRef.current.contains(event.target as Node)
|
|
117
|
+
) {
|
|
118
|
+
setIsDropdownOpen(false);
|
|
103
119
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
123
|
+
return () => {
|
|
124
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
125
|
+
};
|
|
126
|
+
}, []);
|
|
127
|
+
|
|
128
|
+
const isButtonDisabled =
|
|
129
|
+
loading || disabled || (enableFormErrorsValidation && formHasErrors);
|
|
130
|
+
const hasDropdown = dropdownOptions.length > 0;
|
|
131
|
+
|
|
132
|
+
// Si no hay opciones de dropdown, comportamiento normal
|
|
133
|
+
if (!hasDropdown) {
|
|
134
|
+
const buttonContent = (
|
|
135
|
+
<button
|
|
136
|
+
type={type}
|
|
137
|
+
name={"action"}
|
|
138
|
+
value={name}
|
|
139
|
+
disabled={isButtonDisabled}
|
|
140
|
+
onClick={onClickSave}
|
|
141
|
+
className={`${isButtonDisabled ? " bg-opacity-25 " : ""} ${
|
|
142
|
+
loading
|
|
143
|
+
? " cursor-progress"
|
|
144
|
+
: `${isButtonDisabled ? "" : `hover:${color.bg700}`}`
|
|
145
|
+
} inline-flex justify-center rounded-md border border-transparent ${
|
|
146
|
+
color.bg600
|
|
147
|
+
} py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:${
|
|
148
|
+
color.ring500
|
|
149
|
+
} focus:ring-offset-2 ${className}`}
|
|
150
|
+
>
|
|
151
|
+
{loading ? children ?? loadingText : children ?? title}
|
|
152
|
+
</button>
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<>
|
|
157
|
+
{(enableFormErrorsValidation && formHasErrors && errorMessage) ||
|
|
158
|
+
(enableFormErrorsDescriptions && errorMessage) ? (
|
|
159
|
+
<div className="flex flex-col items-end mb-2">
|
|
160
|
+
<div className="p-2 bg-red-100 border border-red-400 text-red-700 rounded-md shadow-sm">
|
|
161
|
+
<p className="text-sm">{errorMessage}</p>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
) : null}
|
|
165
|
+
{buttonContent}
|
|
166
|
+
</>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Comportamiento con dropdown
|
|
171
|
+
const dropdownContent = (
|
|
172
|
+
<div className="relative inline-block text-left" ref={dropdownRef}>
|
|
173
|
+
<div>
|
|
174
|
+
<button
|
|
175
|
+
type="button"
|
|
176
|
+
disabled={isButtonDisabled}
|
|
177
|
+
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
|
178
|
+
className={`${isButtonDisabled ? " bg-opacity-25 " : ""} ${
|
|
179
|
+
loading
|
|
180
|
+
? " cursor-progress"
|
|
181
|
+
: `${isButtonDisabled ? "" : `hover:${color.bg700}`}`
|
|
182
|
+
} inline-flex justify-center items-center rounded-md border border-transparent ${
|
|
183
|
+
color.bg600
|
|
184
|
+
} py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:${
|
|
185
|
+
color.ring500
|
|
186
|
+
} focus:ring-offset-2 ${className}`}
|
|
187
|
+
>
|
|
188
|
+
{loading
|
|
189
|
+
? children ?? loadingText
|
|
190
|
+
: children ?? dropdownTitle ?? title}
|
|
191
|
+
<svg
|
|
192
|
+
className={`ml-2 -mr-1 h-4 w-4 transition-transform ${
|
|
193
|
+
isDropdownOpen ? "rotate-180" : ""
|
|
194
|
+
}`}
|
|
195
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
196
|
+
viewBox="0 0 20 20"
|
|
197
|
+
fill="currentColor"
|
|
198
|
+
>
|
|
199
|
+
<path
|
|
200
|
+
fillRule="evenodd"
|
|
201
|
+
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
|
202
|
+
clipRule="evenodd"
|
|
203
|
+
/>
|
|
204
|
+
</svg>
|
|
205
|
+
</button>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
{isDropdownOpen && (
|
|
209
|
+
<div className="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
|
210
|
+
<div className="py-1">
|
|
211
|
+
{dropdownOptions.map((option, index) => (
|
|
212
|
+
<button
|
|
213
|
+
key={index}
|
|
214
|
+
type="button"
|
|
215
|
+
onClick={() => {
|
|
216
|
+
option.onClick();
|
|
217
|
+
setIsDropdownOpen(false);
|
|
218
|
+
}}
|
|
219
|
+
className="block w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 text-left"
|
|
220
|
+
>
|
|
221
|
+
{option.label}
|
|
222
|
+
</button>
|
|
223
|
+
))}
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
125
228
|
);
|
|
126
229
|
|
|
127
230
|
return (
|
|
@@ -134,7 +237,7 @@ export const Button = (props: Props) => {
|
|
|
134
237
|
</div>
|
|
135
238
|
</div>
|
|
136
239
|
) : null}
|
|
137
|
-
{
|
|
240
|
+
{dropdownContent}
|
|
138
241
|
</>
|
|
139
242
|
);
|
|
140
243
|
};
|