-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react): AspectRatio Slot 적용 (#579)
* feat(react): AspectRatio Slot 적용 * fix: docs
- Loading branch information
Showing
4 changed files
with
132 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@modern-kit/react': minor | ||
--- | ||
|
||
feat(react): AspectRatio Slot 적용 - @ssi0214 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,66 @@ | ||
import { | ||
CSSProperties, | ||
Children, | ||
ComponentProps, | ||
PropsWithChildren, | ||
forwardRef, | ||
useMemo, | ||
} from 'react'; | ||
import classNames from 'classnames'; | ||
import styles from './AspectRatio.module.css'; | ||
import { Slot } from '../Slot'; | ||
import React from 'react'; | ||
|
||
interface AspectRatioProps extends ComponentProps<'div'> { | ||
ratio: number; | ||
} | ||
type AspectRatioProps<T extends React.ElementType = 'div'> = | ||
React.ComponentPropsWithoutRef<T> & { | ||
asChild?: boolean; | ||
ratio: number; | ||
}; | ||
|
||
export const AspectRatio = forwardRef< | ||
HTMLDivElement, | ||
PropsWithChildren<AspectRatioProps> | ||
>(({ ratio, children, className, style, ...props }, ref) => { | ||
const customStyle: CSSProperties = useMemo( | ||
/** | ||
* @description 주어진 aspect-ratio 비율에 맞게 가로/세로 비율을 편리하게 맞춰주는 컴포넌트입니다. | ||
* | ||
* 미리 영역을 확보하여 Layout Shift를 방지하는데 효과적입니다. | ||
* | ||
* @template T - 사용할 HTML 요소의 타입을 지정합니다. 기본값은 'div'입니다. | ||
* | ||
* @param {PropsWithChildren<AspectRatioProps<T>>} aspectRatioProps - 컴포넌트에 전달되는 속성들입니다. | ||
* @param {boolean} [aspectRatioProps.asChild=false] - `true`로 설정하면 `Slot` 컴포넌트를 사용해 자식 요소에 aspect-ratio 속성을 적용합니다. | ||
* @param {number} aspectRatioProps.ratio - 자식 요소의 가로 세로 비율을 지정합니다. | ||
* @param {React.ReactNode} aspectRatioProps.children - 렌더링 할 자식요소 입니다. | ||
* @param {Object} [aspectRatioProps.props] - 기타 추가할 나머지 속성들입니다. | ||
* | ||
* @returns {JSX.Element} 주어진 비율에 맞춰 스타일이 적용된 래퍼 요소를 반환합니다. | ||
* | ||
* @example | ||
* // 기본적으로 div를 생성 후 aspect-ratio 속성을 적용합니다. | ||
* ```tsx | ||
* <AspectRatio ratio={16 / 9}> | ||
* <p>Content</p> | ||
* </AspectRatio> | ||
* ``` | ||
* | ||
* @example | ||
* // asChild 속성을 true로 설정하면 자식 요소에 aspect-ratio 속성을 적용합니다. | ||
* ```tsx | ||
* <AspectRatio ratio={16 / 9} asChild> | ||
* <section>Content</section> | ||
* </AspectRatio> | ||
* ``` | ||
*/ | ||
export const AspectRatio = <T extends React.ElementType = 'div'>({ | ||
asChild = false, | ||
ratio, | ||
children, | ||
...props | ||
}: React.PropsWithChildren<AspectRatioProps<T>>): JSX.Element => { | ||
const Wrapper = asChild ? Slot : 'div'; | ||
const customStyle: React.CSSProperties = React.useMemo( | ||
() => ({ | ||
paddingTop: `calc(100% * (1 / ${ratio}))`, | ||
...style, | ||
...props.style, | ||
}), | ||
[ratio, style] | ||
[ratio, props.style] | ||
); | ||
|
||
if (Children.count(children) > 1) { | ||
throw new Error('AspectRatio component expects exactly one child element.'); | ||
} | ||
return ( | ||
<div | ||
ref={ref} | ||
className={classNames(styles['wrapper'], className)} | ||
<Wrapper | ||
className={classNames(styles['wrapper'], props.className)} | ||
style={customStyle} | ||
{...props}> | ||
{Children.only(children)} | ||
</div> | ||
{React.Children.only(children)} | ||
</Wrapper> | ||
); | ||
}); | ||
|
||
AspectRatio.displayName = 'AspectRatio'; | ||
}; |