pixelon 0.1.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 +54 -0
- package/dist/App.d.ts +3 -0
- package/dist/App.js +6 -0
- package/dist/assets/output-BpGRJPjR.css +1 -0
- package/dist/assets/output-DGLCW84k.css +1 -0
- package/dist/components/atoms/AttachmentPreview/AttachmentPreview.d.ts +8 -0
- package/dist/components/atoms/AttachmentPreview/AttachmentPreview.js +44 -0
- package/dist/components/atoms/Avatar/Avatar.d.ts +10 -0
- package/dist/components/atoms/Avatar/Avatar.js +28 -0
- package/dist/components/atoms/Button/Button.d.ts +10 -0
- package/dist/components/atoms/Button/Button.js +137 -0
- package/dist/components/atoms/DateDivider/DateDivider.d.ts +7 -0
- package/dist/components/atoms/DateDivider/DateDivider.js +40 -0
- package/dist/components/atoms/DateDivider/index.d.ts +2 -0
- package/dist/components/atoms/DateDivider/index.js +1 -0
- package/dist/components/atoms/Input/Input.d.ts +10 -0
- package/dist/components/atoms/Input/Input.js +111 -0
- package/dist/components/atoms/Link/Link.d.ts +14 -0
- package/dist/components/atoms/Link/Link.js +16 -0
- package/dist/components/atoms/SelectMenu/SelectMenu.d.ts +9 -0
- package/dist/components/atoms/SelectMenu/SelectMenu.js +23 -0
- package/dist/components/atoms/Text/Text.d.ts +11 -0
- package/dist/components/atoms/Text/Text.js +93 -0
- package/dist/components/atoms/Textarea/Textarea.d.ts +6 -0
- package/dist/components/atoms/Textarea/Textarea.js +5 -0
- package/dist/components/atoms/logo/Logo.d.ts +6 -0
- package/dist/components/atoms/logo/Logo.js +5 -0
- package/dist/components/molecules/chat/AssistantMessage/AssistantMessage.d.ts +7 -0
- package/dist/components/molecules/chat/AssistantMessage/AssistantMessage.js +10 -0
- package/dist/components/molecules/chat/AssistantThinkingIndicator/AssistantThinkingIndicator.d.ts +3 -0
- package/dist/components/molecules/chat/AssistantThinkingIndicator/AssistantThinkingIndicator.js +36 -0
- package/dist/components/molecules/chat/ChatHeader/ChatHeader.d.ts +6 -0
- package/dist/components/molecules/chat/ChatHeader/ChatHeader.js +6 -0
- package/dist/components/molecules/chat/ChatInput/ChatInput.d.ts +17 -0
- package/dist/components/molecules/chat/ChatInput/ChatInput.js +56 -0
- package/dist/components/molecules/chat/FileViewer/FileViewer.d.ts +7 -0
- package/dist/components/molecules/chat/FileViewer/FileViewer.js +41 -0
- package/dist/components/molecules/chat/FullscreenViewer/FullscreenViewer.d.ts +10 -0
- package/dist/components/molecules/chat/FullscreenViewer/FullscreenViewer.js +65 -0
- package/dist/components/molecules/chat/MessageStatus/MessageStatus.d.ts +9 -0
- package/dist/components/molecules/chat/MessageStatus/MessageStatus.js +14 -0
- package/dist/components/molecules/chat/Modal/Modal.d.ts +12 -0
- package/dist/components/molecules/chat/Modal/Modal.js +29 -0
- package/dist/components/molecules/chat/UserAttachments/UserAttachments.d.ts +6 -0
- package/dist/components/molecules/chat/UserAttachments/UserAttachments.js +8 -0
- package/dist/components/molecules/chat/UserMessage/UserMessage.d.ts +11 -0
- package/dist/components/molecules/chat/UserMessage/UserMessage.js +8 -0
- package/dist/components/organisms/chat/Chat.d.ts +21 -0
- package/dist/components/organisms/chat/Chat.js +278 -0
- package/dist/components/organisms/chat/index.d.ts +1 -0
- package/dist/components/organisms/chat/index.js +1 -0
- package/dist/index.d.ts +171 -0
- package/dist/index.esm.js +21664 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +21666 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +6 -0
- package/dist/styles.css +1 -0
- package/dist/styles.css.map +1 -0
- package/dist/test/setup.d.ts +1 -0
- package/dist/test/setup.js +20 -0
- package/package.json +95 -0
- package/src/styles/_variables.scss +84 -0
- package/src/styles/global.scss +58 -0
- package/src/styles/index.scss +30 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const inputStyles = {
|
|
3
|
+
wrapper: {
|
|
4
|
+
display: 'flex',
|
|
5
|
+
flexDirection: 'column',
|
|
6
|
+
gap: 'var(--spacing-xs)',
|
|
7
|
+
},
|
|
8
|
+
wrapperFullWidth: {
|
|
9
|
+
width: '100%',
|
|
10
|
+
},
|
|
11
|
+
wrapperError: {
|
|
12
|
+
// Los estilos de error se aplican al input interno
|
|
13
|
+
},
|
|
14
|
+
wrapperChat: {
|
|
15
|
+
// Los estilos de chat se aplican al input interno
|
|
16
|
+
},
|
|
17
|
+
label: {
|
|
18
|
+
fontSize: 'var(--font-size-sm)',
|
|
19
|
+
fontWeight: 'var(--font-weight-medium)',
|
|
20
|
+
color: 'var(--color-text)',
|
|
21
|
+
},
|
|
22
|
+
container: {
|
|
23
|
+
position: 'relative',
|
|
24
|
+
display: 'flex',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
},
|
|
27
|
+
icon: {
|
|
28
|
+
position: 'absolute',
|
|
29
|
+
left: 'var(--spacing-md)',
|
|
30
|
+
display: 'flex',
|
|
31
|
+
alignItems: 'center',
|
|
32
|
+
justifyContent: 'center',
|
|
33
|
+
color: 'var(--color-text-secondary)',
|
|
34
|
+
},
|
|
35
|
+
error: {
|
|
36
|
+
fontSize: 'var(--font-size-sm)',
|
|
37
|
+
color: 'var(--color-error)',
|
|
38
|
+
},
|
|
39
|
+
input: {
|
|
40
|
+
width: '100%',
|
|
41
|
+
padding: 'var(--spacing-sm) var(--spacing-md)',
|
|
42
|
+
fontFamily: 'var(--font-family-base)',
|
|
43
|
+
fontSize: 'var(--font-size-base)',
|
|
44
|
+
lineHeight: 'var(--line-height-normal)',
|
|
45
|
+
color: 'var(--color-text)',
|
|
46
|
+
backgroundColor: 'var(--color-background)',
|
|
47
|
+
border: '1px solid var(--color-border)',
|
|
48
|
+
borderRadius: 'var(--border-radius-md)',
|
|
49
|
+
transition: 'all 0.2s ease-in-out',
|
|
50
|
+
'&::placeholder': {
|
|
51
|
+
color: 'var(--color-text-secondary)',
|
|
52
|
+
},
|
|
53
|
+
'&:focus': {
|
|
54
|
+
outline: 'none',
|
|
55
|
+
borderColor: 'var(--color-primary)',
|
|
56
|
+
boxShadow: '0 0 0 2px rgba(0, 0, 0, 0.1)',
|
|
57
|
+
},
|
|
58
|
+
'&:disabled': {
|
|
59
|
+
backgroundColor: 'var(--color-background-alt)',
|
|
60
|
+
cursor: 'not-allowed',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
inputWithIcon: {
|
|
64
|
+
paddingLeft: 'calc(var(--spacing-md) * 2 + 16px)',
|
|
65
|
+
},
|
|
66
|
+
inputChat: {
|
|
67
|
+
backgroundColor: 'var(--color-chat-input-bg)',
|
|
68
|
+
border: '1px solid var(--color-chat-input-border)',
|
|
69
|
+
borderRadius: 'var(--border-radius-lg)',
|
|
70
|
+
padding: '0 var(--spacing-lg)',
|
|
71
|
+
fontSize: 'var(--font-size-base)',
|
|
72
|
+
lineHeight: 'var(--line-height-relaxed)',
|
|
73
|
+
height: '40px',
|
|
74
|
+
minHeight: '40px',
|
|
75
|
+
boxSizing: 'border-box',
|
|
76
|
+
display: 'flex',
|
|
77
|
+
alignItems: 'center',
|
|
78
|
+
'&:focus': {
|
|
79
|
+
borderColor: 'var(--color-chat-input-focus)',
|
|
80
|
+
boxShadow: '0 0 0 2px rgba(0, 0, 0, 0.1)',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
inputError: {
|
|
84
|
+
borderColor: 'var(--color-error)',
|
|
85
|
+
'&:focus': {
|
|
86
|
+
borderColor: 'var(--color-error)',
|
|
87
|
+
boxShadow: '0 0 0 2px rgba(239, 68, 68, 0.1)',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
const getInputStyles = (props) => {
|
|
92
|
+
const { fullWidth = false, error, variant = 'default', icon } = props;
|
|
93
|
+
const wrapperStyle = {
|
|
94
|
+
...inputStyles.wrapper,
|
|
95
|
+
...(fullWidth && inputStyles.wrapperFullWidth),
|
|
96
|
+
...(error && inputStyles.wrapperError),
|
|
97
|
+
...(variant === 'chat' && inputStyles.wrapperChat),
|
|
98
|
+
};
|
|
99
|
+
const inputStyle = {
|
|
100
|
+
...inputStyles.input,
|
|
101
|
+
...(variant === 'chat' && inputStyles.inputChat),
|
|
102
|
+
...(error && inputStyles.inputError),
|
|
103
|
+
...(icon && inputStyles.inputWithIcon),
|
|
104
|
+
};
|
|
105
|
+
return { wrapperStyle, inputStyle };
|
|
106
|
+
};
|
|
107
|
+
export const Input = ({ label, error, icon, fullWidth = false, variant = 'default', className = '', ...props }) => {
|
|
108
|
+
const { wrapperStyle, inputStyle } = getInputStyles({ fullWidth, error, variant, icon });
|
|
109
|
+
return (_jsxs("div", { style: wrapperStyle, children: [label && (_jsx("label", { style: inputStyles.label, children: label })), _jsxs("div", { style: inputStyles.container, children: [icon && (_jsx("span", { style: inputStyles.icon, children: icon })), _jsx("input", { style: inputStyle, className: className, ...props })] }), error && (_jsx("span", { style: inputStyles.error, children: error }))] }));
|
|
110
|
+
};
|
|
111
|
+
export default Input;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface LinkProps {
|
|
3
|
+
href: string;
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
variant?: 'primary' | 'secondary' | 'accent';
|
|
6
|
+
size?: 'small' | 'medium' | 'large';
|
|
7
|
+
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
8
|
+
underline?: boolean;
|
|
9
|
+
external?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare const Link: React.FC<LinkProps>;
|
|
14
|
+
export default Link;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export const Link = ({ href, children, variant = 'primary', size = 'medium', weight = 'normal', underline = true, external = false, className = '', onClick, }) => {
|
|
3
|
+
const handleClick = (e) => {
|
|
4
|
+
onClick?.(e);
|
|
5
|
+
};
|
|
6
|
+
return (_jsxs("a", { href: href, className: `
|
|
7
|
+
link
|
|
8
|
+
link--${variant}
|
|
9
|
+
link--${size}
|
|
10
|
+
link--${weight}
|
|
11
|
+
${underline ? 'link--underline' : ''}
|
|
12
|
+
${external ? 'link--external' : ''}
|
|
13
|
+
${className}
|
|
14
|
+
`, target: external ? '_blank' : undefined, rel: external ? 'noopener noreferrer' : undefined, onClick: handleClick, children: [children, external && (_jsxs("svg", { className: "link__external-icon", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), _jsx("polyline", { points: "15,3 21,3 21,9" }), _jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }))] }));
|
|
15
|
+
};
|
|
16
|
+
export default Link;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface SelectMenuProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
anchorRef: React.RefObject<HTMLElement>;
|
|
5
|
+
onSelect: (option: string) => void;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const SelectMenu: React.FC<SelectMenuProps>;
|
|
9
|
+
export default SelectMenu;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
export const SelectMenu = ({ open, anchorRef, onSelect, onClose }) => {
|
|
4
|
+
const menuRef = useRef(null);
|
|
5
|
+
const [menuStyle, setMenuStyle] = useState({});
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (open && anchorRef.current && menuRef.current) {
|
|
8
|
+
const rect = anchorRef.current.getBoundingClientRect();
|
|
9
|
+
const menuHeight = menuRef.current.offsetHeight;
|
|
10
|
+
setMenuStyle({
|
|
11
|
+
position: 'absolute',
|
|
12
|
+
top: rect.top + window.scrollY - menuHeight - 8, // 8px de separación
|
|
13
|
+
left: rect.left + window.scrollX,
|
|
14
|
+
zIndex: 1000,
|
|
15
|
+
minWidth: 180,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}, [open, anchorRef]);
|
|
19
|
+
if (!open)
|
|
20
|
+
return null;
|
|
21
|
+
return (_jsx("div", { className: "select-menu__backdrop", onClick: onClose, children: _jsx("div", { className: "select-menu", ref: menuRef, style: menuStyle, onClick: e => e.stopPropagation(), children: _jsx("button", { className: "select-menu__item", onClick: () => { onSelect('attachment'); onClose(); }, children: "Agregar fotos y archivos" }) }) }));
|
|
22
|
+
};
|
|
23
|
+
export default SelectMenu;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface TextProps {
|
|
3
|
+
variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'body' | 'small' | 'caption';
|
|
4
|
+
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
5
|
+
color?: 'primary' | 'secondary' | 'inherit';
|
|
6
|
+
align?: 'left' | 'center' | 'right';
|
|
7
|
+
className?: string;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare const Text: React.FC<TextProps>;
|
|
11
|
+
export default Text;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
const textStyles = {
|
|
3
|
+
base: {
|
|
4
|
+
margin: 0,
|
|
5
|
+
fontFamily: 'var(--font-family-base)',
|
|
6
|
+
},
|
|
7
|
+
variants: {
|
|
8
|
+
h1: {
|
|
9
|
+
fontSize: 'var(--font-size-2xl)',
|
|
10
|
+
lineHeight: 'var(--line-height-tight)',
|
|
11
|
+
fontWeight: 'var(--font-weight-bold)',
|
|
12
|
+
},
|
|
13
|
+
h2: {
|
|
14
|
+
fontSize: 'var(--font-size-xl)',
|
|
15
|
+
lineHeight: 'var(--line-height-tight)',
|
|
16
|
+
fontWeight: 'var(--font-weight-semibold)',
|
|
17
|
+
},
|
|
18
|
+
h3: {
|
|
19
|
+
fontSize: 'var(--font-size-lg)',
|
|
20
|
+
lineHeight: 'var(--line-height-tight)',
|
|
21
|
+
fontWeight: 'var(--font-weight-semibold)',
|
|
22
|
+
},
|
|
23
|
+
h4: {
|
|
24
|
+
fontSize: 'var(--font-size-base)',
|
|
25
|
+
lineHeight: 'var(--line-height-normal)',
|
|
26
|
+
fontWeight: 'var(--font-weight-semibold)',
|
|
27
|
+
},
|
|
28
|
+
body: {
|
|
29
|
+
fontSize: 'var(--font-size-base)',
|
|
30
|
+
lineHeight: 'var(--line-height-normal)',
|
|
31
|
+
},
|
|
32
|
+
small: {
|
|
33
|
+
fontSize: 'var(--font-size-sm)',
|
|
34
|
+
lineHeight: 'var(--line-height-normal)',
|
|
35
|
+
},
|
|
36
|
+
caption: {
|
|
37
|
+
fontSize: 'var(--font-size-xs)',
|
|
38
|
+
lineHeight: 'var(--line-height-normal)',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
weights: {
|
|
42
|
+
normal: {
|
|
43
|
+
fontWeight: 'var(--font-weight-normal)',
|
|
44
|
+
},
|
|
45
|
+
medium: {
|
|
46
|
+
fontWeight: 'var(--font-weight-medium)',
|
|
47
|
+
},
|
|
48
|
+
semibold: {
|
|
49
|
+
fontWeight: 'var(--font-weight-semibold)',
|
|
50
|
+
},
|
|
51
|
+
bold: {
|
|
52
|
+
fontWeight: 'var(--font-weight-bold)',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
colors: {
|
|
56
|
+
primary: {
|
|
57
|
+
color: 'var(--color-text)',
|
|
58
|
+
},
|
|
59
|
+
secondary: {
|
|
60
|
+
color: 'var(--color-text-secondary)',
|
|
61
|
+
},
|
|
62
|
+
inherit: {
|
|
63
|
+
color: 'inherit',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
alignment: {
|
|
67
|
+
left: {
|
|
68
|
+
textAlign: 'left',
|
|
69
|
+
},
|
|
70
|
+
center: {
|
|
71
|
+
textAlign: 'center',
|
|
72
|
+
},
|
|
73
|
+
right: {
|
|
74
|
+
textAlign: 'right',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const getTextStyles = (props) => {
|
|
79
|
+
const { variant = 'body', weight = 'normal', color = 'primary', align = 'left' } = props;
|
|
80
|
+
return {
|
|
81
|
+
...textStyles.base,
|
|
82
|
+
...textStyles.variants[variant],
|
|
83
|
+
...textStyles.weights[weight],
|
|
84
|
+
...textStyles.colors[color],
|
|
85
|
+
...textStyles.alignment[align],
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
export const Text = ({ variant = 'body', weight = 'normal', color = 'primary', align = 'left', className = '', children, }) => {
|
|
89
|
+
const Component = variant.startsWith('h') ? variant : 'p';
|
|
90
|
+
const textStyle = getTextStyles({ variant, weight, color, align });
|
|
91
|
+
return (_jsx(Component, { style: textStyle, className: className, children: children }));
|
|
92
|
+
};
|
|
93
|
+
export default Text;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
3
|
+
className?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
6
|
+
export default Textarea;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export const Textarea = React.forwardRef(({ className = '', ...props }, ref) => (_jsx("textarea", { ref: ref, className: `textarea ${className}`, ...props })));
|
|
4
|
+
Textarea.displayName = 'Textarea';
|
|
5
|
+
export default Textarea;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
export const Logo = ({ className = '' }) => {
|
|
3
|
+
return (_jsx("div", { className: `logo ${className}`, children: _jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("circle", { cx: "12", cy: "12", r: "8", fill: "black" }) }) }));
|
|
4
|
+
};
|
|
5
|
+
export default Logo;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import ReactMarkdown from 'react-markdown';
|
|
3
|
+
import { Text } from '../../../atoms/Text/Text';
|
|
4
|
+
import { Link } from '../../../atoms/Link/Link';
|
|
5
|
+
export const AssistantMessage = ({ content, timestamp, }) => {
|
|
6
|
+
return (_jsx("div", { className: "assistant-message", children: _jsxs("div", { className: "assistant-message__content", children: [_jsxs("div", { className: "assistant-message__bubble", children: [_jsx(Text, { variant: "caption", color: "secondary", className: "assistant-message__role", children: "Assistant" }), _jsx("div", { className: "assistant-message__markdown", children: _jsx(ReactMarkdown, { components: {
|
|
7
|
+
a: ({ href, children }) => (_jsx(Link, { href: href || '#', variant: "primary", size: "medium", weight: "bold", underline: false, children: children })),
|
|
8
|
+
}, children: content }) })] }), _jsx(Text, { variant: "caption", color: "secondary", className: "assistant-message__timestamp", children: timestamp })] }) }));
|
|
9
|
+
};
|
|
10
|
+
export default AssistantMessage;
|
package/dist/components/molecules/chat/AssistantThinkingIndicator/AssistantThinkingIndicator.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { Text } from '../../../atoms/Text/Text';
|
|
4
|
+
const messages = {
|
|
5
|
+
short: ["Revisando...", "Procesando...", "Analizando..."],
|
|
6
|
+
medium: ["Analizando tu consulta...", "Estoy viendo cómo resolverlo...", "Buscando la mejor manera de ayudarte..."],
|
|
7
|
+
long: ["Sigo con esto. Un momento más.", "Estoy en eso, ya te respondo.", "Ya casi lo tengo."],
|
|
8
|
+
veryLong: ["Esto está tardando mas de lo habitual.", "Esto lleva un poco más de tiempo.", "Lleva un poco más de lo normal."],
|
|
9
|
+
};
|
|
10
|
+
const getRandomMessage = (category) => {
|
|
11
|
+
const categoryMessages = messages[category];
|
|
12
|
+
return categoryMessages[Math.floor(Math.random() * categoryMessages.length)];
|
|
13
|
+
};
|
|
14
|
+
export const AssistantThinkingIndicator = () => {
|
|
15
|
+
const [displayedMessage, setDisplayedMessage] = useState(getRandomMessage('short'));
|
|
16
|
+
const [elapsedSeconds, setElapsedSeconds] = useState(0);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const timer = setInterval(() => {
|
|
19
|
+
setElapsedSeconds(prev => prev + 1);
|
|
20
|
+
}, 1000);
|
|
21
|
+
return () => clearInterval(timer);
|
|
22
|
+
}, []);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (elapsedSeconds === 6) {
|
|
25
|
+
setDisplayedMessage(getRandomMessage('medium'));
|
|
26
|
+
}
|
|
27
|
+
else if (elapsedSeconds === 13) {
|
|
28
|
+
setDisplayedMessage(getRandomMessage('long'));
|
|
29
|
+
}
|
|
30
|
+
else if (elapsedSeconds === 21) {
|
|
31
|
+
setDisplayedMessage(getRandomMessage('veryLong'));
|
|
32
|
+
}
|
|
33
|
+
}, [elapsedSeconds]);
|
|
34
|
+
return (_jsx("div", { className: "assistant-thinking-indicator", children: _jsx("div", { className: "assistant-thinking-indicator__content", children: _jsxs("div", { className: "assistant-thinking-indicator__bubble", children: [_jsx(Text, { variant: "caption", color: "secondary", className: "assistant-thinking-indicator__role", children: "Assistant" }), _jsx("div", { className: "assistant-thinking-indicator__text-container", children: _jsx(Text, { variant: "body", color: "primary", className: "assistant-thinking-indicator__wave-text", children: displayedMessage.split('').map((char, index) => (_jsx("span", { style: { animationDelay: `${index * 0.05}s` }, children: char === ' ' ? '\u00A0' : char }, index))) }) })] }) }) }));
|
|
35
|
+
};
|
|
36
|
+
export default AssistantThinkingIndicator;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Logo } from '../../../atoms/logo/Logo';
|
|
3
|
+
export const ChatHeader = ({ title = 'Asistente' }) => {
|
|
4
|
+
return (_jsx("header", { className: "chat-header", children: _jsxs("div", { className: "chat-header__content", children: [_jsx(Logo, { className: "chat-header__logo" }), _jsx("span", { className: "chat-header__title", children: title })] }) }));
|
|
5
|
+
};
|
|
6
|
+
export default ChatHeader;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface ChatInputProps {
|
|
3
|
+
value: string;
|
|
4
|
+
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
|
5
|
+
onSend: () => void;
|
|
6
|
+
onSendWithFiles?: (content: string, files: File[]) => void;
|
|
7
|
+
onInputKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
8
|
+
onInputFocus?: () => void;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
maxFiles?: number;
|
|
11
|
+
accept?: string;
|
|
12
|
+
attachments?: File[];
|
|
13
|
+
onAttachmentsChange?: (files: File[]) => void;
|
|
14
|
+
textareaRef?: React.RefObject<HTMLTextAreaElement>;
|
|
15
|
+
}
|
|
16
|
+
export declare const ChatInput: React.ForwardRefExoticComponent<ChatInputProps & React.RefAttributes<HTMLFormElement>>;
|
|
17
|
+
export default ChatInput;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState, forwardRef } from 'react';
|
|
3
|
+
import { Button } from '../../../atoms/Button/Button';
|
|
4
|
+
import { Textarea } from '../../../atoms/Textarea/Textarea';
|
|
5
|
+
import { SelectMenu } from '../../../atoms/SelectMenu/SelectMenu';
|
|
6
|
+
import { AttachmentPreview } from '../../../atoms/AttachmentPreview/AttachmentPreview';
|
|
7
|
+
import { FullscreenViewer } from '../FullscreenViewer/FullscreenViewer';
|
|
8
|
+
export const ChatInput = forwardRef(({ value, onChange, onSend, onSendWithFiles, onInputKeyDown, onInputFocus, disabled = false, maxFiles = 5, accept = 'image/*,video/*,application/pdf', attachments = [], onAttachmentsChange, textareaRef, }, ref) => {
|
|
9
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
10
|
+
const [isViewerOpen, setIsViewerOpen] = useState(false);
|
|
11
|
+
const [viewerIndex, setViewerIndex] = useState(null);
|
|
12
|
+
const moreBtnRef = useRef(null);
|
|
13
|
+
const fileInputRef = useRef(null);
|
|
14
|
+
const handleMenuSelect = (option) => {
|
|
15
|
+
if (option === 'attachment') {
|
|
16
|
+
fileInputRef.current?.click();
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const handleFileChange = (e) => {
|
|
20
|
+
const files = e.target.files;
|
|
21
|
+
if (files && files.length > 0) {
|
|
22
|
+
const newFiles = Array.from(files).slice(0, maxFiles - attachments.length);
|
|
23
|
+
const updated = [...attachments, ...newFiles].slice(0, maxFiles);
|
|
24
|
+
onAttachmentsChange?.(updated);
|
|
25
|
+
e.target.value = '';
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const handleRemoveAttachment = (index) => {
|
|
29
|
+
const updated = attachments.filter((_, i) => i !== index);
|
|
30
|
+
onAttachmentsChange?.(updated);
|
|
31
|
+
};
|
|
32
|
+
const handleAttachmentClick = (file) => {
|
|
33
|
+
const idx = attachments.findIndex(f => f === file);
|
|
34
|
+
setViewerIndex(idx);
|
|
35
|
+
setIsViewerOpen(true);
|
|
36
|
+
};
|
|
37
|
+
const handleCloseViewer = () => {
|
|
38
|
+
setIsViewerOpen(false);
|
|
39
|
+
setViewerIndex(null);
|
|
40
|
+
};
|
|
41
|
+
const handleChangeIndex = (newIndex) => {
|
|
42
|
+
setViewerIndex(newIndex);
|
|
43
|
+
};
|
|
44
|
+
const handleSend = () => {
|
|
45
|
+
if (attachments.length > 0 && onSendWithFiles) {
|
|
46
|
+
onSendWithFiles(value, attachments);
|
|
47
|
+
onAttachmentsChange?.([]);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
onSend();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
return (_jsxs("form", { ref: ref, className: "chat__input", onSubmit: e => { e.preventDefault(); handleSend(); }, children: [_jsx("input", { type: "file", ref: fileInputRef, style: { display: 'none' }, multiple: maxFiles > 1, accept: accept, onChange: handleFileChange }), _jsxs("div", { className: "chat__input-stack", children: [attachments.length > 0 && (_jsxs("div", { className: "chat__attachments-wrapper", style: { position: 'relative' }, children: [_jsx("div", { className: "chat__attachments", children: attachments.map((file, idx) => (_jsx(AttachmentPreview, { file: file, onRemove: () => handleRemoveAttachment(idx), onClick: () => handleAttachmentClick(file) }, file.name + file.size + idx))) }), _jsxs("div", { className: "chat__attachments-count", children: [attachments.length, " archivo", attachments.length > 1 ? 's' : ''] })] })), _jsx(Textarea, { className: "chat__textarea", placeholder: "Type a message...", value: value, onChange: onChange, onKeyDown: onInputKeyDown, onFocus: onInputFocus, rows: 1, disabled: disabled, ref: textareaRef }), _jsxs("div", { className: "chat__actions", children: [_jsxs("div", { className: "chat__actions-left", children: [_jsx(Button, { type: "button", className: "chat__icon-btn", "aria-label": "Add file", tabIndex: -1, icon: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), _jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }), ref: moreBtnRef, onClick: () => setMenuOpen(true) }), _jsx(SelectMenu, { open: menuOpen, anchorRef: moreBtnRef, onSelect: handleMenuSelect, onClose: () => setMenuOpen(false) })] }), _jsxs("div", { className: "chat__actions-right", children: [_jsx(Button, { type: "button", className: "chat__icon-btn", "aria-label": "Microphone", tabIndex: -1, icon: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { x: "9", y: "2", width: "6", height: "12", rx: "3" }), _jsx("path", { d: "M5 10v2a7 7 0 0 0 14 0v-2" }), _jsx("line", { x1: "12", y1: "22", x2: "12", y2: "18" }), _jsx("line", { x1: "8", y1: "22", x2: "16", y2: "22" })] }) }), _jsx(Button, { variant: "circle", type: "submit", icon: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M12 8v8", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round" }), _jsx("path", { d: "M8 12l4-4 4 4", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })] }), "aria-label": "Send", disabled: disabled })] })] })] }), isViewerOpen && viewerIndex !== null && attachments.length > 0 && (_jsx(FullscreenViewer, { isOpen: isViewerOpen, onClose: handleCloseViewer, files: attachments, currentIndex: viewerIndex, onChangeIndex: handleChangeIndex }))] }));
|
|
54
|
+
});
|
|
55
|
+
ChatInput.displayName = 'ChatInput';
|
|
56
|
+
export default ChatInput;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { FullscreenViewer } from '../FullscreenViewer/FullscreenViewer';
|
|
4
|
+
export const FileViewer = ({ files, onClose, }) => {
|
|
5
|
+
const [currentFileIndex, setCurrentFileIndex] = useState(0);
|
|
6
|
+
const [isViewerOpen, setIsViewerOpen] = useState(false);
|
|
7
|
+
const currentFile = files[currentFileIndex];
|
|
8
|
+
const handleOpenViewer = (index) => {
|
|
9
|
+
setCurrentFileIndex(index);
|
|
10
|
+
setIsViewerOpen(true);
|
|
11
|
+
};
|
|
12
|
+
const handleCloseViewer = () => {
|
|
13
|
+
setIsViewerOpen(false);
|
|
14
|
+
onClose?.();
|
|
15
|
+
};
|
|
16
|
+
const handleChangeIndex = (newIndex) => {
|
|
17
|
+
setCurrentFileIndex(newIndex);
|
|
18
|
+
};
|
|
19
|
+
const handleKeyDown = (e) => {
|
|
20
|
+
if (e.key === 'ArrowLeft') {
|
|
21
|
+
handleChangeIndex((currentFileIndex - 1 + files.length) % files.length);
|
|
22
|
+
}
|
|
23
|
+
else if (e.key === 'ArrowRight') {
|
|
24
|
+
handleChangeIndex((currentFileIndex + 1) % files.length);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
if (!files || files.length === 0)
|
|
28
|
+
return null;
|
|
29
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "file-viewer", children: _jsx("div", { className: "file-viewer__grid", children: files.map((file, index) => {
|
|
30
|
+
const isImage = file.type.startsWith('image/');
|
|
31
|
+
const isVideo = file.type.startsWith('video/');
|
|
32
|
+
const isPdf = file.type === 'application/pdf';
|
|
33
|
+
return (_jsx("div", { className: "file-viewer__item", onClick: () => handleOpenViewer(index), onKeyDown: (e) => {
|
|
34
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
handleOpenViewer(index);
|
|
37
|
+
}
|
|
38
|
+
}, tabIndex: 0, role: "button", "aria-label": `Ver ${file.name} en pantalla completa`, children: isImage ? (_jsxs("div", { className: "file-viewer__image-container", children: [_jsx("img", { src: URL.createObjectURL(file), alt: file.name, className: "file-viewer__image" }), _jsx("div", { className: "file-viewer__overlay", children: _jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "11", cy: "11", r: "8" }), _jsx("path", { d: "m21 21-4.35-4.35" }), _jsx("path", { d: "M11 8v6" }), _jsx("path", { d: "M8 11h6" })] }) })] })) : (_jsxs("div", { className: "file-viewer__file-container", children: [_jsxs("div", { className: "file-viewer__file-icon", children: [isVideo && (_jsxs("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { x: "2", y: "4", width: "20", height: "16", rx: "2" }), _jsx("polygon", { points: "10 9 15 12 10 15 10 9" })] })), isPdf && (_jsxs("div", { className: "file-viewer__pdf-icon", children: [_jsxs("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), _jsx("polyline", { points: "14,2 14,8 20,8" }), _jsx("line", { x1: "16", y1: "13", x2: "8", y2: "13" }), _jsx("line", { x1: "16", y1: "17", x2: "8", y2: "17" }), _jsx("polyline", { points: "10,9 9,9 8,9" })] }), _jsx("span", { children: "PDF" })] })), !isVideo && !isPdf && (_jsxs("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), _jsx("polyline", { points: "14,2 14,8 20,8" }), _jsx("line", { x1: "16", y1: "13", x2: "8", y2: "13" }), _jsx("line", { x1: "16", y1: "17", x2: "8", y2: "17" }), _jsx("polyline", { points: "10,9 9,9 8,9" })] }))] }), _jsxs("div", { className: "file-viewer__file-info", children: [_jsx("div", { className: "file-viewer__file-name", children: file.name }), _jsx("div", { className: "file-viewer__file-type", children: file.type || 'Archivo' })] })] })) }, `${file.name}-${file.size}-${index}`));
|
|
39
|
+
}) }) }), isViewerOpen && currentFile && (_jsx("div", { onKeyDown: handleKeyDown, tabIndex: -1, children: _jsx(FullscreenViewer, { isOpen: isViewerOpen, onClose: handleCloseViewer, files: files, currentIndex: currentFileIndex, onChangeIndex: handleChangeIndex }) }))] }));
|
|
40
|
+
};
|
|
41
|
+
export default FileViewer;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface FullscreenViewerProps {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
files: File[];
|
|
6
|
+
currentIndex: number;
|
|
7
|
+
onChangeIndex: (newIndex: number) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const FullscreenViewer: React.FC<FullscreenViewerProps>;
|
|
10
|
+
export default FullscreenViewer;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { Modal } from '../Modal/Modal';
|
|
4
|
+
import { Button } from '../../../atoms/Button/Button';
|
|
5
|
+
export const FullscreenViewer = ({ isOpen, onClose, files, currentIndex, onChangeIndex, }) => {
|
|
6
|
+
const file = files[currentIndex];
|
|
7
|
+
const [imageLoaded, setImageLoaded] = useState(false);
|
|
8
|
+
const [imageError, setImageError] = useState(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
setImageLoaded(false);
|
|
11
|
+
setImageError(false);
|
|
12
|
+
}, [file]);
|
|
13
|
+
const isImage = file.type.startsWith('image/');
|
|
14
|
+
const isVideo = file.type.startsWith('video/');
|
|
15
|
+
const isPdf = file.type === 'application/pdf';
|
|
16
|
+
const formatSize = (size) => {
|
|
17
|
+
if (size < 1024)
|
|
18
|
+
return `${size} B`;
|
|
19
|
+
if (size < 1024 * 1024)
|
|
20
|
+
return `${(size / 1024).toFixed(1)} KB`;
|
|
21
|
+
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
22
|
+
};
|
|
23
|
+
const getFileIcon = () => {
|
|
24
|
+
if (isVideo) {
|
|
25
|
+
return (_jsxs("svg", { width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { x: "2", y: "4", width: "20", height: "16", rx: "2" }), _jsx("polygon", { points: "10 9 15 12 10 15 10 9" })] }));
|
|
26
|
+
}
|
|
27
|
+
if (isPdf) {
|
|
28
|
+
return (_jsxs("div", { className: "fullscreen-viewer__pdf-icon", children: [_jsxs("svg", { width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), _jsx("polyline", { points: "14,2 14,8 20,8" }), _jsx("line", { x1: "16", y1: "13", x2: "8", y2: "13" }), _jsx("line", { x1: "16", y1: "17", x2: "8", y2: "17" }), _jsx("polyline", { points: "10,9 9,9 8,9" })] }), _jsx("span", { children: "PDF" })] }));
|
|
29
|
+
}
|
|
30
|
+
return (_jsxs("svg", { width: "64", height: "64", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), _jsx("polyline", { points: "14,2 14,8 20,8" }), _jsx("line", { x1: "16", y1: "13", x2: "8", y2: "13" }), _jsx("line", { x1: "16", y1: "17", x2: "8", y2: "17" }), _jsx("polyline", { points: "10,9 9,9 8,9" })] }));
|
|
31
|
+
};
|
|
32
|
+
const handleDownload = () => {
|
|
33
|
+
const url = URL.createObjectURL(file);
|
|
34
|
+
const a = document.createElement('a');
|
|
35
|
+
a.href = url;
|
|
36
|
+
a.download = file.name;
|
|
37
|
+
document.body.appendChild(a);
|
|
38
|
+
a.click();
|
|
39
|
+
document.body.removeChild(a);
|
|
40
|
+
URL.revokeObjectURL(url);
|
|
41
|
+
};
|
|
42
|
+
const handlePrevious = () => {
|
|
43
|
+
onChangeIndex((currentIndex - 1 + files.length) % files.length);
|
|
44
|
+
};
|
|
45
|
+
const handleNext = () => {
|
|
46
|
+
onChangeIndex((currentIndex + 1) % files.length);
|
|
47
|
+
};
|
|
48
|
+
const handleKeyDown = (e) => {
|
|
49
|
+
if (e.key === 'ArrowLeft')
|
|
50
|
+
handlePrevious();
|
|
51
|
+
if (e.key === 'ArrowRight')
|
|
52
|
+
handleNext();
|
|
53
|
+
};
|
|
54
|
+
const renderContent = () => {
|
|
55
|
+
if (isImage) {
|
|
56
|
+
return (_jsxs("div", { className: "fullscreen-viewer__image-container", children: [!imageLoaded && !imageError && (_jsxs("div", { className: "fullscreen-viewer__loading", children: [_jsx("div", { className: "fullscreen-viewer__spinner" }), _jsx("p", { children: "Cargando imagen..." })] })), imageError && (_jsxs("div", { className: "fullscreen-viewer__error", children: [_jsxs("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("line", { x1: "15", y1: "9", x2: "9", y2: "15" }), _jsx("line", { x1: "9", y1: "9", x2: "15", y2: "15" })] }), _jsx("p", { children: "Error al cargar la imagen" })] })), _jsx("img", { src: URL.createObjectURL(file), alt: file.name, className: `fullscreen-viewer__image ${imageLoaded ? 'fullscreen-viewer__image--loaded' : ''}`, onLoad: () => setImageLoaded(true), onError: () => setImageError(true), style: { display: imageError ? 'none' : 'block' } })] }));
|
|
57
|
+
}
|
|
58
|
+
if (isVideo) {
|
|
59
|
+
return (_jsx("div", { className: "fullscreen-viewer__video-container", children: _jsx("video", { controls: true, className: "fullscreen-viewer__video", src: URL.createObjectURL(file), children: "Tu navegador no soporta el elemento de video." }) }));
|
|
60
|
+
}
|
|
61
|
+
return (_jsxs("div", { className: "fullscreen-viewer__file-container", children: [_jsx("div", { className: "fullscreen-viewer__file-icon", children: getFileIcon() }), _jsxs("div", { className: "fullscreen-viewer__file-info", children: [_jsx("h3", { className: "fullscreen-viewer__file-name", children: file.name }), _jsxs("p", { className: "fullscreen-viewer__file-details", children: [file.type || 'Archivo', " \u00B7 ", formatSize(file.size)] }), _jsx(Button, { onClick: handleDownload, className: "fullscreen-viewer__download-btn", icon: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), _jsx("polyline", { points: "7,10 12,15 17,10" }), _jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })] }), children: "Descargar" })] })] }));
|
|
62
|
+
};
|
|
63
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: undefined, showCloseButton: false, closeOnOverlayClick: true, closeOnEscape: true, children: _jsxs("div", { className: "fullscreen-viewer", onKeyDown: handleKeyDown, tabIndex: 0, children: [_jsxs("div", { className: "fullscreen-viewer__floating-header", children: [_jsx("span", { className: "fullscreen-viewer__floating-title", children: file.name }), _jsx("button", { className: "fullscreen-viewer__floating-close", onClick: onClose, "aria-label": "Cerrar visor", type: "button", children: _jsxs("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), _jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] }), files.length > 1 && (_jsxs(_Fragment, { children: [_jsx(Button, { className: "fullscreen-viewer__arrow fullscreen-viewer__arrow--left", variant: "circle", type: "button", onClick: handlePrevious, "aria-label": "Archivo anterior", icon: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "15,18 9,12 15,6" }) }) }), _jsx(Button, { className: "fullscreen-viewer__arrow fullscreen-viewer__arrow--right", variant: "circle", type: "button", onClick: handleNext, "aria-label": "Archivo siguiente", icon: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "9,18 15,12 9,6" }) }) })] })), renderContent(), files.length > 1 && (_jsxs("div", { className: "fullscreen-viewer__counter", children: [currentIndex + 1, " de ", files.length] }))] }) }));
|
|
64
|
+
};
|
|
65
|
+
export default FullscreenViewer;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type MessageStatusType = 'sending' | 'sent' | 'failed';
|
|
3
|
+
interface MessageStatusProps {
|
|
4
|
+
status: MessageStatusType;
|
|
5
|
+
className?: string;
|
|
6
|
+
onRetry?: () => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const MessageStatus: React.FC<MessageStatusProps>;
|
|
9
|
+
export default MessageStatus;
|