UNPKG

@wordpress/editor

Version:
404 lines (402 loc) 12.9 kB
"use strict"; 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