Skip to content

Commit

Permalink
feat(ui/react): Portal 단순화, ModalPortal, ModalContext 변경 및 개편 (#206)
Browse files Browse the repository at this point in the history
* feat(ui/react/modal): ModalClose 컴포넌트 추가

* refactor(ui/react/modal): ModalOverlay style transform 활용하여 변경

* refactor(ui/react/modal): ModalContent 내부 ModalOverlay 제거

* refactor(ui/react/modal): ModalOverlay style inset으로 단축, recipe 제거

* feat(ui/react/modal): 변경된 style 반영, onOpenChange 속성 내부 추가

* feat(ui/react/portal): Portal 간소화, 종속되는 styles 제거

* feat(ui/react/modal): ModalPortal 추가

* refactor(ui/react/modal): ModalTitle style textAlign으로 변경, 적용

* feat(ui/react/icons): CloseIcon 추가

* feat(ui/react/modal): ModalTopbar 텍스트 아이콘으로 변경

* feat(ui/react/modal): ModalTrigger 추가

* feat(ui/react/modal): ModalContextValue 속성 단순화

* feat(ui/react/modal): ModalContext 반영, 내외부 onOpen, onOpenChange 속성 받게 추가
  • Loading branch information
sukvvon authored Jul 5, 2024
1 parent 9896563 commit 6405453
Show file tree
Hide file tree
Showing 18 changed files with 240 additions and 177 deletions.
10 changes: 10 additions & 0 deletions packages/ui/icons/src/close.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createIcon } from './icon';

export const CloseIcon = createIcon({
defaultProps: {
width: '1em',
height: '1em',
fill: 'none',
},
d: 'M10.0001 11.0628L6.58346 14.4795C6.43068 14.6323 6.25707 14.7052 6.06263 14.6982C5.86818 14.6913 5.69457 14.6114 5.54179 14.4587C5.38902 14.3059 5.31263 14.1288 5.31263 13.9274C5.31263 13.726 5.38902 13.5489 5.54179 13.3962L8.93763 10.0003L5.52096 6.58366C5.36818 6.43088 5.29527 6.2538 5.30221 6.05241C5.30916 5.85102 5.38902 5.67394 5.54179 5.52116C5.69457 5.36838 5.87166 5.29199 6.07304 5.29199C6.27443 5.29199 6.45152 5.36838 6.60429 5.52116L10.0001 8.93783L13.4168 5.52116C13.5696 5.36838 13.7467 5.29199 13.948 5.29199C14.1494 5.29199 14.3265 5.36838 14.4793 5.52116C14.6321 5.67394 14.7085 5.85102 14.7085 6.05241C14.7085 6.2538 14.6321 6.43088 14.4793 6.58366L11.0626 10.0003L14.4793 13.417C14.6321 13.5698 14.7085 13.7434 14.7085 13.9378C14.7085 14.1323 14.6321 14.3059 14.4793 14.4587C14.3265 14.6114 14.1494 14.6878 13.948 14.6878C13.7467 14.6878 13.5696 14.6114 13.4168 14.4587L10.0001 11.0628Z',
});
2 changes: 1 addition & 1 deletion packages/ui/icons/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/* eslint-disable @stylistic/padding-line-between-statements, react-refresh/only-export-components */
export * from './icon';

export * from './add';
export * from './bell';
export * from './cancel';
export * from './chevron-down';
export * from './chevron-left';
export * from './chevron-right';
export * from './chevron-up';
export * from './close';
export * from './drawer';
export * from './edit';
export * from './heart';
Expand Down
31 changes: 31 additions & 0 deletions packages/ui/react/src/components/modal/modal-close.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
type HTMLFavolinkProps,
favolink,
forwardRef,
} from '@favolink-ui/system';
import { cx, mergeFns } from '@favolink-ui/utils';
import { useModalContext } from './modal.context';

export type ModalCloseProps = HTMLFavolinkProps<'button'>;

