diff --git a/src/Modal.tsx b/src/Modal.tsx index deef7635..b8f75305 100644 --- a/src/Modal.tsx +++ b/src/Modal.tsx @@ -27,6 +27,11 @@ import { useMediaQuery } from "./useMediaQuery"; export interface Props { title: string; + /** + * Hide the modal header. Used for smaller popups where the context is readily apparent. + * A title should still be specified for users using assistive technology. + */ + hideHeader?: boolean; children: ReactNode; className?: string; /** @@ -54,6 +59,7 @@ export interface Props { */ export const Modal: FC = ({ title, + hideHeader, children, className, open, @@ -108,6 +114,28 @@ export const Modal: FC = ({ ); } else { + const titleNode = ( + + + {title} + + + ); + const header = ( +
+ {titleNode} + {onDismiss !== undefined && ( + + + + )} +
+ ); + return ( @@ -119,31 +147,19 @@ export const Modal: FC = ({
-
- - - {title} - - - {onDismiss !== undefined && ( - - - - )} -
+ {!hideHeader ? header : null} + {hideHeader ? ( + {titleNode} + ) : null}
{children}
diff --git a/src/button/ReactionToggleButton.module.css b/src/button/ReactionToggleButton.module.css index 16a5bf0a..c6b65de5 100644 --- a/src/button/ReactionToggleButton.module.css +++ b/src/button/ReactionToggleButton.module.css @@ -3,13 +3,20 @@ } .reactionPopupMenu { - background: var(--cpd-color-bg-canvas-default); - border-radius: var(--cpd-space-4x); - width: fit-content; - top: 70vh; - padding: 1em; - position: absolute; - border: none; + display: flex; +} + +/* These styles override the Modal styles to ensure our emoji + picker appears near the toolbar */ + +.reactionPopupMenuModal { + width: fit-content !important; + top: 82vh !important; +} + +.reactionPopupMenuModal > div > div { + padding-inline: var(--cpd-space-6x) !important; + padding-block: var(--cpd-space-6x) var(--cpd-space-8x) !important; } .reactionPopupMenu menu { @@ -35,6 +42,8 @@ } .reactionButton { + padding: 1em; + font-size: 2em; width: 2em; height: 2em; border-radius: 2em; diff --git a/src/button/ReactionToggleButton.tsx b/src/button/ReactionToggleButton.tsx index 052053df..7aa877e3 100644 --- a/src/button/ReactionToggleButton.tsx +++ b/src/button/ReactionToggleButton.tsx @@ -22,14 +22,10 @@ import { ChangeEventHandler, ComponentPropsWithoutRef, FC, - forwardRef, KeyboardEventHandler, - PropsWithRef, ReactNode, useCallback, - useEffect, useMemo, - useRef, useState, } from "react"; import { useTranslation } from "react-i18next"; @@ -47,6 +43,7 @@ import { ReactionSet, ElementCallReactionEventType, } from "../reactions"; +import { Modal } from "../Modal"; interface InnerButtonProps extends ComponentPropsWithoutRef<"button"> { raised: boolean; @@ -70,17 +67,17 @@ const InnerButton: FC = ({ raised, open, ...props }) => { ); }; -interface ReactionsPopupMenuProps { +export function ReactionPopupMenu({ + sendReaction, + toggleRaisedHand, + isHandRaised, + canReact, +}: { sendReaction: (reaction: ReactionOption) => void; toggleRaisedHand: () => void; isHandRaised: boolean; canReact: boolean; -} - -export const ReactionPopupMenu = forwardRef< - HTMLDialogElement, - ReactionsPopupMenuProps ->(({ sendReaction, toggleRaisedHand, isHandRaised, canReact }, ref) => { +}): ReactNode { const { t } = useTranslation(); const [searchText, setSearchText] = useState(""); const [isSearching, setIsSearching] = useState(false); @@ -122,84 +119,82 @@ export const ReactionPopupMenu = forwardRef< ); return ( - -
-
- - toggleRaisedHand()} - > - πŸ–οΈ - - -
-
-
- {isSearching ? ( - <> - - +
+
+ + toggleRaisedHand()} + > + πŸ–οΈ + + +
+
+
+ {isSearching ? ( + <> + + + setIsSearching(false)} + /> + + + + ) : null} + + {filteredReactionSet.map((reaction) => ( +
  • + setIsSearching(false)} + kind="secondary" + className={styles.reactionButton} + disabled={!canReact} + onClick={() => sendReaction(reaction)} + > + {reaction.emoji} + + +
  • + ))} + {!isSearching ? ( +
  • + + setIsSearching(true)} /> - - - + +
  • ) : null} - - {filteredReactionSet.map((reaction) => ( -
  • - - sendReaction(reaction)} - > - {reaction.emoji} - - -
  • - ))} - {!isSearching ? ( -
  • - - setIsSearching(true)} - /> - -
  • - ) : null} -
    -
    -
    -
    + + + ); -}); +} interface ReactionToggleButtonProps { rtcSession: MatrixRTCSession; @@ -215,37 +210,10 @@ export function ReactionToggleButton({ const userId = client.getUserId()!; const isHandRaised = !!raisedHands[userId]; const memberships = useMatrixRTCSessionMemberships(rtcSession); - const ref = useRef(null); + const [showReactionsMenu, setShowReactionsMenu] = useState(false); 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( async (reaction: ReactionOption) => { const myMembership = memberships.find((m) => m.sender === userId); @@ -273,7 +241,7 @@ export function ReactionToggleButton({ logger.error("Failed to send reaction", ex); } finally { setBusy(false); - hideReactionsMenu(); + setShowReactionsMenu(false); } }, [memberships, client, userId, rtcSession], @@ -285,6 +253,7 @@ export function ReactionToggleButton({ try { setBusy(true); await lowerHand(); + setShowReactionsMenu(false); } finally { setBusy(false); } @@ -313,7 +282,7 @@ export function ReactionToggleButton({ logger.error("Failed to send reaction event", ex); } finally { setBusy(false); - hideReactionsMenu(); + setShowReactionsMenu(false); } } }; @@ -332,17 +301,24 @@ export function ReactionToggleButton({ <> - void sendRelation(reaction)} - toggleRaisedHand={toggleRaisedHand} + onClick={() => setShowReactionsMenu((show) => !show)} + raised={isHandRaised || showReactionsMenu} + open={showReactionsMenu} /> + setShowReactionsMenu(false)} + > + void sendRelation(reaction)} + toggleRaisedHand={toggleRaisedHand} + /> + ); } diff --git a/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap index 34127b51..514e7f35 100644 --- a/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap +++ b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap @@ -1,11 +1,42 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Can close menu 1`] = ` +exports[`Can close search 1`] = ` + +`; + +exports[`Can close search with the escape key 1`] = `
    -
    -`; - -exports[`Can close search 1`] = ` -
    - -
    -
    - -
    -
    -
    - -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
    -
    -
    -
    -`; - -exports[`Can close search with the escape key 1`] = ` -
    - -
    -
    - -
    -
    -
    - -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
    -
    -
    `; exports[`Can lower hand 1`] = ` -
    +