UNPKG

@wordpress/editor

Version:
216 lines (215 loc) 6.92 kB
// packages/editor/src/components/collab-sidebar/notes.js import { useEffect, useMemo } from "@wordpress/element"; import { __ } from "@wordpress/i18n"; import { useSelect, useDispatch } from "@wordpress/data"; import { Stack } from "@wordpress/ui"; import { store as blockEditorStore, privateApis as blockEditorPrivateApis } from "@wordpress/block-editor"; import { unlock } from "../../lock-unlock.mjs"; import { NoteThread } from "./note-thread.mjs"; import { focusNoteThread } from "./utils.mjs"; import { useFloatingBoard, useNoteActions } from "./hooks.mjs"; import { AddNote } from "./add-note.mjs"; import { store as editorStore } from "../../store/index.mjs"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var { useBlockElement } = unlock(blockEditorPrivateApis); function Notes({ notes, sidebarRef, isFloating = false, styles }) { const { onCreate: onAddReply, onEdit: onEditNote, onDelete } = useNoteActions(); const { selectNote } = unlock(useDispatch(editorStore)); const { selectBlock, toggleBlockSpotlight } = unlock( useDispatch(blockEditorStore) ); const { blockNoteId, selectedBlockClientId, orderedBlockIds } = useSelect( (select) => { const { getBlockAttributes, getSelectedBlockClientId, getClientIdsWithDescendants } = select(blockEditorStore); const clientId = getSelectedBlockClientId(); return { blockNoteId: clientId ? getBlockAttributes(clientId)?.metadata?.noteId : null, selectedBlockClientId: clientId, orderedBlockIds: getClientIdsWithDescendants() }; }, [] ); const { selectedNote, noteFocused } = useSelect((select) => { const { getSelectedNote, isNoteFocused } = unlock( select(editorStore) ); return { selectedNote: getSelectedNote(), noteFocused: isNoteFocused() }; }, []); const relatedBlockElement = useBlockElement(selectedBlockClientId); const threads = useMemo(() => { if (!isFloating || selectedNote !== "new") { return notes; } const newNoteThread = { id: "new", blockClientId: selectedBlockClientId, content: { rendered: "" } }; const out = []; orderedBlockIds.forEach((blockId) => { if (blockId === selectedBlockClientId) { out.push(newNoteThread); } else { const threadForBlock = notes.find( (t) => t.blockClientId === blockId ); if (threadForBlock) { out.push(threadForBlock); } } }); return out; }, [ notes, isFloating, selectedNote, selectedBlockClientId, orderedBlockIds ]); const handleDelete = async (note) => { const currentIndex = threads.findIndex((t) => t.id === note.id); const nextThread = threads[currentIndex + 1]; const prevThread = threads[currentIndex - 1]; await onDelete(note); if (note.parent !== 0) { selectNote(note.parent); focusNoteThread(note.parent, sidebarRef.current); return; } if (nextThread) { selectNote(nextThread.id); focusNoteThread(nextThread.id, sidebarRef.current); } else if (prevThread) { selectNote(prevThread.id); focusNoteThread(prevThread.id, sidebarRef.current); } else { selectNote(void 0); toggleBlockSpotlight(note.blockClientId, false); relatedBlockElement?.focus(); } }; useEffect(() => { selectNote(blockNoteId ?? void 0); }, [blockNoteId, selectNote]); useEffect(() => { if (noteFocused && selectedNote) { focusNoteThread( selectedNote, sidebarRef.current, selectedNote === "new" ? "textarea" : void 0 ); selectNote(selectedNote); } }, [noteFocused, selectedNote, selectNote, sidebarRef]); const { notePositions, registerThread, unregisterThread } = useFloatingBoard({ threads, selectedNoteId: selectedNote, isFloating, sidebarRef }); const hasThreads = Array.isArray(threads) && threads.length > 0; const navigate = (event, thread, isSelected) => { if (event.defaultPrevented) { return; } const currentIndex = threads.findIndex((t) => t.id === thread.id); const isSelfTarget = event.currentTarget === event.target; if ((event.key === "Enter" || event.key === "ArrowRight") && isSelfTarget && !isSelected) { selectNote(thread.id); if (!!thread.blockClientId) { selectBlock(thread.blockClientId, null); toggleBlockSpotlight(thread.blockClientId, true); } } else if ((event.key === "Enter" || event.key === "ArrowLeft") && isSelfTarget && isSelected || event.key === "Escape") { selectNote(void 0); if (thread.blockClientId) { toggleBlockSpotlight(thread.blockClientId, false); } focusNoteThread(thread.id, sidebarRef.current); } else if (event.key === "ArrowDown" && currentIndex < threads.length - 1 && isSelfTarget) { focusNoteThread( threads[currentIndex + 1].id, sidebarRef.current ); } else if (event.key === "ArrowUp" && currentIndex > 0 && isSelfTarget) { focusNoteThread( threads[currentIndex - 1].id, sidebarRef.current ); } else if (event.key === "Home" && isSelfTarget) { focusNoteThread(threads[0].id, sidebarRef.current); } else if (event.key === "End" && isSelfTarget) { focusNoteThread( threads[threads.length - 1].id, sidebarRef.current ); } }; return /* @__PURE__ */ jsx( Stack, { className: "editor-collab-sidebar-panel", style: styles, role: "tree", direction: "column", gap: "md", justify: "flex-start", ref: (node) => { if (node) { sidebarRef.current = node; } }, "aria-label": isFloating ? __("Unresolved notes") : __("All notes"), children: !hasThreads && !isFloating ? /* @__PURE__ */ jsx(AddNote, { onSubmit: onAddReply, sidebarRef }) : /* @__PURE__ */ jsxs(Fragment, { children: [ !isFloating && selectedNote === "new" && /* @__PURE__ */ jsx( AddNote, { onSubmit: onAddReply, sidebarRef } ), threads.map((thread) => /* @__PURE__ */ jsx( NoteThread, { note: thread, onAddReply, onDeleteNote: handleDelete, onEditNote, isSelected: selectedNote === thread.id, sidebarRef, floating: isFloating ? { y: notePositions[thread.id], registerThread, unregisterThread } : void 0, onKeyDown: (event) => navigate( event, thread, selectedNote === thread.id ) }, thread.id )) ] }) } ); } export { Notes }; //# sourceMappingURL=notes.mjs.map