Migrate Guide.tsx from Reakit to Radix Dialog (#9752)

* Migrate Guide.tsx from Reakit to Radix Dialog

- Replace Reakit Dialog imports with @radix-ui/react-dialog
- Update component structure to use Dialog.Root, Dialog.Portal, Dialog.Overlay, and Dialog.Content
- Remove useDialogState hook in favor of direct open/onOpenChange props
- Preserve identical styling and animations using data-state attributes
- Maintain slide-in animation from right side with 250ms transition
- Keep all existing props and behavior intact

* stash

* fix animations

---------

Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
This commit is contained in:
codegen-sh[bot]
2025-07-28 07:54:29 -04:00
committed by GitHub
parent 520a2e363e
commit 7e11b23f87
2 changed files with 71 additions and 59 deletions
+42 -59
View File
@@ -1,9 +1,14 @@
import * as React from "react"; import * as React from "react";
import { Dialog, DialogBackdrop, useDialogState } from "reakit/Dialog"; import * as Dialog from "@radix-ui/react-dialog";
import styled from "styled-components"; import styled from "styled-components";
import { depths, s } from "@shared/styles"; import { depths, s } from "@shared/styles";
import Scrollable from "~/components/Scrollable"; import Scrollable from "~/components/Scrollable";
import usePrevious from "~/hooks/usePrevious"; import {
fadeIn,
fadeOut,
fadeInAndSlideLeft,
fadeOutAndSlideRight,
} from "~/styles/animations";
type Props = { type Props = {
children?: React.ReactNode; children?: React.ReactNode;
@@ -18,55 +23,33 @@ const Guide: React.FC<Props> = ({
title = "Untitled", title = "Untitled",
onRequestClose, onRequestClose,
...rest ...rest
}: Props) => { }: Props) => (
const dialog = useDialogState({ <Dialog.Root open={isOpen} onOpenChange={(open) => !open && onRequestClose()}>
animated: 250, <Dialog.Portal>
}); <StyledOverlay>
const wasOpen = usePrevious(isOpen); <Scene
onEscapeKeyDown={onRequestClose}
onPointerDownOutside={onRequestClose}
aria-describedby={undefined}
{...rest}
>
<Content>
{title && <Header>{title}</Header>}
{children}
</Content>
</Scene>
</StyledOverlay>
</Dialog.Portal>
</Dialog.Root>
);
React.useEffect(() => { const Header = styled(Dialog.Title)`
if (!wasOpen && isOpen) {
dialog.show();
}
if (wasOpen && !isOpen) {
dialog.hide();
}
}, [dialog, wasOpen, isOpen]);
return (
<DialogBackdrop {...dialog}>
{(backdropProps) => (
<Backdrop {...backdropProps}>
<Dialog
{...dialog}
aria-label={title}
preventBodyScroll
hideOnEsc
hide={onRequestClose}
>
{(dialogProps) => (
<Scene {...dialogProps} {...rest}>
<Content>
{title && <Header>{title}</Header>}
{children}
</Content>
</Scene>
)}
</Dialog>
</Backdrop>
)}
</DialogBackdrop>
);
};
const Header = styled.h1`
font-size: 18px; font-size: 18px;
margin-top: 0; margin-top: 0;
margin-bottom: 1em; margin-bottom: 1em;
`; `;
const Backdrop = styled.div` const StyledOverlay = styled(Dialog.Overlay)`
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@@ -74,37 +57,37 @@ const Backdrop = styled.div`
bottom: 0; bottom: 0;
background-color: ${s("backdrop")} !important; background-color: ${s("backdrop")} !important;
z-index: ${depths.overlay}; z-index: ${depths.overlay};
transition: opacity 200ms ease-in-out;
opacity: 0;
&[data-enter] { &[data-state="open"] {
opacity: 1; animation: ${fadeIn} 200ms ease;
}
&[data-state="closed"] {
animation: ${fadeOut} 200ms ease;
} }
`; `;
const Scene = styled.div` const Scene = styled(Dialog.Content)`
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
margin: 12px; margin: 12px;
z-index: ${depths.modal};
display: flex; display: flex;
z-index: ${depths.modal};
justify-content: center; justify-content: center;
align-items: flex-start; align-items: flex-start;
width: 350px; width: 350px;
background: ${s("background")}; background: ${s("background")};
border-radius: 8px; border-radius: 8px;
outline: none; outline: none;
opacity: 0;
transform: translateX(16px);
transition:
transform 250ms ease,
opacity 250ms ease;
&[data-enter] { &[data-state="open"] {
opacity: 1; animation: ${fadeInAndSlideLeft} 200ms ease;
transform: translateX(0px); }
&[data-state="closed"] {
animation: ${fadeOutAndSlideRight} 200ms ease;
} }
`; `;
+29
View File
@@ -5,6 +5,11 @@ export const fadeIn = keyframes`
to { opacity: 1; } to { opacity: 1; }
`; `;
export const fadeOut = keyframes`
from { opacity: 1; }
to { opacity: 0; }
`;
export const fadeOutCursor = keyframes` export const fadeOutCursor = keyframes`
0% { opacity: 1; } 0% { opacity: 1; }
90% { opacity: 1; } 90% { opacity: 1; }
@@ -47,6 +52,30 @@ export const fadeAndSlideUp = keyframes`
} }
`; `;
export const fadeInAndSlideLeft = keyframes`
from {
opacity: 0;
transform: translateX(10px);
}
to {
opacity: 1;
transform: translateX(0px);
}
`;
export const fadeOutAndSlideRight = keyframes`
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(10px);
}
`;
export const mobileContextMenu = keyframes` export const mobileContextMenu = keyframes`
from { from {
opacity: 0; opacity: 0;