export const ModalClose = forwardRef<ModalCloseProps, 'button'>(
function ModalClose(props, ref) {
const { children, className, onClick, ...restProps } = props;

const { onOpenChange } = useModalContext();

return (
<favolink.button
{...restProps}
asChild
ref={ref}
className={cx('favolink-modal__close', className)}
onClick={mergeFns(onClick, () => {
onOpenChange(false);
})}
>
{children}
</favolink.button>
);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { vars } from '../../styles/vars.css';
export const modalContentBase = style({
display: 'flex',
gap: 16,
position: 'relative',
zIndex: 98,
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
flexDirection: 'column',
padding: 24,
minWidth: 300,
padding: '20px 24px',
maxWidth: 'max-content',
backgroundColor: vars.color.gray[200],
borderRadius: 20,
});
32 changes: 11 additions & 21 deletions packages/ui/react/src/components/modal/modal-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,25 @@ import {
} from '@favolink-ui/system';
import { cx } from '@favolink-ui/utils';
import * as styles from './modal-content.styles.css';
import { ModalOverlay } from './modal-overlay';
import { useModalContext } from './modal.context';

export type ModalContentProps = HTMLFavolinkProps<'div'>;

export const ModalContent = forwardRef<ModalContentProps, 'div'>(
function ModalContent(props, ref) {
const { children, className, ...restPorps } = props;

const { onClose, closeOnOverlayClick } = useModalContext();

return (
<>
<ModalOverlay
onClick={closeOnOverlayClick ? onClose : undefined}
variant="withContent"
/>
<favolink.div
{...restPorps}
ref={ref}
className={cx(
'favolink-modal__content',
styles.modalContentBase,
className,
)}
>
{children}
</favolink.div>
</>
<favolink.div
{...restPorps}
ref={ref}
className={cx(
'favolink-modal__content',
styles.modalContentBase,
className,
)}
>
{children}
</favolink.div>
);
},
);
35 changes: 5 additions & 30 deletions packages/ui/react/src/components/modal/modal-overlay.styles.css.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
import { type RecipeVariants, recipe } from '@vanilla-extract/recipes';
import { style } from '@vanilla-extract/css';

export const modalOverlay = recipe({
base: {
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},

variants: {
variant: {
original: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
zIndex: 96,
},
withContent: {
zIndex: 97,
},
},
},

defaultVariants: {
variant: 'original',
},
export const modalOverlayBase = style({
position: 'fixed',
inset: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
});

export type ModalOverlayVariants = RecipeVariants<typeof modalOverlay>;
20 changes: 15 additions & 5 deletions packages/ui/react/src/components/modal/modal-overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,33 @@ import {
favolink,
forwardRef,
} from '@favolink-ui/system';
import { cx } from '@favolink-ui/utils';
import { cx, mergeFns } from '@favolink-ui/utils';
import * as styles from './modal-overlay.styles.css';
import { useModalContext } from './modal.context';

export type ModalOverlayProps = HTMLFavolinkProps<'div'> &
styles.ModalOverlayVariants;
export type ModalOverlayProps = HTMLFavolinkProps<'div'>;

export const ModalOverlay = forwardRef<ModalOverlayProps, 'div'>(
function ModalOverlay(props, ref) {
const { variant, ...restProps } = props;
const { className, onClick, ...restProps } = props;

const { onOpenChange, closeOnOverlayClick } = useModalContext();

return (
<favolink.div
{...restProps}
ref={ref}
onClick={
closeOnOverlayClick
? mergeFns(() => {
onOpenChange(false);
}, onClick)
: onClick
}
className={cx(
'favolink-modal__overlay',
styles.modalOverlay({ variant }),
styles.modalOverlayBase,
className,
)}
/>
);
Expand Down
22 changes: 22 additions & 0 deletions packages/ui/react/src/components/modal/modal-portal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Children } from 'react';
import { useModalContext } from './modal.context';
import { Portal, type PortalProps } from '../portal/';

export type ModalPortalProps = PortalProps;

export function ModalPortal(props: ModalPortalProps) {
const { children, ...restProps } = props;

const { onOpen } = useModalContext();

return (
onOpen &&
Children.map(children, (child) => {
return (
<Portal {...restProps} asChild>
{child}
</Portal>
);
})
);
}
20 changes: 11 additions & 9 deletions packages/ui/react/src/components/modal/modal-title.styles.css.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import { style } from '@vanilla-extract/css';
import { type RecipeVariants, recipe } from '@vanilla-extract/recipes';
import { vars } from '../../styles/vars.css';

export const modalTitle = recipe({
base: {
display: 'flex',
color: vars.color.gray[1000],
},
const base = style({
color: vars.color.gray[1000],
});

export const modalTitleVariants = recipe({
base,

variants: {
layout: {
left: {
justifyContent: 'flex-start',
textAlign: 'left',
},
center: {
justifyContent: 'center',
textAlign: 'center',
},
right: {
justifyContent: 'flex-end',
textAlign: 'right',
},
},
},
Expand All @@ -26,4 +28,4 @@ export const modalTitle = recipe({
},
});

export type ModalTitleVariants = RecipeVariants<typeof modalTitle>;
export type ModalTitleVariants = RecipeVariants<typeof modalTitleVariants>;
28 changes: 13 additions & 15 deletions packages/ui/react/src/components/modal/modal-title.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { favolink, forwardRef } from '@favolink-ui/system';
import { forwardRef } from '@favolink-ui/system';
import { cx } from '@favolink-ui/utils';
import * as styles from './modal-title.styles.css';
import { Heading, type HeadingProps } from '../typography';
Expand All @@ -10,20 +10,18 @@ export const ModalTitle = forwardRef<ModalTitleProps, typeof Heading>(
const { children, className, layout, ...restProps } = props;

return (
<favolink.div className={styles.modalTitle({ layout })}>
<Heading
as="h4"
{...restProps}
ref={ref}
className={cx(
'favolink-modal__title',
styles.modalTitle.classNames.base,
className,
)}
>
{children}
</Heading>
</favolink.div>
<Heading
as="h4"
{...restProps}
ref={ref}
className={cx(
'favolink-modal__title',
styles.modalTitleVariants({ layout }),
className,
)}
>
{children}
</Heading>
);
},
);
17 changes: 13 additions & 4 deletions packages/ui/react/src/components/modal/modal-topbar.styles.css.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { style } from '@vanilla-extract/css';
import { type RecipeVariants, recipe } from '@vanilla-extract/recipes';

const base = style({
display: 'flex',
});

export const modalTopbar = recipe({
base: {
display: 'flex',
},
base,

variants: {
layout: {
Expand All @@ -17,8 +20,14 @@ export const modalTopbar = recipe({
},

defaultVariants: {
layout: 'couple',
layout: 'single',
},
});

export type ModalTopbarVariants = RecipeVariants<typeof modalTopbar>;

export const modalTopbarIcon = style({
width: 20,
height: 20,
cursor: 'pointer',
});
34 changes: 27 additions & 7 deletions packages/ui/react/src/components/modal/modal-topbar.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import { ChevronLeftIcon, CloseIcon } from '@favolink-ui/icons';
import {
type HTMLFavolinkProps,
favolink,
forwardRef,
} from '@favolink-ui/system';
import { cx } from '@favolink-ui/utils';
import { cx, mergeFns } from '@favolink-ui/utils';
import { type MouseEvent } from 'react';
import * as styles from './modal-topbar.styles.css';
import { useModalContext } from './modal.context';
import { Text } from '../typography';

export type ModalTopbarProps = HTMLFavolinkProps<'div'> &
styles.ModalTopbarVariants;
styles.ModalTopbarVariants & {
onLeftIconClick?: (event: MouseEvent) => void;
onRightIconClick?: (event: MouseEvent) => void;
};

export const ModalTopbar = forwardRef<ModalTopbarProps, 'div'>(
function ModalTopBar(props, ref) {
const { children, layout, className, ...restProps } = props;
const {
layout,
className,
onLeftIconClick,
onRightIconClick,
...restProps
} = props;

const { onClose } = useModalContext();
const { onOpenChange } = useModalContext();

const isCouple = layout !== 'single';

Expand All @@ -29,8 +39,18 @@ export const ModalTopbar = forwardRef<ModalTopbarProps, 'div'>(
className,
)}
>
{isCouple && <Text>{children}</Text>}
<Text onClick={onClose}>닫기</Text>
{isCouple && (
<ChevronLeftIcon
className={styles.modalTopbarIcon}
onClick={onLeftIconClick}
/>
)}
<CloseIcon
className={styles.modalTopbarIcon}
onClick={mergeFns(() => {
onOpenChange(false);
}, onRightIconClick)}
/>
</favolink.div>
);
},
Expand Down
Loading

0 comments on commit 6405453

Please sign in to comment.