From 8c547207706a8d0e98d807646af1d31759081bbe Mon Sep 17 00:00:00 2001 From: KANAMORI Yu Date: Wed, 6 Dec 2023 17:28:29 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20Layout=20=E3=82=B3=E3=83=B3=E3=83=9D?= =?UTF-8?q?=E3=83=BC=E3=83=8D=E3=83=B3=E3=83=88=E3=81=AB=20ref=20=E3=82=92?= =?UTF-8?q?=E6=B8=A1=E3=81=9B=E3=81=AA=E3=81=84=E4=B8=8D=E5=85=B7=E5=90=88?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#4090)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: Layout コンポーネントに ref を渡せない不具合を修正 * fix: lint error --- src/components/ErrorScreen/ErrorScreen.tsx | 6 +- src/components/Layout/Center/Center.tsx | 44 ++++--- src/components/Layout/Cluster/Cluster.tsx | 28 ++--- src/components/Layout/Reel/Reel.tsx | 21 ++-- src/components/Layout/Sidebar/Sidebar.tsx | 129 ++++++++++----------- 5 files changed, 113 insertions(+), 115 deletions(-) diff --git a/src/components/ErrorScreen/ErrorScreen.tsx b/src/components/ErrorScreen/ErrorScreen.tsx index f90769d84a..13c8c2ba70 100644 --- a/src/components/ErrorScreen/ErrorScreen.tsx +++ b/src/components/ErrorScreen/ErrorScreen.tsx @@ -1,4 +1,4 @@ -import React, { ComponentProps, FC, ReactNode, useMemo } from 'react' +import React, { useMemo } from 'react' import { tv } from 'tailwind-variants' import { PageHeading } from '../Heading' @@ -6,6 +6,8 @@ import { Center, Stack } from '../Layout' import { SmartHRLogo } from '../SmartHRLogo' import { TextLink } from '../TextLink' +import type { ComponentPropsWithoutRef, FC, ReactNode } from 'react' + type Props = { /** ロゴ */ logo?: ReactNode @@ -28,7 +30,7 @@ type Props = { className?: string } -type ElementProps = Omit, keyof Props> +type ElementProps = Omit, keyof Props> const errorScreen = tv({ base: 'smarthr-ui-ErrorScreen shr-bg-background', diff --git a/src/components/Layout/Center/Center.tsx b/src/components/Layout/Center/Center.tsx index d0b0bc6650..d9b7d7a82e 100644 --- a/src/components/Layout/Center/Center.tsx +++ b/src/components/Layout/Center/Center.tsx @@ -1,7 +1,8 @@ -import React, { ComponentPropsWithoutRef, PropsWithChildren, useMemo } from 'react' +import React, { forwardRef, useMemo } from 'react' import { tv } from 'tailwind-variants' import type { Gap } from '../../../types' +import type { ComponentPropsWithRef, PropsWithChildren } from 'react' type Props = PropsWithChildren<{ /** コンテンツの最小高さ */ @@ -14,7 +15,7 @@ type Props = PropsWithChildren<{ verticalCentering?: boolean as?: string | React.ComponentType }> -type ElementProps = Omit, keyof Props> +type ElementProps = Omit, keyof Props> const center = tv({ base: 'shr-mx-auto shr-box-content shr-flex shr-flex-col shr-items-center', @@ -49,25 +50,22 @@ const center = tv({ }, }) -export const Center: React.FC = ({ - minHeight, - maxWidth, - padding, - verticalCentering, - as: Component = 'div', - className, - ...props -}) => { - const styleProps = useMemo( - () => ({ - className: center({ padding, verticalCentering, className }), - style: { - minHeight: minHeight ?? undefined, - maxWidth: maxWidth ?? undefined, - }, - }), - [padding, verticalCentering, className, minHeight, maxWidth], - ) +export const Center = forwardRef( + ( + { minHeight, maxWidth, padding, verticalCentering, as: Component = 'div', className, ...props }, + ref, + ) => { + const styleProps = useMemo( + () => ({ + className: center({ padding, verticalCentering, className }), + style: { + minHeight: minHeight ?? undefined, + maxWidth: maxWidth ?? undefined, + }, + }), + [padding, verticalCentering, className, minHeight, maxWidth], + ) - return -} + return + }, +) diff --git a/src/components/Layout/Cluster/Cluster.tsx b/src/components/Layout/Cluster/Cluster.tsx index 37b6ee7f36..7d067dfe4d 100644 --- a/src/components/Layout/Cluster/Cluster.tsx +++ b/src/components/Layout/Cluster/Cluster.tsx @@ -1,8 +1,8 @@ -import React, { useMemo } from 'react' +import React, { forwardRef, useMemo } from 'react' import { VariantProps, tv } from 'tailwind-variants' import type { Gap, SeparateGap } from '../../../types' -import type { ComponentProps, PropsWithChildren } from 'react' +import type { ComponentPropsWithRef, PropsWithChildren } from 'react' const cluster = tv({ base: 'shr-flex-wrap [&:empty]:shr-gap-0', @@ -115,17 +115,19 @@ type Props = PropsWithChildren< gap?: Gap | SeparateGap } > & - ComponentProps<'div'> + ComponentPropsWithRef<'div'> -export const Cluster: React.FC = ({ as: Component = 'div', gap = 0.5, ...props }) => { - const rowGap = gap instanceof Object ? gap.row : gap - const columnGap = gap instanceof Object ? gap.column : gap +export const Cluster = forwardRef( + ({ as: Component = 'div', gap = 0.5, ...props }, ref) => { + const rowGap = gap instanceof Object ? gap.row : gap + const columnGap = gap instanceof Object ? gap.column : gap - const { inline = false, align, justify, className, ...others } = props - const styles = useMemo( - () => cluster({ inline, rowGap, columnGap, align, justify, className }), - [inline, rowGap, columnGap, align, justify, className], - ) + const { inline = false, align, justify, className, ...others } = props + const styles = useMemo( + () => cluster({ inline, rowGap, columnGap, align, justify, className }), + [inline, rowGap, columnGap, align, justify, className], + ) - return -} + return + }, +) diff --git a/src/components/Layout/Reel/Reel.tsx b/src/components/Layout/Reel/Reel.tsx index 2efffb3885..28ef19d87d 100644 --- a/src/components/Layout/Reel/Reel.tsx +++ b/src/components/Layout/Reel/Reel.tsx @@ -1,13 +1,14 @@ -import React, { ComponentProps, PropsWithChildren, useMemo } from 'react' +import React, { forwardRef, useMemo } from 'react' import { VariantProps, tv } from 'tailwind-variants' import type { Gap } from '../../../types' +import type { ComponentPropsWithRef, PropsWithChildren } from 'react' type Props = VariantProps & PropsWithChildren<{ as?: string | React.ComponentType }> & - ComponentProps<'div'> + ComponentPropsWithRef<'div'> const reel = tv({ base: [ @@ -83,13 +84,9 @@ const reel = tv({ }, }) -export const Reel: React.FC = ({ - as: Component = 'div', - gap = 0.5, - padding = 0, - className, - ...props -}) => { - const styles = useMemo(() => reel({ gap, padding, className }), [className, gap, padding]) - return -} +export const Reel = forwardRef( + ({ as: Component = 'div', gap = 0.5, padding = 0, className, ...props }, ref) => { + const styles = useMemo(() => reel({ gap, padding, className }), [className, gap, padding]) + return + }, +) diff --git a/src/components/Layout/Sidebar/Sidebar.tsx b/src/components/Layout/Sidebar/Sidebar.tsx index 3cb3b836af..b15a9b35a6 100644 --- a/src/components/Layout/Sidebar/Sidebar.tsx +++ b/src/components/Layout/Sidebar/Sidebar.tsx @@ -1,14 +1,8 @@ -import { - type CSSProperties, - type ComponentProps, - type PropsWithChildren, - ReactElement, - useMemo, -} from 'react' -import React from 'react' +import React, { ReactElement, forwardRef, useMemo } from 'react' import { VariantProps, tv } from 'tailwind-variants' import type { Gap, SeparateGap } from '../../../types' +import type { CSSProperties, ComponentPropsWithRef, PropsWithChildren } from 'react' const sidebar = tv({ base: ['shr-flex shr-flex-wrap', 'empty:shr-gap-0'], @@ -100,66 +94,71 @@ type Props = Omit, 'rowGap' | 'columnGap'> & /** 各領域の間隔の指定(gap) */ gap?: Gap | SeparateGap }> & - ComponentProps<'div'> + ComponentPropsWithRef<'div'> -export const Sidebar: React.FC = ({ - as: Component = 'div', - align = 'stretch', - contentsMinWidth = '50%', - gap = 1, - right = false, - className, - children, - ...props -}) => { - const rowGap = gap instanceof Object ? gap.row : gap - const columnGap = gap instanceof Object ? gap.column : gap - - const wrapperStyle = useMemo( - () => sidebar({ align, rowGap, columnGap, className }), - [align, rowGap, columnGap, className], - ) - const { firstItemStyleProps, lastItemStyleProps } = useMemo(() => { - const { firstItem, lastItem } = sidebarItem({ right }) - const styleProps = { - minWidth: contentsMinWidth, - } - return { - firstItemStyleProps: { - className: firstItem(), - style: right ? styleProps : undefined, - }, - lastItemStyleProps: { - className: lastItem(), - style: right ? undefined : styleProps, - }, - } - }, [contentsMinWidth, right]) +export const Sidebar = forwardRef( + ( + { + as: Component = 'div', + align = 'stretch', + contentsMinWidth = '50%', + gap = 1, + right = false, + className, + children, + ...props + }, + ref, + ) => { + const rowGap = gap instanceof Object ? gap.row : gap + const columnGap = gap instanceof Object ? gap.column : gap - // tailwindcss で :first-child / :last-child に対して動的な min-height を当てられないため、React で疑似的に処理している - const styledChildren = React.Children.map(children, (child, i) => { - if (React.isValidElement(child)) { - const childClassName = child.props.className ?? '' - if (i === 0) { - return React.cloneElement(child as ReactElement, { - className: `${firstItemStyleProps.className} ${childClassName}`, - style: { ...firstItemStyleProps.style, ...child.props.style }, - }) + const wrapperStyle = useMemo( + () => sidebar({ align, rowGap, columnGap, className }), + [align, rowGap, columnGap, className], + ) + const { firstItemStyleProps, lastItemStyleProps } = useMemo(() => { + const { firstItem, lastItem } = sidebarItem({ right }) + const styleProps = { + minWidth: contentsMinWidth, } - if (i === React.Children.count(children) - 1) { - return React.cloneElement(child as ReactElement, { - className: `${lastItemStyleProps.className} ${childClassName}`, - style: { ...lastItemStyleProps.style, ...child.props.style }, - }) + return { + firstItemStyleProps: { + className: firstItem(), + style: right ? styleProps : undefined, + }, + lastItemStyleProps: { + className: lastItem(), + style: right ? undefined : styleProps, + }, } - } + }, [contentsMinWidth, right]) - return child - }) + // tailwindcss で :first-child / :last-child に対して動的な min-height を当てられないため、React で疑似的に処理している + const styledChildren = React.Children.map(children, (child, i) => { + if (React.isValidElement(child)) { + const childClassName = child.props.className ?? '' + if (i === 0) { + return React.cloneElement(child as ReactElement, { + className: `${firstItemStyleProps.className} ${childClassName}`, + style: { ...firstItemStyleProps.style, ...child.props.style }, + }) + } + if (i === React.Children.count(children) - 1) { + return React.cloneElement(child as ReactElement, { + className: `${lastItemStyleProps.className} ${childClassName}`, + style: { ...lastItemStyleProps.style, ...child.props.style }, + }) + } + } + + return child + }) - return ( - - {styledChildren} - - ) -} + return ( + + {styledChildren} + + ) + }, +)