Skip to content

Commit

Permalink
Merge pull request #94 from ynput/develop
Browse files Browse the repository at this point in the history
Release 1.0.7
  • Loading branch information
LudoHolbik authored May 8, 2024
2 parents 4641876 + 7864362 commit 91cfd37
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 91 deletions.
4 changes: 4 additions & 0 deletions src/Dropdowns/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,10 @@ export const Dropdown = forwardRef<DropdownRef, DropdownProps>(
// focus back on button
valueRef.current?.focus()
}

// stop default tab behavior
e.preventDefault()
e.stopPropagation()
handleClose()
}
}
Expand Down
43 changes: 30 additions & 13 deletions src/Overlay/Dialog/Dialog.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { Meta, StoryObj } from '@storybook/react'
import { Dialog } from '.'
import { useState } from 'react'
import { Button } from '../../Button'
import { InputColor } from '../../Inputs/InputColor'
import { Dropdown } from '../../Dropdowns/Dropdown'

const meta: Meta<typeof Dialog> = {
component: Dialog,
Expand Down Expand Up @@ -29,6 +31,15 @@ const closeProps = {
label: 'Close',
}

const defaultArgs: Story['args'] = {
header: <HeaderContent />,
children: <BodyContent />,
size: 'full',
onShow: () => console.log('onShow'),
closeProps: closeProps,
hideCancelButton: true,
}

const Template = (args: Story['args']) => {
const [openModal, setOpenModal] = useState(false)

Expand All @@ -37,17 +48,7 @@ const Template = (args: Story['args']) => {
<Button onClick={() => setOpenModal(!openModal)} icon="open_in_full">
Show Modal
</Button>
<Dialog
header={<HeaderContent />}
children={<BodyContent />}
isOpen={openModal}
onClose={() => setOpenModal(false)}
closeProps={closeProps}
hideCancelButton={false}
size="full"
onShow={() => console.log('test123')}
{...args}
/>
<Dialog {...defaultArgs} {...args} isOpen={openModal} onClose={() => setOpenModal(false)} />
</>
)
}
Expand All @@ -62,10 +63,26 @@ export const Footer: Story = {
footer: <FooterContent />,
},
}
export const DropdownInput: Story = {
render: Template,
args: {
children: (
<Dropdown
options={[
{ value: 'option1' },
{
value: 'option2',
},
]}
value={['option1']}
/>
),
},
}

