@zauru-sdk/components 2.1.1 → 2.2.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 CHANGED
@@ -3,6 +3,17 @@
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.2.0](https://github.com/intuitiva/zauru-typescript-sdk/compare/v2.1.1...v2.2.0) (2025-06-26)
7
+
8
+
9
+ ### Features
10
+
11
+ * updating payee relations ([f133cbd](https://github.com/intuitiva/zauru-typescript-sdk/commit/f133cbdbd633330571fe99b1c775fbb900c1caba))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [2.1.1](https://github.com/intuitiva/zauru-typescript-sdk/compare/v2.1.0...v2.1.1) (2025-06-18)
7
18
 
8
19
  **Note:** Version bump only for package @zauru-sdk/components
@@ -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
- const buttonContent = (_jsx("button", { type: type, name: "action", value: name, disabled: loading || disabled || (enableFormErrorsValidation && formHasErrors), onClick: onClickSave, className: `${loading || disabled || (enableFormErrorsValidation && formHasErrors)
62
- ? " bg-opacity-25 "
63
- : ""} ${loading
64
- ? " cursor-progress"
65
- : `${disabled || (enableFormErrorsValidation && formHasErrors)
66
- ? ""
67
- : `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 }));
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, buttonContent] }));
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.1.1",
3
+ "version": "2.2.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.1.1",
39
- "@zauru-sdk/hooks": "^2.1.1",
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.1.1",
42
- "@zauru-sdk/utils": "^2.1.1",
41
+ "@zauru-sdk/types": "^2.2.0",
42
+ "@zauru-sdk/utils": "^2.2.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": "6f082bf26e4d521870f887a8576a2c6f41d13392"
55
+ "gitHead": "eaa293691510bcff7233d44098944210bf109bdb"
56
56
  }
@@ -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
- const buttonContent = (
97
- <button
98
- type={type}
99
- name={"action"}
100
- value={name}
101
- disabled={
102
- loading || disabled || (enableFormErrorsValidation && formHasErrors)
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
- onClick={onClickSave}
105
- className={`${
106
- loading || disabled || (enableFormErrorsValidation && formHasErrors)
107
- ? " bg-opacity-25 "
108
- : ""
109
- } ${
110
- loading
111
- ? " cursor-progress"
112
- : `${
113
- disabled || (enableFormErrorsValidation && formHasErrors)
114
- ? ""
115
- : `hover:${color.bg700}`
116
- }`
117
- } inline-flex justify-center rounded-md border border-transparent ${
118
- color.bg600
119
- } py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:${
120
- color.ring500
121
- } focus:ring-offset-2 ${className}`}
122
- >
123
- {loading ? children ?? loadingText : children ?? title}
124
- </button>
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
- {buttonContent}
240
+ {dropdownContent}
138
241
  </>
139
242
  );
140
243
  };