modal impl

This commit is contained in:
Half-Shot
2024-11-07 11:33:10 +00:00
parent 29d562aea1
commit 60300c2f84
2 changed files with 136 additions and 104 deletions
+5 -9
View File
@@ -3,17 +3,13 @@
} }
.reactionPopupMenu { .reactionPopupMenu {
background: var(--cpd-color-bg-canvas-default);
border-radius: var(--cpd-space-4x);
width: fit-content;
top: 70vh;
padding: 1em; padding: 1em;
position: absolute; position: absolute;
background: var(--cpd-color-bg-canvas-default); border: none;
top: -8em;
border-radius: var(--cpd-space-4x);
display: flex;
width: fit-content;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
} }
.reactionPopupMenu menu { .reactionPopupMenu menu {
+54 -18
View File
@@ -16,15 +16,20 @@ import {
SearchIcon, SearchIcon,
CloseIcon, CloseIcon,
RaisedHandSolidIcon, RaisedHandSolidIcon,
ReactionIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons"; } from "@vector-im/compound-design-tokens/assets/web/icons";
import { import {
ChangeEventHandler, ChangeEventHandler,
ComponentPropsWithoutRef, ComponentPropsWithoutRef,
FC, FC,
forwardRef,
KeyboardEventHandler, KeyboardEventHandler,
PropsWithRef,
ReactNode, ReactNode,
useCallback, useCallback,
useEffect,
useMemo, useMemo,
useRef,
useState, useState,
} from "react"; } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -45,9 +50,10 @@ import {
interface InnerButtonProps extends ComponentPropsWithoutRef<"button"> { interface InnerButtonProps extends ComponentPropsWithoutRef<"button"> {
raised: boolean; raised: boolean;
open: boolean;
} }
const InnerButton: FC<InnerButtonProps> = ({ raised, ...props }) => { const InnerButton: FC<InnerButtonProps> = ({ raised, open, ...props }) => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@@ -55,26 +61,26 @@ const InnerButton: FC<InnerButtonProps> = ({ raised, ...props }) => {
<CpdButton <CpdButton
className={classNames(raised && styles.raisedButton)} className={classNames(raised && styles.raisedButton)}
aria-expanded={raised} aria-expanded={raised}
kind={raised ? "primary" : "secondary"} kind={raised || open ? "primary" : "secondary"}
iconOnly iconOnly
Icon={RaisedHandSolidIcon} Icon={raised ? RaisedHandSolidIcon : ReactionIcon}
{...props} {...props}
/> />
</Tooltip> </Tooltip>
); );
}; };
export function ReactionPopupMenu({ interface ReactionsPopupMenuProps {
sendReaction,
toggleRaisedHand,
isHandRaised,
canReact,
}: {
sendReaction: (reaction: ReactionOption) => void; sendReaction: (reaction: ReactionOption) => void;
toggleRaisedHand: () => void; toggleRaisedHand: () => void;
isHandRaised: boolean; isHandRaised: boolean;
canReact: boolean; canReact: boolean;
}): ReactNode { }
export const ReactionPopupMenu = forwardRef<
HTMLDialogElement,
ReactionsPopupMenuProps
>(({ sendReaction, toggleRaisedHand, isHandRaised, canReact }, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
const [isSearching, setIsSearching] = useState(false); const [isSearching, setIsSearching] = useState(false);
@@ -116,7 +122,8 @@ export function ReactionPopupMenu({
); );
return ( return (
<div className={styles.reactionPopupMenu}> <dialog ref={ref} className={styles.reactionPopupMenu} open={false}>
<div style={{ display: "flex" }}>
<section className={styles.handRaiseSection}> <section className={styles.handRaiseSection}>
<Tooltip label={t("common.raise_hand")}> <Tooltip label={t("common.raise_hand")}>
<CpdButton <CpdButton
@@ -190,8 +197,9 @@ export function ReactionPopupMenu({
</menu> </menu>
</section> </section>
</div> </div>
</dialog>
); );
} });
interface ReactionToggleButtonProps { interface ReactionToggleButtonProps {
rtcSession: MatrixRTCSession; rtcSession: MatrixRTCSession;
@@ -207,10 +215,37 @@ export function ReactionToggleButton({
const userId = client.getUserId()!; const userId = client.getUserId()!;
const isHandRaised = !!raisedHands[userId]; const isHandRaised = !!raisedHands[userId];
const memberships = useMatrixRTCSessionMemberships(rtcSession); const memberships = useMatrixRTCSessionMemberships(rtcSession);
const [showReactionsMenu, setShowReactionsMenu] = useState(false); const ref = useRef<HTMLDialogElement>(null);
const canReact = !reactions[userId]; const canReact = !reactions[userId];
const showReactionsMenu = useCallback(() => {
if (ref.current) {
ref.current.showModal();
}
}, [ref]);
const hideReactionsMenu = useCallback(() => {
if (ref.current) {
ref.current.close();
}
}, [ref]);
useEffect(() => {
if (!ref.current) {
return;
}
function onClick(evt: MouseEvent) {
if (evt.target === ref.current) {
hideReactionsMenu();
}
}
ref.current.addEventListener("click", onClick);
return () => {
ref.current?.removeEventListener("click", onClick);
};
}, [ref]);
const sendRelation = useCallback( const sendRelation = useCallback(
async (reaction: ReactionOption) => { async (reaction: ReactionOption) => {
const myMembership = memberships.find((m) => m.sender === userId); const myMembership = memberships.find((m) => m.sender === userId);
@@ -238,6 +273,7 @@ export function ReactionToggleButton({
logger.error("Failed to send reaction", ex); logger.error("Failed to send reaction", ex);
} finally { } finally {
setBusy(false); setBusy(false);
hideReactionsMenu();
} }
}, },
[memberships, client, userId, rtcSession], [memberships, client, userId, rtcSession],
@@ -277,7 +313,7 @@ export function ReactionToggleButton({
logger.error("Failed to send reaction event", ex); logger.error("Failed to send reaction event", ex);
} finally { } finally {
setBusy(false); setBusy(false);
setShowReactionsMenu(false); hideReactionsMenu();
} }
} }
}; };
@@ -296,17 +332,17 @@ export function ReactionToggleButton({
<> <>
<InnerButton <InnerButton
disabled={busy} disabled={busy}
onClick={() => setShowReactionsMenu((show) => !show)} onClick={showReactionsMenu}
raised={isHandRaised || showReactionsMenu} raised={isHandRaised}
open={!!ref.current?.open}
/> />
{showReactionsMenu && (
<ReactionPopupMenu <ReactionPopupMenu
ref={ref}
isHandRaised={isHandRaised} isHandRaised={isHandRaised}
canReact={canReact} canReact={canReact}
sendReaction={(reaction) => void sendRelation(reaction)} sendReaction={(reaction) => void sendRelation(reaction)}
toggleRaisedHand={toggleRaisedHand} toggleRaisedHand={toggleRaisedHand}
/> />
)}
</> </>
); );
} }