@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,49 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import MuiTimeline from "@mui/lab/Timeline";
|
|
3
|
+
import { timelineContentClasses } from "@mui/lab/TimelineContent";
|
|
4
|
+
import { timelineItemClasses, TimelineItemProps as MuiTimelineItemProps } from "@mui/lab/TimelineItem";
|
|
5
|
+
import { Box } from "@mui/material";
|
|
6
|
+
|
|
7
|
+
interface TimelineItemProps extends MuiTimelineItemProps {
|
|
8
|
+
connectorColor?: string;
|
|
9
|
+
sx?: object;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface TimelineProps {
|
|
13
|
+
children: React.ReactElement<TimelineItemProps>[];
|
|
14
|
+
position?: "left" | "right" | 'alternate' | 'alternate-reverse' ;
|
|
15
|
+
sx?: object;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const Timeline: React.FC<TimelineProps> = ({ children, position = "right", sx }) => {
|
|
19
|
+
const count = React.Children.count(children);
|
|
20
|
+
|
|
21
|
+
const customSx = (position === "left" || position === "right")
|
|
22
|
+
? {
|
|
23
|
+
[`& .${timelineItemClasses.root}:before`]: {
|
|
24
|
+
flex: 0,
|
|
25
|
+
padding: 0,
|
|
26
|
+
},
|
|
27
|
+
...sx,
|
|
28
|
+
}
|
|
29
|
+
: sx;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<MuiTimeline position={position}
|
|
33
|
+
sx={{...customSx}}
|
|
34
|
+
>
|
|
35
|
+
{React.Children.map(children, (child, idx) =>
|
|
36
|
+
React.isValidElement(child)
|
|
37
|
+
? React.cloneElement(child, {
|
|
38
|
+
connectorColor: idx < count - 1
|
|
39
|
+
? child.props.connectorColor ?? 'gray.main'
|
|
40
|
+
: undefined,
|
|
41
|
+
})
|
|
42
|
+
: null
|
|
43
|
+
)}
|
|
44
|
+
</MuiTimeline>
|
|
45
|
+
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default Timeline;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
import MuiTimelineItem from "@mui/lab/TimelineItem";
|
|
3
|
+
import MuiTimelineSeparator from "@mui/lab/TimelineSeparator";
|
|
4
|
+
import MuiTimelineConnector from "@mui/lab/TimelineConnector";
|
|
5
|
+
import MuiTimelineContent from "@mui/lab/TimelineContent";
|
|
6
|
+
import MuiTimelineDot from "@mui/lab/TimelineDot";
|
|
7
|
+
|
|
8
|
+
export interface TimelineItemProps {
|
|
9
|
+
dotColor?: 'primary' | 'secondary' | 'grey' | 'inherit' | 'success' | 'error' | 'info' | 'warning';
|
|
10
|
+
connectorColor?: 'success' | 'error' | 'info' | 'warning' | 'primary' | 'secondary' | 'grey';
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const TimelineItem: React.FC<TimelineItemProps> = ({
|
|
15
|
+
dotColor = 'grey',
|
|
16
|
+
connectorColor,
|
|
17
|
+
children
|
|
18
|
+
}) => {
|
|
19
|
+
const color = connectorColor ? `${connectorColor}.main` : 'secondary.main';
|
|
20
|
+
return (
|
|
21
|
+
<MuiTimelineItem>
|
|
22
|
+
<MuiTimelineSeparator>
|
|
23
|
+
<MuiTimelineDot color={dotColor} />
|
|
24
|
+
{connectorColor !== undefined && <MuiTimelineConnector sx={{ bgcolor: color }} />}
|
|
25
|
+
</MuiTimelineSeparator>
|
|
26
|
+
<MuiTimelineContent>{children}</MuiTimelineContent>
|
|
27
|
+
</MuiTimelineItem>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default TimelineItem
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import Tooltip from './Tooltip'; // Asegúrate que la ruta sea correcta
|
|
4
|
+
import Button from '@mui/material/Button';
|
|
5
|
+
import Stack from '@mui/material/Stack';
|
|
6
|
+
import { Input } from '../Input/Input';
|
|
7
|
+
|
|
8
|
+
// Helper controlado para envolver nuestro Input propio con estado local
|
|
9
|
+
const TooltipNameInput: React.FC = () => {
|
|
10
|
+
const [value, setValue] = useState<string | number>('');
|
|
11
|
+
return (
|
|
12
|
+
<Input
|
|
13
|
+
label="Nombre"
|
|
14
|
+
value={value}
|
|
15
|
+
onChange={(next) => setValue(next)}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// --- Metadatos de la Historia ---
|
|
21
|
+
// Define la configuración global de tu componente en Storybook
|
|
22
|
+
const meta: Meta<typeof Tooltip> = {
|
|
23
|
+
// Título que aparecerá en el sidebar de Storybook
|
|
24
|
+
title: 'Components/Tooltip',
|
|
25
|
+
component: Tooltip,
|
|
26
|
+
// Parámetros para configurar el comportamiento en la documentación
|
|
27
|
+
parameters: {
|
|
28
|
+
layout: 'centered', // Centra el componente en el lienzo
|
|
29
|
+
},
|
|
30
|
+
// Documentación de los argumentos (props)
|
|
31
|
+
|
|
32
|
+
tags: ["autodocs"]
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default meta;
|
|
36
|
+
|
|
37
|
+
// Define el tipo de tu historia para inferir los argumentos (props)
|
|
38
|
+
type Story = StoryObj<typeof Tooltip>;
|
|
39
|
+
|
|
40
|
+
// --- Definición de Historias ---
|
|
41
|
+
|
|
42
|
+
const textoLargo = "Este es un texto de Tooltip muy largo para demostrar cómo el 'maxWidth' limita la longitud de la línea y obliga al texto a saltar a la siguiente línea, mejorando la legibilidad en tooltips largos.";
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Historia 1: Tooltip Básico
|
|
47
|
+
* Muestra el Tooltip sobre un botón sin especificar un ancho máximo.
|
|
48
|
+
*/
|
|
49
|
+
export const TooltipBasico: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
text: 'Información corta y concisa.',
|
|
52
|
+
children: <Button variant="contained">Pasa el ratón aquí</Button>,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Historia 2: Tooltip con Ancho Máximo Específico
|
|
58
|
+
* Muestra un texto largo y aplica un ancho máximo para forzar el salto de línea.
|
|
59
|
+
*/
|
|
60
|
+
export const TooltipConAnchoMaximo: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
text: textoLargo,
|
|
63
|
+
maxWidth: 200, // Aplicamos el ancho máximo
|
|
64
|
+
children: <Button variant="outlined">Tooltip con Ancho Máximo (200px)</Button>,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Historia 3: Tooltip Aplicado a un Campo de Texto (TextField)
|
|
70
|
+
* Demuestra que el componente funciona con cualquier elemento.
|
|
71
|
+
*/
|
|
72
|
+
export const AplicadoAElementoDeFormulario: Story = {
|
|
73
|
+
args: {
|
|
74
|
+
text: 'Introduce tu nombre completo y validado.',
|
|
75
|
+
maxWidth: 300,
|
|
76
|
+
children: <TooltipNameInput />,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Historia 4: Ejemplo en un Contenedor con Estilos
|
|
82
|
+
* Muestra el uso de la propiedad 'sx' opcional en el contenedor.
|
|
83
|
+
*/
|
|
84
|
+
export const ContenedorEstilizado: Story = {
|
|
85
|
+
args: {
|
|
86
|
+
text: 'Este texto se muestra al pasar el ratón sobre el span estilizado.',
|
|
87
|
+
maxWidth: 250,
|
|
88
|
+
sx: { // Estilos aplicados al Box que envuelve el children
|
|
89
|
+
border: '2px dashed #007bff',
|
|
90
|
+
padding: '8px',
|
|
91
|
+
borderRadius: '4px',
|
|
92
|
+
backgroundColor: '#e6f7ff',
|
|
93
|
+
},
|
|
94
|
+
children: <span>Contenedor Estilizado (Hover aquí)</span>,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Historia 5: Variantes Múltiples
|
|
101
|
+
* Utiliza el 'render' para mostrar varios ejemplos a la vez.
|
|
102
|
+
*/
|
|
103
|
+
export const VariantesMultiples: Story = {
|
|
104
|
+
// Necesitamos un Stack para organizar los elementos
|
|
105
|
+
render: (args) => (
|
|
106
|
+
<Stack spacing={4} alignItems="flex-start" sx={{ padding: '20px' }}>
|
|
107
|
+
<Tooltip {...args} text="Tooltip #1: Básico" maxWidth={150}>
|
|
108
|
+
<Button variant="contained" color="primary">Botón A</Button>
|
|
109
|
+
</Tooltip>
|
|
110
|
+
|
|
111
|
+
<Tooltip {...args} text="Tooltip #2: Un poco más de info" maxWidth={180}>
|
|
112
|
+
<Button variant="contained" color="secondary">Botón B</Button>
|
|
113
|
+
</Tooltip>
|
|
114
|
+
|
|
115
|
+
<Tooltip {...args} text={textoLargo} maxWidth={280}>
|
|
116
|
+
<Button variant="text">Texto Largo</Button>
|
|
117
|
+
</Tooltip>
|
|
118
|
+
</Stack>
|
|
119
|
+
),
|
|
120
|
+
// Eliminamos el layout centered para esta historia
|
|
121
|
+
parameters: {
|
|
122
|
+
layout: 'padded',
|
|
123
|
+
},
|
|
124
|
+
// Dejamos el 'args' de 'text' vacío ya que se sobrescribe en el render
|
|
125
|
+
args: {
|
|
126
|
+
text: '',
|
|
127
|
+
children: undefined,
|
|
128
|
+
}
|
|
129
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import MuiTooltip from '@mui/material/Tooltip';
|
|
3
|
+
import Typography from '@mui/material/Typography';
|
|
4
|
+
import Box from '@mui/material/Box';
|
|
5
|
+
import { SxProps, Theme } from '@mui/material';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Propiedades del componente Tooltip.
|
|
9
|
+
* @param {string} text - El texto a mostrar dentro del Tooltip.
|
|
10
|
+
* @param {number} [maxWidth] - El ancho máximo opcional del Tooltip en píxeles.
|
|
11
|
+
* @param {React.ReactNode} children - El elemento al que se le aplicará el Tooltip.
|
|
12
|
+
* @param {SxProps<Theme>} [sx] - Estilos opcionales para el Box contenedor.
|
|
13
|
+
*/
|
|
14
|
+
interface TooltipProps {
|
|
15
|
+
text: string;
|
|
16
|
+
maxWidth?: number;
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
sx?: SxProps<Theme>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Componente Tooltip reutilizable.
|
|
23
|
+
* Muestra un texto al pasar el ratón sobre su elemento hijo,
|
|
24
|
+
* permitiendo un ancho máximo configurable.
|
|
25
|
+
* * @param {TooltipProps} props - Propiedades del Tooltip.
|
|
26
|
+
*/
|
|
27
|
+
const Tooltip: React.FC<TooltipProps> = ({ text, maxWidth, children, sx }) => {
|
|
28
|
+
|
|
29
|
+
const tooltipContentStyle: SxProps<Theme> = {
|
|
30
|
+
|
|
31
|
+
...(maxWidth && { maxWidth: maxWidth }),
|
|
32
|
+
padding: '4px 8px',
|
|
33
|
+
textAlign: 'center',
|
|
34
|
+
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
|
|
39
|
+
<MuiTooltip
|
|
40
|
+
title={
|
|
41
|
+
|
|
42
|
+
<Typography variant="caption" component="span" sx={tooltipContentStyle}>
|
|
43
|
+
{text}
|
|
44
|
+
</Typography>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
placement="top"
|
|
48
|
+
arrow
|
|
49
|
+
>
|
|
50
|
+
|
|
51
|
+
<Box component="span" sx={{ display: 'inline-block', ...sx }}>
|
|
52
|
+
{children}
|
|
53
|
+
</Box>
|
|
54
|
+
</MuiTooltip>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default Tooltip;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Tooltip } from './Tooltip';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material/styles';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
LABEL_COLOR_FILLED,
|
|
5
|
+
LABEL_COLOR_FOCUSED,
|
|
6
|
+
LABEL_COLOR_REST,
|
|
7
|
+
LABEL_FONT_WEIGHT_FILLED,
|
|
8
|
+
LABEL_MARGIN_TOP,
|
|
9
|
+
LABEL_SHRINK_TRANSFORM,
|
|
10
|
+
LABEL_TRANSFORM_ORIGIN,
|
|
11
|
+
LABEL_TRANSITION,
|
|
12
|
+
} from '../../theme/tokens';
|
|
13
|
+
|
|
14
|
+
export type LabelPosition = 'outside' | 'floating';
|
|
15
|
+
|
|
16
|
+
export interface BuildFormFieldSxOptions {
|
|
17
|
+
borderRadius: number | string;
|
|
18
|
+
labelPosition: LabelPosition;
|
|
19
|
+
/**
|
|
20
|
+
* Si es `true`, los selectores incluyen también la API nueva de MUI X
|
|
21
|
+
* Pickers (v8): `MuiPickersInputBase-root`, `MuiPickersOutlinedInput-*`.
|
|
22
|
+
*/
|
|
23
|
+
includePickersApi?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Selectores/valores extra que se mergean SOLO cuando labelPosition === 'outside'.
|
|
26
|
+
* Usar para paddings específicos del componente (p.ej. MuiAutocomplete-input).
|
|
27
|
+
*/
|
|
28
|
+
extraOutsideSx?: Record<string, any>;
|
|
29
|
+
/**
|
|
30
|
+
* Selectores/valores extra que se mergean SOLO cuando labelPosition === 'floating'.
|
|
31
|
+
*/
|
|
32
|
+
extraFloatingSx?: Record<string, any>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const toRadius = (borderRadius: number | string) =>
|
|
36
|
+
typeof borderRadius === 'number' ? `${borderRadius}px` : borderRadius;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Builder del sx compartido por todos los campos de formulario
|
|
40
|
+
* (Input/Select/Autocomplete/DatePicker/DateTimePicker).
|
|
41
|
+
*
|
|
42
|
+
* Produce:
|
|
43
|
+
* - en modo "floating": sólo el border-radius del outlined input;
|
|
44
|
+
* - en modo "outside": todo el pack (margen superior, label flotante afuera,
|
|
45
|
+
* input base, notched outline oculto, helper text sin margen izquierdo).
|
|
46
|
+
*
|
|
47
|
+
* Componentes con paddings o selectores propios pueden inyectarlos vía
|
|
48
|
+
* `extraOutsideSx` / `extraFloatingSx`.
|
|
49
|
+
*/
|
|
50
|
+
export const buildFormFieldSx = ({
|
|
51
|
+
borderRadius,
|
|
52
|
+
labelPosition,
|
|
53
|
+
includePickersApi = false,
|
|
54
|
+
extraOutsideSx,
|
|
55
|
+
extraFloatingSx,
|
|
56
|
+
}: BuildFormFieldSxOptions): SxProps<Theme> => {
|
|
57
|
+
const radius = toRadius(borderRadius);
|
|
58
|
+
|
|
59
|
+
// ── Selectores (alternan según la API de pickers) ────────────────────
|
|
60
|
+
const outlinedRootSelector = includePickersApi
|
|
61
|
+
? '& .MuiOutlinedInput-root, & .MuiPickersOutlinedInput-root'
|
|
62
|
+
: '& .MuiOutlinedInput-root';
|
|
63
|
+
|
|
64
|
+
const inputBaseSelector = includePickersApi
|
|
65
|
+
? '& .MuiInputBase-root, & .MuiPickersInputBase-root'
|
|
66
|
+
: '& .MuiInputBase-root';
|
|
67
|
+
|
|
68
|
+
const notchedOutlineSelector = includePickersApi
|
|
69
|
+
? '& .MuiOutlinedInput-notchedOutline, & .MuiPickersOutlinedInput-notchedOutline'
|
|
70
|
+
: '& .MuiOutlinedInput-notchedOutline';
|
|
71
|
+
|
|
72
|
+
// ── Floating ─────────────────────────────────────────────────────────
|
|
73
|
+
if (labelPosition === 'floating') {
|
|
74
|
+
return {
|
|
75
|
+
[outlinedRootSelector]: { borderRadius: radius },
|
|
76
|
+
...(extraFloatingSx ?? {}),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── Outside ──────────────────────────────────────────────────────────
|
|
81
|
+
return {
|
|
82
|
+
marginTop: LABEL_MARGIN_TOP,
|
|
83
|
+
|
|
84
|
+
'& .MuiInputLabel-root': {
|
|
85
|
+
color: LABEL_COLOR_REST,
|
|
86
|
+
transition: LABEL_TRANSITION,
|
|
87
|
+
|
|
88
|
+
// Estado shrunk (focused o con valor): flota ARRIBA del input.
|
|
89
|
+
'&.MuiInputLabel-shrink': {
|
|
90
|
+
transform: LABEL_SHRINK_TRANSFORM,
|
|
91
|
+
transformOrigin: LABEL_TRANSFORM_ORIGIN,
|
|
92
|
+
color: LABEL_COLOR_FILLED,
|
|
93
|
+
fontWeight: LABEL_FONT_WEIGHT_FILLED,
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
'&.Mui-focused': { color: LABEL_COLOR_FOCUSED },
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
// Input base — centra verticalmente (clásico y, si aplica, pickers).
|
|
100
|
+
[inputBaseSelector]: {
|
|
101
|
+
borderRadius: radius,
|
|
102
|
+
display: 'flex',
|
|
103
|
+
alignItems: 'center',
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
// El label flota afuera: ocultar legend del notch.
|
|
107
|
+
[notchedOutlineSelector]: {
|
|
108
|
+
top: 0,
|
|
109
|
+
'& legend': { display: 'none' },
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
'& .MuiFormHelperText-root': {
|
|
113
|
+
marginLeft: 0,
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
...(extraOutsideSx ?? {}),
|
|
117
|
+
};
|
|
118
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material/styles';
|
|
2
|
+
|
|
3
|
+
import type { LibraryComponentName, PresetStyle } from '../../theme/componentStyles';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Nombre del preset built-in del paquete. No se registra en el theme: si un
|
|
7
|
+
* componente recibe `preset="default"` (o no recibe preset), sólo aplica el
|
|
8
|
+
* estilo base de fábrica.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_PRESET_NAME = 'default';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resuelve el sx de un preset registrado en `theme.styles[componentName]`.
|
|
14
|
+
*
|
|
15
|
+
* - Si `presetName` es `"default"` o `undefined`, devuelve `undefined`
|
|
16
|
+
* (caller sólo aplicará el estilo base del paquete).
|
|
17
|
+
* - Si la entrada en el theme es una función `(theme) => sx`, se ejecuta.
|
|
18
|
+
* - Si la entrada no existe, devuelve `undefined` silenciosamente (no se
|
|
19
|
+
* rompe el componente — la responsabilidad de registrarlo es del consumer).
|
|
20
|
+
*/
|
|
21
|
+
export function resolvePreset(
|
|
22
|
+
componentName: LibraryComponentName,
|
|
23
|
+
presetName: string | undefined,
|
|
24
|
+
theme: Theme,
|
|
25
|
+
): SxProps<Theme> | undefined {
|
|
26
|
+
if (!presetName || presetName === DEFAULT_PRESET_NAME) return undefined;
|
|
27
|
+
|
|
28
|
+
const componentPresets = theme.styles?.[componentName];
|
|
29
|
+
if (!componentPresets) return undefined;
|
|
30
|
+
|
|
31
|
+
const entry: PresetStyle | undefined = componentPresets[presetName];
|
|
32
|
+
if (!entry) return undefined;
|
|
33
|
+
|
|
34
|
+
return typeof entry === 'function' ? entry(theme) : entry;
|
|
35
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, Button, Typography } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
// Importa el componente de demostración unificado
|
|
6
|
+
import ClipboardUnifiedDemo from './ClipboardUnifiedDemo';
|
|
7
|
+
|
|
8
|
+
// Importa las definiciones de las historias
|
|
9
|
+
|
|
10
|
+
import useClipboard from './ClipBoard';
|
|
11
|
+
|
|
12
|
+
// Componente Wrapper (Mantenido para la tipificación)
|
|
13
|
+
const ClipboardDemoWrapper: React.FC = () => <ClipboardUnifiedDemo {...(meta.args as any)} />;
|
|
14
|
+
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Definición de las meta-propiedades para Storybook
|
|
17
|
+
// =============================================================================
|
|
18
|
+
const meta: Meta<typeof ClipboardUnifiedDemo> = {
|
|
19
|
+
title: 'Hooks/useClipboard (Unificado Ref & Función)',
|
|
20
|
+
component: ClipboardUnifiedDemo,
|
|
21
|
+
tags: ['autodocs'],
|
|
22
|
+
parameters: {
|
|
23
|
+
layout: 'padded',
|
|
24
|
+
docs: {
|
|
25
|
+
description: {
|
|
26
|
+
component: 'Hook unificado que soporta la **copia por Referencia (`ref`)** para un solo elemento y la **copia por Función (`copy(text)`)** para iteraciones (tablas, listas), proporcionando feedback visual con un Snackbar de Material UI.',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
// Argumentos de control para el MODO FUNCIÓN
|
|
31
|
+
argTypes: {
|
|
32
|
+
message: { control: 'text', description: 'Mensaje de éxito para el MODO FUNCIÓN.' },
|
|
33
|
+
severity: { control: 'select', options: ['success', 'info', 'warning', 'error'], description: 'Gravedad del Alert para el MODO FUNCIÓN.' },
|
|
34
|
+
duration: { control: { type: 'number', min: 0, max: 5000, step: 500 }, description: 'Duración del Snackbar.' },
|
|
35
|
+
vertical: { control: 'select', options: ['top', 'bottom'], description: 'Posición vertical.' },
|
|
36
|
+
horizontal: { control: 'select', options: ['left', 'center', 'right'], description: 'Posición horizontal.' },
|
|
37
|
+
},
|
|
38
|
+
args: {
|
|
39
|
+
message: '¡Dato copiado con la función!',
|
|
40
|
+
severity: 'success',
|
|
41
|
+
duration: 2000,
|
|
42
|
+
vertical: 'bottom',
|
|
43
|
+
horizontal: 'center',
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default meta;
|
|
48
|
+
type Story = StoryObj<typeof ClipboardUnifiedDemo>;
|
|
49
|
+
|
|
50
|
+
// =============================================================================
|
|
51
|
+
// Historias
|
|
52
|
+
// =============================================================================
|
|
53
|
+
|
|
54
|
+
export const DemoUnificada: Story = {
|
|
55
|
+
name: 'A. Demostración Completa',
|
|
56
|
+
args: {
|
|
57
|
+
message: 'Dato copiado con éxito (Función)',
|
|
58
|
+
severity: 'success',
|
|
59
|
+
},
|
|
60
|
+
parameters: {
|
|
61
|
+
docs: {
|
|
62
|
+
description: {
|
|
63
|
+
story: "Muestra ambos modos de uso (`ref` y `copy`) simultáneamente. El feedback del modo `ref` tiene color amarillo (warning) por defecto para diferenciarlo del modo `copy`."
|
|
64
|
+
},
|
|
65
|
+
// source: { code: MultipleFunctionElementsDefinition.trim() }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// --------------------------------------------------
|
|
71
|
+
|
|
72
|
+
export const UsoBasicoPorRef: Story = {
|
|
73
|
+
name: 'B. Modo Ref: Uso Básico',
|
|
74
|
+
render: () => {
|
|
75
|
+
const { ref, CopyMessage } = useClipboard(); // Sin opciones, usa valores por defecto
|
|
76
|
+
return (
|
|
77
|
+
<Box>
|
|
78
|
+
<Typography ref={ref} sx={{ p: 2, border: '1px solid #ccc', cursor: 'pointer' }}>
|
|
79
|
+
Texto para copiar por ref (click aquí).
|
|
80
|
+
</Typography>
|
|
81
|
+
<CopyMessage />
|
|
82
|
+
</Box>
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
parameters: {
|
|
86
|
+
docs: {
|
|
87
|
+
description: {
|
|
88
|
+
story: "Demuestra el modo **Referencia**, donde el hook adjunta un listener de clic al elemento y copia su `innerText`."
|
|
89
|
+
},
|
|
90
|
+
// source: { code: BasicRefUsageDefinition.trim() }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// --------------------------------------------------
|
|
96
|
+
|
|
97
|
+
// Datos simulados para la demostración del modo función
|
|
98
|
+
const mockData = [
|
|
99
|
+
{ id: 1, content: 'Dato 1' },
|
|
100
|
+
{ id: 2, content: 'Dato 2' },
|
|
101
|
+
{ id: 3, content: 'Dato 3' },
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
export const UsoPorFuncionEnIteracion: Story = {
|
|
105
|
+
name: 'C. Modo Función: Uso en Iteración',
|
|
106
|
+
render: () => {
|
|
107
|
+
const { copy, CopyMessage } = useClipboard({
|
|
108
|
+
message: 'Contenido de la fila copiado.',
|
|
109
|
+
severity: 'info',
|
|
110
|
+
position: { vertical: 'top', horizontal: 'right' }
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<Box>
|
|
115
|
+
<Typography variant="h6">Copiar datos individuales:</Typography>
|
|
116
|
+
{mockData.map(item => (
|
|
117
|
+
<Button
|
|
118
|
+
key={item.id}
|
|
119
|
+
variant="outlined"
|
|
120
|
+
onClick={() => copy(item.content)}
|
|
121
|
+
sx={{ mr: 2 }}
|
|
122
|
+
>
|
|
123
|
+
Copiar: {item.content}
|
|
124
|
+
</Button>
|
|
125
|
+
))}
|
|
126
|
+
<CopyMessage />
|
|
127
|
+
</Box>
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
parameters: {
|
|
131
|
+
docs: {
|
|
132
|
+
description: {
|
|
133
|
+
story: "Demuestra el modo **Función**, donde se llama a `copy(text)` dentro de un bucle, permitiendo controlar exactamente qué texto se copia."
|
|
134
|
+
},
|
|
135
|
+
// source: { code: FunctionModeUsageDefinition.trim() }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// --------------------------------------------------
|
|
141
|
+
|
|
142
|
+
export const CustomConfiguracionRef: Story = {
|
|
143
|
+
name: 'D. Modo Ref: Con Configuración Custom',
|
|
144
|
+
render: () => {
|
|
145
|
+
const { ref, CopyMessage } = useClipboard({
|
|
146
|
+
message: 'Copia OK en la parte superior.',
|
|
147
|
+
severity: 'success',
|
|
148
|
+
duration: 1800,
|
|
149
|
+
position: { vertical: 'top', horizontal: 'left' }
|
|
150
|
+
});
|
|
151
|
+
return (
|
|
152
|
+
<Box>
|
|
153
|
+
<Typography ref={ref} sx={{ p: 2, border: '1px solid #0a0', backgroundColor: '#eef', cursor: 'pointer' }}>
|
|
154
|
+
Clic. Snackbar aparecerá arriba a la izquierda.
|
|
155
|
+
</Typography>
|
|
156
|
+
<CopyMessage />
|
|
157
|
+
</Box>
|
|
158
|
+
);
|
|
159
|
+
},
|
|
160
|
+
parameters: {
|
|
161
|
+
docs: {
|
|
162
|
+
description: {
|
|
163
|
+
story: "Muestra cómo aplicar opciones personalizadas de Snackbar al modo **Referencia**."
|
|
164
|
+
},
|
|
165
|
+
// source: { code: CustomRefUsageDefinition.trim() }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|