anima-ds-nucleus 1.0.8 → 1.0.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anima-ds-nucleus",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Anima Design System - A comprehensive React component library",
5
5
  "author": "Nucleus Labs <ipvasallo@nucleus.com.ar>",
6
6
  "license": "UNLICENSED",
@@ -14,8 +14,7 @@ export const CardError = ({
14
14
  width: '379.5px',
15
15
  height: '476px',
16
16
  borderRadius: '14px',
17
- border: '1px solid #0000001A',
18
- boxShadow: '0px 4px 6px -2px #0000001A, 0px 10px 15px -3px #0000001A',
17
+ border: '1px solid #C4C4C4',
19
18
  opacity: 1
20
19
  }}
21
20
  {...props}
@@ -9,8 +9,7 @@ export const CardSkeleton = ({
9
9
  width: '379.5px',
10
10
  height: '476px',
11
11
  borderRadius: '14px',
12
- border: '1px solid #0000001A',
13
- boxShadow: '0px 4px 6px -2px #0000001A, 0px 10px 15px -3px #0000001A',
12
+ border: '1px solid #C4C4C4',
14
13
  opacity: 1
15
14
  }}
16
15
  {...props}
@@ -13,8 +13,7 @@ export const CardTituloCorto = ({
13
13
  width: '379.5px',
14
14
  height: '476px',
15
15
  borderRadius: '14px',
16
- border: '1px solid #0000001A',
17
- boxShadow: '0px 4px 6px -2px #0000001A, 0px 10px 15px -3px #0000001A',
16
+ border: '1px solid #C4C4C4',
18
17
  opacity: 1
19
18
  }}
20
19
  {...props}
@@ -14,8 +14,7 @@ export const CardTituloCortoMasEstado = ({
14
14
  width: '379.5px',
15
15
  height: '476px',
16
16
  borderRadius: '14px',
17
- border: '1px solid #0000001A',
18
- boxShadow: '0px 4px 6px -2px #0000001A, 0px 10px 15px -3px #0000001A',
17
+ border: '1px solid #C4C4C4',
19
18
  opacity: 1
20
19
  }}
21
20
  {...props}
@@ -13,8 +13,7 @@ export const CardTituloLargo = ({
13
13
  width: '379.5px',
14
14
  height: '476px',
15
15
  borderRadius: '14px',
16
- border: '1px solid #0000001A',
17
- boxShadow: '0px 4px 6px -2px #0000001A, 0px 10px 15px -3px #0000001A',
16
+ border: '1px solid #C4C4C4',
18
17
  opacity: 1
19
18
  }}
20
19
  {...props}
@@ -14,8 +14,7 @@ export const CardTituloLargoMasEstado = ({
14
14
  width: '379.5px',
15
15
  height: '476px',
16
16
  borderRadius: '14px',
17
- border: '1px solid #0000001A',
18
- boxShadow: '0px 4px 6px -2px #0000001A, 0px 10px 15px -3px #0000001A',
17
+ border: '1px solid #C4C4C4',
19
18
  opacity: 1
20
19
  }}
21
20
  {...props}
@@ -13,8 +13,7 @@ export const CardVacia = ({
13
13
  width: '379.5px',
14
14
  height: '476px',
15
15
  borderRadius: '14px',
16
- border: '1px solid #0000001A',
17
- boxShadow: '0px 4px 6px -2px #0000001A, 0px 10px 15px -3px #0000001A',
16
+ border: '1px solid #C4C4C4',
18
17
  opacity: 1
19
18
  }}
20
19
  {...props}
@@ -2,6 +2,7 @@ import { useState } from 'react';
2
2
  import { Icon } from '../../Atoms/Icon/Icon';
3
3
  import { Avatar } from '../../Atoms/Avatar/Avatar';
4
4
  import { Typography } from '../../Atoms/Typography/Typography';
5
+ import { SidebarCoreMobile } from '../Sidebar/SidebarCore';
5
6
 
