mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
Replace reakit/Popover with radix-ui in Collaborators component (#9360)
- Migrated from reakit usePopoverState to radix-ui Popover.Root - Replaced PopoverDisclosure with Popover.Trigger - Updated popover content with styled component matching original design - Maintained same styling with fadeAndScaleIn animation, menuBackground, and menuShadow - Added @radix-ui/react-popover dependency - Preserved all existing functionality and accessibility features 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:
@@ -1,3 +1,4 @@
|
||||
import * as Popover from "@radix-ui/react-popover";
|
||||
import filter from "lodash/filter";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import orderBy from "lodash/orderBy";
|
||||
@@ -5,15 +6,16 @@ import uniq from "lodash/uniq";
|
||||
import { observer } from "mobx-react";
|
||||
import { useState, useMemo, useEffect, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { usePopoverState, PopoverDisclosure } from "reakit/Popover";
|
||||
import styled from "styled-components";
|
||||
import { depths, s } from "@shared/styles";
|
||||
import Document from "~/models/Document";
|
||||
import { AvatarSize, AvatarWithPresence } from "~/components/Avatar";
|
||||
import DocumentViews from "~/components/DocumentViews";
|
||||
import Facepile from "~/components/Facepile";
|
||||
import NudeButton from "~/components/NudeButton";
|
||||
import Popover from "~/components/Popover";
|
||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { fadeAndScaleIn } from "~/styles/animations";
|
||||
|
||||
type Props = {
|
||||
/** The document to display live collaborators for */
|
||||
@@ -22,6 +24,21 @@ type Props = {
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
// Styled components to match the original Popover styling
|
||||
const StyledPopoverContent = styled(Popover.Content)`
|
||||
animation: ${fadeAndScaleIn} 200ms ease;
|
||||
transform-origin: 75% 0;
|
||||
background: ${s("menuBackground")};
|
||||
border-radius: 6px;
|
||||
padding: 12px 24px;
|
||||
max-height: 75vh;
|
||||
box-shadow: ${s("menuShadow")};
|
||||
z-index: ${depths.modal};
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
outline: none;
|
||||
`;
|
||||
|
||||
/**
|
||||
* Displays a list of live collaborators for a document, including their avatars
|
||||
* and presence status.
|
||||
@@ -32,6 +49,7 @@ function Collaborators(props: Props) {
|
||||
const user = useCurrentUser();
|
||||
const currentUserId = user?.id;
|
||||
const [requestedUserIds, setRequestedUserIds] = useState<string[]>([]);
|
||||
const [popoverOpen, setPopoverOpen] = useState(false);
|
||||
const { users, presence, ui } = useStores();
|
||||
const { document } = props;
|
||||
const { observingUserId } = ui;
|
||||
@@ -94,11 +112,6 @@ function Collaborators(props: Props) {
|
||||
}
|
||||
}, [missingUserIds, requestedUserIds, users]);
|
||||
|
||||
const popover = usePopoverState({
|
||||
gutter: 0,
|
||||
placement: "bottom-end",
|
||||
});
|
||||
|
||||
// Memoize onClick handler to avoid inline function creation
|
||||
const handleAvatarClick = useCallback(
|
||||
(
|
||||
@@ -150,28 +163,33 @@ function Collaborators(props: Props) {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PopoverDisclosure {...popover}>
|
||||
{(popoverProps) => (
|
||||
<NudeButton
|
||||
width={Math.min(collaborators.length, limit) * AvatarSize.Large}
|
||||
height={AvatarSize.Large}
|
||||
{...popoverProps}
|
||||
>
|
||||
<Facepile
|
||||
size={AvatarSize.Large}
|
||||
limit={limit}
|
||||
overflow={Math.max(0, collaborators.length - limit)}
|
||||
users={collaborators}
|
||||
renderAvatar={renderAvatar}
|
||||
/>
|
||||
</NudeButton>
|
||||
)}
|
||||
</PopoverDisclosure>
|
||||
<Popover {...popover} width={300} aria-label={t("Viewers")} tabIndex={0}>
|
||||
{popover.visible && <DocumentViews document={document} />}
|
||||
</Popover>
|
||||
</>
|
||||
<Popover.Root open={popoverOpen} onOpenChange={setPopoverOpen}>
|
||||
<Popover.Trigger asChild>
|
||||
<NudeButton
|
||||
width={Math.min(collaborators.length, limit) * AvatarSize.Large}
|
||||
height={AvatarSize.Large}
|
||||
>
|
||||
<Facepile
|
||||
size={AvatarSize.Large}
|
||||
limit={limit}
|
||||
overflow={Math.max(0, collaborators.length - limit)}
|
||||
users={collaborators}
|
||||
renderAvatar={renderAvatar}
|
||||
/>
|
||||
</NudeButton>
|
||||
</Popover.Trigger>
|
||||
<Popover.Portal>
|
||||
<StyledPopoverContent
|
||||
side="bottom"
|
||||
align="end"
|
||||
sideOffset={0}
|
||||
aria-label={t("Viewers")}
|
||||
style={{ width: 300 }}
|
||||
>
|
||||
<DocumentViews document={document} />
|
||||
</StyledPopoverContent>
|
||||
</Popover.Portal>
|
||||
</Popover.Root>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user