a tool for shared writing and social publishing
at refactor/domain-management 114 lines 3.7 kB view raw
1import { useState } from "react"; 2import { blockCommands } from "./BlockCommands"; 3import { useReplicache } from "src/replicache"; 4import { useEntitySetContext } from "components/EntitySetProvider"; 5import { useLeafletPublicationData } from "components/PageSWRDataProvider"; 6import { setEditorState, useEditorStates } from "src/state/useEditorState"; 7import { Combobox, ComboboxResult } from "components/Combobox"; 8 9type Props = { 10 parent: string; 11 entityID: string | null; 12 position: string | null; 13 nextPosition: string | null; 14 factID?: string | undefined; 15 first?: boolean; 16 className?: string; 17}; 18 19export const BlockCommandBar = ({ 20 props, 21 searchValue, 22}: { 23 props: Props; 24 searchValue: string; 25}) => { 26 let [highlighted, setHighlighted] = useState<string | undefined>(undefined); 27 28 let { rep, undoManager } = useReplicache(); 29 let entity_set = useEntitySetContext(); 30 let { data: pub } = useLeafletPublicationData(); 31 32 // This clears '/' AND anything typed after it 33 const clearCommandSearchText = () => { 34 if (!props.entityID) return; 35 const entityID = props.entityID; 36 37 const existingState = useEditorStates.getState().editorStates[entityID]; 38 if (!existingState) return; 39 40 const tr = existingState.editor.tr; 41 tr.deleteRange(1, tr.doc.content.size - 1); 42 setEditorState(entityID, { editor: existingState.editor.apply(tr) }); 43 }; 44 45 let commandResults = blockCommands.filter((command) => { 46 const lowerSearchValue = searchValue.toLocaleLowerCase(); 47 const matchesName = command.name 48 .toLocaleLowerCase() 49 .includes(lowerSearchValue); 50 const matchesAlternate = 51 command.alternateNames?.some((altName) => 52 altName.toLocaleLowerCase().includes(lowerSearchValue), 53 ) ?? false; 54 const matchesSearch = matchesName || matchesAlternate; 55 const isVisible = !pub || !command.hiddenInPublication; 56 return matchesSearch && isVisible; 57 }); 58 59 return ( 60 <Combobox 61 open 62 triggerClassName="absolute left-0" 63 results={commandResults.map((r) => r.name)} 64 highlighted={highlighted} 65 setHighlighted={setHighlighted} 66 onSelect={async () => { 67 let command = commandResults.find((c) => c.name === highlighted); 68 if (!command || !rep) return; 69 undoManager.startGroup(); 70 await command.onSelect( 71 rep, 72 { ...props, entity_set: entity_set.set }, 73 undoManager, 74 ); 75 undoManager.endGroup(); 76 }} 77 onOpenChange={() => clearCommandSearchText()} 78 > 79 {commandResults.length === 0 ? ( 80 <div className="w-full text-tertiary text-center italic py-2 px-2 "> 81 No blocks found 82 </div> 83 ) : ( 84 commandResults.map((result, index) => ( 85 <div key={index} className="contents"> 86 <ComboboxResult 87 className="pl-0!" 88 result={result.name} 89 onSelect={() => { 90 rep && 91 result.onSelect( 92 rep, 93 { ...props, entity_set: entity_set.set }, 94 undoManager, 95 ); 96 }} 97 highlighted={highlighted} 98 setHighlighted={setHighlighted} 99 > 100 <div className="text-tertiary w-8 shrink-0 flex justify-center"> 101 {result.icon} 102 </div> 103 {result.name} 104 </ComboboxResult> 105 {commandResults[index + 1] && 106 result.type !== commandResults[index + 1].type && ( 107 <hr className="mx-2 my-0.5 border-border" /> 108 )} 109 </div> 110 )) 111 )} 112 </Combobox> 113 ); 114};