@wordpress/editor 14.50.0 → 14.51.1-next.v.202607070741.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/build/components/collab-sidebar/format.cjs +83 -0
- package/build/components/collab-sidebar/format.cjs.map +7 -0
- package/build/components/collab-sidebar/hooks.cjs +117 -16
- package/build/components/collab-sidebar/hooks.cjs.map +2 -2
- package/build/components/collab-sidebar/index.cjs +18 -2
- package/build/components/collab-sidebar/index.cjs.map +2 -2
- package/build/components/collab-sidebar/note-highlight-styles.cjs +67 -0
- package/build/components/collab-sidebar/note-highlight-styles.cjs.map +7 -0
- package/build/components/collab-sidebar/utils.cjs +139 -0
- package/build/components/collab-sidebar/utils.cjs.map +2 -2
- package/build/components/collaborators-presence/use-collaborator-notifications.cjs +55 -30
- package/build/components/collaborators-presence/use-collaborator-notifications.cjs.map +2 -2
- package/build/components/commands/index.cjs +99 -3
- package/build/components/commands/index.cjs.map +2 -2
- package/build/components/document-bar/index.cjs +28 -34
- package/build/components/document-bar/index.cjs.map +2 -2
- package/build/components/entities-saved-states/index.cjs +1 -4
- package/build/components/entities-saved-states/index.cjs.map +2 -2
- package/build/components/media-categories/index.cjs +92 -6
- package/build/components/media-categories/index.cjs.map +2 -2
- package/build/components/page-attributes/parent.cjs +0 -1
- package/build/components/page-attributes/parent.cjs.map +2 -2
- package/build/components/post-author/combobox.cjs +0 -1
- package/build/components/post-author/combobox.cjs.map +2 -2
- package/build/components/post-author/select.cjs +0 -1
- package/build/components/post-author/select.cjs.map +2 -2
- package/build/components/post-preview-button/index.cjs +29 -2
- package/build/components/post-preview-button/index.cjs.map +2 -2
- package/build/components/post-publish-panel/maybe-upload-media.cjs +15 -26
- package/build/components/post-publish-panel/maybe-upload-media.cjs.map +3 -3
- package/build/components/post-revisions-preview/revisions-slider.cjs +3 -1
- package/build/components/post-revisions-preview/revisions-slider.cjs.map +2 -2
- package/build/components/post-taxonomies/flat-term-selector.cjs +0 -1
- package/build/components/post-taxonomies/flat-term-selector.cjs.map +2 -2
- package/build/components/post-taxonomies/hierarchical-term-selector.cjs +0 -2
- package/build/components/post-taxonomies/hierarchical-term-selector.cjs.map +2 -2
- package/build/components/post-template/classic-theme.cjs +0 -1
- package/build/components/post-template/classic-theme.cjs.map +2 -2
- package/build/components/preferences-modal/index.cjs +54 -28
- package/build/components/preferences-modal/index.cjs.map +2 -2
- package/build/components/preview-dropdown/index.cjs +3 -3
- package/build/components/preview-dropdown/index.cjs.map +2 -2
- package/build/components/provider/use-block-editor-settings.cjs +23 -3
- package/build/components/provider/use-block-editor-settings.cjs.map +3 -3
- package/build/components/sidebar/dataform-post-summary.cjs +5 -1
- package/build/components/sidebar/dataform-post-summary.cjs.map +3 -3
- package/build/components/sidebar/index.cjs +29 -38
- package/build/components/sidebar/index.cjs.map +3 -3
- package/build/components/sidebar/post-summary.cjs +0 -13
- package/build/components/sidebar/post-summary.cjs.map +3 -3
- package/build/dataviews/store/private-actions.cjs +24 -19
- package/build/dataviews/store/private-actions.cjs.map +2 -2
- package/build/store/private-actions.cjs +5 -2
- package/build/store/private-actions.cjs.map +2 -2
- package/build/utils/media-sideload-from-url/index.cjs +63 -0
- package/build/utils/media-sideload-from-url/index.cjs.map +7 -0
- package/build-module/components/collab-sidebar/format.mjs +57 -0
- package/build-module/components/collab-sidebar/format.mjs.map +7 -0
- package/build-module/components/collab-sidebar/hooks.mjs +121 -16
- package/build-module/components/collab-sidebar/hooks.mjs.map +2 -2
- package/build-module/components/collab-sidebar/index.mjs +21 -5
- package/build-module/components/collab-sidebar/index.mjs.map +2 -2
- package/build-module/components/collab-sidebar/note-highlight-styles.mjs +41 -0
- package/build-module/components/collab-sidebar/note-highlight-styles.mjs.map +7 -0
- package/build-module/components/collab-sidebar/utils.mjs +133 -0
- package/build-module/components/collab-sidebar/utils.mjs.map +2 -2
- package/build-module/components/collaborators-presence/use-collaborator-notifications.mjs +55 -30
- package/build-module/components/collaborators-presence/use-collaborator-notifications.mjs.map +2 -2
- package/build-module/components/commands/index.mjs +99 -3
- package/build-module/components/commands/index.mjs.map +2 -2
- package/build-module/components/document-bar/index.mjs +28 -34
- package/build-module/components/document-bar/index.mjs.map +2 -2
- package/build-module/components/entities-saved-states/index.mjs +1 -4
- package/build-module/components/entities-saved-states/index.mjs.map +2 -2
- package/build-module/components/media-categories/index.mjs +93 -7
- package/build-module/components/media-categories/index.mjs.map +2 -2
- package/build-module/components/page-attributes/parent.mjs +0 -1
- package/build-module/components/page-attributes/parent.mjs.map +2 -2
- package/build-module/components/post-author/combobox.mjs +0 -1
- package/build-module/components/post-author/combobox.mjs.map +2 -2
- package/build-module/components/post-author/select.mjs +0 -1
- package/build-module/components/post-author/select.mjs.map +2 -2
- package/build-module/components/post-preview-button/index.mjs +29 -2
- package/build-module/components/post-preview-button/index.mjs.map +2 -2
- package/build-module/components/post-publish-panel/maybe-upload-media.mjs +19 -27
- package/build-module/components/post-publish-panel/maybe-upload-media.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/revisions-slider.mjs +3 -1
- package/build-module/components/post-revisions-preview/revisions-slider.mjs.map +2 -2
- package/build-module/components/post-taxonomies/flat-term-selector.mjs +0 -1
- package/build-module/components/post-taxonomies/flat-term-selector.mjs.map +2 -2
- package/build-module/components/post-taxonomies/hierarchical-term-selector.mjs +0 -2
- package/build-module/components/post-taxonomies/hierarchical-term-selector.mjs.map +2 -2
- package/build-module/components/post-template/classic-theme.mjs +0 -1
- package/build-module/components/post-template/classic-theme.mjs.map +2 -2
- package/build-module/components/preferences-modal/index.mjs +54 -28
- package/build-module/components/preferences-modal/index.mjs.map +2 -2
- package/build-module/components/preview-dropdown/index.mjs +3 -3
- package/build-module/components/preview-dropdown/index.mjs.map +2 -2
- package/build-module/components/provider/use-block-editor-settings.mjs +23 -3
- package/build-module/components/provider/use-block-editor-settings.mjs.map +2 -2
- package/build-module/components/sidebar/dataform-post-summary.mjs +6 -2
- package/build-module/components/sidebar/dataform-post-summary.mjs.map +2 -2
- package/build-module/components/sidebar/index.mjs +29 -42
- package/build-module/components/sidebar/index.mjs.map +2 -2
- package/build-module/components/sidebar/post-summary.mjs +0 -13
- package/build-module/components/sidebar/post-summary.mjs.map +2 -2
- package/build-module/dataviews/store/private-actions.mjs +26 -19
- package/build-module/dataviews/store/private-actions.mjs.map +2 -2
- package/build-module/store/private-actions.mjs +5 -2
- package/build-module/store/private-actions.mjs.map +2 -2
- package/build-module/utils/media-sideload-from-url/index.mjs +32 -0
- package/build-module/utils/media-sideload-from-url/index.mjs.map +7 -0
- package/build-style/style-rtl.css +65 -33
- package/build-style/style.css +65 -33
- package/build-types/components/collab-sidebar/format.d.ts +17 -0
- package/build-types/components/collab-sidebar/format.d.ts.map +1 -0
- package/build-types/components/collab-sidebar/hooks.d.ts.map +1 -1
- package/build-types/components/collab-sidebar/index.d.ts.map +1 -1
- package/build-types/components/collab-sidebar/note-highlight-styles.d.ts +31 -0
- package/build-types/components/collab-sidebar/note-highlight-styles.d.ts.map +1 -0
- package/build-types/components/collab-sidebar/utils.d.ts +82 -2
- package/build-types/components/collab-sidebar/utils.d.ts.map +1 -1
- package/build-types/components/collaborators-presence/use-collaborator-notifications.d.ts.map +1 -1
- package/build-types/components/commands/index.d.ts.map +1 -1
- package/build-types/components/document-bar/index.d.ts.map +1 -1
- package/build-types/components/entities-saved-states/index.d.ts.map +1 -1
- package/build-types/components/media-categories/index.d.ts +12 -3
- package/build-types/components/media-categories/index.d.ts.map +1 -1
- package/build-types/components/page-attributes/parent.d.ts.map +1 -1
- package/build-types/components/post-author/combobox.d.ts.map +1 -1
- package/build-types/components/post-author/select.d.ts.map +1 -1
- package/build-types/components/post-preview-button/index.d.ts.map +1 -1
- package/build-types/components/post-publish-panel/maybe-upload-media.d.ts.map +1 -1
- package/build-types/components/post-revisions-preview/revisions-slider.d.ts.map +1 -1
- package/build-types/components/post-taxonomies/flat-term-selector.d.ts.map +1 -1
- package/build-types/components/post-taxonomies/hierarchical-term-selector.d.ts.map +1 -1
- package/build-types/components/post-template/classic-theme.d.ts.map +1 -1
- package/build-types/components/provider/use-block-editor-settings.d.ts.map +1 -1
- package/build-types/components/sidebar/dataform-post-summary.d.ts.map +1 -1
- package/build-types/components/sidebar/index.d.ts.map +1 -1
- package/build-types/components/sidebar/post-summary.d.ts.map +1 -1
- package/build-types/dataviews/store/private-actions.d.ts.map +1 -1
- package/build-types/store/private-actions.d.ts.map +1 -1
- package/build-types/store/selectors.d.ts.map +1 -1
- package/build-types/utils/get-template-part-icon.d.ts.map +1 -1
- package/build-types/utils/media-sideload-from-url/index.d.ts +18 -0
- package/build-types/utils/media-sideload-from-url/index.d.ts.map +1 -0
- package/package.json +48 -46
- package/src/components/collab-sidebar/format.js +72 -0
- package/src/components/collab-sidebar/hooks.js +203 -22
- package/src/components/collab-sidebar/index.js +19 -5
- package/src/components/collab-sidebar/note-highlight-styles.js +83 -0
- package/src/components/collab-sidebar/style.scss +9 -0
- package/src/components/collab-sidebar/test/note-highlight-styles.js +106 -0
- package/src/components/collab-sidebar/test/utils.js +471 -0
- package/src/components/collab-sidebar/utils.js +225 -2
- package/src/components/collaborators-presence/test/use-collaborator-notifications.ts +265 -262
- package/src/components/collaborators-presence/use-collaborator-notifications.ts +70 -34
- package/src/components/commands/index.js +142 -0
- package/src/components/document-bar/index.js +0 -9
- package/src/components/document-bar/style.scss +0 -17
- package/src/components/entities-saved-states/index.js +1 -5
- package/src/components/header/style.scss +7 -3
- package/src/components/media-categories/index.js +160 -6
- package/src/components/media-categories/test/index.js +202 -0
- package/src/components/page-attributes/parent.js +0 -1
- package/src/components/post-author/combobox.js +0 -1
- package/src/components/post-author/select.js +0 -1
- package/src/components/post-preview-button/index.js +68 -2
- package/src/components/post-publish-panel/maybe-upload-media.js +25 -30
- package/src/components/post-publish-panel/style.scss +1 -1
- package/src/components/post-publish-panel/test/__snapshots__/index.js.snap +11 -127
- package/src/components/post-revisions-preview/revisions-slider.js +4 -1
- package/src/components/post-revisions-preview/test/block-diff.js +2 -2
- package/src/components/post-taxonomies/flat-term-selector.js +0 -1
- package/src/components/post-taxonomies/hierarchical-term-selector.js +0 -2
- package/src/components/post-template/classic-theme.js +0 -1
- package/src/components/preferences-modal/index.js +40 -22
- package/src/components/preferences-modal/test/index.js +83 -0
- package/src/components/preview-dropdown/index.js +3 -3
- package/src/components/provider/use-block-editor-settings.js +42 -1
- package/src/components/sidebar/dataform-post-summary.js +13 -1
- package/src/components/sidebar/index.js +32 -47
- package/src/components/sidebar/post-summary.js +0 -17
- package/src/dataviews/store/private-actions.ts +30 -26
- package/src/store/private-actions.js +6 -2
- package/src/utils/media-sideload-from-url/index.js +54 -0
- package/src/utils/media-sideload-from-url/test/index.js +162 -0
- package/build/components/post-publish-panel/media-util.cjs +0 -72
- package/build/components/post-publish-panel/media-util.cjs.map +0 -7
- package/build-module/components/post-publish-panel/media-util.mjs +0 -46
- package/build-module/components/post-publish-panel/media-util.mjs.map +0 -7
- package/build-types/components/post-publish-panel/media-util.d.ts +0 -20
- package/build-types/components/post-publish-panel/media-util.d.ts.map +0 -1
- package/src/components/post-publish-panel/media-util.js +0 -87
- package/src/components/post-publish-panel/test/media-util.js +0 -118
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/collab-sidebar/hooks.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { __ } from '@wordpress/i18n';\nimport {\n\tuseState,\n\tuseEffect,\n\tuseMemo,\n\tuseSyncExternalStore,\n} from '@wordpress/element';\nimport { useEntityRecords, store as coreStore } from '@wordpress/core-data';\nimport { useDispatch, useRegistry, useSelect } from '@wordpress/data';\nimport {\n\tstore as blockEditorStore,\n\tprivateApis as blockEditorPrivateApis,\n} from '@wordpress/block-editor';\nimport { store as noticesStore } from '@wordpress/notices';\nimport { getScrollContainer } from '@wordpress/dom';\nimport { decodeEntities } from '@wordpress/html-entities';\nimport { store as interfaceStore } from '@wordpress/interface';\n\n/**\n * Internal dependencies\n */\nimport { store as editorStore } from '../../store';\nimport { FLOATING_NOTES_SIDEBAR } from './constants';\nimport { unlock } from '../../lock-unlock';\nimport { createBoardStore } from './board-store';\nimport {\n\tcalculateNotePositions,\n\tgetNoteIdsFromMetadata,\n\taddNoteIdToMetadata,\n\tremoveNoteIdFromMetadata,\n} from './utils';\n\nconst { cleanEmptyObject } = unlock( blockEditorPrivateApis );\n\nexport function useNoteThreads( postId ) {\n\tconst queryArgs = {\n\t\tpost: postId,\n\t\ttype: 'note',\n\t\tstatus: 'all',\n\t\tper_page: -1,\n\t};\n\n\tconst { records: threads } = useEntityRecords(\n\t\t'root',\n\t\t'comment',\n\t\tqueryArgs,\n\t\t{ enabled: !! postId && typeof postId === 'number' }\n\t);\n\n\tconst { getBlockAttributes } = useSelect( blockEditorStore );\n\tconst { clientIds } = useSelect( ( select ) => {\n\t\tconst { getClientIdsWithDescendants } = select( blockEditorStore );\n\t\treturn {\n\t\t\tclientIds: getClientIdsWithDescendants(),\n\t\t};\n\t}, [] );\n\n\t// Process notes to build the tree structure.\n\tconst { notes, unresolvedNotes } = useMemo( () => {\n\t\tif ( ! threads || threads.length === 0 ) {\n\t\t\treturn { notes: [], unresolvedNotes: [] };\n\t\t}\n\n\t\t// Single pass over clientIds builds the forward map and reverse lookup\n\t\t// together. getNoteIdsFromMetadata returns numeric ids, matching the\n\t\t// types returned by the comments REST endpoint.\n\t\tconst blocksWithNotes = {};\n\t\tconst clientIdByNoteId = new Map();\n\t\tfor ( const clientId of clientIds ) {\n\t\t\tconst metadata = getBlockAttributes( clientId )?.metadata;\n\t\t\tconst noteIds = getNoteIdsFromMetadata( metadata );\n\t\t\tif ( noteIds.length > 0 ) {\n\t\t\t\tblocksWithNotes[ clientId ] = noteIds;\n\t\t\t\tfor ( const noteId of noteIds ) {\n\t\t\t\t\tclientIdByNoteId.set( noteId, clientId );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Materialize threads; collect roots; replies linked in a second pass\n\t\t// via unshift to invert order (matches prior reverse semantics).\n\t\tconst threadsById = new Map();\n\t\tconst rootThreads = [];\n\t\tfor ( const item of threads ) {\n\t\t\tconst thread = {\n\t\t\t\t...item,\n\t\t\t\treply: [],\n\t\t\t\tblockClientId:\n\t\t\t\t\titem.parent === 0\n\t\t\t\t\t\t? clientIdByNoteId.get( item.id ) ?? null\n\t\t\t\t\t\t: null,\n\t\t\t};\n\t\t\tthreadsById.set( item.id, thread );\n\t\t\tif ( item.parent === 0 ) {\n\t\t\t\trootThreads.push( thread );\n\t\t\t}\n\t\t}\n\t\tfor ( const item of threads ) {\n\t\t\tif ( item.parent !== 0 ) {\n\t\t\t\tthreadsById\n\t\t\t\t\t.get( item.parent )\n\t\t\t\t\t?.reply.unshift( threadsById.get( item.id ) );\n\t\t\t}\n\t\t}\n\n\t\tif ( rootThreads.length === 0 ) {\n\t\t\treturn { notes: [], unresolvedNotes: [] };\n\t\t}\n\n\t\t// Single partition over notes-in-block-order. Each block can have\n\t\t// multiple note IDs, so iterate the flattened list.\n\t\tconst unresolved = [];\n\t\tconst resolved = [];\n\t\tfor ( const noteIds of Object.values( blocksWithNotes ) ) {\n\t\t\tfor ( const noteId of noteIds ) {\n\t\t\t\tconst thread = threadsById.get( noteId );\n\t\t\t\tif ( ! thread ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif ( thread.status === 'hold' ) {\n\t\t\t\t\tunresolved.push( thread );\n\t\t\t\t} else if ( thread.status === 'approved' ) {\n\t\t\t\t\tresolved.push( thread );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Orphans: root threads without a linked block. They only need to come last.\n\t\tconst orphans = rootThreads.filter(\n\t\t\t( thread ) => ! thread.blockClientId\n\t\t);\n\n\t\treturn {\n\t\t\tnotes: [ ...unresolved, ...resolved, ...orphans ],\n\t\t\tunresolvedNotes: unresolved,\n\t\t};\n\t}, [ clientIds, threads, getBlockAttributes ] );\n\n\treturn {\n\t\tnotes,\n\t\tunresolvedNotes,\n\t};\n}\n\nexport function useNoteActions() {\n\tconst { createNotice } = useDispatch( noticesStore );\n\tconst { saveEntityRecord, deleteEntityRecord } = useDispatch( coreStore );\n\tconst { getCurrentPostId } = useSelect( editorStore );\n\tconst { getBlockAttributes, getSelectedBlockClientId } =\n\t\tuseSelect( blockEditorStore );\n\tconst { updateBlockAttributes } = useDispatch( blockEditorStore );\n\n\tconst onError = ( error ) => {\n\t\tconst errorMessage =\n\t\t\terror.message && error.code !== 'unknown_error'\n\t\t\t\t? decodeEntities( error.message )\n\t\t\t\t: __( 'An error occurred while performing an update.' );\n\t\tcreateNotice( 'error', errorMessage, {\n\t\t\ttype: 'snackbar',\n\t\t\tisDismissible: true,\n\t\t} );\n\t};\n\n\tconst onCreate = async ( { content, parent } ) => {\n\t\ttry {\n\t\t\tconst savedRecord = await saveEntityRecord(\n\t\t\t\t'root',\n\t\t\t\t'comment',\n\t\t\t\t{\n\t\t\t\t\tpost: getCurrentPostId(),\n\t\t\t\t\tcontent,\n\t\t\t\t\tstatus: 'hold',\n\t\t\t\t\ttype: 'note',\n\t\t\t\t\tparent: parent || 0,\n\t\t\t\t},\n\t\t\t\t{ throwOnError: true }\n\t\t\t);\n\n\t\t\t// If it's a top-level note, update the block attributes with the note id.\n\t\t\t// Read-modify-write on metadata is racy under concurrent edits:\n\t\t\t// two near-simultaneous adds against the same base will each write\n\t\t\t// a 2-element array and the later write wins, dropping the other\n\t\t\t// id. Tracking issue: https://github.com/WordPress/gutenberg/issues/74751.\n\t\t\tif ( ! parent && savedRecord?.id ) {\n\t\t\t\tconst clientId = getSelectedBlockClientId();\n\t\t\t\tif ( ! clientId ) {\n\t\t\t\t\treturn savedRecord;\n\t\t\t\t}\n\t\t\t\tconst metadata = getBlockAttributes( clientId )?.metadata;\n\t\t\t\tconst updatedMetadata = addNoteIdToMetadata(\n\t\t\t\t\tmetadata,\n\t\t\t\t\tsavedRecord.id\n\t\t\t\t);\n\t\t\t\tupdateBlockAttributes( clientId, {\n\t\t\t\t\tmetadata: cleanEmptyObject( updatedMetadata ),\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tcreateNotice(\n\t\t\t\t'snackbar',\n\t\t\t\tparent ? __( 'Reply added.' ) : __( 'Note added.' ),\n\t\t\t\t{\n\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\tisDismissible: true,\n\t\t\t\t}\n\t\t\t);\n\t\t\treturn savedRecord;\n\t\t} catch ( error ) {\n\t\t\tonError( error );\n\t\t}\n\t};\n\n\tconst onEdit = async ( { id, content, status } ) => {\n\t\tconst messageType = status ? status : 'updated';\n\t\tconst messages = {\n\t\t\tapproved: __( 'Note marked as resolved.' ),\n\t\t\thold: __( 'Note reopened.' ),\n\t\t\tupdated: __( 'Note updated.' ),\n\t\t};\n\n\t\ttry {\n\t\t\t// For resolution or reopen actions, create a new note with metadata.\n\t\t\tif ( status === 'approved' || status === 'hold' ) {\n\t\t\t\t// First, update the thread status.\n\t\t\t\tawait saveEntityRecord(\n\t\t\t\t\t'root',\n\t\t\t\t\t'comment',\n\t\t\t\t\t{\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tthrowOnError: true,\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\t// Then create a new note with the metadata.\n\t\t\t\tconst newNoteData = {\n\t\t\t\t\tpost: getCurrentPostId(),\n\t\t\t\t\tcontent: content || '', // Empty content for resolve, content for reopen.\n\t\t\t\t\ttype: 'note',\n\t\t\t\t\tstatus,\n\t\t\t\t\tparent: id,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\t_wp_note_status:\n\t\t\t\t\t\t\tstatus === 'approved' ? 'resolved' : 'reopen',\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tawait saveEntityRecord( 'root', 'comment', newNoteData, {\n\t\t\t\t\tthrowOnError: true,\n\t\t\t\t} );\n\t\t\t} else {\n\t\t\t\tconst updateData = {\n\t\t\t\t\tid,\n\t\t\t\t\tcontent,\n\t\t\t\t\tstatus,\n\t\t\t\t};\n\n\t\t\t\tawait saveEntityRecord( 'root', 'comment', updateData, {\n\t\t\t\t\tthrowOnError: true,\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tcreateNotice(\n\t\t\t\t'snackbar',\n\t\t\t\tmessages[ messageType ] ?? __( 'Note updated.' ),\n\t\t\t\t{\n\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\tisDismissible: true,\n\t\t\t\t}\n\t\t\t);\n\t\t} catch ( error ) {\n\t\t\tonError( error );\n\t\t}\n\t};\n\n\tconst onDelete = async ( note ) => {\n\t\ttry {\n\t\t\tawait deleteEntityRecord( 'root', 'comment', note.id, undefined, {\n\t\t\t\tthrowOnError: true,\n\t\t\t} );\n\n\t\t\tif ( ! note.parent ) {\n\t\t\t\t// Use blockClientId if available, otherwise fall back to selected block.\n\t\t\t\tconst clientId =\n\t\t\t\t\tnote.blockClientId || getSelectedBlockClientId();\n\t\t\t\tif ( ! clientId ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst metadata = getBlockAttributes( clientId )?.metadata;\n\t\t\t\tconst updatedMetadata = removeNoteIdFromMetadata(\n\t\t\t\t\tmetadata,\n\t\t\t\t\tnote.id\n\t\t\t\t);\n\t\t\t\tupdateBlockAttributes( clientId, {\n\t\t\t\t\tmetadata: cleanEmptyObject( updatedMetadata ),\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tcreateNotice( 'snackbar', __( 'Note deleted.' ), {\n\t\t\t\ttype: 'snackbar',\n\t\t\t\tisDismissible: true,\n\t\t\t} );\n\t\t} catch ( error ) {\n\t\t\tonError( error );\n\t\t}\n\t};\n\n\treturn { onCreate, onEdit, onDelete };\n}\n\nexport function useEnableFloatingSidebar( enabled = false ) {\n\tconst registry = useRegistry();\n\tuseEffect( () => {\n\t\tif ( ! enabled ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { getActiveComplementaryArea } =\n\t\t\tregistry.select( interfaceStore );\n\t\tconst { disableComplementaryArea, enableComplementaryArea } =\n\t\t\tregistry.dispatch( interfaceStore );\n\n\t\tconst unsubscribe = registry.subscribe( () => {\n\t\t\t// Return `null` to indicate the user hid the complementary area.\n\t\t\tif ( getActiveComplementaryArea( 'core' ) === null ) {\n\t\t\t\tenableComplementaryArea( 'core', FLOATING_NOTES_SIDEBAR );\n\t\t\t}\n\t\t} );\n\n\t\treturn () => {\n\t\t\tunsubscribe();\n\t\t\tif (\n\t\t\t\tgetActiveComplementaryArea( 'core' ) === FLOATING_NOTES_SIDEBAR\n\t\t\t) {\n\t\t\t\tdisableComplementaryArea( 'core', FLOATING_NOTES_SIDEBAR );\n\t\t\t}\n\t\t};\n\t}, [ enabled, registry ] );\n}\n\nexport function useFloatingBoard( {\n\tthreads,\n\tselectedNoteId,\n\tisFloating,\n\tsidebarRef,\n} ) {\n\tconst [ notePositions, setNotePositions ] = useState( {} );\n\tconst [ store ] = useState( createBoardStore );\n\n\tconst heights = useSyncExternalStore( store.subscribe, store.getSnapshot );\n\n\t// Notes are positioned in canvas content-space; CSS inherits\n\t// `--canvas-scroll` to translate each thread in sync with the canvas.\n\tuseEffect( () => {\n\t\tif ( ! isFloating || ! sidebarRef?.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst panel = sidebarRef.current;\n\t\tconst blockEl = store.getFirstBlockElement();\n\t\t// Climb to the block-list root so nested scroll containers\n\t\t// (e.g. a Group with overflow:auto) don't shadow the canvas.\n\t\tconst rootEl = blockEl?.closest( '.is-root-container' ) ?? blockEl;\n\t\tconst canvas = rootEl ? getScrollContainer( rootEl ) : null;\n\n\t\tconst applyScroll = () => {\n\t\t\tpanel.style.setProperty(\n\t\t\t\t'--canvas-scroll',\n\t\t\t\t`${ -( canvas?.scrollTop ?? 0 ) }px`\n\t\t\t);\n\t\t};\n\n\t\t// Recalc is deferred to a rAF; back-to-back updates collapse into one paint.\n\t\tconst rafId = window.requestAnimationFrame( () => {\n\t\t\tconst result = calculateNotePositions( {\n\t\t\t\tthreads,\n\t\t\t\tselectedNoteId,\n\t\t\t\tblockRects: store.getBlockRects(),\n\t\t\t\theights,\n\t\t\t\tscrollTop: canvas?.scrollTop ?? 0,\n\t\t\t} );\n\n\t\t\tsetNotePositions( result.positions );\n\t\t\tapplyScroll();\n\t\t} );\n\n\t\t// Root scrolling elements (documentElement/body) don't fire scroll\n\t\t// on themselves; capture on the window catches them in either canvas.\n\t\tconst view = canvas?.ownerDocument?.defaultView;\n\t\tconst listenerOptions = { passive: true, capture: true };\n\t\tview?.addEventListener( 'scroll', applyScroll, listenerOptions );\n\n\t\treturn () => {\n\t\t\twindow.cancelAnimationFrame( rafId );\n\t\t\tview?.removeEventListener( 'scroll', applyScroll, listenerOptions );\n\t\t};\n\t}, [ sidebarRef, heights, isFloating, selectedNoteId, store, threads ] );\n\n\treturn {\n\t\tnotePositions,\n\t\tregisterThread: store.registerThread,\n\t\tunregisterThread: store.unregisterThread,\n\t};\n}\n"],
|
|
5
|
-
"mappings": ";AAGA,SAAS,UAAU;AACnB;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,kBAAkB,SAAS,iBAAiB;AACrD,SAAS,aAAa,aAAa,iBAAiB;AACpD;AAAA,EACC,SAAS;AAAA,EACT,eAAe;AAAA,OACT;AACP,SAAS,SAAS,oBAAoB;AACtC,SAAS,0BAA0B;AACnC,SAAS,sBAAsB;AAC/B,SAAS,SAAS,sBAAsB;
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { __ } from '@wordpress/i18n';\nimport {\n\tuseState,\n\tuseEffect,\n\tuseMemo,\n\tuseSyncExternalStore,\n} from '@wordpress/element';\nimport { useEntityRecords, store as coreStore } from '@wordpress/core-data';\nimport { useDispatch, useRegistry, useSelect } from '@wordpress/data';\nimport {\n\tstore as blockEditorStore,\n\tprivateApis as blockEditorPrivateApis,\n} from '@wordpress/block-editor';\nimport { store as noticesStore } from '@wordpress/notices';\nimport { getScrollContainer } from '@wordpress/dom';\nimport { decodeEntities } from '@wordpress/html-entities';\nimport { store as interfaceStore } from '@wordpress/interface';\nimport { RichTextData, create } from '@wordpress/rich-text';\n\n/**\n * Internal dependencies\n */\nimport { store as editorStore } from '../../store';\nimport { FLOATING_NOTES_SIDEBAR } from './constants';\nimport { unlock } from '../../lock-unlock';\nimport { createBoardStore } from './board-store';\nimport { NOTE_FORMAT_NAME } from './format';\nimport {\n\tapplyNoteFormat,\n\tcalculateNotePositions,\n\tfindNoteInBlock,\n\tgetInlineMarkerStart,\n\tgetNoteIdsFromMetadata,\n\taddNoteIdToMetadata,\n\tremoveNoteFormat,\n\tremoveNoteIdFromMetadata,\n} from './utils';\n\nconst { cleanEmptyObject } = unlock( blockEditorPrivateApis );\n\nexport function useNoteThreads( postId ) {\n\tconst queryArgs = {\n\t\tpost: postId,\n\t\ttype: 'note',\n\t\tstatus: 'all',\n\t\tper_page: -1,\n\t};\n\n\tconst { records: threads } = useEntityRecords(\n\t\t'root',\n\t\t'comment',\n\t\tqueryArgs,\n\t\t{ enabled: !! postId && typeof postId === 'number' }\n\t);\n\n\tconst { getBlockAttributes } = useSelect( blockEditorStore );\n\tconst { clientIds } = useSelect( ( select ) => {\n\t\tconst { getClientIdsWithDescendants } = select( blockEditorStore );\n\t\treturn {\n\t\t\tclientIds: getClientIdsWithDescendants(),\n\t\t};\n\t}, [] );\n\n\t// Process notes to build the tree structure.\n\tconst { notes, unresolvedNotes } = useMemo( () => {\n\t\tif ( ! threads || threads.length === 0 ) {\n\t\t\treturn { notes: [], unresolvedNotes: [] };\n\t\t}\n\n\t\t// Single pass over clientIds builds the forward map and reverse lookup\n\t\t// together. getNoteIdsFromMetadata returns numeric ids, matching the\n\t\t// types returned by the comments REST endpoint.\n\t\tconst blocksWithNotes = {};\n\t\tconst clientIdByNoteId = new Map();\n\t\tfor ( const clientId of clientIds ) {\n\t\t\tconst metadata = getBlockAttributes( clientId )?.metadata;\n\t\t\tconst noteIds = getNoteIdsFromMetadata( metadata );\n\t\t\tif ( noteIds.length > 0 ) {\n\t\t\t\tblocksWithNotes[ clientId ] = noteIds;\n\t\t\t\tfor ( const noteId of noteIds ) {\n\t\t\t\t\tclientIdByNoteId.set( noteId, clientId );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Materialize threads; collect roots; replies linked in a second pass\n\t\t// via unshift to invert order (matches prior reverse semantics).\n\t\tconst threadsById = new Map();\n\t\tconst rootThreads = [];\n\t\tfor ( const item of threads ) {\n\t\t\tconst thread = {\n\t\t\t\t...item,\n\t\t\t\treply: [],\n\t\t\t\tblockClientId:\n\t\t\t\t\titem.parent === 0\n\t\t\t\t\t\t? clientIdByNoteId.get( item.id ) ?? null\n\t\t\t\t\t\t: null,\n\t\t\t};\n\t\t\tthreadsById.set( item.id, thread );\n\t\t\tif ( item.parent === 0 ) {\n\t\t\t\trootThreads.push( thread );\n\t\t\t}\n\t\t}\n\t\tfor ( const item of threads ) {\n\t\t\tif ( item.parent !== 0 ) {\n\t\t\t\tthreadsById\n\t\t\t\t\t.get( item.parent )\n\t\t\t\t\t?.reply.unshift( threadsById.get( item.id ) );\n\t\t\t}\n\t\t}\n\n\t\tif ( rootThreads.length === 0 ) {\n\t\t\treturn { notes: [], unresolvedNotes: [] };\n\t\t}\n\n\t\t// Order within a block: block-level notes (no inline anchor) come\n\t\t// first as the \"overall comment\", then inline notes ascending by\n\t\t// marker start offset. Ties (rare; two markers at the same offset)\n\t\t// fall back to creation order via thread id. Blocks themselves are\n\t\t// already iterated in document order above.\n\t\tconst unresolved = [];\n\t\tconst resolved = [];\n\t\tfor ( const [ clientId, noteIds ] of Object.entries(\n\t\t\tblocksWithNotes\n\t\t) ) {\n\t\t\tconst attributes = getBlockAttributes( clientId );\n\t\t\tconst orderedThreads = noteIds\n\t\t\t\t.map( ( noteId ) => {\n\t\t\t\t\tconst thread = threadsById.get( noteId );\n\t\t\t\t\tif ( ! thread ) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tthread,\n\t\t\t\t\t\tstart: getInlineMarkerStart( thread, attributes ),\n\t\t\t\t\t};\n\t\t\t\t} )\n\t\t\t\t.filter( Boolean )\n\t\t\t\t.sort( ( a, b ) => {\n\t\t\t\t\tif ( a.start !== b.start ) {\n\t\t\t\t\t\treturn a.start - b.start;\n\t\t\t\t\t}\n\t\t\t\t\treturn a.thread.id - b.thread.id;\n\t\t\t\t} );\n\t\t\tfor ( const { thread } of orderedThreads ) {\n\t\t\t\tif ( thread.status === 'hold' ) {\n\t\t\t\t\tunresolved.push( thread );\n\t\t\t\t} else if ( thread.status === 'approved' ) {\n\t\t\t\t\tresolved.push( thread );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Orphans: root threads without a linked block. They only need to come last.\n\t\tconst orphans = rootThreads.filter(\n\t\t\t( thread ) => ! thread.blockClientId\n\t\t);\n\n\t\treturn {\n\t\t\tnotes: [ ...unresolved, ...resolved, ...orphans ],\n\t\t\tunresolvedNotes: unresolved,\n\t\t};\n\t}, [ clientIds, threads, getBlockAttributes ] );\n\n\treturn {\n\t\tnotes,\n\t\tunresolvedNotes,\n\t};\n}\n\n/**\n * Read an inline selection from block-editor selection state, returning\n * normalized anchor data when a non-collapsed selection sits inside a single\n * rich-text attribute. Returns null for block-level or collapsed selections.\n *\n * @param {Function} getSelectionStart Block-editor selector.\n * @param {Function} getSelectionEnd Block-editor selector.\n * @return {?Object} { clientId, attributeKey, start, end } or null.\n */\nfunction readInlineSelection( getSelectionStart, getSelectionEnd ) {\n\tconst start = getSelectionStart();\n\tconst end = getSelectionEnd();\n\tif (\n\t\t! start?.clientId ||\n\t\tstart.clientId !== end.clientId ||\n\t\t! start.attributeKey ||\n\t\tstart.offset === undefined ||\n\t\tend.offset === undefined ||\n\t\tstart.offset === end.offset\n\t) {\n\t\treturn null;\n\t}\n\t// Normalize direction so callers don't have to think about reversed ranges.\n\tconst [ startOffset, endOffset ] =\n\t\tstart.offset < end.offset\n\t\t\t? [ start.offset, end.offset ]\n\t\t\t: [ end.offset, start.offset ];\n\treturn {\n\t\tclientId: start.clientId,\n\t\tattributeKey: start.attributeKey,\n\t\tstart: startOffset,\n\t\tend: endOffset,\n\t};\n}\n\n/**\n * Wrap a rich-text range with a core/note marker. Returns a new\n * RichTextData ready to write back into block attributes, or null when the\n * incoming value isn't a rich-text instance (legacy/string attributes).\n *\n * @param {*} value Existing block attribute value.\n * @param {number} id New note id to embed as `data-id`.\n * @param {number} start Range start offset.\n * @param {number} end Range end offset.\n * @return {?RichTextData} Wrapped value or null when the attribute isn't rich text.\n */\nfunction wrapInlineNote( value, id, start, end ) {\n\tif ( ! ( value instanceof RichTextData ) ) {\n\t\treturn null;\n\t}\n\tconst record = applyNoteFormat(\n\t\tcreate( { html: value.toHTMLString() } ),\n\t\t{ type: NOTE_FORMAT_NAME, attributes: { 'data-id': String( id ) } },\n\t\tstart,\n\t\tend\n\t);\n\t// Round-trip through HTML to normalise format references (applyNoteFormat\n\t// leaves them un-normalised) so the stored value matches a fresh reload.\n\treturn RichTextData.fromHTMLString(\n\t\tnew RichTextData( record ).toHTMLString()\n\t);\n}\n\n/**\n * Strip a note's inline `core/note` marker from whichever block holds it, if\n * any, so a deleted or resolved note's highlight does not linger in the content.\n * No-op for block-level notes (those carry no marker). Used by the resolve path,\n * which only knows the note id; the delete path strips the marker inline since\n * it already has the block.\n *\n * @param {number} noteId Note id whose marker to remove.\n * @param {Function} getClientIdsWithDescendants Block-editor selector.\n * @param {Function} getBlockAttributes Block-editor selector.\n * @param {Function} updateBlockAttributes Block-editor action.\n */\nfunction clearInlineNoteMarker(\n\tnoteId,\n\tgetClientIdsWithDescendants,\n\tgetBlockAttributes,\n\tupdateBlockAttributes\n) {\n\tfor ( const clientId of getClientIdsWithDescendants() ) {\n\t\tconst attributes = getBlockAttributes( clientId );\n\t\tconst found = findNoteInBlock( attributes, noteId );\n\t\tif ( ! found ) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst next = removeNoteFormat(\n\t\t\tattributes[ found.attributeKey ],\n\t\t\tnoteId\n\t\t);\n\t\tif ( next ) {\n\t\t\tupdateBlockAttributes( clientId, { [ found.attributeKey ]: next } );\n\t\t}\n\t\treturn;\n\t}\n}\n\nexport function useNoteActions() {\n\tconst { createNotice } = useDispatch( noticesStore );\n\tconst { saveEntityRecord, deleteEntityRecord } = useDispatch( coreStore );\n\tconst { getCurrentPostId } = useSelect( editorStore );\n\tconst {\n\t\tgetBlockAttributes,\n\t\tgetClientIdsWithDescendants,\n\t\tgetSelectedBlockClientId,\n\t\tgetSelectionStart,\n\t\tgetSelectionEnd,\n\t} = useSelect( blockEditorStore );\n\tconst { updateBlockAttributes } = useDispatch( blockEditorStore );\n\n\tconst onError = ( error ) => {\n\t\tconst errorMessage =\n\t\t\terror.message && error.code !== 'unknown_error'\n\t\t\t\t? decodeEntities( error.message )\n\t\t\t\t: __( 'An error occurred while performing an update.' );\n\t\tcreateNotice( 'error', errorMessage, {\n\t\t\ttype: 'snackbar',\n\t\t\tisDismissible: true,\n\t\t} );\n\t};\n\n\tconst onCreate = async ( { content, parent } ) => {\n\t\ttry {\n\t\t\t// Capture inline selection *before* the async save: focus may shift\n\t\t\t// during the round-trip and the editor's stored selection can\n\t\t\t// collapse if the user clicks elsewhere. The selection drives the\n\t\t\t// in-content marker written below, which is the note's only anchor.\n\t\t\tconst inlineSelection = ! parent\n\t\t\t\t? readInlineSelection( getSelectionStart, getSelectionEnd )\n\t\t\t\t: null;\n\n\t\t\tconst savedRecord = await saveEntityRecord(\n\t\t\t\t'root',\n\t\t\t\t'comment',\n\t\t\t\t{\n\t\t\t\t\tpost: getCurrentPostId(),\n\t\t\t\t\tcontent,\n\t\t\t\t\tstatus: 'hold',\n\t\t\t\t\ttype: 'note',\n\t\t\t\t\tparent: parent || 0,\n\t\t\t\t},\n\t\t\t\t{ throwOnError: true }\n\t\t\t);\n\n\t\t\t// If it's a top-level note, update the block attributes with the note id.\n\t\t\t// Read-modify-write on metadata is racy under concurrent edits:\n\t\t\t// two near-simultaneous adds against the same base will each write\n\t\t\t// a 2-element array and the later write wins, dropping the other\n\t\t\t// id. Tracking issue: https://github.com/WordPress/gutenberg/issues/74751.\n\t\t\tif ( ! parent && savedRecord?.id ) {\n\t\t\t\tconst clientId =\n\t\t\t\t\tinlineSelection?.clientId || getSelectedBlockClientId();\n\t\t\t\tif ( ! clientId ) {\n\t\t\t\t\treturn savedRecord;\n\t\t\t\t}\n\t\t\t\tconst attributes = getBlockAttributes( clientId );\n\t\t\t\tconst metadata = attributes?.metadata;\n\t\t\t\tconst updatedMetadata = addNoteIdToMetadata(\n\t\t\t\t\tmetadata,\n\t\t\t\t\tsavedRecord.id\n\t\t\t\t);\n\t\t\t\tconst newAttributes = {\n\t\t\t\t\tmetadata: cleanEmptyObject( updatedMetadata ),\n\t\t\t\t};\n\n\t\t\t\t// Inline path: also wrap the selected text with a core/note\n\t\t\t\t// marker so the anchor survives later edits.\n\t\t\t\tif ( inlineSelection ) {\n\t\t\t\t\tconst wrapped = wrapInlineNote(\n\t\t\t\t\t\tattributes?.[ inlineSelection.attributeKey ],\n\t\t\t\t\t\tsavedRecord.id,\n\t\t\t\t\t\tinlineSelection.start,\n\t\t\t\t\t\tinlineSelection.end\n\t\t\t\t\t);\n\t\t\t\t\tif ( wrapped ) {\n\t\t\t\t\t\tnewAttributes[ inlineSelection.attributeKey ] = wrapped;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tupdateBlockAttributes( clientId, newAttributes );\n\t\t\t}\n\n\t\t\tcreateNotice(\n\t\t\t\t'snackbar',\n\t\t\t\tparent ? __( 'Reply added.' ) : __( 'Note added.' ),\n\t\t\t\t{\n\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\tisDismissible: true,\n\t\t\t\t}\n\t\t\t);\n\t\t\treturn savedRecord;\n\t\t} catch ( error ) {\n\t\t\tonError( error );\n\t\t}\n\t};\n\n\tconst onEdit = async ( { id, content, status } ) => {\n\t\tconst messageType = status ? status : 'updated';\n\t\tconst messages = {\n\t\t\tapproved: __( 'Note marked as resolved.' ),\n\t\t\thold: __( 'Note reopened.' ),\n\t\t\tupdated: __( 'Note updated.' ),\n\t\t};\n\n\t\ttry {\n\t\t\t// For resolution or reopen actions, create a new note with metadata.\n\t\t\tif ( status === 'approved' || status === 'hold' ) {\n\t\t\t\t// First, update the thread status.\n\t\t\t\tawait saveEntityRecord(\n\t\t\t\t\t'root',\n\t\t\t\t\t'comment',\n\t\t\t\t\t{\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tthrowOnError: true,\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\t// Then create a new note with the metadata.\n\t\t\t\tconst newNoteData = {\n\t\t\t\t\tpost: getCurrentPostId(),\n\t\t\t\t\tcontent: content || '', // Empty content for resolve, content for reopen.\n\t\t\t\t\ttype: 'note',\n\t\t\t\t\tstatus,\n\t\t\t\t\tparent: id,\n\t\t\t\t\tmeta: {\n\t\t\t\t\t\t_wp_note_status:\n\t\t\t\t\t\t\tstatus === 'approved' ? 'resolved' : 'reopen',\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tawait saveEntityRecord( 'root', 'comment', newNoteData, {\n\t\t\t\t\tthrowOnError: true,\n\t\t\t\t} );\n\n\t\t\t\t// Resolving a note drops its inline highlight: strip the marker\n\t\t\t\t// so the note falls back to a block-level note in the content.\n\t\t\t\tif ( status === 'approved' ) {\n\t\t\t\t\tclearInlineNoteMarker(\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tgetClientIdsWithDescendants,\n\t\t\t\t\t\tgetBlockAttributes,\n\t\t\t\t\t\tupdateBlockAttributes\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst updateData = {\n\t\t\t\t\tid,\n\t\t\t\t\tcontent,\n\t\t\t\t\tstatus,\n\t\t\t\t};\n\n\t\t\t\tawait saveEntityRecord( 'root', 'comment', updateData, {\n\t\t\t\t\tthrowOnError: true,\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tcreateNotice(\n\t\t\t\t'snackbar',\n\t\t\t\tmessages[ messageType ] ?? __( 'Note updated.' ),\n\t\t\t\t{\n\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\tisDismissible: true,\n\t\t\t\t}\n\t\t\t);\n\t\t} catch ( error ) {\n\t\t\tonError( error );\n\t\t}\n\t};\n\n\tconst onDelete = async ( note ) => {\n\t\ttry {\n\t\t\tawait deleteEntityRecord( 'root', 'comment', note.id, undefined, {\n\t\t\t\tthrowOnError: true,\n\t\t\t} );\n\n\t\t\tif ( ! note.parent ) {\n\t\t\t\t// Use blockClientId if available, otherwise fall back to selected block.\n\t\t\t\tconst clientId =\n\t\t\t\t\tnote.blockClientId || getSelectedBlockClientId();\n\t\t\t\tif ( ! clientId ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst attributes = getBlockAttributes( clientId );\n\t\t\t\tconst newAttributes = {\n\t\t\t\t\tmetadata: cleanEmptyObject(\n\t\t\t\t\t\tremoveNoteIdFromMetadata(\n\t\t\t\t\t\t\tattributes?.metadata,\n\t\t\t\t\t\t\tnote.id\n\t\t\t\t\t\t)\n\t\t\t\t\t),\n\t\t\t\t};\n\t\t\t\t// Strip the inline marker too (if any) so the deleted note's\n\t\t\t\t// highlight doesn't linger in the content. Folded into the same\n\t\t\t\t// attribute update so it's a single undo step.\n\t\t\t\tconst found = findNoteInBlock( attributes, note.id );\n\t\t\t\tif ( found ) {\n\t\t\t\t\tconst next = removeNoteFormat(\n\t\t\t\t\t\tattributes[ found.attributeKey ],\n\t\t\t\t\t\tnote.id\n\t\t\t\t\t);\n\t\t\t\t\tif ( next ) {\n\t\t\t\t\t\tnewAttributes[ found.attributeKey ] = next;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tupdateBlockAttributes( clientId, newAttributes );\n\t\t\t}\n\n\t\t\tcreateNotice( 'snackbar', __( 'Note deleted.' ), {\n\t\t\t\ttype: 'snackbar',\n\t\t\t\tisDismissible: true,\n\t\t\t} );\n\t\t} catch ( error ) {\n\t\t\tonError( error );\n\t\t}\n\t};\n\n\treturn { onCreate, onEdit, onDelete };\n}\n\nexport function useEnableFloatingSidebar( enabled = false ) {\n\tconst registry = useRegistry();\n\tuseEffect( () => {\n\t\tif ( ! enabled ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { getActiveComplementaryArea } =\n\t\t\tregistry.select( interfaceStore );\n\t\tconst { disableComplementaryArea, enableComplementaryArea } =\n\t\t\tregistry.dispatch( interfaceStore );\n\n\t\tconst unsubscribe = registry.subscribe( () => {\n\t\t\t// Return `null` to indicate the user hid the complementary area.\n\t\t\tif ( getActiveComplementaryArea( 'core' ) === null ) {\n\t\t\t\tenableComplementaryArea( 'core', FLOATING_NOTES_SIDEBAR );\n\t\t\t}\n\t\t} );\n\n\t\treturn () => {\n\t\t\tunsubscribe();\n\t\t\tif (\n\t\t\t\tgetActiveComplementaryArea( 'core' ) === FLOATING_NOTES_SIDEBAR\n\t\t\t) {\n\t\t\t\tdisableComplementaryArea( 'core', FLOATING_NOTES_SIDEBAR );\n\t\t\t}\n\t\t};\n\t}, [ enabled, registry ] );\n}\n\nexport function useFloatingBoard( {\n\tthreads,\n\tselectedNoteId,\n\tisFloating,\n\tsidebarRef,\n} ) {\n\tconst [ notePositions, setNotePositions ] = useState( {} );\n\tconst [ store ] = useState( createBoardStore );\n\n\tconst heights = useSyncExternalStore( store.subscribe, store.getSnapshot );\n\n\t// Notes are positioned in canvas content-space; CSS inherits\n\t// `--canvas-scroll` to translate each thread in sync with the canvas.\n\tuseEffect( () => {\n\t\tif ( ! isFloating || ! sidebarRef?.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst panel = sidebarRef.current;\n\t\tconst blockEl = store.getFirstBlockElement();\n\t\t// Climb to the block-list root so nested scroll containers\n\t\t// (e.g. a Group with overflow:auto) don't shadow the canvas.\n\t\tconst rootEl = blockEl?.closest( '.is-root-container' ) ?? blockEl;\n\t\tconst canvas = rootEl ? getScrollContainer( rootEl ) : null;\n\n\t\tconst applyScroll = () => {\n\t\t\tpanel.style.setProperty(\n\t\t\t\t'--canvas-scroll',\n\t\t\t\t`${ -( canvas?.scrollTop ?? 0 ) }px`\n\t\t\t);\n\t\t};\n\n\t\t// Recalc is deferred to a rAF; back-to-back updates collapse into one paint.\n\t\tconst rafId = window.requestAnimationFrame( () => {\n\t\t\tconst result = calculateNotePositions( {\n\t\t\t\tthreads,\n\t\t\t\tselectedNoteId,\n\t\t\t\tblockRects: store.getBlockRects(),\n\t\t\t\theights,\n\t\t\t\tscrollTop: canvas?.scrollTop ?? 0,\n\t\t\t} );\n\n\t\t\tsetNotePositions( result.positions );\n\t\t\tapplyScroll();\n\t\t} );\n\n\t\t// Root scrolling elements (documentElement/body) don't fire scroll\n\t\t// on themselves; capture on the window catches them in either canvas.\n\t\tconst view = canvas?.ownerDocument?.defaultView;\n\t\tconst listenerOptions = { passive: true, capture: true };\n\t\tview?.addEventListener( 'scroll', applyScroll, listenerOptions );\n\n\t\treturn () => {\n\t\t\twindow.cancelAnimationFrame( rafId );\n\t\t\tview?.removeEventListener( 'scroll', applyScroll, listenerOptions );\n\t\t};\n\t}, [ sidebarRef, heights, isFloating, selectedNoteId, store, threads ] );\n\n\treturn {\n\t\tnotePositions,\n\t\tregisterThread: store.registerThread,\n\t\tunregisterThread: store.unregisterThread,\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// packages/editor/src/components/collab-sidebar/index.js
|
|
2
2
|
import { __ } from "@wordpress/i18n";
|
|
3
|
-
import {
|
|
4
|
-
import { useRef } from "@wordpress/element";
|
|
3
|
+
import { useDispatch, useSelect } from "@wordpress/data";
|
|
4
|
+
import { useEffect, useRef } from "@wordpress/element";
|
|
5
5
|
import { useViewportMatch } from "@wordpress/compose";
|
|
6
6
|
import { useShortcut } from "@wordpress/keyboard-shortcuts";
|
|
7
7
|
import { comment as commentIcon } from "@wordpress/icons";
|
|
8
8
|
import { store as blockEditorStore } from "@wordpress/block-editor";
|
|
9
9
|
import { store as interfaceStore } from "@wordpress/interface";
|
|
10
10
|
import { store as preferencesStore } from "@wordpress/preferences";
|
|
11
|
+
import { registerFormatType, unregisterFormatType } from "@wordpress/rich-text";
|
|
11
12
|
import PluginSidebar from "../plugin-sidebar/index.mjs";
|
|
12
13
|
import {
|
|
13
14
|
ALL_NOTES_SIDEBAR,
|
|
@@ -18,13 +19,21 @@ import { Notes } from "./notes.mjs";
|
|
|
18
19
|
import { store as editorStore } from "../../store/index.mjs";
|
|
19
20
|
import { AddNoteMenuItem } from "./add-note-menu-item.mjs";
|
|
20
21
|
import { NoteAvatarIndicator } from "./note-indicator-toolbar.mjs";
|
|
22
|
+
import { NoteHighlightStyles } from "./note-highlight-styles.mjs";
|
|
21
23
|
import { useGlobalStyles } from "../global-styles/index.mjs";
|
|
22
|
-
import {
|
|
24
|
+
import { useEnableFloatingSidebar, useNoteThreads } from "./hooks.mjs";
|
|
23
25
|
import { getNoteIdsFromMetadata, pickPrimaryNote } from "./utils.mjs";
|
|
26
|
+
import { NOTE_FORMAT_NAME, noteFormat } from "./format.mjs";
|
|
24
27
|
import PostTypeSupportCheck from "../post-type-support-check/index.mjs";
|
|
25
28
|
import { unlock } from "../../lock-unlock.mjs";
|
|
26
29
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
27
30
|
function NotesSidebar({ postId }) {
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
registerFormatType(NOTE_FORMAT_NAME, noteFormat);
|
|
33
|
+
return () => {
|
|
34
|
+
unregisterFormatType(NOTE_FORMAT_NAME);
|
|
35
|
+
};
|
|
36
|
+
}, []);
|
|
28
37
|
const { getActiveComplementaryArea } = useSelect(interfaceStore);
|
|
29
38
|
const { enableComplementaryArea } = useDispatch(interfaceStore);
|
|
30
39
|
const { toggleBlockSpotlight, selectBlock } = unlock(
|
|
@@ -49,7 +58,7 @@ function NotesSidebar({ postId }) {
|
|
|
49
58
|
isDistractionFree: get("core", "distractionFree")
|
|
50
59
|
};
|
|
51
60
|
}, []);
|
|
52
|
-
const
|
|
61
|
+
const selectedNoteId = useSelect(
|
|
53
62
|
(select) => unlock(select(editorStore)).getSelectedNote(),
|
|
54
63
|
[]
|
|
55
64
|
);
|
|
@@ -57,7 +66,7 @@ function NotesSidebar({ postId }) {
|
|
|
57
66
|
const showFloatingSidebar = isLargeViewport;
|
|
58
67
|
const showAllNotesSidebar = notes.length > 0 || !showFloatingSidebar;
|
|
59
68
|
useEnableFloatingSidebar(
|
|
60
|
-
showFloatingSidebar && (unresolvedNotes.length > 0 ||
|
|
69
|
+
showFloatingSidebar && (unresolvedNotes.length > 0 || selectedNoteId !== void 0)
|
|
61
70
|
);
|
|
62
71
|
async function focusNote({
|
|
63
72
|
targetClientId,
|
|
@@ -120,6 +129,13 @@ function NotesSidebar({ postId }) {
|
|
|
120
129
|
return /* @__PURE__ */ jsx(AddNoteMenuItem, { isDistractionFree: true });
|
|
121
130
|
}
|
|
122
131
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
132
|
+
/* @__PURE__ */ jsx(
|
|
133
|
+
NoteHighlightStyles,
|
|
134
|
+
{
|
|
135
|
+
threads: unresolvedNotes,
|
|
136
|
+
selectedId: selectedNoteId
|
|
137
|
+
}
|
|
138
|
+
),
|
|
123
139
|
!!currentThread && /* @__PURE__ */ jsx(
|
|
124
140
|
NoteAvatarIndicator,
|
|
125
141
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/collab-sidebar/index.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { __ } from '@wordpress/i18n';\nimport {
|
|
5
|
-
"mappings": ";AAGA,SAAS,UAAU;AACnB,SAAS,
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { __ } from '@wordpress/i18n';\nimport { useDispatch, useSelect } from '@wordpress/data';\nimport { useEffect, useRef } from '@wordpress/element';\nimport { useViewportMatch } from '@wordpress/compose';\nimport { useShortcut } from '@wordpress/keyboard-shortcuts';\nimport { comment as commentIcon } from '@wordpress/icons';\nimport { store as blockEditorStore } from '@wordpress/block-editor';\nimport { store as interfaceStore } from '@wordpress/interface';\nimport { store as preferencesStore } from '@wordpress/preferences';\nimport { registerFormatType, unregisterFormatType } from '@wordpress/rich-text';\n\n/**\n * Internal dependencies\n */\nimport PluginSidebar from '../plugin-sidebar';\nimport {\n\tALL_NOTES_SIDEBAR,\n\tFLOATING_NOTES_SIDEBAR,\n\tSIDEBARS,\n} from './constants';\nimport { Notes } from './notes';\nimport { store as editorStore } from '../../store';\nimport { AddNoteMenuItem } from './add-note-menu-item';\nimport { NoteAvatarIndicator } from './note-indicator-toolbar';\nimport { NoteHighlightStyles } from './note-highlight-styles';\nimport { useGlobalStyles } from '../global-styles';\nimport { useEnableFloatingSidebar, useNoteThreads } from './hooks';\nimport { getNoteIdsFromMetadata, pickPrimaryNote } from './utils';\nimport { NOTE_FORMAT_NAME, noteFormat } from './format';\nimport PostTypeSupportCheck from '../post-type-support-check';\nimport { unlock } from '../../lock-unlock';\n\nfunction NotesSidebar( { postId } ) {\n\tuseEffect( () => {\n\t\tregisterFormatType( NOTE_FORMAT_NAME, noteFormat );\n\t\treturn () => {\n\t\t\tunregisterFormatType( NOTE_FORMAT_NAME );\n\t\t};\n\t}, [] );\n\n\tconst { getActiveComplementaryArea } = useSelect( interfaceStore );\n\tconst { enableComplementaryArea } = useDispatch( interfaceStore );\n\tconst { toggleBlockSpotlight, selectBlock } = unlock(\n\t\tuseDispatch( blockEditorStore )\n\t);\n\tconst { selectNote } = unlock( useDispatch( editorStore ) );\n\tconst isLargeViewport = useViewportMatch( 'medium' );\n\tconst sidebarRef = useRef( null );\n\n\tconst { clientId, noteId, isClassicBlock } = useSelect( ( select ) => {\n\t\tconst { getBlockAttributes, getSelectedBlockClientId, getBlockName } =\n\t\t\tselect( blockEditorStore );\n\t\tconst _clientId = getSelectedBlockClientId();\n\t\treturn {\n\t\t\tclientId: _clientId,\n\t\t\tnoteId: _clientId\n\t\t\t\t? getBlockAttributes( _clientId )?.metadata?.noteId\n\t\t\t\t: null,\n\t\t\tisClassicBlock: _clientId\n\t\t\t\t? getBlockName( _clientId ) === 'core/freeform'\n\t\t\t\t: false,\n\t\t};\n\t}, [] );\n\n\tconst blockNoteIds = getNoteIdsFromMetadata( { noteId } );\n\tconst { isDistractionFree } = useSelect( ( select ) => {\n\t\tconst { get } = select( preferencesStore );\n\t\treturn {\n\t\t\tisDistractionFree: get( 'core', 'distractionFree' ),\n\t\t};\n\t}, [] );\n\tconst selectedNoteId = useSelect(\n\t\t( select ) => unlock( select( editorStore ) ).getSelectedNote(),\n\t\t[]\n\t);\n\n\tconst { notes, unresolvedNotes } = useNoteThreads( postId );\n\n\t// Only enable the floating sidebar for large viewports.\n\tconst showFloatingSidebar = isLargeViewport;\n\t// Fallback to \"All notes\" sidebar on smaller viewports.\n\tconst showAllNotesSidebar = notes.length > 0 || ! showFloatingSidebar;\n\tuseEnableFloatingSidebar(\n\t\tshowFloatingSidebar &&\n\t\t\t( unresolvedNotes.length > 0 || selectedNoteId !== undefined )\n\t);\n\n\tasync function focusNote( {\n\t\ttargetClientId,\n\t\tnoteId: targetNoteId,\n\t\tisApproved,\n\t} ) {\n\t\tif ( ! targetClientId ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst prevArea = await getActiveComplementaryArea( 'core' );\n\t\tif ( isApproved ) {\n\t\t\tenableComplementaryArea( 'core', ALL_NOTES_SIDEBAR );\n\t\t} else if ( ! SIDEBARS.includes( prevArea ) || ! showAllNotesSidebar ) {\n\t\t\tenableComplementaryArea(\n\t\t\t\t'core',\n\t\t\t\tshowFloatingSidebar ? FLOATING_NOTES_SIDEBAR : ALL_NOTES_SIDEBAR\n\t\t\t);\n\t\t}\n\n\t\tconst currentArea = await getActiveComplementaryArea( 'core' );\n\t\t// Bail out if the current active area is not one of note sidebars.\n\t\tif ( ! SIDEBARS.includes( currentArea ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// A special case for the List View, where block selection isn't required to trigger an action.\n\t\t// The action won't do anything if the block is already selected.\n\t\tselectBlock( targetClientId, null );\n\t\ttoggleBlockSpotlight( targetClientId, true );\n\t\tselectNote( targetNoteId, { focus: true } );\n\t}\n\n\tfunction openNoteForBlock( targetClientId ) {\n\t\t// A block can carry multiple threads; surface the most relevant.\n\t\tconst blockThreads = notes.filter(\n\t\t\t( thread ) => thread.blockClientId === targetClientId\n\t\t);\n\t\tconst target = pickPrimaryNote( blockThreads );\n\t\treturn focusNote( {\n\t\t\ttargetClientId,\n\t\t\tnoteId: target?.id ?? 'new',\n\t\t\tisApproved: target?.status === 'approved',\n\t\t} );\n\t}\n\n\tfunction addNewNoteForBlock( targetClientId ) {\n\t\treturn focusNote( {\n\t\t\ttargetClientId,\n\t\t\tnoteId: 'new',\n\t\t\tisApproved: false,\n\t\t} );\n\t}\n\n\tuseShortcut(\n\t\t'core/editor/new-note',\n\t\t( event ) => {\n\t\t\tevent.preventDefault();\n\t\t\taddNewNoteForBlock( clientId );\n\t\t},\n\t\t{\n\t\t\tisDisabled: isDistractionFree || isClassicBlock || ! clientId,\n\t\t}\n\t);\n\n\t// Get the global styles to set the background color of the sidebar.\n\tconst { merged: GlobalStyles } = useGlobalStyles();\n\tconst backgroundColor = GlobalStyles?.styles?.color?.background;\n\n\t// Surface one thread for the avatar indicator.\n\tconst currentThreads =\n\t\tblockNoteIds.length > 0\n\t\t\t? notes.filter( ( thread ) => blockNoteIds.includes( thread.id ) )\n\t\t\t: [];\n\tconst currentThread = pickPrimaryNote( currentThreads );\n\n\tif ( isDistractionFree ) {\n\t\treturn <AddNoteMenuItem isDistractionFree />;\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<NoteHighlightStyles\n\t\t\t\tthreads={ unresolvedNotes }\n\t\t\t\tselectedId={ selectedNoteId }\n\t\t\t/>\n\t\t\t{ !! currentThread && (\n\t\t\t\t<NoteAvatarIndicator\n\t\t\t\t\tnote={ currentThread }\n\t\t\t\t\tonClick={ () => openNoteForBlock( clientId ) }\n\t\t\t\t/>\n\t\t\t) }\n\t\t\t<AddNoteMenuItem\n\t\t\t\tonClick={ ( menuClientId ) =>\n\t\t\t\t\taddNewNoteForBlock( menuClientId )\n\t\t\t\t}\n\t\t\t/>\n\t\t\t{ showAllNotesSidebar && (\n\t\t\t\t<PluginSidebar\n\t\t\t\t\tidentifier={ ALL_NOTES_SIDEBAR }\n\t\t\t\t\tname={ ALL_NOTES_SIDEBAR }\n\t\t\t\t\ttitle={ __( 'All notes' ) }\n\t\t\t\t\theader={\n\t\t\t\t\t\t<h2 className=\"interface-complementary-area-header__title\">\n\t\t\t\t\t\t\t{ __( 'All notes' ) }\n\t\t\t\t\t\t</h2>\n\t\t\t\t\t}\n\t\t\t\t\ticon={ commentIcon }\n\t\t\t\t\tcloseLabel={ __( 'Close Notes' ) }\n\t\t\t\t>\n\t\t\t\t\t<Notes notes={ notes } sidebarRef={ sidebarRef } />\n\t\t\t\t</PluginSidebar>\n\t\t\t) }\n\t\t\t{ isLargeViewport && (\n\t\t\t\t<PluginSidebar\n\t\t\t\t\tisPinnable={ false }\n\t\t\t\t\theader={ false }\n\t\t\t\t\tidentifier={ FLOATING_NOTES_SIDEBAR }\n\t\t\t\t\tclassName=\"editor-collab-sidebar\"\n\t\t\t\t\theaderClassName=\"editor-collab-sidebar__header\"\n\t\t\t\t\tbackgroundColor={ backgroundColor }\n\t\t\t\t>\n\t\t\t\t\t<Notes\n\t\t\t\t\t\tnotes={ unresolvedNotes }\n\t\t\t\t\t\tsidebarRef={ sidebarRef }\n\t\t\t\t\t\tstyles={ { backgroundColor } }\n\t\t\t\t\t\tisFloating\n\t\t\t\t\t/>\n\t\t\t\t</PluginSidebar>\n\t\t\t) }\n\t\t</>\n\t);\n}\n\nexport default function NotesSidebarContainer() {\n\tconst { postId, editorMode, revisionsMode } = useSelect( ( select ) => {\n\t\tconst { getCurrentPostId, getEditorMode, isRevisionsMode } = unlock(\n\t\t\tselect( editorStore )\n\t\t);\n\t\treturn {\n\t\t\tpostId: getCurrentPostId(),\n\t\t\teditorMode: getEditorMode(),\n\t\t\trevisionsMode: isRevisionsMode(),\n\t\t};\n\t}, [] );\n\n\tif ( ! postId || typeof postId !== 'number' ) {\n\t\treturn null;\n\t}\n\n\t// Hide Notes sidebar for Code Editor and in-editor revision mode.\n\tif ( editorMode === 'text' || revisionsMode ) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<PostTypeSupportCheck supportKeys=\"editor.notes\">\n\t\t\t<NotesSidebar postId={ postId } />\n\t\t</PostTypeSupportCheck>\n\t);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// packages/editor/src/components/collab-sidebar/note-highlight-styles.js
|
|
2
|
+
import { useMemo } from "@wordpress/element";
|
|
3
|
+
import { useStyleOverride } from "@wordpress/block-editor";
|
|
4
|
+
import { getAvatarBorderColor } from "./utils.mjs";
|
|
5
|
+
var REST_ALPHA = "40";
|
|
6
|
+
var ACTIVE_ALPHA = "80";
|
|
7
|
+
var BASE_RESET = "mark.wp-note{background-color:transparent;color:inherit;}";
|
|
8
|
+
function buildHighlightCss(threads, selectedId = null) {
|
|
9
|
+
const rules = [BASE_RESET];
|
|
10
|
+
for (const thread of threads ?? []) {
|
|
11
|
+
if (!thread?.id) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const color = getAvatarBorderColor(thread.author ?? 0);
|
|
15
|
+
const escapedId = String(thread.id).replace(/["\\]/g, "\\$&");
|
|
16
|
+
const sel = `mark.wp-note[data-id="${escapedId}"]`;
|
|
17
|
+
rules.push(`${sel}{background-color:${color}${REST_ALPHA};}`);
|
|
18
|
+
rules.push(
|
|
19
|
+
`${sel}:hover,${sel}:focus-within{background-color:${color}${ACTIVE_ALPHA};}`
|
|
20
|
+
);
|
|
21
|
+
if (selectedId && String(selectedId) === String(thread.id)) {
|
|
22
|
+
rules.push(
|
|
23
|
+
`${sel}{background-color:${color}${ACTIVE_ALPHA};}`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return rules.join("");
|
|
28
|
+
}
|
|
29
|
+
function NoteHighlightStyles({ threads, selectedId }) {
|
|
30
|
+
const css = useMemo(
|
|
31
|
+
() => buildHighlightCss(threads, selectedId),
|
|
32
|
+
[threads, selectedId]
|
|
33
|
+
);
|
|
34
|
+
useStyleOverride({ id: "core-note-highlights", css });
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
NoteHighlightStyles,
|
|
39
|
+
buildHighlightCss
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=note-highlight-styles.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/components/collab-sidebar/note-highlight-styles.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { useMemo } from '@wordpress/element';\nimport { useStyleOverride } from '@wordpress/block-editor';\n\n/**\n * Internal dependencies\n */\nimport { getAvatarBorderColor } from './utils';\n\n// Hex alpha suffixes for the rest / active states. Kept low so the marker\n// reads as a soft tint at rest and gets noticeably stronger when focused or\n// hovered. (0x40 ≈ 25%, 0x80 ≈ 50%.)\nconst REST_ALPHA = '40';\nconst ACTIVE_ALPHA = '80';\n\n// Reset the browser's default `<mark>` styling so the per-author rules below\n// are what readers actually see (without it, `mark` ships with a bright yellow\n// background in every browser). The `core/note` anchor marker serializes as a\n// `<mark>` and would otherwise inherit the yellow default in the editor canvas.\nconst BASE_RESET = 'mark.wp-note{background-color:transparent;color:inherit;}';\n\n/**\n * Build the CSS rule set that tints each inline-note marker with its author's\n * avatar color. Pure helper extracted so it can be unit-tested without React.\n *\n * @param {Array} threads Unresolved note threads (each with `id` and `author`).\n * @param {string|null} selectedId ID of the currently selected note, if any.\n * @return {string} A serialized CSS string targeting the in-content note markers.\n */\nexport function buildHighlightCss( threads, selectedId = null ) {\n\tconst rules = [ BASE_RESET ];\n\tfor ( const thread of threads ?? [] ) {\n\t\tif ( ! thread?.id ) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst color = getAvatarBorderColor( thread.author ?? 0 );\n\t\t// The `core/note` format serializes the id into `data-id`, so the marker\n\t\t// can be targeted directly without a separate annotation layer.\n\t\t// `thread.id` is a server comment ID (always a positive integer), but\n\t\t// escape `\"`/`\\` defensively since it composes a quoted attribute value\n\t\t// from stored data.\n\t\tconst escapedId = String( thread.id ).replace( /[\"\\\\]/g, '\\\\$&' );\n\t\tconst sel = `mark.wp-note[data-id=\"${ escapedId }\"]`;\n\t\trules.push( `${ sel }{background-color:${ color }${ REST_ALPHA };}` );\n\t\trules.push(\n\t\t\t`${ sel }:hover,${ sel }:focus-within{background-color:${ color }${ ACTIVE_ALPHA };}`\n\t\t);\n\t\tif ( selectedId && String( selectedId ) === String( thread.id ) ) {\n\t\t\trules.push(\n\t\t\t\t`${ sel }{background-color:${ color }${ ACTIVE_ALPHA };}`\n\t\t\t);\n\t\t}\n\t}\n\treturn rules.join( '' );\n}\n\n/**\n * Injects per-note background rules into the editor canvas so inline-note\n * markers carry their author's avatar color. The `core/note` format serializes\n * each marker as `<mark class=\"wp-note\" data-id=\"{noteId}\">`, which we target\n * directly.\n *\n * Uses `useStyleOverride` so the styles reach the iframed canvas; a plain\n * `<style>` element rendered in the sidebar would only affect the parent doc.\n *\n * Opacity boosts on `:hover`, `:focus-within`, and when the matching thread is\n * the editor's selected note.\n *\n * @param {Object} props\n * @param {Array} props.threads Unresolved note threads.\n * @param {string|null} [props.selectedId] ID of the currently selected note.\n * @return {null} Renders nothing; styles are applied via `useStyleOverride`.\n */\nexport function NoteHighlightStyles( { threads, selectedId } ) {\n\tconst css = useMemo(\n\t\t() => buildHighlightCss( threads, selectedId ),\n\t\t[ threads, selectedId ]\n\t);\n\tuseStyleOverride( { id: 'core-note-highlights', css } );\n\treturn null;\n}\n"],
|
|
5
|
+
"mappings": ";AAGA,SAAS,eAAe;AACxB,SAAS,wBAAwB;AAKjC,SAAS,4BAA4B;AAKrC,IAAM,aAAa;AACnB,IAAM,eAAe;AAMrB,IAAM,aAAa;AAUZ,SAAS,kBAAmB,SAAS,aAAa,MAAO;AAC/D,QAAM,QAAQ,CAAE,UAAW;AAC3B,aAAY,UAAU,WAAW,CAAC,GAAI;AACrC,QAAK,CAAE,QAAQ,IAAK;AACnB;AAAA,IACD;AACA,UAAM,QAAQ,qBAAsB,OAAO,UAAU,CAAE;AAMvD,UAAM,YAAY,OAAQ,OAAO,EAAG,EAAE,QAAS,UAAU,MAAO;AAChE,UAAM,MAAM,yBAA0B,SAAU;AAChD,UAAM,KAAM,GAAI,GAAI,qBAAsB,KAAM,GAAI,UAAW,IAAK;AACpE,UAAM;AAAA,MACL,GAAI,GAAI,UAAW,GAAI,kCAAmC,KAAM,GAAI,YAAa;AAAA,IAClF;AACA,QAAK,cAAc,OAAQ,UAAW,MAAM,OAAQ,OAAO,EAAG,GAAI;AACjE,YAAM;AAAA,QACL,GAAI,GAAI,qBAAsB,KAAM,GAAI,YAAa;AAAA,MACtD;AAAA,IACD;AAAA,EACD;AACA,SAAO,MAAM,KAAM,EAAG;AACvB;AAmBO,SAAS,oBAAqB,EAAE,SAAS,WAAW,GAAI;AAC9D,QAAM,MAAM;AAAA,IACX,MAAM,kBAAmB,SAAS,UAAW;AAAA,IAC7C,CAAE,SAAS,UAAW;AAAA,EACvB;AACA,mBAAkB,EAAE,IAAI,wBAAwB,IAAI,CAAE;AACtD,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// packages/editor/src/components/collab-sidebar/utils.js
|
|
2
2
|
import { _x } from "@wordpress/i18n";
|
|
3
|
+
import { create, RichTextData } from "@wordpress/rich-text";
|
|
3
4
|
function sanitizeNoteContent(str) {
|
|
4
5
|
return str.trim();
|
|
5
6
|
}
|
|
@@ -65,6 +66,132 @@ function addNoteIdToMetadata(metadata, noteId) {
|
|
|
65
66
|
ids.add(id);
|
|
66
67
|
return { ...metadata, noteId: [...ids] };
|
|
67
68
|
}
|
|
69
|
+
var NOTE_FORMAT_TYPE = "core/note";
|
|
70
|
+
function findNoteRange(value, noteId) {
|
|
71
|
+
if (noteId === void 0 || noteId === null) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
let html = null;
|
|
75
|
+
if (value instanceof RichTextData) {
|
|
76
|
+
html = value.toHTMLString();
|
|
77
|
+
} else if (typeof value === "string") {
|
|
78
|
+
html = value;
|
|
79
|
+
}
|
|
80
|
+
if (!html || html.indexOf("wp-note") === -1) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const target = String(noteId);
|
|
84
|
+
const record = create({ html });
|
|
85
|
+
const formats = record.formats;
|
|
86
|
+
let start = -1;
|
|
87
|
+
for (let i = 0; i < formats.length; i++) {
|
|
88
|
+
const stack = formats[i];
|
|
89
|
+
const hit = stack?.find(
|
|
90
|
+
(f) => f.type === NOTE_FORMAT_TYPE && f.attributes && f.attributes["data-id"] === target
|
|
91
|
+
);
|
|
92
|
+
if (hit) {
|
|
93
|
+
if (start === -1) {
|
|
94
|
+
start = i;
|
|
95
|
+
}
|
|
96
|
+
} else if (start !== -1) {
|
|
97
|
+
return { start, end: i };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (start !== -1) {
|
|
101
|
+
return { start, end: formats.length };
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function findNoteInBlock(attributes, noteId) {
|
|
106
|
+
if (!attributes) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
for (const attributeKey of Object.keys(attributes)) {
|
|
110
|
+
const range = findNoteRange(attributes[attributeKey], noteId);
|
|
111
|
+
if (range) {
|
|
112
|
+
return { attributeKey, start: range.start, end: range.end };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
var BLOCK_LEVEL_NOTE_START = -1;
|
|
118
|
+
function getInlineMarkerStart(thread, attributes) {
|
|
119
|
+
const found = findNoteInBlock(attributes, thread?.id);
|
|
120
|
+
return found ? found.start : BLOCK_LEVEL_NOTE_START;
|
|
121
|
+
}
|
|
122
|
+
function applyNoteFormat(record, format, start, end) {
|
|
123
|
+
const formats = record.formats.slice();
|
|
124
|
+
for (let i = start; i < end; i++) {
|
|
125
|
+
const stack = formats[i] ? formats[i].slice() : [];
|
|
126
|
+
stack.push(format);
|
|
127
|
+
formats[i] = stack;
|
|
128
|
+
}
|
|
129
|
+
const spans = /* @__PURE__ */ new Map();
|
|
130
|
+
for (let i = 0; i < formats.length; i++) {
|
|
131
|
+
const stack = formats[i];
|
|
132
|
+
if (!stack) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
for (const fmt of stack) {
|
|
136
|
+
if (fmt.type !== NOTE_FORMAT_TYPE) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const id = fmt.attributes?.["data-id"];
|
|
140
|
+
const span = spans.get(id);
|
|
141
|
+
if (span) {
|
|
142
|
+
span.end = i;
|
|
143
|
+
} else {
|
|
144
|
+
spans.set(id, { start: i, end: i });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const sizeOf = (id) => {
|
|
149
|
+
const span = spans.get(id);
|
|
150
|
+
return span ? span.end - span.start : 0;
|
|
151
|
+
};
|
|
152
|
+
for (let i = 0; i < formats.length; i++) {
|
|
153
|
+
const stack = formats[i];
|
|
154
|
+
if (!stack || stack.length < 2) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const notes = stack.filter((fmt) => fmt.type === NOTE_FORMAT_TYPE);
|
|
158
|
+
if (notes.length === 0) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (notes.length > 1) {
|
|
162
|
+
notes.sort(
|
|
163
|
+
(a, b) => sizeOf(b.attributes?.["data-id"]) - sizeOf(a.attributes?.["data-id"])
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
const others = stack.filter((fmt) => fmt.type !== NOTE_FORMAT_TYPE);
|
|
167
|
+
formats[i] = [...notes, ...others];
|
|
168
|
+
}
|
|
169
|
+
return { ...record, formats };
|
|
170
|
+
}
|
|
171
|
+
function removeNoteFormat(value, noteId) {
|
|
172
|
+
if (!(value instanceof RichTextData)) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
const target = String(noteId);
|
|
176
|
+
const record = create({ html: value.toHTMLString() });
|
|
177
|
+
let changed = false;
|
|
178
|
+
const formats = record.formats.map((stack) => {
|
|
179
|
+
if (!stack) {
|
|
180
|
+
return stack;
|
|
181
|
+
}
|
|
182
|
+
const filtered = stack.filter(
|
|
183
|
+
(format) => !(format.type === NOTE_FORMAT_TYPE && format.attributes?.["data-id"] === target)
|
|
184
|
+
);
|
|
185
|
+
if (filtered.length === stack.length) {
|
|
186
|
+
return stack;
|
|
187
|
+
}
|
|
188
|
+
changed = true;
|
|
189
|
+
return filtered.length ? filtered : void 0;
|
|
190
|
+
});
|
|
191
|
+
return changed ? RichTextData.fromHTMLString(
|
|
192
|
+
new RichTextData({ ...record, formats }).toHTMLString()
|
|
193
|
+
) : null;
|
|
194
|
+
}
|
|
68
195
|
function pickPrimaryNote(threads) {
|
|
69
196
|
return threads.find((thread) => thread.status === "hold") ?? threads[0] ?? null;
|
|
70
197
|
}
|
|
@@ -183,13 +310,19 @@ function scrollNoteThreadIntoView(noteId, container) {
|
|
|
183
310
|
});
|
|
184
311
|
}
|
|
185
312
|
export {
|
|
313
|
+
BLOCK_LEVEL_NOTE_START,
|
|
186
314
|
addNoteIdToMetadata,
|
|
315
|
+
applyNoteFormat,
|
|
187
316
|
calculateNotePositions,
|
|
317
|
+
findNoteInBlock,
|
|
318
|
+
findNoteRange,
|
|
188
319
|
focusNoteThread,
|
|
189
320
|
getAvatarBorderColor,
|
|
321
|
+
getInlineMarkerStart,
|
|
190
322
|
getNoteExcerpt,
|
|
191
323
|
getNoteIdsFromMetadata,
|
|
192
324
|
pickPrimaryNote,
|
|
325
|
+
removeNoteFormat,
|
|
193
326
|
removeNoteIdFromMetadata,
|
|
194
327
|
sanitizeNoteContent,
|
|
195
328
|
scrollNoteThreadIntoView
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/collab-sidebar/utils.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { _x } from '@wordpress/i18n';\n\n/**\n * Sanitizes a note string by removing non-printable ASCII characters.\n *\n * @param {string} str - The note string to sanitize.\n * @return {string} - The sanitized note string.\n */\nexport function sanitizeNoteContent( str ) {\n\treturn str.trim();\n}\n\nconst THREAD_ALIGN_OFFSET = -16;\nconst THREAD_GAP = 16;\nconst OVERLAP_MARGIN = 20;\n\n/**\n * Avatar border colors chosen to be visually distinct from each other and from\n * the editor's semantic UI colors (Delta E > 10 between all pairs).\n */\nconst AVATAR_BORDER_COLORS = [\n\t'#C36EFF', // Purple\n\t'#D94145', // Red\n\t'#E4780A', // Orange\n\t'#FF35EE', // Magenta\n\t'#879F11', // Olive\n\t'#46A494', // Teal\n\t'#00A2C3', // Cyan\n];\n\n/**\n * Gets the border color for an avatar based on the user ID.\n *\n * @param {number} userId - The user ID.\n * @return {string} - The border color.\n */\nexport function getAvatarBorderColor( userId ) {\n\treturn AVATAR_BORDER_COLORS[ userId % AVATAR_BORDER_COLORS.length ];\n}\n\n/**\n * Generates a note excerpt from text based on word count type and length.\n *\n * @param {string} text - The note text to generate excerpt from.\n * @param {number} excerptLength - The maximum length for the note excerpt.\n * @return {string} - The generated note excerpt.\n */\nexport function getNoteExcerpt( text, excerptLength = 10 ) {\n\tif ( ! text ) {\n\t\treturn '';\n\t}\n\n\t/*\n\t * translators: If your word count is based on single characters (e.g. East Asian characters),\n\t * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.\n\t * Do not translate into your own language.\n\t */\n\tconst wordCountType = _x( 'words', 'Word count type. Do not translate!' );\n\n\tconst rawText = text.trim();\n\tlet trimmedExcerpt = '';\n\n\tif ( wordCountType === 'words' ) {\n\t\ttrimmedExcerpt = rawText.split( ' ', excerptLength ).join( ' ' );\n\t} else if ( wordCountType === 'characters_excluding_spaces' ) {\n\t\t/*\n\t\t * 1. Split the text at the character limit,\n\t\t * then join the substrings back into one string.\n\t\t * 2. Count the number of spaces in the text\n\t\t * by comparing the lengths of the string with and without spaces.\n\t\t * 3. Add the number to the length of the visible excerpt,\n\t\t * so that the spaces are excluded from the word count.\n\t\t */\n\t\tconst textWithSpaces = rawText.split( '', excerptLength ).join( '' );\n\n\t\tconst numberOfSpaces =\n\t\t\ttextWithSpaces.length - textWithSpaces.replaceAll( ' ', '' ).length;\n\n\t\ttrimmedExcerpt = rawText\n\t\t\t.split( '', excerptLength + numberOfSpaces )\n\t\t\t.join( '' );\n\t} else if ( wordCountType === 'characters_including_spaces' ) {\n\t\ttrimmedExcerpt = rawText.split( '', excerptLength ).join( '' );\n\t}\n\n\tconst isTrimmed = trimmedExcerpt !== rawText;\n\treturn isTrimmed ? trimmedExcerpt + '…' : trimmedExcerpt;\n}\n\n/**\n * Normalizes noteId metadata to always return an array of unique numeric ids,\n * preserving insertion order. Handles both scalar (legacy, possibly\n * string-typed) and array (new) values.\n *\n * @param {Object} metadata Block metadata object\n * @return {number[]} Array of note IDs (may be empty)\n */\nexport function getNoteIdsFromMetadata( metadata ) {\n\tconst noteId = metadata?.noteId;\n\tconst raw = Array.isArray( noteId ) ? noteId : [ noteId ];\n\tconst ids = new Set();\n\tfor ( const value of raw ) {\n\t\tconst id = Number( value );\n\t\tif ( Number.isFinite( id ) && id > 0 ) {\n\t\t\tids.add( id );\n\t\t}\n\t}\n\treturn [ ...ids ];\n}\n\n/**\n * Adds a note ID to the metadata.\n * Converts scalar to array if needed, otherwise appends.\n *\n * @param {Object} metadata Existing block metadata\n * @param {number} noteId Note ID to add\n * @return {Object} Updated metadata object\n */\nexport function addNoteIdToMetadata( metadata, noteId ) {\n\tconst ids = new Set( getNoteIdsFromMetadata( metadata ) );\n\tconst id = Number( noteId );\n\tif ( ids.has( id ) ) {\n\t\treturn metadata;\n\t}\n\tids.add( id );\n\treturn { ...metadata, noteId: [ ...ids ] };\n}\n\n/**\n * Picks the most relevant thread from a list: first unresolved, else first.\n *\n * @param {Array} threads Ordered list of thread objects.\n * @return {Object|null} Selected thread or null when the list is empty.\n */\nexport function pickPrimaryNote( threads ) {\n\treturn (\n\t\tthreads.find( ( thread ) => thread.status === 'hold' ) ??\n\t\tthreads[ 0 ] ??\n\t\tnull\n\t);\n}\n\n/**\n * Removes a note ID from the metadata.\n *\n * @param {Object} metadata Existing block metadata\n * @param {number} noteId Note ID to remove\n * @return {Object} Updated metadata object\n */\nexport function removeNoteIdFromMetadata( metadata, noteId ) {\n\tconst ids = new Set( getNoteIdsFromMetadata( metadata ) );\n\tids.delete( Number( noteId ) );\n\treturn {\n\t\t...metadata,\n\t\tnoteId: ids.size > 0 ? [ ...ids ] : undefined,\n\t};\n}\n\n/**\n * Calculate final top positions for all floating note threads in the\n * editor's content coordinate space. Adjusts positions to prevent overlapping\n * by pushing threads above the selected one upward and threads below it downward.\n *\n * @param {Object} params\n * @param {Array} params.threads Ordered list of thread objects.\n * @param {string|number|undefined} params.selectedNoteId ID of the currently selected thread.\n * @param {Object<string,DOMRect>} params.blockRects Pre-read bounding rects keyed by thread ID.\n * @param {Object<string,number>} params.heights Rendered heights keyed by thread ID.\n * @param {number} params.scrollTop Current scroll offset of the editor content.\n * @return {{ positions: Object<string,number> }} Computed top positions.\n */\nexport function calculateNotePositions( {\n\tthreads,\n\tselectedNoteId,\n\tblockRects,\n\theights,\n\tscrollTop = 0,\n} ) {\n\tconst offsets = {};\n\n\tconst anchorIndex = Math.max(\n\t\t0,\n\t\tthreads.findIndex( ( thread ) => thread.id === selectedNoteId )\n\t);\n\n\tconst anchorThread = threads[ anchorIndex ];\n\n\tif ( ! anchorThread || ! blockRects[ anchorThread.id ] ) {\n\t\treturn { positions: {} };\n\t}\n\n\tconst anchorRect = blockRects[ anchorThread.id ];\n\tconst anchorTop = anchorRect.top || 0;\n\tconst anchorHeight = heights[ anchorThread.id ] || 0;\n\n\toffsets[ anchorThread.id ] = THREAD_ALIGN_OFFSET;\n\n\t// Process threads after the anchor, offsetting overlapping threads downward.\n\tlet prevAdjustedTop = anchorTop + THREAD_ALIGN_OFFSET;\n\tlet prevHeight = anchorHeight;\n\n\tfor ( let i = anchorIndex + 1; i < threads.length; i++ ) {\n\t\tconst thread = threads[ i ];\n\t\tconst threadRect = blockRects[ thread.id ];\n\t\tif ( ! threadRect ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst threadTop = threadRect.top || 0;\n\t\tconst threadHeight = heights[ thread.id ] || 0;\n\n\t\tlet offset = THREAD_ALIGN_OFFSET;\n\n\t\tconst prevBottom = prevAdjustedTop + prevHeight;\n\t\tif ( threadTop < prevBottom + THREAD_GAP ) {\n\t\t\toffset = prevBottom - threadTop + OVERLAP_MARGIN;\n\t\t}\n\n\t\toffsets[ thread.id ] = offset;\n\n\t\tprevAdjustedTop = threadTop + offset;\n\t\tprevHeight = threadHeight;\n\t}\n\n\t// Process threads before the anchor, offsetting overlapping threads upward.\n\tlet belowAdjustedTop = anchorTop + THREAD_ALIGN_OFFSET;\n\n\tfor ( let i = anchorIndex - 1; i >= 0; i-- ) {\n\t\tconst thread = threads[ i ];\n\t\tconst threadRect = blockRects[ thread.id ];\n\t\tif ( ! threadRect ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst threadTop = threadRect.top || 0;\n\t\tconst threadHeight = heights[ thread.id ] || 0;\n\n\t\tlet offset = THREAD_ALIGN_OFFSET;\n\n\t\tconst threadBottom = threadTop + threadHeight;\n\n\t\tif ( threadBottom > belowAdjustedTop ) {\n\t\t\toffset =\n\t\t\t\tbelowAdjustedTop - threadTop - threadHeight - OVERLAP_MARGIN;\n\t\t}\n\n\t\toffsets[ thread.id ] = offset;\n\n\t\tbelowAdjustedTop = threadTop + offset;\n\t}\n\n\t// blockRect.top + scrollTop is the block's absolute y within the editor's\n\t// scroll content; CSS translates each thread by -scrollTop at render time.\n\tconst positions = {};\n\tfor ( const thread of threads ) {\n\t\tconst blockRect = blockRects[ thread.id ];\n\t\tif ( blockRect && offsets[ thread.id ] !== undefined ) {\n\t\t\tpositions[ thread.id ] =\n\t\t\t\tblockRect.top + scrollTop + offsets[ thread.id ];\n\t\t}\n\t}\n\n\treturn { positions };\n}\n\n/**\n * Resolve the DOM element for a note thread once it's mounted,\n * or `null` if not found within 3 seconds.\n *\n * @param {string} noteId Note thread ID.\n * @param {?HTMLElement} container Container to search within.\n * @param {string} additionalSelector Optional descendant selector.\n * @return {Promise<HTMLElement|null>} Resolved element, or `null` on timeout.\n */\nfunction findNoteThread( noteId, container, additionalSelector ) {\n\tif ( ! container ) {\n\t\treturn Promise.resolve( null );\n\t}\n\n\t// A thread without a noteId is a new note thread.\n\tconst threadSelector =\n\t\tnoteId && noteId !== 'new'\n\t\t\t? `[role=treeitem][id=\"note-thread-${ noteId }\"]`\n\t\t\t: '[role=treeitem]:not([id])';\n\tconst selector = additionalSelector\n\t\t? `${ threadSelector } ${ additionalSelector }`\n\t\t: threadSelector;\n\n\treturn new Promise( ( resolve ) => {\n\t\tif ( container.querySelector( selector ) ) {\n\t\t\treturn resolve( container.querySelector( selector ) );\n\t\t}\n\n\t\tlet timer = null;\n\t\t// Wait for the element to be added to the DOM.\n\t\tconst observer = new window.MutationObserver( () => {\n\t\t\tif ( container.querySelector( selector ) ) {\n\t\t\t\tclearTimeout( timer );\n\t\t\t\tobserver.disconnect();\n\t\t\t\tresolve( container.querySelector( selector ) );\n\t\t\t}\n\t\t} );\n\n\t\tobserver.observe( container, { childList: true, subtree: true } );\n\n\t\t// Stop trying after 3 seconds.\n\t\ttimer = setTimeout( () => {\n\t\t\tobserver.disconnect();\n\t\t\tresolve( null );\n\t\t}, 3000 );\n\t} );\n}\n\n/**\n * Focus a note thread (or a descendant) and scroll it into view.\n *\n * @param {string} noteId Note thread ID.\n * @param {?HTMLElement} container Container to search within.\n * @param {string} additionalSelector Optional descendant selector.\n */\nexport function focusNoteThread( noteId, container, additionalSelector ) {\n\treturn findNoteThread( noteId, container, additionalSelector ).then(\n\t\t( element ) => {\n\t\t\tif ( ! element ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telement.focus();\n\t\t\telement.scrollIntoView( { block: 'nearest' } );\n\t\t}\n\t);\n}\n\n/**\n * Scroll a note thread into view without changing focus.\n *\n * @param {string} noteId Note thread ID.\n * @param {?HTMLElement} container Container to search within.\n */\nexport function scrollNoteThreadIntoView( noteId, container ) {\n\treturn findNoteThread( noteId, container ).then( ( element ) => {\n\t\telement?.scrollIntoView( { block: 'nearest' } );\n\t} );\n}\n"],
|
|
5
|
-
"mappings": ";AAGA,SAAS,UAAU;
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { _x } from '@wordpress/i18n';\nimport { create, RichTextData } from '@wordpress/rich-text';\n\n/**\n * Sanitizes a note string by trimming leading and trailing whitespace.\n *\n * @param {string} str - The note string to sanitize.\n * @return {string} - The sanitized note string.\n */\nexport function sanitizeNoteContent( str ) {\n\treturn str.trim();\n}\n\nconst THREAD_ALIGN_OFFSET = -16;\nconst THREAD_GAP = 16;\nconst OVERLAP_MARGIN = 20;\n\n/**\n * Avatar border colors chosen to be visually distinct from each other and from\n * the editor's semantic UI colors (Delta E > 10 between all pairs).\n */\nconst AVATAR_BORDER_COLORS = [\n\t'#C36EFF', // Purple\n\t'#D94145', // Red\n\t'#E4780A', // Orange\n\t'#FF35EE', // Magenta\n\t'#879F11', // Olive\n\t'#46A494', // Teal\n\t'#00A2C3', // Cyan\n];\n\n/**\n * Gets the border color for an avatar based on the user ID.\n *\n * Always returns a 6-digit `#RRGGBB` hex string; callers (e.g. the highlight\n * styles) rely on this format to append alpha suffixes.\n *\n * @param {number} userId - The user ID.\n * @return {string} - The border color as a `#RRGGBB` hex string.\n */\nexport function getAvatarBorderColor( userId ) {\n\treturn AVATAR_BORDER_COLORS[ userId % AVATAR_BORDER_COLORS.length ];\n}\n\n/**\n * Generates a note excerpt from text based on word count type and length.\n *\n * @param {string} text - The note text to generate excerpt from.\n * @param {number} excerptLength - The maximum length for the note excerpt.\n * @return {string} - The generated note excerpt.\n */\nexport function getNoteExcerpt( text, excerptLength = 10 ) {\n\tif ( ! text ) {\n\t\treturn '';\n\t}\n\n\t/*\n\t * translators: If your word count is based on single characters (e.g. East Asian characters),\n\t * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.\n\t * Do not translate into your own language.\n\t */\n\tconst wordCountType = _x( 'words', 'Word count type. Do not translate!' );\n\n\tconst rawText = text.trim();\n\tlet trimmedExcerpt = '';\n\n\tif ( wordCountType === 'words' ) {\n\t\ttrimmedExcerpt = rawText.split( ' ', excerptLength ).join( ' ' );\n\t} else if ( wordCountType === 'characters_excluding_spaces' ) {\n\t\t/*\n\t\t * 1. Split the text at the character limit,\n\t\t * then join the substrings back into one string.\n\t\t * 2. Count the number of spaces in the text\n\t\t * by comparing the lengths of the string with and without spaces.\n\t\t * 3. Add the number to the length of the visible excerpt,\n\t\t * so that the spaces are excluded from the word count.\n\t\t */\n\t\tconst textWithSpaces = rawText.split( '', excerptLength ).join( '' );\n\n\t\tconst numberOfSpaces =\n\t\t\ttextWithSpaces.length - textWithSpaces.replaceAll( ' ', '' ).length;\n\n\t\ttrimmedExcerpt = rawText\n\t\t\t.split( '', excerptLength + numberOfSpaces )\n\t\t\t.join( '' );\n\t} else if ( wordCountType === 'characters_including_spaces' ) {\n\t\ttrimmedExcerpt = rawText.split( '', excerptLength ).join( '' );\n\t}\n\n\tconst isTrimmed = trimmedExcerpt !== rawText;\n\treturn isTrimmed ? trimmedExcerpt + '…' : trimmedExcerpt;\n}\n\n/**\n * Normalizes noteId metadata to always return an array of unique numeric ids,\n * preserving insertion order. Handles both scalar (legacy, possibly\n * string-typed) and array (new) values.\n *\n * @param {Object} metadata Block metadata object\n * @return {number[]} Array of note IDs (may be empty)\n */\nexport function getNoteIdsFromMetadata( metadata ) {\n\tconst noteId = metadata?.noteId;\n\tconst raw = Array.isArray( noteId ) ? noteId : [ noteId ];\n\tconst ids = new Set();\n\tfor ( const value of raw ) {\n\t\tconst id = Number( value );\n\t\tif ( Number.isFinite( id ) && id > 0 ) {\n\t\t\tids.add( id );\n\t\t}\n\t}\n\treturn [ ...ids ];\n}\n\n/**\n * Adds a note ID to the metadata.\n * Converts scalar to array if needed, otherwise appends.\n *\n * @param {Object} metadata Existing block metadata\n * @param {number} noteId Note ID to add\n * @return {Object} Updated metadata object\n */\nexport function addNoteIdToMetadata( metadata, noteId ) {\n\tconst ids = new Set( getNoteIdsFromMetadata( metadata ) );\n\tconst id = Number( noteId );\n\tif ( ids.has( id ) ) {\n\t\treturn metadata;\n\t}\n\tids.add( id );\n\treturn { ...metadata, noteId: [ ...ids ] };\n}\n\nconst NOTE_FORMAT_TYPE = 'core/note';\n\n/**\n * Search a rich-text value for a `core/note` marker matching `noteId` and\n * return its character range. Used to derive an inline note's anchor from\n * the in-content marker (resilient to edits) rather than stale offset meta.\n *\n * @param {*} value Block attribute value (RichTextData, string, or other).\n * @param {number|string} noteId Note id to search for.\n * @return {?{start: number, end: number}} Range or null when no marker is found.\n */\nexport function findNoteRange( value, noteId ) {\n\tif ( noteId === undefined || noteId === null ) {\n\t\treturn null;\n\t}\n\tlet html = null;\n\tif ( value instanceof RichTextData ) {\n\t\thtml = value.toHTMLString();\n\t} else if ( typeof value === 'string' ) {\n\t\thtml = value;\n\t}\n\tif ( ! html || html.indexOf( 'wp-note' ) === -1 ) {\n\t\treturn null;\n\t}\n\tconst target = String( noteId );\n\tconst record = create( { html } );\n\tconst formats = record.formats;\n\tlet start = -1;\n\tfor ( let i = 0; i < formats.length; i++ ) {\n\t\tconst stack = formats[ i ];\n\t\tconst hit = stack?.find(\n\t\t\t( f ) =>\n\t\t\t\tf.type === NOTE_FORMAT_TYPE &&\n\t\t\t\tf.attributes &&\n\t\t\t\tf.attributes[ 'data-id' ] === target\n\t\t);\n\t\tif ( hit ) {\n\t\t\tif ( start === -1 ) {\n\t\t\t\tstart = i;\n\t\t\t}\n\t\t} else if ( start !== -1 ) {\n\t\t\treturn { start, end: i };\n\t\t}\n\t}\n\tif ( start !== -1 ) {\n\t\treturn { start, end: formats.length };\n\t}\n\treturn null;\n}\n\n/**\n * Locate a note's in-content `core/note` marker across all of a block's\n * attributes. The marker (carrying `data-id`) is the single source of truth for\n * an inline note's anchor: a note is inline iff a marker with its id exists in\n * the block, and the attribute that holds it is discovered here rather than\n * stored separately. Returns the matching attribute key and the marker range.\n *\n * @param {?Object} attributes Block attributes, or null/undefined when unloaded.\n * @param {number|string} noteId Note id to search for.\n * @return {?{attributeKey: string, start: number, end: number}} Anchor or null when no marker is found.\n */\nexport function findNoteInBlock( attributes, noteId ) {\n\tif ( ! attributes ) {\n\t\treturn null;\n\t}\n\tfor ( const attributeKey of Object.keys( attributes ) ) {\n\t\tconst range = findNoteRange( attributes[ attributeKey ], noteId );\n\t\tif ( range ) {\n\t\t\treturn { attributeKey, start: range.start, end: range.end };\n\t\t}\n\t}\n\treturn null;\n}\n\n// Sentinel that sorts a block-level (whole-block) note before any inline note\n// within the same block. Negative so any real character offset (>= 0) ranks\n// after it. Number.NEGATIVE_INFINITY would work too; -1 is enough and keeps\n// the diff arithmetic in safe integers.\nexport const BLOCK_LEVEL_NOTE_START = -1;\n\n/**\n * Resolve an inline note's character offset in its block so threads can be\n * sorted by reading order. A note is inline iff an in-content `core/note`\n * marker carries its id; block-level notes (no marker) sort first within their\n * block via a sentinel.\n *\n * @param {Object} thread Materialized thread record (with `.id`).\n * @param {?Object} attributes Block attributes for the thread's block.\n * @return {number} Marker start offset, or `BLOCK_LEVEL_NOTE_START` when there is no inline anchor.\n */\nexport function getInlineMarkerStart( thread, attributes ) {\n\tconst found = findNoteInBlock( attributes, thread?.id );\n\treturn found ? found.start : BLOCK_LEVEL_NOTE_START;\n}\n\n/**\n * Apply a `core/note` marker across `[start, end)` without removing notes\n * already present in that range.\n *\n * Rich-text's `applyFormat` strips any existing format of the same type before\n * applying, so two `core/note` markers can't coexist - a note drawn over an\n * existing one would wipe it in the overlap. This keeps every overlapping note\n * and orders the markers outermost-first by span, so a note fully contained in\n * another nests inside it (`<mark><mark>…</mark></mark>`). Crossing (partial)\n * overlaps can't nest in HTML and serialize as split runs, but each note keeps\n * its full range. The returned record is not normalised; callers should\n * round-trip it (e.g. through `RichTextData`) before storing.\n *\n * @param {Object} record A rich-text record (`{ text, formats, … }`).\n * @param {Object} format The `core/note` format to add (`{ type, attributes }`).\n * @param {number} start Range start (inclusive).\n * @param {number} end Range end (exclusive).\n * @return {Object} A new record with the note applied.\n */\nexport function applyNoteFormat( record, format, start, end ) {\n\tconst formats = record.formats.slice();\n\tfor ( let i = start; i < end; i++ ) {\n\t\tconst stack = formats[ i ] ? formats[ i ].slice() : [];\n\t\tstack.push( format );\n\t\tformats[ i ] = stack;\n\t}\n\n\t// Measure each note's full span so containment can order the markers.\n\tconst spans = new Map();\n\tfor ( let i = 0; i < formats.length; i++ ) {\n\t\tconst stack = formats[ i ];\n\t\tif ( ! stack ) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor ( const fmt of stack ) {\n\t\t\tif ( fmt.type !== NOTE_FORMAT_TYPE ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst id = fmt.attributes?.[ 'data-id' ];\n\t\t\tconst span = spans.get( id );\n\t\t\tif ( span ) {\n\t\t\t\tspan.end = i;\n\t\t\t} else {\n\t\t\t\tspans.set( id, { start: i, end: i } );\n\t\t\t}\n\t\t}\n\t}\n\tconst sizeOf = ( id ) => {\n\t\tconst span = spans.get( id );\n\t\treturn span ? span.end - span.start : 0;\n\t};\n\n\t// Order markers outermost-first (widest span) so `toTree` nests them rather\n\t// than splitting an outer note around an inner one. Notes sort ahead of\n\t// other formats so a note wraps the formatted text it spans.\n\tfor ( let i = 0; i < formats.length; i++ ) {\n\t\tconst stack = formats[ i ];\n\t\tif ( ! stack || stack.length < 2 ) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst notes = stack.filter( ( fmt ) => fmt.type === NOTE_FORMAT_TYPE );\n\t\tif ( notes.length === 0 ) {\n\t\t\tcontinue;\n\t\t}\n\t\tif ( notes.length > 1 ) {\n\t\t\tnotes.sort(\n\t\t\t\t( a, b ) =>\n\t\t\t\t\tsizeOf( b.attributes?.[ 'data-id' ] ) -\n\t\t\t\t\tsizeOf( a.attributes?.[ 'data-id' ] )\n\t\t\t);\n\t\t}\n\t\tconst others = stack.filter( ( fmt ) => fmt.type !== NOTE_FORMAT_TYPE );\n\t\tformats[ i ] = [ ...notes, ...others ];\n\t}\n\n\treturn { ...record, formats };\n}\n\n/**\n * Remove a single note's `core/note` marker from a rich-text value, leaving any\n * other notes nested or overlapping with it intact. Used when a note is deleted\n * or resolved so its highlight does not linger in the content.\n *\n * Rich-text's `removeFormat` strips every `core/note` marker in a range, so it\n * would wipe co-located notes; this filters by `data-id` to drop only the target\n * marker.\n *\n * @param {*} value Block attribute value (RichTextData or other).\n * @param {number|string} noteId Note id whose marker should be removed.\n * @return {?RichTextData} A new value with the marker removed, or null when the\n * attribute isn't rich text or carries no such marker.\n */\nexport function removeNoteFormat( value, noteId ) {\n\tif ( ! ( value instanceof RichTextData ) ) {\n\t\treturn null;\n\t}\n\tconst target = String( noteId );\n\tconst record = create( { html: value.toHTMLString() } );\n\tlet changed = false;\n\tconst formats = record.formats.map( ( stack ) => {\n\t\tif ( ! stack ) {\n\t\t\treturn stack;\n\t\t}\n\t\tconst filtered = stack.filter(\n\t\t\t( format ) =>\n\t\t\t\t! (\n\t\t\t\t\tformat.type === NOTE_FORMAT_TYPE &&\n\t\t\t\t\tformat.attributes?.[ 'data-id' ] === target\n\t\t\t\t)\n\t\t);\n\t\tif ( filtered.length === stack.length ) {\n\t\t\treturn stack;\n\t\t}\n\t\tchanged = true;\n\t\treturn filtered.length ? filtered : undefined;\n\t} );\n\t// Round-trip through HTML so the stored value matches a fresh reload.\n\treturn changed\n\t\t? RichTextData.fromHTMLString(\n\t\t\t\tnew RichTextData( { ...record, formats } ).toHTMLString()\n\t\t )\n\t\t: null;\n}\n\n/**\n * Picks the most relevant thread from a list: first unresolved, else first.\n *\n * @param {Array} threads Ordered list of thread objects.\n * @return {Object|null} Selected thread or null when the list is empty.\n */\nexport function pickPrimaryNote( threads ) {\n\treturn (\n\t\tthreads.find( ( thread ) => thread.status === 'hold' ) ??\n\t\tthreads[ 0 ] ??\n\t\tnull\n\t);\n}\n\n/**\n * Removes a note ID from the metadata.\n *\n * @param {Object} metadata Existing block metadata\n * @param {number} noteId Note ID to remove\n * @return {Object} Updated metadata object\n */\nexport function removeNoteIdFromMetadata( metadata, noteId ) {\n\tconst ids = new Set( getNoteIdsFromMetadata( metadata ) );\n\tids.delete( Number( noteId ) );\n\treturn {\n\t\t...metadata,\n\t\tnoteId: ids.size > 0 ? [ ...ids ] : undefined,\n\t};\n}\n\n/**\n * Calculate final top positions for all floating note threads in the\n * editor's content coordinate space. Adjusts positions to prevent overlapping\n * by pushing threads above the selected one upward and threads below it downward.\n *\n * @param {Object} params\n * @param {Array} params.threads Ordered list of thread objects.\n * @param {string|number|undefined} params.selectedNoteId ID of the currently selected thread.\n * @param {Object<string,DOMRect>} params.blockRects Pre-read bounding rects keyed by thread ID.\n * @param {Object<string,number>} params.heights Rendered heights keyed by thread ID.\n * @param {number} params.scrollTop Current scroll offset of the editor content.\n * @return {{ positions: Object<string,number> }} Computed top positions.\n */\nexport function calculateNotePositions( {\n\tthreads,\n\tselectedNoteId,\n\tblockRects,\n\theights,\n\tscrollTop = 0,\n} ) {\n\tconst offsets = {};\n\n\tconst anchorIndex = Math.max(\n\t\t0,\n\t\tthreads.findIndex( ( thread ) => thread.id === selectedNoteId )\n\t);\n\n\tconst anchorThread = threads[ anchorIndex ];\n\n\tif ( ! anchorThread || ! blockRects[ anchorThread.id ] ) {\n\t\treturn { positions: {} };\n\t}\n\n\tconst anchorRect = blockRects[ anchorThread.id ];\n\tconst anchorTop = anchorRect.top || 0;\n\tconst anchorHeight = heights[ anchorThread.id ] || 0;\n\n\toffsets[ anchorThread.id ] = THREAD_ALIGN_OFFSET;\n\n\t// Process threads after the anchor, offsetting overlapping threads downward.\n\tlet prevAdjustedTop = anchorTop + THREAD_ALIGN_OFFSET;\n\tlet prevHeight = anchorHeight;\n\n\tfor ( let i = anchorIndex + 1; i < threads.length; i++ ) {\n\t\tconst thread = threads[ i ];\n\t\tconst threadRect = blockRects[ thread.id ];\n\t\tif ( ! threadRect ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst threadTop = threadRect.top || 0;\n\t\tconst threadHeight = heights[ thread.id ] || 0;\n\n\t\tlet offset = THREAD_ALIGN_OFFSET;\n\n\t\tconst prevBottom = prevAdjustedTop + prevHeight;\n\t\tif ( threadTop < prevBottom + THREAD_GAP ) {\n\t\t\toffset = prevBottom - threadTop + OVERLAP_MARGIN;\n\t\t}\n\n\t\toffsets[ thread.id ] = offset;\n\n\t\tprevAdjustedTop = threadTop + offset;\n\t\tprevHeight = threadHeight;\n\t}\n\n\t// Process threads before the anchor, offsetting overlapping threads upward.\n\tlet belowAdjustedTop = anchorTop + THREAD_ALIGN_OFFSET;\n\n\tfor ( let i = anchorIndex - 1; i >= 0; i-- ) {\n\t\tconst thread = threads[ i ];\n\t\tconst threadRect = blockRects[ thread.id ];\n\t\tif ( ! threadRect ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst threadTop = threadRect.top || 0;\n\t\tconst threadHeight = heights[ thread.id ] || 0;\n\n\t\tlet offset = THREAD_ALIGN_OFFSET;\n\n\t\tconst threadBottom = threadTop + threadHeight;\n\n\t\tif ( threadBottom > belowAdjustedTop ) {\n\t\t\toffset =\n\t\t\t\tbelowAdjustedTop - threadTop - threadHeight - OVERLAP_MARGIN;\n\t\t}\n\n\t\toffsets[ thread.id ] = offset;\n\n\t\tbelowAdjustedTop = threadTop + offset;\n\t}\n\n\t// blockRect.top + scrollTop is the block's absolute y within the editor's\n\t// scroll content; CSS translates each thread by -scrollTop at render time.\n\tconst positions = {};\n\tfor ( const thread of threads ) {\n\t\tconst blockRect = blockRects[ thread.id ];\n\t\tif ( blockRect && offsets[ thread.id ] !== undefined ) {\n\t\t\tpositions[ thread.id ] =\n\t\t\t\tblockRect.top + scrollTop + offsets[ thread.id ];\n\t\t}\n\t}\n\n\treturn { positions };\n}\n\n/**\n * Resolve the DOM element for a note thread once it's mounted,\n * or `null` if not found within 3 seconds.\n *\n * @param {string} noteId Note thread ID.\n * @param {?HTMLElement} container Container to search within.\n * @param {string} additionalSelector Optional descendant selector.\n * @return {Promise<HTMLElement|null>} Resolved element, or `null` on timeout.\n */\nfunction findNoteThread( noteId, container, additionalSelector ) {\n\tif ( ! container ) {\n\t\treturn Promise.resolve( null );\n\t}\n\n\t// A thread without a noteId is a new note thread.\n\tconst threadSelector =\n\t\tnoteId && noteId !== 'new'\n\t\t\t? `[role=treeitem][id=\"note-thread-${ noteId }\"]`\n\t\t\t: '[role=treeitem]:not([id])';\n\tconst selector = additionalSelector\n\t\t? `${ threadSelector } ${ additionalSelector }`\n\t\t: threadSelector;\n\n\treturn new Promise( ( resolve ) => {\n\t\tif ( container.querySelector( selector ) ) {\n\t\t\treturn resolve( container.querySelector( selector ) );\n\t\t}\n\n\t\tlet timer = null;\n\t\t// Wait for the element to be added to the DOM.\n\t\tconst observer = new window.MutationObserver( () => {\n\t\t\tif ( container.querySelector( selector ) ) {\n\t\t\t\tclearTimeout( timer );\n\t\t\t\tobserver.disconnect();\n\t\t\t\tresolve( container.querySelector( selector ) );\n\t\t\t}\n\t\t} );\n\n\t\tobserver.observe( container, { childList: true, subtree: true } );\n\n\t\t// Stop trying after 3 seconds.\n\t\ttimer = setTimeout( () => {\n\t\t\tobserver.disconnect();\n\t\t\tresolve( null );\n\t\t}, 3000 );\n\t} );\n}\n\n/**\n * Focus a note thread (or a descendant) and scroll it into view.\n *\n * @param {string} noteId Note thread ID.\n * @param {?HTMLElement} container Container to search within.\n * @param {string} additionalSelector Optional descendant selector.\n */\nexport function focusNoteThread( noteId, container, additionalSelector ) {\n\treturn findNoteThread( noteId, container, additionalSelector ).then(\n\t\t( element ) => {\n\t\t\tif ( ! element ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telement.focus();\n\t\t\telement.scrollIntoView( { block: 'nearest' } );\n\t\t}\n\t);\n}\n\n/**\n * Scroll a note thread into view without changing focus.\n *\n * @param {string} noteId Note thread ID.\n * @param {?HTMLElement} container Container to search within.\n */\nexport function scrollNoteThreadIntoView( noteId, container ) {\n\treturn findNoteThread( noteId, container ).then( ( element ) => {\n\t\telement?.scrollIntoView( { block: 'nearest' } );\n\t} );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|