From 7801b0d93fcdfab65bf30b1d1ac76e4a699f1490 Mon Sep 17 00:00:00 2001 From: toshusai Date: Mon, 23 Oct 2023 10:54:41 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20focus-scope=E3=81=AE=E5=88=A9?= =?UTF-8?q?=E7=94=A8=E6=96=B9=E6=B3=95=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/react/src/_lib/useForwardedRef.tsx | 16 +++ .../src/components/Modal/Dialog/index.tsx | 81 +++++++++++++ packages/react/src/components/Modal/index.tsx | 106 ++++-------------- 3 files changed, 119 insertions(+), 84 deletions(-) create mode 100644 packages/react/src/_lib/useForwardedRef.tsx create mode 100644 packages/react/src/components/Modal/Dialog/index.tsx diff --git a/packages/react/src/_lib/useForwardedRef.tsx b/packages/react/src/_lib/useForwardedRef.tsx new file mode 100644 index 000000000..21eb5af89 --- /dev/null +++ b/packages/react/src/_lib/useForwardedRef.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; + +export function useForwardedRef(ref: React.ForwardedRef) { + const innerRef = React.useRef(null); + + React.useEffect(() => { + if (!ref) return; + if (typeof ref === 'function') { + ref(innerRef.current); + } else { + ref.current = innerRef.current; + } + }); + + return innerRef; +} diff --git a/packages/react/src/components/Modal/Dialog/index.tsx b/packages/react/src/components/Modal/Dialog/index.tsx new file mode 100644 index 000000000..2147cfaf0 --- /dev/null +++ b/packages/react/src/components/Modal/Dialog/index.tsx @@ -0,0 +1,81 @@ +import { forwardRef } from 'react' +import * as React from 'react' +import styled, { css } from 'styled-components' +import { useDialog } from '@react-aria/dialog' +import { columnSystem, COLUMN_UNIT, GUTTER_UNIT } from '@charcoal-ui/foundation' +import { unreachable } from '../../../_lib' +import { maxWidth } from '@charcoal-ui/utils' +import { animated } from 'react-spring' +import { useForwardedRef } from '../../../_lib/useForwardedRef' +import { Size, BottomSheet } from '..' + +export const Dialog = forwardRef< + HTMLDivElement, + React.ComponentProps +>(function Dialog(props, forwardRef) { + const ref = useForwardedRef(forwardRef) + const { dialogProps } = useDialog( + { + role: 'dialog', + }, + ref + ) + + return ( + + ) +}) + +const AnimatedStyledDialogDiv = animated(styled.div<{ + size: Size + bottomSheet: BottomSheet +}>` + margin: auto; + position: relative; + height: fit-content; + width: ${(p) => { + switch (p.size) { + case 'S': { + return columnSystem(3, COLUMN_UNIT, GUTTER_UNIT) + GUTTER_UNIT * 2 + } + case 'M': { + return columnSystem(4, COLUMN_UNIT, GUTTER_UNIT) + GUTTER_UNIT * 2 + } + case 'L': { + return columnSystem(6, COLUMN_UNIT, GUTTER_UNIT) + GUTTER_UNIT * 2 + } + default: { + return unreachable(p.size) + } + } + }}px; + + background-color: ${({ theme }) => theme.color.background1}; + border-radius: 24px; + + @media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} { + max-width: 440px; + width: calc(100% - 48px); + ${(p) => + p.bottomSheet !== false && + css` + width: 100%; + border-radius: 0; + margin: auto 0 0 0; + ${p.bottomSheet === 'full' && + css` + min-height: 100%; + `} + `} + } + :focus { + outline: none; + } +`) diff --git a/packages/react/src/components/Modal/index.tsx b/packages/react/src/components/Modal/index.tsx index 3ca2c3c6c..ca72b1e02 100644 --- a/packages/react/src/components/Modal/index.tsx +++ b/packages/react/src/components/Modal/index.tsx @@ -7,20 +7,17 @@ import { } from '@react-aria/overlays' import styled, { css, useTheme } from 'styled-components' import { theme } from '../../styled' -import { useDialog } from '@react-aria/dialog' import { AriaDialogProps } from '@react-types/dialog' -import { columnSystem, COLUMN_UNIT, GUTTER_UNIT } from '@charcoal-ui/foundation' -import { unreachable } from '../../_lib' import { maxWidth } from '@charcoal-ui/utils' import { useMedia } from '@charcoal-ui/styled' import { animated, useTransition, easings } from 'react-spring' import Button, { ButtonProps } from '../Button' import IconButton from '../IconButton' import { useObjectRef } from '@react-aria/utils' -import { FocusScope } from '@react-aria/focus' +import { Dialog } from './Dialog' export type BottomSheet = boolean | 'full' -type Size = 'S' | 'M' | 'L' +export type Size = 'S' | 'M' | 'L' export type ModalProps = AriaModalOverlayProps & AriaDialogProps & { @@ -142,7 +139,7 @@ const Modal = forwardRef(function ModalInner( style={transitionEnabled ? { backgroundColor, overflow } : {}} $bottomSheet={bottomSheet} > - (function ModalInner( bottomSheet={bottomSheet} className={className} > - - - - {children} - {isDismissable === true && ( - - )} - - - - + + {children} + {isDismissable === true && ( + + )} + + ) @@ -182,17 +175,6 @@ const Modal = forwardRef(function ModalInner( export default memo(Modal) -function Dialog({ children }: { children: React.ReactNode }) { - const r = React.useRef(null) - const { dialogProps } = useDialog( - { - role: 'dialog', - }, - r - ) - return
{children}
-} - export const ModalContext = React.createContext<{ /** * @deprecated @@ -237,50 +219,6 @@ const ModalBackground = animated(styled.div<{ } `) -const ModalDialog = animated(styled.div<{ - size: Size - bottomSheet: BottomSheet -}>` - margin: auto; - position: relative; - height: fit-content; - width: ${(p) => { - switch (p.size) { - case 'S': { - return columnSystem(3, COLUMN_UNIT, GUTTER_UNIT) + GUTTER_UNIT * 2 - } - case 'M': { - return columnSystem(4, COLUMN_UNIT, GUTTER_UNIT) + GUTTER_UNIT * 2 - } - case 'L': { - return columnSystem(6, COLUMN_UNIT, GUTTER_UNIT) + GUTTER_UNIT * 2 - } - default: { - return unreachable(p.size) - } - } - }}px; - - background-color: ${({ theme }) => theme.color.background1}; - border-radius: 24px; - - @media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} { - max-width: 440px; - width: calc(100% - 48px); - ${(p) => - p.bottomSheet !== false && - css` - width: 100%; - border-radius: 0; - margin: auto 0 0 0; - ${p.bottomSheet === 'full' && - css` - min-height: 100%; - `} - `} - } -`) - const ModalCrossButton = styled(IconButton)` position: absolute; top: 8px;