mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-02-24 06:03:04 +01:00
update folds
This commit is contained in:
parent
e4c6f455ef
commit
fb642087f7
13 changed files with 393 additions and 322 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -31,7 +31,7 @@
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"flux": "4.0.3",
|
"flux": "4.0.3",
|
||||||
"focus-trap-react": "10.0.2",
|
"focus-trap-react": "10.0.2",
|
||||||
"folds": "1.7.0",
|
"folds": "2.0.0",
|
||||||
"formik": "2.2.9",
|
"formik": "2.2.9",
|
||||||
"html-dom-parser": "4.0.0",
|
"html-dom-parser": "4.0.0",
|
||||||
"html-react-parser": "4.2.0",
|
"html-react-parser": "4.2.0",
|
||||||
|
@ -5247,9 +5247,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/folds": {
|
"node_modules/folds": {
|
||||||
"version": "1.7.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/folds/-/folds-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/folds/-/folds-2.0.0.tgz",
|
||||||
"integrity": "sha512-Uz8RIsABKbWT/mBRCaiScxuYhmA3v6HrJ0UdiRSM/mG7ssCaAIMRP3aBa5zQwdiCvqsHgZqhFZT1AFgY6PGA7Q==",
|
"integrity": "sha512-lKv31vij4GEpEzGKWk5c3ar78fMZ9Di5n1XFR14Z2wnnpqhiiM5JTIzr127Gk5dOfy4mJkjnv/ZfMZvM2k+OQg==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vanilla-extract/css": "^1.9.2",
|
"@vanilla-extract/css": "^1.9.2",
|
||||||
"@vanilla-extract/recipes": "^0.3.0",
|
"@vanilla-extract/recipes": "^0.3.0",
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"flux": "4.0.3",
|
"flux": "4.0.3",
|
||||||
"focus-trap-react": "10.0.2",
|
"focus-trap-react": "10.0.2",
|
||||||
"folds": "1.7.0",
|
"folds": "2.0.0",
|
||||||
"formik": "2.2.9",
|
"formik": "2.2.9",
|
||||||
"html-dom-parser": "4.0.0",
|
"html-dom-parser": "4.0.0",
|
||||||
"html-react-parser": "4.2.0",
|
"html-react-parser": "4.2.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
|
/* 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 classNames from 'classnames';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
@ -13,6 +13,7 @@ import {
|
||||||
Input,
|
Input,
|
||||||
Menu,
|
Menu,
|
||||||
PopOut,
|
PopOut,
|
||||||
|
RectCords,
|
||||||
Scroll,
|
Scroll,
|
||||||
Spinner,
|
Spinner,
|
||||||
Text,
|
Text,
|
||||||
|
@ -48,7 +49,7 @@ export const PdfViewer = as<'div', PdfViewerProps>(
|
||||||
const isError =
|
const isError =
|
||||||
pdfJSState.status === AsyncStatus.Error || docState.status === AsyncStatus.Error;
|
pdfJSState.status === AsyncStatus.Error || docState.status === AsyncStatus.Error;
|
||||||
const [pageNo, setPageNo] = useState(1);
|
const [pageNo, setPageNo] = useState(1);
|
||||||
const [openJump, setOpenJump] = useState(false);
|
const [jumpAnchor, setJumpAnchor] = useState<RectCords>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadPdfJS();
|
loadPdfJS();
|
||||||
|
@ -86,7 +87,7 @@ export const PdfViewer = as<'div', PdfViewerProps>(
|
||||||
if (!jumpInput) return;
|
if (!jumpInput) return;
|
||||||
const jumpTo = parseInt(jumpInput.value, 10);
|
const jumpTo = parseInt(jumpInput.value, 10);
|
||||||
setPageNo(Math.max(1, Math.min(docState.data.numPages, jumpTo)));
|
setPageNo(Math.max(1, Math.min(docState.data.numPages, jumpTo)));
|
||||||
setOpenJump(false);
|
setJumpAnchor(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePrevPage = () => {
|
const handlePrevPage = () => {
|
||||||
|
@ -98,6 +99,10 @@ export const PdfViewer = as<'div', PdfViewerProps>(
|
||||||
setPageNo((n) => Math.min(n + 1, docState.data.numPages));
|
setPageNo((n) => Math.min(n + 1, docState.data.numPages));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenJump: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
|
setJumpAnchor(evt.currentTarget.getBoundingClientRect());
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className={classNames(css.PdfViewer, className)} direction="Column" {...props} ref={ref}>
|
<Box className={classNames(css.PdfViewer, className)} direction="Column" {...props} ref={ref}>
|
||||||
<Header className={css.PdfViewerHeader} size="400">
|
<Header className={css.PdfViewerHeader} size="400">
|
||||||
|
@ -187,14 +192,14 @@ export const PdfViewer = as<'div', PdfViewerProps>(
|
||||||
</Chip>
|
</Chip>
|
||||||
<Box grow="Yes" justifyContent="Center" alignItems="Center" gap="200">
|
<Box grow="Yes" justifyContent="Center" alignItems="Center" gap="200">
|
||||||
<PopOut
|
<PopOut
|
||||||
open={openJump}
|
anchor={jumpAnchor}
|
||||||
align="Center"
|
align="Center"
|
||||||
position="Top"
|
position="Top"
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setOpenJump(false),
|
onDeactivate: () => setJumpAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -227,17 +232,14 @@ export const PdfViewer = as<'div', PdfViewerProps>(
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<Chip
|
<Chip
|
||||||
onClick={() => setOpenJump(!openJump)}
|
onClick={handleOpenJump}
|
||||||
ref={anchorRef}
|
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
radii="300"
|
radii="300"
|
||||||
aria-pressed={openJump}
|
aria-pressed={jumpAnchor !== undefined}
|
||||||
>
|
>
|
||||||
<Text size="B300">{`${pageNo}/${docState.data.numPages}`}</Text>
|
<Text size="B300">{`${pageNo}/${docState.data.numPages}`}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
</Box>
|
</Box>
|
||||||
<Chip
|
<Chip
|
||||||
|
|
|
@ -10,13 +10,14 @@ import {
|
||||||
Line,
|
Line,
|
||||||
Menu,
|
Menu,
|
||||||
PopOut,
|
PopOut,
|
||||||
|
RectCords,
|
||||||
Scroll,
|
Scroll,
|
||||||
Text,
|
Text,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
toRem,
|
toRem,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import React, { ReactNode, useState } from 'react';
|
import React, { MouseEventHandler, ReactNode, useState } from 'react';
|
||||||
import { ReactEditor, useSlate } from 'slate-react';
|
import { ReactEditor, useSlate } from 'slate-react';
|
||||||
import {
|
import {
|
||||||
headingLevel,
|
headingLevel,
|
||||||
|
@ -119,26 +120,33 @@ export function BlockButton({ format, icon, tooltip }: BlockButtonProps) {
|
||||||
export function HeadingBlockButton() {
|
export function HeadingBlockButton() {
|
||||||
const editor = useSlate();
|
const editor = useSlate();
|
||||||
const level = headingLevel(editor);
|
const level = headingLevel(editor);
|
||||||
const [open, setOpen] = useState(false);
|
const [anchor, setAnchor] = useState<RectCords>();
|
||||||
const isActive = isBlockActive(editor, BlockType.Heading);
|
const isActive = isBlockActive(editor, BlockType.Heading);
|
||||||
const modKey = isMacOS() ? KeySymbol.Command : 'Ctrl';
|
const modKey = isMacOS() ? KeySymbol.Command : 'Ctrl';
|
||||||
|
|
||||||
const handleMenuSelect = (selectedLevel: HeadingLevel) => {
|
const handleMenuSelect = (selectedLevel: HeadingLevel) => {
|
||||||
setOpen(false);
|
setAnchor(undefined);
|
||||||
toggleBlock(editor, BlockType.Heading, { level: selectedLevel });
|
toggleBlock(editor, BlockType.Heading, { level: selectedLevel });
|
||||||
ReactEditor.focus(editor);
|
ReactEditor.focus(editor);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMenuOpen: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
|
if (isActive) {
|
||||||
|
toggleBlock(editor, BlockType.Heading);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setAnchor(evt.currentTarget.getBoundingClientRect());
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<PopOut
|
<PopOut
|
||||||
open={open}
|
anchor={anchor}
|
||||||
offset={5}
|
offset={5}
|
||||||
position="Top"
|
position="Top"
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setOpen(false),
|
onDeactivate: () => setAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) =>
|
isKeyForward: (evt: KeyboardEvent) =>
|
||||||
evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
|
evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
|
||||||
|
@ -197,12 +205,10 @@ export function HeadingBlockButton() {
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(ref) => (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
style={{ width: 'unset' }}
|
style={{ width: 'unset' }}
|
||||||
ref={ref}
|
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
onClick={() => (isActive ? toggleBlock(editor, BlockType.Heading) : setOpen(!open))}
|
onClick={handleMenuOpen}
|
||||||
aria-pressed={isActive}
|
aria-pressed={isActive}
|
||||||
size="400"
|
size="400"
|
||||||
radii="300"
|
radii="300"
|
||||||
|
@ -210,7 +216,6 @@ export function HeadingBlockButton() {
|
||||||
<Icon size="200" src={level ? Icons[`Heading${level}`] : Icons.Heading1} />
|
<Icon size="200" src={level ? Icons[`Heading${level}`] : Icons.Heading1} />
|
||||||
<Icon size="200" src={isActive ? Icons.Cross : Icons.ChevronBottom} />
|
<Icon size="200" src={isActive ? Icons.Cross : Icons.ChevronBottom} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
Button,
|
Button,
|
||||||
Input,
|
Input,
|
||||||
Badge,
|
Badge,
|
||||||
|
RectCords,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import { SearchOrderBy } from 'matrix-js-sdk';
|
import { SearchOrderBy } from 'matrix-js-sdk';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
@ -43,24 +44,27 @@ type OrderButtonProps = {
|
||||||
onChange: (order?: string) => void;
|
onChange: (order?: string) => void;
|
||||||
};
|
};
|
||||||
function OrderButton({ order, onChange }: OrderButtonProps) {
|
function OrderButton({ order, onChange }: OrderButtonProps) {
|
||||||
const [menu, setMenu] = useState(false);
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
const rankOrder = order === SearchOrderBy.Rank;
|
const rankOrder = order === SearchOrderBy.Rank;
|
||||||
|
|
||||||
const setOrder = (o?: string) => {
|
const setOrder = (o?: string) => {
|
||||||
setMenu(false);
|
setMenuAnchor(undefined);
|
||||||
onChange(o);
|
onChange(o);
|
||||||
};
|
};
|
||||||
|
const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
|
setMenuAnchor(evt.currentTarget.getBoundingClientRect());
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopOut
|
<PopOut
|
||||||
open={menu}
|
anchor={menuAnchor}
|
||||||
align="End"
|
align="End"
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setMenu(false),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -93,17 +97,14 @@ function OrderButton({ order, onChange }: OrderButtonProps) {
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<Chip
|
<Chip
|
||||||
ref={anchorRef}
|
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
radii="Pill"
|
radii="Pill"
|
||||||
after={<Icon size="50" src={Icons.Sort} />}
|
after={<Icon size="50" src={Icons.Sort} />}
|
||||||
onClick={() => setMenu(true)}
|
onClick={handleOpenMenu}
|
||||||
>
|
>
|
||||||
{rankOrder ? <Text size="T200">Relevance</Text> : <Text size="T200">Recent</Text>}
|
{rankOrder ? <Text size="T200">Relevance</Text> : <Text size="T200">Recent</Text>}
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -126,7 +127,7 @@ type SelectRoomButtonProps = {
|
||||||
function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButtonProps) {
|
function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButtonProps) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const [menu, setMenu] = useState(false);
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
const [localSelected, setLocalSelected] = useState(selectedRooms);
|
const [localSelected, setLocalSelected] = useState(selectedRooms);
|
||||||
|
|
||||||
const getRoomNameStr: SearchItemStrGetter<string> = useCallback(
|
const getRoomNameStr: SearchItemStrGetter<string> = useCallback(
|
||||||
|
@ -172,30 +173,34 @@ function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButto
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
setMenu(false);
|
setMenuAnchor(undefined);
|
||||||
onChange(localSelected);
|
onChange(localSelected);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeselectAll = () => {
|
const handleDeselectAll = () => {
|
||||||
setMenu(false);
|
setMenuAnchor(undefined);
|
||||||
onChange(undefined);
|
onChange(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocalSelected(selectedRooms);
|
setLocalSelected(selectedRooms);
|
||||||
resetSearch();
|
resetSearch();
|
||||||
}, [menu, selectedRooms, resetSearch]);
|
}, [menuAnchor, selectedRooms, resetSearch]);
|
||||||
|
|
||||||
|
const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
|
setMenuAnchor(evt.currentTarget.getBoundingClientRect());
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopOut
|
<PopOut
|
||||||
open={menu}
|
anchor={menuAnchor}
|
||||||
align="Center"
|
align="Center"
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setMenu(false),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -307,17 +312,14 @@ function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButto
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<Chip
|
<Chip
|
||||||
onClick={() => setMenu(true)}
|
onClick={handleOpenMenu}
|
||||||
ref={anchorRef}
|
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
radii="Pill"
|
radii="Pill"
|
||||||
before={<Icon size="100" src={Icons.PlusCircle} />}
|
before={<Icon size="100" src={Icons.PlusCircle} />}
|
||||||
>
|
>
|
||||||
<Text size="T200">Select Rooms</Text>
|
<Text size="T200">Select Rooms</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
PopOut,
|
PopOut,
|
||||||
toRem,
|
toRem,
|
||||||
Line,
|
Line,
|
||||||
|
RectCords,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import { useFocusWithin, useHover } from 'react-aria';
|
import { useFocusWithin, useHover } from 'react-aria';
|
||||||
import FocusTrap from 'focus-trap-react';
|
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 [hover, setHover] = useState(false);
|
||||||
const { hoverProps } = useHover({ onHoverChange: setHover });
|
const { hoverProps } = useHover({ onHoverChange: setHover });
|
||||||
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
|
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
|
||||||
const [menu, setMenu] = useState(false);
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
||||||
|
|
||||||
const handleContextMenu: MouseEventHandler<HTMLElement> = (evt) => {
|
const handleContextMenu: MouseEventHandler<HTMLElement> = (evt) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
setMenu(true);
|
setMenuAnchor({
|
||||||
|
x: evt.clientX,
|
||||||
|
y: evt.clientY,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const optionsVisible = hover || menu;
|
const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
|
setMenuAnchor(evt.currentTarget.getBoundingClientRect());
|
||||||
|
};
|
||||||
|
|
||||||
|
const optionsVisible = hover || !!menuAnchor;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavItem
|
<NavItem
|
||||||
|
@ -166,7 +176,7 @@ export function RoomNavItem({ room, selected, showAvatar, muted, linkPath }: Roo
|
||||||
radii="400"
|
radii="400"
|
||||||
highlight={unread !== undefined || selected}
|
highlight={unread !== undefined || selected}
|
||||||
aria-selected={selected}
|
aria-selected={selected}
|
||||||
data-hover={menu}
|
data-hover={!!menuAnchor}
|
||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
{...hoverProps}
|
{...hoverProps}
|
||||||
{...focusWithinProps}
|
{...focusWithinProps}
|
||||||
|
@ -208,16 +218,17 @@ export function RoomNavItem({ room, selected, showAvatar, muted, linkPath }: Roo
|
||||||
{optionsVisible && (
|
{optionsVisible && (
|
||||||
<NavItemOptions>
|
<NavItemOptions>
|
||||||
<PopOut
|
<PopOut
|
||||||
open={menu}
|
anchor={menuAnchor}
|
||||||
alignOffset={-5}
|
offset={menuAnchor?.width === 0 ? 0 : undefined}
|
||||||
|
alignOffset={menuAnchor?.width === 0 ? 0 : -5}
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
align="End"
|
align={menuAnchor?.width === 0 ? 'Start' : 'End'}
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
returnFocusOnDeactivate: false,
|
returnFocusOnDeactivate: false,
|
||||||
onDeactivate: () => setMenu(false),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
@ -226,16 +237,14 @@ export function RoomNavItem({ room, selected, showAvatar, muted, linkPath }: Roo
|
||||||
<RoomNavItemMenu
|
<RoomNavItemMenu
|
||||||
room={room}
|
room={room}
|
||||||
linkPath={linkPath}
|
linkPath={linkPath}
|
||||||
requestClose={() => setMenu(false)}
|
requestClose={() => setMenuAnchor(undefined)}
|
||||||
/>
|
/>
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
ref={anchorRef}
|
onClick={handleOpenMenu}
|
||||||
onClick={() => setMenu(true)}
|
aria-pressed={!!menuAnchor}
|
||||||
aria-pressed={menu}
|
|
||||||
variant="Background"
|
variant="Background"
|
||||||
fill="None"
|
fill="None"
|
||||||
size="300"
|
size="300"
|
||||||
|
@ -243,7 +252,6 @@ export function RoomNavItem({ room, selected, showAvatar, muted, linkPath }: Roo
|
||||||
>
|
>
|
||||||
<Icon size="50" src={Icons.VerticalDots} />
|
<Icon size="50" src={Icons.VerticalDots} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
</NavItemOptions>
|
</NavItemOptions>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import {
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
PopOut,
|
PopOut,
|
||||||
|
RectCords,
|
||||||
Scroll,
|
Scroll,
|
||||||
Spinner,
|
Spinner,
|
||||||
Text,
|
Text,
|
||||||
|
@ -289,10 +290,10 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
<Box className={css.MemberDrawerContent} direction="Column" gap="200">
|
<Box className={css.MemberDrawerContent} direction="Column" gap="200">
|
||||||
<Box ref={scrollTopAnchorRef} className={css.DrawerGroup} direction="Column" gap="200">
|
<Box ref={scrollTopAnchorRef} className={css.DrawerGroup} direction="Column" gap="200">
|
||||||
<Box alignItems="Center" justifyContent="SpaceBetween" gap="200">
|
<Box alignItems="Center" justifyContent="SpaceBetween" gap="200">
|
||||||
<UseStateProvider initial={false}>
|
<UseStateProvider initial={undefined}>
|
||||||
{(open, setOpen) => (
|
{(anchor: RectCords | undefined, setAnchor) => (
|
||||||
<PopOut
|
<PopOut
|
||||||
open={open}
|
anchor={anchor}
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
align="Start"
|
align="Start"
|
||||||
offset={4}
|
offset={4}
|
||||||
|
@ -300,7 +301,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setOpen(false),
|
onDeactivate: () => setAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
@ -319,7 +320,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
radii="300"
|
radii="300"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMembershipFilterIndex(index);
|
setMembershipFilterIndex(index);
|
||||||
setOpen(false);
|
setAnchor(undefined);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text>{menuItem.name}</Text>
|
<Text>{menuItem.name}</Text>
|
||||||
|
@ -329,10 +330,13 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<Chip
|
<Chip
|
||||||
ref={anchorRef}
|
onClick={
|
||||||
onClick={() => setOpen(!open)}
|
((evt) =>
|
||||||
|
setAnchor(
|
||||||
|
evt.currentTarget.getBoundingClientRect()
|
||||||
|
)) as MouseEventHandler<HTMLButtonElement>
|
||||||
|
}
|
||||||
variant={membershipFilter.color}
|
variant={membershipFilter.color}
|
||||||
size="400"
|
size="400"
|
||||||
radii="300"
|
radii="300"
|
||||||
|
@ -340,14 +344,13 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
>
|
>
|
||||||
<Text size="T200">{membershipFilter.name}</Text>
|
<Text size="T200">{membershipFilter.name}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
)}
|
)}
|
||||||
</UseStateProvider>
|
</UseStateProvider>
|
||||||
<UseStateProvider initial={false}>
|
<UseStateProvider initial={undefined}>
|
||||||
{(open, setOpen) => (
|
{(anchor: RectCords | undefined, setAnchor) => (
|
||||||
<PopOut
|
<PopOut
|
||||||
open={open}
|
anchor={anchor}
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
align="End"
|
align="End"
|
||||||
offset={4}
|
offset={4}
|
||||||
|
@ -355,7 +358,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setOpen(false),
|
onDeactivate: () => setAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
@ -370,7 +373,7 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
radii="300"
|
radii="300"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSortFilterIndex(index);
|
setSortFilterIndex(index);
|
||||||
setOpen(false);
|
setAnchor(undefined);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text>{menuItem.name}</Text>
|
<Text>{menuItem.name}</Text>
|
||||||
|
@ -380,10 +383,13 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<Chip
|
<Chip
|
||||||
ref={anchorRef}
|
onClick={
|
||||||
onClick={() => setOpen(!open)}
|
((evt) =>
|
||||||
|
setAnchor(
|
||||||
|
evt.currentTarget.getBoundingClientRect()
|
||||||
|
)) as MouseEventHandler<HTMLButtonElement>
|
||||||
|
}
|
||||||
variant="Background"
|
variant="Background"
|
||||||
size="400"
|
size="400"
|
||||||
radii="300"
|
radii="300"
|
||||||
|
@ -391,7 +397,6 @@ export function MembersDrawer({ room }: MembersDrawerProps) {
|
||||||
>
|
>
|
||||||
<Text size="T200">{sortFilter.name}</Text>
|
<Text size="T200">{sortFilter.name}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
)}
|
)}
|
||||||
</UseStateProvider>
|
</UseStateProvider>
|
||||||
|
|
|
@ -119,6 +119,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||||
const [enterForNewline] = useSetting(settingsAtom, 'enterForNewline');
|
const [enterForNewline] = useSetting(settingsAtom, 'enterForNewline');
|
||||||
const [isMarkdown] = useSetting(settingsAtom, 'isMarkdown');
|
const [isMarkdown] = useSetting(settingsAtom, 'isMarkdown');
|
||||||
const commands = useCommands(mx, room);
|
const commands = useCommands(mx, room);
|
||||||
|
const emojiBtnRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
const [msgDraft, setMsgDraft] = useAtom(roomIdToMsgDraftAtomFamily(roomId));
|
const [msgDraft, setMsgDraft] = useAtom(roomIdToMsgDraftAtomFamily(roomId));
|
||||||
const [replyDraft, setReplyDraft] = useAtom(roomIdToReplyDraftAtomFamily(roomId));
|
const [replyDraft, setReplyDraft] = useAtom(roomIdToReplyDraftAtomFamily(roomId));
|
||||||
|
@ -521,7 +522,11 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||||
alignOffset={-44}
|
alignOffset={-44}
|
||||||
position="Top"
|
position="Top"
|
||||||
align="End"
|
align="End"
|
||||||
open={!!emojiBoardTab}
|
anchor={
|
||||||
|
emojiBoardTab === undefined
|
||||||
|
? undefined
|
||||||
|
: emojiBtnRef.current?.getBoundingClientRect() ?? undefined
|
||||||
|
}
|
||||||
content={
|
content={
|
||||||
<EmojiBoard
|
<EmojiBoard
|
||||||
tab={emojiBoardTab}
|
tab={emojiBoardTab}
|
||||||
|
@ -538,8 +543,6 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<>
|
|
||||||
{!hideStickerBtn && (
|
{!hideStickerBtn && (
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-pressed={emojiBoardTab === EmojiBoardTab.Sticker}
|
aria-pressed={emojiBoardTab === EmojiBoardTab.Sticker}
|
||||||
|
@ -555,7 +558,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
<IconButton
|
<IconButton
|
||||||
ref={anchorRef}
|
ref={emojiBtnRef}
|
||||||
aria-pressed={
|
aria-pressed={
|
||||||
hideStickerBtn ? !!emojiBoardTab : emojiBoardTab === EmojiBoardTab.Emoji
|
hideStickerBtn ? !!emojiBoardTab : emojiBoardTab === EmojiBoardTab.Emoji
|
||||||
}
|
}
|
||||||
|
@ -567,14 +570,10 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||||
<Icon
|
<Icon
|
||||||
src={Icons.Smile}
|
src={Icons.Smile}
|
||||||
filled={
|
filled={
|
||||||
hideStickerBtn
|
hideStickerBtn ? !!emojiBoardTab : emojiBoardTab === EmojiBoardTab.Emoji
|
||||||
? !!emojiBoardTab
|
|
||||||
: emojiBoardTab === EmojiBoardTab.Emoji
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
)}
|
)}
|
||||||
</UseStateProvider>
|
</UseStateProvider>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
OverlayBackdrop,
|
OverlayBackdrop,
|
||||||
OverlayCenter,
|
OverlayCenter,
|
||||||
PopOut,
|
PopOut,
|
||||||
|
RectCords,
|
||||||
Spinner,
|
Spinner,
|
||||||
Text,
|
Text,
|
||||||
as,
|
as,
|
||||||
|
@ -610,8 +611,8 @@ export const Message = as<'div', MessageProps>(
|
||||||
const [hover, setHover] = useState(false);
|
const [hover, setHover] = useState(false);
|
||||||
const { hoverProps } = useHover({ onHoverChange: setHover });
|
const { hoverProps } = useHover({ onHoverChange: setHover });
|
||||||
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
|
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
|
||||||
const [menu, setMenu] = useState(false);
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
const [emojiBoard, setEmojiBoard] = useState(false);
|
const [emojiBoardAnchor, setEmojiBoardAnchor] = useState<RectCords>();
|
||||||
|
|
||||||
const senderDisplayName =
|
const senderDisplayName =
|
||||||
getMemberDisplayName(room, senderId) ?? getMxIdLocalPart(senderId) ?? senderId;
|
getMemberDisplayName(room, senderId) ?? getMxIdLocalPart(senderId) ?? senderId;
|
||||||
|
@ -706,11 +707,36 @@ export const Message = as<'div', MessageProps>(
|
||||||
const tag = (evt.target as any).tagName;
|
const tag = (evt.target as any).tagName;
|
||||||
if (typeof tag === 'string' && tag.toLowerCase() === 'a') return;
|
if (typeof tag === 'string' && tag.toLowerCase() === 'a') return;
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
setMenu(true);
|
setMenuAnchor({
|
||||||
|
x: evt.clientX,
|
||||||
|
y: evt.clientY,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
|
const target = evt.currentTarget.parentElement?.parentElement ?? evt.currentTarget;
|
||||||
|
setMenuAnchor(target.getBoundingClientRect());
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeMenu = () => {
|
const closeMenu = () => {
|
||||||
setMenu(false);
|
setMenuAnchor(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenEmojiBoard: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
|
const target = evt.currentTarget.parentElement?.parentElement ?? evt.currentTarget;
|
||||||
|
setEmojiBoardAnchor(target.getBoundingClientRect());
|
||||||
|
};
|
||||||
|
const handleAddReactions: MouseEventHandler<HTMLButtonElement> = () => {
|
||||||
|
const rect = menuAnchor;
|
||||||
|
closeMenu();
|
||||||
|
// open it with timeout because closeMenu
|
||||||
|
// FocusTrap will return focus from emojiBoard
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setEmojiBoardAnchor(rect);
|
||||||
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -720,22 +746,22 @@ export const Message = as<'div', MessageProps>(
|
||||||
space={messageSpacing}
|
space={messageSpacing}
|
||||||
collapse={collapse}
|
collapse={collapse}
|
||||||
highlight={highlight}
|
highlight={highlight}
|
||||||
selected={menu || emojiBoard}
|
selected={!!menuAnchor || !!emojiBoardAnchor}
|
||||||
{...props}
|
{...props}
|
||||||
{...hoverProps}
|
{...hoverProps}
|
||||||
{...focusWithinProps}
|
{...focusWithinProps}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
{!edit && (hover || menu || emojiBoard) && (
|
{!edit && (hover || !!menuAnchor || !!emojiBoardAnchor) && (
|
||||||
<div className={css.MessageOptionsBase}>
|
<div className={css.MessageOptionsBase}>
|
||||||
<Menu className={css.MessageOptionsBar} variant="SurfaceVariant">
|
<Menu className={css.MessageOptionsBar} variant="SurfaceVariant">
|
||||||
<Box gap="100">
|
<Box gap="100">
|
||||||
{canSendReaction && (
|
{canSendReaction && (
|
||||||
<PopOut
|
<PopOut
|
||||||
alignOffset={-65}
|
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
align="End"
|
align={emojiBoardAnchor?.width === 0 ? 'Start' : 'End'}
|
||||||
open={emojiBoard}
|
offset={emojiBoardAnchor?.width === 0 ? 0 : undefined}
|
||||||
|
anchor={emojiBoardAnchor}
|
||||||
content={
|
content={
|
||||||
<EmojiBoard
|
<EmojiBoard
|
||||||
imagePackRooms={imagePackRooms ?? []}
|
imagePackRooms={imagePackRooms ?? []}
|
||||||
|
@ -743,30 +769,27 @@ export const Message = as<'div', MessageProps>(
|
||||||
allowTextCustomEmoji
|
allowTextCustomEmoji
|
||||||
onEmojiSelect={(key) => {
|
onEmojiSelect={(key) => {
|
||||||
onReactionToggle(mEvent.getId()!, key);
|
onReactionToggle(mEvent.getId()!, key);
|
||||||
setEmojiBoard(false);
|
setEmojiBoardAnchor(undefined);
|
||||||
}}
|
}}
|
||||||
onCustomEmojiSelect={(mxc, shortcode) => {
|
onCustomEmojiSelect={(mxc, shortcode) => {
|
||||||
onReactionToggle(mEvent.getId()!, mxc, shortcode);
|
onReactionToggle(mEvent.getId()!, mxc, shortcode);
|
||||||
setEmojiBoard(false);
|
setEmojiBoardAnchor(undefined);
|
||||||
}}
|
}}
|
||||||
requestClose={() => {
|
requestClose={() => {
|
||||||
setEmojiBoard(false);
|
setEmojiBoardAnchor(undefined);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
ref={anchorRef}
|
onClick={handleOpenEmojiBoard}
|
||||||
onClick={() => setEmojiBoard(true)}
|
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
size="300"
|
size="300"
|
||||||
radii="300"
|
radii="300"
|
||||||
aria-pressed={emojiBoard}
|
aria-pressed={!!emojiBoardAnchor}
|
||||||
>
|
>
|
||||||
<Icon src={Icons.SmilePlus} size="100" />
|
<Icon src={Icons.SmilePlus} size="100" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
)}
|
)}
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -789,15 +812,15 @@ export const Message = as<'div', MessageProps>(
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
<PopOut
|
<PopOut
|
||||||
open={menu}
|
anchor={menuAnchor}
|
||||||
alignOffset={-5}
|
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
align="End"
|
align={menuAnchor?.width === 0 ? 'Start' : 'End'}
|
||||||
|
offset={menuAnchor?.width === 0 ? 0 : undefined}
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setMenu(false),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
@ -818,12 +841,7 @@ export const Message = as<'div', MessageProps>(
|
||||||
size="300"
|
size="300"
|
||||||
after={<Icon size="100" src={Icons.SmilePlus} />}
|
after={<Icon size="100" src={Icons.SmilePlus} />}
|
||||||
radii="300"
|
radii="300"
|
||||||
onClick={() => {
|
onClick={handleAddReactions}
|
||||||
closeMenu();
|
|
||||||
// open it with timeout because closeMenu
|
|
||||||
// FocusTrap will return focus from emojiBoard
|
|
||||||
setTimeout(() => setEmojiBoard(true), 100);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
className={css.MessageMenuItemText}
|
className={css.MessageMenuItemText}
|
||||||
|
@ -915,18 +933,15 @@ export const Message = as<'div', MessageProps>(
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(targetRef) => (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
ref={targetRef}
|
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
size="300"
|
size="300"
|
||||||
radii="300"
|
radii="300"
|
||||||
onClick={() => setMenu((v) => !v)}
|
onClick={handleOpenMenu}
|
||||||
aria-pressed={menu}
|
aria-pressed={!!menuAnchor}
|
||||||
>
|
>
|
||||||
<Icon src={Icons.VerticalDots} size="100" />
|
<Icon src={Icons.VerticalDots} size="100" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
</Box>
|
</Box>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
@ -967,7 +982,7 @@ export const Event = as<'div', EventProps>(
|
||||||
const [hover, setHover] = useState(false);
|
const [hover, setHover] = useState(false);
|
||||||
const { hoverProps } = useHover({ onHoverChange: setHover });
|
const { hoverProps } = useHover({ onHoverChange: setHover });
|
||||||
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
|
const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
|
||||||
const [menu, setMenu] = useState(false);
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
const stateEvent = typeof mEvent.getStateKey() === 'string';
|
const stateEvent = typeof mEvent.getStateKey() === 'string';
|
||||||
|
|
||||||
const handleContextMenu: MouseEventHandler<HTMLDivElement> = (evt) => {
|
const handleContextMenu: MouseEventHandler<HTMLDivElement> = (evt) => {
|
||||||
|
@ -975,11 +990,21 @@ export const Event = as<'div', EventProps>(
|
||||||
const tag = (evt.target as any).tagName;
|
const tag = (evt.target as any).tagName;
|
||||||
if (typeof tag === 'string' && tag.toLowerCase() === 'a') return;
|
if (typeof tag === 'string' && tag.toLowerCase() === 'a') return;
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
setMenu(true);
|
setMenuAnchor({
|
||||||
|
x: evt.clientX,
|
||||||
|
y: evt.clientY,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||||
|
const target = evt.currentTarget.parentElement?.parentElement ?? evt.currentTarget;
|
||||||
|
setMenuAnchor(target.getBoundingClientRect());
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeMenu = () => {
|
const closeMenu = () => {
|
||||||
setMenu(false);
|
setMenuAnchor(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -989,26 +1014,26 @@ export const Event = as<'div', EventProps>(
|
||||||
space={messageSpacing}
|
space={messageSpacing}
|
||||||
autoCollapse
|
autoCollapse
|
||||||
highlight={highlight}
|
highlight={highlight}
|
||||||
selected={menu}
|
selected={!!menuAnchor}
|
||||||
{...props}
|
{...props}
|
||||||
{...hoverProps}
|
{...hoverProps}
|
||||||
{...focusWithinProps}
|
{...focusWithinProps}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
{(hover || menu) && (
|
{(hover || !!menuAnchor) && (
|
||||||
<div className={css.MessageOptionsBase}>
|
<div className={css.MessageOptionsBase}>
|
||||||
<Menu className={css.MessageOptionsBar} variant="SurfaceVariant">
|
<Menu className={css.MessageOptionsBar} variant="SurfaceVariant">
|
||||||
<Box gap="100">
|
<Box gap="100">
|
||||||
<PopOut
|
<PopOut
|
||||||
open={menu}
|
anchor={menuAnchor}
|
||||||
alignOffset={-5}
|
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
align="End"
|
align={menuAnchor?.width === 0 ? 'Start' : 'End'}
|
||||||
|
offset={menuAnchor?.width === 0 ? 0 : undefined}
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setMenu(false),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
@ -1049,18 +1074,15 @@ export const Event = as<'div', EventProps>(
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(targetRef) => (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
ref={targetRef}
|
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
size="300"
|
size="300"
|
||||||
radii="300"
|
radii="300"
|
||||||
onClick={() => setMenu((v) => !v)}
|
onClick={handleOpenMenu}
|
||||||
aria-pressed={menu}
|
aria-pressed={!!menuAnchor}
|
||||||
>
|
>
|
||||||
<Icon src={Icons.VerticalDots} size="100" />
|
<Icon src={Icons.VerticalDots} size="100" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
</Box>
|
</Box>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
|
@ -1,5 +1,24 @@
|
||||||
import React, { KeyboardEventHandler, useCallback, useEffect, useState } from 'react';
|
import React, {
|
||||||
import { Box, Chip, Icon, IconButton, Icons, Line, PopOut, Spinner, Text, as, config } from 'folds';
|
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 { Editor, Transforms } from 'slate';
|
||||||
import { ReactEditor } from 'slate-react';
|
import { ReactEditor } from 'slate-react';
|
||||||
import { IContent, MatrixEvent, RelationType, Room } from 'matrix-js-sdk';
|
import { IContent, MatrixEvent, RelationType, Room } from 'matrix-js-sdk';
|
||||||
|
@ -258,13 +277,13 @@ export const MessageEditor = as<'div', MessageEditorProps>(
|
||||||
>
|
>
|
||||||
<Icon size="400" src={toolbar ? Icons.AlphabetUnderline : Icons.Alphabet} />
|
<Icon size="400" src={toolbar ? Icons.AlphabetUnderline : Icons.Alphabet} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<UseStateProvider initial={false}>
|
<UseStateProvider initial={undefined}>
|
||||||
{(emojiBoard: boolean, setEmojiBoard) => (
|
{(anchor: RectCords | undefined, setAnchor) => (
|
||||||
<PopOut
|
<PopOut
|
||||||
|
anchor={anchor}
|
||||||
alignOffset={-8}
|
alignOffset={-8}
|
||||||
position="Top"
|
position="Top"
|
||||||
align="End"
|
align="End"
|
||||||
open={!!emojiBoard}
|
|
||||||
content={
|
content={
|
||||||
<EmojiBoard
|
<EmojiBoard
|
||||||
imagePackRooms={imagePackRooms ?? []}
|
imagePackRooms={imagePackRooms ?? []}
|
||||||
|
@ -272,24 +291,26 @@ export const MessageEditor = as<'div', MessageEditorProps>(
|
||||||
onEmojiSelect={handleEmoticonSelect}
|
onEmojiSelect={handleEmoticonSelect}
|
||||||
onCustomEmojiSelect={handleEmoticonSelect}
|
onCustomEmojiSelect={handleEmoticonSelect}
|
||||||
requestClose={() => {
|
requestClose={() => {
|
||||||
setEmojiBoard(false);
|
setAnchor(undefined);
|
||||||
if (!mobileOrTablet()) ReactEditor.focus(editor);
|
if (!mobileOrTablet()) ReactEditor.focus(editor);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
ref={anchorRef}
|
aria-pressed={anchor !== undefined}
|
||||||
aria-pressed={emojiBoard}
|
onClick={
|
||||||
onClick={() => setEmojiBoard(true)}
|
((evt) =>
|
||||||
|
setAnchor(
|
||||||
|
evt.currentTarget.getBoundingClientRect()
|
||||||
|
)) as MouseEventHandler<HTMLButtonElement>
|
||||||
|
}
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
size="300"
|
size="300"
|
||||||
radii="300"
|
radii="300"
|
||||||
>
|
>
|
||||||
<Icon size="400" src={Icons.Smile} filled={emojiBoard} />
|
<Icon size="400" src={Icons.Smile} filled={anchor !== undefined} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
)}
|
)}
|
||||||
</UseStateProvider>
|
</UseStateProvider>
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
PopOut,
|
PopOut,
|
||||||
|
RectCords,
|
||||||
Text,
|
Text,
|
||||||
config,
|
config,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
|
@ -33,7 +34,7 @@ export function ServerPicker({
|
||||||
allowCustomServer?: boolean;
|
allowCustomServer?: boolean;
|
||||||
onServerChange: (server: string) => void;
|
onServerChange: (server: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const [serverMenu, setServerMenu] = useState(false);
|
const [serverMenuAnchor, setServerMenuAnchor] = useState<RectCords>();
|
||||||
const serverInputRef = useRef<HTMLInputElement>(null);
|
const serverInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -53,7 +54,7 @@ export function ServerPicker({
|
||||||
const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (evt) => {
|
const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (evt) => {
|
||||||
if (evt.key === 'ArrowDown') {
|
if (evt.key === 'ArrowDown') {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
setServerMenu(true);
|
setServerMenuAnchor(undefined);
|
||||||
}
|
}
|
||||||
if (evt.key === 'Enter') {
|
if (evt.key === 'Enter') {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
@ -67,7 +68,12 @@ export function ServerPicker({
|
||||||
if (selectedServer) {
|
if (selectedServer) {
|
||||||
onServerChange(selectedServer);
|
onServerChange(selectedServer);
|
||||||
}
|
}
|
||||||
setServerMenu(false);
|
setServerMenuAnchor(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenServerMenu: MouseEventHandler<HTMLElement> = (evt) => {
|
||||||
|
const target = evt.currentTarget.parentElement ?? evt.currentTarget;
|
||||||
|
setServerMenuAnchor(target.getBoundingClientRect());
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -81,11 +87,11 @@ export function ServerPicker({
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
size="500"
|
size="500"
|
||||||
readOnly={!allowCustomServer}
|
readOnly={!allowCustomServer}
|
||||||
onClick={allowCustomServer ? undefined : () => setServerMenu(true)}
|
onClick={allowCustomServer ? undefined : handleOpenServerMenu}
|
||||||
after={
|
after={
|
||||||
serverList.length === 0 || (serverList.length === 1 && !allowCustomServer) ? undefined : (
|
serverList.length === 0 || (serverList.length === 1 && !allowCustomServer) ? undefined : (
|
||||||
<PopOut
|
<PopOut
|
||||||
open={serverMenu}
|
anchor={serverMenuAnchor}
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
align="End"
|
align="End"
|
||||||
offset={4}
|
offset={4}
|
||||||
|
@ -93,7 +99,7 @@ export function ServerPicker({
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setServerMenu(false),
|
onDeactivate: () => setServerMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
|
||||||
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
|
||||||
|
@ -120,18 +126,15 @@ export function ServerPicker({
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
ref={anchorRef}
|
onClick={handleOpenServerMenu}
|
||||||
onClick={() => setServerMenu(true)}
|
|
||||||
variant={allowCustomServer ? 'Background' : 'Surface'}
|
variant={allowCustomServer ? 'Background' : 'Surface'}
|
||||||
size="300"
|
size="300"
|
||||||
aria-pressed={serverMenu}
|
aria-pressed={!!serverMenuAnchor}
|
||||||
radii="300"
|
radii="300"
|
||||||
>
|
>
|
||||||
<Icon src={Icons.ChevronBottom} />
|
<Icon src={Icons.ChevronBottom} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { FormEventHandler, useCallback, useState } from 'react';
|
import React, { FormEventHandler, MouseEventHandler, useCallback, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
@ -12,6 +12,7 @@ import {
|
||||||
OverlayBackdrop,
|
OverlayBackdrop,
|
||||||
OverlayCenter,
|
OverlayCenter,
|
||||||
PopOut,
|
PopOut,
|
||||||
|
RectCords,
|
||||||
Spinner,
|
Spinner,
|
||||||
Text,
|
Text,
|
||||||
config,
|
config,
|
||||||
|
@ -37,17 +38,21 @@ import { FieldError } from '../FiledError';
|
||||||
import { getResetPasswordPath } from '../../pathUtils';
|
import { getResetPasswordPath } from '../../pathUtils';
|
||||||
|
|
||||||
function UsernameHint({ server }: { server: string }) {
|
function UsernameHint({ server }: { server: string }) {
|
||||||
const [open, setOpen] = useState(false);
|
const [anchor, setAnchor] = useState<RectCords>();
|
||||||
|
|
||||||
|
const handleOpenMenu: MouseEventHandler<HTMLElement> = (evt) => {
|
||||||
|
setAnchor(evt.currentTarget.getBoundingClientRect());
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<PopOut
|
<PopOut
|
||||||
open={open}
|
anchor={anchor}
|
||||||
position="Top"
|
position="Top"
|
||||||
align="End"
|
align="End"
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setOpen(false),
|
onDeactivate: () => setAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -84,20 +89,17 @@ function UsernameHint({ server }: { server: string }) {
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(targetRef) => (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
onClick={() => setOpen(true)}
|
onClick={handleOpenMenu}
|
||||||
ref={targetRef}
|
|
||||||
type="button"
|
type="button"
|
||||||
variant="Background"
|
variant="Background"
|
||||||
size="300"
|
size="300"
|
||||||
radii="300"
|
radii="300"
|
||||||
aria-pressed={open}
|
aria-pressed={!!anchor}
|
||||||
>
|
>
|
||||||
<Icon style={{ opacity: config.opacity.P300 }} size="100" src={Icons.Info} />
|
<Icon style={{ opacity: config.opacity.P300 }} size="100" src={Icons.Info} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
PopOut,
|
PopOut,
|
||||||
|
RectCords,
|
||||||
Scroll,
|
Scroll,
|
||||||
Spinner,
|
Spinner,
|
||||||
Text,
|
Text,
|
||||||
|
@ -149,7 +150,7 @@ function ThirdPartyProtocolsSelector({
|
||||||
onChange: (instanceId?: string) => void;
|
onChange: (instanceId?: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const [menu, setMenu] = useState(false);
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
|
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ['thirdparty', 'protocols'],
|
queryKey: ['thirdparty', 'protocols'],
|
||||||
|
@ -159,7 +160,11 @@ function ThirdPartyProtocolsSelector({
|
||||||
const handleInstanceSelect: MouseEventHandler<HTMLButtonElement> = (evt): void => {
|
const handleInstanceSelect: MouseEventHandler<HTMLButtonElement> = (evt): void => {
|
||||||
const insId = evt.currentTarget.getAttribute('data-instance-id') ?? undefined;
|
const insId = evt.currentTarget.getAttribute('data-instance-id') ?? undefined;
|
||||||
onChange(insId);
|
onChange(insId);
|
||||||
setMenu(false);
|
setMenuAnchor(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenMenu: MouseEventHandler<HTMLElement> = (evt) => {
|
||||||
|
setMenuAnchor(evt.currentTarget.getBoundingClientRect());
|
||||||
};
|
};
|
||||||
|
|
||||||
const instances = data && Object.keys(data).flatMap((protocol) => data[protocol].instances);
|
const instances = data && Object.keys(data).flatMap((protocol) => data[protocol].instances);
|
||||||
|
@ -168,14 +173,14 @@ function ThirdPartyProtocolsSelector({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopOut
|
<PopOut
|
||||||
open={menu}
|
anchor={menuAnchor}
|
||||||
align="End"
|
align="End"
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setMenu(false),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -221,11 +226,9 @@ function ThirdPartyProtocolsSelector({
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<Chip
|
<Chip
|
||||||
ref={anchorRef}
|
onClick={handleOpenMenu}
|
||||||
onClick={() => setMenu(!menu)}
|
aria-pressed={!!menuAnchor}
|
||||||
aria-pressed={menu}
|
|
||||||
radii="Pill"
|
radii="Pill"
|
||||||
size="400"
|
size="400"
|
||||||
variant={instanceId ? 'Success' : 'SurfaceVariant'}
|
variant={instanceId ? 'Success' : 'SurfaceVariant'}
|
||||||
|
@ -235,7 +238,6 @@ function ThirdPartyProtocolsSelector({
|
||||||
{selectedInstance?.desc ?? DEFAULT_INSTANCE_NAME}
|
{selectedInstance?.desc ?? DEFAULT_INSTANCE_NAME}
|
||||||
</Text>
|
</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -245,7 +247,7 @@ type LimitButtonProps = {
|
||||||
onLimitChange: (limit: string) => void;
|
onLimitChange: (limit: string) => void;
|
||||||
};
|
};
|
||||||
function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
const [openLimit, setOpenLimit] = useState(false);
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
|
|
||||||
const handleLimitSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
const handleLimitSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
@ -257,20 +259,23 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setLimit = (l: string) => {
|
const setLimit = (l: string) => {
|
||||||
setOpenLimit(false);
|
setMenuAnchor(undefined);
|
||||||
onLimitChange(l);
|
onLimitChange(l);
|
||||||
};
|
};
|
||||||
|
const handleOpenMenu: MouseEventHandler<HTMLElement> = (evt) => {
|
||||||
|
setMenuAnchor(evt.currentTarget.getBoundingClientRect());
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopOut
|
<PopOut
|
||||||
open={openLimit}
|
anchor={menuAnchor}
|
||||||
align="End"
|
align="End"
|
||||||
position="Bottom"
|
position="Bottom"
|
||||||
content={
|
content={
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: () => setOpenLimit(false),
|
onDeactivate: () => setMenuAnchor(undefined),
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -315,11 +320,9 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(anchorRef) => (
|
|
||||||
<Chip
|
<Chip
|
||||||
ref={anchorRef}
|
onClick={handleOpenMenu}
|
||||||
onClick={() => setOpenLimit(!openLimit)}
|
aria-pressed={!!menuAnchor}
|
||||||
aria-pressed={openLimit}
|
|
||||||
radii="Pill"
|
radii="Pill"
|
||||||
size="400"
|
size="400"
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
|
@ -327,7 +330,6 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
>
|
>
|
||||||
<Text size="T200" truncate>{`Page Limit: ${limit}`}</Text>
|
<Text size="T200" truncate>{`Page Limit: ${limit}`}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
|
||||||
</PopOut>
|
</PopOut>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue