import { useEffect, useRef, useState } from "react"; import * as Popover from "@radix-ui/react-popover"; import { blockCommands } from "./BlockCommands"; import { useReplicache } from "src/replicache"; import { useEntitySetContext } from "components/EntitySetProvider"; import { NestedCardThemeProvider } from "components/ThemeManager/ThemeProvider"; import { UndoManager } from "src/undoManager"; import { useLeafletPublicationData } from "components/PageSWRDataProvider"; import { setEditorState, useEditorStates } from "src/state/useEditorState"; type Props = { parent: string; entityID: string | null; position: string | null; nextPosition: string | null; factID?: string | undefined; first?: boolean; className?: string; }; export const BlockCommandBar = ({ props, searchValue, }: { props: Props; searchValue: string; }) => { let ref = useRef(null); let [highlighted, setHighlighted] = useState(undefined); let { rep, undoManager } = useReplicache(); let entity_set = useEntitySetContext(); let { data: pub } = useLeafletPublicationData(); // This clears '/' AND anything typed after it const clearCommandSearchText = () => { if (!props.entityID) return; const entityID = props.entityID; const existingState = useEditorStates.getState().editorStates[entityID]; if (!existingState) return; const tr = existingState.editor.tr; tr.deleteRange(1, tr.doc.content.size - 1); setEditorState(entityID, { editor: existingState.editor.apply(tr) }); }; let commandResults = blockCommands.filter((command) => { const lowerSearchValue = searchValue.toLocaleLowerCase(); const matchesName = command.name .toLocaleLowerCase() .includes(lowerSearchValue); const matchesAlternate = command.alternateNames?.some((altName) => altName.toLocaleLowerCase().includes(lowerSearchValue) ) ?? false; const matchesSearch = matchesName || matchesAlternate; const isVisible = !pub || !command.hiddenInPublication; return matchesSearch && isVisible; }); useEffect(() => { if ( !highlighted || !commandResults.find((result) => result.name === highlighted) ) setHighlighted(commandResults[0]?.name); if (commandResults.length === 1) { setHighlighted(commandResults[0].name); } }, [commandResults, setHighlighted, highlighted]); useEffect(() => { let listener = async (e: KeyboardEvent) => { let reverseDir = ref.current?.dataset.side === "top"; let currentHighlightIndex = commandResults.findIndex( (command: { name: string }) => highlighted && command.name === highlighted, ); if (reverseDir ? e.key === "ArrowUp" : e.key === "ArrowDown") { setHighlighted( commandResults[ currentHighlightIndex === commandResults.length - 1 || currentHighlightIndex === undefined ? 0 : currentHighlightIndex + 1 ].name, ); return; } if (reverseDir ? e.key === "ArrowDown" : e.key === "ArrowUp") { setHighlighted( commandResults[ currentHighlightIndex === 0 || currentHighlightIndex === undefined || currentHighlightIndex === -1 ? commandResults.length - 1 : currentHighlightIndex - 1 ].name, ); return; } // on enter, select the highlighted item if (e.key === "Enter") { undoManager.startGroup(); e.preventDefault(); rep && (await commandResults[currentHighlightIndex]?.onSelect( rep, { ...props, entity_set: entity_set.set, }, undoManager, )); undoManager.endGroup(); return; } }; window.addEventListener("keydown", listener); return () => window.removeEventListener("keydown", listener); }, [highlighted, setHighlighted, commandResults, rep, entity_set.set, props]); return ( { if (!open) { clearCommandSearchText(); } }} > e.preventDefault()} className={` commandMenuContent group/cmd-menu z-20 w-[264px] flex data-[side=top]:items-end items-start `} >
{commandResults.length === 0 ? (
No blocks found
) : ( commandResults.map((result, index) => (
{ rep && result.onSelect( rep, { ...props, entity_set: entity_set.set, }, undoManager, ); }} highlighted={highlighted} setHighlighted={(highlighted) => setHighlighted(highlighted) } /> {commandResults[index + 1] && result.type !== commandResults[index + 1].type && (
)}
)) )}
); }; const CommandResult = (props: { name: string; icon: React.ReactNode; onSelect: () => void; highlighted: string | undefined; setHighlighted: (state: string | undefined) => void; }) => { let isHighlighted = props.highlighted === props.name; return ( ); }; function usePublicationContext() { throw new Error("Function not implemented."); }