From 1eda2da4ea509f5b52b6537d09a14fdb500746b0 Mon Sep 17 00:00:00 2001 From: stavast Date: Wed, 13 Jul 2022 03:39:04 +0200 Subject: [PATCH 1/2] Added the ability for the active/selected item in the CommandList to always be visible by calculating the boundries of the commandList container and adjusting the scroll position accordingly --- .../extensions/slash-menu/CommandList.tsx | 168 ++++++++++-------- src/utils/updateScrollView.ts | 17 ++ 2 files changed, 107 insertions(+), 78 deletions(-) create mode 100644 src/utils/updateScrollView.ts diff --git a/src/components/editor/extensions/slash-menu/CommandList.tsx b/src/components/editor/extensions/slash-menu/CommandList.tsx index eace20d..6e076f5 100644 --- a/src/components/editor/extensions/slash-menu/CommandList.tsx +++ b/src/components/editor/extensions/slash-menu/CommandList.tsx @@ -1,87 +1,99 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState, useRef, useLayoutEffect } from 'react' import { stopPrevent } from '../../../../utils' +import { updateScrollView } from '../../../../utils/updateScrollView' import './styles/CommandList.scss' interface CommandListProps { - items: any[], - command: Function, - event: any + items: any[] + command: Function + event: any } export const CommandList: React.FC = ({ items, command, event }) => { - const [selectedIndex, setSelectedIndex] = useState(0) - - useEffect(() => setSelectedIndex(0), [items]) - - const onKeyDown = (event: KeyboardEvent) => { - if (event.key === 'ArrowUp') { - stopPrevent(event) - upHandler() - return true - } - - if (event.key === 'ArrowDown') { - stopPrevent(event) - downHandler() - return true - } - - if (event.key === 'Enter') { - stopPrevent(event) - enterHandler() - return true - } - - return false - } - - useEffect(() => { onKeyDown(event) }, [event]) - - const upHandler = () => { - setSelectedIndex(((selectedIndex + items.length) - 1) % items.length) - } - - const downHandler = () => { - setSelectedIndex((selectedIndex + 1) % items.length) - } - - const enterHandler = () => { - selectItem(selectedIndex) - } - - const selectItem = (index: number) => { - const item = items[index] - - if (item) setTimeout(() => command(item)) - } - - return ( -
- { - items.length - ? ( - <> - { - items.map((item, index) => { - return ( -
selectItem(index)} - onMouseEnter={() => setSelectedIndex(index)} - > - - {item.icon()} - - {item.shortcut && {item.shortcut}} -
- ) - }) - } - - ) :
No result
- } -
- ) + const [selectedIndex, setSelectedIndex] = useState(0) + + const commandListContainer = useRef(null) + + useEffect(() => setSelectedIndex(0), [items]) + + useLayoutEffect(() => { + // Get Container + const container = commandListContainer?.current || null + // Get active/selected item from list + const item = (container!.children[selectedIndex] as HTMLElement) || null + // Update the scroll position + updateScrollView(container, item) + }, [selectedIndex]) + + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === 'ArrowUp') { + stopPrevent(event) + upHandler() + return true + } + + if (event.key === 'ArrowDown') { + stopPrevent(event) + downHandler() + return true + } + + if (event.key === 'Enter') { + stopPrevent(event) + enterHandler() + return true + } + + return false + } + + useEffect(() => { + onKeyDown(event) + }, [event]) + + const upHandler = () => { + setSelectedIndex((selectedIndex + items.length - 1) % items.length) + } + + const downHandler = () => { + setSelectedIndex((selectedIndex + 1) % items.length) + } + + const enterHandler = () => { + selectItem(selectedIndex) + } + + const selectItem = (index: number) => { + const item = items[index] + + if (item) setTimeout(() => command(item)) + } + + return ( +
+ {items.length ? ( + <> + {items.map((item, index) => { + return ( +
selectItem(index)} + onMouseEnter={() => setSelectedIndex(index)} + > + + {item.icon()}{' '} + + + {item.shortcut && {item.shortcut}} +
+ ) + })} + + ) : ( +
No result
+ )} +
+ ) } diff --git a/src/utils/updateScrollView.ts b/src/utils/updateScrollView.ts new file mode 100644 index 0000000..b1c8437 --- /dev/null +++ b/src/utils/updateScrollView.ts @@ -0,0 +1,17 @@ +export const updateScrollView = (container: HTMLElement | null, item: HTMLElement | null) => { + if (item && container) { + //Get the height f the list container + const ContainerHeight = container.offsetHeight; + const itemHeight = item ? item.offsetHeight : 0; + + //Calculate item distance from top and bottom + const top = item.offsetTop; + const bottom = top + itemHeight; + + if (top < container.scrollTop) { + container.scrollTop -= container.scrollTop - top + 5; + } else if (bottom > ContainerHeight + container.scrollTop) { + container.scrollTop += bottom - ContainerHeight - container.scrollTop + 5; + } + } +}; From 603737f28d447af3ff176b5339e23c52751693a2 Mon Sep 17 00:00:00 2001 From: stavast Date: Wed, 13 Jul 2022 03:43:23 +0200 Subject: [PATCH 2/2] Added ref on CommandList Div --- src/components/editor/extensions/slash-menu/CommandList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/editor/extensions/slash-menu/CommandList.tsx b/src/components/editor/extensions/slash-menu/CommandList.tsx index 6e076f5..09ac81e 100644 --- a/src/components/editor/extensions/slash-menu/CommandList.tsx +++ b/src/components/editor/extensions/slash-menu/CommandList.tsx @@ -71,7 +71,7 @@ export const CommandList: React.FC = ({ items, command, event } return ( -
+
{items.length ? ( <> {items.map((item, index) => {