export const DialogVariant: Story = {
export const ColorPicker: Story = {
render: Template,
args: {
variant: 'dialog',
children: <InputColor value={'#fff'} onChange={() => console.log('onChange')} />,
},
}
63 changes: 41 additions & 22 deletions src/Overlay/Dialog/Dialog.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { titleMedium } from '../../theme'
const fadeInAnimation = keyframes`
0% {
opacity: 0.3;
transform: scale(0.8);
scale: 0.8;
}
100% {
opacity: 1;
transform: scale(1);
scale: 1;
}
`

Expand All @@ -32,15 +32,13 @@ const getWidthSize = (size: string) =>
const getHeightSize = (size: string) =>
size ? heightSizes[size as keyof typeof heightSizes] : heightSizes.sm

export const Dialog = styled.dialog<{ $size?: string }>`
export const Dialog = styled.div<{ $size?: string }>`
background-color: var(--md-sys-color-surface-container);
border: none;
border-radius: var(--border-radius-m);
flex-direction: column;
padding: 0;
min-width: 200px;
min-height: 100px;
max-width: 85%;
width: ${({ $size }) =>
$size
? css`
Expand All @@ -54,31 +52,34 @@ export const Dialog = styled.dialog<{ $size?: string }>`
`
: '100px'};
/* Backdrop property affects inactive area around modal */
&::backdrop {
background-color: rgba(0, 0, 0, 0.5);
}
position: fixed;
/* Styles for dialogs that carry modal behavior */
&:modal {
}
min-width: 200px;
min-height: 100px;
max-width: 85%;
/* Styles for dialogs that carry non-modal behavior */
&:not(:modal) {
}
left: 50%;
top: 50%;
translate: -50% -50%;
&[open] {
display: flex;
animation: ${fadeInAnimation} 150ms ease-in-out forwards,
${fadeInAnimation} 150ms ease-in-out backwards;
animation-fill-mode: both;
}
display: flex;
animation: ${fadeInAnimation} 150ms ease-in-out forwards,
${fadeInAnimation} 150ms ease-in-out backwards;
animation-fill-mode: both;
z-index: 1000;
`

export const Close = styled(Button)`
position: absolute;
right: 8px;
top: 8px;
&.isHidden {
user-select: none;
opacity: 0;
pointer-events: none;
}
`

export const BaseDialogEdge = styled.div`
Expand Down Expand Up @@ -117,3 +118,21 @@ export const Body = styled.div`
overflow: auto;
flex-grow: 1;
`

const fadeIn = keyframes`
from {
opacity: 0;
} to {
opacity: 1;
}`

export const Backdrop = styled.div`
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
inset: 0;
animation: ${fadeIn} 150ms ease-in-out forwards;
animation-fill-mode: both;
`
120 changes: 64 additions & 56 deletions src/Overlay/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { forwardRef, useEffect, useRef, useState } from 'react'
import { forwardRef, useEffect } from 'react'
import * as Styled from './Dialog.styled'
import { Button, ButtonProps } from '../../Button'
import clsx from 'clsx'
import { createPortal } from 'react-dom'

export interface DialogProps extends Omit<React.HTMLAttributes<HTMLDialogElement>, 'open'> {
export interface DialogProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'open'> {
header?: React.ReactNode
children?: React.ReactNode
footer?: React.ReactNode
closeProps?: ButtonProps
hideCancelButton?: boolean
showCloseButton?: boolean
isOpen: boolean
onClose?: () => void
onClose: (e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void
onShow?: () => void
classNames?: ClassNames
variant?: 'dialog' | 'modal'
size?: 'sm' | 'md' | 'lg' | 'full'
}

Expand All @@ -26,7 +26,7 @@ type ClassNames = {
closeButton?: string
}

export const Dialog = forwardRef<HTMLDialogElement, DialogProps>((props) => {
export const Dialog = forwardRef<HTMLDivElement, DialogProps>((props, ref) => {
const {
children,
header,
Expand All @@ -40,70 +40,78 @@ export const Dialog = forwardRef<HTMLDialogElement, DialogProps>((props) => {
classNames,
size,
onShow,
variant = 'modal',
...rest
} = props

const [isModalOpen, setModalOpen] = useState(isOpen)
const modalRef = useRef<HTMLDialogElement | null>(null)

const closeIfClickOutside = (e: React.MouseEvent) => {
if (e.currentTarget === e.target) handleCloseModal()
const handleCloseModal = (
e: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
) => {
onClose && onClose(e)
}

useEffect(() => setModalOpen(isOpen), [isOpen])

// onShow callback
useEffect(() => {
const modalElement = modalRef.current
if (!modalElement) return
const showDialog = variant === 'dialog' && modalElement.show()
const showModal = variant === 'modal' && modalElement.showModal()
const showAll = () => {
showDialog || showModal
onShow && onShow()
}
isModalOpen ? showAll() : modalElement.close()
}, [isModalOpen])
if (isOpen && onShow) onShow()
}, [isOpen])

const handleCloseModal = () => {
if (onClose) onClose()
setModalOpen(false)
const closeIfClickOutside = (e: React.MouseEvent<HTMLElement>) => {
if (e.currentTarget === e.target) handleCloseModal(e)
}

const handleKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
if (e.key === 'Escape') {
handleCloseModal(e)
}
}

return (
<Styled.Dialog
$size={size}
ref={modalRef}
onClick={(e) => closeIfClickOutside(e)}
className={clsx('modal', className)}
{...props}
>
<Styled.Header className={clsx('header', { hideCancelButton }, classNames?.header)}>
{header ? header : ''}
{hideCancelButton ? null : (
if (!isOpen) return null

return createPortal(
<>
<Styled.Backdrop onClick={(e) => closeIfClickOutside(e)} />
<Styled.Dialog
$size={size}
ref={ref}
className={clsx('dialog', className)}
onKeyDown={handleKeyDown}
tabIndex={0}
{...rest}
>
<Styled.Header className={clsx('header', { hideCancelButton }, classNames?.header)}>
{header ? header : ''}

<Styled.Close
className={clsx('cancelButton', classNames?.cancelButton)}
className={clsx(
'cancelButton',
{ isHidden: hideCancelButton },
classNames?.cancelButton,
)}
icon="close"
variant="text"
autoFocus
onClick={handleCloseModal}
autoFocus
/>
</Styled.Header>
{children && (
<Styled.Body className={clsx('body', classNames?.body)}>{children}</Styled.Body>
)}
{(footer || showCloseButton) && (
<Styled.Footer className={clsx('footer', classNames?.footer)}>
{showCloseButton && (
<Button
label={!!closeProps?.label ? closeProps.label : 'Close'}
className={clsx('closeButton', classNames?.closeButton)}
variant="text"
onClick={handleCloseModal}
{...closeProps}
/>
)}
{footer && footer}
</Styled.Footer>
)}
</Styled.Header>
{children && <Styled.Body className={clsx('body', classNames?.body)}>{children}</Styled.Body>}
{(footer || showCloseButton) && (
<Styled.Footer className={clsx('footer', classNames?.footer)}>
{showCloseButton && (
<Button
label={!!closeProps?.label ? closeProps.label : 'Close'}
className={clsx('closeButton', classNames?.closeButton)}
variant="text"
onClick={handleCloseModal}
{...closeProps}
/>
)}
{footer && footer}
</Styled.Footer>
)}
</Styled.Dialog>
</Styled.Dialog>
</>,

document.body,
)
})

0 comments on commit 91cfd37

Please sign in to comment.