6
7
  export const HeaderCore = ({
7
8
  searchPlaceholder = 'Buscar empleados, reportes, configuraciones...',
@@ -11,10 +12,21 @@ export const HeaderCore = ({
11
12
  onSearch,
12
13
  onNotificationClick,
13
14
  onUserClick,
15
+ sidebarSections,
16
+ sidebarActiveItem,
17
+ onSidebarItemClick,
18
+ sidebarItemBadges,
19
+ sidebarCompanyName,
20
+ sidebarCompanyLogo,
21
+ sidebarFooterText,
22
+ sidebarFooterCollapsedContent,
23
+ sidebarCoreContainerStyle,
24
+ sidebarCoreTextStyle,
14
25
  className = '',
15
26
  ...props
16
27
  }) => {
17
28
  const [searchValue, setSearchValue] = useState('');
29
+ const [isSidebarOpen, setIsSidebarOpen] = useState(false);
18
30
 
19
31
  const handleSearchChange = (e) => {
20
32
  const value = e.target.value;
@@ -217,6 +229,7 @@ export const HeaderCore = ({
217
229
  <div className="flex items-center space-x-2">
218
230
  {/* Botón menú hamburguesa */}
219
231
  <button
232
+ onClick={() => setIsSidebarOpen(true)}
220
233
  className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
221
234
  aria-label="Menú"
222
235
  >
@@ -340,6 +353,31 @@ export const HeaderCore = ({
340
353
  </div>
341
354
  </div>
342
355
  </div>
356
+
357
+ {/* SidebarCoreMobile - Solo visible en móvil cuando está abierto */}
358
+ {isSidebarOpen && (
359
+ <div className="header-core-mobile-sidebar-wrapper">
360
+ <SidebarCoreMobile
361
+ sections={sidebarSections}
362
+ activeItem={sidebarActiveItem}
363
+ onItemClick={(itemId) => {
364
+ if (onSidebarItemClick) {
365
+ onSidebarItemClick(itemId);
366
+ }
367
+ setIsSidebarOpen(false);
368
+ }}
369
+ itemBadges={sidebarItemBadges}
370
+ companyName={sidebarCompanyName}
371
+ companyLogo={sidebarCompanyLogo}
372
+ footerText={sidebarFooterText}
373
+ footerCollapsedContent={sidebarFooterCollapsedContent}
374
+ coreContainerStyle={sidebarCoreContainerStyle}
375
+ coreTextStyle={sidebarCoreTextStyle}
376
+ isOpen={isSidebarOpen}
377
+ onClose={() => setIsSidebarOpen(false)}
378
+ />
379
+ </div>
380
+ )}
343
381
  </header>
344
382
  );
345
383
  };
@@ -2,6 +2,7 @@ import { useState } from 'react';
2
2
  import { Icon } from '../../Atoms/Icon/Icon';
3
3
  import { Avatar } from '../../Atoms/Avatar/Avatar';
4
4
  import { Typography } from '../../Atoms/Typography/Typography';
5
+ import { SidebarPointMobile } from '../Sidebar/SidebarPoint';
5
6
 
6
7
  export const HeaderPoint = ({
7
8
  searchPlaceholder = 'Buscar empleados, reportes, configuraciones...',
@@ -11,10 +12,17 @@ export const HeaderPoint = ({
11
12
  onSearch,
12
13
  onNotificationClick,
13
14
  onUserClick,
15
+ sidebarSections,
16
+ sidebarActiveItem,
17
+ onSidebarItemClick,
18
+ sidebarItemBadges,
19
+ sidebarCompanyName,
20
+ sidebarCompanyLogo,
14
21
  className = '',
15
22
  ...props
16
23
  }) => {
17
24
  const [searchValue, setSearchValue] = useState('');
25
+ const [isSidebarOpen, setIsSidebarOpen] = useState(false);
18
26
 
19
27
  const handleSearchChange = (e) => {
20
28
  const value = e.target.value;
@@ -65,6 +73,12 @@ export const HeaderPoint = ({
65
73
  background-color: transparent !important;
66
74
  background: transparent !important;
67
75
  border-bottom: none !important;
76
+ width: 100% !important;
77
+ max-width: 100% !important;
78
+ margin: 0 !important;
79
+ padding: 0 !important;
80
+ left: 0 !important;
81
+ right: 0 !important;
68
82
  }
69
83
  header.header-point-mobile[style*="background"],
70
84
  .header-point-mobile[style*="background"],
@@ -72,9 +86,18 @@ export const HeaderPoint = ({
72
86
  background-color: transparent !important;
73
87
  background: transparent !important;
74
88
  }
89
+ header.header-point-mobile > div {
90
+ width: 100% !important;
91
+ max-width: 100% !important;
92
+ padding-left: 0 !important;
93
+ padding-right: 0 !important;
94
+ margin: 0 !important;
95
+ }
75
96
  .header-mobile-layout {
76
97
  display: block !important;
77
98
  position: relative !important;
99
+ width: 100% !important;
100
+ max-width: 100% !important;
78
101
  }
79
102
  .header-mobile-top-row {
80
103
  display: flex !important;
@@ -83,6 +106,9 @@ export const HeaderPoint = ({
83
106
  padding: 24px 24px 12px 24px !important;
84
107
  min-height: 64px !important;
85
108
  height: 64px !important;
109
+ width: 100% !important;
110
+ max-width: 100% !important;
111
+ box-sizing: border-box !important;
86
112
  }
87
113
  .header-mobile-menu-button,
88
114
  .header-mobile-notification-button {
@@ -118,11 +144,12 @@ export const HeaderPoint = ({
118
144
  .header-mobile-search-row {
119
145
  position: relative;
120
146
  z-index: 2 !important;
121
- }
122
- .header-mobile-search-row {
123
147
  display: block;
124
148
  padding: 12px 24px 0 24px;
125
149
  margin-top: -8px;
150
+ width: 100% !important;
151
+ max-width: 100% !important;
152
+ box-sizing: border-box !important;
126
153
  }
127
154
  .header-desktop-layout {
128
155
  display: none !important;
@@ -283,6 +310,7 @@ export const HeaderPoint = ({
283
310
  {/* Lado izquierdo: Menú hamburguesa */}
284
311
  <div className="flex items-center">
285
312
  <button
313
+ onClick={() => setIsSidebarOpen(true)}
286
314
  className="p-2 hover:bg-gray-100 rounded-lg transition-colors header-mobile-menu-button"
287
315
  style={{ cursor: 'pointer', padding: '8px' }}
288
316
  aria-label="Menú"
@@ -402,6 +430,27 @@ export const HeaderPoint = ({
402
430
  </div>
403
431
  </div>
404
432
  </div>
433
+
434
+ {/* SidebarPointMobile - Solo visible en móvil cuando está abierto */}
435
+ {isSidebarOpen && (
436
+ <div className="header-mobile-sidebar-wrapper">
437
+ <SidebarPointMobile
438
+ sections={sidebarSections}
439
+ activeItem={sidebarActiveItem}
440
+ onItemClick={(itemId) => {
441
+ if (onSidebarItemClick) {
442
+ onSidebarItemClick(itemId);
443
+ }
444
+ setIsSidebarOpen(false);
445
+ }}
446
+ itemBadges={sidebarItemBadges}
447
+ companyName={sidebarCompanyName}
448
+ companyLogo={sidebarCompanyLogo}
449
+ isOpen={isSidebarOpen}
450
+ onClose={() => setIsSidebarOpen(false)}
451
+ />
452
+ </div>
453
+ )}
405
454
  </header>
406
455
  );
407
456
  };
@@ -34,13 +34,23 @@ const SidebarCoreMobile = ({
34
34
  footerCollapsedContent, // Contenido a mostrar en el footer cuando está colapsado (puede ser string, ReactNode, o objeto con icon y text)
35
35
  coreContainerStyle, // Estilos personalizados para el contenedor de LogoHexa y "Core"
36
36
  coreTextStyle, // Estilos personalizados para el texto "Core"
37
+ isOpen: controlledIsOpen,
38
+ onClose: controlledOnClose,
37
39
  className = '',
38
40
  ...props
39
41
  }) => {
40
- const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
42
+ const [internalIsOpen, setInternalIsOpen] = useState(false);
43
+
44
+ // Si se proporciona control externo, usarlo; si no, usar estado interno
45
+ const isMobileMenuOpen = controlledIsOpen !== undefined ? controlledIsOpen : internalIsOpen;
46
+ const setIsMobileMenuOpen = controlledOnClose ? controlledOnClose : setInternalIsOpen;
41
47
 
42
48
  const toggleMobileMenu = () => {
43
- setIsMobileMenuOpen(!isMobileMenuOpen);
49
+ if (controlledOnClose) {
50
+ controlledOnClose();
51
+ } else {
52
+ setInternalIsOpen(!internalIsOpen);
53
+ }
44
54
  };
45
55
 
46
56
  // Generar footerCollapsedContent automáticamente si no se proporciona pero sí hay footerText
@@ -79,12 +89,13 @@ const SidebarCoreMobile = ({
79
89
  transition: transform 0.3s ease-in-out;
80
90
  }
81
91
  `}</style>
82
- {/* Botón hamburguesa - solo visible cuando el menú está cerrado */}
83
- {!isMobileMenuOpen && (
92
+ {/* Botón hamburguesa - solo visible cuando el menú está cerrado y no hay control externo */}
93
+ {!isMobileMenuOpen && controlledIsOpen === undefined && (
84
94
  <button
85
95
  onClick={toggleMobileMenu}
86
- className="fixed top-4 left-4 z-50 p-2 bg-white border border-gray-200 rounded-lg shadow-md
96
+ className="fixed top-4 left-4 z-50 p-2 bg-white rounded-lg
87
97
  hover:bg-gray-50 transition-colors"
98
+ style={{ border: '1px solid #C4C4C4' }}
88
99
  aria-label="Toggle menu"
89
100
  >
90
101
  <Icon
@@ -96,14 +107,24 @@ const SidebarCoreMobile = ({
96
107
  </button>
97
108
  )}
98
109
  {/* Overlay para cerrar el menú */}
99
- {isMobileMenuOpen && (
100
- <div
101
- className="fixed inset-0 bg-black bg-opacity-50 z-40"
102
- onClick={toggleMobileMenu}
103
- />
104
- )}
110
+ <div
111
+ className="fixed inset-0 bg-black bg-opacity-50 z-40"
112
+ style={{
113
+ opacity: isMobileMenuOpen ? 1 : 0,
114
+ pointerEvents: isMobileMenuOpen ? 'auto' : 'none',
115
+ transition: 'opacity 0.3s ease-in-out'
116
+ }}
117
+ onClick={() => {
118
+ if (controlledOnClose) {
119
+ controlledOnClose();
120
+ } else {
121
+ toggleMobileMenu();
122
+ }
123
+ }}
124
+ />
105
125
  <aside
106
- className={`bg-white border-r border-gray-200 sidebar-core-mobile ${className}`}
126
+ className={`bg-white sidebar-core-mobile ${className}`}
127
+ style={{ border: '1px solid #C4C4C4', borderRadius: '16px' }}
107
128
  {...props}
108
129
  >
109
130
  <nav className="h-full flex flex-col">
@@ -145,7 +166,13 @@ const SidebarCoreMobile = ({
145
166
 
146
167
  {/* Lado derecho: Botón X */}
147
168
  <button
148
- onClick={toggleMobileMenu}
169
+ onClick={() => {
170
+ if (controlledOnClose) {
171
+ controlledOnClose();
172
+ } else {
173
+ toggleMobileMenu();
174
+ }
175
+ }}
149
176
  className="p-1 hover:bg-gray-100 rounded transition-colors flex-shrink-0"
150
177
  aria-label="Cerrar menú"
151
178
  >
@@ -164,7 +191,11 @@ const SidebarCoreMobile = ({
164
191
  <button
165
192
  onClick={() => {
166
193
  onItemClick && onItemClick('inicio');
167
- toggleMobileMenu();
194
+ if (controlledOnClose) {
195
+ controlledOnClose();
196
+ } else {
197
+ toggleMobileMenu();
198
+ }
168
199
  }}
169
200
  className={`w-full flex items-center cursor-pointer px-4 justify-between py-2.5 rounded-lg transition-all duration-200 ${
170
201
  activeItem === 'inicio'
@@ -232,7 +263,11 @@ const SidebarCoreMobile = ({
232
263
  <button
233
264
  onClick={() => {
234
265
  onItemClick && onItemClick(item.id);
235
- toggleMobileMenu();
266
+ if (controlledOnClose) {
267
+ controlledOnClose();
268
+ } else {
269
+ toggleMobileMenu();
270
+ }
236
271
  }}
237
272
  className={`w-full flex items-center cursor-pointer px-4 justify-between py-2.5 rounded-lg transition-all duration-200 ${
238
273
  activeItem === item.id
@@ -431,9 +466,10 @@ export const SidebarCore = ({
431
466
 
432
467
  return (
433
468
  <aside
434
- className={`bg-white border-r border-gray-200 transition-all duration-300 ease-in-out h-full ${
469
+ className={`bg-white transition-all duration-300 ease-in-out h-full ${
435
470
  isCollapsed ? 'w-20' : 'w-64'
436
471
  } ${className}`}
472
+ style={{ border: '1px solid #C4C4C4', borderRadius: '16px' }}
437
473
  {...props}
438
474
  >
439
475
  <nav className="h-full flex flex-col" style={{ overflow: 'hidden' }}>
@@ -1,4 +1,4 @@
1
- import { useState } from 'react';
1
+ import { useState, useEffect } from 'react';
2
2
  import { Icon } from '../../Atoms/Icon/Icon';
3
3
  import { Typography } from '../../Atoms/Typography/Typography';
4
4
  import { LogoHexa } from '../../Atoms/LogoHexa/LogoHexa';
@@ -331,13 +331,44 @@ const SidebarPointMobile = ({
331
331
  companyLogo,
332
332
  onCompanyClick,
333
333
  itemBadges = {},
334
+ isOpen: controlledIsOpen,
335
+ onClose: controlledOnClose,
334
336
  className = '',
335
337
  ...props
336
338
  }) => {
337
- const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
339
+ const [internalIsOpen, setInternalIsOpen] = useState(false);
340
+ const [shouldAnimate, setShouldAnimate] = useState(false);
341
+
342
+ // Si se proporciona control externo, usarlo; si no, usar estado interno
343
+ const isMobileMenuOpen = controlledIsOpen !== undefined ? controlledIsOpen : internalIsOpen;
344
+ const setIsMobileMenuOpen = controlledOnClose ? controlledOnClose : setInternalIsOpen;
345
+
346
+ // Efecto para manejar la animación cuando se controla externamente
347
+ useEffect(() => {
348
+ if (controlledIsOpen !== undefined) {
349
+ if (controlledIsOpen) {
350
+ // Cuando se abre, primero montamos sin animación, luego activamos la animación
351
+ setShouldAnimate(false);
352
+ // Pequeño delay para asegurar que el DOM esté listo antes de animar
353
+ const timer = setTimeout(() => {
354
+ setShouldAnimate(true);
355
+ }, 10);
356
+ return () => clearTimeout(timer);
357
+ } else {
358
+ // Cuando se cierra, desactivamos la animación inmediatamente
359
+ setShouldAnimate(false);
360
+ }
361
+ } else {
362
+ setShouldAnimate(internalIsOpen);
363
+ }
364
+ }, [controlledIsOpen, internalIsOpen]);
338
365
 
339
366
  const toggleMobileMenu = () => {
340
- setIsMobileMenuOpen(!isMobileMenuOpen);
367
+ if (controlledOnClose) {
368
+ controlledOnClose();
369
+ } else {
370
+ setInternalIsOpen(!internalIsOpen);
371
+ }
341
372
  };
342
373
 
343
374
  // Formatear fecha de hoy
@@ -360,11 +391,20 @@ const SidebarPointMobile = ({
360
391
  top: 0;
361
392
  height: 100vh;
362
393
  z-index: 50;
363
- transition: transform 0.3s ease-in-out;
394
+ transition: ${shouldAnimate ? 'transform 0.3s ease-in-out' : 'none'};
395
+ }
396
+ .sidebar-point-mobile-overlay {
397
+ position: fixed;
398
+ inset: 0;
399
+ background-color: rgba(0, 0, 0, 0.5);
400
+ z-index: 40;
401
+ transition: opacity 0.3s ease-in-out;
402
+ opacity: ${isMobileMenuOpen ? '1' : '0'};
403
+ pointer-events: ${isMobileMenuOpen ? 'auto' : 'none'};
364
404
  }
365
405
  `}</style>
366
- {/* Botón hamburguesa - solo visible cuando el menú está cerrado */}
367
- {!isMobileMenuOpen && (
406
+ {/* Botón hamburguesa - solo visible cuando el menú está cerrado y no hay control externo */}
407
+ {!isMobileMenuOpen && controlledIsOpen === undefined && (
368
408
  <button
369
409
  onClick={toggleMobileMenu}
370
410
  className="fixed top-4 left-4 z-50 p-2 bg-white border border-gray-200 rounded-lg shadow-md
@@ -380,19 +420,23 @@ const SidebarPointMobile = ({
380
420
  </button>
381
421
  )}
382
422
  {/* Overlay para cerrar el menú */}
383
- {isMobileMenuOpen && (
384
- <div
385
- className="fixed inset-0 bg-black bg-opacity-50 z-40"
386
- onClick={toggleMobileMenu}
387
- />
388
- )}
423
+ <div
424
+ className="sidebar-point-mobile-overlay"
425
+ onClick={() => {
426
+ if (controlledOnClose) {
427
+ controlledOnClose();
428
+ } else {
429
+ toggleMobileMenu();
430
+ }
431
+ }}
432
+ />
389
433
  <aside
390
434
  className={`bg-gray-100 border-r border-gray-200 sidebar-point-mobile ${className}`}
391
435
  {...props}
392
436
  >
393
437
  <nav className="h-full flex flex-col">
394
- {/* Header verde con "Point" y botón X */}
395
- <div className="bg-brand px-4 py-4 flex items-center justify-between" style={{ backgroundColor: '#56B676' }}>
438
+ {/* Header MAIN con "Point" y botón X */}
439
+ <div className="px-4 py-4 flex items-center justify-between" style={{ backgroundColor: 'var(--color-main)' }}>
396
440
  <Typography
397
441
  variant="h5"
398
442
  className="color-white font-bold"
@@ -400,8 +444,15 @@ const SidebarPointMobile = ({
400
444
  Point
401
445
  </Typography>
402
446
  <button
403
- onClick={toggleMobileMenu}
447
+ onClick={() => {
448
+ if (controlledOnClose) {
449
+ controlledOnClose();
450
+ } else {
451
+ toggleMobileMenu();
452
+ }
453
+ }}
404
454
  className="p-1 hover:bg-opacity-80 rounded transition-colors"
455
+ style={{ cursor: 'pointer' }}
405
456
  aria-label="Cerrar menú"
406
457
  >
407
458
  <Icon
@@ -421,7 +472,7 @@ const SidebarPointMobile = ({
421
472
  {capitalizedDate}
422
473
  </Typography>
423
474
  </div>
424
- {/* Logo de empresa (si se proporciona) */}
475
+ {/* Logo de empresa (solo si se proporciona) */}
425
476
  {companyLogo && (
426
477
  <div className="bg-gray-100 px-4 py-6 flex justify-center border-b border-gray-200">
427
478
  <img