anima-ds-nucleus 1.0.7 → 1.0.9

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.7",
3
+ "version": "1.0.9",
4
4
  "description": "Anima Design System - A comprehensive React component library",
5
5
  "author": "Nucleus Labs <ipvasallo@nucleus.com.ar>",
6
6
  "license": "UNLICENSED",
@@ -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;
@@ -33,7 +41,10 @@ export const HeaderPoint = ({
33
41
 
34
42
  return (
35
43
  <header
36
- className={`bg-white border-b border-gray-200 header-point-mobile ${className}`}
44
+ className={`bg-white header-point-mobile ${className}`}
45
+ style={{
46
+ ...(props.style || {}),
47
+ }}
37
48
  {...props}
38
49
  >
39
50
  <style>{`
@@ -56,35 +67,89 @@ export const HeaderPoint = ({
56
67
  box-shadow: 0 0 0 1px #3b82f6;
57
68
  }
58
69
  @media (max-width: 768px) {
59
- .header-point-mobile {
70
+ header.header-point-mobile,
71
+ .header-point-mobile,
72
+ header.bg-white.header-point-mobile {
60
73
  background-color: transparent !important;
74
+ background: transparent !important;
61
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;
82
+ }
83
+ header.header-point-mobile[style*="background"],
84
+ .header-point-mobile[style*="background"],
85
+ header.header-point-mobile[style*="backgroundColor"] {
86
+ background-color: transparent !important;
87
+ background: transparent !important;
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;
62
95
  }
63
96
  .header-mobile-layout {
64
- display: block;
65
- position: relative;
97
+ display: block !important;
98
+ position: relative !important;
99
+ width: 100% !important;
100
+ max-width: 100% !important;
66
101
  }
67
102
  .header-mobile-top-row {
68
- display: flex;
69
- align-items: center;
70
- justify-content: space-between;
71
- padding: 8px 4px;
72
- min-height: 48px;
103
+ display: flex !important;
104
+ align-items: center !important;
105
+ justify-content: space-between !important;
106
+ padding: 24px 24px 12px 24px !important;
107
+ min-height: 64px !important;
108
+ height: 64px !important;
109
+ width: 100% !important;
110
+ max-width: 100% !important;
111
+ box-sizing: border-box !important;
112
+ }
113
+ .header-mobile-menu-button,
114
+ .header-mobile-notification-button {
115
+ display: flex !important;
116
+ align-items: center !important;
117
+ justify-content: center !important;
118
+ }
119
+ .header-mobile-point-text {
120
+ display: flex !important;
121
+ align-items: center !important;
73
122
  }
74
123
  .header-mobile-layout::after {
75
- content: '';
76
- position: absolute;
77
- top: 0;
78
- left: 0;
79
- right: 0;
80
- height: calc(48px + 16px + 4px + 20px);
81
- background-color: var(--color-main);
82
- z-index: -1;
83
- border-bottom: 1px solid var(--color-main-800);
124
+ content: '' !important;
125
+ position: absolute !important;
126
+ top: 0 !important;
127
+ left: 0 !important;
128
+ right: 0 !important;
129
+ width: 100% !important;
130
+ height: calc(64px + 12px + 20px) !important;
131
+ background-color: var(--color-main) !important;
132
+ background: var(--color-main) !important;
133
+ z-index: 0 !important;
134
+ border-bottom: 1px solid var(--color-main-800) !important;
135
+ pointer-events: none !important;
136
+ display: block !important;
137
+ visibility: visible !important;
138
+ opacity: 1 !important;
139
+ }
140
+ .header-mobile-top-row {
141
+ position: relative;
142
+ z-index: 2 !important;
84
143
  }
85
144
  .header-mobile-search-row {
145
+ position: relative;
146
+ z-index: 2 !important;
86
147
  display: block;
87
- padding: 4px 4px 8px 4px;
148
+ padding: 12px 24px 0 24px;
149
+ margin-top: -8px;
150
+ width: 100% !important;
151
+ max-width: 100% !important;
152
+ box-sizing: border-box !important;
88
153
  }
89
154
  .header-desktop-layout {
90
155
  display: none !important;
@@ -95,7 +160,8 @@ export const HeaderPoint = ({
95
160
  padding-right: 8px !important;
96
161
  }
97
162
  .header-mobile-layout .header-search-icon-container {
98
- width: 36px !important;
163
+ width: 44px !important;
164
+ height: 44px !important;
99
165
  }
100
166
  .header-mobile-menu-icon {
101
167
  color: var(--color-neutrals-white) !important;
@@ -111,6 +177,9 @@ export const HeaderPoint = ({
111
177
  .header-mobile-layout {
112
178
  display: none;
113
179
  }
180
+ header.header-point-mobile {
181
+ border-bottom: none !important;
182
+ }
114
183
  }
115
184
  `}</style>
116
185
 
@@ -241,8 +310,9 @@ export const HeaderPoint = ({
241
310
  {/* Lado izquierdo: Menú hamburguesa */}
242
311
  <div className="flex items-center">
243
312
  <button
313
+ onClick={() => setIsSidebarOpen(true)}
244
314
  className="p-2 hover:bg-gray-100 rounded-lg transition-colors header-mobile-menu-button"
245
- style={{ cursor: 'pointer' }}
315
+ style={{ cursor: 'pointer', padding: '8px' }}
246
316
  aria-label="Menú"
247
317
  >
248
318
  <Icon
@@ -259,14 +329,17 @@ export const HeaderPoint = ({
259
329
  style={{
260
330
  position: 'absolute',
261
331
  left: '50%',
262
- transform: 'translateX(-50%)'
332
+ top: '54%',
333
+ transform: 'translate(-50%, -50%)',
334
+ display: 'flex',
335
+ alignItems: 'center'
263
336
  }}
264
337
  >
265
338
  <Typography
266
339
  variant="h6"
267
340
  className="header-mobile-point-text"
268
341
  style={{
269
- fontSize: '24px',
342
+ fontSize: '20px',
270
343
  fontFamily: 'IBM Plex Sans',
271
344
  fontWeight: 600,
272
345
  lineHeight: '1.2',
@@ -283,7 +356,7 @@ export const HeaderPoint = ({
283
356
  <button
284
357
  onClick={onNotificationClick}
285
358
  className="relative p-2 hover:bg-gray-100 rounded-lg transition-colors header-mobile-notification-button"
286
- style={{ cursor: 'pointer' }}
359
+ style={{ cursor: 'pointer', padding: '8px' }}
287
360
  aria-label="Notificaciones"
288
361
  >
289
362
  <Icon
@@ -319,10 +392,10 @@ export const HeaderPoint = ({
319
392
  value={searchValue}
320
393
  onChange={handleSearchChange}
321
394
  placeholder={searchPlaceholder}
322
- className="header-search-input flex-1 pl-3 pr-3 py-2 bg-white border border-gray-400 rounded-l-lg
395
+ className="header-search-input flex-1 pl-4 pr-4 py-2 bg-white border border-gray-400 rounded-l-lg
323
396
  focus:outline-none text-sm"
324
397
  style={{
325
- height: '40px',
398
+ height: '44px',
326
399
  borderRight: 'none',
327
400
  borderTopRightRadius: '0',
328
401
  borderBottomRightRadius: '0',
@@ -357,6 +430,27 @@ export const HeaderPoint = ({
357
430
  </div>
358
431
  </div>
359
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
+ )}
360
454
  </header>
361
455
  );
362
456
  };
@@ -19,13 +19,22 @@ export const NavPoint = ({
19
19
 
20
20
  return (
21
21
  <nav
22
- className={`fixed bottom-0 left-0 right-0 z-50 md:hidden bg-white rounded-t-lg shadow-md overflow-hidden nav-point ${shouldCompress ? 'compressed' : ''} ${className}`}
22
+ className={`fixed bottom-0 left-0 right-0 z-50 md:hidden bg-white rounded-t-lg shadow-md nav-point ${shouldCompress ? 'compressed' : ''} ${className}`}
23
23
  style={{
24
- boxShadow: '0 -2px 8px rgba(0, 0, 0, 0.1)'
24
+ boxShadow: '0 -2px 8px rgba(0, 0, 0, 0.1)',
25
+ borderRadius: '16px 16px 0 0',
26
+ overflow: 'hidden'
25
27
  }}
26
28
  {...props}
27
29
  >
28
30
  <style>{`
31
+ .nav-point {
32
+ border-radius: 16px 16px 0 0 !important;
33
+ overflow: hidden !important;
34
+ }
35
+ .nav-point > div {
36
+ overflow: hidden;
37
+ }
29
38
  .nav-point-text {
30
39
  display: block;
31
40
  transition: opacity 0.2s, max-height 0.2s;
@@ -63,7 +72,7 @@ export const NavPoint = ({
63
72
  }
64
73
  }
65
74
  `}</style>
66
- <div className="flex items-stretch">
75
+ <div className="flex items-stretch" style={{ overflow: 'hidden' }}>
67
76
  {items.map((item, index) => {
68
77
  const isActive = activeItem === item.id;
69
78
  const isFirst = index === 0;
@@ -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,8 +89,8 @@ 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
96
  className="fixed top-4 left-4 z-50 p-2 bg-white border border-gray-200 rounded-lg shadow-md
@@ -96,12 +106,21 @@ const SidebarCoreMobile = ({
96
106
  </button>
97
107
  )}
98
108
  {/* 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
- )}
109
+ <div
110
+ className="fixed inset-0 bg-black bg-opacity-50 z-40"
111
+ style={{
112
+ opacity: isMobileMenuOpen ? 1 : 0,
113
+ pointerEvents: isMobileMenuOpen ? 'auto' : 'none',
114
+ transition: 'opacity 0.3s ease-in-out'
115
+ }}
116
+ onClick={() => {
117
+ if (controlledOnClose) {
118
+ controlledOnClose();
119
+ } else {
120
+ toggleMobileMenu();
121
+ }
122
+ }}
123
+ />
105
124
  <aside
106
125
  className={`bg-white border-r border-gray-200 sidebar-core-mobile ${className}`}
107
126
  {...props}
@@ -145,7 +164,13 @@ const SidebarCoreMobile = ({
145
164
 
146
165
  {/* Lado derecho: Botón X */}
147
166
  <button
148
- onClick={toggleMobileMenu}
167
+ onClick={() => {
168
+ if (controlledOnClose) {
169
+ controlledOnClose();
170
+ } else {
171
+ toggleMobileMenu();
172
+ }
173
+ }}
149
174
  className="p-1 hover:bg-gray-100 rounded transition-colors flex-shrink-0"
150
175
  aria-label="Cerrar menú"
151
176
  >
@@ -164,7 +189,11 @@ const SidebarCoreMobile = ({
164
189
  <button
165
190
  onClick={() => {
166
191
  onItemClick && onItemClick('inicio');
167
- toggleMobileMenu();
192
+ if (controlledOnClose) {
193
+ controlledOnClose();
194
+ } else {
195
+ toggleMobileMenu();
196
+ }
168
197
  }}
169
198
  className={`w-full flex items-center cursor-pointer px-4 justify-between py-2.5 rounded-lg transition-all duration-200 ${
170
199
  activeItem === 'inicio'
@@ -232,7 +261,11 @@ const SidebarCoreMobile = ({
232
261
  <button
233
262
  onClick={() => {
234
263
  onItemClick && onItemClick(item.id);
235
- toggleMobileMenu();
264
+ if (controlledOnClose) {
265
+ controlledOnClose();
266
+ } else {
267
+ toggleMobileMenu();
268
+ }
236
269
  }}
237
270
  className={`w-full flex items-center cursor-pointer px-4 justify-between py-2.5 rounded-lg transition-all duration-200 ${
238
271
  activeItem === item.id