Enable keyboard navigation in member invite list (#7102)
* feat: keyboard nav in share popover * fix: memoize
This commit is contained in:
@@ -20,6 +20,7 @@ import useBoolean from "~/hooks/useBoolean";
|
||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||
import useKeyDown from "~/hooks/useKeyDown";
|
||||
import usePolicy from "~/hooks/usePolicy";
|
||||
import usePrevious from "~/hooks/usePrevious";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { EmptySelectValue, Permission } from "~/types";
|
||||
import { collectionPath, urlify } from "~/utils/routeHelpers";
|
||||
@@ -56,6 +57,11 @@ function SharePopover({ collection, visible, onRequestClose }: Props) {
|
||||
CollectionPermission.Read
|
||||
);
|
||||
|
||||
const prevPendingIds = usePrevious(pendingIds);
|
||||
|
||||
const suggestionsRef = React.useRef<HTMLDivElement | null>(null);
|
||||
const searchInputRef = React.useRef<HTMLInputElement | null>(null);
|
||||
|
||||
useKeyDown(
|
||||
"Escape",
|
||||
(ev) => {
|
||||
@@ -97,6 +103,19 @@ function SharePopover({ collection, visible, onRequestClose }: Props) {
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (prevPendingIds && pendingIds.length > prevPendingIds.length) {
|
||||
setQuery("");
|
||||
searchInputRef.current?.focus();
|
||||
} else if (prevPendingIds && pendingIds.length < prevPendingIds.length) {
|
||||
const firstPending = suggestionsRef.current?.firstElementChild;
|
||||
|
||||
if (firstPending) {
|
||||
(firstPending as HTMLAnchorElement).focus();
|
||||
}
|
||||
}
|
||||
}, [pendingIds, prevPendingIds]);
|
||||
|
||||
const handleQuery = React.useCallback(
|
||||
(event) => {
|
||||
showPicker();
|
||||
@@ -119,6 +138,39 @@ function SharePopover({ collection, visible, onRequestClose }: Props) {
|
||||
[setPendingIds]
|
||||
);
|
||||
|
||||
const handleKeyDown = React.useCallback(
|
||||
(ev: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (ev.nativeEvent.isComposing) {
|
||||
return;
|
||||
}
|
||||
if (ev.key === "ArrowDown" && !ev.shiftKey) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (ev.currentTarget.value) {
|
||||
const length = ev.currentTarget.value.length;
|
||||
const selectionStart = ev.currentTarget.selectionStart || 0;
|
||||
if (selectionStart < length) {
|
||||
ev.currentTarget.selectionStart = length;
|
||||
ev.currentTarget.selectionEnd = length;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const firstSuggestion = suggestionsRef.current?.firstElementChild;
|
||||
|
||||
if (firstSuggestion) {
|
||||
(firstSuggestion as HTMLAnchorElement).focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleEscape = React.useCallback(
|
||||
() => searchInputRef.current?.focus(),
|
||||
[]
|
||||
);
|
||||
|
||||
const inviteAction = React.useMemo(
|
||||
() =>
|
||||
createAction({
|
||||
@@ -292,8 +344,10 @@ function SharePopover({ collection, visible, onRequestClose }: Props) {
|
||||
<Wrapper>
|
||||
{can.update && (
|
||||
<SearchInput
|
||||
ref={searchInputRef}
|
||||
onChange={handleQuery}
|
||||
onClick={showPicker}
|
||||
onKeyDown={handleKeyDown}
|
||||
query={query}
|
||||
back={backButton}
|
||||
action={rightButton}
|
||||
@@ -303,11 +357,13 @@ function SharePopover({ collection, visible, onRequestClose }: Props) {
|
||||
{picker && (
|
||||
<div>
|
||||
<Suggestions
|
||||
ref={suggestionsRef}
|
||||
query={query}
|
||||
collection={collection}
|
||||
pendingIds={pendingIds}
|
||||
addPendingId={handleAddPendingId}
|
||||
removePendingId={handleRemovePendingId}
|
||||
onEscape={handleEscape}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user