/** * based on: * https://github.com/ueberdosis/tiptap/issues/1508 * MIT License https://github.com/fantasticit/think/blob/main/packages/client/src/tiptap/core/extensions/slash.ts#L11 * https://github.com/fantasticit/magic-editor/blob/main/src/extensions/slash/slash-menu-view.tsx#L68 */ import React, { ElementRef, RefObject } from "react"; import i18next from "i18next"; class SlashView extends React.Component< { items: any; command: any }, { selectedIndex: number } > { $container: RefObject; constructor(props: { items: any; command: any }) { super(props); this.$container = React.createRef(); this.state = { selectedIndex: 0, }; } selectItem = (index: number) => { const { items, command } = this.props; const selectedCommand = items[index]; if (selectedCommand) { command(selectedCommand); } }; upHandler = () => { const { items } = this.props; this.setState((prevState: { selectedIndex: number }) => ({ selectedIndex: (prevState.selectedIndex + items.length - 1) % items.length, })); }; downHandler = () => { const { items } = this.props; this.setState((prevState: { selectedIndex: number }) => ({ selectedIndex: (prevState.selectedIndex + 1) % items.length, })); }; enterHandler = () => { this.selectItem(this.state.selectedIndex); }; componentDidMount() { this.setState({ selectedIndex: 0 }); } componentDidUpdate(prevProps: { items: any }) { if (prevProps.items !== this.props.items) { this.setState({ selectedIndex: 0 }); } const { selectedIndex } = this.state; if (!Number.isNaN(selectedIndex + 1)) { const el = this.$container?.current?.querySelector( `.slash-menu-item:nth-of-type(${selectedIndex + 1})`, ); el && el.scrollIntoView({ behavior: "smooth", scrollMode: "if-needed" }); } } onKeyDown = ({ event }: { event: KeyboardEvent }) => { if (event.key === "ArrowUp") { this.upHandler(); return true; } if (event.key === "ArrowDown") { this.downHandler(); return true; } if (event.key === "Enter") { this.enterHandler(); return true; } return false; }; render() { const { items } = this.props; const { selectedIndex } = this.state; return (
{items.map(({ name, i18Name }: any, idx: number) => (
  • this.selectItem(idx)} className={ selectedIndex === idx ? "is-active DropdownMenuItem" : "DropdownMenuItem" } > {i18Name ? i18next.t(name) : name}
  • ))}
    ); } } export default SlashView;