Fix roving tab index getting confused after dragging space order (#10901)

* Fix roving tab index getting confused after dragging space order

* Fix roving tab index for drag reordering

* delint

* Add test

* Make types happier

* Remove snapshot
This commit is contained in:
Michael Telatynski 2023-05-17 15:18:21 +01:00 committed by GitHub
parent 2da199c41d
commit d9d53870e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 150 additions and 33 deletions

View file

@ -78,16 +78,40 @@ export enum Type {
Register = "REGISTER",
Unregister = "UNREGISTER",
SetFocus = "SET_FOCUS",
Update = "UPDATE",
}
export interface IAction {
type: Type;
type: Exclude<Type, Type.Update>;
payload: {
ref: Ref;
};
}
export const reducer: Reducer<IState, IAction> = (state: IState, action: IAction) => {
interface UpdateAction {
type: Type.Update;
payload?: undefined;
}
type Action = IAction | UpdateAction;
const refSorter = (a: Ref, b: Ref): number => {
if (a === b) {
return 0;
}
const position = a.current!.compareDocumentPosition(b.current!);
if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
return -1;
} else if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
return 1;
} else {
return 0;
}
};
export const reducer: Reducer<IState, Action> = (state: IState, action: Action) => {
switch (action.type) {
case Type.Register: {
if (!state.activeRef) {
@ -97,21 +121,7 @@ export const reducer: Reducer<IState, IAction> = (state: IState, action: IAction
// Sadly due to the potential of DOM elements swapping order we can't do anything fancy like a binary insert
state.refs.push(action.payload.ref);
state.refs.sort((a, b) => {
if (a === b) {
return 0;
}
const position = a.current!.compareDocumentPosition(b.current!);
if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
return -1;
} else if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
return 1;
} else {
return 0;
}
});
state.refs.sort(refSorter);
return { ...state };
}
@ -150,6 +160,11 @@ export const reducer: Reducer<IState, IAction> = (state: IState, action: IAction
return { ...state };
}
case Type.Update: {
state.refs.sort(refSorter);
return { ...state };
}
default:
return state;
}
@ -160,7 +175,7 @@ interface IProps {
handleHomeEnd?: boolean;
handleUpDown?: boolean;
handleLeftRight?: boolean;
children(renderProps: { onKeyDownHandler(ev: React.KeyboardEvent): void }): ReactNode;
children(renderProps: { onKeyDownHandler(ev: React.KeyboardEvent): void; onDragEndHandler(): void }): ReactNode;
onKeyDown?(ev: React.KeyboardEvent, state: IState, dispatch: Dispatch<IAction>): void;
}
@ -199,7 +214,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
handleLoop,
onKeyDown,
}) => {
const [state, dispatch] = useReducer<Reducer<IState, IAction>>(reducer, {
const [state, dispatch] = useReducer<Reducer<IState, Action>>(reducer, {
refs: [],
});
@ -301,9 +316,15 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
[context, onKeyDown, handleHomeEnd, handleUpDown, handleLeftRight, handleLoop],
);
const onDragEndHandler = useCallback(() => {
dispatch({
type: Type.Update,
});
}, []);
return (
<RovingTabIndexContext.Provider value={context}>
{children({ onKeyDownHandler })}
{children({ onKeyDownHandler, onDragEndHandler })}
</RovingTabIndexContext.Provider>
);
};

View file

@ -330,6 +330,7 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(
);
const SpacePanel: React.FC = () => {
const [dragging, setDragging] = useState(false);
const [isPanelCollapsed, setPanelCollapsed] = useState(true);
const ref = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
@ -344,14 +345,19 @@ const SpacePanel: React.FC = () => {
});
return (
<DragDropContext
onDragEnd={(result) => {
if (!result.destination) return; // dropped outside the list
SpaceStore.instance.moveRootSpace(result.source.index, result.destination.index);
}}
>
<RovingTabIndexProvider handleHomeEnd handleUpDown>
{({ onKeyDownHandler }) => (
<RovingTabIndexProvider handleHomeEnd handleUpDown={!dragging}>
{({ onKeyDownHandler, onDragEndHandler }) => (
<DragDropContext
onDragStart={() => {
setDragging(true);
}}
onDragEnd={(result) => {
setDragging(false);
if (!result.destination) return; // dropped outside the list
SpaceStore.instance.moveRootSpace(result.source.index, result.destination.index);
onDragEndHandler();
}}
>
<div
className={classNames("mx_SpacePanel", { collapsed: isPanelCollapsed })}
onKeyDown={onKeyDownHandler}
@ -395,9 +401,9 @@ const SpacePanel: React.FC = () => {
<QuickSettingsButton isPanelCollapsed={isPanelCollapsed} />
</div>
)}
</RovingTabIndexProvider>
</DragDropContext>
</DragDropContext>
)}
</RovingTabIndexProvider>
);
};