diff --git a/package-lock.json b/package-lock.json index 8b907442..c36981c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "file-saver": "2.0.5", "flux": "4.0.3", "focus-trap-react": "10.0.2", - "folds": "1.7.0", + "folds": "2.0.0", "formik": "2.2.9", "html-dom-parser": "4.0.0", "html-react-parser": "4.2.0", @@ -5247,9 +5247,9 @@ } }, "node_modules/folds": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/folds/-/folds-1.7.0.tgz", - "integrity": "sha512-Uz8RIsABKbWT/mBRCaiScxuYhmA3v6HrJ0UdiRSM/mG7ssCaAIMRP3aBa5zQwdiCvqsHgZqhFZT1AFgY6PGA7Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/folds/-/folds-2.0.0.tgz", + "integrity": "sha512-lKv31vij4GEpEzGKWk5c3ar78fMZ9Di5n1XFR14Z2wnnpqhiiM5JTIzr127Gk5dOfy4mJkjnv/ZfMZvM2k+OQg==", "peerDependencies": { "@vanilla-extract/css": "^1.9.2", "@vanilla-extract/recipes": "^0.3.0", diff --git a/package.json b/package.json index 2896f446..60e1a0c5 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "file-saver": "2.0.5", "flux": "4.0.3", "focus-trap-react": "10.0.2", - "folds": "1.7.0", + "folds": "2.0.0", "formik": "2.2.9", "html-dom-parser": "4.0.0", "html-react-parser": "4.2.0", diff --git a/src/app/components/Pdf-viewer/PdfViewer.tsx b/src/app/components/Pdf-viewer/PdfViewer.tsx index c440cce9..a78c13f2 100644 --- a/src/app/components/Pdf-viewer/PdfViewer.tsx +++ b/src/app/components/Pdf-viewer/PdfViewer.tsx @@ -1,6 +1,6 @@ /* eslint-disable no-param-reassign */ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ -import React, { FormEventHandler, useEffect, useRef, useState } from 'react'; +import React, { FormEventHandler, MouseEventHandler, useEffect, useRef, useState } from 'react'; import classNames from 'classnames'; import { Box, @@ -13,6 +13,7 @@ import { Input, Menu, PopOut, + RectCords, Scroll, Spinner, Text, @@ -48,7 +49,7 @@ export const PdfViewer = as<'div', PdfViewerProps>( const isError = pdfJSState.status === AsyncStatus.Error || docState.status === AsyncStatus.Error; const [pageNo, setPageNo] = useState(1); - const [openJump, setOpenJump] = useState(false); + const [jumpAnchor, setJumpAnchor] = useState(); useEffect(() => { loadPdfJS(); @@ -86,7 +87,7 @@ export const PdfViewer = as<'div', PdfViewerProps>( if (!jumpInput) return; const jumpTo = parseInt(jumpInput.value, 10); setPageNo(Math.max(1, Math.min(docState.data.numPages, jumpTo))); - setOpenJump(false); + setJumpAnchor(undefined); }; const handlePrevPage = () => { @@ -98,6 +99,10 @@ export const PdfViewer = as<'div', PdfViewerProps>( setPageNo((n) => Math.min(n + 1, docState.data.numPages)); }; + const handleOpenJump: MouseEventHandler = (evt) => { + setJumpAnchor(evt.currentTarget.getBoundingClientRect()); + }; + return (
@@ -187,14 +192,14 @@ export const PdfViewer = as<'div', PdfViewerProps>( setOpenJump(false), + onDeactivate: () => setJumpAnchor(undefined), clickOutsideDeactivates: true, }} > @@ -227,17 +232,14 @@ export const PdfViewer = as<'div', PdfViewerProps>( } > - {(anchorRef) => ( - setOpenJump(!openJump)} - ref={anchorRef} - variant="SurfaceVariant" - radii="300" - aria-pressed={openJump} - > - {`${pageNo}/${docState.data.numPages}`} - - )} + + {`${pageNo}/${docState.data.numPages}`} + (); const isActive = isBlockActive(editor, BlockType.Heading); const modKey = isMacOS() ? KeySymbol.Command : 'Ctrl'; const handleMenuSelect = (selectedLevel: HeadingLevel) => { - setOpen(false); + setAnchor(undefined); toggleBlock(editor, BlockType.Heading, { level: selectedLevel }); ReactEditor.focus(editor); }; + const handleMenuOpen: MouseEventHandler = (evt) => { + if (isActive) { + toggleBlock(editor, BlockType.Heading); + return; + } + setAnchor(evt.currentTarget.getBoundingClientRect()); + }; return ( setOpen(false), + onDeactivate: () => setAnchor(undefined), clickOutsideDeactivates: true, isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown' || evt.key === 'ArrowRight', @@ -197,20 +205,17 @@ export function HeadingBlockButton() { } > - {(ref) => ( - (isActive ? toggleBlock(editor, BlockType.Heading) : setOpen(!open))} - aria-pressed={isActive} - size="400" - radii="300" - > - - - - )} + + + + ); } diff --git a/src/app/features/message-search/SearchFilters.tsx b/src/app/features/message-search/SearchFilters.tsx index f46d6749..5de188d4 100644 --- a/src/app/features/message-search/SearchFilters.tsx +++ b/src/app/features/message-search/SearchFilters.tsx @@ -23,6 +23,7 @@ import { Button, Input, Badge, + RectCords, } from 'folds'; import { SearchOrderBy } from 'matrix-js-sdk'; import FocusTrap from 'focus-trap-react'; @@ -43,24 +44,27 @@ type OrderButtonProps = { onChange: (order?: string) => void; }; function OrderButton({ order, onChange }: OrderButtonProps) { - const [menu, setMenu] = useState(false); + const [menuAnchor, setMenuAnchor] = useState(); const rankOrder = order === SearchOrderBy.Rank; const setOrder = (o?: string) => { - setMenu(false); + setMenuAnchor(undefined); onChange(o); }; + const handleOpenMenu: MouseEventHandler = (evt) => { + setMenuAnchor(evt.currentTarget.getBoundingClientRect()); + }; return ( setMenu(false), + onDeactivate: () => setMenuAnchor(undefined), clickOutsideDeactivates: true, }} > @@ -93,17 +97,14 @@ function OrderButton({ order, onChange }: OrderButtonProps) { } > - {(anchorRef) => ( - } - onClick={() => setMenu(true)} - > - {rankOrder ? Relevance : Recent} - - )} + } + onClick={handleOpenMenu} + > + {rankOrder ? Relevance : Recent} + ); } @@ -126,7 +127,7 @@ type SelectRoomButtonProps = { function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButtonProps) { const mx = useMatrixClient(); const scrollRef = useRef(null); - const [menu, setMenu] = useState(false); + const [menuAnchor, setMenuAnchor] = useState(); const [localSelected, setLocalSelected] = useState(selectedRooms); const getRoomNameStr: SearchItemStrGetter = useCallback( @@ -172,30 +173,34 @@ function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButto }; const handleSave = () => { - setMenu(false); + setMenuAnchor(undefined); onChange(localSelected); }; const handleDeselectAll = () => { - setMenu(false); + setMenuAnchor(undefined); onChange(undefined); }; useEffect(() => { setLocalSelected(selectedRooms); resetSearch(); - }, [menu, selectedRooms, resetSearch]); + }, [menuAnchor, selectedRooms, resetSearch]); + + const handleOpenMenu: MouseEventHandler = (evt) => { + setMenuAnchor(evt.currentTarget.getBoundingClientRect()); + }; return ( setMenu(false), + onDeactivate: () => setMenuAnchor(undefined), clickOutsideDeactivates: true, }} > @@ -307,17 +312,14 @@ function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButto } > - {(anchorRef) => ( - setMenu(true)} - ref={anchorRef} - variant="SurfaceVariant" - radii="Pill" - before={} - > - Select Rooms - - )} + } + > + Select Rooms + ); } diff --git a/src/app/features/room-nav/RoomNavItem.tsx b/src/app/features/room-nav/RoomNavItem.tsx index 3c9610a1..3c4d321b 100644 --- a/src/app/features/room-nav/RoomNavItem.tsx +++ b/src/app/features/room-nav/RoomNavItem.tsx @@ -13,6 +13,7 @@ import { PopOut, toRem, Line, + RectCords, } from 'folds'; import { useFocusWithin, useHover } from 'react-aria'; import FocusTrap from 'focus-trap-react'; @@ -150,15 +151,24 @@ export function RoomNavItem({ room, selected, showAvatar, muted, linkPath }: Roo const [hover, setHover] = useState(false); const { hoverProps } = useHover({ onHoverChange: setHover }); const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover }); - const [menu, setMenu] = useState(false); + const [menuAnchor, setMenuAnchor] = useState(); const unread = useRoomUnread(room.roomId, roomToUnreadAtom); const handleContextMenu: MouseEventHandler = (evt) => { evt.preventDefault(); - setMenu(true); + setMenuAnchor({ + x: evt.clientX, + y: evt.clientY, + width: 0, + height: 0, + }); }; - const optionsVisible = hover || menu; + const handleOpenMenu: MouseEventHandler = (evt) => { + setMenuAnchor(evt.currentTarget.getBoundingClientRect()); + }; + + const optionsVisible = hover || !!menuAnchor; return ( setMenu(false), + onDeactivate: () => setMenuAnchor(undefined), clickOutsideDeactivates: true, isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown', isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', @@ -226,24 +237,21 @@ export function RoomNavItem({ room, selected, showAvatar, muted, linkPath }: Roo setMenu(false)} + requestClose={() => setMenuAnchor(undefined)} /> } > - {(anchorRef) => ( - setMenu(true)} - aria-pressed={menu} - variant="Background" - fill="None" - size="300" - radii="300" - > - - - )} + + + )} diff --git a/src/app/features/room/MembersDrawer.tsx b/src/app/features/room/MembersDrawer.tsx index 318d4663..58b7fcef 100644 --- a/src/app/features/room/MembersDrawer.tsx +++ b/src/app/features/room/MembersDrawer.tsx @@ -22,6 +22,7 @@ import { Menu, MenuItem, PopOut, + RectCords, Scroll, Spinner, Text, @@ -289,10 +290,10 @@ export function MembersDrawer({ room }: MembersDrawerProps) { - - {(open, setOpen) => ( + + {(anchor: RectCords | undefined, setAnchor) => ( setOpen(false), + onDeactivate: () => setAnchor(undefined), clickOutsideDeactivates: true, isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown', isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', @@ -319,7 +320,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) { radii="300" onClick={() => { setMembershipFilterIndex(index); - setOpen(false); + setAnchor(undefined); }} > {menuItem.name} @@ -329,25 +330,27 @@ export function MembersDrawer({ room }: MembersDrawerProps) { } > - {(anchorRef) => ( - setOpen(!open)} - variant={membershipFilter.color} - size="400" - radii="300" - before={} - > - {membershipFilter.name} - - )} + + setAnchor( + evt.currentTarget.getBoundingClientRect() + )) as MouseEventHandler + } + variant={membershipFilter.color} + size="400" + radii="300" + before={} + > + {membershipFilter.name} + )} - - {(open, setOpen) => ( + + {(anchor: RectCords | undefined, setAnchor) => ( setOpen(false), + onDeactivate: () => setAnchor(undefined), clickOutsideDeactivates: true, isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown', isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', @@ -370,7 +373,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) { radii="300" onClick={() => { setSortFilterIndex(index); - setOpen(false); + setAnchor(undefined); }} > {menuItem.name} @@ -380,18 +383,20 @@ export function MembersDrawer({ room }: MembersDrawerProps) { } > - {(anchorRef) => ( - setOpen(!open)} - variant="Background" - size="400" - radii="300" - after={} - > - {sortFilter.name} - - )} + + setAnchor( + evt.currentTarget.getBoundingClientRect() + )) as MouseEventHandler + } + variant="Background" + size="400" + radii="300" + after={} + > + {sortFilter.name} + )} diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index c0b7607b..b87ba1c2 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -119,6 +119,7 @@ export const RoomInput = forwardRef( const [enterForNewline] = useSetting(settingsAtom, 'enterForNewline'); const [isMarkdown] = useSetting(settingsAtom, 'isMarkdown'); const commands = useCommands(mx, room); + const emojiBtnRef = useRef(null); const [msgDraft, setMsgDraft] = useAtom(roomIdToMsgDraftAtomFamily(roomId)); const [replyDraft, setReplyDraft] = useAtom(roomIdToReplyDraftAtomFamily(roomId)); @@ -521,7 +522,11 @@ export const RoomInput = forwardRef( alignOffset={-44} position="Top" align="End" - open={!!emojiBoardTab} + anchor={ + emojiBoardTab === undefined + ? undefined + : emojiBtnRef.current?.getBoundingClientRect() ?? undefined + } content={ ( /> } > - {(anchorRef) => ( - <> - {!hideStickerBtn && ( - setEmojiBoardTab(EmojiBoardTab.Sticker)} - variant="SurfaceVariant" - size="300" - radii="300" - > - - - )} - setEmojiBoardTab(EmojiBoardTab.Emoji)} - variant="SurfaceVariant" - size="300" - radii="300" - > - - - + {!hideStickerBtn && ( + setEmojiBoardTab(EmojiBoardTab.Sticker)} + variant="SurfaceVariant" + size="300" + radii="300" + > + + )} + setEmojiBoardTab(EmojiBoardTab.Emoji)} + variant="SurfaceVariant" + size="300" + radii="300" + > + + )} diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index e9d9685f..834efe8d 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -18,6 +18,7 @@ import { OverlayBackdrop, OverlayCenter, PopOut, + RectCords, Spinner, Text, as, @@ -610,8 +611,8 @@ export const Message = as<'div', MessageProps>( const [hover, setHover] = useState(false); const { hoverProps } = useHover({ onHoverChange: setHover }); const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover }); - const [menu, setMenu] = useState(false); - const [emojiBoard, setEmojiBoard] = useState(false); + const [menuAnchor, setMenuAnchor] = useState(); + const [emojiBoardAnchor, setEmojiBoardAnchor] = useState(); const senderDisplayName = getMemberDisplayName(room, senderId) ?? getMxIdLocalPart(senderId) ?? senderId; @@ -706,11 +707,36 @@ export const Message = as<'div', MessageProps>( const tag = (evt.target as any).tagName; if (typeof tag === 'string' && tag.toLowerCase() === 'a') return; evt.preventDefault(); - setMenu(true); + setMenuAnchor({ + x: evt.clientX, + y: evt.clientY, + width: 0, + height: 0, + }); + }; + + const handleOpenMenu: MouseEventHandler = (evt) => { + const target = evt.currentTarget.parentElement?.parentElement ?? evt.currentTarget; + setMenuAnchor(target.getBoundingClientRect()); }; const closeMenu = () => { - setMenu(false); + setMenuAnchor(undefined); + }; + + const handleOpenEmojiBoard: MouseEventHandler = (evt) => { + const target = evt.currentTarget.parentElement?.parentElement ?? evt.currentTarget; + setEmojiBoardAnchor(target.getBoundingClientRect()); + }; + const handleAddReactions: MouseEventHandler = () => { + const rect = menuAnchor; + closeMenu(); + // open it with timeout because closeMenu + // FocusTrap will return focus from emojiBoard + + setTimeout(() => { + setEmojiBoardAnchor(rect); + }, 100); }; return ( @@ -720,22 +746,22 @@ export const Message = as<'div', MessageProps>( space={messageSpacing} collapse={collapse} highlight={highlight} - selected={menu || emojiBoard} + selected={!!menuAnchor || !!emojiBoardAnchor} {...props} {...hoverProps} {...focusWithinProps} ref={ref} > - {!edit && (hover || menu || emojiBoard) && ( + {!edit && (hover || !!menuAnchor || !!emojiBoardAnchor) && (
{canSendReaction && ( ( allowTextCustomEmoji onEmojiSelect={(key) => { onReactionToggle(mEvent.getId()!, key); - setEmojiBoard(false); + setEmojiBoardAnchor(undefined); }} onCustomEmojiSelect={(mxc, shortcode) => { onReactionToggle(mEvent.getId()!, mxc, shortcode); - setEmojiBoard(false); + setEmojiBoardAnchor(undefined); }} requestClose={() => { - setEmojiBoard(false); + setEmojiBoardAnchor(undefined); }} /> } > - {(anchorRef) => ( - setEmojiBoard(true)} - variant="SurfaceVariant" - size="300" - radii="300" - aria-pressed={emojiBoard} - > - - - )} + + + )} ( )} setMenu(false), + onDeactivate: () => setMenuAnchor(undefined), clickOutsideDeactivates: true, isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown', isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', @@ -818,12 +841,7 @@ export const Message = as<'div', MessageProps>( size="300" after={} radii="300" - onClick={() => { - closeMenu(); - // open it with timeout because closeMenu - // FocusTrap will return focus from emojiBoard - setTimeout(() => setEmojiBoard(true), 100); - }} + onClick={handleAddReactions} > ( } > - {(targetRef) => ( - setMenu((v) => !v)} - aria-pressed={menu} - > - - - )} + + + @@ -967,7 +982,7 @@ export const Event = as<'div', EventProps>( const [hover, setHover] = useState(false); const { hoverProps } = useHover({ onHoverChange: setHover }); const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover }); - const [menu, setMenu] = useState(false); + const [menuAnchor, setMenuAnchor] = useState(); const stateEvent = typeof mEvent.getStateKey() === 'string'; const handleContextMenu: MouseEventHandler = (evt) => { @@ -975,11 +990,21 @@ export const Event = as<'div', EventProps>( const tag = (evt.target as any).tagName; if (typeof tag === 'string' && tag.toLowerCase() === 'a') return; evt.preventDefault(); - setMenu(true); + setMenuAnchor({ + x: evt.clientX, + y: evt.clientY, + width: 0, + height: 0, + }); + }; + + const handleOpenMenu: MouseEventHandler = (evt) => { + const target = evt.currentTarget.parentElement?.parentElement ?? evt.currentTarget; + setMenuAnchor(target.getBoundingClientRect()); }; const closeMenu = () => { - setMenu(false); + setMenuAnchor(undefined); }; return ( @@ -989,26 +1014,26 @@ export const Event = as<'div', EventProps>( space={messageSpacing} autoCollapse highlight={highlight} - selected={menu} + selected={!!menuAnchor} {...props} {...hoverProps} {...focusWithinProps} ref={ref} > - {(hover || menu) && ( + {(hover || !!menuAnchor) && (
setMenu(false), + onDeactivate: () => setMenuAnchor(undefined), clickOutsideDeactivates: true, isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown', isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', @@ -1049,18 +1074,15 @@ export const Event = as<'div', EventProps>( } > - {(targetRef) => ( - setMenu((v) => !v)} - aria-pressed={menu} - > - - - )} + + + diff --git a/src/app/features/room/message/MessageEditor.tsx b/src/app/features/room/message/MessageEditor.tsx index 006b46a8..0c995030 100644 --- a/src/app/features/room/message/MessageEditor.tsx +++ b/src/app/features/room/message/MessageEditor.tsx @@ -1,5 +1,24 @@ -import React, { KeyboardEventHandler, useCallback, useEffect, useState } from 'react'; -import { Box, Chip, Icon, IconButton, Icons, Line, PopOut, Spinner, Text, as, config } from 'folds'; +import React, { + KeyboardEventHandler, + MouseEventHandler, + useCallback, + useEffect, + useState, +} from 'react'; +import { + Box, + Chip, + Icon, + IconButton, + Icons, + Line, + PopOut, + RectCords, + Spinner, + Text, + as, + config, +} from 'folds'; import { Editor, Transforms } from 'slate'; import { ReactEditor } from 'slate-react'; import { IContent, MatrixEvent, RelationType, Room } from 'matrix-js-sdk'; @@ -258,13 +277,13 @@ export const MessageEditor = as<'div', MessageEditorProps>( > - - {(emojiBoard: boolean, setEmojiBoard) => ( + + {(anchor: RectCords | undefined, setAnchor) => ( ( onEmojiSelect={handleEmoticonSelect} onCustomEmojiSelect={handleEmoticonSelect} requestClose={() => { - setEmojiBoard(false); + setAnchor(undefined); if (!mobileOrTablet()) ReactEditor.focus(editor); }} /> } > - {(anchorRef) => ( - setEmojiBoard(true)} - variant="SurfaceVariant" - size="300" - radii="300" - > - - - )} + + setAnchor( + evt.currentTarget.getBoundingClientRect() + )) as MouseEventHandler + } + variant="SurfaceVariant" + size="300" + radii="300" + > + + )} diff --git a/src/app/pages/auth/ServerPicker.tsx b/src/app/pages/auth/ServerPicker.tsx index 5f5dcf65..18201c98 100644 --- a/src/app/pages/auth/ServerPicker.tsx +++ b/src/app/pages/auth/ServerPicker.tsx @@ -15,6 +15,7 @@ import { Menu, MenuItem, PopOut, + RectCords, Text, config, } from 'folds'; @@ -33,7 +34,7 @@ export function ServerPicker({ allowCustomServer?: boolean; onServerChange: (server: string) => void; }) { - const [serverMenu, setServerMenu] = useState(false); + const [serverMenuAnchor, setServerMenuAnchor] = useState(); const serverInputRef = useRef(null); useEffect(() => { @@ -53,7 +54,7 @@ export function ServerPicker({ const handleKeyDown: KeyboardEventHandler = (evt) => { if (evt.key === 'ArrowDown') { evt.preventDefault(); - setServerMenu(true); + setServerMenuAnchor(undefined); } if (evt.key === 'Enter') { evt.preventDefault(); @@ -67,7 +68,12 @@ export function ServerPicker({ if (selectedServer) { onServerChange(selectedServer); } - setServerMenu(false); + setServerMenuAnchor(undefined); + }; + + const handleOpenServerMenu: MouseEventHandler = (evt) => { + const target = evt.currentTarget.parentElement ?? evt.currentTarget; + setServerMenuAnchor(target.getBoundingClientRect()); }; return ( @@ -81,11 +87,11 @@ export function ServerPicker({ onKeyDown={handleKeyDown} size="500" readOnly={!allowCustomServer} - onClick={allowCustomServer ? undefined : () => setServerMenu(true)} + onClick={allowCustomServer ? undefined : handleOpenServerMenu} after={ serverList.length === 0 || (serverList.length === 1 && !allowCustomServer) ? undefined : ( setServerMenu(false), + onDeactivate: () => setServerMenuAnchor(undefined), clickOutsideDeactivates: true, isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown', isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', @@ -120,18 +126,15 @@ export function ServerPicker({ } > - {(anchorRef) => ( - setServerMenu(true)} - variant={allowCustomServer ? 'Background' : 'Surface'} - size="300" - aria-pressed={serverMenu} - radii="300" - > - - - )} + + + ) } diff --git a/src/app/pages/auth/login/PasswordLoginForm.tsx b/src/app/pages/auth/login/PasswordLoginForm.tsx index ea52aad8..b9dd14b7 100644 --- a/src/app/pages/auth/login/PasswordLoginForm.tsx +++ b/src/app/pages/auth/login/PasswordLoginForm.tsx @@ -1,4 +1,4 @@ -import React, { FormEventHandler, useCallback, useState } from 'react'; +import React, { FormEventHandler, MouseEventHandler, useCallback, useState } from 'react'; import { Box, Button, @@ -12,6 +12,7 @@ import { OverlayBackdrop, OverlayCenter, PopOut, + RectCords, Spinner, Text, config, @@ -37,17 +38,21 @@ import { FieldError } from '../FiledError'; import { getResetPasswordPath } from '../../pathUtils'; function UsernameHint({ server }: { server: string }) { - const [open, setOpen] = useState(false); + const [anchor, setAnchor] = useState(); + + const handleOpenMenu: MouseEventHandler = (evt) => { + setAnchor(evt.currentTarget.getBoundingClientRect()); + }; return ( setOpen(false), + onDeactivate: () => setAnchor(undefined), clickOutsideDeactivates: true, }} > @@ -84,20 +89,17 @@ function UsernameHint({ server }: { server: string }) { } > - {(targetRef) => ( - setOpen(true)} - ref={targetRef} - type="button" - variant="Background" - size="300" - radii="300" - aria-pressed={open} - > - - - )} + + + ); } diff --git a/src/app/pages/client/explore/Server.tsx b/src/app/pages/client/explore/Server.tsx index 10791302..9fe4e78e 100644 --- a/src/app/pages/client/explore/Server.tsx +++ b/src/app/pages/client/explore/Server.tsx @@ -19,6 +19,7 @@ import { Menu, MenuItem, PopOut, + RectCords, Scroll, Spinner, Text, @@ -149,7 +150,7 @@ function ThirdPartyProtocolsSelector({ onChange: (instanceId?: string) => void; }) { const mx = useMatrixClient(); - const [menu, setMenu] = useState(false); + const [menuAnchor, setMenuAnchor] = useState(); const { data } = useQuery({ queryKey: ['thirdparty', 'protocols'], @@ -159,7 +160,11 @@ function ThirdPartyProtocolsSelector({ const handleInstanceSelect: MouseEventHandler = (evt): void => { const insId = evt.currentTarget.getAttribute('data-instance-id') ?? undefined; onChange(insId); - setMenu(false); + setMenuAnchor(undefined); + }; + + const handleOpenMenu: MouseEventHandler = (evt) => { + setMenuAnchor(evt.currentTarget.getBoundingClientRect()); }; const instances = data && Object.keys(data).flatMap((protocol) => data[protocol].instances); @@ -168,14 +173,14 @@ function ThirdPartyProtocolsSelector({ return ( setMenu(false), + onDeactivate: () => setMenuAnchor(undefined), clickOutsideDeactivates: true, }} > @@ -221,21 +226,18 @@ function ThirdPartyProtocolsSelector({ } > - {(anchorRef) => ( - setMenu(!menu)} - aria-pressed={menu} - radii="Pill" - size="400" - variant={instanceId ? 'Success' : 'SurfaceVariant'} - after={} - > - - {selectedInstance?.desc ?? DEFAULT_INSTANCE_NAME} - - - )} + } + > + + {selectedInstance?.desc ?? DEFAULT_INSTANCE_NAME} + + ); } @@ -245,7 +247,7 @@ type LimitButtonProps = { onLimitChange: (limit: string) => void; }; function LimitButton({ limit, onLimitChange }: LimitButtonProps) { - const [openLimit, setOpenLimit] = useState(false); + const [menuAnchor, setMenuAnchor] = useState(); const handleLimitSubmit: FormEventHandler = (evt) => { evt.preventDefault(); @@ -257,20 +259,23 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) { }; const setLimit = (l: string) => { - setOpenLimit(false); + setMenuAnchor(undefined); onLimitChange(l); }; + const handleOpenMenu: MouseEventHandler = (evt) => { + setMenuAnchor(evt.currentTarget.getBoundingClientRect()); + }; return ( setOpenLimit(false), + onDeactivate: () => setMenuAnchor(undefined), clickOutsideDeactivates: true, }} > @@ -315,19 +320,16 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) { } > - {(anchorRef) => ( - setOpenLimit(!openLimit)} - aria-pressed={openLimit} - radii="Pill" - size="400" - variant="SurfaceVariant" - after={} - > - {`Page Limit: ${limit}`} - - )} + } + > + {`Page Limit: ${limit}`} + ); }