@soyfri/shared-library 2.0.0-beta.2 → 2.0.0-beta.4
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/.dockerignore +8 -0
- package/.github/workflows/publish.yml +107 -0
- package/.prettierrc +3 -0
- package/.storybook/main.ts +19 -0
- package/.storybook/preview.ts +14 -0
- package/.storybook/vitest.setup.ts +9 -0
- package/Dockerfile +37 -0
- package/build.js +102 -0
- package/chromatic.config.json +5 -0
- package/cleanDirectories.js +40 -0
- package/dist/README.md +243 -0
- package/dist/components/Icon/Icon.js +1 -1
- package/dist/components/Table/Table.js +1 -1
- package/dist/index.cjs +24 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -1
- package/dist/mui.d.ts +1 -0
- package/dist/package.json +197 -0
- package/package.json +4 -32
- package/rollup.config.cjs +87 -0
- package/src/components/ActionMenu/ActionMenu.stories.tsx +230 -0
- package/src/components/ActionMenu/ActionMenu.tsx +174 -0
- package/src/components/ActionMenu/index.ts +2 -0
- package/src/components/AppBar/AppBar.stories.tsx +272 -0
- package/src/components/AppBar/AppBar.sx.ts +32 -0
- package/src/components/AppBar/AppBar.tsx +123 -0
- package/src/components/AppBar/AppBarBrand.tsx +120 -0
- package/src/components/AppBar/AppBarContext.ts +25 -0
- package/src/components/AppBar/AppBarMenuToggle.tsx +90 -0
- package/src/components/AppBar/AppBarUserMenu.tsx +217 -0
- package/src/components/AppBar/index.ts +25 -0
- package/src/components/Autocomplete/Autocomplete.definitions.ts +477 -0
- package/src/components/Autocomplete/Autocomplete.helpers.ts +60 -0
- package/src/components/Autocomplete/Autocomplete.stories.tsx +748 -0
- package/src/components/Autocomplete/Autocomplete.sx.ts +30 -0
- package/src/components/Autocomplete/Autocomplete.tsx +361 -0
- package/src/components/Autocomplete/Autocomplete.types.ts +13 -0
- package/src/components/Autocomplete/_parts/AutocompleteChips.tsx +55 -0
- package/src/components/Autocomplete/_parts/AutocompleteLoader.tsx +17 -0
- package/src/components/Autocomplete/_parts/AutocompleteOption.tsx +31 -0
- package/src/components/Autocomplete/index.ts +12 -0
- package/src/components/Avatar/Avatar.definitions.ts +162 -0
- package/src/components/Avatar/Avatar.stories.tsx +258 -0
- package/src/components/Avatar/Avatar.tsx +206 -0
- package/src/components/Avatar/index.ts +1 -0
- package/src/components/Button/Button.definition.ts +97 -0
- package/src/components/Button/Button.stories.tsx +285 -0
- package/src/components/Button/Button.tsx +67 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Card/Card.definition.ts +5 -0
- package/src/components/Card/Card.stories.tsx +221 -0
- package/src/components/Card/Card.sx.ts +104 -0
- package/src/components/Card/Card.tsx +200 -0
- package/src/components/Card/index.ts +9 -0
- package/src/components/Chip/Chip.definitions.ts +167 -0
- package/src/components/Chip/Chip.stories.tsx +265 -0
- package/src/components/Chip/Chip.tsx +61 -0
- package/src/components/Chip/index.ts +1 -0
- package/src/components/Column/Column.tsx +29 -0
- package/src/components/Column/index.ts +1 -0
- package/src/components/DatePicker/DatePicker.definitions.ts +228 -0
- package/src/components/DatePicker/DatePicker.helpers.ts +24 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +309 -0
- package/src/components/DatePicker/DatePicker.sx.ts +33 -0
- package/src/components/DatePicker/DatePicker.tsx +189 -0
- package/src/components/DatePicker/DatePicker.types.ts +10 -0
- package/src/components/DatePicker/index.ts +9 -0
- package/src/components/DateRangePicker/DateRangePicker.definitions.ts +191 -0
- package/src/components/DateRangePicker/DateRangePicker.stories.tsx +252 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +56 -0
- package/src/components/DateRangePicker/index.ts +1 -0
- package/src/components/DateTimePicker/DateTimePicker.definitions.ts +256 -0
- package/src/components/DateTimePicker/DateTimePicker.helpers.ts +38 -0
- package/src/components/DateTimePicker/DateTimePicker.stories.tsx +418 -0
- package/src/components/DateTimePicker/DateTimePicker.sx.ts +30 -0
- package/src/components/DateTimePicker/DateTimePicker.tsx +225 -0
- package/src/components/DateTimePicker/DateTimePicker.types.ts +10 -0
- package/src/components/DateTimePicker/index.ts +9 -0
- package/src/components/Drawer/Drawer.stories.tsx +270 -0
- package/src/components/Drawer/Drawer.sx.ts +106 -0
- package/src/components/Drawer/Drawer.tsx +214 -0
- package/src/components/Drawer/DrawerContext.ts +26 -0
- package/src/components/Drawer/DrawerItem.tsx +110 -0
- package/src/components/Drawer/index.ts +10 -0
- package/src/components/Flyout/Flyout.stories.tsx +282 -0
- package/src/components/Flyout/Flyout.tsx +122 -0
- package/src/components/Flyout/index.ts +1 -0
- package/src/components/Gallery/Gallery.definition.tsx +37 -0
- package/src/components/Gallery/Gallery.stories.tsx +82 -0
- package/src/components/Gallery/Gallery.tsx +118 -0
- package/src/components/Gallery/GalleryLightbox.tsx +170 -0
- package/src/components/Gallery/GalleryMain.tsx +84 -0
- package/src/components/Gallery/GalleryThumbnails.tsx +106 -0
- package/src/components/Gallery/index.ts +1 -0
- package/src/components/Icon/Icon.stories.tsx +121 -0
- package/src/components/Icon/Icon.tsx +175 -0
- package/src/components/Icon/index.ts +2 -0
- package/src/components/Input/Input.definitions.ts +324 -0
- package/src/components/Input/Input.helpers.ts +49 -0
- package/src/components/Input/Input.stories.tsx +499 -0
- package/src/components/Input/Input.sx.ts +42 -0
- package/src/components/Input/Input.tsx +141 -0
- package/src/components/Input/Input.types.ts +10 -0
- package/src/components/Input/index.ts +9 -0
- package/src/components/InputGroup/InputGroup.definitions.ts +158 -0
- package/src/components/InputGroup/InputGroup.stories.tsx +267 -0
- package/src/components/InputGroup/InputGroup.tsx +179 -0
- package/src/components/InputGroup/index.ts +1 -0
- package/src/components/MenuButton/MenuButton.stories.tsx +197 -0
- package/src/components/MenuButton/MenuButton.tsx +100 -0
- package/src/components/MenuButton/index.ts +1 -0
- package/src/components/Modal/Modal.stories.tsx +721 -0
- package/src/components/Modal/Modal.tsx +355 -0
- package/src/components/Modal/ModalBody.tsx +16 -0
- package/src/components/Modal/ModalFooter.tsx +71 -0
- package/src/components/Modal/ModalHeader.tsx +18 -0
- package/src/components/Modal/index.ts +6 -0
- package/src/components/PageLoader/PageLoader.stories.tsx +217 -0
- package/src/components/PageLoader/PageLoader.tsx +96 -0
- package/src/components/PageLoader/index.ts +2 -0
- package/src/components/ScrollTopButton/ScrollTopButton.stories.tsx +158 -0
- package/src/components/ScrollTopButton/ScrollTopButton.tsx +135 -0
- package/src/components/ScrollTopButton/index.ts +8 -0
- package/src/components/ScrollTopButton/scrollToTop.ts +37 -0
- package/src/components/Select/Select.definitions.ts +602 -0
- package/src/components/Select/Select.helpers.ts +71 -0
- package/src/components/Select/Select.stories.tsx +687 -0
- package/src/components/Select/Select.sx.ts +14 -0
- package/src/components/Select/Select.tsx +429 -0
- package/src/components/Select/Select.types.ts +15 -0
- package/src/components/Select/_parts/SelectMenuItem.tsx +40 -0
- package/src/components/Select/_parts/SelectSearchHeader.tsx +51 -0
- package/src/components/Select/_parts/SelectValue.tsx +96 -0
- package/src/components/Select/index.ts +14 -0
- package/src/components/Stat/Stat.stories.tsx +85 -0
- package/src/components/Stat/Stat.tsx +117 -0
- package/src/components/Stat/index.ts +2 -0
- package/src/components/StatusMessage/StatusMessage.stories.tsx +130 -0
- package/src/components/StatusMessage/StatusMessage.tsx +162 -0
- package/src/components/StatusMessage/index.ts +2 -0
- package/src/components/Stepper/Step.tsx +21 -0
- package/src/components/Stepper/Stepper.definition.ts +75 -0
- package/src/components/Stepper/Stepper.stories.tsx +122 -0
- package/src/components/Stepper/Stepper.tsx +75 -0
- package/src/components/Stepper/index.ts +2 -0
- package/src/components/Table/EmptyTable.png +0 -0
- package/src/components/Table/Table.definition.ts +580 -0
- package/src/components/Table/Table.stories.tsx +853 -0
- package/src/components/Table/Table.tsx +495 -0
- package/src/components/Table/data.ts +134 -0
- package/src/components/Table/exportsUtils.ts +195 -0
- package/src/components/Table/index.ts +3 -0
- package/src/components/Table/types.ts +34 -0
- package/src/components/Tabs/Tab.definition.ts +53 -0
- package/src/components/Tabs/Tab.tsx +19 -0
- package/src/components/Tabs/Tabs.stories.tsx +118 -0
- package/src/components/Tabs/Tabs.tsx +99 -0
- package/src/components/Tabs/_tabUtils.tsx +4 -0
- package/src/components/Tabs/index.ts +2 -0
- package/src/components/Timeline/Timeline.definition.ts +43 -0
- package/src/components/Timeline/Timeline.stories.tsx +108 -0
- package/src/components/Timeline/Timeline.tsx +49 -0
- package/src/components/Timeline/TimelineItem.tsx +31 -0
- package/src/components/Timeline/index.ts +2 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +129 -0
- package/src/components/Tooltip/Tooltip.tsx +58 -0
- package/src/components/Tooltip/index.ts +1 -0
- package/src/components/_shared/formField.sx.ts +118 -0
- package/src/components/_shared/resolvePreset.ts +35 -0
- package/src/hooks/ClipBoard/ClipBoard.stories.tsx +168 -0
- package/src/hooks/ClipBoard/ClipBoard.tsx +131 -0
- package/src/hooks/ClipBoard/ClipboardUnifiedDemo.tsx +111 -0
- package/src/hooks/ClipBoard/index.ts +1 -0
- package/src/hooks/Wizard/Wizard.stories.tsx +301 -0
- package/src/hooks/Wizard/WizardContext.tsx +166 -0
- package/src/hooks/Wizard/index.ts +6 -0
- package/src/hooks/Wizard/useWizard.ts +13 -0
- package/src/index.ts +17 -0
- package/src/mui.ts +54 -0
- package/src/styles.css +3 -0
- package/src/theme/componentStyles.ts +47 -0
- package/src/theme/tokens.ts +43 -0
- package/tailwind.config.js +10 -0
- package/tsconfig.json +48 -0
- package/tsup.config.js +41 -0
- package/vite.config.js +132 -0
- package/vitest.config.ts +35 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React, { useState, type ReactNode, type MouseEvent } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
IconButton,
|
|
4
|
+
Menu,
|
|
5
|
+
MenuItem,
|
|
6
|
+
ListItemIcon,
|
|
7
|
+
ListItemText,
|
|
8
|
+
Divider,
|
|
9
|
+
Tooltip,
|
|
10
|
+
} from '@mui/material';
|
|
11
|
+
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
12
|
+
import type { SxProps, Theme } from '@mui/material/styles';
|
|
13
|
+
|
|
14
|
+
export interface ActionMenuItem {
|
|
15
|
+
/** Key única para React. Si no se provee, se usa el label. */
|
|
16
|
+
key?: string;
|
|
17
|
+
/** Texto del item. */
|
|
18
|
+
label: ReactNode;
|
|
19
|
+
/** Icono opcional a la izquierda. */
|
|
20
|
+
icon?: ReactNode;
|
|
21
|
+
/** Handler de click. Recibe el evento del MenuItem. */
|
|
22
|
+
onClick?: (event: MouseEvent<HTMLLIElement>) => void;
|
|
23
|
+
/** Deshabilita el item. */
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
/** Marca el item como destructivo (pinta color error). */
|
|
26
|
+
danger?: boolean;
|
|
27
|
+
/** Inserta un `<Divider />` ANTES de este item. */
|
|
28
|
+
dividerBefore?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ActionMenuProps {
|
|
32
|
+
/** Lista de items del menú. */
|
|
33
|
+
items: ActionMenuItem[];
|
|
34
|
+
/**
|
|
35
|
+
* Elemento trigger. Si no se provee, se renderiza un IconButton con icono
|
|
36
|
+
* de tres puntos (MoreVertIcon) — el patrón típico de celda de tabla.
|
|
37
|
+
*/
|
|
38
|
+
trigger?: ReactNode;
|
|
39
|
+
/** Texto de tooltip sobre el trigger default. Default: "Acciones". */
|
|
40
|
+
triggerTooltip?: string;
|
|
41
|
+
/** Anchor origin del menu. Default: { vertical: 'bottom', horizontal: 'right' }. */
|
|
42
|
+
anchorOrigin?: {
|
|
43
|
+
vertical: 'top' | 'center' | 'bottom';
|
|
44
|
+
horizontal: 'left' | 'center' | 'right';
|
|
45
|
+
};
|
|
46
|
+
/** Transform origin del menu. Default: { vertical: 'top', horizontal: 'right' }. */
|
|
47
|
+
transformOrigin?: {
|
|
48
|
+
vertical: 'top' | 'center' | 'bottom';
|
|
49
|
+
horizontal: 'left' | 'center' | 'right';
|
|
50
|
+
};
|
|
51
|
+
/** sx del Menu (Paper interno). */
|
|
52
|
+
menuSx?: SxProps<Theme>;
|
|
53
|
+
/** Deshabilita el trigger entero. */
|
|
54
|
+
disabled?: boolean;
|
|
55
|
+
className?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Menú de acciones compacto, pensado para celdas de tabla y cabeceras.
|
|
60
|
+
* Reemplaza los patrones `<Dropdown>` de react-bootstrap.
|
|
61
|
+
*
|
|
62
|
+
* ```tsx
|
|
63
|
+
* <ActionMenu
|
|
64
|
+
* items={[
|
|
65
|
+
* { label: 'Editar', icon: <EditIcon />, onClick: handleEdit },
|
|
66
|
+
* { label: 'Duplicar', icon: <CopyIcon />, onClick: handleDup },
|
|
67
|
+
* { label: 'Eliminar', icon: <TrashIcon />, onClick: handleDel, danger: true, dividerBefore: true },
|
|
68
|
+
* ]}
|
|
69
|
+
* />
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function ActionMenu({
|
|
73
|
+
items,
|
|
74
|
+
trigger,
|
|
75
|
+
triggerTooltip = 'Acciones',
|
|
76
|
+
anchorOrigin = { vertical: 'bottom', horizontal: 'right' },
|
|
77
|
+
transformOrigin = { vertical: 'top', horizontal: 'right' },
|
|
78
|
+
menuSx,
|
|
79
|
+
disabled = false,
|
|
80
|
+
className,
|
|
81
|
+
}: ActionMenuProps) {
|
|
82
|
+
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
|
|
83
|
+
const open = Boolean(anchorEl);
|
|
84
|
+
|
|
85
|
+
const handleOpen = (event: MouseEvent<HTMLElement>) => {
|
|
86
|
+
if (disabled) return;
|
|
87
|
+
event.stopPropagation();
|
|
88
|
+
setAnchorEl(event.currentTarget);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const handleClose = () => setAnchorEl(null);
|
|
92
|
+
|
|
93
|
+
const handleItemClick = (
|
|
94
|
+
event: MouseEvent<HTMLLIElement>,
|
|
95
|
+
item: ActionMenuItem,
|
|
96
|
+
) => {
|
|
97
|
+
event.stopPropagation();
|
|
98
|
+
item.onClick?.(event);
|
|
99
|
+
handleClose();
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const triggerElement = trigger ? (
|
|
103
|
+
React.isValidElement(trigger) ? (
|
|
104
|
+
React.cloneElement(trigger as React.ReactElement<any>, {
|
|
105
|
+
onClick: handleOpen,
|
|
106
|
+
disabled,
|
|
107
|
+
})
|
|
108
|
+
) : (
|
|
109
|
+
<span onClick={handleOpen}>{trigger}</span>
|
|
110
|
+
)
|
|
111
|
+
) : (
|
|
112
|
+
<Tooltip title={triggerTooltip} arrow>
|
|
113
|
+
<span>
|
|
114
|
+
<IconButton
|
|
115
|
+
size="small"
|
|
116
|
+
onClick={handleOpen}
|
|
117
|
+
disabled={disabled}
|
|
118
|
+
aria-label={triggerTooltip}
|
|
119
|
+
>
|
|
120
|
+
<MoreVertIcon fontSize="small" />
|
|
121
|
+
</IconButton>
|
|
122
|
+
</span>
|
|
123
|
+
</Tooltip>
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<span className={className}>
|
|
128
|
+
{triggerElement}
|
|
129
|
+
<Menu
|
|
130
|
+
anchorEl={anchorEl}
|
|
131
|
+
open={open}
|
|
132
|
+
onClose={handleClose}
|
|
133
|
+
anchorOrigin={anchorOrigin}
|
|
134
|
+
transformOrigin={transformOrigin}
|
|
135
|
+
slotProps={{ paper: { sx: { minWidth: 180, ...(menuSx as any) } } }}
|
|
136
|
+
>
|
|
137
|
+
{items.map((item, idx) => {
|
|
138
|
+
const key = item.key ?? `${String(item.label)}-${idx}`;
|
|
139
|
+
const node = (
|
|
140
|
+
<MenuItem
|
|
141
|
+
key={key}
|
|
142
|
+
disabled={item.disabled}
|
|
143
|
+
onClick={(event) => handleItemClick(event, item)}
|
|
144
|
+
sx={(theme) => ({
|
|
145
|
+
color: item.danger ? theme.palette.error.main : 'inherit',
|
|
146
|
+
'& .MuiListItemIcon-root': {
|
|
147
|
+
color: item.danger
|
|
148
|
+
? theme.palette.error.main
|
|
149
|
+
: 'inherit',
|
|
150
|
+
minWidth: 32,
|
|
151
|
+
},
|
|
152
|
+
})}
|
|
153
|
+
>
|
|
154
|
+
{item.icon && <ListItemIcon>{item.icon}</ListItemIcon>}
|
|
155
|
+
<ListItemText primary={item.label} />
|
|
156
|
+
</MenuItem>
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (item.dividerBefore && idx > 0) {
|
|
160
|
+
return (
|
|
161
|
+
<React.Fragment key={`${key}-frag`}>
|
|
162
|
+
<Divider />
|
|
163
|
+
{node}
|
|
164
|
+
</React.Fragment>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return node;
|
|
168
|
+
})}
|
|
169
|
+
</Menu>
|
|
170
|
+
</span>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export default ActionMenu;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { Box, Typography, Button, IconButton, Tooltip } from '@mui/material';
|
|
4
|
+
import NotificationsIcon from '@mui/icons-material/Notifications';
|
|
5
|
+
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
|
|
6
|
+
import PersonIcon from '@mui/icons-material/Person';
|
|
7
|
+
import SettingsIcon from '@mui/icons-material/Settings';
|
|
8
|
+
import LogoutIcon from '@mui/icons-material/Logout';
|
|
9
|
+
import DashboardIcon from '@mui/icons-material/Dashboard';
|
|
10
|
+
|
|
11
|
+
import { AppBar } from './AppBar';
|
|
12
|
+
import { AppBarBrand } from './AppBarBrand';
|
|
13
|
+
import { AppBarMenuToggle } from './AppBarMenuToggle';
|
|
14
|
+
import { AppBarUserMenu } from './AppBarUserMenu';
|
|
15
|
+
|
|
16
|
+
const meta: Meta<typeof AppBar> = {
|
|
17
|
+
title: 'Components/AppBar',
|
|
18
|
+
component: AppBar,
|
|
19
|
+
tags: ['autodocs'],
|
|
20
|
+
parameters: {
|
|
21
|
+
layout: 'fullscreen',
|
|
22
|
+
docs: {
|
|
23
|
+
description: {
|
|
24
|
+
component:
|
|
25
|
+
'AppBar (header superior) pensado como shell compositivo. El consumer arma su contenido con los sub-componentes `AppBarBrand`, `AppBarMenuToggle` y `AppBarUserMenu`, además de cualquier acción custom. Reemplaza el header de Metronic.',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
argTypes: {
|
|
30
|
+
position: {
|
|
31
|
+
control: 'select',
|
|
32
|
+
options: ['fixed', 'sticky', 'static', 'absolute', 'relative'],
|
|
33
|
+
},
|
|
34
|
+
color: {
|
|
35
|
+
control: 'select',
|
|
36
|
+
options: ['default', 'primary', 'secondary', 'transparent', 'inherit'],
|
|
37
|
+
},
|
|
38
|
+
elevation: { control: { type: 'number', min: 0, max: 24, step: 1 } },
|
|
39
|
+
height: { control: { type: 'number', min: 48, max: 96, step: 4 } },
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default meta;
|
|
44
|
+
type Story = StoryObj<typeof AppBar>;
|
|
45
|
+
|
|
46
|
+
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
const SampleLogo = () => (
|
|
49
|
+
<Box
|
|
50
|
+
sx={{
|
|
51
|
+
width: 32,
|
|
52
|
+
height: 32,
|
|
53
|
+
borderRadius: 1,
|
|
54
|
+
display: 'flex',
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
justifyContent: 'center',
|
|
57
|
+
backgroundColor: 'primary.main',
|
|
58
|
+
color: 'primary.contrastText',
|
|
59
|
+
fontWeight: 700,
|
|
60
|
+
fontSize: 14,
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
F
|
|
64
|
+
</Box>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const SampleBody = () => (
|
|
68
|
+
<Box sx={{ p: 3 }}>
|
|
69
|
+
<Typography variant="h5" gutterBottom>
|
|
70
|
+
Contenido de la página
|
|
71
|
+
</Typography>
|
|
72
|
+
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
|
73
|
+
Este bloque sólo demuestra el layout bajo el AppBar.
|
|
74
|
+
</Typography>
|
|
75
|
+
{Array.from({ length: 20 }).map((_, idx) => (
|
|
76
|
+
<Typography key={idx} variant="body2" sx={{ mb: 1 }}>
|
|
77
|
+
Línea {idx + 1} — contenido de ejemplo para scroll sticky.
|
|
78
|
+
</Typography>
|
|
79
|
+
))}
|
|
80
|
+
</Box>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const userMenuItems = [
|
|
84
|
+
{
|
|
85
|
+
label: 'Mi perfil',
|
|
86
|
+
icon: <PersonIcon fontSize="small" />,
|
|
87
|
+
onClick: () => console.log('profile'),
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: 'Configuración',
|
|
91
|
+
icon: <SettingsIcon fontSize="small" />,
|
|
92
|
+
onClick: () => console.log('settings'),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
label: 'Cerrar sesión',
|
|
96
|
+
icon: <LogoutIcon fontSize="small" />,
|
|
97
|
+
onClick: () => console.log('logout'),
|
|
98
|
+
danger: true,
|
|
99
|
+
dividerBefore: true,
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
// ── Stories ──────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
export const BasicHeader: Story = {
|
|
106
|
+
args: {
|
|
107
|
+
position: 'sticky',
|
|
108
|
+
color: 'default',
|
|
109
|
+
elevation: 1,
|
|
110
|
+
height: 64,
|
|
111
|
+
},
|
|
112
|
+
render: (args) => (
|
|
113
|
+
<>
|
|
114
|
+
<AppBar {...args}>
|
|
115
|
+
<AppBarBrand
|
|
116
|
+
logo={<SampleLogo />}
|
|
117
|
+
title="Afiliaciones"
|
|
118
|
+
subtitle="Panel administrativo"
|
|
119
|
+
/>
|
|
120
|
+
</AppBar>
|
|
121
|
+
<SampleBody />
|
|
122
|
+
</>
|
|
123
|
+
),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const WithMenuToggle: Story = {
|
|
127
|
+
render: (args) => {
|
|
128
|
+
const [open, setOpen] = useState(false);
|
|
129
|
+
return (
|
|
130
|
+
<>
|
|
131
|
+
<AppBar
|
|
132
|
+
{...args}
|
|
133
|
+
onMenuToggle={() => setOpen((value) => !value)}
|
|
134
|
+
menuOpen={open}
|
|
135
|
+
>
|
|
136
|
+
<AppBarMenuToggle />
|
|
137
|
+
<AppBarBrand logo={<SampleLogo />} title="Afiliaciones" />
|
|
138
|
+
</AppBar>
|
|
139
|
+
<Box sx={{ p: 3 }}>
|
|
140
|
+
<Typography variant="body2">
|
|
141
|
+
Drawer {open ? 'abierto' : 'cerrado'} — el icono cambia según el
|
|
142
|
+
estado. Click en el botón hamburguesa para alternar.
|
|
143
|
+
</Typography>
|
|
144
|
+
</Box>
|
|
145
|
+
</>
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const WithUserMenu: Story = {
|
|
151
|
+
render: (args) => (
|
|
152
|
+
<>
|
|
153
|
+
<AppBar {...args}>
|
|
154
|
+
<AppBarBrand logo={<SampleLogo />} title="Afiliaciones" />
|
|
155
|
+
<Box sx={{ flex: 1 }} />
|
|
156
|
+
<AppBarUserMenu
|
|
157
|
+
user={{
|
|
158
|
+
name: 'Andrea Pineda',
|
|
159
|
+
email: 'andrea@soyfri.com',
|
|
160
|
+
}}
|
|
161
|
+
items={userMenuItems}
|
|
162
|
+
/>
|
|
163
|
+
</AppBar>
|
|
164
|
+
<SampleBody />
|
|
165
|
+
</>
|
|
166
|
+
),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export const CompleteDashboardHeader: Story = {
|
|
170
|
+
render: (args) => {
|
|
171
|
+
const [open, setOpen] = useState(true);
|
|
172
|
+
return (
|
|
173
|
+
<>
|
|
174
|
+
<AppBar
|
|
175
|
+
{...args}
|
|
176
|
+
onMenuToggle={() => setOpen((value) => !value)}
|
|
177
|
+
menuOpen={open}
|
|
178
|
+
>
|
|
179
|
+
<AppBarMenuToggle />
|
|
180
|
+
<AppBarBrand
|
|
181
|
+
logo={<SampleLogo />}
|
|
182
|
+
title="Afiliaciones"
|
|
183
|
+
subtitle="Panel administrativo"
|
|
184
|
+
onClick={() => console.log('go home')}
|
|
185
|
+
/>
|
|
186
|
+
<Box sx={{ flex: 1 }} />
|
|
187
|
+
<Tooltip title="Notificaciones" arrow>
|
|
188
|
+
<IconButton color="inherit">
|
|
189
|
+
<NotificationsIcon />
|
|
190
|
+
</IconButton>
|
|
191
|
+
</Tooltip>
|
|
192
|
+
<Tooltip title="Ayuda" arrow>
|
|
193
|
+
<IconButton color="inherit">
|
|
194
|
+
<HelpOutlineIcon />
|
|
195
|
+
</IconButton>
|
|
196
|
+
</Tooltip>
|
|
197
|
+
<AppBarUserMenu
|
|
198
|
+
user={{
|
|
199
|
+
name: 'Andrea Pineda',
|
|
200
|
+
email: 'andrea@soyfri.com',
|
|
201
|
+
}}
|
|
202
|
+
items={userMenuItems}
|
|
203
|
+
/>
|
|
204
|
+
</AppBar>
|
|
205
|
+
<SampleBody />
|
|
206
|
+
</>
|
|
207
|
+
);
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const TransparentVariant: Story = {
|
|
212
|
+
args: {
|
|
213
|
+
color: 'transparent',
|
|
214
|
+
elevation: 0,
|
|
215
|
+
},
|
|
216
|
+
render: (args) => (
|
|
217
|
+
<Box
|
|
218
|
+
sx={{
|
|
219
|
+
minHeight: '100vh',
|
|
220
|
+
background:
|
|
221
|
+
'linear-gradient(135deg, #f0f4ff 0%, #e6f7ff 50%, #fff5f5 100%)',
|
|
222
|
+
}}
|
|
223
|
+
>
|
|
224
|
+
<AppBar {...args}>
|
|
225
|
+
<AppBarBrand logo={<SampleLogo />} title="Afiliaciones" />
|
|
226
|
+
<Box sx={{ flex: 1 }} />
|
|
227
|
+
<Button variant="contained" color="primary" size="small">
|
|
228
|
+
Iniciar sesión
|
|
229
|
+
</Button>
|
|
230
|
+
</AppBar>
|
|
231
|
+
<Box sx={{ p: 3 }}>
|
|
232
|
+
<Typography variant="h5">Landing page transparente</Typography>
|
|
233
|
+
<Typography variant="body2" color="text.secondary">
|
|
234
|
+
Sin sombra, con borde sutil — útil para headers sobre hero sections.
|
|
235
|
+
</Typography>
|
|
236
|
+
</Box>
|
|
237
|
+
</Box>
|
|
238
|
+
),
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export const PrimaryColorWithMenu: Story = {
|
|
242
|
+
args: {
|
|
243
|
+
color: 'primary',
|
|
244
|
+
},
|
|
245
|
+
render: (args) => {
|
|
246
|
+
const [open, setOpen] = useState(false);
|
|
247
|
+
return (
|
|
248
|
+
<>
|
|
249
|
+
<AppBar
|
|
250
|
+
{...args}
|
|
251
|
+
onMenuToggle={() => setOpen((value) => !value)}
|
|
252
|
+
menuOpen={open}
|
|
253
|
+
>
|
|
254
|
+
<AppBarMenuToggle />
|
|
255
|
+
<DashboardIcon sx={{ mr: 1 }} />
|
|
256
|
+
<Typography variant="h6" sx={{ fontWeight: 700 }}>
|
|
257
|
+
Dashboard
|
|
258
|
+
</Typography>
|
|
259
|
+
<Box sx={{ flex: 1 }} />
|
|
260
|
+
<AppBarUserMenu
|
|
261
|
+
user={{
|
|
262
|
+
name: 'Andrea Pineda',
|
|
263
|
+
email: 'andrea@soyfri.com',
|
|
264
|
+
}}
|
|
265
|
+
items={userMenuItems}
|
|
266
|
+
/>
|
|
267
|
+
</AppBar>
|
|
268
|
+
<SampleBody />
|
|
269
|
+
</>
|
|
270
|
+
);
|
|
271
|
+
},
|
|
272
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material/styles';
|
|
2
|
+
|
|
3
|
+
export interface BuildAppBarSxArgs {
|
|
4
|
+
/** Altura en px. Default: 64. */
|
|
5
|
+
height?: number;
|
|
6
|
+
/** Color del AppBar (se pasa al MuiAppBar). */
|
|
7
|
+
transparent?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* sx del root del MuiAppBar. Define altura fija y layout flex del Toolbar
|
|
12
|
+
* interno. El color/elevación se dejan a las props nativas de MuiAppBar.
|
|
13
|
+
*/
|
|
14
|
+
export function buildAppBarSx({
|
|
15
|
+
height = 64,
|
|
16
|
+
transparent = false,
|
|
17
|
+
}: BuildAppBarSxArgs): SxProps<Theme> {
|
|
18
|
+
return (theme) => ({
|
|
19
|
+
minHeight: height,
|
|
20
|
+
justifyContent: 'center',
|
|
21
|
+
backgroundImage: 'none',
|
|
22
|
+
...(transparent && {
|
|
23
|
+
backgroundColor: 'transparent',
|
|
24
|
+
boxShadow: 'none',
|
|
25
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
26
|
+
}),
|
|
27
|
+
'& .MuiToolbar-root': {
|
|
28
|
+
minHeight: height,
|
|
29
|
+
gap: 1.5,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React, { useMemo, type ReactNode } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
AppBar as MuiAppBar,
|
|
4
|
+
Toolbar,
|
|
5
|
+
type AppBarProps as MuiAppBarProps,
|
|
6
|
+
} from '@mui/material';
|
|
7
|
+
import {
|
|
8
|
+
useTheme,
|
|
9
|
+
type SxProps,
|
|
10
|
+
type Theme,
|
|
11
|
+
} from '@mui/material/styles';
|
|
12
|
+
|
|
13
|
+
import { AppBarContext } from './AppBarContext';
|
|
14
|
+
import { buildAppBarSx } from './AppBar.sx';
|
|
15
|
+
import { resolvePreset } from '../_shared/resolvePreset';
|
|
16
|
+
|
|
17
|
+
export type AppBarPosition = 'fixed' | 'sticky' | 'static' | 'absolute' | 'relative';
|
|
18
|
+
export type AppBarColor =
|
|
19
|
+
| 'default'
|
|
20
|
+
| 'primary'
|
|
21
|
+
| 'secondary'
|
|
22
|
+
| 'transparent'
|
|
23
|
+
| 'inherit';
|
|
24
|
+
|
|
25
|
+
export interface AppBarProps {
|
|
26
|
+
/** Contenido del AppBar (típicamente sub-componentes + acciones custom). */
|
|
27
|
+
children?: ReactNode;
|
|
28
|
+
/** Posicionamiento. Default: `'sticky'`. */
|
|
29
|
+
position?: AppBarPosition;
|
|
30
|
+
/** Color. Default: `'default'`. */
|
|
31
|
+
color?: AppBarColor;
|
|
32
|
+
/** Nivel de sombra. Default: 1. */
|
|
33
|
+
elevation?: number;
|
|
34
|
+
/** Altura en px. Default: 64. */
|
|
35
|
+
height?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Handler del botón hamburguesa. Se expone vía AppBarContext para que el
|
|
38
|
+
* sub-componente `<AppBarMenuToggle>` lo consuma sin prop drilling.
|
|
39
|
+
*/
|
|
40
|
+
onMenuToggle?: () => void;
|
|
41
|
+
/** Estado del drawer asociado (para que el icono del toggle cambie). */
|
|
42
|
+
menuOpen?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Nombre del preset de estilo registrado en `theme.styles.AppBar`.
|
|
45
|
+
* - `"default"` (o ausente) = estilo built-in del paquete.
|
|
46
|
+
*/
|
|
47
|
+
preset?: string;
|
|
48
|
+
/** sx del root (se mergea después del preset). */
|
|
49
|
+
sx?: SxProps<Theme>;
|
|
50
|
+
/** sx del Toolbar interno. */
|
|
51
|
+
toolbarSx?: SxProps<Theme>;
|
|
52
|
+
className?: string;
|
|
53
|
+
/** Otras props nativas del MuiAppBar (ej. `enableColorOnDark`). */
|
|
54
|
+
appBarProps?: Omit<
|
|
55
|
+
MuiAppBarProps,
|
|
56
|
+
'position' | 'color' | 'elevation' | 'sx' | 'children' | 'className'
|
|
57
|
+
>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* AppBar (header superior) del paquete. Se diseñó como un shell compositivo
|
|
62
|
+
* — el consumer arma el contenido con los sub-componentes que exporta el
|
|
63
|
+
* paquete (`AppBarBrand`, `AppBarMenuToggle`, `AppBarUserMenu`) + cualquier
|
|
64
|
+
* otra cosa custom.
|
|
65
|
+
*
|
|
66
|
+
* Patrón recomendado para navegación en dashboards:
|
|
67
|
+
*
|
|
68
|
+
* ```tsx
|
|
69
|
+
* <AppBar onMenuToggle={toggleDrawer}>
|
|
70
|
+
* <AppBarMenuToggle />
|
|
71
|
+
* <AppBarBrand logo={<Logo />} title="Afiliaciones" />
|
|
72
|
+
* <Box sx={{ flex: 1 }} />
|
|
73
|
+
* <AppBarUserMenu user={user} items={menuItems} />
|
|
74
|
+
* </AppBar>
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function AppBar({
|
|
78
|
+
children,
|
|
79
|
+
position = 'sticky',
|
|
80
|
+
color = 'default',
|
|
81
|
+
elevation = 1,
|
|
82
|
+
height = 64,
|
|
83
|
+
onMenuToggle,
|
|
84
|
+
menuOpen,
|
|
85
|
+
preset,
|
|
86
|
+
sx,
|
|
87
|
+
toolbarSx,
|
|
88
|
+
className,
|
|
89
|
+
appBarProps,
|
|
90
|
+
}: AppBarProps) {
|
|
91
|
+
const theme = useTheme();
|
|
92
|
+
const presetSx = resolvePreset('AppBar', preset, theme);
|
|
93
|
+
|
|
94
|
+
const transparent = color === 'transparent';
|
|
95
|
+
|
|
96
|
+
const rootSx: SxProps<Theme> = [
|
|
97
|
+
buildAppBarSx({ height, transparent }),
|
|
98
|
+
...(presetSx ? [presetSx] : []),
|
|
99
|
+
...(Array.isArray(sx) ? sx : sx ? [sx] : []),
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const contextValue = useMemo(
|
|
103
|
+
() => ({ onMenuToggle, menuOpen }),
|
|
104
|
+
[onMenuToggle, menuOpen],
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<AppBarContext.Provider value={contextValue}>
|
|
109
|
+
<MuiAppBar
|
|
110
|
+
position={position}
|
|
111
|
+
color={color === 'transparent' ? 'transparent' : color}
|
|
112
|
+
elevation={elevation}
|
|
113
|
+
className={className}
|
|
114
|
+
sx={rootSx}
|
|
115
|
+
{...appBarProps}
|
|
116
|
+
>
|
|
117
|
+
<Toolbar sx={toolbarSx}>{children}</Toolbar>
|
|
118
|
+
</MuiAppBar>
|
|
119
|
+
</AppBarContext.Provider>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default AppBar;
|