Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/components/ContextDropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,23 +83,27 @@ export function ContextDropdown({ renderTitle }: ContextDropdownProps) {
transition={{ duration: animationDuration }}
>
<div className="flex h-16 items-center justify-between border-b px-4">
<div className="flex items-center gap-2">
<div className="flex h-full items-center gap-2">
{currentIndex > 0 && (
<button
<motion.button
animate={{ x: 0 }}
transition={{
duration: 300,
}}
onClick={popScreen}
className="hover:bg-muted mr-2 rounded-full border p-2 disabled:opacity-50 disabled:hover:bg-transparent"
aria-label="Go back"
>
<Icon name="chevron-left" className="size-4" />
</button>
</motion.button>
)}

{/* Animated title */}
<div className="relative h-7 w-fit min-w-xs overflow-x-hidden">
<div className="relative flex h-full w-fit min-w-xs items-center overflow-x-hidden">
<AnimatePresence initial={false} mode="sync" custom={isForward}>
<MotionHeading
key={`title-${currentIndex}`}
className="text-md absolute !leading-6 font-medium"
className="text-heading-md absolute"
custom={isForward}
variants={slideVariants}
initial="enter"
Expand Down
22 changes: 18 additions & 4 deletions src/components/Modal/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Meta, StoryObj } from '@storybook/react'
import { Modal } from '.'
import { ModalProvider } from '@/context/ModalContext'
import { useModal } from '@/hooks/useModal'
import { memo, useEffect } from 'react'
import { memo, useCallback, useEffect } from 'react'
import { faker } from '@faker-js/faker'
import { Button, Icon } from '@/index'

Expand Down Expand Up @@ -36,25 +36,29 @@ const Underlay = memo(() => {
})

const ModalRenderer = ({
className,
closable = false,
multiScreen = false,
}: {
closable?: boolean
multiScreen?: boolean
className?: string
}) => {
const { openScreen, pushScreen } = useModal()

const addScreen = () => {
const addScreen = useCallback(() => {
pushScreen({
id: faker.string.uuid(),
title: faker.lorem.sentence({ min: 1, max: 3 }),
component: <div>{faker.lorem.paragraph()}</div>,
closable,
})
}
}, [pushScreen, closable])

useEffect(() => {
openScreen({
id: '1',
closable,
title: 'Lorem ipsum dolor sit amet',
component: (
<div className="text-body-md flex flex-col gap-3">
Expand Down Expand Up @@ -84,7 +88,7 @@ const ModalRenderer = ({
return (
<div>
<Underlay />
<Modal closable={closable} />
<Modal className={className} />
</div>
)
}
Expand Down Expand Up @@ -118,3 +122,13 @@ export const MultiScreen: Story = {
)
},
}

export const CustomClassName: Story = {
render: () => {
return (
<ModalProvider>
<ModalRenderer className="bg-red-500" />
</ModalProvider>
)
},
}
24 changes: 8 additions & 16 deletions src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,41 @@ import {
} from '@radix-ui/react-dialog'
import { useModal } from '@/hooks/useModal'
import { cn } from '@/lib/utils'
import { IconButton } from '@/components/IconButton'
import { Icon } from '../Icon'

export interface ModalProps {
closable?: boolean
className?: string
}

export const Modal = ({ closable = false, className }: ModalProps) => {
export const Modal = ({ className }: ModalProps) => {
const { screens, currentIndex, isOpen, close } = useModal()

const currentScreen = screens[currentIndex]

if (!isOpen) return null
if (!currentScreen) return null

const closable = currentScreen.closable

return (
<Dialog open={isOpen} onOpenChange={closable ? close : undefined}>
<DialogPortal>
<DialogOverlay className="bg-surface-secondary fixed h-screen w-screen opacity-85" />
<DialogContent
className={cn(
'bg-surface-primary border-neutral-default fixed top-1/2 left-1/2 z-10 flex h-auto max-h-[85vh] min-h-[40vh] w-[90vw] max-w-[800px] -translate-x-1/2 -translate-y-1/2 flex-col gap-3 overflow-y-auto rounded-md p-10 shadow-lg outline-none',
'bg-surface-primary border-neutral-default fixed top-1/2 left-1/2 z-10 flex h-auto max-h-[85vh] min-h-[40vh] w-[90vw] max-w-[800px] -translate-x-1/2 -translate-y-1/2 flex-col gap-3 overflow-y-auto rounded-md px-10 py-6 shadow-lg outline-none',
className
)}
>
{closable && (
<DialogClose asChild>
<IconButton
variant="tertiary"
icon={
<Icon
name="x"
className="text-neutral-default hover:text-neutral-default/80 size-7 focus:outline-none"
/>
}
aria-label="Close modal"
className="absolute top-4 right-4 focus:ring-0 focus:outline-none focus-visible:ring-0"
/>
<button className="hover:bg-muted/40 border-neutral-softest absolute top-6 right-6 rounded-full border p-2">
<Icon name="x" className="size-5 text-current" />
</button>
</DialogClose>
)}
{currentScreen.title && (
<DialogTitle className="text-display-sm">
<DialogTitle className="text-heading-lg">
{currentScreen.title}
</DialogTitle>
)}
Expand Down
1 change: 1 addition & 0 deletions src/context/ModalContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type Screen = {
id: string
title: string
component: ReactNode
closable?: boolean
}

type ModalContextType = {
Expand Down