Skip to content

Commit 96c9e6c

Browse files
feat(organisms): modal multiple
1 parent 820e243 commit 96c9e6c

14 files changed

Lines changed: 601 additions & 160 deletions

File tree

src/documentation/components/Organisms/Modals.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
ModalButtons,
99
ModalContent,
1010
} from '@/organisms/Modals/'
11+
import { ModalMultiple, ModalMultipleProps } from '@/organisms/Modals/ModalMultiple/ModalMultiple'
12+
import { useState } from 'react'
1113

1214
export const ModalDemo = ({
1315
type,
@@ -298,3 +300,40 @@ export const ModalAlertDemo = ({
298300
</>
299301
)
300302
}
303+
304+
export const ModalMultipleDemo = (): JSX.Element => {
305+
const { isOpen, onOpen, onClose } = useDisclosure()
306+
const [type, setType] = useState<ModalMultipleProps['type']>('modal')
307+
return (
308+
<>
309+
<BtnPrimary onClick={onOpen}>ModalMultiple</BtnPrimary>
310+
<ModalMultiple
311+
type={type}
312+
isOpen={isOpen}
313+
onClose={onClose}
314+
title={type === 'modal' ? 'Confirmación' : '¿Seguro que deseas borrar esta pregunta?'}
315+
status="info"
316+
description="Por favor escoge otro horario."
317+
>
318+
{type === 'modal' ? (
319+
<ModalContent>
320+
<p>alumnos, además de definir el uso de la plataforma de estudio.</p>
321+
<ModalButtons>
322+
<BtnPrimary onClick={() => setType('modal')}>Guardar</BtnPrimary>
323+
<BtnSecondary onClick={() => onClose()}>Cancelar</BtnSecondary>
324+
</ModalButtons>
325+
</ModalContent>
326+
) : (
327+
<ModalAlertButtons>
328+
<BtnLink as="button" onClick={() => setType('modal')}>
329+
Aceptar
330+
</BtnLink>
331+
<BtnLink as="button" onClick={() => onClose()}>
332+
Cancelar
333+
</BtnLink>
334+
</ModalAlertButtons>
335+
)}
336+
</ModalMultiple>
337+
</>
338+
)
339+
}

src/documentation/pages/Organisms/Modals.tsx

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { MyHeading, MyText, MyTitle, Code, ListComponent } from '@/documentation/components'
2-
import { ModalAlertDemo, ModalDemo } from '@/documentation/components/Organisms/Modals'
2+
import {
3+
ModalAlertDemo,
4+
ModalDemo,
5+
ModalMultipleDemo,
6+
} from '@/documentation/components/Organisms/Modals'
37

48
export const ViewModals = (): JSX.Element => {
59
return (
610
<>
7-
<div>
8-
<p>ejemplo multiple</p>
9-
</div>
1011
<MyHeading>Modales</MyHeading>
1112
<MyText>
1213
Para los modales, tenemos dos tipos de componentes: Modal y ModalAlert. Cada uno tiene sus{' '}
@@ -134,6 +135,72 @@ export function View(){
134135
withoutDescription
135136
/>
136137
</ListComponent>
138+
<MyTitle>Tipo ModalMultiple</MyTitle>
139+
<MyText>
140+
Es un componente unificador que permite renderizar dos tipos de modal distintos dentro de un
141+
mismo flujo: <br />
142+
<br /> <strong>modal</strong> → Modal tradicional (contenido libre, cabecera, footer,
143+
botones, scroll).
144+
<br /> <strong>modalAlert / modalLoading</strong> → Modal de alerta o de carga, con
145+
contenido reducido y foco en la acción del usuario. <br />
146+
<br /> Está pensado para casos donde el estado del modal cambia (por ejemplo,
147+
confirmaciones, advertencias o pasos intermedios) sin necesidad de cerrar y volver a abrir
148+
otro modal.
149+
</MyText>
150+
<ListComponent>
151+
<ModalMultipleDemo />
152+
</ListComponent>
153+
<Code
154+
text={`
155+
import {
156+
ModalMultiple,
157+
ModalMultipleProps,
158+
BtnPrimary,
159+
BtnSecondary,
160+
ModalContent,
161+
ModalButtons,
162+
ModalAlertButtons,
163+
BtnLink
164+
} from '@eclass/ui-kit'
165+
import { useDisclosure } from '@chakra-ui/react'
166+
167+
export function View(){
168+
const { isOpen, onOpen, onClose } = useDisclosure()
169+
const [type, setType] = useState<ModalMultipleProps['type']>('modal')
170+
171+
return (
172+
<>
173+
<BtnPrimary onClick={onOpen}>ModalMultiple</BtnPrimary>
174+
<ModalMultiple
175+
type={type}
176+
isOpen={isOpen}
177+
onClose={onClose}
178+
title={type === 'modal' ? 'Confirmación' : '¿Seguro que deseas borrar esta pregunta?'}
179+
status="info"
180+
description="Por favor escoge otro horario."
181+
>
182+
{type === 'modal' ? (
183+
<ModalContent>
184+
<p>alumnos, además de definir el uso de la plataforma de estudio.</p>
185+
<ModalButtons>
186+
<BtnPrimary onClick={() => setType('modalAlert')}>Guardar</BtnPrimary>
187+
<BtnSecondary onClick={() => onClose()}>Cancelar</BtnSecondary>
188+
</ModalButtons>
189+
</ModalContent>
190+
) : (
191+
<ModalAlertButtons>
192+
<BtnLink as="button" onClick={() => setType('modal')}>
193+
Aceptar
194+
</BtnLink>
195+
<BtnLink as="button" onClick={() => onClose()}>
196+
Cancelar
197+
</BtnLink>
198+
</ModalAlertButtons>
199+
)}
200+
</ModalMultiple>
201+
</>)
202+
}`}
203+
/>
137204
</>
138205
)
139206
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export {
2525
ModalButtons,
2626
ModalContent,
2727
ModalAlertButtons,
28+
ModalMultiple,
29+
ModalMultipleProps,
2830
} from './organisms/Modals'
2931
export { ModalAlert } from './organisms/ModalAlert'
3032
export { Eventos } from './organisms/Events'
Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
import { Modal as ChakraModal, ModalOverlay } from '@chakra-ui/react'
1+
import { Modal as ChakraModal, ModalOverlay, ModalContent } from '@chakra-ui/react'
22

33
import { IModal } from '../types'
44
import { ModalContentBase } from './ModalContentBase'
5+
import { useModalConfig } from './useModalConfig'
56

67
export const uiKitModalIsDesktop = 641
8+
export const ModalPadding = {
9+
py: '32px',
10+
px: '24px',
11+
}
712

813
export const Modal = ({
914
children,
@@ -19,29 +24,36 @@ export const Modal = ({
1924
}: IModal): JSX.Element => {
2025
const isInside = scrollBehavior === 'inside' || fixedButtons
2126

27+
const modalConfig = useModalConfig({
28+
closeOnOverlayClick,
29+
scrollBehavior,
30+
fixedButtons,
31+
withoutMargin,
32+
})
33+
2234
return (
23-
<>
24-
<ChakraModal
25-
closeOnOverlayClick={closeOnOverlayClick}
26-
closeOnEsc={closeOnOverlayClick}
27-
isOpen={isOpen}
28-
motionPreset="scale"
29-
onClose={onClose}
30-
scrollBehavior={isInside ? 'inside' : 'outside'}
31-
autoFocus={autoFocus}
32-
blockScrollOnMount={false}
33-
>
34-
<ModalOverlay />
35+
<ChakraModal
36+
closeOnOverlayClick={modalConfig.closeOnOverlayClick}
37+
closeOnEsc={modalConfig.closeOnEsc}
38+
isOpen={isOpen}
39+
motionPreset="scale"
40+
onClose={onClose}
41+
scrollBehavior={isInside ? 'inside' : 'outside'}
42+
autoFocus={autoFocus}
43+
blockScrollOnMount={false}
44+
>
45+
<ModalOverlay />
46+
<ModalContent {...modalConfig.contentProps}>
3547
<ModalContentBase
36-
isInside={isInside}
3748
fixedButtons={fixedButtons}
3849
withoutMargin={withoutMargin}
3950
title={title}
4051
closeOnOverlayClick={closeOnOverlayClick}
4152
fixedSubtitle={fixedSubtitle}
42-
children={children}
43-
/>
44-
</ChakraModal>
45-
</>
53+
>
54+
{children}
55+
</ModalContentBase>
56+
</ModalContent>
57+
</ChakraModal>
4658
)
4759
}
Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,22 @@
1-
import {
2-
Box,
3-
ModalCloseButton,
4-
ModalContent as ChakraModalContent,
5-
ModalHeader,
6-
useMediaQuery,
7-
} from '@chakra-ui/react'
1+
import { Box, ModalCloseButton, ModalHeader, useMediaQuery } from '@chakra-ui/react'
82

93
import { vars } from '@/theme'
104
import { IModalContentBase } from '../types'
115
import { uiKitModalIsDesktop } from './Modal'
126

137
export const ModalContentBase = ({
14-
isInside,
15-
fixedButtons,
16-
withoutMargin,
17-
title,
8+
children,
189
closeOnOverlayClick,
1910
fixedSubtitle,
20-
children,
11+
title,
12+
withoutMargin,
2113
}: IModalContentBase): JSX.Element => {
2214
const [isDesktop] = useMediaQuery(`(min-width: ${uiKitModalIsDesktop}px)`)
2315
const py = '32px'
2416
const px = '24px'
2517

2618
return (
27-
<ChakraModalContent
28-
maxH={isInside ? '100dvh' : 'auto'}
29-
minH={isDesktop ? '300px' : '100dvh'}
30-
padding={0}
31-
width="100%"
32-
sx={{
33-
bgColor: vars('colors-neutral-white'),
34-
borderRadius: isDesktop ? '8px' : 0,
35-
mt: isDesktop ? '48px' : 0,
36-
mb: isDesktop ? '48px' : 0,
37-
marginX: isDesktop ? 'auto' : 0,
38-
maxH: isInside ? 'calc(100dvh - 96px)' : 'auto',
39-
maxWidth: isDesktop ? '690px' : '100%',
40-
41-
...(fixedButtons && {
42-
'.uikit-modalContent': {
43-
pb: 0,
44-
},
45-
'.uikit-modalButtons': {
46-
py: py,
47-
px: px,
48-
},
49-
}),
50-
...(withoutMargin && {
51-
'.uikit-modalContent': {
52-
pt: 0,
53-
px: 0,
54-
},
55-
'.uikit-modalButtons': {
56-
px: px,
57-
},
58-
}),
59-
}}
60-
>
19+
<>
6120
<ModalHeader
6221
bg={vars('colors-main-deepSkyBlue')}
6322
borderTopRadius={isDesktop ? '8px' : 0}
@@ -98,6 +57,6 @@ export const ModalContentBase = ({
9857
</Box>
9958
)}
10059
{children}
101-
</ChakraModalContent>
60+
</>
10261
)
10362
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { BoxProps, useMediaQuery } from '@chakra-ui/react'
2+
import { vars } from '@/theme'
3+
import { ModalPadding, uiKitModalIsDesktop } from '../Modal/Modal'
4+
5+
interface ModalConfig {
6+
closeOnOverlayClick: boolean
7+
closeOnEsc: boolean
8+
scrollBehavior?: 'outside' | 'inside'
9+
contentProps: BoxProps
10+
}
11+
12+
interface UseModalConfigParams {
13+
closeOnOverlayClick: boolean
14+
scrollBehavior: 'outside' | 'inside'
15+
fixedButtons: boolean
16+
withoutMargin: boolean
17+
}
18+
19+
export const useModalConfig = ({
20+
closeOnOverlayClick,
21+
scrollBehavior,
22+
fixedButtons,
23+
withoutMargin,
24+
}: UseModalConfigParams): ModalConfig => {
25+
const [isDesktop] = useMediaQuery(`(min-width: ${uiKitModalIsDesktop}px)`)
26+
// fixedButtons requiere scroll interno
27+
const isInsideScroll = scrollBehavior === 'inside'
28+
const shouldForceInsideScroll = fixedButtons
29+
const isInside = isInsideScroll || shouldForceInsideScroll
30+
return {
31+
closeOnOverlayClick,
32+
closeOnEsc: closeOnOverlayClick,
33+
scrollBehavior: isInside ? 'inside' : 'outside',
34+
contentProps: {
35+
maxH: isInside ? '100dvh' : 'auto',
36+
minH: isDesktop ? '300px' : '100dvh',
37+
padding: 0,
38+
width: '100%',
39+
animation: 'none',
40+
sx: {
41+
bgColor: vars('colors-neutral-white'),
42+
borderRadius: isDesktop ? '8px' : 0,
43+
mt: isDesktop ? '48px' : 0,
44+
mb: isDesktop ? '48px' : 0,
45+
marginX: isDesktop ? 'auto' : 0,
46+
maxH: isInside ? 'calc(100dvh - 96px)' : 'auto',
47+
maxWidth: isDesktop ? '690px' : '100%',
48+
49+
...(fixedButtons && {
50+
'.uikit-modalContent': {
51+
pb: 0,
52+
},
53+
'.uikit-modalButtons': {
54+
...ModalPadding,
55+
},
56+
}),
57+
...(withoutMargin && {
58+
'.uikit-modalContent': {
59+
pt: 0,
60+
px: 0,
61+
},
62+
'.uikit-modalButtons': {
63+
px: ModalPadding.px,
64+
},
65+
}),
66+
},
67+
},
68+
}
69+
}

0 commit comments

Comments
 (0)