anteriority-ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/.github/workflows/ci.yml +31 -0
  2. package/.github/workflows/publish.yml +45 -0
  3. package/.npmrc.bak +1 -0
  4. package/.storybook/main.js +15 -0
  5. package/.storybook/preview.jsx +26 -0
  6. package/AnteriorityUI.md +2400 -0
  7. package/CLAUDE.md +91 -0
  8. package/README.md +182 -0
  9. package/assets/anteriorityUI/AnteriorityUI.logo.jpeg +0 -0
  10. package/assets/favicon/anteriorityUI.ico +0 -0
  11. package/common/api/client.js +43 -0
  12. package/common/storage/LocalStorage.js +13 -0
  13. package/common/styles/typography.css +5 -0
  14. package/common/styles/variables.css +192 -0
  15. package/common/theme/muiTheme.js +9 -0
  16. package/features/password-reset/api.js +39 -0
  17. package/features/user-profile/api.js +5 -0
  18. package/hooks/useAdminPasswordReset.js +133 -0
  19. package/index.js +57 -0
  20. package/package.json +57 -0
  21. package/pnpm-workspace.yaml +3 -0
  22. package/screens/components/AlertModal/AlertModal.jsx +259 -0
  23. package/screens/components/AlertModal/AlertModal.module.css +185 -0
  24. package/screens/components/AlertModal/AlertModal.stories.jsx +87 -0
  25. package/screens/components/Avatar/index.jsx +0 -0
  26. package/screens/components/Avatar/index.module.css +0 -0
  27. package/screens/components/Avatar/utils.js +0 -0
  28. package/screens/components/Badge/index.jsx +33 -0
  29. package/screens/components/Badge/index.module.css +18 -0
  30. package/screens/components/Badge/index.stories.jsx +40 -0
  31. package/screens/components/Barnner/index.jsx +21 -0
  32. package/screens/components/Barnner/index.module.css +33 -0
  33. package/screens/components/Barnner/index.stories.jsx +39 -0
  34. package/screens/components/Barnner/utils.js +0 -0
  35. package/screens/components/BlueLink/index.jsx +27 -0
  36. package/screens/components/BlueLink/index.module.css +11 -0
  37. package/screens/components/BlueLink/index.stories.jsx +29 -0
  38. package/screens/components/Breadcrumbs/index.jsx +62 -0
  39. package/screens/components/Breadcrumbs/index.module.css +158 -0
  40. package/screens/components/Breadcrumbs/index.stories.jsx +37 -0
  41. package/screens/components/Button/index.jsx +169 -0
  42. package/screens/components/Button/index.module.css +168 -0
  43. package/screens/components/Button/index.stories.jsx +71 -0
  44. package/screens/components/Button/utils.js +28 -0
  45. package/screens/components/CalendarComponent/CustomDatePicker.jsx +248 -0
  46. package/screens/components/CalendarComponent/CustomDatePicker.module.css +323 -0
  47. package/screens/components/CalendarComponent/CustomDatePicker.stories.jsx +45 -0
  48. package/screens/components/CalendarPicker/Constants.js +12 -0
  49. package/screens/components/CalendarPicker/index.jsx +263 -0
  50. package/screens/components/CalendarPicker/index.module.css +147 -0
  51. package/screens/components/CalendarPicker/index.stories.jsx +45 -0
  52. package/screens/components/CalendarPicker/utils.js +42 -0
  53. package/screens/components/CardV1/index.jsx +165 -0
  54. package/screens/components/CardV1/index.module.css +232 -0
  55. package/screens/components/CardV1/index.stories.jsx +62 -0
  56. package/screens/components/CardV1/utils.js +49 -0
  57. package/screens/components/ChatWindow/index.jsx +251 -0
  58. package/screens/components/ChatWindow/index.module.css +311 -0
  59. package/screens/components/ChatWindow/index.stories.jsx +62 -0
  60. package/screens/components/Checks/index.jsx +31 -0
  61. package/screens/components/Checks/index.module.css +32 -0
  62. package/screens/components/Checks/index.stories.jsx +32 -0
  63. package/screens/components/Container/index.jsx +36 -0
  64. package/screens/components/Container/index.module.css +24 -0
  65. package/screens/components/Container/index.stories.jsx +32 -0
  66. package/screens/components/CopyText/index.jsx +42 -0
  67. package/screens/components/CopyText/index.module.css +52 -0
  68. package/screens/components/CopyText/index.stories.jsx +24 -0
  69. package/screens/components/CountdownBar/constants.js +3 -0
  70. package/screens/components/CountdownBar/index.jsx +81 -0
  71. package/screens/components/CountdownBar/index.module.css +45 -0
  72. package/screens/components/CountdownBar/index.stories.jsx +43 -0
  73. package/screens/components/CountdownBar/utils.js +119 -0
  74. package/screens/components/DateTag/index.jsx +33 -0
  75. package/screens/components/DateTag/index.module.css +12 -0
  76. package/screens/components/DateTag/index.stories.jsx +29 -0
  77. package/screens/components/DesplegablePanel/constants.js +0 -0
  78. package/screens/components/DesplegablePanel/index.jsx +61 -0
  79. package/screens/components/DesplegablePanel/index.module.css +120 -0
  80. package/screens/components/DesplegablePanel/index.stories.jsx +44 -0
  81. package/screens/components/DesplegablePanel/utils.js +0 -0
  82. package/screens/components/DrawPanel/index.jsx +217 -0
  83. package/screens/components/DrawPanel/index.module.css +252 -0
  84. package/screens/components/DrawPanel/index.stories.jsx +66 -0
  85. package/screens/components/DrawPanel/utils.js +0 -0
  86. package/screens/components/Filter/index.jsx +105 -0
  87. package/screens/components/Filter/index.module.css +91 -0
  88. package/screens/components/Filter/index.stories.jsx +46 -0
  89. package/screens/components/Filter/utils.js +0 -0
  90. package/screens/components/FormControls/FormControls.module.css +86 -0
  91. package/screens/components/FormControls/index.jsx +51 -0
  92. package/screens/components/FormControls/index.stories.jsx +36 -0
  93. package/screens/components/Frame/index.jsx +54 -0
  94. package/screens/components/Frame/index.module.css +102 -0
  95. package/screens/components/Frame/index.stories.jsx +37 -0
  96. package/screens/components/Frame/utils.js +0 -0
  97. package/screens/components/GreenHighlight/index.jsx +11 -0
  98. package/screens/components/GreenHighlight/index.module.css +7 -0
  99. package/screens/components/GreenHighlight/index.stories.jsx +26 -0
  100. package/screens/components/GreenHighlight/utils.jsx +45 -0
  101. package/screens/components/HoursPicker/Constants.js +26 -0
  102. package/screens/components/HoursPicker/index.jsx +350 -0
  103. package/screens/components/HoursPicker/index.module.css +99 -0
  104. package/screens/components/HoursPicker/index.stories.jsx +44 -0
  105. package/screens/components/HoursPicker/utils.js +48 -0
  106. package/screens/components/InfoTooltip/index.jsx +59 -0
  107. package/screens/components/InfoTooltip/index.module.css +98 -0
  108. package/screens/components/InfoTooltip/index.stories.jsx +35 -0
  109. package/screens/components/Input/index.jsx +36 -0
  110. package/screens/components/Input/index.module.css +49 -0
  111. package/screens/components/Input/index.stories.jsx +37 -0
  112. package/screens/components/Input/utils.js +0 -0
  113. package/screens/components/Loading/index.jsx +76 -0
  114. package/screens/components/Loading/index.module.css +193 -0
  115. package/screens/components/Loading/index.stories.jsx +38 -0
  116. package/screens/components/Loading/utils.js +0 -0
  117. package/screens/components/MarkdownContent/index.jsx +16 -0
  118. package/screens/components/MarkdownContent/index.module.css +149 -0
  119. package/screens/components/MarkdownContent/index.stories.jsx +30 -0
  120. package/screens/components/MarkdownEditor/index.jsx +430 -0
  121. package/screens/components/MarkdownEditor/index.module.css +192 -0
  122. package/screens/components/MarkdownEditor/index.stories.jsx +50 -0
  123. package/screens/components/Material-UI/Components/InputWithIcon/index.jsx +87 -0
  124. package/screens/components/Material-UI/Components/InputWithIcon/index.stories.jsx +49 -0
  125. package/screens/components/Material-UI/Components/MenuPopover/index.jsx +117 -0
  126. package/screens/components/Material-UI/Components/MenuPopover/index.stories.jsx +36 -0
  127. package/screens/components/Material-UI/Components/MultilinePlaceholder/index.jsx +44 -0
  128. package/screens/components/Material-UI/Components/MultilinePlaceholder/index.stories.jsx +45 -0
  129. package/screens/components/Material-UI/Components/Paginador/index.jsx +79 -0
  130. package/screens/components/Material-UI/Components/Paginador/index.stories.jsx +49 -0
  131. package/screens/components/Material-UI/Components/Search/index.jsx +81 -0
  132. package/screens/components/Material-UI/Components/Search/index.stories.jsx +68 -0
  133. package/screens/components/Material-UI/Components/Selector/index.jsx +100 -0
  134. package/screens/components/Material-UI/Components/Selector/index.module.css +0 -0
  135. package/screens/components/Material-UI/Components/Selector/index.stories.jsx +52 -0
  136. package/screens/components/Material-UI/Components/Selector/utils.js +0 -0
  137. package/screens/components/Material-UI/Components/TextField/index.jsx +61 -0
  138. package/screens/components/Material-UI/Components/TextField/index.stories.jsx +53 -0
  139. package/screens/components/ModalTooltip/ModalTooltip.jsx +122 -0
  140. package/screens/components/ModalTooltip/ModalTooltip.module.css +65 -0
  141. package/screens/components/ModalTooltip/ModalTooltip.stories.jsx +39 -0
  142. package/screens/components/Notification/Error/ErrorMessage.jsx +30 -0
  143. package/screens/components/Notification/Error/ErrorMessage.module.css +57 -0
  144. package/screens/components/Notification/Error/ErrorMessage.stories.jsx +37 -0
  145. package/screens/components/PasswordResetPanel/index.jsx +325 -0
  146. package/screens/components/PasswordResetPanel/index.stories.jsx +26 -0
  147. package/screens/components/PlaceholderButton/index.jsx +169 -0
  148. package/screens/components/PlaceholderButton/index.module.css +162 -0
  149. package/screens/components/PlaceholderButton/index.stories.jsx +69 -0
  150. package/screens/components/PlaceholderButton/utils.js +42 -0
  151. package/screens/components/Profile/Avatar/index.jsx +125 -0
  152. package/screens/components/Profile/Avatar/index.module.css +86 -0
  153. package/screens/components/Profile/Avatar/index.stories.jsx +48 -0
  154. package/screens/components/Profile/Avatar/utils.js +18 -0
  155. package/screens/components/Profile/index.jsx +58 -0
  156. package/screens/components/Profile/index.module.css +0 -0
  157. package/screens/components/Profile/index.stories.jsx +38 -0
  158. package/screens/components/Profile/utils.js +0 -0
  159. package/screens/components/ScrollBar/index.jsx +62 -0
  160. package/screens/components/ScrollBar/index.module.css +78 -0
  161. package/screens/components/ScrollBar/index.stories.jsx +64 -0
  162. package/screens/components/ScrollBar/utils.js +127 -0
  163. package/screens/components/Sidebar/constants.js +16 -0
  164. package/screens/components/Sidebar/index.jsx +399 -0
  165. package/screens/components/Sidebar/index.module.css +405 -0
  166. package/screens/components/Sidebar/index.stories.jsx +86 -0
  167. package/screens/components/Sidebar/utils.js +75 -0
  168. package/screens/components/Skeleton/index.jsx +11 -0
  169. package/screens/components/Skeleton/index.module.css +26 -0
  170. package/screens/components/Skeleton/index.stories.jsx +42 -0
  171. package/screens/components/SubSidebarLayout/index.jsx +44 -0
  172. package/screens/components/SubSidebarLayout/index.module.css +80 -0
  173. package/screens/components/SubSidebarLayout/index.stories.jsx +51 -0
  174. package/screens/components/SummaryCard/index.jsx +50 -0
  175. package/screens/components/SummaryCard/index.module.css +30 -0
  176. package/screens/components/SummaryCard/index.stories.jsx +29 -0
  177. package/screens/components/Swich/constants.js +4 -0
  178. package/screens/components/Swich/index.jsx +85 -0
  179. package/screens/components/Swich/index.module.css +68 -0
  180. package/screens/components/Swich/index.stories.jsx +44 -0
  181. package/screens/components/Swich/utils.js +0 -0
  182. package/screens/components/TableA/index.jsx +156 -0
  183. package/screens/components/TableA/index.module.css +105 -0
  184. package/screens/components/TableA/index.stories.jsx +116 -0
  185. package/screens/components/TableA/utils.js +11 -0
  186. package/screens/components/TableB/index.jsx +222 -0
  187. package/screens/components/TableB/index.module.css +295 -0
  188. package/screens/components/TableB/index.stories.jsx +110 -0
  189. package/screens/components/TableB/utils.js +11 -0
  190. package/screens/components/ToggleCard/index.jsx +64 -0
  191. package/screens/components/ToggleCard/index.module.css +111 -0
  192. package/screens/components/ToggleCard/index.stories.jsx +49 -0
  193. package/screens/components/UserEditPopover/index.jsx +102 -0
  194. package/screens/components/UserEditPopover/index.module.css +97 -0
  195. package/screens/components/UserEditPopover/index.stories.jsx +49 -0
  196. package/scripts/build.mjs +129 -0
  197. package/scripts/check-case.mjs +127 -0
  198. package/scripts/verify-dist.mjs +45 -0
  199. package/vitest.config.js +36 -0
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import { ChevronDown, ChevronRight } from 'lucide-react';
3
+ import styles from './index.module.css';
4
+
5
+ const DesplegablePanel = ({
6
+ title,
7
+ description,
8
+ icon: Icon,
9
+ iconColorVariant,
10
+ iconColor,
11
+ iconBgColor,
12
+ isOpen = false,
13
+ onToggle,
14
+ action,
15
+ children
16
+ }) => {
17
+ const handleRowClick = () => {
18
+ if (onToggle) onToggle();
19
+ };
20
+
21
+ return (
22
+ <div className={styles.panelContainer}>
23
+ <div
24
+ className={styles.settingRow}
25
+ onClick={handleRowClick}
26
+ >
27
+ <div className={styles.settingInfo}>
28
+ {Icon && (
29
+ <div
30
+ className={`${styles.iconWrapper} ${styles[iconColorVariant] || ''}`}
31
+ style={{
32
+ ...(iconColor ? { '--dp-icon-color': iconColor } : {}),
33
+ ...(iconBgColor ? { '--dp-icon-bg': iconBgColor } : {}),
34
+ }}
35
+ >
36
+ <Icon size={20} />
37
+ </div>
38
+ )}
39
+ <div className={styles.textContainer}>
40
+ <h3>{title}</h3>
41
+ {description && <p>{description}</p>}
42
+ </div>
43
+ </div>
44
+
45
+ <div className={styles.rightSection}>
46
+ {action ? action : (
47
+ isOpen ? <ChevronDown size={20} className={styles.arrow} /> : <ChevronRight size={20} className={styles.arrow} />
48
+ )}
49
+ </div>
50
+ </div>
51
+
52
+ {isOpen && children && (
53
+ <div className={styles.reportExpandableContent}>
54
+ {children}
55
+ </div>
56
+ )}
57
+ </div>
58
+ );
59
+ };
60
+
61
+ export default DesplegablePanel;
@@ -0,0 +1,120 @@
1
+ /* Container for the whole panel (expandable variant) */
2
+ .panelContainer {
3
+ display: flex;
4
+ flex-direction: column;
5
+ width: 100%;
6
+ }
7
+
8
+ .settingRow {
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: space-between;
12
+ padding: 1.5rem;
13
+ cursor: pointer;
14
+ transition: all 0.2s ease;
15
+ background-color: transparent;
16
+ border-bottom: 1px solid var(--border-gray-low)
17
+ }
18
+
19
+ .panelContainer:last-child .settingRow {
20
+ border-bottom: none;
21
+ }
22
+
23
+ .settingRow:hover {
24
+ background-color: #adabab7b;
25
+ }
26
+
27
+ .settingInfo {
28
+ display: flex;
29
+ align-items: center;
30
+ gap: 1.5rem;
31
+ }
32
+
33
+ .textContainer h3 {
34
+ margin: 0;
35
+ font-size: 1.05rem;
36
+ font-weight: 600;
37
+ color: #111827;
38
+ }
39
+
40
+ .textContainer p {
41
+ margin: 0.25rem 0 0;
42
+ font-size: 0.875rem;
43
+ color: #6b7280;
44
+ line-height: 1.4;
45
+ }
46
+
47
+ .reportExpandableContent {
48
+ padding: 2rem 1.5rem;
49
+ background-color: #fafafa;
50
+ border-top: 1px solid #f1f5f9;
51
+ border-bottom: 1px solid #f1f5f9;
52
+ animation: fadeIn 0.3s ease-out;
53
+ }
54
+
55
+ @keyframes fadeIn {
56
+ from { opacity: 0; transform: translateY(-5px); }
57
+ to { opacity: 1; transform: translateY(0); }
58
+ }
59
+
60
+ .iconWrapper {
61
+ width: 44px;
62
+ height: 44px;
63
+ border-radius: 12px;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+ flex-shrink: 0;
68
+ }
69
+
70
+ /* Icon Colors */
71
+ .blue { background-color: var(--dp-icon-bg, #eff6ff); color: var(--dp-icon-color, #2563eb); }
72
+ .green { background-color: var(--dp-icon-bg, #f0fdf4); color: var(--dp-icon-color, #16a34a); }
73
+ .purple { background-color: var(--dp-icon-bg, #faf5ff); color: var(--dp-icon-color, #9333ea); }
74
+ .red { background-color: var(--dp-icon-bg, #fef2f2); color: var(--dp-icon-color, #dc2626); }
75
+ .green2 { background-color: var(--dp-icon-bg, #ecfdf5); color: var(--dp-icon-color, #008a1c); }
76
+
77
+ .arrow {
78
+ color: #9ca3af;
79
+ transition: all 0.2s ease;
80
+ }
81
+
82
+ .settingRow:hover .arrow {
83
+ transform: translateX(4px);
84
+ color: #6b7280;
85
+ }
86
+
87
+ .rightSection {
88
+ display: flex;
89
+ align-items: center;
90
+ gap: 10px;
91
+ }
92
+
93
+ /* Responsive */
94
+ @media (max-width: 640px) {
95
+ .settingRow {
96
+ padding: 1rem;
97
+ }
98
+
99
+ .settingInfo {
100
+ gap: 1rem;
101
+ }
102
+
103
+ .textContainer h3 {
104
+ font-size: 0.95rem;
105
+ }
106
+
107
+ .textContainer p {
108
+ font-size: 0.8rem;
109
+ }
110
+
111
+ .iconWrapper {
112
+ width: 36px;
113
+ height: 36px;
114
+ border-radius: 10px;
115
+ }
116
+
117
+ .reportExpandableContent {
118
+ padding: 1.25rem 1rem;
119
+ }
120
+ }
@@ -0,0 +1,44 @@
1
+ import { Bell, Shield, User } from 'lucide-react';
2
+ import DesplegablePanel from './index.jsx';
3
+
4
+ export default {
5
+ title: 'Components/DesplegablePanel',
6
+ component: DesplegablePanel,
7
+ };
8
+
9
+ export const Default = {
10
+ args: {
11
+ title: 'Notificaciones',
12
+ description: 'Administra cómo recibes las alertas del sistema',
13
+ icon: Bell,
14
+ isOpen: false,
15
+ onToggle: () => console.log('toggled'),
16
+ },
17
+ };
18
+
19
+ export const OpenWithContent = {
20
+ args: {
21
+ title: 'Seguridad',
22
+ description: 'Configura las opciones de seguridad de tu cuenta',
23
+ icon: Shield,
24
+ iconColorVariant: 'green',
25
+ isOpen: true,
26
+ onToggle: () => console.log('toggled'),
27
+ children: (
28
+ <p style={{ margin: 0 }}>
29
+ Aquí puedes activar la autenticación en dos pasos y revisar tus sesiones activas.
30
+ </p>
31
+ ),
32
+ },
33
+ };
34
+
35
+ export const WithCustomAction = {
36
+ args: {
37
+ title: 'Perfil',
38
+ description: 'Información pública de tu cuenta',
39
+ icon: User,
40
+ isOpen: false,
41
+ action: <span style={{ fontSize: '0.8rem', color: '#64748b' }}>Editar</span>,
42
+ onToggle: () => console.log('toggled'),
43
+ },
44
+ };
File without changes
@@ -0,0 +1,217 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { ChevronRight, ChevronUp } from 'lucide-react'; // <-- Cambiado de X a ChevronRight y ChevronUp
4
+ import styles from './index.module.css';
5
+
6
+ import Button from '../Button/index';
7
+ import CountdownBar from '../CountdownBar/index';
8
+
9
+ const DrawPanel = ({
10
+ isOpen,
11
+ onClose,
12
+ title,
13
+ description,
14
+ descriptionPosition = 'top',
15
+ children,
16
+ width = '400px',
17
+ showActions = false,
18
+ onConfirm,
19
+ confirmText = 'Confirmar',
20
+ cancelText = 'Cancelar',
21
+ formId,
22
+ loading = false,
23
+ success = undefined,
24
+ className = '',
25
+ showCountdown = false,
26
+ countdownActive = true,
27
+ countdownDuration,
28
+ countdownOnComplete,
29
+ countdownExpiresAt,
30
+ countdownTotalDuration,
31
+ countdownValue,
32
+ countdownHeight,
33
+ countdownColors,
34
+ countdownIncrement = true,
35
+ progressMode = 'count',
36
+ totalFields = 0,
37
+ filledFields = 0,
38
+ totalItems = [],
39
+ selectedItems = [],
40
+ formData,
41
+ requiredFields = [],
42
+ panelColor = '#ffffff',
43
+ textColor = '#0f172a',
44
+ mutedTextColor = '#64748b',
45
+ borderColor = '#e2e8f0',
46
+ hasUnsavedChanges,
47
+ closeConfirmMessage = 'Vuelve a hacer clic fuera para cerrar sin guardar',
48
+ anchor = 'right'
49
+ }) => {
50
+ const [render, setRender] = useState(isOpen);
51
+ const [pendingClose, setPendingClose] = useState(false);
52
+ const pendingCloseTimeoutRef = useRef(null);
53
+
54
+ useEffect(() => {
55
+ if (isOpen) setRender(true);
56
+ }, [isOpen]);
57
+
58
+ useEffect(() => {
59
+ return () => {
60
+ if (pendingCloseTimeoutRef.current) clearTimeout(pendingCloseTimeoutRef.current);
61
+ };
62
+ }, []);
63
+
64
+ const onAnimationEnd = () => {
65
+ if (!isOpen) {
66
+ setRender(false);
67
+ setPendingClose(false);
68
+ if (pendingCloseTimeoutRef.current) {
69
+ clearTimeout(pendingCloseTimeoutRef.current);
70
+ pendingCloseTimeoutRef.current = null;
71
+ }
72
+ }
73
+ };
74
+
75
+ if (!render) return null;
76
+
77
+ // Contenido "sucio": si hay datos cargados en el formulario, un solo clic (fuera
78
+ // o en el botón de cerrar) no cierra el panel, para no perder lo que el usuario
79
+ // escribió; requiere un segundo clic. Sin datos, el panel se cierra normalmente.
80
+ const formHasValues = formData
81
+ ? Object.values(formData).some((val) => val !== undefined && val !== null && String(val).trim() !== '')
82
+ : false;
83
+ const isDirty = hasUnsavedChanges ?? formHasValues;
84
+
85
+ const handleCloseClick = () => {
86
+ if (!isDirty) {
87
+ onClose();
88
+ return;
89
+ }
90
+ if (pendingClose) {
91
+ if (pendingCloseTimeoutRef.current) clearTimeout(pendingCloseTimeoutRef.current);
92
+ setPendingClose(false);
93
+ onClose();
94
+ return;
95
+ }
96
+ setPendingClose(true);
97
+ pendingCloseTimeoutRef.current = setTimeout(() => setPendingClose(false), 2500);
98
+ };
99
+
100
+ // Lógica Genérica de Cálculo de Progreso
101
+ let calculatedProgress = countdownValue ?? 0;
102
+
103
+ if (progressMode === 'selection') {
104
+ const total = Array.isArray(totalItems) ? totalItems.length : Number(totalItems) || 0;
105
+ const selected = Array.isArray(selectedItems) ? selectedItems.length : Number(selectedItems) || 0;
106
+ calculatedProgress = total > 0 ? (selected / total) * 100 : 0;
107
+ } else if (progressMode === 'form' && formData && Array.isArray(requiredFields) && requiredFields.length > 0) {
108
+ const filledCount = requiredFields.filter(field => {
109
+ const val = formData[field];
110
+ return val !== undefined && val !== null && String(val).trim() !== '';
111
+ }).length;
112
+ calculatedProgress = (filledCount / requiredFields.length) * 100;
113
+ } else if (progressMode === 'count' && totalFields > 0) {
114
+ calculatedProgress = Math.min(100, Math.max(0, (filledFields / totalFields) * 100));
115
+ }
116
+
117
+ const overlayClass = anchor === 'top' ? styles.overlayTop : styles.overlay;
118
+ const panelClass = anchor === 'top' ? styles.panelTop : styles.panel;
119
+ const animClass = isOpen
120
+ ? (anchor === 'top' ? styles.slideInTop : styles.slideIn)
121
+ : (anchor === 'top' ? styles.slideOutTop : styles.slideOut);
122
+ const CloseIcon = anchor === 'top' ? ChevronUp : ChevronRight;
123
+
124
+ return createPortal(
125
+ <div
126
+ className={`${overlayClass} ${isOpen ? styles.fadeIn : styles.fadeOut}`}
127
+ onClick={handleCloseClick}
128
+ onAnimationEnd={onAnimationEnd}
129
+ >
130
+ <div
131
+ className={`${panelClass} ${animClass} ${className}`}
132
+ style={{ maxWidth: width, backgroundColor: panelColor }}
133
+ onClick={(e) => e.stopPropagation()}
134
+ >
135
+ {pendingClose && (
136
+ <div className={styles.closeHint}>{closeConfirmMessage}</div>
137
+ )}
138
+
139
+ <div className={styles.header} style={{ backgroundColor: panelColor, borderBottomColor: borderColor }}>
140
+ <div className={styles.headerTitleContainer}>
141
+ <h3 className={styles.title} style={{ color: textColor }}>{title}</h3>
142
+ {description && descriptionPosition === 'top' && (
143
+ <p className={styles.description} style={{ color: mutedTextColor }}>{description}</p>
144
+ )}
145
+ </div>
146
+ <div className={styles.closeButtonWrapper}>
147
+ <Button
148
+ variant={Button.VARIANTS.DANGER}
149
+ color={Button.COLORS.TRANSPARENT}
150
+ onClick={handleCloseClick}
151
+ circle
152
+ aria-label="Cerrar panel"
153
+ >
154
+ <CloseIcon size={24} color={mutedTextColor} />
155
+ </Button>
156
+ </div>
157
+ </div>
158
+
159
+ {showCountdown && (
160
+ <div style={{ padding: '0 1.5rem', width: '100%', boxSizing: 'border-box' }}>
161
+ <CountdownBar
162
+ active={countdownActive}
163
+ duration={countdownDuration}
164
+ onComplete={countdownOnComplete}
165
+ expiresAt={countdownExpiresAt}
166
+ totalDuration={countdownTotalDuration}
167
+ value={calculatedProgress}
168
+ height={countdownHeight}
169
+ colors={countdownColors}
170
+ width="100%"
171
+ increment={countdownIncrement}
172
+ />
173
+ </div>
174
+ )}
175
+
176
+ <div className={styles.content}>
177
+ {children}
178
+ {description && descriptionPosition === 'bottom' && (
179
+ <div className={styles.descriptionBottom} style={{ borderTopColor: borderColor }}>
180
+ <h4 className={styles.descriptionBottomTitle} style={{ color: textColor }}>Descripción de Tarea</h4>
181
+ <p className={styles.descriptionBottomText} style={{ color: mutedTextColor }}>{description}</p>
182
+ </div>
183
+ )}
184
+ </div>
185
+
186
+ {showActions && (
187
+ <div className={styles.footer} style={{ backgroundColor: panelColor, borderTopColor: borderColor }}>
188
+ <Button
189
+ variant=""
190
+ onClick={onClose}
191
+ border={Button.COLORS.RED}
192
+ disabled={loading}
193
+ fullWidth
194
+ >
195
+ {cancelText}
196
+ </Button>
197
+ <Button
198
+ type={formId ? 'submit' : 'button'}
199
+ form={formId}
200
+ variant="primary"
201
+ color={Button.COLORS.PRIMARY}
202
+ onClick={onConfirm}
203
+ loading={loading}
204
+ success={success}
205
+ fullWidth
206
+ >
207
+ {confirmText}
208
+ </Button>
209
+ </div>
210
+ )}
211
+ </div>
212
+ </div>,
213
+ document.body
214
+ );
215
+ };
216
+
217
+ export default DrawPanel;
@@ -0,0 +1,252 @@
1
+ .overlay {
2
+ position: fixed;
3
+ inset: 0;
4
+ background-color: rgba(15, 23, 42, 0.4);
5
+ backdrop-filter: blur(4px);
6
+ z-index: 1000;
7
+ display: flex;
8
+ justify-content: flex-end;
9
+ }
10
+
11
+ .fadeIn {
12
+ animation: fadeIn 0.3s ease-out forwards;
13
+ }
14
+
15
+ .fadeOut {
16
+ animation: fadeOut 0.3s ease-in forwards;
17
+ }
18
+
19
+ @keyframes fadeIn {
20
+ from {
21
+ opacity: 0;
22
+ }
23
+ to {
24
+ opacity: 1;
25
+ }
26
+ }
27
+
28
+ @keyframes fadeOut {
29
+ from {
30
+ opacity: 1;
31
+ }
32
+ to {
33
+ opacity: 0;
34
+ }
35
+ }
36
+
37
+ .panel {
38
+ background-color: #ffffff;
39
+ height: 100%;
40
+ width: 100%;
41
+ box-shadow: -10px 0 25px rgba(0, 0, 0, 0.1);
42
+ display: flex;
43
+ flex-direction: column;
44
+ position: relative;
45
+ }
46
+
47
+ .slideIn {
48
+ animation: slideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
49
+ }
50
+
51
+ .slideOut {
52
+ animation: slideOut 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
53
+ }
54
+
55
+ @keyframes slideIn {
56
+ from {
57
+ transform: translateX(100%);
58
+ }
59
+ to {
60
+ transform: translateX(0);
61
+ }
62
+ }
63
+
64
+ @keyframes slideOut {
65
+ from {
66
+ transform: translateX(0);
67
+ }
68
+ to {
69
+ transform: translateX(100%);
70
+ }
71
+ }
72
+
73
+ .closeHint {
74
+ position: absolute;
75
+ top: 14px;
76
+ left: 50%;
77
+ transform: translateX(-50%);
78
+ background-color: #0f172a;
79
+ color: #ffffff;
80
+ font-size: 0.78rem;
81
+ font-weight: 600;
82
+ padding: 8px 16px;
83
+ border-radius: 999px;
84
+ box-shadow: 0 6px 16px rgba(15, 23, 42, 0.25);
85
+ z-index: 5;
86
+ pointer-events: none;
87
+ white-space: nowrap;
88
+ animation: closeHintFade 2.5s ease forwards;
89
+ }
90
+
91
+ @keyframes closeHintFade {
92
+ 0% {
93
+ opacity: 0;
94
+ transform: translate(-50%, -6px);
95
+ }
96
+ 10% {
97
+ opacity: 1;
98
+ transform: translate(-50%, 0);
99
+ }
100
+ 80% {
101
+ opacity: 1;
102
+ }
103
+ 100% {
104
+ opacity: 0;
105
+ }
106
+ }
107
+
108
+ .header {
109
+ display: flex;
110
+ align-items: flex-start;
111
+ justify-content: space-between;
112
+ padding: 1.25rem 1.5rem;
113
+ gap: 1rem;
114
+ border-bottom: 1px solid #e2e8f0;
115
+ background-color: #f8fafc;
116
+ }
117
+
118
+ .headerTitleContainer {
119
+ display: flex;
120
+ flex-direction: column;
121
+ gap: 0.35rem;
122
+ }
123
+
124
+ .title {
125
+ margin: 0;
126
+ font-size: 1.2rem;
127
+ font-weight: 700;
128
+ color: #0f172a;
129
+ }
130
+
131
+ .description {
132
+ margin: 0;
133
+ font-size: 0.85rem;
134
+ color: #64748b;
135
+ line-height: 1.4;
136
+ }
137
+
138
+ .closeButtonWrapper {
139
+ flex-shrink: 0;
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: center;
143
+ }
144
+
145
+ .closeBtn {
146
+ background: transparent;
147
+ border: none;
148
+ color: #64748b;
149
+ cursor: pointer;
150
+ padding: 6px;
151
+ border-radius: 50%;
152
+ display: flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ transition: all 0.2s;
156
+ }
157
+
158
+ .closeBtn:hover {
159
+ background-color: #e2e8f0;
160
+ color: #0f172a;
161
+ }
162
+
163
+ .content {
164
+ flex: 1;
165
+ overflow-y: auto;
166
+ padding: 1.5rem;
167
+ -ms-overflow-style: none; /* IE and Edge */
168
+ scrollbar-width: none; /* Firefox */
169
+ }
170
+
171
+ .content::-webkit-scrollbar {
172
+ display: none; /* Chrome, Safari and Opera */
173
+ }
174
+
175
+ .footer {
176
+ display: flex;
177
+ justify-content: flex-end;
178
+ gap: 0.75rem;
179
+ padding: 1rem 1.5rem;
180
+ border-top: 1px solid #e2e8f0;
181
+ background-color: #f8fafc;
182
+ }
183
+
184
+ .descriptionBottom {
185
+ margin-top: 1.5rem;
186
+ border-top: 1px solid #e2e8f0;
187
+ padding-top: 1.5rem;
188
+ display: flex;
189
+ flex-direction: column;
190
+ }
191
+
192
+ .descriptionBottomTitle {
193
+ font-weight: 600;
194
+ color: #1e293b;
195
+ font-size: 0.85rem;
196
+ margin: 0;
197
+ }
198
+
199
+ .descriptionBottomText {
200
+ font-size: 0.8rem;
201
+ color: #64748b;
202
+ margin-top: 6px;
203
+ line-height: 1.45;
204
+ }
205
+
206
+ /* Top anchor styles */
207
+ .overlayTop {
208
+ position: fixed;
209
+ inset: 0;
210
+ background-color: rgba(15, 23, 42, 0.4);
211
+ backdrop-filter: blur(4px);
212
+ z-index: 1000;
213
+ display: flex;
214
+ flex-direction: column;
215
+ justify-content: flex-start;
216
+ align-items: center;
217
+ }
218
+
219
+ .panelTop {
220
+ background-color: #ffffff;
221
+ width: 100%;
222
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
223
+ display: flex;
224
+ flex-direction: column;
225
+ position: relative;
226
+ }
227
+
228
+ .slideInTop {
229
+ animation: slideInTop 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
230
+ }
231
+
232
+ .slideOutTop {
233
+ animation: slideOutTop 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
234
+ }
235
+
236
+ @keyframes slideInTop {
237
+ from {
238
+ transform: translateY(-100%);
239
+ }
240
+ to {
241
+ transform: translateY(0);
242
+ }
243
+ }
244
+
245
+ @keyframes slideOutTop {
246
+ from {
247
+ transform: translateY(0);
248
+ }
249
+ to {
250
+ transform: translateY(-100%);
251
+ }
252
+ }