@soyfri/shared-library 1.4.0 → 1.4.2
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/hooks/ClipBoard/ClipBoard.cjs +44 -34
- package/hooks/ClipBoard/ClipBoard.cjs.map +1 -1
- package/hooks/ClipBoard/ClipBoard.d.ts +9 -5
- package/hooks/ClipBoard/ClipBoard.js +44 -34
- package/hooks/ClipBoard/ClipBoard.js.map +1 -1
- package/hooks/ClipBoard/ClipboardUnifiedDemo.d.ts +14 -0
- package/package.json +1 -1
- package/hooks/ClipBoard/ClipBoard.definitions.d.ts +0 -5
|
@@ -4,68 +4,78 @@ const jsxRuntime = require("react/jsx-runtime");
|
|
|
4
4
|
const React = require("react");
|
|
5
5
|
const material = require("@mui/material");
|
|
6
6
|
function useClipboard(options) {
|
|
7
|
+
var _a;
|
|
7
8
|
const elementRef = React.useRef(null);
|
|
8
9
|
const [snackbarOpen, setSnackbarOpen] = React.useState(false);
|
|
9
10
|
const [snackbarMessage, setSnackbarMessage] = React.useState((options == null ? void 0 : options.message) || "¡Copiado al portapapeles!");
|
|
10
11
|
const [snackbarSeverity, setSnackbarSeverity] = React.useState((options == null ? void 0 : options.severity) || "success");
|
|
11
12
|
const [snackbarPosition, setSnackbarPosition] = React.useState((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
12
|
-
const [snackbarDuration, setSnackbarDuration] = React.useState((options == null ? void 0 : options.duration)
|
|
13
|
+
const [snackbarDuration, setSnackbarDuration] = React.useState((_a = options == null ? void 0 : options.duration) != null ? _a : 3e3);
|
|
13
14
|
const handleSnackbarClose = React.useCallback((event, reason) => {
|
|
14
15
|
if (reason === "clickaway") {
|
|
15
16
|
return;
|
|
16
17
|
}
|
|
17
18
|
setSnackbarOpen(false);
|
|
18
19
|
}, []);
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const executeCopy = React.useCallback((text) => {
|
|
21
|
+
var _a2, _b;
|
|
22
|
+
try {
|
|
23
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
24
|
+
navigator.clipboard.writeText(text);
|
|
25
|
+
} else {
|
|
22
26
|
const textarea = document.createElement("textarea");
|
|
23
|
-
textarea.value =
|
|
27
|
+
textarea.value = text;
|
|
24
28
|
document.body.appendChild(textarea);
|
|
25
29
|
textarea.select();
|
|
26
30
|
document.execCommand("copy");
|
|
27
31
|
document.body.removeChild(textarea);
|
|
28
|
-
setSnackbarMessage((options == null ? void 0 : options.message) || "¡Copiado al portapapeles!");
|
|
29
|
-
setSnackbarSeverity((options == null ? void 0 : options.severity) || "success");
|
|
30
|
-
setSnackbarPosition((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
31
|
-
setSnackbarDuration((options == null ? void 0 : options.duration) || 3e3);
|
|
32
|
-
setSnackbarOpen(true);
|
|
33
|
-
} catch (err) {
|
|
34
|
-
console.error("Error al copiar al portapapeles:", err);
|
|
35
|
-
setSnackbarMessage("Error al copiar.");
|
|
36
|
-
setSnackbarSeverity("error");
|
|
37
|
-
setSnackbarPosition((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
38
|
-
setSnackbarDuration((options == null ? void 0 : options.duration) || 3e3);
|
|
39
|
-
setSnackbarOpen(true);
|
|
40
32
|
}
|
|
33
|
+
setSnackbarMessage((options == null ? void 0 : options.message) || "¡Copiado al portapapeles!");
|
|
34
|
+
setSnackbarSeverity((options == null ? void 0 : options.severity) || "success");
|
|
35
|
+
setSnackbarPosition((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
36
|
+
setSnackbarDuration((_a2 = options == null ? void 0 : options.duration) != null ? _a2 : 3e3);
|
|
37
|
+
setSnackbarOpen(true);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.error("Error al copiar al portapapeles:", err);
|
|
40
|
+
setSnackbarMessage("Error al copiar.");
|
|
41
|
+
setSnackbarSeverity("error");
|
|
42
|
+
setSnackbarPosition((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
43
|
+
setSnackbarDuration((_b = options == null ? void 0 : options.duration) != null ? _b : 3e3);
|
|
44
|
+
setSnackbarOpen(true);
|
|
41
45
|
}
|
|
42
46
|
}, [options]);
|
|
47
|
+
const copyFromRef = React.useCallback(() => {
|
|
48
|
+
if (elementRef.current) {
|
|
49
|
+
const textToCopy = elementRef.current.innerText || "";
|
|
50
|
+
executeCopy(textToCopy);
|
|
51
|
+
}
|
|
52
|
+
}, [executeCopy]);
|
|
43
53
|
React.useEffect(() => {
|
|
44
54
|
const currentElement = elementRef.current;
|
|
45
55
|
if (currentElement) {
|
|
46
56
|
currentElement.style.cursor = "pointer";
|
|
47
|
-
currentElement.addEventListener("click",
|
|
57
|
+
currentElement.addEventListener("click", copyFromRef);
|
|
48
58
|
}
|
|
49
59
|
return () => {
|
|
50
60
|
if (currentElement) {
|
|
51
|
-
currentElement.removeEventListener("click",
|
|
61
|
+
currentElement.removeEventListener("click", copyFromRef);
|
|
52
62
|
}
|
|
53
63
|
};
|
|
54
|
-
}, [
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return { ref: elementRef,
|
|
64
|
+
}, [copyFromRef]);
|
|
65
|
+
const CopyMessage = () => {
|
|
66
|
+
if (snackbarDuration <= 0) return null;
|
|
67
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
68
|
+
material.Snackbar,
|
|
69
|
+
{
|
|
70
|
+
open: snackbarOpen,
|
|
71
|
+
autoHideDuration: snackbarDuration,
|
|
72
|
+
onClose: handleSnackbarClose,
|
|
73
|
+
anchorOrigin: snackbarPosition,
|
|
74
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(material.Alert, { onClose: handleSnackbarClose, severity: snackbarSeverity, sx: { width: "100%" }, children: snackbarMessage })
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
return { ref: elementRef, copy: executeCopy, CopyMessage };
|
|
69
79
|
}
|
|
70
80
|
exports.useClipboard = useClipboard;
|
|
71
81
|
//# sourceMappingURL=ClipBoard.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClipBoard.cjs","sources":["../../../src/hooks/ClipBoard/ClipBoard.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"ClipBoard.cjs","sources":["../../../src/hooks/ClipBoard/ClipBoard.tsx"],"sourcesContent":["// useClipboard.tsx (Versión Unificada)\nimport React, { useRef, useEffect, useState, useCallback } from 'react';\nimport { Snackbar, Alert } from '@mui/material';\nimport { AlertProps } from '@mui/material/Alert';\n\n// Tipo para las opciones del Snackbar\ninterface SnackbarOptions {\n message?: string;\n duration?: number;\n severity?: AlertProps['severity'];\n position?: { vertical: 'top' | 'bottom'; horizontal: 'left' | 'center' | 'right' };\n}\n\n/**\n * Hook 'useClipboard'.\n * Proporciona funcionalidad de copiado de texto y feedback visual (Snackbar).\n * Permite dos modos de uso:\n * 1. Uso por Referencia (ref): Se adjunta a un elemento, copia su innerText al hacer clic.\n * 2. Uso por Función (copy): Recibe el texto a copiar directamente como argumento.\n *\n * @param options Opciones de configuración para el Snackbar de feedback.\n * @returns Un objeto que contiene:\n * - 'ref': Una React Ref para adjuntar a un elemento (Modo Ref).\n * - 'copy': Una función (text: string) => void para copiar texto (Modo Función).\n * - 'CopyMessage': El componente Snackbar para renderizar el feedback.\n */\nexport function useClipboard(options?: SnackbarOptions) {\n // --- Elementos de Copiado por Referencia ---\n const elementRef = useRef<HTMLElement>(null);\n\n // --- Estados y Opciones del Snackbar ---\n const [snackbarOpen, setSnackbarOpen] = useState(false);\n const [snackbarMessage, setSnackbarMessage] = useState(options?.message || '¡Copiado al portapapeles!');\n const [snackbarSeverity, setSnackbarSeverity] = useState<AlertProps['severity']>(options?.severity || 'success');\n const [snackbarPosition, setSnackbarPosition] = useState<import('@mui/material').SnackbarOrigin>(options?.position || { vertical: 'bottom', horizontal: 'center' });\n const [snackbarDuration, setSnackbarDuration] = useState(options?.duration ?? 3000);\n\n // Función para cerrar el Snackbar\n const handleSnackbarClose = useCallback((event?: React.SyntheticEvent | Event, reason?: string) => {\n if (reason === 'clickaway') {\n return;\n }\n setSnackbarOpen(false);\n }, []);\n\n /**\n * FUNCIÓN PRINCIPAL DE COPIADO.\n * Se usa internamente para 'ref' y se expone para 'copy(text)'.\n * @param text El texto exacto a copiar.\n */\n const executeCopy = useCallback((text: string) => {\n try {\n if (navigator.clipboard && window.isSecureContext) {\n // 1. API moderna\n navigator.clipboard.writeText(text);\n } else {\n // 2. Fallback usando textarea\n const textarea = document.createElement('textarea');\n textarea.value = text;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n }\n \n // Mostrar éxito\n setSnackbarMessage(options?.message || '¡Copiado al portapapeles!');\n setSnackbarSeverity(options?.severity || 'success');\n setSnackbarPosition(options?.position || { vertical: 'bottom', horizontal: 'center' });\n setSnackbarDuration(options?.duration ?? 3000);\n setSnackbarOpen(true);\n\n } catch (err) {\n // Mostrar error\n console.error('Error al copiar al portapapeles:', err);\n setSnackbarMessage('Error al copiar.');\n setSnackbarSeverity('error');\n setSnackbarPosition(options?.position || { vertical: 'bottom', horizontal: 'center' });\n setSnackbarDuration(options?.duration ?? 3000);\n setSnackbarOpen(true);\n }\n }, [options]); \n\n // --- Lógica del Modo Ref ---\n const copyFromRef = useCallback(() => {\n if (elementRef.current) {\n const textToCopy = elementRef.current.innerText || '';\n executeCopy(textToCopy);\n }\n }, [executeCopy]);\n\n // Adjunta un event listener de clic al elemento referenciado (Modo Ref)\n useEffect(() => {\n const currentElement = elementRef.current;\n if (currentElement) {\n currentElement.style.cursor = 'pointer';\n // Adjuntamos la función que copia el texto del ref\n currentElement.addEventListener('click', copyFromRef);\n }\n\n return () => {\n if (currentElement) {\n currentElement.removeEventListener('click', copyFromRef);\n }\n };\n }, [copyFromRef]);\n\n\n // Componente Snackbar encapsulado. Solo se renderiza si duration es > 0.\n const CopyMessage = () => {\n if (snackbarDuration <= 0) return null; // No renderizar si la duración es 0\n\n return (\n <Snackbar\n open={snackbarOpen}\n autoHideDuration={snackbarDuration}\n onClose={handleSnackbarClose}\n anchorOrigin={snackbarPosition}\n >\n <Alert onClose={handleSnackbarClose} severity={snackbarSeverity} sx={{ width: '100%' }}>\n {snackbarMessage}\n </Alert>\n </Snackbar>\n );\n };\n\n // Retorna ambas funcionalidades para que el usuario elija\n return { ref: elementRef, copy: executeCopy, CopyMessage };\n}\n\nexport default useClipboard;"],"names":["useRef","useState","useCallback","_a","useEffect","jsx","Snackbar","Alert"],"mappings":";;;;;AA0BO,SAAS,aAAa,SAA2B;;AAEtD,QAAM,aAAaA,MAAAA,OAAoB,IAAI;AAG3C,QAAM,CAAC,cAAc,eAAe,IAAIC,MAAAA,SAAS,KAAK;AACtD,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,MAAAA,UAAS,mCAAS,YAAW,2BAA2B;AACtG,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,MAAAA,UAAiC,mCAAS,aAAY,SAAS;AAC/G,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,MAAAA,UAAiD,mCAAS,aAAY,EAAE,UAAU,UAAU,YAAY,SAAA,CAAU;AAClK,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,MAAAA,UAAS,wCAAS,aAAT,YAAqB,GAAI;AAGlF,QAAM,sBAAsBC,MAAAA,YAAY,CAAC,OAAsC,WAAoB;AACjG,QAAI,WAAW,aAAa;AAC1B;AAAA,IACF;AACA,oBAAgB,KAAK;AAAA,EACvB,GAAG,CAAA,CAAE;AAOL,QAAM,cAAcA,kBAAY,CAAC,SAAiB;;AAChD,QAAI;AACF,UAAI,UAAU,aAAa,OAAO,iBAAiB;AAEjD,kBAAU,UAAU,UAAU,IAAI;AAAA,MACpC,OAAO;AAEL,cAAM,WAAW,SAAS,cAAc,UAAU;AAClD,iBAAS,QAAQ;AACjB,iBAAS,KAAK,YAAY,QAAQ;AAClC,iBAAS,OAAA;AACT,iBAAS,YAAY,MAAM;AAC3B,iBAAS,KAAK,YAAY,QAAQ;AAAA,MACpC;AAGA,0BAAmB,mCAAS,YAAW,2BAA2B;AAClE,2BAAoB,mCAAS,aAAY,SAAS;AAClD,2BAAoB,mCAAS,aAAY,EAAE,UAAU,UAAU,YAAY,UAAU;AACrF,2BAAoBC,MAAA,mCAAS,aAAT,OAAAA,MAAqB,GAAI;AAC7C,sBAAgB,IAAI;AAAA,IAEtB,SAAS,KAAK;AAEZ,cAAQ,MAAM,oCAAoC,GAAG;AACrD,yBAAmB,kBAAkB;AACrC,0BAAoB,OAAO;AAC3B,2BAAoB,mCAAS,aAAY,EAAE,UAAU,UAAU,YAAY,UAAU;AACrF,2BAAoB,wCAAS,aAAT,YAAqB,GAAI;AAC7C,sBAAgB,IAAI;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAGZ,QAAM,cAAcD,MAAAA,YAAY,MAAM;AACpC,QAAI,WAAW,SAAS;AACpB,YAAM,aAAa,WAAW,QAAQ,aAAa;AACnD,kBAAY,UAAU;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAGhBE,QAAAA,UAAU,MAAM;AACd,UAAM,iBAAiB,WAAW;AAClC,QAAI,gBAAgB;AAClB,qBAAe,MAAM,SAAS;AAE9B,qBAAe,iBAAiB,SAAS,WAAW;AAAA,IACtD;AAEA,WAAO,MAAM;AACX,UAAI,gBAAgB;AAClB,uBAAe,oBAAoB,SAAS,WAAW;AAAA,MACzD;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAIhB,QAAM,cAAc,MAAM;AACxB,QAAI,oBAAoB,EAAG,QAAO;AAElC,WACEC,2BAAAA;AAAAA,MAACC,SAAAA;AAAAA,MAAA;AAAA,QACC,MAAM;AAAA,QACN,kBAAkB;AAAA,QAClB,SAAS;AAAA,QACT,cAAc;AAAA,QAEd,UAAAD,2BAAAA,IAACE,SAAAA,OAAA,EAAM,SAAS,qBAAqB,UAAU,kBAAkB,IAAI,EAAE,OAAO,OAAA,GAC3E,UAAA,gBAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAGA,SAAO,EAAE,KAAK,YAAY,MAAM,aAAa,YAAA;AAC/C;;"}
|
|
@@ -11,16 +11,20 @@ interface SnackbarOptions {
|
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Hook 'useClipboard'.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Proporciona funcionalidad de copiado de texto y feedback visual (Snackbar).
|
|
15
|
+
* Permite dos modos de uso:
|
|
16
|
+
* 1. Uso por Referencia (ref): Se adjunta a un elemento, copia su innerText al hacer clic.
|
|
17
|
+
* 2. Uso por Función (copy): Recibe el texto a copiar directamente como argumento.
|
|
16
18
|
*
|
|
17
19
|
* @param options Opciones de configuración para el Snackbar de feedback.
|
|
18
20
|
* @returns Un objeto que contiene:
|
|
19
|
-
* - 'ref': Una
|
|
20
|
-
* - '
|
|
21
|
+
* - 'ref': Una React Ref para adjuntar a un elemento (Modo Ref).
|
|
22
|
+
* - 'copy': Una función (text: string) => void para copiar texto (Modo Función).
|
|
23
|
+
* - 'CopyMessage': El componente Snackbar para renderizar el feedback.
|
|
21
24
|
*/
|
|
22
25
|
export declare function useClipboard(options?: SnackbarOptions): {
|
|
23
26
|
ref: React.RefObject<HTMLElement | null>;
|
|
24
|
-
|
|
27
|
+
copy: (text: string) => void;
|
|
28
|
+
CopyMessage: () => import("react/jsx-runtime").JSX.Element | null;
|
|
25
29
|
};
|
|
26
30
|
export default useClipboard;
|
|
@@ -2,68 +2,78 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useRef, useState, useCallback, useEffect } from "react";
|
|
3
3
|
import { Snackbar, Alert } from "@mui/material";
|
|
4
4
|
function useClipboard(options) {
|
|
5
|
+
var _a;
|
|
5
6
|
const elementRef = useRef(null);
|
|
6
7
|
const [snackbarOpen, setSnackbarOpen] = useState(false);
|
|
7
8
|
const [snackbarMessage, setSnackbarMessage] = useState((options == null ? void 0 : options.message) || "¡Copiado al portapapeles!");
|
|
8
9
|
const [snackbarSeverity, setSnackbarSeverity] = useState((options == null ? void 0 : options.severity) || "success");
|
|
9
10
|
const [snackbarPosition, setSnackbarPosition] = useState((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
10
|
-
const [snackbarDuration, setSnackbarDuration] = useState((options == null ? void 0 : options.duration)
|
|
11
|
+
const [snackbarDuration, setSnackbarDuration] = useState((_a = options == null ? void 0 : options.duration) != null ? _a : 3e3);
|
|
11
12
|
const handleSnackbarClose = useCallback((event, reason) => {
|
|
12
13
|
if (reason === "clickaway") {
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
15
16
|
setSnackbarOpen(false);
|
|
16
17
|
}, []);
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const executeCopy = useCallback((text) => {
|
|
19
|
+
var _a2, _b;
|
|
20
|
+
try {
|
|
21
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
22
|
+
navigator.clipboard.writeText(text);
|
|
23
|
+
} else {
|
|
20
24
|
const textarea = document.createElement("textarea");
|
|
21
|
-
textarea.value =
|
|
25
|
+
textarea.value = text;
|
|
22
26
|
document.body.appendChild(textarea);
|
|
23
27
|
textarea.select();
|
|
24
28
|
document.execCommand("copy");
|
|
25
29
|
document.body.removeChild(textarea);
|
|
26
|
-
setSnackbarMessage((options == null ? void 0 : options.message) || "¡Copiado al portapapeles!");
|
|
27
|
-
setSnackbarSeverity((options == null ? void 0 : options.severity) || "success");
|
|
28
|
-
setSnackbarPosition((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
29
|
-
setSnackbarDuration((options == null ? void 0 : options.duration) || 3e3);
|
|
30
|
-
setSnackbarOpen(true);
|
|
31
|
-
} catch (err) {
|
|
32
|
-
console.error("Error al copiar al portapapeles:", err);
|
|
33
|
-
setSnackbarMessage("Error al copiar.");
|
|
34
|
-
setSnackbarSeverity("error");
|
|
35
|
-
setSnackbarPosition((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
36
|
-
setSnackbarDuration((options == null ? void 0 : options.duration) || 3e3);
|
|
37
|
-
setSnackbarOpen(true);
|
|
38
30
|
}
|
|
31
|
+
setSnackbarMessage((options == null ? void 0 : options.message) || "¡Copiado al portapapeles!");
|
|
32
|
+
setSnackbarSeverity((options == null ? void 0 : options.severity) || "success");
|
|
33
|
+
setSnackbarPosition((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
34
|
+
setSnackbarDuration((_a2 = options == null ? void 0 : options.duration) != null ? _a2 : 3e3);
|
|
35
|
+
setSnackbarOpen(true);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error("Error al copiar al portapapeles:", err);
|
|
38
|
+
setSnackbarMessage("Error al copiar.");
|
|
39
|
+
setSnackbarSeverity("error");
|
|
40
|
+
setSnackbarPosition((options == null ? void 0 : options.position) || { vertical: "bottom", horizontal: "center" });
|
|
41
|
+
setSnackbarDuration((_b = options == null ? void 0 : options.duration) != null ? _b : 3e3);
|
|
42
|
+
setSnackbarOpen(true);
|
|
39
43
|
}
|
|
40
44
|
}, [options]);
|
|
45
|
+
const copyFromRef = useCallback(() => {
|
|
46
|
+
if (elementRef.current) {
|
|
47
|
+
const textToCopy = elementRef.current.innerText || "";
|
|
48
|
+
executeCopy(textToCopy);
|
|
49
|
+
}
|
|
50
|
+
}, [executeCopy]);
|
|
41
51
|
useEffect(() => {
|
|
42
52
|
const currentElement = elementRef.current;
|
|
43
53
|
if (currentElement) {
|
|
44
54
|
currentElement.style.cursor = "pointer";
|
|
45
|
-
currentElement.addEventListener("click",
|
|
55
|
+
currentElement.addEventListener("click", copyFromRef);
|
|
46
56
|
}
|
|
47
57
|
return () => {
|
|
48
58
|
if (currentElement) {
|
|
49
|
-
currentElement.removeEventListener("click",
|
|
59
|
+
currentElement.removeEventListener("click", copyFromRef);
|
|
50
60
|
}
|
|
51
61
|
};
|
|
52
|
-
}, [
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return { ref: elementRef,
|
|
62
|
+
}, [copyFromRef]);
|
|
63
|
+
const CopyMessage = () => {
|
|
64
|
+
if (snackbarDuration <= 0) return null;
|
|
65
|
+
return /* @__PURE__ */ jsx(
|
|
66
|
+
Snackbar,
|
|
67
|
+
{
|
|
68
|
+
open: snackbarOpen,
|
|
69
|
+
autoHideDuration: snackbarDuration,
|
|
70
|
+
onClose: handleSnackbarClose,
|
|
71
|
+
anchorOrigin: snackbarPosition,
|
|
72
|
+
children: /* @__PURE__ */ jsx(Alert, { onClose: handleSnackbarClose, severity: snackbarSeverity, sx: { width: "100%" }, children: snackbarMessage })
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
return { ref: elementRef, copy: executeCopy, CopyMessage };
|
|
67
77
|
}
|
|
68
78
|
export {
|
|
69
79
|
useClipboard
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClipBoard.js","sources":["../../../src/hooks/ClipBoard/ClipBoard.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"ClipBoard.js","sources":["../../../src/hooks/ClipBoard/ClipBoard.tsx"],"sourcesContent":["// useClipboard.tsx (Versión Unificada)\nimport React, { useRef, useEffect, useState, useCallback } from 'react';\nimport { Snackbar, Alert } from '@mui/material';\nimport { AlertProps } from '@mui/material/Alert';\n\n// Tipo para las opciones del Snackbar\ninterface SnackbarOptions {\n message?: string;\n duration?: number;\n severity?: AlertProps['severity'];\n position?: { vertical: 'top' | 'bottom'; horizontal: 'left' | 'center' | 'right' };\n}\n\n/**\n * Hook 'useClipboard'.\n * Proporciona funcionalidad de copiado de texto y feedback visual (Snackbar).\n * Permite dos modos de uso:\n * 1. Uso por Referencia (ref): Se adjunta a un elemento, copia su innerText al hacer clic.\n * 2. Uso por Función (copy): Recibe el texto a copiar directamente como argumento.\n *\n * @param options Opciones de configuración para el Snackbar de feedback.\n * @returns Un objeto que contiene:\n * - 'ref': Una React Ref para adjuntar a un elemento (Modo Ref).\n * - 'copy': Una función (text: string) => void para copiar texto (Modo Función).\n * - 'CopyMessage': El componente Snackbar para renderizar el feedback.\n */\nexport function useClipboard(options?: SnackbarOptions) {\n // --- Elementos de Copiado por Referencia ---\n const elementRef = useRef<HTMLElement>(null);\n\n // --- Estados y Opciones del Snackbar ---\n const [snackbarOpen, setSnackbarOpen] = useState(false);\n const [snackbarMessage, setSnackbarMessage] = useState(options?.message || '¡Copiado al portapapeles!');\n const [snackbarSeverity, setSnackbarSeverity] = useState<AlertProps['severity']>(options?.severity || 'success');\n const [snackbarPosition, setSnackbarPosition] = useState<import('@mui/material').SnackbarOrigin>(options?.position || { vertical: 'bottom', horizontal: 'center' });\n const [snackbarDuration, setSnackbarDuration] = useState(options?.duration ?? 3000);\n\n // Función para cerrar el Snackbar\n const handleSnackbarClose = useCallback((event?: React.SyntheticEvent | Event, reason?: string) => {\n if (reason === 'clickaway') {\n return;\n }\n setSnackbarOpen(false);\n }, []);\n\n /**\n * FUNCIÓN PRINCIPAL DE COPIADO.\n * Se usa internamente para 'ref' y se expone para 'copy(text)'.\n * @param text El texto exacto a copiar.\n */\n const executeCopy = useCallback((text: string) => {\n try {\n if (navigator.clipboard && window.isSecureContext) {\n // 1. API moderna\n navigator.clipboard.writeText(text);\n } else {\n // 2. Fallback usando textarea\n const textarea = document.createElement('textarea');\n textarea.value = text;\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n }\n \n // Mostrar éxito\n setSnackbarMessage(options?.message || '¡Copiado al portapapeles!');\n setSnackbarSeverity(options?.severity || 'success');\n setSnackbarPosition(options?.position || { vertical: 'bottom', horizontal: 'center' });\n setSnackbarDuration(options?.duration ?? 3000);\n setSnackbarOpen(true);\n\n } catch (err) {\n // Mostrar error\n console.error('Error al copiar al portapapeles:', err);\n setSnackbarMessage('Error al copiar.');\n setSnackbarSeverity('error');\n setSnackbarPosition(options?.position || { vertical: 'bottom', horizontal: 'center' });\n setSnackbarDuration(options?.duration ?? 3000);\n setSnackbarOpen(true);\n }\n }, [options]); \n\n // --- Lógica del Modo Ref ---\n const copyFromRef = useCallback(() => {\n if (elementRef.current) {\n const textToCopy = elementRef.current.innerText || '';\n executeCopy(textToCopy);\n }\n }, [executeCopy]);\n\n // Adjunta un event listener de clic al elemento referenciado (Modo Ref)\n useEffect(() => {\n const currentElement = elementRef.current;\n if (currentElement) {\n currentElement.style.cursor = 'pointer';\n // Adjuntamos la función que copia el texto del ref\n currentElement.addEventListener('click', copyFromRef);\n }\n\n return () => {\n if (currentElement) {\n currentElement.removeEventListener('click', copyFromRef);\n }\n };\n }, [copyFromRef]);\n\n\n // Componente Snackbar encapsulado. Solo se renderiza si duration es > 0.\n const CopyMessage = () => {\n if (snackbarDuration <= 0) return null; // No renderizar si la duración es 0\n\n return (\n <Snackbar\n open={snackbarOpen}\n autoHideDuration={snackbarDuration}\n onClose={handleSnackbarClose}\n anchorOrigin={snackbarPosition}\n >\n <Alert onClose={handleSnackbarClose} severity={snackbarSeverity} sx={{ width: '100%' }}>\n {snackbarMessage}\n </Alert>\n </Snackbar>\n );\n };\n\n // Retorna ambas funcionalidades para que el usuario elija\n return { ref: elementRef, copy: executeCopy, CopyMessage };\n}\n\nexport default useClipboard;"],"names":["_a"],"mappings":";;;AA0BO,SAAS,aAAa,SAA2B;;AAEtD,QAAM,aAAa,OAAoB,IAAI;AAG3C,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,UAAS,mCAAS,YAAW,2BAA2B;AACtG,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,UAAiC,mCAAS,aAAY,SAAS;AAC/G,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,UAAiD,mCAAS,aAAY,EAAE,UAAU,UAAU,YAAY,SAAA,CAAU;AAClK,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,UAAS,wCAAS,aAAT,YAAqB,GAAI;AAGlF,QAAM,sBAAsB,YAAY,CAAC,OAAsC,WAAoB;AACjG,QAAI,WAAW,aAAa;AAC1B;AAAA,IACF;AACA,oBAAgB,KAAK;AAAA,EACvB,GAAG,CAAA,CAAE;AAOL,QAAM,cAAc,YAAY,CAAC,SAAiB;;AAChD,QAAI;AACF,UAAI,UAAU,aAAa,OAAO,iBAAiB;AAEjD,kBAAU,UAAU,UAAU,IAAI;AAAA,MACpC,OAAO;AAEL,cAAM,WAAW,SAAS,cAAc,UAAU;AAClD,iBAAS,QAAQ;AACjB,iBAAS,KAAK,YAAY,QAAQ;AAClC,iBAAS,OAAA;AACT,iBAAS,YAAY,MAAM;AAC3B,iBAAS,KAAK,YAAY,QAAQ;AAAA,MACpC;AAGA,0BAAmB,mCAAS,YAAW,2BAA2B;AAClE,2BAAoB,mCAAS,aAAY,SAAS;AAClD,2BAAoB,mCAAS,aAAY,EAAE,UAAU,UAAU,YAAY,UAAU;AACrF,2BAAoBA,MAAA,mCAAS,aAAT,OAAAA,MAAqB,GAAI;AAC7C,sBAAgB,IAAI;AAAA,IAEtB,SAAS,KAAK;AAEZ,cAAQ,MAAM,oCAAoC,GAAG;AACrD,yBAAmB,kBAAkB;AACrC,0BAAoB,OAAO;AAC3B,2BAAoB,mCAAS,aAAY,EAAE,UAAU,UAAU,YAAY,UAAU;AACrF,2BAAoB,wCAAS,aAAT,YAAqB,GAAI;AAC7C,sBAAgB,IAAI;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAGZ,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,WAAW,SAAS;AACpB,YAAM,aAAa,WAAW,QAAQ,aAAa;AACnD,kBAAY,UAAU;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAGhB,YAAU,MAAM;AACd,UAAM,iBAAiB,WAAW;AAClC,QAAI,gBAAgB;AAClB,qBAAe,MAAM,SAAS;AAE9B,qBAAe,iBAAiB,SAAS,WAAW;AAAA,IACtD;AAEA,WAAO,MAAM;AACX,UAAI,gBAAgB;AAClB,uBAAe,oBAAoB,SAAS,WAAW;AAAA,MACzD;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAIhB,QAAM,cAAc,MAAM;AACxB,QAAI,oBAAoB,EAAG,QAAO;AAElC,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAM;AAAA,QACN,kBAAkB;AAAA,QAClB,SAAS;AAAA,QACT,cAAc;AAAA,QAEd,UAAA,oBAAC,OAAA,EAAM,SAAS,qBAAqB,UAAU,kBAAkB,IAAI,EAAE,OAAO,OAAA,GAC3E,UAAA,gBAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAGA,SAAO,EAAE,KAAK,YAAY,MAAM,aAAa,YAAA;AAC/C;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Componente Wrapper para demostrar ambos modos de uso del hook useClipboard unificado.
|
|
4
|
+
* @param {object} args - Propiedades del Snackbar que se pasan al hook.
|
|
5
|
+
*/
|
|
6
|
+
interface ClipboardDemoProps {
|
|
7
|
+
message: string;
|
|
8
|
+
severity: 'success' | 'info' | 'warning' | 'error';
|
|
9
|
+
duration: number;
|
|
10
|
+
vertical: 'top' | 'bottom';
|
|
11
|
+
horizontal: 'left' | 'center' | 'right';
|
|
12
|
+
}
|
|
13
|
+
declare const ClipboardUnifiedDemo: React.FC<ClipboardDemoProps>;
|
|
14
|
+
export default ClipboardUnifiedDemo;
|
package/package.json
CHANGED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export declare const BasicClipboardUsageDefinition = "\nimport React from 'react';\nimport { Box, Typography, Paper } from '@mui/material';\nimport ContentCopyIcon from '@mui/icons-material/ContentCopy';\nimport { useClipboard } from './useClipboard'; // Ajusta la ruta si es necesario\n\nexport const BasicClipboardUsageExample = () => {\n const { ref, SnackbarComponent } = useClipboard();\n\n return (\n <Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Typography variant=\"h6\">Haz clic para copiar:</Typography>\n <Paper\n ref={ref}\n sx={{\n p: 2,\n border: '1px solid #ccc',\n borderRadius: '8px',\n backgroundColor: '#f5f5f5',\n cursor: 'pointer',\n '&:hover': {\n backgroundColor: '#e0e0e0',\n },\n }}\n >\n <Typography variant=\"body1\" sx={{ userSelect: 'none' }}>\n Este texto se copiar\u00E1 al portapapeles.\n </Typography>\n <ContentCopyIcon sx={{ fontSize: 16, ml: 1, verticalAlign: 'middle', color: 'text.secondary' }} />\n </Paper>\n <SnackbarComponent /> {/* Renderiza el Snackbar del hook */}\n </Box>\n );\n};\n";
|
|
2
|
-
export declare const CustomMessageClipboardDefinition = "\nimport React from 'react';\nimport { Box, Typography, Paper } from '@mui/material';\nimport ContentCopyIcon from '@mui/icons-material/ContentCopy';\nimport { useClipboard } from './useClipboard'; // Ajusta la ruta si es necesario\n\nexport const CustomMessageClipboardExample = () => {\n const { ref, SnackbarComponent } = useClipboard({\n message: '\u00A1El contenido de la celda ha sido copiado exitosamente!',\n duration: 2500,\n severity: 'info',\n });\n\n return (\n <Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Typography variant=\"h6\">Copia con mensaje personalizado:</Typography>\n <Paper\n ref={ref}\n sx={{\n p: 2,\n border: '1px solid #cce',\n borderRadius: '8px',\n backgroundColor: '#eef',\n cursor: 'pointer',\n '&:hover': {\n backgroundColor: '#dde',\n },\n }}\n >\n <Typography variant=\"body1\" sx={{ userSelect: 'none' }}>\n Informaci\u00F3n importante para copiar.\n </Typography>\n <ContentCopyIcon sx={{ fontSize: 16, ml: 1, verticalAlign: 'middle', color: 'text.secondary' }} />\n </Paper>\n <SnackbarComponent /> {/* Renderiza el Snackbar del hook */}\n </Box>\n );\n};\n";
|
|
3
|
-
export declare const ErrorHandlingClipboardDefinition = "\nimport React from 'react';\nimport { Box, Typography, Paper, Button } from '@mui/material';\nimport ContentCopyIcon from '@mui/icons-material/ContentCopy';\nimport { useClipboard } from './useClipboard'; // Ajusta la ruta si es necesario\n\nexport const ErrorHandlingClipboardExample = () => {\n // Simular un error forzando la copia de un elemento no existente\n // (Esto es solo para demostrar el path de error del Snackbar, en una app real no har\u00EDas esto)\n const { ref, SnackbarComponent } = useClipboard({\n message: '\u00A1Copiado con \u00E9xito!',\n severity: 'success'\n });\n\n // Para demostrar el error, tendr\u00EDamos que provocar que document.execCommand falle\n // lo cual es dif\u00EDcil de simular consistentemente en un Storybook.\n // El hook ya maneja el catch y cambia la severidad a 'error'.\n // Esta historia se enfoca en c\u00F3mo el snackbar se ver\u00EDa en un error si ocurriera.\n\n return (\n <Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Typography variant=\"h6\">Copia (observa el Snackbar en caso de error):</Typography>\n <Paper\n ref={ref} // Si el ref.current fuera null, se podr\u00EDa ver el mensaje de error.\n sx={{\n p: 2,\n border: '1px solid #eec',\n borderRadius: '8px',\n backgroundColor: '#ffe',\n cursor: 'pointer',\n '&:hover': {\n backgroundColor: '#eed',\n },\n }}\n >\n <Typography variant=\"body1\" sx={{ userSelect: 'none' }}>\n Intenta copiar este texto.\n </Typography>\n <ContentCopyIcon sx={{ fontSize: 16, ml: 1, verticalAlign: 'middle', color: 'text.secondary' }} />\n </Paper>\n <Typography variant=\"caption\" color=\"text.secondary\">\n (El mensaje de error del Snackbar se mostrar\u00EDa si la copia fallara internamente).\n </Typography>\n <SnackbarComponent /> {/* Renderiza el Snackbar del hook */}\n </Box>\n );\n};\n";
|
|
4
|
-
export declare const MultipleClipboardElementsDefinition = "\nimport React from 'react';\nimport { Box, Typography, Paper } from '@mui/material';\nimport ContentCopyIcon from '@mui/icons-material/ContentCopy';\nimport { useClipboard } from './useClipboard'; // Ajusta la ruta si es necesario\n\nexport const MultipleClipboardElementsExample = () => {\n const { ref: ref1, SnackbarComponent: Snackbar1 } = useClipboard({ message: 'URL copiada', severity: 'success' });\n const { ref: ref2, SnackbarComponent: Snackbar2 } = useClipboard({ message: 'ID de producto copiado', severity: 'info', position: { vertical: 'top', horizontal: 'center' } });\n const { ref: ref3, SnackbarComponent: Snackbar3 } = useClipboard({ message: 'Nombre de usuario copiado', severity: 'warning', duration: 1500 });\n\n return (\n <Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Typography variant=\"h6\">M\u00FAltiples elementos con diferentes configuraciones:</Typography>\n <Paper\n ref={ref1}\n sx={{\n p: 2,\n border: '1px solid #ccc',\n borderRadius: '8px',\n backgroundColor: '#f5f5f5',\n cursor: 'pointer',\n '&:hover': { backgroundColor: '#e0e0e0' },\n }}\n >\n <Typography variant=\"body1\" sx={{ userSelect: 'none' }}>\n https://www.ejemplo.com/recurso/123\n </Typography>\n <ContentCopyIcon sx={{ fontSize: 16, ml: 1, verticalAlign: 'middle', color: 'text.secondary' }} />\n </Paper>\n\n <Paper\n ref={ref2}\n sx={{\n p: 2,\n border: '1px solid #cce',\n borderRadius: '8px',\n backgroundColor: '#eef',\n cursor: 'pointer',\n '&:hover': { backgroundColor: '#dde' },\n }}\n >\n <Typography variant=\"body1\" sx={{ userSelect: 'none' }}>\n ID: PROD-7890\n </Typography>\n <ContentCopyIcon sx={{ fontSize: 16, ml: 1, verticalAlign: 'middle', color: 'text.secondary' }} />\n </Paper>\n\n <Paper\n ref={ref3}\n sx={{\n p: 2,\n border: '1px solid #ecc',\n borderRadius: '8px',\n backgroundColor: '#fee',\n cursor: 'pointer',\n '&:hover': { backgroundColor: '#edd' },\n }}\n >\n <Typography variant=\"body1\" sx={{ userSelect: 'none' }}>\n Usuario: Alice_W\n </Typography>\n <ContentCopyIcon sx={{ fontSize: 16, ml: 1, verticalAlign: 'middle', color: 'text.secondary' }} />\n </Paper>\n\n <Snackbar1 />\n <Snackbar2 />\n <Snackbar3 />\n </Box>\n );\n};\n";
|
|
5
|
-
export declare const ClipboardWithNoVisualFeedbackDefinition = "\nimport React from 'react';\nimport { Box, Typography, Paper } from '@mui/material';\nimport ContentCopyIcon from '@mui/icons-material/ContentCopy';\nimport { useClipboard } from './useClipboard'; // Ajusta la ruta si es necesario\n\nexport const ClipboardWithNoVisualFeedbackExample = () => {\n // Configura el hook sin mensaje para que no haya feedback visual expl\u00EDcito\n const { ref } = useClipboard({ duration: 0 }); // duration: 0 para que no se muestre el snackbar\n\n return (\n <Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Typography variant=\"h6\">Copia sin feedback visual (revisa el portapapeles):</Typography>\n <Paper\n ref={ref}\n sx={{\n p: 2,\n border: '1px solid #eee',\n borderRadius: '8px',\n backgroundColor: '#fafafa',\n cursor: 'pointer',\n '&:hover': { backgroundColor: '#e9e9e9' },\n }}\n >\n <Typography variant=\"body1\" sx={{ userSelect: 'none' }}>\n Este texto se copiar\u00E1 silenciosamente.\n </Typography>\n <ContentCopyIcon sx={{ fontSize: 16, ml: 1, verticalAlign: 'middle', color: 'text.secondary' }} />\n </Paper>\n <Typography variant=\"caption\" color=\"text.secondary\">\n (No se mostrar\u00E1 el Snackbar despu\u00E9s de copiar).\n </Typography>\n {/* No renderizamos SnackbarComponent aqu\u00ED */}\n </Box>\n );\n};\n";
|