@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,110 @@
|
|
|
1
|
+
import React, { type ReactNode, type MouseEvent } from 'react';
|
|
2
|
+
import { Box, Tooltip, Typography } from '@mui/material';
|
|
3
|
+
import type { SxProps, Theme } from '@mui/material/styles';
|
|
4
|
+
|
|
5
|
+
import { useDrawerContext } from './DrawerContext';
|
|
6
|
+
import { buildDrawerItemSx } from './Drawer.sx';
|
|
7
|
+
|
|
8
|
+
export interface DrawerItemProps {
|
|
9
|
+
/** Icono del item. Se muestra siempre (expanded y collapsed). */
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
/** Texto del item. Se oculta cuando el drawer está colapsado (muestra tooltip). */
|
|
12
|
+
label: ReactNode;
|
|
13
|
+
/** Handler de click. */
|
|
14
|
+
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
|
15
|
+
/** Marca el item como activo (destaca color y fondo). */
|
|
16
|
+
active?: boolean;
|
|
17
|
+
/** Marca el item como "destructivo" (p.ej. cerrar sesión): color error. */
|
|
18
|
+
danger?: boolean;
|
|
19
|
+
/** Deshabilita click + bajo opacidad. */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/** Contenido adicional a la derecha del label (badge, count, chevron). */
|
|
22
|
+
endAdornment?: ReactNode;
|
|
23
|
+
/** sx override del consumer, se mergea al final. */
|
|
24
|
+
sx?: SxProps<Theme>;
|
|
25
|
+
className?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Item de un Drawer de shared-library. Lee el estado `collapsed` del
|
|
30
|
+
* DrawerContext para adaptar layout y mostrar tooltip en modo mini.
|
|
31
|
+
*
|
|
32
|
+
* Uso típico:
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <Drawer collapsed={collapsed} onToggleCollapse={toggle}>
|
|
35
|
+
* <DrawerItem icon={<HomeIcon />} label="Inicio" active />
|
|
36
|
+
* <DrawerItem icon={<UserIcon />} label="Perfil" />
|
|
37
|
+
* </Drawer>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function DrawerItem({
|
|
41
|
+
icon,
|
|
42
|
+
label,
|
|
43
|
+
onClick,
|
|
44
|
+
active = false,
|
|
45
|
+
danger = false,
|
|
46
|
+
disabled = false,
|
|
47
|
+
endAdornment,
|
|
48
|
+
sx,
|
|
49
|
+
className,
|
|
50
|
+
}: DrawerItemProps) {
|
|
51
|
+
const { collapsed } = useDrawerContext();
|
|
52
|
+
|
|
53
|
+
const baseSx = buildDrawerItemSx({ collapsed, active, danger });
|
|
54
|
+
|
|
55
|
+
const mergedSx: SxProps<Theme> = [
|
|
56
|
+
baseSx,
|
|
57
|
+
disabled && { pointerEvents: 'none', opacity: 0.5 },
|
|
58
|
+
...(Array.isArray(sx) ? sx : sx ? [sx] : []),
|
|
59
|
+
].filter(Boolean) as SxProps<Theme>;
|
|
60
|
+
|
|
61
|
+
const content = (
|
|
62
|
+
<Box
|
|
63
|
+
role="button"
|
|
64
|
+
tabIndex={disabled ? -1 : 0}
|
|
65
|
+
aria-disabled={disabled}
|
|
66
|
+
onClick={disabled ? undefined : onClick}
|
|
67
|
+
onKeyDown={(event) => {
|
|
68
|
+
if (disabled) return;
|
|
69
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
onClick?.(event as unknown as MouseEvent<HTMLDivElement>);
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
74
|
+
className={className}
|
|
75
|
+
sx={mergedSx}
|
|
76
|
+
>
|
|
77
|
+
{icon !== undefined && (
|
|
78
|
+
<Box className="drawer-item__icon" component="span">
|
|
79
|
+
{icon}
|
|
80
|
+
</Box>
|
|
81
|
+
)}
|
|
82
|
+
<Typography
|
|
83
|
+
component="span"
|
|
84
|
+
variant="body2"
|
|
85
|
+
className="drawer-item__label"
|
|
86
|
+
sx={{ fontWeight: active ? 600 : 400 }}
|
|
87
|
+
>
|
|
88
|
+
{label}
|
|
89
|
+
</Typography>
|
|
90
|
+
{!collapsed && endAdornment && (
|
|
91
|
+
<Box component="span" sx={{ flexShrink: 0, ml: 'auto' }}>
|
|
92
|
+
{endAdornment}
|
|
93
|
+
</Box>
|
|
94
|
+
)}
|
|
95
|
+
</Box>
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Cuando está colapsado, envolvemos en tooltip para revelar el label al hover.
|
|
99
|
+
if (collapsed && typeof label === 'string') {
|
|
100
|
+
return (
|
|
101
|
+
<Tooltip title={label} placement="right" arrow>
|
|
102
|
+
{content}
|
|
103
|
+
</Tooltip>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return content;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default DrawerItem;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { Drawer, default } from './Drawer';
|
|
2
|
+
export { DrawerItem } from './DrawerItem';
|
|
3
|
+
export { useDrawerContext } from './DrawerContext';
|
|
4
|
+
export type {
|
|
5
|
+
DrawerProps,
|
|
6
|
+
DrawerVariant,
|
|
7
|
+
DrawerAnchor,
|
|
8
|
+
} from './Drawer';
|
|
9
|
+
export type { DrawerItemProps } from './DrawerItem';
|
|
10
|
+
export type { DrawerContextValue } from './DrawerContext';
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { Box, Button, List, ListItem, ListItemText, Typography } from '@mui/material';
|
|
4
|
+
import Flyout from './Flyout'; // Adjust the import path if your Flyout.tsx is in a different directory
|
|
5
|
+
import { Input } from '../Input/Input';
|
|
6
|
+
|
|
7
|
+
// Define the Meta for the Flyout component
|
|
8
|
+
const meta: Meta<typeof Flyout> = {
|
|
9
|
+
title: 'Components/Flyout', // Category in Storybook
|
|
10
|
+
component: Flyout,
|
|
11
|
+
tags: ['autodocs'], // Automatically generate documentation
|
|
12
|
+
argTypes: {
|
|
13
|
+
open: {
|
|
14
|
+
control: 'boolean',
|
|
15
|
+
description: 'Controls the visibility of the flyout.',
|
|
16
|
+
},
|
|
17
|
+
onClose: {
|
|
18
|
+
action: 'closed', // Log the onClose event in Storybook actions panel
|
|
19
|
+
description: 'Callback function when the flyout is requested to close.',
|
|
20
|
+
},
|
|
21
|
+
title: {
|
|
22
|
+
control: 'text',
|
|
23
|
+
description: 'Title displayed at the top of the flyout.',
|
|
24
|
+
},
|
|
25
|
+
anchor: {
|
|
26
|
+
control: 'select',
|
|
27
|
+
options: ['left', 'top', 'right', 'bottom'],
|
|
28
|
+
description: 'Side from which the flyout will appear.',
|
|
29
|
+
},
|
|
30
|
+
size: {
|
|
31
|
+
control: 'select',
|
|
32
|
+
options: ['small', 'medium', 'large'],
|
|
33
|
+
description: 'Predefined size of the flyout (width for left/right, height for top/bottom).',
|
|
34
|
+
},
|
|
35
|
+
height: {
|
|
36
|
+
control: 'text',
|
|
37
|
+
description: 'Custom height for top/bottom anchored flyouts (e.g., "50vh", "300px").',
|
|
38
|
+
if: { arg: 'anchor', eq: ['top', 'bottom'] }, // Only show if anchor is top or bottom
|
|
39
|
+
},
|
|
40
|
+
children: {
|
|
41
|
+
control: 'text', // Can be set to 'text' for simple string content, or 'disable'
|
|
42
|
+
description: 'Content to be rendered inside the flyout.',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
parameters: {
|
|
46
|
+
docs: {
|
|
47
|
+
description: {
|
|
48
|
+
component: 'A customizable Drawer component that slides in from a specified anchor point, ideal for displaying supplementary content or forms without navigating away from the main page.',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default meta;
|
|
55
|
+
|
|
56
|
+
type Story = StoryObj<typeof Flyout>;
|
|
57
|
+
|
|
58
|
+
// --- Helper component for demonstration content ---
|
|
59
|
+
const DefaultValueInput: React.FC = () => {
|
|
60
|
+
const [value, setValue] = useState<string | number>('Some text');
|
|
61
|
+
return (
|
|
62
|
+
<Box sx={{ my: 2 }}>
|
|
63
|
+
<Input
|
|
64
|
+
label="Input Field"
|
|
65
|
+
value={value}
|
|
66
|
+
onChange={(next) => setValue(next)}
|
|
67
|
+
/>
|
|
68
|
+
</Box>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const DemoContent: React.FC = () => (
|
|
73
|
+
<Box>
|
|
74
|
+
<Typography variant="body1" paragraph>
|
|
75
|
+
This is some example content inside the flyout. It can contain any React elements.
|
|
76
|
+
</Typography>
|
|
77
|
+
<List dense>
|
|
78
|
+
<ListItem disablePadding>
|
|
79
|
+
<ListItemText primary="Item 1:" secondary="Detail A" />
|
|
80
|
+
</ListItem>
|
|
81
|
+
<ListItem disablePadding>
|
|
82
|
+
<ListItemText primary="Item 2:" secondary="Detail B" />
|
|
83
|
+
</ListItem>
|
|
84
|
+
<ListItem disablePadding>
|
|
85
|
+
<ListItemText primary="Item 3:" secondary="Detail C" />
|
|
86
|
+
</ListItem>
|
|
87
|
+
{[...Array(15)].map((_, i) => ( // Add more items to demonstrate scrolling
|
|
88
|
+
<ListItem key={i + 4} disablePadding>
|
|
89
|
+
<ListItemText primary={`More Item ${i + 4}:`} secondary={`More Detail ${i + 4}`} />
|
|
90
|
+
</ListItem>
|
|
91
|
+
))}
|
|
92
|
+
</List>
|
|
93
|
+
<DefaultValueInput />
|
|
94
|
+
<Button variant="contained" color="primary" sx={{ mt: 2 }}>
|
|
95
|
+
Action Button
|
|
96
|
+
</Button>
|
|
97
|
+
</Box>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
// --- Stories ---
|
|
102
|
+
|
|
103
|
+
// Default story for a basic right-anchored flyout
|
|
104
|
+
export const Default: Story = {
|
|
105
|
+
render: (args) => {
|
|
106
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
107
|
+
return (
|
|
108
|
+
<Box sx={{ height: '200vh', p: 2, backgroundColor: '#f0f0f0' }}> {/* Added height to simulate scrollable page */}
|
|
109
|
+
<Typography variant="h5" mb={2}>Default Flyout Example</Typography>
|
|
110
|
+
<Button variant="contained" onClick={() => setIsOpen(true)}>
|
|
111
|
+
Open Flyout (Right)
|
|
112
|
+
</Button>
|
|
113
|
+
<Flyout {...args} open={isOpen} onClose={() => setIsOpen(false)}>
|
|
114
|
+
<DemoContent />
|
|
115
|
+
</Flyout>
|
|
116
|
+
</Box>
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
args: {
|
|
120
|
+
title: 'Flyout Title',
|
|
121
|
+
anchor: 'right',
|
|
122
|
+
size: 'medium',
|
|
123
|
+
// children is handled by the render function with DemoContent
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Story for a small flyout from the left
|
|
128
|
+
export const SmallLeft: Story = {
|
|
129
|
+
render: (args) => {
|
|
130
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
131
|
+
return (
|
|
132
|
+
<Box sx={{ p: 2 }}>
|
|
133
|
+
<Typography variant="h5" mb={2}>Small Flyout from Left</Typography>
|
|
134
|
+
<Button variant="contained" onClick={() => setIsOpen(true)}>
|
|
135
|
+
Open Small Flyout (Left)
|
|
136
|
+
</Button>
|
|
137
|
+
<Flyout {...args} open={isOpen} onClose={() => setIsOpen(false)}>
|
|
138
|
+
<DemoContent />
|
|
139
|
+
</Flyout>
|
|
140
|
+
</Box>
|
|
141
|
+
);
|
|
142
|
+
},
|
|
143
|
+
args: {
|
|
144
|
+
title: 'Small Flyout',
|
|
145
|
+
anchor: 'left',
|
|
146
|
+
size: 'small',
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Story for a large flyout from the right
|
|
151
|
+
export const LargeRight: Story = {
|
|
152
|
+
render: (args) => {
|
|
153
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
154
|
+
return (
|
|
155
|
+
<Box sx={{ p: 2 }}>
|
|
156
|
+
<Typography variant="h5" mb={2}>Large Flyout from Right</Typography>
|
|
157
|
+
<Button variant="contained" onClick={() => setIsOpen(true)}>
|
|
158
|
+
Open Large Flyout (Right)
|
|
159
|
+
</Button>
|
|
160
|
+
<Flyout {...args} open={isOpen} onClose={() => setIsOpen(false)}>
|
|
161
|
+
<DemoContent />
|
|
162
|
+
</Flyout>
|
|
163
|
+
</Box>
|
|
164
|
+
);
|
|
165
|
+
},
|
|
166
|
+
args: {
|
|
167
|
+
title: 'Large Flyout',
|
|
168
|
+
anchor: 'right',
|
|
169
|
+
size: 'large',
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Story for a top-anchored flyout with custom height
|
|
174
|
+
export const TopAnchor: Story = {
|
|
175
|
+
render: (args) => {
|
|
176
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
177
|
+
return (
|
|
178
|
+
<Box sx={{ p: 2 }}>
|
|
179
|
+
<Typography variant="h5" mb={2}>Top Anchored Flyout</Typography>
|
|
180
|
+
<Button variant="contained" onClick={() => setIsOpen(true)}>
|
|
181
|
+
Open Top Flyout
|
|
182
|
+
</Button>
|
|
183
|
+
<Flyout {...args} open={isOpen} onClose={() => setIsOpen(false)}>
|
|
184
|
+
<DemoContent />
|
|
185
|
+
</Flyout>
|
|
186
|
+
</Box>
|
|
187
|
+
);
|
|
188
|
+
},
|
|
189
|
+
args: {
|
|
190
|
+
title: 'Top Flyout',
|
|
191
|
+
anchor: 'top',
|
|
192
|
+
height: '300px', // Custom height for top/bottom anchor
|
|
193
|
+
size: 'medium', // Size prop still applies but width is 100% for top/bottom
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Story for a bottom-anchored flyout with custom height
|
|
198
|
+
export const BottomAnchor: Story = {
|
|
199
|
+
render: (args) => {
|
|
200
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
201
|
+
return (
|
|
202
|
+
<Box sx={{ p: 2 }}>
|
|
203
|
+
<Typography variant="h5" mb={2}>Bottom Anchored Flyout</Typography>
|
|
204
|
+
<Button variant="contained" onClick={() => setIsOpen(true)}>
|
|
205
|
+
Open Bottom Flyout
|
|
206
|
+
</Button>
|
|
207
|
+
<Flyout {...args} open={isOpen} onClose={() => setIsOpen(false)}>
|
|
208
|
+
<DemoContent />
|
|
209
|
+
</Flyout>
|
|
210
|
+
</Box>
|
|
211
|
+
);
|
|
212
|
+
},
|
|
213
|
+
args: {
|
|
214
|
+
title: 'Bottom Flyout',
|
|
215
|
+
anchor: 'bottom',
|
|
216
|
+
height: '40vh', // Custom height using viewport units
|
|
217
|
+
size: 'medium',
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Story with no title
|
|
222
|
+
export const NoTitle: Story = {
|
|
223
|
+
render: (args) => {
|
|
224
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
225
|
+
return (
|
|
226
|
+
<Box sx={{ p: 2 }}>
|
|
227
|
+
<Typography variant="h5" mb={2}>Flyout with No Title</Typography>
|
|
228
|
+
<Button variant="contained" onClick={() => setIsOpen(true)}>
|
|
229
|
+
Open Flyout (No Title)
|
|
230
|
+
</Button>
|
|
231
|
+
<Flyout {...args} open={isOpen} onClose={() => setIsOpen(false)}>
|
|
232
|
+
<DemoContent />
|
|
233
|
+
</Flyout>
|
|
234
|
+
</Box>
|
|
235
|
+
);
|
|
236
|
+
},
|
|
237
|
+
args: {
|
|
238
|
+
title: undefined, // Explicitly no title
|
|
239
|
+
anchor: 'right',
|
|
240
|
+
size: 'medium',
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// Story with custom content
|
|
245
|
+
export const CustomContent: Story = {
|
|
246
|
+
render: (args) => {
|
|
247
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
248
|
+
const [customText, setCustomText] = useState('This is some custom text that can be edited.');
|
|
249
|
+
return (
|
|
250
|
+
<Box sx={{ p: 2 }}>
|
|
251
|
+
<Typography variant="h5" mb={2}>Flyout with Custom Content</Typography>
|
|
252
|
+
<Button variant="contained" onClick={() => setIsOpen(true)}>
|
|
253
|
+
Open Custom Content Flyout
|
|
254
|
+
</Button>
|
|
255
|
+
<Flyout {...args} open={isOpen} onClose={() => setIsOpen(false)}>
|
|
256
|
+
<Box sx={{ p: 2 }}>
|
|
257
|
+
<Typography variant="h6" gutterBottom>Custom Form</Typography>
|
|
258
|
+
<Box sx={{ mb: 2 }}>
|
|
259
|
+
<Input
|
|
260
|
+
label="Your Message"
|
|
261
|
+
multiline
|
|
262
|
+
rows={4}
|
|
263
|
+
fullWidth
|
|
264
|
+
value={customText}
|
|
265
|
+
onChange={(next) => setCustomText(String(next))}
|
|
266
|
+
/>
|
|
267
|
+
</Box>
|
|
268
|
+
<Button variant="contained" color="secondary">
|
|
269
|
+
Submit Custom Content
|
|
270
|
+
</Button>
|
|
271
|
+
</Box>
|
|
272
|
+
</Flyout>
|
|
273
|
+
</Box>
|
|
274
|
+
);
|
|
275
|
+
},
|
|
276
|
+
args: {
|
|
277
|
+
title: 'Custom Content Flyout',
|
|
278
|
+
anchor: 'right',
|
|
279
|
+
size: 'medium',
|
|
280
|
+
// children is handled by the render function
|
|
281
|
+
},
|
|
282
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
Typography,
|
|
5
|
+
IconButton,
|
|
6
|
+
Divider,
|
|
7
|
+
Drawer,
|
|
8
|
+
List,
|
|
9
|
+
ListItem,
|
|
10
|
+
ListItemText,
|
|
11
|
+
TextField,
|
|
12
|
+
Button,
|
|
13
|
+
} from '@mui/material';
|
|
14
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
15
|
+
|
|
16
|
+
// Define the size types for the Flyout
|
|
17
|
+
type FlyoutSize = 'small' | 'medium' | 'large';
|
|
18
|
+
|
|
19
|
+
// Define the props for the Flyout component
|
|
20
|
+
export interface FlyoutProps {
|
|
21
|
+
open: boolean;
|
|
22
|
+
onClose: () => void;
|
|
23
|
+
title?: string;
|
|
24
|
+
anchor?: 'left' | 'top' | 'right' | 'bottom';
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
size?: FlyoutSize; // New prop for predefined size
|
|
27
|
+
height?: string | number; // Allows custom height for top/bottom anchors
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const Flyout: React.FC<FlyoutProps> = ({
|
|
31
|
+
open,
|
|
32
|
+
onClose,
|
|
33
|
+
title,
|
|
34
|
+
anchor = 'right',
|
|
35
|
+
children,
|
|
36
|
+
size = 'medium', // Default size: 'medium'
|
|
37
|
+
height = 'auto',
|
|
38
|
+
}) => {
|
|
39
|
+
// Mapping of predefined sizes to pixel widths
|
|
40
|
+
const getWidthBySize = (flyoutSize: FlyoutSize): number => {
|
|
41
|
+
const baseMultiplier = 1.15; // 15% larger than base
|
|
42
|
+
switch (flyoutSize) {
|
|
43
|
+
case 'small':
|
|
44
|
+
return Math.round(300 * baseMultiplier); // ~345px
|
|
45
|
+
case 'medium':
|
|
46
|
+
return Math.round(450 * baseMultiplier); // ~518px (rounded)
|
|
47
|
+
case 'large':
|
|
48
|
+
return Math.round(600 * baseMultiplier); // ~690px
|
|
49
|
+
default:
|
|
50
|
+
return Math.round(450 * baseMultiplier); // Fallback, ~518px
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Calculate width based on size prop for left/right anchors
|
|
55
|
+
const calculatedWidth = getWidthBySize(size);
|
|
56
|
+
|
|
57
|
+
// Determine the style for the content box based on anchor
|
|
58
|
+
const contentStyle = useMemo(() => {
|
|
59
|
+
if (anchor === 'left' || anchor === 'right') {
|
|
60
|
+
return { width: calculatedWidth }; // Use the calculated width
|
|
61
|
+
} else {
|
|
62
|
+
// For 'top' or 'bottom' anchors, width is 100% and height is controlled
|
|
63
|
+
return { height: height, width: '100%' };
|
|
64
|
+
}
|
|
65
|
+
}, [anchor, calculatedWidth, height]);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Drawer
|
|
69
|
+
anchor={anchor}
|
|
70
|
+
open={open}
|
|
71
|
+
onClose={onClose}
|
|
72
|
+
// Allows closing with Escape key, but prevents closing by clicking outside if ModalProps.disableBackdropClick is true
|
|
73
|
+
disableEscapeKeyDown={false}
|
|
74
|
+
ModalProps={{
|
|
75
|
+
// Keep mounted for better performance if content is complex or needs to retain state
|
|
76
|
+
keepMounted: true,
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
<Box
|
|
80
|
+
sx={{
|
|
81
|
+
...contentStyle, // Apply calculated width/height
|
|
82
|
+
display: 'flex',
|
|
83
|
+
flexDirection: 'column',
|
|
84
|
+
p: 2, // Padding around the content
|
|
85
|
+
backgroundColor: (theme) => theme.palette.background.paper, // Use theme background color
|
|
86
|
+
color: (theme) => theme.palette.text.primary, // Use theme text color
|
|
87
|
+
height: '100%', // Ensure the main container of the Drawer takes full height
|
|
88
|
+
}}
|
|
89
|
+
role="presentation" // ARIA role for accessibility
|
|
90
|
+
>
|
|
91
|
+
{/* Header section with title and close button */}
|
|
92
|
+
<Box
|
|
93
|
+
sx={{
|
|
94
|
+
display: 'flex',
|
|
95
|
+
justifyContent: 'space-between',
|
|
96
|
+
alignItems: 'center',
|
|
97
|
+
mb: 2, // Margin bottom for separation from content
|
|
98
|
+
flexShrink: 0, // Prevent the header from shrinking when content scrolls
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
{title && (
|
|
102
|
+
<Typography variant="h6" component="div" sx={{ fontWeight: 'bold' }}>
|
|
103
|
+
{title}
|
|
104
|
+
</Typography>
|
|
105
|
+
)}
|
|
106
|
+
<IconButton onClick={onClose} sx={{ ml: 'auto' }}>
|
|
107
|
+
<CloseIcon />
|
|
108
|
+
</IconButton>
|
|
109
|
+
</Box>
|
|
110
|
+
|
|
111
|
+
<Divider sx={{ mb: 2 }} /> {/* Separator line */}
|
|
112
|
+
|
|
113
|
+
{/* Scrollable content area */}
|
|
114
|
+
<Box sx={{ flexGrow: 1, overflowY: 'auto' }}>
|
|
115
|
+
{children}
|
|
116
|
+
</Box>
|
|
117
|
+
</Box>
|
|
118
|
+
</Drawer>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export default Flyout;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Flyout } from './Flyout';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const DefaultGalleryDefinition = `
|
|
2
|
+
<Gallery
|
|
3
|
+
maxWidth="600px"
|
|
4
|
+
items={[
|
|
5
|
+
{
|
|
6
|
+
type: "image",
|
|
7
|
+
url: "https://images.unsplash.com/photo-1506744038136-46273834b3fb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80",
|
|
8
|
+
title: "Image 1",
|
|
9
|
+
thumbnail: "https://images.unsplash.com/photo-1506744038136-46273834b3fb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
type: "image",
|
|
13
|
+
url: "https://images.unsplash.com/photo-1511765224389-37f0e77cf0eb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80",
|
|
14
|
+
title: "Image 2",
|
|
15
|
+
thumbnail: "https://images.unsplash.com/photo-1511765224389-37f0e77cf0eb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
type: "image",
|
|
19
|
+
url: "https://images.unsplash.com/photo-1500534623283-312aade485b7?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80",
|
|
20
|
+
title: "Image 3",
|
|
21
|
+
thumbnail: "https://images.unsplash.com/photo-1500534623283-312aade485b7?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: "video",
|
|
25
|
+
url: "https://www.w3schools.com/html/mov_bbb.mp4",
|
|
26
|
+
title: "Video 1",
|
|
27
|
+
thumbnail: "https://peach.blender.org/wp-content/uploads/title_anouncement.jpg?x11217"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: "image",
|
|
31
|
+
url: "https://images.unsplash.com/photo-1494526585095-c41746248156?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80",
|
|
32
|
+
title: "Image 4",
|
|
33
|
+
thumbnail: "https://images.unsplash.com/photo-1494526585095-c41746248156?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
|
|
34
|
+
}
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
`.trim();
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import Gallery from "./Gallery";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { DefaultGalleryDefinition } from "./Gallery.definition";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Gallery> = {
|
|
6
|
+
title: "Components/Gallery",
|
|
7
|
+
component: Gallery,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "centered"
|
|
10
|
+
},
|
|
11
|
+
tags: ["autodocs"]
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
type Story = StoryObj<typeof Gallery>;
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export const Default: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
maxWidth: "600px",
|
|
23
|
+
items: [
|
|
24
|
+
{
|
|
25
|
+
type: "image",
|
|
26
|
+
url: "https://images.unsplash.com/photo-1506744038136-46273834b3fb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80",
|
|
27
|
+
title: "Image 1",
|
|
28
|
+
thumbnail: "https://images.unsplash.com/photo-1506744038136-46273834b3fb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: "image",
|
|
32
|
+
url: "https://images.unsplash.com/photo-1511765224389-37f0e77cf0eb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80",
|
|
33
|
+
title: "Image 2",
|
|
34
|
+
thumbnail: "https://images.unsplash.com/photo-1511765224389-37f0e77cf0eb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: "image",
|
|
38
|
+
url: "https://images.unsplash.com/photo-1500534623283-312aade485b7?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80",
|
|
39
|
+
title: "Image 3",
|
|
40
|
+
thumbnail: "https://images.unsplash.com/photo-1500534623283-312aade485b7?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: "video",
|
|
44
|
+
url: "https://www.w3schools.com/html/mov_bbb.mp4",
|
|
45
|
+
title: "Video 1",
|
|
46
|
+
thumbnail: "https://peach.blender.org/wp-content/uploads/title_anouncement.jpg?x11217"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: "image",
|
|
50
|
+
url: "https://images.unsplash.com/photo-1494526585095-c41746248156?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80",
|
|
51
|
+
title: "Image 4",
|
|
52
|
+
thumbnail: "https://images.unsplash.com/photo-1494526585095-c41746248156?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80"
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
render: (args) => (
|
|
59
|
+
|
|
60
|
+
<Gallery {...args} />
|
|
61
|
+
|
|
62
|
+
),
|
|
63
|
+
parameters: {
|
|
64
|
+
docs: {
|
|
65
|
+
description: {
|
|
66
|
+
story: "Ejemplo básico de Gallery, permite ver imagenes y videos"
|
|
67
|
+
},
|
|
68
|
+
source: {
|
|
69
|
+
code: DefaultGalleryDefinition
|
|
70
|
+
},
|
|
71
|
+
argTypes: {
|
|
72
|
+
maxWidth: { description: 'Permite ajustar el maxwidht de el componente gallery' },
|
|
73
|
+
items: { control: false, description: `Array de objetos con la información de las imágenes y videos a mostrar, la estructura de los objetos es la siguiente : {
|
|
74
|
+
type: "image" | "video",
|
|
75
|
+
url: string,
|
|
76
|
+
title: string,
|
|
77
|
+
thumbnail: string
|
|
78
|
+
}` }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|