@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
404 lines (402 loc) • 12.9 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// packages/editor/src/components/collab-sidebar/utils.js
var utils_exports = {};
__export(utils_exports, {
BLOCK_LEVEL_NOTE_START: () => BLOCK_LEVEL_NOTE_START,
addNoteIdToMetadata: () => addNoteIdToMetadata,
applyNoteFormat: () => applyNoteFormat,
calculateNotePositions: () => calculateNotePositions,
findNoteInBlock: () => findNoteInBlock,
findNoteRange: () => findNoteRange,
focusNoteThread: () => focusNoteThread,
getAvatarBorderColor: () => getAvatarBorderColor,
getInlineMarkerStart: () => getInlineMarkerStart,
getNoteExcerpt: () => getNoteExcerpt,
getNoteIdsFromMetadata: () => getNoteIdsFromMetadata,
getNoteMarkerSelector: () => getNoteMarkerSelector,
getSelectionRect: () => getSelectionRect,
pickPrimaryNote: () => pickPrimaryNote,
removeNoteFormat: () => removeNoteFormat,
removeNoteIdFromMetadata: () => removeNoteIdFromMetadata,
sanitizeNoteContent: () => sanitizeNoteContent,
scrollNoteThreadIntoView: () => scrollNoteThreadIntoView
});
module.exports = __toCommonJS(utils_exports);
var import_i18n = require("@wordpress/i18n");
var import_rich_text = require("@wordpress/rich-text");
var import_dom = require("@wordpress/dom");
function sanitizeNoteContent(str) {
return str.trim();
}
var THREAD_ALIGN_OFFSET = -16;
var THREAD_GAP = 16;
var OVERLAP_MARGIN = 20;
var AVATAR_BORDER_COLORS = [
"#6F42C1",
// Purple
"#D94145",
// Red
"#FBBF24",
// Orange
"#FF35EE",
// Magenta
"#879F11",
// Olive
"#0F766E",
// Teal
"#00CFFF"
// Cyan
];
function getAvatarBorderColor(userId) {
return AVATAR_BORDER_COLORS[userId % AVATAR_BORDER_COLORS.length];
}
function getNoteExcerpt(text, excerptLength = 10) {
if (!text) {
return "";
}
const wordCountType = (0, import_i18n._x)("words", "Word count type. Do not translate!");
const rawText = text.trim();
let trimmedExcerpt = "";
if (wordCountType === "words") {
trimmedExcerpt = rawText.split(" ", excerptLength).join(" ");
} else if (wordCountType === "characters_excluding_spaces") {
const textWithSpaces = rawText.split("", excerptLength).join("");
const numberOfSpaces = textWithSpaces.length - textWithSpaces.replaceAll(" ", "").length;
trimmedExcerpt = rawText.split("", excerptLength + numberOfSpaces).join("");
} else if (wordCountType === "characters_including_spaces") {
trimmedExcerpt = rawText.split("", excerptLength).join("");
}
const isTrimmed = trimmedExcerpt !== rawText;
return isTrimmed ? trimmedExcerpt + "…" : trimmedExcerpt;
}
function getNoteIdsFromMetadata(metadata) {
const noteId = metadata?.noteId;
const raw = Array.isArray(noteId) ? noteId : [noteId];
const ids = /* @__PURE__ */ new Set();
for (const value of raw) {
const id = Number(value);
if (Number.isFinite(id) && id > 0) {
ids.add(id);
}
}
return [...ids];
}
function addNoteIdToMetadata(metadata, noteId) {
const ids = new Set(getNoteIdsFromMetadata(metadata));
const id = Number(noteId);
if (ids.has(id)) {
return metadata;
}
ids.add(id);
return { ...metadata, noteId: [...ids] };
}
var NOTE_FORMAT_TYPE = "core/note";
function findNoteRange(value, noteId) {
if (noteId === void 0 || noteId === null) {
return null;
}
let html = null;
if (value instanceof import_rich_text.RichTextData) {
html = value.toHTMLString();
} else if (typeof value === "string") {
html = value;
}
if (!html || html.indexOf("wp-note") === -1) {
return null;
}
const target = String(noteId);
const record = (0, import_rich_text.create)({ html });
const formats = record.formats;
let start = -1;
for (let i = 0; i < formats.length; i++) {
const stack = formats[i];
const hit = stack?.find(
(f) => f.type === NOTE_FORMAT_TYPE && f.attributes && f.attributes["data-id"] === target
);
if (hit) {
if (start === -1) {
start = i;
}
} else if (start !== -1) {
return { start, end: i };
}
}
if (start !== -1) {
return { start, end: formats.length };
}
return null;
}
function findNoteInBlock(attributes, noteId) {
if (!attributes) {
return null;
}
for (const attributeKey of Object.keys(attributes)) {
const range = findNoteRange(attributes[attributeKey], noteId);
if (range) {
return { attributeKey, start: range.start, end: range.end };
}
}
return null;
}
function getNoteMarkerSelector(noteId) {
const escapedId = String(noteId).replace(
/["\\\n\r\f]/g,
(char) => char === '"' || char === "\\" ? `\\${char}` : `\\${char.codePointAt(0).toString(16)} `
);
return `mark.wp-note[data-id="${escapedId}"]`;
}
function getSelectionRect(blockEl) {
const selection = blockEl.ownerDocument.defaultView?.getSelection();
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
return null;
}
const range = selection.getRangeAt(0);
if (range.collapsed) {
return null;
}
if (!blockEl.contains(range.commonAncestorContainer)) {
return null;
}
const rect = (0, import_dom.getRectangleFromRange)(range);
if (!rect || rect.width === 0 && rect.height === 0) {
return null;
}
return rect;
}
var BLOCK_LEVEL_NOTE_START = -1;
function getInlineMarkerStart(thread, attributes) {
const found = findNoteInBlock(attributes, thread?.id);
return found ? found.start : BLOCK_LEVEL_NOTE_START;
}
function applyNoteFormat(record, format, start, end) {
const formats = record.formats.slice();
for (let i = start; i < end; i++) {
const stack = formats[i] ? formats[i].slice() : [];
stack.push(format);
formats[i] = stack;
}
const spans = /* @__PURE__ */ new Map();
for (let i = 0; i < formats.length; i++) {
const stack = formats[i];
if (!stack) {
continue;
}
for (const fmt of stack) {
if (fmt.type !== NOTE_FORMAT_TYPE) {
continue;
}
const id = fmt.attributes?.["data-id"];
const span = spans.get(id);
if (span) {
span.end = i;
} else {
spans.set(id, { start: i, end: i });
}
}
}
const sizeOf = (id) => {
const span = spans.get(id);
return span ? span.end - span.start : 0;
};
for (let i = 0; i < formats.length; i++) {
const stack = formats[i];
if (!stack || stack.length < 2) {
continue;
}
const notes = stack.filter((fmt) => fmt.type === NOTE_FORMAT_TYPE);
if (notes.length === 0) {
continue;
}
if (notes.length > 1) {
notes.sort(
(a, b) => sizeOf(b.attributes?.["data-id"]) - sizeOf(a.attributes?.["data-id"])
);
}
const others = stack.filter((fmt) => fmt.type !== NOTE_FORMAT_TYPE);
formats[i] = [...notes, ...others];
}
return { ...record, formats };
}
function removeNoteFormat(value, noteId) {
if (!(value instanceof import_rich_text.RichTextData)) {
return null;
}
const target = String(noteId);
const record = (0, import_rich_text.create)({ html: value.toHTMLString() });
let changed = false;
const formats = record.formats.map((stack) => {
if (!stack) {
return stack;
}
const filtered = stack.filter(
(format) => !(format.type === NOTE_FORMAT_TYPE && format.attributes?.["data-id"] === target)
);
if (filtered.length === stack.length) {
return stack;
}
changed = true;
return filtered.length ? filtered : void 0;
});
return changed ? import_rich_text.RichTextData.fromHTMLString(
new import_rich_text.RichTextData({ ...record, formats }).toHTMLString()
) : null;
}
function pickPrimaryNote(threads) {
return threads.find((thread) => thread.status === "hold") ?? threads[0] ?? null;
}
function removeNoteIdFromMetadata(metadata, noteId) {
const ids = new Set(getNoteIdsFromMetadata(metadata));
ids.delete(Number(noteId));
return {
...metadata,
noteId: ids.size > 0 ? [...ids] : void 0
};
}
function calculateNotePositions({
threads,
selectedNoteId,
blockRects,
heights,
scrollTop = 0
}) {
const offsets = {};
const orderedThreads = [...threads].sort(
(a, b) => (blockRects[a.id]?.top ?? Number.MAX_VALUE) - (blockRects[b.id]?.top ?? Number.MAX_VALUE)
);
const anchorIndex = Math.max(
0,
orderedThreads.findIndex((thread) => thread.id === selectedNoteId)
);
const anchorThread = orderedThreads[anchorIndex];
if (!anchorThread || !blockRects[anchorThread.id]) {
return { positions: {} };
}
const anchorRect = blockRects[anchorThread.id];
const anchorTop = anchorRect.top || 0;
const anchorHeight = heights[anchorThread.id] || 0;
offsets[anchorThread.id] = THREAD_ALIGN_OFFSET;
let prevAdjustedTop = anchorTop + THREAD_ALIGN_OFFSET;
let prevHeight = anchorHeight;
for (let i = anchorIndex + 1; i < orderedThreads.length; i++) {
const thread = orderedThreads[i];
const threadRect = blockRects[thread.id];
if (!threadRect) {
continue;
}
const threadTop = threadRect.top || 0;
const threadHeight = heights[thread.id] || 0;
let offset = THREAD_ALIGN_OFFSET;
const prevBottom = prevAdjustedTop + prevHeight;
if (threadTop < prevBottom + THREAD_GAP) {
offset = prevBottom - threadTop + OVERLAP_MARGIN;
}
offsets[thread.id] = offset;
prevAdjustedTop = threadTop + offset;
prevHeight = threadHeight;
}
let belowAdjustedTop = anchorTop + THREAD_ALIGN_OFFSET;
for (let i = anchorIndex - 1; i >= 0; i--) {
const thread = orderedThreads[i];
const threadRect = blockRects[thread.id];
if (!threadRect) {
continue;
}
const threadTop = threadRect.top || 0;
const threadHeight = heights[thread.id] || 0;
let offset = THREAD_ALIGN_OFFSET;
const threadBottom = threadTop + threadHeight;
if (threadBottom > belowAdjustedTop) {
offset = belowAdjustedTop - threadTop - threadHeight - OVERLAP_MARGIN;
}
offsets[thread.id] = offset;
belowAdjustedTop = threadTop + offset;
}
const positions = {};
for (const thread of orderedThreads) {
const blockRect = blockRects[thread.id];
if (blockRect && offsets[thread.id] !== void 0) {
positions[thread.id] = blockRect.top + scrollTop + offsets[thread.id];
}
}
return { positions };
}
function findNoteThread(noteId, container, additionalSelector) {
if (!container) {
return Promise.resolve(null);
}
const threadSelector = noteId && noteId !== "new" ? `[role=treeitem][id="note-thread-${noteId}"]` : "[role=treeitem]:not([id])";
const selector = additionalSelector ? `${threadSelector} ${additionalSelector}` : threadSelector;
return new Promise((resolve) => {
if (container.querySelector(selector)) {
return resolve(container.querySelector(selector));
}
let timer = null;
const observer = new window.MutationObserver(() => {
if (container.querySelector(selector)) {
clearTimeout(timer);
observer.disconnect();
resolve(container.querySelector(selector));
}
});
observer.observe(container, { childList: true, subtree: true });
timer = setTimeout(() => {
observer.disconnect();
resolve(null);
}, 3e3);
});
}
function focusNoteThread(noteId, container, additionalSelector) {
return findNoteThread(noteId, container, additionalSelector).then(
(element) => {
if (!element) {
return;
}
element.focus();
element.scrollIntoView({ block: "nearest" });
}
);
}
function scrollNoteThreadIntoView(noteId, container) {
return findNoteThread(noteId, container).then((element) => {
element?.scrollIntoView({ block: "nearest" });
});
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BLOCK_LEVEL_NOTE_START,
addNoteIdToMetadata,
applyNoteFormat,
calculateNotePositions,
findNoteInBlock,
findNoteRange,
focusNoteThread,
getAvatarBorderColor,
getInlineMarkerStart,
getNoteExcerpt,
getNoteIdsFromMetadata,
getNoteMarkerSelector,
getSelectionRect,
pickPrimaryNote,
removeNoteFormat,
removeNoteIdFromMetadata,
sanitizeNoteContent,
scrollNoteThreadIntoView
});
//# sourceMappingURL=utils.cjs.map