@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
216 lines (215 loc) • 6.92 kB
JavaScript
// 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