@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
8 lines (7 loc) • 7.7 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../src/components/collaborators-overlay/use-render-cursors.ts"],
"sourcesContent": ["import {\n\tprivateApis as coreDataPrivateApis,\n\tSelectionType,\n\ttype PostEditorAwarenessState as ActiveCollaborator,\n} from '@wordpress/core-data';\nimport { useSelect } from '@wordpress/data';\nimport { useEffect, useState } from '@wordpress/element';\nimport { store as preferencesStore } from '@wordpress/preferences';\nimport type { ResolvedSelection } from '@wordpress/core-data';\n\nimport { unlock } from '../../lock-unlock';\nimport { getAvatarUrl } from './get-avatar-url';\nimport { getAvatarBorderColor } from '../collab-sidebar/utils';\nimport { computeSelectionVisual } from './compute-selection';\nimport { useDebouncedRecompute } from './use-debounced-recompute';\nimport type { SelectionRect } from './cursor-dom-utils';\n\nconst { useActiveCollaborators, useResolvedSelection } =\n\tunlock( coreDataPrivateApis );\n\nexport type { SelectionRect };\n\nexport interface CursorData {\n\tuserName: string;\n\tclientId: number;\n\tcolor: string;\n\tavatarUrl?: string;\n\tx: number;\n\ty: number;\n\theight: number;\n\tisMe?: boolean;\n\tselectionRects?: SelectionRect[];\n}\n\n/**\n * Custom hook that computes cursor positions for each remote user in the editor.\n *\n * @param overlayElement - The overlay element\n * @param blockEditorDocument - The block editor document\n * @param postId - The ID of the post\n * @param postType - The type of the post\n * @param delayMs - Milliseconds to wait before recomputing cursor positions.\n * @return An array of cursor data for rendering, and a function to trigger a delayed recompute.\n */\nexport function useRenderCursors(\n\toverlayElement: HTMLElement | null,\n\tblockEditorDocument: Document | null,\n\tpostId: number | null,\n\tpostType: string | null,\n\tdelayMs: number\n): { cursors: CursorData[]; rerenderCursorsAfterDelay: () => () => void } {\n\tconst sortedUsers = useActiveCollaborators(\n\t\tpostId ?? null,\n\t\tpostType ?? null\n\t);\n\tconst resolveSelection = useResolvedSelection(\n\t\tpostId ?? null,\n\t\tpostType ?? null\n\t);\n\n\tconst showOwnCursor = useSelect(\n\t\t( select ) =>\n\t\t\tselect( preferencesStore ).get( 'core', 'showCollaborationCursor' ),\n\t\t[]\n\t);\n\n\tconst [ cursorPositions, setCursorPositions ] = useState< CursorData[] >(\n\t\t[]\n\t);\n\n\t// Bump this counter to force the effect to re-run (e.g. after a layout shift).\n\tconst [ recomputeToken, rerenderCursorsAfterDelay ] =\n\t\tuseDebouncedRecompute( delayMs );\n\n\t// All DOM position computations live inside useEffect.\n\tuseEffect( () => {\n\t\tif ( ! overlayElement || ! blockEditorDocument ) {\n\t\t\tsetCursorPositions( [] );\n\t\t\treturn;\n\t\t}\n\n\t\t// Pre-compute the overlay rect once, same for every user.\n\t\tconst overlayRect = overlayElement.getBoundingClientRect();\n\t\tconst overlayContext = {\n\t\t\teditorDocument: blockEditorDocument,\n\t\t\toverlayRect,\n\t\t};\n\n\t\tconst results: CursorData[] = [];\n\n\t\tconst hasOtherCollaborators = sortedUsers.some(\n\t\t\t( u: ActiveCollaborator ) => ! u.isMe\n\t\t);\n\n\t\tsortedUsers.forEach( ( user: ActiveCollaborator ) => {\n\t\t\tif ( user.isMe && ( ! showOwnCursor || ! hasOtherCollaborators ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst selection = user.editorState?.selection ?? {\n\t\t\t\ttype: SelectionType.None,\n\t\t\t};\n\n\t\t\tlet start: ResolvedSelection = {\n\t\t\t\trichTextOffset: null,\n\t\t\t\tlocalClientId: null,\n\t\t\t};\n\t\t\tlet end: ResolvedSelection | undefined;\n\n\t\t\tif ( selection.type === SelectionType.Cursor ) {\n\t\t\t\ttry {\n\t\t\t\t\tstart = resolveSelection( selection );\n\t\t\t\t} catch {\n\t\t\t\t\t// Selection may reference a stale Yjs position.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tselection.type === SelectionType.SelectionInOneBlock ||\n\t\t\t\tselection.type === SelectionType.SelectionInMultipleBlocks\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\tstart = resolveSelection( {\n\t\t\t\t\t\ttype: SelectionType.Cursor,\n\t\t\t\t\t\tcursorPosition: selection.cursorStartPosition,\n\t\t\t\t\t} );\n\n\t\t\t\t\tend = resolveSelection( {\n\t\t\t\t\t\ttype: SelectionType.Cursor,\n\t\t\t\t\t\tcursorPosition: selection.cursorEndPosition,\n\t\t\t\t\t} );\n\t\t\t\t} catch {\n\t\t\t\t\t// Selection may reference a stale Yjs position.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst userName = user.collaboratorInfo.name;\n\t\t\tconst clientId = user.clientId;\n\t\t\tconst color = user.isMe\n\t\t\t\t? 'var(--wp-admin-theme-color)'\n\t\t\t\t: getAvatarBorderColor( user.collaboratorInfo.id );\n\t\t\tconst avatarUrl = getAvatarUrl( user.collaboratorInfo.avatar_urls );\n\n\t\t\tconst selectionVisual = computeSelectionVisual(\n\t\t\t\tselection,\n\t\t\t\tstart,\n\t\t\t\tend,\n\t\t\t\toverlayContext\n\t\t\t);\n\n\t\t\tif ( selectionVisual.coords ) {\n\t\t\t\tconst cursorData: CursorData = {\n\t\t\t\t\tuserName,\n\t\t\t\t\tclientId,\n\t\t\t\t\tcolor,\n\t\t\t\t\tavatarUrl,\n\t\t\t\t\tisMe: user.isMe,\n\t\t\t\t\t...selectionVisual.coords,\n\t\t\t\t};\n\n\t\t\t\tif ( selectionVisual.selectionRects ) {\n\t\t\t\t\tcursorData.selectionRects = selectionVisual.selectionRects;\n\t\t\t\t}\n\n\t\t\t\tresults.push( cursorData );\n\t\t\t}\n\t\t} );\n\n\t\tsetCursorPositions( results );\n\t}, [\n\t\tblockEditorDocument,\n\t\tresolveSelection,\n\t\toverlayElement,\n\t\tsortedUsers,\n\t\tshowOwnCursor,\n\t\trecomputeToken,\n\t] );\n\n\treturn { cursors: cursorPositions, rerenderCursorsAfterDelay };\n}\n"],
"mappings": "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",
"names": ["coreDataPrivateApis", "preferencesStore"]
}