Skip to content

Commit

Permalink
fix: Layout コンポーネントに ref を渡せない不具合を修正 (#4090)
Browse files Browse the repository at this point in the history
* fix: Layout コンポーネントに ref を渡せない不具合を修正

* fix: lint error
  • Loading branch information
uknmr authored Dec 6, 2023
1 parent c2e9ead commit 8c54720
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 115 deletions.
6 changes: 4 additions & 2 deletions src/components/ErrorScreen/ErrorScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { ComponentProps, FC, ReactNode, useMemo } from 'react'
import React, { useMemo } from 'react'
import { tv } from 'tailwind-variants'

import { PageHeading } from '../Heading'
import { Center, Stack } from '../Layout'
import { SmartHRLogo } from '../SmartHRLogo'
import { TextLink } from '../TextLink'

import type { ComponentPropsWithoutRef, FC, ReactNode } from 'react'

type Props = {
/** ロゴ */
logo?: ReactNode
Expand All @@ -28,7 +30,7 @@ type Props = {
className?: string
}

type ElementProps = Omit<ComponentProps<'div'>, keyof Props>
type ElementProps = Omit<ComponentPropsWithoutRef<'div'>, keyof Props>

const errorScreen = tv({
base: 'smarthr-ui-ErrorScreen shr-bg-background',
Expand Down
44 changes: 21 additions & 23 deletions src/components/Layout/Center/Center.tsx
Original file line number Diff line number Diff line change
@@ -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<{
/** コンテンツの最小高さ */
Expand All @@ -14,7 +15,7 @@ type Props = PropsWithChildren<{
verticalCentering?: boolean
as?: string | React.ComponentType<any>
}>
type ElementProps = Omit<ComponentPropsWithoutRef<'div'>, keyof Props>
type ElementProps = Omit<ComponentPropsWithRef<'div'>, keyof Props>

const center = tv({
base: 'shr-mx-auto shr-box-content shr-flex shr-flex-col shr-items-center',
Expand Down Expand Up @@ -49,25 +50,22 @@ const center = tv({
},
})

export const Center: React.FC<Props & ElementProps> = ({
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<HTMLDivElement, Props & ElementProps>(
(
{ 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 <Component {...props} {...styleProps} />
}
return <Component {...styleProps} {...props} ref={ref} />
},
)
28 changes: 15 additions & 13 deletions src/components/Layout/Cluster/Cluster.tsx
Original file line number Diff line number Diff line change
@@ -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',
Expand Down Expand Up @@ -115,17 +115,19 @@ type Props = PropsWithChildren<
gap?: Gap | SeparateGap
}
> &
ComponentProps<'div'>
ComponentPropsWithRef<'div'>

export const Cluster: React.FC<Props> = ({ 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<HTMLDivElement, Props>(
({ 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 <Component {...others} className={styles} />
}
return <Component {...others} ref={ref} className={styles} />
},
)
21 changes: 9 additions & 12 deletions src/components/Layout/Reel/Reel.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof reel> &
PropsWithChildren<{
as?: string | React.ComponentType<any>
}> &
ComponentProps<'div'>
ComponentPropsWithRef<'div'>

const reel = tv({
base: [
Expand Down Expand Up @@ -83,13 +84,9 @@ const reel = tv({
},
})

export const Reel: React.FC<Props> = ({
as: Component = 'div',
gap = 0.5,
padding = 0,
className,
...props
}) => {
const styles = useMemo(() => reel({ gap, padding, className }), [className, gap, padding])
return <Component {...props} className={styles} />
}
export const Reel = forwardRef<HTMLDivElement, Props>(
({ as: Component = 'div', gap = 0.5, padding = 0, className, ...props }, ref) => {
const styles = useMemo(() => reel({ gap, padding, className }), [className, gap, padding])
return <Component {...props} ref={ref} className={styles} />
},
)
129 changes: 64 additions & 65 deletions src/components/Layout/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -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'],
Expand Down Expand Up @@ -100,66 +94,71 @@ type Props = Omit<VariantProps<typeof sidebar>, 'rowGap' | 'columnGap'> &
/** 各領域の間隔の指定(gap) */
gap?: Gap | SeparateGap
}> &
ComponentProps<'div'>
ComponentPropsWithRef<'div'>

export const Sidebar: React.FC<Props> = ({
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<HTMLDivElement, Props>(
(
{
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 (
<Component {...props} className={wrapperStyle}>
{styledChildren}
</Component>
)
}
return (
<Component {...props} ref={ref} className={wrapperStyle}>
{styledChildren}
</Component>
)
},
)

0 comments on commit 8c54720

Please sign in to comment.