Add expander for reactions view

This commit is contained in:
Will Hunt 2024-11-11 12:53:25 +00:00
parent 543f656548
commit 2c889f9b45
2 changed files with 11 additions and 80 deletions

View File

@ -22,6 +22,8 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: var(--cpd-separator-spacing); gap: var(--cpd-separator-spacing);
max-height: 10em;
max-width: 22.5em;
} }
.reactionPopupMenu section { .reactionPopupMenu section {
@ -63,18 +65,6 @@
margin-left: var(--cpd-separator-spacing); margin-left: var(--cpd-separator-spacing);
margin-right: var(--cpd-separator-spacing); margin-right: var(--cpd-separator-spacing);
} }
.searchForm {
display: flex;
flex-direction: row;
gap: var(--cpd-separator-spacing);
margin-bottom: var(--cpd-space-3x);
}
.searchForm > label {
flex: auto;
}
.alert { .alert {
margin-bottom: var(--cpd-space-3x); margin-bottom: var(--cpd-space-3x);
animation: grow-in 200ms; animation: grow-in 200ms;

View File

@ -13,16 +13,14 @@ import {
Alert, Alert,
} from "@vector-im/compound-web"; } from "@vector-im/compound-web";
import { import {
SearchIcon,
CloseIcon,
RaisedHandSolidIcon, RaisedHandSolidIcon,
ReactionIcon, ReactionIcon,
ChevronDownIcon,
ChevronUpIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons"; } from "@vector-im/compound-design-tokens/assets/web/icons";
import { import {
ChangeEventHandler,
ComponentPropsWithoutRef, ComponentPropsWithoutRef,
FC, FC,
KeyboardEventHandler,
ReactNode, ReactNode,
useCallback, useCallback,
useEffect, useEffect,
@ -84,44 +82,14 @@ export function ReactionPopupMenu({
canReact: boolean; canReact: boolean;
}): ReactNode { }): ReactNode {
const { t } = useTranslation(); const { t } = useTranslation();
const [searchText, setSearchText] = useState(""); const [isFullyExpanded, setExpanded] = useState(false);
const [isSearching, setIsSearching] = useState(false);
const onSearch = useCallback<ChangeEventHandler<HTMLInputElement>>((ev) => {
ev.preventDefault();
setSearchText(ev.target.value.trim().toLocaleLowerCase());
}, []);
const filteredReactionSet = useMemo( const filteredReactionSet = useMemo(
() => () =>
ReactionSet.filter( isFullyExpanded ? ReactionSet : ReactionSet.slice(0, 6),
(reaction) => [isFullyExpanded],
!isSearching ||
(!!searchText &&
(reaction.name.startsWith(searchText) ||
reaction.alias?.some((a) => a.startsWith(searchText)))),
).slice(0, 6),
[searchText, isSearching],
); );
const onSearchKeyDown = useCallback<KeyboardEventHandler<never>>(
(ev) => {
if (ev.key === "Enter") {
ev.preventDefault();
if (!canReact) {
return;
}
if (filteredReactionSet.length !== 1) {
return;
}
sendReaction(filteredReactionSet[0]);
setIsSearching(false);
} else if (ev.key === "Escape") {
ev.preventDefault();
setIsSearching(false);
}
},
[sendReaction, filteredReactionSet, canReact, setIsSearching],
);
const label = isHandRaised ? t("action.lower_hand") : t("action.raise_hand"); const label = isHandRaised ? t("action.lower_hand") : t("action.raise_hand");
return ( return (
<> <>
@ -149,31 +117,6 @@ export function ReactionPopupMenu({
</section> </section>
<div className={styles.verticalSeperator} /> <div className={styles.verticalSeperator} />
<section> <section>
{isSearching ? (
<>
<Form.Root className={styles.searchForm}>
<Search
required
value={searchText}
name="reactionSearch"
placeholder={t("reaction_search")}
onChange={onSearch}
onKeyDown={onSearchKeyDown}
// This is a reasonable use of autofocus, we are focusing when
// the search button is clicked (which matches the Element Web reaction picker)
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
/>
<CpdButton
Icon={CloseIcon}
aria-label={t("action.close_search")}
size="sm"
kind="destructive"
onClick={() => setIsSearching(false)}
/>
</Form.Root>
</>
) : null}
<menu className={styles.reactionsMenu}> <menu className={styles.reactionsMenu}>
{filteredReactionSet.map((reaction) => ( {filteredReactionSet.map((reaction) => (
<li className={styles.reactionPopupMenuItem} key={reaction.name}> <li className={styles.reactionPopupMenuItem} key={reaction.name}>
@ -191,21 +134,19 @@ export function ReactionPopupMenu({
))} ))}
</menu> </menu>
</section> </section>
{!isSearching ? (
<section style={{ marginLeft: "var(--cpd-separator-spacing)" }}> <section style={{ marginLeft: "var(--cpd-separator-spacing)" }}>
<li key="search" className={styles.reactionPopupMenuItem}> <li key="search" className={styles.reactionPopupMenuItem}>
<Tooltip label={t("common.search")}> <Tooltip label={isFullyExpanded ? t("action.show_less") : t("action.show_more")}>
<CpdButton <CpdButton
iconOnly iconOnly
aria-label={t("action.open_search")} aria-label={isFullyExpanded ? t("action.show_less") : t("action.show_more")}
Icon={SearchIcon} Icon={isFullyExpanded ? ChevronUpIcon : ChevronDownIcon}
kind="tertiary" kind="tertiary"
onClick={() => setIsSearching(true)} onClick={() => setExpanded(!isFullyExpanded)}
/> />
</Tooltip> </Tooltip>
</li> </li>
</section> </section>
) : null}
</div> </div>
</> </>
); );