@wordpress/editor 14.41.2-next.v.202603161435.0 → 14.42.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 +1 -1
- package/build/components/collaborators-overlay/compute-selection.cjs +10 -10
- package/build/components/collaborators-overlay/compute-selection.cjs.map +2 -2
- package/build/components/collaborators-overlay/overlay.cjs +11 -0
- package/build/components/collaborators-overlay/overlay.cjs.map +2 -2
- package/build/components/collaborators-overlay/timing-utils.cjs +46 -0
- package/build/components/collaborators-overlay/timing-utils.cjs.map +7 -0
- package/build/components/collaborators-overlay/use-render-cursors.cjs +1 -1
- package/build/components/collaborators-overlay/use-render-cursors.cjs.map +2 -2
- package/build/components/post-locked-modal/index.cjs +16 -3
- package/build/components/post-locked-modal/index.cjs.map +2 -2
- package/build/components/post-revisions-preview/block-diff.cjs +39 -11
- package/build/components/post-revisions-preview/block-diff.cjs.map +2 -2
- package/build/components/post-revisions-preview/diff-markers.cjs +2 -2
- package/build/components/post-revisions-preview/diff-markers.cjs.map +2 -2
- package/build/components/post-revisions-preview/revisions-canvas.cjs +1 -1
- package/build/components/post-revisions-preview/revisions-canvas.cjs.map +2 -2
- package/build/components/post-template/block-theme.cjs +7 -4
- package/build/components/post-template/block-theme.cjs.map +2 -2
- package/build/components/post-template/hooks.cjs +39 -2
- package/build/components/post-template/hooks.cjs.map +2 -2
- package/build/components/post-template/panel.cjs +5 -42
- package/build/components/post-template/panel.cjs.map +3 -3
- package/build/components/provider/use-block-editor-settings.cjs +2 -0
- package/build/components/provider/use-block-editor-settings.cjs.map +3 -3
- package/build/components/revision-block-diff/index.cjs +84 -0
- package/build/components/revision-block-diff/index.cjs.map +7 -0
- package/build/components/sidebar/dataform-post-summary.cjs +17 -2
- package/build/components/sidebar/dataform-post-summary.cjs.map +2 -2
- package/build/components/sidebar/index.cjs +5 -1
- package/build/components/sidebar/index.cjs.map +3 -3
- package/build/components/{sync-connection-modal → sync-connection-error-modal}/index.cjs +87 -78
- package/build/components/sync-connection-error-modal/index.cjs.map +7 -0
- package/build/components/{sync-connection-modal → sync-connection-error-modal}/use-retry-countdown.cjs +14 -27
- package/build/components/sync-connection-error-modal/use-retry-countdown.cjs.map +7 -0
- package/build/components/visual-editor/index.cjs +2 -2
- package/build/components/visual-editor/index.cjs.map +2 -2
- package/build/store/actions.cjs +1 -3
- package/build/store/actions.cjs.map +2 -2
- package/build/utils/media-finalize/index.cjs +43 -0
- package/build/utils/media-finalize/index.cjs.map +7 -0
- package/build/utils/sync-error-messages.cjs +29 -16
- package/build/utils/sync-error-messages.cjs.map +3 -3
- package/build-module/components/collaborators-overlay/compute-selection.mjs +10 -10
- package/build-module/components/collaborators-overlay/compute-selection.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/overlay.mjs +11 -0
- package/build-module/components/collaborators-overlay/overlay.mjs.map +2 -2
- package/build-module/components/collaborators-overlay/timing-utils.mjs +21 -0
- package/build-module/components/collaborators-overlay/timing-utils.mjs.map +7 -0
- package/build-module/components/collaborators-overlay/use-render-cursors.mjs +1 -1
- package/build-module/components/collaborators-overlay/use-render-cursors.mjs.map +2 -2
- package/build-module/components/post-locked-modal/index.mjs +16 -3
- package/build-module/components/post-locked-modal/index.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/block-diff.mjs +39 -11
- package/build-module/components/post-revisions-preview/block-diff.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/diff-markers.mjs +2 -2
- package/build-module/components/post-revisions-preview/diff-markers.mjs.map +2 -2
- package/build-module/components/post-revisions-preview/revisions-canvas.mjs +1 -1
- package/build-module/components/post-revisions-preview/revisions-canvas.mjs.map +2 -2
- package/build-module/components/post-template/block-theme.mjs +7 -4
- package/build-module/components/post-template/block-theme.mjs.map +2 -2
- package/build-module/components/post-template/hooks.mjs +37 -1
- package/build-module/components/post-template/hooks.mjs.map +2 -2
- package/build-module/components/post-template/panel.mjs +5 -42
- package/build-module/components/post-template/panel.mjs.map +2 -2
- package/build-module/components/provider/use-block-editor-settings.mjs +2 -0
- package/build-module/components/provider/use-block-editor-settings.mjs.map +2 -2
- package/build-module/components/revision-block-diff/index.mjs +53 -0
- package/build-module/components/revision-block-diff/index.mjs.map +7 -0
- package/build-module/components/sidebar/dataform-post-summary.mjs +17 -2
- package/build-module/components/sidebar/dataform-post-summary.mjs.map +2 -2
- package/build-module/components/sidebar/index.mjs +5 -1
- package/build-module/components/sidebar/index.mjs.map +2 -2
- package/build-module/components/sync-connection-error-modal/index.mjs +177 -0
- package/build-module/components/sync-connection-error-modal/index.mjs.map +7 -0
- package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs +36 -0
- package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs.map +7 -0
- package/build-module/components/visual-editor/index.mjs +2 -2
- package/build-module/components/visual-editor/index.mjs.map +2 -2
- package/build-module/store/actions.mjs +1 -3
- package/build-module/store/actions.mjs.map +2 -2
- package/build-module/utils/media-finalize/index.mjs +12 -0
- package/build-module/utils/media-finalize/index.mjs.map +7 -0
- package/build-module/utils/sync-error-messages.mjs +24 -16
- package/build-module/utils/sync-error-messages.mjs.map +3 -3
- package/build-style/style-rtl.css +24 -8
- package/build-style/style.css +24 -8
- package/build-types/components/collaborators-overlay/overlay.d.ts.map +1 -1
- package/build-types/components/collaborators-overlay/timing-utils.d.ts +11 -0
- package/build-types/components/collaborators-overlay/timing-utils.d.ts.map +1 -0
- package/build-types/components/post-locked-modal/index.d.ts +2 -2
- package/build-types/components/post-locked-modal/index.d.ts.map +1 -1
- package/build-types/components/post-revisions-preview/block-diff.d.ts.map +1 -1
- package/build-types/components/post-template/block-theme.d.ts +1 -3
- package/build-types/components/post-template/block-theme.d.ts.map +1 -1
- package/build-types/components/post-template/hooks.d.ts +1 -0
- package/build-types/components/post-template/hooks.d.ts.map +1 -1
- package/build-types/components/post-template/panel.d.ts.map +1 -1
- package/build-types/components/provider/use-block-editor-settings.d.ts.map +1 -1
- package/build-types/components/revision-block-diff/index.d.ts +6 -0
- package/build-types/components/revision-block-diff/index.d.ts.map +1 -0
- 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/sync-connection-error-modal/index.d.ts +22 -0
- package/build-types/components/sync-connection-error-modal/index.d.ts.map +1 -0
- package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts +11 -0
- package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts.map +1 -0
- package/build-types/store/actions.d.ts.map +1 -1
- package/build-types/utils/media-finalize/index.d.ts +2 -0
- package/build-types/utils/media-finalize/index.d.ts.map +1 -0
- package/build-types/utils/sync-error-messages.d.ts +17 -3
- package/build-types/utils/sync-error-messages.d.ts.map +1 -1
- package/package.json +44 -44
- package/src/components/collaborators-overlay/compute-selection.ts +13 -13
- package/src/components/collaborators-overlay/overlay.tsx +19 -0
- package/src/components/collaborators-overlay/timing-utils.ts +30 -0
- package/src/components/collaborators-overlay/use-render-cursors.ts +1 -1
- package/src/components/post-locked-modal/index.js +21 -3
- package/src/components/post-revisions-preview/block-diff.js +59 -20
- package/src/components/post-revisions-preview/diff-markers.js +2 -2
- package/src/components/post-revisions-preview/revisions-canvas.js +1 -1
- package/src/components/post-revisions-preview/test/block-diff.js +69 -31
- package/src/components/post-template/block-theme.js +4 -1
- package/src/components/post-template/hooks.js +42 -0
- package/src/components/post-template/panel.js +5 -59
- package/src/components/provider/use-block-editor-settings.js +2 -0
- package/src/components/revision-block-diff/index.js +74 -0
- package/src/components/revision-block-diff/style.scss +13 -0
- package/src/components/sidebar/dataform-post-summary.js +37 -13
- package/src/components/sidebar/index.js +2 -0
- package/src/components/sync-connection-error-modal/index.tsx +265 -0
- package/src/components/sync-connection-error-modal/style.scss +14 -0
- package/src/components/sync-connection-error-modal/use-retry-countdown.ts +57 -0
- package/src/components/visual-editor/index.js +2 -2
- package/src/store/actions.js +1 -4
- package/src/style.scss +2 -1
- package/src/utils/media-finalize/index.js +11 -0
- package/src/utils/media-finalize/test/index.js +34 -0
- package/src/utils/sync-error-messages.ts +72 -0
- package/src/utils/test/sync-error-messages.js +9 -32
- package/build/components/sync-connection-modal/index.cjs.map +0 -7
- package/build/components/sync-connection-modal/use-retry-countdown.cjs.map +0 -7
- package/build-module/components/sync-connection-modal/index.mjs +0 -170
- package/build-module/components/sync-connection-modal/index.mjs.map +0 -7
- package/build-module/components/sync-connection-modal/use-retry-countdown.mjs +0 -49
- package/build-module/components/sync-connection-modal/use-retry-countdown.mjs.map +0 -7
- package/build-types/components/sync-connection-modal/index.d.ts +0 -8
- package/build-types/components/sync-connection-modal/index.d.ts.map +0 -1
- package/build-types/components/sync-connection-modal/use-retry-countdown.d.ts +0 -9
- package/build-types/components/sync-connection-modal/use-retry-countdown.d.ts.map +0 -1
- package/src/components/sync-connection-modal/index.js +0 -206
- package/src/components/sync-connection-modal/style.scss +0 -14
- package/src/components/sync-connection-modal/use-retry-countdown.js +0 -70
- package/src/utils/sync-error-messages.js +0 -58
package/CHANGELOG.md
CHANGED
|
@@ -46,7 +46,7 @@ function computeCursorOnly(start, overlayContext) {
|
|
|
46
46
|
);
|
|
47
47
|
return {
|
|
48
48
|
coords: (0, import_cursor_dom_utils.getCursorPosition)(
|
|
49
|
-
start.
|
|
49
|
+
start.richTextOffset,
|
|
50
50
|
blockElement,
|
|
51
51
|
overlayContext.editorDocument,
|
|
52
52
|
overlayContext.overlayRect
|
|
@@ -54,7 +54,7 @@ function computeCursorOnly(start, overlayContext) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
function computeTextSelection(selection, start, end, overlayContext) {
|
|
57
|
-
if (!start.localClientId || !end.localClientId || start.
|
|
57
|
+
if (!start.localClientId || !end.localClientId || start.richTextOffset === null || end.richTextOffset === null) {
|
|
58
58
|
return {};
|
|
59
59
|
}
|
|
60
60
|
const isReverse = selection.selectionDirection === import_core_data.SelectionDirection.Backward;
|
|
@@ -73,7 +73,7 @@ function computeTextSelection(selection, start, end, overlayContext) {
|
|
|
73
73
|
if (allRects.length > 0) {
|
|
74
74
|
return {
|
|
75
75
|
coords: (0, import_cursor_dom_utils.getCursorPosition)(
|
|
76
|
-
activeEnd.
|
|
76
|
+
activeEnd.richTextOffset,
|
|
77
77
|
activeEndBlock,
|
|
78
78
|
overlayContext.editorDocument,
|
|
79
79
|
overlayContext.overlayRect
|
|
@@ -86,7 +86,7 @@ function computeTextSelection(selection, start, end, overlayContext) {
|
|
|
86
86
|
);
|
|
87
87
|
return {
|
|
88
88
|
coords: (0, import_cursor_dom_utils.getCursorPosition)(
|
|
89
|
-
start.
|
|
89
|
+
start.richTextOffset,
|
|
90
90
|
startBlock,
|
|
91
91
|
overlayContext.editorDocument,
|
|
92
92
|
overlayContext.overlayRect
|
|
@@ -97,14 +97,14 @@ function computeSingleBlockRects(start, end, overlayContext) {
|
|
|
97
97
|
const blockElement = overlayContext.editorDocument.querySelector(
|
|
98
98
|
`[data-block="${start.localClientId}"]`
|
|
99
99
|
);
|
|
100
|
-
if (!blockElement || start.
|
|
100
|
+
if (!blockElement || start.richTextOffset === null || end.richTextOffset === null) {
|
|
101
101
|
return { rects: [], blockElement: null };
|
|
102
102
|
}
|
|
103
103
|
return {
|
|
104
104
|
rects: (0, import_cursor_dom_utils.getSelectionRects)(
|
|
105
105
|
blockElement,
|
|
106
|
-
start.
|
|
107
|
-
end.
|
|
106
|
+
start.richTextOffset,
|
|
107
|
+
end.richTextOffset,
|
|
108
108
|
overlayContext.editorDocument,
|
|
109
109
|
overlayContext.overlayRect
|
|
110
110
|
) ?? [],
|
|
@@ -125,7 +125,7 @@ function computeMultiBlockRects(start, end, overlayContext) {
|
|
|
125
125
|
docLast = start;
|
|
126
126
|
[firstBlock, lastBlock] = [lastBlock, firstBlock];
|
|
127
127
|
}
|
|
128
|
-
if (!firstBlock || !lastBlock || docFirst.
|
|
128
|
+
if (!firstBlock || !lastBlock || docFirst.richTextOffset === null || docLast.richTextOffset === null || !docFirst.localClientId || !docLast.localClientId) {
|
|
129
129
|
return {
|
|
130
130
|
rects: [],
|
|
131
131
|
firstBlock: null,
|
|
@@ -136,7 +136,7 @@ function computeMultiBlockRects(start, end, overlayContext) {
|
|
|
136
136
|
const allRects = [];
|
|
137
137
|
const startRects = (0, import_cursor_dom_utils.getSelectionRects)(
|
|
138
138
|
firstBlock,
|
|
139
|
-
docFirst.
|
|
139
|
+
docFirst.richTextOffset,
|
|
140
140
|
Number.MAX_SAFE_INTEGER,
|
|
141
141
|
overlayContext.editorDocument,
|
|
142
142
|
overlayContext.overlayRect
|
|
@@ -160,7 +160,7 @@ function computeMultiBlockRects(start, end, overlayContext) {
|
|
|
160
160
|
const endRects = (0, import_cursor_dom_utils.getSelectionRects)(
|
|
161
161
|
lastBlock,
|
|
162
162
|
0,
|
|
163
|
-
docLast.
|
|
163
|
+
docLast.richTextOffset,
|
|
164
164
|
overlayContext.editorDocument,
|
|
165
165
|
overlayContext.overlayRect
|
|
166
166
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/collaborators-overlay/compute-selection.ts"],
|
|
4
|
-
"sourcesContent": ["import { SelectionDirection, SelectionType } from '@wordpress/core-data';\nimport type { ResolvedSelection } from '@wordpress/core-data';\n\nimport {\n\tgetCursorPosition,\n\tgetSelectionRects,\n\tgetFullBlockSelectionRects,\n\tgetBlocksBetween,\n\tisNodeBefore,\n} from './cursor-dom-utils';\nimport type { CursorCoords, SelectionRect } from './cursor-dom-utils';\n\n/** Common parameters passed to cursor/selection computation helpers. */\ninterface OverlayContext {\n\teditorDocument: Document;\n\toverlayRect: DOMRect;\n}\n\n/** Selection rects and the resolved block element for a single-block selection. */\ninterface SingleBlockResult {\n\trects: SelectionRect[];\n\tblockElement: HTMLElement | null;\n}\n\n/** Selection rects and the resolved block elements for a multi-block selection. */\ninterface MultiBlockResult {\n\trects: SelectionRect[];\n\tfirstBlock: HTMLElement | null;\n\tlastBlock: HTMLElement | null;\n\tfirstBlockClientId: string | null;\n}\n\n/** Result of computing visual cursor/selection state for a single user. */\nexport interface SelectionVisual {\n\tcoords?: CursorCoords | null;\n\tselectionRects?: SelectionRect[];\n}\n\n/**\n * Compute cursor coords and optional selection rects for a single user's selection.\n *\n * @param selection - The selection state from the awareness layer.\n * @param start - Start position (block clientId + text index).\n * @param end - End position (only for range selections).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Cursor coordinates and optional selection rectangles.\n */\nexport function computeSelectionVisual(\n\tselection: any,\n\tstart: ResolvedSelection,\n\tend: ResolvedSelection | undefined,\n\toverlayContext: OverlayContext\n): SelectionVisual {\n\tif (\n\t\tselection.type === SelectionType.None ||\n\t\tselection.type === SelectionType.WholeBlock\n\t) {\n\t\treturn {};\n\t}\n\n\tif ( selection.type === SelectionType.Cursor ) {\n\t\treturn computeCursorOnly( start, overlayContext );\n\t}\n\n\t// SelectionInOneBlock or SelectionInMultipleBlocks.\n\tif ( ! end ) {\n\t\treturn {};\n\t}\n\treturn computeTextSelection( selection, start, end, overlayContext );\n}\n\n/**\n * Compute cursor coordinates for a simple cursor (no highlighted text).\n *\n * @param start - Cursor position (block clientId + text index).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Cursor coordinates.\n */\nfunction computeCursorOnly(\n\tstart: ResolvedSelection,\n\toverlayContext: OverlayContext\n): SelectionVisual {\n\tif ( ! start.localClientId ) {\n\t\treturn {};\n\t}\n\tconst blockElement =\n\t\toverlayContext.editorDocument.querySelector< HTMLElement >(\n\t\t\t`[data-block=\"${ start.localClientId }\"]`\n\t\t);\n\treturn {\n\t\tcoords: getCursorPosition(\n\t\t\tstart.
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAkD;AAGlD,8BAMO;AAsCA,SAAS,uBACf,WACA,OACA,KACA,gBACkB;AAClB,MACC,UAAU,SAAS,+BAAc,QACjC,UAAU,SAAS,+BAAc,YAChC;AACD,WAAO,CAAC;AAAA,EACT;AAEA,MAAK,UAAU,SAAS,+BAAc,QAAS;AAC9C,WAAO,kBAAmB,OAAO,cAAe;AAAA,EACjD;AAGA,MAAK,CAAE,KAAM;AACZ,WAAO,CAAC;AAAA,EACT;AACA,SAAO,qBAAsB,WAAW,OAAO,KAAK,cAAe;AACpE;AASA,SAAS,kBACR,OACA,gBACkB;AAClB,MAAK,CAAE,MAAM,eAAgB;AAC5B,WAAO,CAAC;AAAA,EACT;AACA,QAAM,eACL,eAAe,eAAe;AAAA,IAC7B,gBAAiB,MAAM,aAAc;AAAA,EACtC;AACD,SAAO;AAAA,IACN,YAAQ;AAAA,MACP,MAAM;AAAA,MACN;AAAA,MACA,eAAe;AAAA,MACf,eAAe;AAAA,IAChB;AAAA,EACD;AACD;AAYA,SAAS,qBACR,WACA,OACA,KACA,gBACkB;AAClB,MACC,CAAE,MAAM,iBACR,CAAE,IAAI,iBACN,MAAM,
|
|
4
|
+
"sourcesContent": ["import { SelectionDirection, SelectionType } from '@wordpress/core-data';\nimport type { ResolvedSelection } from '@wordpress/core-data';\n\nimport {\n\tgetCursorPosition,\n\tgetSelectionRects,\n\tgetFullBlockSelectionRects,\n\tgetBlocksBetween,\n\tisNodeBefore,\n} from './cursor-dom-utils';\nimport type { CursorCoords, SelectionRect } from './cursor-dom-utils';\n\n/** Common parameters passed to cursor/selection computation helpers. */\ninterface OverlayContext {\n\teditorDocument: Document;\n\toverlayRect: DOMRect;\n}\n\n/** Selection rects and the resolved block element for a single-block selection. */\ninterface SingleBlockResult {\n\trects: SelectionRect[];\n\tblockElement: HTMLElement | null;\n}\n\n/** Selection rects and the resolved block elements for a multi-block selection. */\ninterface MultiBlockResult {\n\trects: SelectionRect[];\n\tfirstBlock: HTMLElement | null;\n\tlastBlock: HTMLElement | null;\n\tfirstBlockClientId: string | null;\n}\n\n/** Result of computing visual cursor/selection state for a single user. */\nexport interface SelectionVisual {\n\tcoords?: CursorCoords | null;\n\tselectionRects?: SelectionRect[];\n}\n\n/**\n * Compute cursor coords and optional selection rects for a single user's selection.\n *\n * @param selection - The selection state from the awareness layer.\n * @param start - Start position (block clientId + text index).\n * @param end - End position (only for range selections).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Cursor coordinates and optional selection rectangles.\n */\nexport function computeSelectionVisual(\n\tselection: any,\n\tstart: ResolvedSelection,\n\tend: ResolvedSelection | undefined,\n\toverlayContext: OverlayContext\n): SelectionVisual {\n\tif (\n\t\tselection.type === SelectionType.None ||\n\t\tselection.type === SelectionType.WholeBlock\n\t) {\n\t\treturn {};\n\t}\n\n\tif ( selection.type === SelectionType.Cursor ) {\n\t\treturn computeCursorOnly( start, overlayContext );\n\t}\n\n\t// SelectionInOneBlock or SelectionInMultipleBlocks.\n\tif ( ! end ) {\n\t\treturn {};\n\t}\n\treturn computeTextSelection( selection, start, end, overlayContext );\n}\n\n/**\n * Compute cursor coordinates for a simple cursor (no highlighted text).\n *\n * @param start - Cursor position (block clientId + text index).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Cursor coordinates.\n */\nfunction computeCursorOnly(\n\tstart: ResolvedSelection,\n\toverlayContext: OverlayContext\n): SelectionVisual {\n\tif ( ! start.localClientId ) {\n\t\treturn {};\n\t}\n\tconst blockElement =\n\t\toverlayContext.editorDocument.querySelector< HTMLElement >(\n\t\t\t`[data-block=\"${ start.localClientId }\"]`\n\t\t);\n\treturn {\n\t\tcoords: getCursorPosition(\n\t\t\tstart.richTextOffset,\n\t\t\tblockElement,\n\t\t\toverlayContext.editorDocument,\n\t\t\toverlayContext.overlayRect\n\t\t),\n\t};\n}\n\n/**\n * Compute cursor coordinates and selection highlight rects for a text selection\n * (single-block or multi-block).\n *\n * @param selection - The selection state.\n * @param start - Start position (block clientId + text index).\n * @param end - End position (block clientId + text index).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Cursor coordinates and optional selection rectangles.\n */\nfunction computeTextSelection(\n\tselection: any,\n\tstart: ResolvedSelection,\n\tend: ResolvedSelection,\n\toverlayContext: OverlayContext\n): SelectionVisual {\n\tif (\n\t\t! start.localClientId ||\n\t\t! end.localClientId ||\n\t\tstart.richTextOffset === null ||\n\t\tend.richTextOffset === null\n\t) {\n\t\treturn {};\n\t}\n\n\tconst isReverse =\n\t\tselection.selectionDirection === SelectionDirection.Backward;\n\tconst activeEnd = isReverse ? start : end;\n\n\tlet allRects: SelectionRect[];\n\tlet activeEndBlock: HTMLElement | null = null;\n\n\tif ( selection.type === SelectionType.SelectionInOneBlock ) {\n\t\tconst result = computeSingleBlockRects( start, end, overlayContext );\n\t\tallRects = result.rects;\n\t\t// Single block: start and end share the same block element.\n\t\tactiveEndBlock = result.blockElement;\n\t} else {\n\t\tconst result = computeMultiBlockRects( start, end, overlayContext );\n\t\tallRects = result.rects;\n\t\t// Pick the block element that matches the active end.\n\t\tactiveEndBlock =\n\t\t\tactiveEnd.localClientId === result.firstBlockClientId\n\t\t\t\t? result.firstBlock\n\t\t\t\t: result.lastBlock;\n\t}\n\n\tif ( allRects.length > 0 ) {\n\t\treturn {\n\t\t\tcoords: getCursorPosition(\n\t\t\t\tactiveEnd.richTextOffset,\n\t\t\t\tactiveEndBlock,\n\t\t\t\toverlayContext.editorDocument,\n\t\t\t\toverlayContext.overlayRect\n\t\t\t),\n\t\t\tselectionRects: allRects,\n\t\t};\n\t}\n\n\t// Fallback: cursor at start position only.\n\tconst startBlock =\n\t\toverlayContext.editorDocument.querySelector< HTMLElement >(\n\t\t\t`[data-block=\"${ start.localClientId }\"]`\n\t\t);\n\n\treturn {\n\t\tcoords: getCursorPosition(\n\t\t\tstart.richTextOffset,\n\t\t\tstartBlock,\n\t\t\toverlayContext.editorDocument,\n\t\t\toverlayContext.overlayRect\n\t\t),\n\t};\n}\n\n/**\n * Compute selection rects for a selection within a single block.\n *\n * @param start - Start position (block clientId + text index).\n * @param end - End position (block clientId + text index).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Array of selection rectangles.\n */\nfunction computeSingleBlockRects(\n\tstart: ResolvedSelection,\n\tend: ResolvedSelection,\n\toverlayContext: OverlayContext\n): SingleBlockResult {\n\tconst blockElement =\n\t\toverlayContext.editorDocument.querySelector< HTMLElement >(\n\t\t\t`[data-block=\"${ start.localClientId }\"]`\n\t\t);\n\tif (\n\t\t! blockElement ||\n\t\tstart.richTextOffset === null ||\n\t\tend.richTextOffset === null\n\t) {\n\t\treturn { rects: [], blockElement: null };\n\t}\n\treturn {\n\t\trects:\n\t\t\tgetSelectionRects(\n\t\t\t\tblockElement,\n\t\t\t\tstart.richTextOffset,\n\t\t\t\tend.richTextOffset,\n\t\t\t\toverlayContext.editorDocument,\n\t\t\t\toverlayContext.overlayRect\n\t\t\t) ?? [],\n\t\tblockElement,\n\t};\n}\n\n/**\n * Compute selection rects for a selection spanning multiple blocks.\n *\n * Normalizes to document order \u2014 for backward selections the block editor\n * reports start after end.\n *\n * @param start - Start position (block clientId + text index).\n * @param end - End position (block clientId + text index).\n * @param overlayContext - Shared editor document / overlay references.\n * @return Array of selection rectangles.\n */\nfunction computeMultiBlockRects(\n\tstart: ResolvedSelection,\n\tend: ResolvedSelection,\n\toverlayContext: OverlayContext\n): MultiBlockResult {\n\tlet docFirst = start;\n\tlet docLast = end;\n\tlet firstBlock = overlayContext.editorDocument.querySelector< HTMLElement >(\n\t\t`[data-block=\"${ docFirst.localClientId }\"]`\n\t);\n\tlet lastBlock = overlayContext.editorDocument.querySelector< HTMLElement >(\n\t\t`[data-block=\"${ docLast.localClientId }\"]`\n\t);\n\n\t// Swap to document order if needed.\n\tif ( firstBlock && lastBlock && isNodeBefore( lastBlock, firstBlock ) ) {\n\t\tdocFirst = end;\n\t\tdocLast = start;\n\t\t[ firstBlock, lastBlock ] = [ lastBlock, firstBlock ];\n\t}\n\n\tif (\n\t\t! firstBlock ||\n\t\t! lastBlock ||\n\t\tdocFirst.richTextOffset === null ||\n\t\tdocLast.richTextOffset === null ||\n\t\t! docFirst.localClientId ||\n\t\t! docLast.localClientId\n\t) {\n\t\treturn {\n\t\t\trects: [],\n\t\t\tfirstBlock: null,\n\t\t\tlastBlock: null,\n\t\t\tfirstBlockClientId: null,\n\t\t};\n\t}\n\n\tconst allRects: SelectionRect[] = [];\n\n\t// First block: from start offset to end of block.\n\tconst startRects = getSelectionRects(\n\t\tfirstBlock,\n\t\tdocFirst.richTextOffset,\n\t\tNumber.MAX_SAFE_INTEGER,\n\t\toverlayContext.editorDocument,\n\t\toverlayContext.overlayRect\n\t);\n\tif ( startRects ) {\n\t\tallRects.push( ...startRects );\n\t}\n\n\t// Intermediate blocks: full content.\n\tconst intermediateBlocks = getBlocksBetween(\n\t\tdocFirst.localClientId,\n\t\tdocLast.localClientId,\n\t\toverlayContext.editorDocument\n\t);\n\tfor ( const intermediateBlock of intermediateBlocks ) {\n\t\tconst rects = getFullBlockSelectionRects(\n\t\t\tintermediateBlock,\n\t\t\toverlayContext.editorDocument,\n\t\t\toverlayContext.overlayRect\n\t\t);\n\t\tallRects.push( ...rects );\n\t}\n\n\t// Last block: from 0 to end offset.\n\tconst endRects = getSelectionRects(\n\t\tlastBlock,\n\t\t0,\n\t\tdocLast.richTextOffset,\n\t\toverlayContext.editorDocument,\n\t\toverlayContext.overlayRect\n\t);\n\tif ( endRects ) {\n\t\tallRects.push( ...endRects );\n\t}\n\n\treturn {\n\t\trects: allRects,\n\t\tfirstBlock,\n\t\tlastBlock,\n\t\tfirstBlockClientId: docFirst.localClientId,\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -39,10 +39,12 @@ var import_i18n = require("@wordpress/i18n");
|
|
|
39
39
|
var import_avatar = __toESM(require("../collaborators-presence/avatar/index.cjs"));
|
|
40
40
|
var import_avatar_iframe_styles = require("./avatar-iframe-styles.cjs");
|
|
41
41
|
var import_overlay_iframe_styles = require("./overlay-iframe-styles.cjs");
|
|
42
|
+
var import_timing_utils = require("./timing-utils.cjs");
|
|
42
43
|
var import_use_block_highlighting = require("./use-block-highlighting.cjs");
|
|
43
44
|
var import_use_render_cursors = require("./use-render-cursors.cjs");
|
|
44
45
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
45
46
|
var RERENDER_DELAY_MS = 500;
|
|
47
|
+
var CURSOR_REDRAW_INTERVAL_MS = 1e4;
|
|
46
48
|
function Overlay({
|
|
47
49
|
blockEditorDocument,
|
|
48
50
|
postId,
|
|
@@ -76,6 +78,15 @@ function Overlay({
|
|
|
76
78
|
cleanupHighlights();
|
|
77
79
|
};
|
|
78
80
|
}, [rerenderCursorsAfterDelay, rerenderHighlightsAfterDelay]);
|
|
81
|
+
(0, import_element.useEffect)(() => {
|
|
82
|
+
if (cursors.length === 0) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
return (0, import_timing_utils.setDelayedInterval)(
|
|
86
|
+
rerenderCursorsAfterDelay,
|
|
87
|
+
CURSOR_REDRAW_INTERVAL_MS
|
|
88
|
+
);
|
|
89
|
+
}, [cursors.length, rerenderCursorsAfterDelay]);
|
|
79
90
|
const mergedRef = (0, import_compose.useMergeRefs)([
|
|
80
91
|
setOverlayElement,
|
|
81
92
|
resizeObserverRef
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/collaborators-overlay/overlay.tsx"],
|
|
4
|
-
"sourcesContent": ["import { useResizeObserver, useMergeRefs } from '@wordpress/compose';\nimport { useCallback, useEffect, useState } from '@wordpress/element';\nimport { __ } from '@wordpress/i18n';\n\nimport Avatar from '../collaborators-presence/avatar';\nimport { AVATAR_IFRAME_STYLES } from './avatar-iframe-styles';\nimport { OVERLAY_IFRAME_STYLES } from './overlay-iframe-styles';\nimport { useBlockHighlighting } from './use-block-highlighting';\nimport { useRenderCursors } from './use-render-cursors';\n\nconst RERENDER_DELAY_MS = 500;\n\ninterface OverlayProps {\n\tblockEditorDocument?: Document;\n\tpostId: number | null;\n\tpostType: string | null;\n}\n\n/**\n * This component is responsible for rendering the overlay components within the editor iframe.\n *\n * @param props - The overlay props.\n * @param props.blockEditorDocument - The block editor document.\n * @param props.postId - The ID of the post.\n * @param props.postType - The type of the post.\n * @return The Overlay component.\n */\nexport function Overlay( {\n\tblockEditorDocument,\n\tpostId,\n\tpostType,\n}: OverlayProps ) {\n\t// Use state for the overlay element so that the hook re-runs once the ref is attached.\n\tconst [ overlayElement, setOverlayElement ] =\n\t\tuseState< HTMLDivElement | null >( null );\n\n\tconst { cursors, rerenderCursorsAfterDelay } = useRenderCursors(\n\t\toverlayElement,\n\t\tblockEditorDocument ?? null,\n\t\tpostId ?? null,\n\t\tpostType ?? null,\n\t\tRERENDER_DELAY_MS\n\t);\n\n\tconst { highlights, rerenderHighlightsAfterDelay } = useBlockHighlighting(\n\t\toverlayElement,\n\t\tblockEditorDocument ?? null,\n\t\tpostId ?? null,\n\t\tpostType ?? null,\n\t\tRERENDER_DELAY_MS\n\t);\n\n\t// Detect layout changes on overlay (e.g. turning on \"Show Template\") and window\n\t// resizes, and re-render the cursors and block highlights.\n\tconst onResize = useCallback( () => {\n\t\trerenderCursorsAfterDelay();\n\t\trerenderHighlightsAfterDelay();\n\t}, [ rerenderCursorsAfterDelay, rerenderHighlightsAfterDelay ] );\n\tconst resizeObserverRef = useResizeObserver( onResize );\n\n\t// Trigger the initial position computation on mount.\n\tuseEffect( () => {\n\t\tconst cleanupCursors = rerenderCursorsAfterDelay();\n\t\tconst cleanupHighlights = rerenderHighlightsAfterDelay();\n\t\treturn () => {\n\t\t\tcleanupCursors();\n\t\t\tcleanupHighlights();\n\t\t};\n\t}, [ rerenderCursorsAfterDelay, rerenderHighlightsAfterDelay ] );\n\n\t// Merge the refs to use the same element for both overlay and resize observation\n\tconst mergedRef = useMergeRefs< HTMLDivElement | null >( [\n\t\tsetOverlayElement,\n\t\tresizeObserverRef,\n\t] );\n\n\t// This is a full overlay that covers the entire iframe document. Good for\n\t// scrollable elements like cursor indicators.\n\treturn (\n\t\t<div className=\"collaborators-overlay-full\" ref={ mergedRef }>\n\t\t\t<style>{ AVATAR_IFRAME_STYLES + OVERLAY_IFRAME_STYLES }</style>\n\t\t\t{ cursors.map( ( cursor ) => (\n\t\t\t\t<div key={ cursor.clientId }>\n\t\t\t\t\t{ ! cursor.isMe &&\n\t\t\t\t\t\tcursor.selectionRects?.map( ( rect, index ) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={ `${ cursor.clientId }-sel-${ index }` }\n\t\t\t\t\t\t\t\tclassName=\"collaborators-overlay-selection-rect\"\n\t\t\t\t\t\t\t\tstyle={ {\n\t\t\t\t\t\t\t\t\tleft: `${ rect.x }px`,\n\t\t\t\t\t\t\t\t\ttop: `${ rect.y }px`,\n\t\t\t\t\t\t\t\t\twidth: `${ rect.width }px`,\n\t\t\t\t\t\t\t\t\theight: `${ rect.height }px`,\n\t\t\t\t\t\t\t\t\tbackgroundColor: cursor.color,\n\t\t\t\t\t\t\t\t} }\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) ) }\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"collaborators-overlay-user\"\n\t\t\t\t\t\tstyle={ {\n\t\t\t\t\t\t\tleft: `${ cursor.x }px`,\n\t\t\t\t\t\t\ttop: `${ cursor.y }px`,\n\t\t\t\t\t\t} }\n\t\t\t\t\t>\n\t\t\t\t\t\t{ ! cursor.isMe && (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"collaborators-overlay-user-cursor\"\n\t\t\t\t\t\t\t\tstyle={ {\n\t\t\t\t\t\t\t\t\tbackgroundColor: cursor.color,\n\t\t\t\t\t\t\t\t\theight: `${ cursor.height }px`,\n\t\t\t\t\t\t\t\t} }\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) }\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"collaborators-overlay-user-label\"\n\t\t\t\t\t\t\tvariant=\"badge\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\tsrc={ cursor.avatarUrl }\n\t\t\t\t\t\t\tname={ cursor.userName }\n\t\t\t\t\t\t\tlabel={ cursor.isMe ? __( 'You' ) : undefined }\n\t\t\t\t\t\t\tborderColor={ cursor.color }\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) ) }\n\t\t\t{ highlights.map( ( highlight ) => (\n\t\t\t\t<Avatar\n\t\t\t\t\tkey={ highlight.blockId }\n\t\t\t\t\tclassName=\"collaborators-overlay-block-label\"\n\t\t\t\t\tvariant=\"badge\"\n\t\t\t\t\tsize=\"small\"\n\t\t\t\t\tsrc={ highlight.avatarUrl }\n\t\t\t\t\tname={ highlight.userName }\n\t\t\t\t\tborderColor={ highlight.color }\n\t\t\t\t\tstyle={ {\n\t\t\t\t\t\tleft: `${ highlight.x }px`,\n\t\t\t\t\t\ttop: `${ highlight.y }px`,\n\t\t\t\t\t} }\n\t\t\t\t/>\n\t\t\t) ) }\n\t\t</div>\n\t);\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAgD;AAChD,qBAAiD;AACjD,kBAAmB;AAEnB,oBAAmB;AACnB,kCAAqC;AACrC,mCAAsC;AACtC,oCAAqC;AACrC,gCAAiC;
|
|
4
|
+
"sourcesContent": ["import { useResizeObserver, useMergeRefs } from '@wordpress/compose';\nimport { useCallback, useEffect, useState } from '@wordpress/element';\nimport { __ } from '@wordpress/i18n';\n\nimport Avatar from '../collaborators-presence/avatar';\nimport { AVATAR_IFRAME_STYLES } from './avatar-iframe-styles';\nimport { OVERLAY_IFRAME_STYLES } from './overlay-iframe-styles';\nimport { setDelayedInterval } from './timing-utils';\nimport { useBlockHighlighting } from './use-block-highlighting';\nimport { useRenderCursors } from './use-render-cursors';\n\n// Milliseconds to wait after a change before recomputing cursor positions.\nconst RERENDER_DELAY_MS = 500;\n\n// Periodically recompute cursor positions to account for DOM layout\n// changes that don't trigger awareness state updates (e.g. a collaborator\n// applying formatting shifts text but the cursor's logical position is\n// unchanged). Only active when remote cursors are visible.\nconst CURSOR_REDRAW_INTERVAL_MS = 10_000;\n\ninterface OverlayProps {\n\tblockEditorDocument?: Document;\n\tpostId: number | null;\n\tpostType: string | null;\n}\n\n/**\n * This component is responsible for rendering the overlay components within the editor iframe.\n *\n * @param props - The overlay props.\n * @param props.blockEditorDocument - The block editor document.\n * @param props.postId - The ID of the post.\n * @param props.postType - The type of the post.\n * @return The Overlay component.\n */\nexport function Overlay( {\n\tblockEditorDocument,\n\tpostId,\n\tpostType,\n}: OverlayProps ) {\n\t// Use state for the overlay element so that the hook re-runs once the ref is attached.\n\tconst [ overlayElement, setOverlayElement ] =\n\t\tuseState< HTMLDivElement | null >( null );\n\n\tconst { cursors, rerenderCursorsAfterDelay } = useRenderCursors(\n\t\toverlayElement,\n\t\tblockEditorDocument ?? null,\n\t\tpostId ?? null,\n\t\tpostType ?? null,\n\t\tRERENDER_DELAY_MS\n\t);\n\n\tconst { highlights, rerenderHighlightsAfterDelay } = useBlockHighlighting(\n\t\toverlayElement,\n\t\tblockEditorDocument ?? null,\n\t\tpostId ?? null,\n\t\tpostType ?? null,\n\t\tRERENDER_DELAY_MS\n\t);\n\n\t// Detect layout changes on overlay (e.g. turning on \"Show Template\") and window\n\t// resizes, and re-render the cursors and block highlights.\n\tconst onResize = useCallback( () => {\n\t\trerenderCursorsAfterDelay();\n\t\trerenderHighlightsAfterDelay();\n\t}, [ rerenderCursorsAfterDelay, rerenderHighlightsAfterDelay ] );\n\tconst resizeObserverRef = useResizeObserver( onResize );\n\n\t// Trigger the initial position computation on mount.\n\tuseEffect( () => {\n\t\tconst cleanupCursors = rerenderCursorsAfterDelay();\n\t\tconst cleanupHighlights = rerenderHighlightsAfterDelay();\n\t\treturn () => {\n\t\t\tcleanupCursors();\n\t\t\tcleanupHighlights();\n\t\t};\n\t}, [ rerenderCursorsAfterDelay, rerenderHighlightsAfterDelay ] );\n\n\tuseEffect( () => {\n\t\tif ( cursors.length === 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn setDelayedInterval(\n\t\t\trerenderCursorsAfterDelay,\n\t\t\tCURSOR_REDRAW_INTERVAL_MS\n\t\t);\n\t}, [ cursors.length, rerenderCursorsAfterDelay ] );\n\n\t// Merge the refs to use the same element for both overlay and resize observation\n\tconst mergedRef = useMergeRefs< HTMLDivElement | null >( [\n\t\tsetOverlayElement,\n\t\tresizeObserverRef,\n\t] );\n\n\t// This is a full overlay that covers the entire iframe document. Good for\n\t// scrollable elements like cursor indicators.\n\treturn (\n\t\t<div className=\"collaborators-overlay-full\" ref={ mergedRef }>\n\t\t\t<style>{ AVATAR_IFRAME_STYLES + OVERLAY_IFRAME_STYLES }</style>\n\t\t\t{ cursors.map( ( cursor ) => (\n\t\t\t\t<div key={ cursor.clientId }>\n\t\t\t\t\t{ ! cursor.isMe &&\n\t\t\t\t\t\tcursor.selectionRects?.map( ( rect, index ) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={ `${ cursor.clientId }-sel-${ index }` }\n\t\t\t\t\t\t\t\tclassName=\"collaborators-overlay-selection-rect\"\n\t\t\t\t\t\t\t\tstyle={ {\n\t\t\t\t\t\t\t\t\tleft: `${ rect.x }px`,\n\t\t\t\t\t\t\t\t\ttop: `${ rect.y }px`,\n\t\t\t\t\t\t\t\t\twidth: `${ rect.width }px`,\n\t\t\t\t\t\t\t\t\theight: `${ rect.height }px`,\n\t\t\t\t\t\t\t\t\tbackgroundColor: cursor.color,\n\t\t\t\t\t\t\t\t} }\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) ) }\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"collaborators-overlay-user\"\n\t\t\t\t\t\tstyle={ {\n\t\t\t\t\t\t\tleft: `${ cursor.x }px`,\n\t\t\t\t\t\t\ttop: `${ cursor.y }px`,\n\t\t\t\t\t\t} }\n\t\t\t\t\t>\n\t\t\t\t\t\t{ ! cursor.isMe && (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"collaborators-overlay-user-cursor\"\n\t\t\t\t\t\t\t\tstyle={ {\n\t\t\t\t\t\t\t\t\tbackgroundColor: cursor.color,\n\t\t\t\t\t\t\t\t\theight: `${ cursor.height }px`,\n\t\t\t\t\t\t\t\t} }\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) }\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"collaborators-overlay-user-label\"\n\t\t\t\t\t\t\tvariant=\"badge\"\n\t\t\t\t\t\t\tsize=\"small\"\n\t\t\t\t\t\t\tsrc={ cursor.avatarUrl }\n\t\t\t\t\t\t\tname={ cursor.userName }\n\t\t\t\t\t\t\tlabel={ cursor.isMe ? __( 'You' ) : undefined }\n\t\t\t\t\t\t\tborderColor={ cursor.color }\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) ) }\n\t\t\t{ highlights.map( ( highlight ) => (\n\t\t\t\t<Avatar\n\t\t\t\t\tkey={ highlight.blockId }\n\t\t\t\t\tclassName=\"collaborators-overlay-block-label\"\n\t\t\t\t\tvariant=\"badge\"\n\t\t\t\t\tsize=\"small\"\n\t\t\t\t\tsrc={ highlight.avatarUrl }\n\t\t\t\t\tname={ highlight.userName }\n\t\t\t\t\tborderColor={ highlight.color }\n\t\t\t\t\tstyle={ {\n\t\t\t\t\t\tleft: `${ highlight.x }px`,\n\t\t\t\t\t\ttop: `${ highlight.y }px`,\n\t\t\t\t\t} }\n\t\t\t\t/>\n\t\t\t) ) }\n\t\t</div>\n\t);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["Avatar"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// packages/editor/src/components/collaborators-overlay/timing-utils.ts
|
|
21
|
+
var timing_utils_exports = {};
|
|
22
|
+
__export(timing_utils_exports, {
|
|
23
|
+
setDelayedInterval: () => setDelayedInterval
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(timing_utils_exports);
|
|
26
|
+
function setDelayedInterval(callback, delayMs) {
|
|
27
|
+
let timerHandle = null;
|
|
28
|
+
const runner = () => {
|
|
29
|
+
try {
|
|
30
|
+
callback();
|
|
31
|
+
} catch (error) {
|
|
32
|
+
}
|
|
33
|
+
timerHandle = setTimeout(runner, delayMs);
|
|
34
|
+
};
|
|
35
|
+
timerHandle = setTimeout(runner, delayMs);
|
|
36
|
+
return () => {
|
|
37
|
+
if (timerHandle) {
|
|
38
|
+
clearTimeout(timerHandle);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
43
|
+
0 && (module.exports = {
|
|
44
|
+
setDelayedInterval
|
|
45
|
+
});
|
|
46
|
+
//# sourceMappingURL=timing-utils.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/components/collaborators-overlay/timing-utils.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Like setInterval but chains setTimeout calls, so the delay is measured from\n * the end of one run to the start of the next. This prevents callbacks from\n * stacking up when the main thread is busy.\n *\n * @param callback The function to call repeatedly.\n * @param delayMs Milliseconds between runs.\n * @return A cleanup function that stops the timer.\n */\nexport function setDelayedInterval( callback: () => void, delayMs: number ) {\n\tlet timerHandle: ReturnType< typeof setTimeout > | null = null;\n\n\tconst runner = () => {\n\t\ttry {\n\t\t\tcallback();\n\t\t} catch ( error ) {\n\t\t\t// Do nothing\n\t\t}\n\n\t\ttimerHandle = setTimeout( runner, delayMs );\n\t};\n\n\ttimerHandle = setTimeout( runner, delayMs );\n\n\treturn () => {\n\t\tif ( timerHandle ) {\n\t\t\tclearTimeout( timerHandle );\n\t\t}\n\t};\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASO,SAAS,mBAAoB,UAAsB,SAAkB;AAC3E,MAAI,cAAsD;AAE1D,QAAM,SAAS,MAAM;AACpB,QAAI;AACH,eAAS;AAAA,IACV,SAAU,OAAQ;AAAA,IAElB;AAEA,kBAAc,WAAY,QAAQ,OAAQ;AAAA,EAC3C;AAEA,gBAAc,WAAY,QAAQ,OAAQ;AAE1C,SAAO,MAAM;AACZ,QAAK,aAAc;AAClB,mBAAc,WAAY;AAAA,IAC3B;AAAA,EACD;AACD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/collaborators-overlay/use-render-cursors.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tprivateApis as coreDataPrivateApis,\n\tSelectionType,\n\ttype PostEditorAwarenessState as ActiveCollaborator,\n} from '@wordpress/core-data';\nimport { useSelect } from '@wordpress/data';\nimport { useEffect, useState } from '@wordpress/element';\nimport { store as preferencesStore } from '@wordpress/preferences';\nimport type { ResolvedSelection } from '@wordpress/core-data';\n\nimport { unlock } from '../../lock-unlock';\nimport { getAvatarUrl } from './get-avatar-url';\nimport { getAvatarBorderColor } from '../collab-sidebar/utils';\nimport { computeSelectionVisual } from './compute-selection';\nimport { useDebouncedRecompute } from './use-debounced-recompute';\nimport type { SelectionRect } from './cursor-dom-utils';\n\nconst { useActiveCollaborators, useResolvedSelection } =\n\tunlock( coreDataPrivateApis );\n\nexport type { SelectionRect };\n\nexport interface CursorData {\n\tuserName: string;\n\tclientId: number;\n\tcolor: string;\n\tavatarUrl?: string;\n\tx: number;\n\ty: number;\n\theight: number;\n\tisMe?: boolean;\n\tselectionRects?: SelectionRect[];\n}\n\n/**\n * Custom hook that computes cursor positions for each remote user in the editor.\n *\n * @param overlayElement - The overlay element\n * @param blockEditorDocument - The block editor document\n * @param postId - The ID of the post\n * @param postType - The type of the post\n * @param delayMs - Milliseconds to wait before recomputing cursor positions.\n * @return An array of cursor data for rendering, and a function to trigger a delayed recompute.\n */\nexport function useRenderCursors(\n\toverlayElement: HTMLElement | null,\n\tblockEditorDocument: Document | null,\n\tpostId: number | null,\n\tpostType: string | null,\n\tdelayMs: number\n): { cursors: CursorData[]; rerenderCursorsAfterDelay: () => () => void } {\n\tconst sortedUsers = useActiveCollaborators(\n\t\tpostId ?? null,\n\t\tpostType ?? null\n\t);\n\tconst resolveSelection = useResolvedSelection(\n\t\tpostId ?? null,\n\t\tpostType ?? null\n\t);\n\n\tconst showOwnCursor = useSelect(\n\t\t( select ) =>\n\t\t\tselect( preferencesStore ).get( 'core', 'showCollaborationCursor' ),\n\t\t[]\n\t);\n\n\tconst [ cursorPositions, setCursorPositions ] = useState< CursorData[] >(\n\t\t[]\n\t);\n\n\t// Bump this counter to force the effect to re-run (e.g. after a layout shift).\n\tconst [ recomputeToken, rerenderCursorsAfterDelay ] =\n\t\tuseDebouncedRecompute( delayMs );\n\n\t// All DOM position computations live inside useEffect.\n\tuseEffect( () => {\n\t\tif ( ! overlayElement || ! blockEditorDocument ) {\n\t\t\tsetCursorPositions( [] );\n\t\t\treturn;\n\t\t}\n\n\t\t// Pre-compute the overlay rect once, same for every user.\n\t\tconst overlayRect = overlayElement.getBoundingClientRect();\n\t\tconst overlayContext = {\n\t\t\teditorDocument: blockEditorDocument,\n\t\t\toverlayRect,\n\t\t};\n\n\t\tconst results: CursorData[] = [];\n\n\t\tconst hasOtherCollaborators = sortedUsers.some(\n\t\t\t( u: ActiveCollaborator ) => ! u.isMe\n\t\t);\n\n\t\tsortedUsers.forEach( ( user: ActiveCollaborator ) => {\n\t\t\tif ( user.isMe && ( ! showOwnCursor || ! hasOtherCollaborators ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst selection = user.editorState?.selection ?? {\n\t\t\t\ttype: SelectionType.None,\n\t\t\t};\n\n\t\t\tlet start: ResolvedSelection = {\n\t\t\t\
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAIO;AACP,kBAA0B;AAC1B,qBAAoC;AACpC,yBAA0C;AAG1C,yBAAuB;AACvB,4BAA6B;AAC7B,mBAAqC;AACrC,+BAAuC;AACvC,qCAAsC;AAGtC,IAAM,EAAE,wBAAwB,qBAAqB,QACpD,2BAAQ,iBAAAA,WAAoB;AA0BtB,SAAS,iBACf,gBACA,qBACA,QACA,UACA,SACyE;AACzE,QAAM,cAAc;AAAA,IACnB,UAAU;AAAA,IACV,YAAY;AAAA,EACb;AACA,QAAM,mBAAmB;AAAA,IACxB,UAAU;AAAA,IACV,YAAY;AAAA,EACb;AAEA,QAAM,oBAAgB;AAAA,IACrB,CAAE,WACD,OAAQ,mBAAAC,KAAiB,EAAE,IAAK,QAAQ,yBAA0B;AAAA,IACnE,CAAC;AAAA,EACF;AAEA,QAAM,CAAE,iBAAiB,kBAAmB,QAAI;AAAA,IAC/C,CAAC;AAAA,EACF;AAGA,QAAM,CAAE,gBAAgB,yBAA0B,QACjD,sDAAuB,OAAQ;AAGhC,gCAAW,MAAM;AAChB,QAAK,CAAE,kBAAkB,CAAE,qBAAsB;AAChD,yBAAoB,CAAC,CAAE;AACvB;AAAA,IACD;AAGA,UAAM,cAAc,eAAe,sBAAsB;AACzD,UAAM,iBAAiB;AAAA,MACtB,gBAAgB;AAAA,MAChB;AAAA,IACD;AAEA,UAAM,UAAwB,CAAC;AAE/B,UAAM,wBAAwB,YAAY;AAAA,MACzC,CAAE,MAA2B,CAAE,EAAE;AAAA,IAClC;AAEA,gBAAY,QAAS,CAAE,SAA8B;AACpD,UAAK,KAAK,SAAU,CAAE,iBAAiB,CAAE,wBAA0B;AAClE;AAAA,MACD;AAEA,YAAM,YAAY,KAAK,aAAa,aAAa;AAAA,QAChD,MAAM,+BAAc;AAAA,MACrB;AAEA,UAAI,QAA2B;AAAA,QAC9B,
|
|
4
|
+
"sourcesContent": ["import {\n\tprivateApis as coreDataPrivateApis,\n\tSelectionType,\n\ttype PostEditorAwarenessState as ActiveCollaborator,\n} from '@wordpress/core-data';\nimport { useSelect } from '@wordpress/data';\nimport { useEffect, useState } from '@wordpress/element';\nimport { store as preferencesStore } from '@wordpress/preferences';\nimport type { ResolvedSelection } from '@wordpress/core-data';\n\nimport { unlock } from '../../lock-unlock';\nimport { getAvatarUrl } from './get-avatar-url';\nimport { getAvatarBorderColor } from '../collab-sidebar/utils';\nimport { computeSelectionVisual } from './compute-selection';\nimport { useDebouncedRecompute } from './use-debounced-recompute';\nimport type { SelectionRect } from './cursor-dom-utils';\n\nconst { useActiveCollaborators, useResolvedSelection } =\n\tunlock( coreDataPrivateApis );\n\nexport type { SelectionRect };\n\nexport interface CursorData {\n\tuserName: string;\n\tclientId: number;\n\tcolor: string;\n\tavatarUrl?: string;\n\tx: number;\n\ty: number;\n\theight: number;\n\tisMe?: boolean;\n\tselectionRects?: SelectionRect[];\n}\n\n/**\n * Custom hook that computes cursor positions for each remote user in the editor.\n *\n * @param overlayElement - The overlay element\n * @param blockEditorDocument - The block editor document\n * @param postId - The ID of the post\n * @param postType - The type of the post\n * @param delayMs - Milliseconds to wait before recomputing cursor positions.\n * @return An array of cursor data for rendering, and a function to trigger a delayed recompute.\n */\nexport function useRenderCursors(\n\toverlayElement: HTMLElement | null,\n\tblockEditorDocument: Document | null,\n\tpostId: number | null,\n\tpostType: string | null,\n\tdelayMs: number\n): { cursors: CursorData[]; rerenderCursorsAfterDelay: () => () => void } {\n\tconst sortedUsers = useActiveCollaborators(\n\t\tpostId ?? null,\n\t\tpostType ?? null\n\t);\n\tconst resolveSelection = useResolvedSelection(\n\t\tpostId ?? null,\n\t\tpostType ?? null\n\t);\n\n\tconst showOwnCursor = useSelect(\n\t\t( select ) =>\n\t\t\tselect( preferencesStore ).get( 'core', 'showCollaborationCursor' ),\n\t\t[]\n\t);\n\n\tconst [ cursorPositions, setCursorPositions ] = useState< CursorData[] >(\n\t\t[]\n\t);\n\n\t// Bump this counter to force the effect to re-run (e.g. after a layout shift).\n\tconst [ recomputeToken, rerenderCursorsAfterDelay ] =\n\t\tuseDebouncedRecompute( delayMs );\n\n\t// All DOM position computations live inside useEffect.\n\tuseEffect( () => {\n\t\tif ( ! overlayElement || ! blockEditorDocument ) {\n\t\t\tsetCursorPositions( [] );\n\t\t\treturn;\n\t\t}\n\n\t\t// Pre-compute the overlay rect once, same for every user.\n\t\tconst overlayRect = overlayElement.getBoundingClientRect();\n\t\tconst overlayContext = {\n\t\t\teditorDocument: blockEditorDocument,\n\t\t\toverlayRect,\n\t\t};\n\n\t\tconst results: CursorData[] = [];\n\n\t\tconst hasOtherCollaborators = sortedUsers.some(\n\t\t\t( u: ActiveCollaborator ) => ! u.isMe\n\t\t);\n\n\t\tsortedUsers.forEach( ( user: ActiveCollaborator ) => {\n\t\t\tif ( user.isMe && ( ! showOwnCursor || ! hasOtherCollaborators ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst selection = user.editorState?.selection ?? {\n\t\t\t\ttype: SelectionType.None,\n\t\t\t};\n\n\t\t\tlet start: ResolvedSelection = {\n\t\t\t\trichTextOffset: null,\n\t\t\t\tlocalClientId: null,\n\t\t\t};\n\t\t\tlet end: ResolvedSelection | undefined;\n\n\t\t\tif ( selection.type === SelectionType.Cursor ) {\n\t\t\t\ttry {\n\t\t\t\t\tstart = resolveSelection( selection );\n\t\t\t\t} catch {\n\t\t\t\t\t// Selection may reference a stale Yjs position.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tselection.type === SelectionType.SelectionInOneBlock ||\n\t\t\t\tselection.type === SelectionType.SelectionInMultipleBlocks\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\tstart = resolveSelection( {\n\t\t\t\t\t\ttype: SelectionType.Cursor,\n\t\t\t\t\t\tcursorPosition: selection.cursorStartPosition,\n\t\t\t\t\t} );\n\n\t\t\t\t\tend = resolveSelection( {\n\t\t\t\t\t\ttype: SelectionType.Cursor,\n\t\t\t\t\t\tcursorPosition: selection.cursorEndPosition,\n\t\t\t\t\t} );\n\t\t\t\t} catch {\n\t\t\t\t\t// Selection may reference a stale Yjs position.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst userName = user.collaboratorInfo.name;\n\t\t\tconst clientId = user.clientId;\n\t\t\tconst color = user.isMe\n\t\t\t\t? 'var(--wp-admin-theme-color)'\n\t\t\t\t: getAvatarBorderColor( user.collaboratorInfo.id );\n\t\t\tconst avatarUrl = getAvatarUrl( user.collaboratorInfo.avatar_urls );\n\n\t\t\tconst selectionVisual = computeSelectionVisual(\n\t\t\t\tselection,\n\t\t\t\tstart,\n\t\t\t\tend,\n\t\t\t\toverlayContext\n\t\t\t);\n\n\t\t\tif ( selectionVisual.coords ) {\n\t\t\t\tconst cursorData: CursorData = {\n\t\t\t\t\tuserName,\n\t\t\t\t\tclientId,\n\t\t\t\t\tcolor,\n\t\t\t\t\tavatarUrl,\n\t\t\t\t\tisMe: user.isMe,\n\t\t\t\t\t...selectionVisual.coords,\n\t\t\t\t};\n\n\t\t\t\tif ( selectionVisual.selectionRects ) {\n\t\t\t\t\tcursorData.selectionRects = selectionVisual.selectionRects;\n\t\t\t\t}\n\n\t\t\t\tresults.push( cursorData );\n\t\t\t}\n\t\t} );\n\n\t\tsetCursorPositions( results );\n\t}, [\n\t\tblockEditorDocument,\n\t\tresolveSelection,\n\t\toverlayElement,\n\t\tsortedUsers,\n\t\tshowOwnCursor,\n\t\trecomputeToken,\n\t] );\n\n\treturn { cursors: cursorPositions, rerenderCursorsAfterDelay };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["coreDataPrivateApis", "preferencesStore"]
|
|
7
7
|
}
|
|
@@ -32,15 +32,28 @@ var import_hooks = require("@wordpress/hooks");
|
|
|
32
32
|
var import_compose = require("@wordpress/compose");
|
|
33
33
|
var import_core_data = require("@wordpress/core-data");
|
|
34
34
|
var import_lock_unlock = require("../../lock-unlock.cjs");
|
|
35
|
+
var import_sync_error_messages = require("../../utils/sync-error-messages.cjs");
|
|
35
36
|
var import_store = require("../../store/index.cjs");
|
|
36
37
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
37
38
|
function CollaborationContext() {
|
|
38
|
-
const isCollaborationSupported = (0, import_data.useSelect)(
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const { isCollaborationSupported, syncConnectionStatus } = (0, import_data.useSelect)(
|
|
40
|
+
(select) => {
|
|
41
|
+
const selectors = (0, import_lock_unlock.unlock)(select(import_core_data.store));
|
|
42
|
+
return {
|
|
43
|
+
isCollaborationSupported: selectors.isCollaborationSupported(),
|
|
44
|
+
syncConnectionStatus: selectors.getSyncConnectionStatus()
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
[]
|
|
48
|
+
);
|
|
41
49
|
if (isCollaborationSupported) {
|
|
42
50
|
return null;
|
|
43
51
|
}
|
|
52
|
+
if (import_sync_error_messages.DOCUMENT_SIZE_LIMIT_EXCEEDED === syncConnectionStatus?.error?.code) {
|
|
53
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: (0, import_i18n.__)(
|
|
54
|
+
"Because this post is too large for real-time collaboration, only one person can edit at a time."
|
|
55
|
+
) });
|
|
56
|
+
}
|
|
44
57
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: (0, import_i18n.__)(
|
|
45
58
|
"Because this post uses plugins that aren\u2019t compatible with real-time collaboration, only one person can edit at a time."
|
|
46
59
|
) });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/post-locked-modal/index.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { __, sprintf } from '@wordpress/i18n';\nimport {\n\tModal,\n\tButton,\n\tExternalLink,\n\t__experimentalHStack as HStack,\n\twithFilters,\n} from '@wordpress/components';\nimport { useSelect, useDispatch } from '@wordpress/data';\nimport { addQueryArgs } from '@wordpress/url';\nimport { useEffect, createInterpolateElement } from '@wordpress/element';\nimport { addAction, removeAction } from '@wordpress/hooks';\nimport { useInstanceId } from '@wordpress/compose';\nimport { store as coreStore } from '@wordpress/core-data';\nimport { unlock } from '../../lock-unlock';\n\n/**\n * Internal dependencies\n */\nimport { store as editorStore } from '../../store';\n\nfunction CollaborationContext() {\n\tconst isCollaborationSupported = useSelect( ( select ) => {\n\t\treturn unlock( select( coreStore ) ).isCollaborationSupported();\n\t}, [] );\n\n\tif ( isCollaborationSupported ) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<p>\n\t\t\t{ __(\n\t\t\t\t'Because this post uses plugins that aren\u2019t compatible with real-time collaboration, only one person can edit at a time.'\n\t\t\t) }\n\t\t</p>\n\t);\n}\n\nfunction PostLockedModal() {\n\tconst instanceId = useInstanceId( PostLockedModal );\n\tconst hookName = 'core/editor/post-locked-modal-' + instanceId;\n\tconst { autosave, updatePostLock } = useDispatch( editorStore );\n\tconst {\n\t\tisCollaborationEnabled,\n\t\tisLocked,\n\t\tisTakeover,\n\t\tuser,\n\t\tpostId,\n\t\tpostLockUtils,\n\t\tactivePostLock,\n\t\tpostType,\n\t\tpreviewLink,\n\t} = useSelect( ( select ) => {\n\t\tconst {\n\t\t\tisCollaborationEnabledForCurrentPost,\n\t\t\tisPostLocked,\n\t\t\tisPostLockTakeover,\n\t\t\tgetPostLockUser,\n\t\t\tgetCurrentPostId,\n\t\t\tgetActivePostLock,\n\t\t\tgetEditedPostAttribute,\n\t\t\tgetEditedPostPreviewLink,\n\t\t\tgetEditorSettings,\n\t\t} = select( editorStore );\n\t\tconst { getPostType } = select( coreStore );\n\t\treturn {\n\t\t\tisCollaborationEnabled: isCollaborationEnabledForCurrentPost(),\n\t\t\tisLocked: isPostLocked(),\n\t\t\tisTakeover: isPostLockTakeover(),\n\t\t\tuser: getPostLockUser(),\n\t\t\tpostId: getCurrentPostId(),\n\t\t\tpostLockUtils: getEditorSettings().postLockUtils,\n\t\t\tactivePostLock: getActivePostLock(),\n\t\t\tpostType: getPostType( getEditedPostAttribute( 'type' ) ),\n\t\t\tpreviewLink: getEditedPostPreviewLink(),\n\t\t};\n\t}, [] );\n\n\tuseEffect( () => {\n\t\t/**\n\t\t * Keep the lock refreshed.\n\t\t *\n\t\t * When the user does not send a heartbeat in a heartbeat-tick\n\t\t * the user is no longer editing and another user can start editing.\n\t\t *\n\t\t * @param {Object} data Data to send in the heartbeat request.\n\t\t */\n\t\tfunction sendPostLock( data ) {\n\t\t\tif ( isLocked ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tdata[ 'wp-refresh-post-lock' ] = {\n\t\t\t\tlock: activePostLock,\n\t\t\t\tpost_id: postId,\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Refresh post locks: update the lock string or show the dialog if somebody has taken over editing.\n\t\t *\n\t\t * @param {Object} data Data received in the heartbeat request\n\t\t */\n\t\tfunction receivePostLock( data ) {\n\t\t\tif ( ! data[ 'wp-refresh-post-lock' ] ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst received = data[ 'wp-refresh-post-lock' ];\n\t\t\tif ( received.lock_error ) {\n\t\t\t\t// Auto save and display the takeover modal.\n\t\t\t\tautosave();\n\t\t\t\tupdatePostLock( {\n\t\t\t\t\tisLocked: true,\n\t\t\t\t\tisTakeover: true,\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tname: received.lock_error.name,\n\t\t\t\t\t\tavatar: received.lock_error.avatar_src_2x,\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t} else if ( received.new_lock ) {\n\t\t\t\tupdatePostLock( {\n\t\t\t\t\tisLocked: false,\n\t\t\t\t\tactivePostLock: received.new_lock,\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Unlock the post before the window is exited.\n\t\t */\n\t\tfunction releasePostLock() {\n\t\t\tif ( isLocked || ! activePostLock ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst data = new window.FormData();\n\t\t\tdata.append( 'action', 'wp-remove-post-lock' );\n\t\t\tdata.append( '_wpnonce', postLockUtils.unlockNonce );\n\t\t\tdata.append( 'post_ID', postId );\n\t\t\tdata.append( 'active_post_lock', activePostLock );\n\n\t\t\tif ( window.navigator.sendBeacon ) {\n\t\t\t\twindow.navigator.sendBeacon( postLockUtils.ajaxUrl, data );\n\t\t\t} else {\n\t\t\t\tconst xhr = new window.XMLHttpRequest();\n\t\t\t\txhr.open( 'POST', postLockUtils.ajaxUrl, false );\n\t\t\t\txhr.send( data );\n\t\t\t}\n\t\t}\n\n\t\t// Details on these events on the Heartbeat API docs\n\t\t// https://developer.wordpress.org/plugins/javascript/heartbeat-api/\n\t\taddAction( 'heartbeat.send', hookName, sendPostLock );\n\t\taddAction( 'heartbeat.tick', hookName, receivePostLock );\n\t\twindow.addEventListener( 'beforeunload', releasePostLock );\n\n\t\treturn () => {\n\t\t\tremoveAction( 'heartbeat.send', hookName );\n\t\t\tremoveAction( 'heartbeat.tick', hookName );\n\t\t\twindow.removeEventListener( 'beforeunload', releasePostLock );\n\t\t};\n\t}, [] );\n\n\tif ( ! isLocked ) {\n\t\treturn null;\n\t}\n\n\t// Avoid sending the modal if sync is supported, but retain functionality around locks etc.\n\tif ( isCollaborationEnabled ) {\n\t\treturn null;\n\t}\n\n\tconst userDisplayName = user.name;\n\tconst userAvatar = user.avatar;\n\n\tconst unlockUrl = addQueryArgs( 'post.php', {\n\t\t'get-post-lock': '1',\n\t\tlockKey: true,\n\t\tpost: postId,\n\t\taction: 'edit',\n\t\t_wpnonce: postLockUtils.nonce,\n\t} );\n\tconst allPostsUrl = addQueryArgs( 'edit.php', {\n\t\tpost_type: postType?.slug,\n\t} );\n\tconst allPostsLabel = __( 'Exit editor' );\n\treturn (\n\t\t<Modal\n\t\t\ttitle={\n\t\t\t\tisTakeover\n\t\t\t\t\t? __( 'Someone else has taken over this post' )\n\t\t\t\t\t: __( 'This post is already being edited' )\n\t\t\t}\n\t\t\tfocusOnMount\n\t\t\tshouldCloseOnClickOutside={ false }\n\t\t\tshouldCloseOnEsc={ false }\n\t\t\tisDismissible={ false }\n\t\t\t// Do not remove this class, as this class is used by third party plugins.\n\t\t\tclassName=\"editor-post-locked-modal\"\n\t\t\tsize=\"medium\"\n\t\t>\n\t\t\t<HStack alignment=\"top\" spacing={ 6 }>\n\t\t\t\t{ !! userAvatar && (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={ userAvatar }\n\t\t\t\t\t\talt={ __( 'Avatar' ) }\n\t\t\t\t\t\tclassName=\"editor-post-locked-modal__avatar\"\n\t\t\t\t\t\twidth={ 64 }\n\t\t\t\t\t\theight={ 64 }\n\t\t\t\t\t/>\n\t\t\t\t) }\n\t\t\t\t<div>\n\t\t\t\t\t{ !! isTakeover && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t{ createInterpolateElement(\n\t\t\t\t\t\t\t\t\tuserDisplayName\n\t\t\t\t\t\t\t\t\t\t? sprintf(\n\t\t\t\t\t\t\t\t\t\t\t\t/* translators: %s: user's display name */\n\t\t\t\t\t\t\t\t\t\t\t\t__(\n\t\t\t\t\t\t\t\t\t\t\t\t\t'<strong>%s</strong> now has editing control of this post (<PreviewLink />). Don\u2019t worry, your changes up to this moment have been saved.'\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\tuserDisplayName\n\t\t\t\t\t\t\t\t\t\t )\n\t\t\t\t\t\t\t\t\t\t: __(\n\t\t\t\t\t\t\t\t\t\t\t\t'Another user now has editing control of this post (<PreviewLink />). Don\u2019t worry, your changes up to this moment have been saved.'\n\t\t\t\t\t\t\t\t\t\t ),\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstrong: <strong />,\n\t\t\t\t\t\t\t\t\t\tPreviewLink: (\n\t\t\t\t\t\t\t\t\t\t\t<ExternalLink href={ previewLink }>\n\t\t\t\t\t\t\t\t\t\t\t\t{ __( 'preview' ) }\n\t\t\t\t\t\t\t\t\t\t\t</ExternalLink>\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<CollaborationContext />\n\t\t\t\t\t\t</>\n\t\t\t\t\t) }\n\t\t\t\t\t{ ! isTakeover && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t{ createInterpolateElement(\n\t\t\t\t\t\t\t\t\tuserDisplayName\n\t\t\t\t\t\t\t\t\t\t? sprintf(\n\t\t\t\t\t\t\t\t\t\t\t\t/* translators: %s: user's display name */\n\t\t\t\t\t\t\t\t\t\t\t\t__(\n\t\t\t\t\t\t\t\t\t\t\t\t\t'<strong>%s</strong> is currently working on this post (<PreviewLink />), which means you cannot make changes, unless you take over.'\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\tuserDisplayName\n\t\t\t\t\t\t\t\t\t\t )\n\t\t\t\t\t\t\t\t\t\t: __(\n\t\t\t\t\t\t\t\t\t\t\t\t'Another user is currently working on this post (<PreviewLink />), which means you cannot make changes, unless you take over.'\n\t\t\t\t\t\t\t\t\t\t ),\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstrong: <strong />,\n\t\t\t\t\t\t\t\t\t\tPreviewLink: (\n\t\t\t\t\t\t\t\t\t\t\t<ExternalLink href={ previewLink }>\n\t\t\t\t\t\t\t\t\t\t\t\t{ __( 'preview' ) }\n\t\t\t\t\t\t\t\t\t\t\t</ExternalLink>\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<CollaborationContext />\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t{ __(\n\t\t\t\t\t\t\t\t\t'If you take over, the other user will lose editing control to the post, but their changes will be saved.'\n\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) }\n\n\t\t\t\t\t<HStack\n\t\t\t\t\t\tclassName=\"editor-post-locked-modal__buttons\"\n\t\t\t\t\t\tjustify=\"flex-end\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{ ! isTakeover && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t\thref={ unlockUrl }\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{ __( 'Take over' ) }\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t) }\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t\thref={ allPostsUrl }\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{ allPostsLabel }\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</HStack>\n\t\t\t\t</div>\n\t\t\t</HStack>\n\t\t</Modal>\n\t);\n}\n\n/**\n * A modal component that is displayed when a post is locked for editing by another user.\n * The modal provides information about the lock status and options to take over or exit the editor.\n *\n * @return {React.ReactNode} The rendered PostLockedModal component.\n */\nexport default globalThis.IS_GUTENBERG_PLUGIN\n\t? withFilters( 'editor.PostLockedModal' )( PostLockedModal )\n\t: PostLockedModal;\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAA4B;AAC5B,wBAMO;AACP,kBAAuC;AACvC,iBAA6B;AAC7B,qBAAoD;AACpD,mBAAwC;AACxC,qBAA8B;AAC9B,uBAAmC;AACnC,yBAAuB;
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { __, sprintf } from '@wordpress/i18n';\nimport {\n\tModal,\n\tButton,\n\tExternalLink,\n\t__experimentalHStack as HStack,\n\twithFilters,\n} from '@wordpress/components';\nimport { useSelect, useDispatch } from '@wordpress/data';\nimport { addQueryArgs } from '@wordpress/url';\nimport { useEffect, createInterpolateElement } from '@wordpress/element';\nimport { addAction, removeAction } from '@wordpress/hooks';\nimport { useInstanceId } from '@wordpress/compose';\nimport { store as coreStore } from '@wordpress/core-data';\nimport { unlock } from '../../lock-unlock';\nimport { DOCUMENT_SIZE_LIMIT_EXCEEDED } from '../../utils/sync-error-messages';\n\n/**\n * Internal dependencies\n */\nimport { store as editorStore } from '../../store';\n\nfunction CollaborationContext() {\n\tconst { isCollaborationSupported, syncConnectionStatus } = useSelect(\n\t\t( select ) => {\n\t\t\tconst selectors = unlock( select( coreStore ) );\n\t\t\treturn {\n\t\t\t\tisCollaborationSupported: selectors.isCollaborationSupported(),\n\t\t\t\tsyncConnectionStatus: selectors.getSyncConnectionStatus(),\n\t\t\t};\n\t\t},\n\t\t[]\n\t);\n\n\tif ( isCollaborationSupported ) {\n\t\treturn null;\n\t}\n\n\tif ( DOCUMENT_SIZE_LIMIT_EXCEEDED === syncConnectionStatus?.error?.code ) {\n\t\treturn (\n\t\t\t<p>\n\t\t\t\t{ __(\n\t\t\t\t\t'Because this post is too large for real-time collaboration, only one person can edit at a time.'\n\t\t\t\t) }\n\t\t\t</p>\n\t\t);\n\t}\n\n\treturn (\n\t\t<p>\n\t\t\t{ __(\n\t\t\t\t'Because this post uses plugins that aren\u2019t compatible with real-time collaboration, only one person can edit at a time.'\n\t\t\t) }\n\t\t</p>\n\t);\n}\n\nfunction PostLockedModal() {\n\tconst instanceId = useInstanceId( PostLockedModal );\n\tconst hookName = 'core/editor/post-locked-modal-' + instanceId;\n\tconst { autosave, updatePostLock } = useDispatch( editorStore );\n\tconst {\n\t\tisCollaborationEnabled,\n\t\tisLocked,\n\t\tisTakeover,\n\t\tuser,\n\t\tpostId,\n\t\tpostLockUtils,\n\t\tactivePostLock,\n\t\tpostType,\n\t\tpreviewLink,\n\t} = useSelect( ( select ) => {\n\t\tconst {\n\t\t\tisCollaborationEnabledForCurrentPost,\n\t\t\tisPostLocked,\n\t\t\tisPostLockTakeover,\n\t\t\tgetPostLockUser,\n\t\t\tgetCurrentPostId,\n\t\t\tgetActivePostLock,\n\t\t\tgetEditedPostAttribute,\n\t\t\tgetEditedPostPreviewLink,\n\t\t\tgetEditorSettings,\n\t\t} = select( editorStore );\n\t\tconst { getPostType } = select( coreStore );\n\t\treturn {\n\t\t\tisCollaborationEnabled: isCollaborationEnabledForCurrentPost(),\n\t\t\tisLocked: isPostLocked(),\n\t\t\tisTakeover: isPostLockTakeover(),\n\t\t\tuser: getPostLockUser(),\n\t\t\tpostId: getCurrentPostId(),\n\t\t\tpostLockUtils: getEditorSettings().postLockUtils,\n\t\t\tactivePostLock: getActivePostLock(),\n\t\t\tpostType: getPostType( getEditedPostAttribute( 'type' ) ),\n\t\t\tpreviewLink: getEditedPostPreviewLink(),\n\t\t};\n\t}, [] );\n\n\tuseEffect( () => {\n\t\t/**\n\t\t * Keep the lock refreshed.\n\t\t *\n\t\t * When the user does not send a heartbeat in a heartbeat-tick\n\t\t * the user is no longer editing and another user can start editing.\n\t\t *\n\t\t * @param {Object} data Data to send in the heartbeat request.\n\t\t */\n\t\tfunction sendPostLock( data ) {\n\t\t\tif ( isLocked ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tdata[ 'wp-refresh-post-lock' ] = {\n\t\t\t\tlock: activePostLock,\n\t\t\t\tpost_id: postId,\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Refresh post locks: update the lock string or show the dialog if somebody has taken over editing.\n\t\t *\n\t\t * @param {Object} data Data received in the heartbeat request\n\t\t */\n\t\tfunction receivePostLock( data ) {\n\t\t\tif ( ! data[ 'wp-refresh-post-lock' ] ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst received = data[ 'wp-refresh-post-lock' ];\n\t\t\tif ( received.lock_error ) {\n\t\t\t\t// Auto save and display the takeover modal.\n\t\t\t\tautosave();\n\t\t\t\tupdatePostLock( {\n\t\t\t\t\tisLocked: true,\n\t\t\t\t\tisTakeover: true,\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tname: received.lock_error.name,\n\t\t\t\t\t\tavatar: received.lock_error.avatar_src_2x,\n\t\t\t\t\t},\n\t\t\t\t} );\n\t\t\t} else if ( received.new_lock ) {\n\t\t\t\tupdatePostLock( {\n\t\t\t\t\tisLocked: false,\n\t\t\t\t\tactivePostLock: received.new_lock,\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Unlock the post before the window is exited.\n\t\t */\n\t\tfunction releasePostLock() {\n\t\t\tif ( isLocked || ! activePostLock ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst data = new window.FormData();\n\t\t\tdata.append( 'action', 'wp-remove-post-lock' );\n\t\t\tdata.append( '_wpnonce', postLockUtils.unlockNonce );\n\t\t\tdata.append( 'post_ID', postId );\n\t\t\tdata.append( 'active_post_lock', activePostLock );\n\n\t\t\tif ( window.navigator.sendBeacon ) {\n\t\t\t\twindow.navigator.sendBeacon( postLockUtils.ajaxUrl, data );\n\t\t\t} else {\n\t\t\t\tconst xhr = new window.XMLHttpRequest();\n\t\t\t\txhr.open( 'POST', postLockUtils.ajaxUrl, false );\n\t\t\t\txhr.send( data );\n\t\t\t}\n\t\t}\n\n\t\t// Details on these events on the Heartbeat API docs\n\t\t// https://developer.wordpress.org/plugins/javascript/heartbeat-api/\n\t\taddAction( 'heartbeat.send', hookName, sendPostLock );\n\t\taddAction( 'heartbeat.tick', hookName, receivePostLock );\n\t\twindow.addEventListener( 'beforeunload', releasePostLock );\n\n\t\treturn () => {\n\t\t\tremoveAction( 'heartbeat.send', hookName );\n\t\t\tremoveAction( 'heartbeat.tick', hookName );\n\t\t\twindow.removeEventListener( 'beforeunload', releasePostLock );\n\t\t};\n\t}, [] );\n\n\tif ( ! isLocked ) {\n\t\treturn null;\n\t}\n\n\t// Avoid sending the modal if sync is supported, but retain functionality around locks etc.\n\tif ( isCollaborationEnabled ) {\n\t\treturn null;\n\t}\n\n\tconst userDisplayName = user.name;\n\tconst userAvatar = user.avatar;\n\n\tconst unlockUrl = addQueryArgs( 'post.php', {\n\t\t'get-post-lock': '1',\n\t\tlockKey: true,\n\t\tpost: postId,\n\t\taction: 'edit',\n\t\t_wpnonce: postLockUtils.nonce,\n\t} );\n\tconst allPostsUrl = addQueryArgs( 'edit.php', {\n\t\tpost_type: postType?.slug,\n\t} );\n\tconst allPostsLabel = __( 'Exit editor' );\n\treturn (\n\t\t<Modal\n\t\t\ttitle={\n\t\t\t\tisTakeover\n\t\t\t\t\t? __( 'Someone else has taken over this post' )\n\t\t\t\t\t: __( 'This post is already being edited' )\n\t\t\t}\n\t\t\tfocusOnMount\n\t\t\tshouldCloseOnClickOutside={ false }\n\t\t\tshouldCloseOnEsc={ false }\n\t\t\tisDismissible={ false }\n\t\t\t// Do not remove this class, as this class is used by third party plugins.\n\t\t\tclassName=\"editor-post-locked-modal\"\n\t\t\tsize=\"medium\"\n\t\t>\n\t\t\t<HStack alignment=\"top\" spacing={ 6 }>\n\t\t\t\t{ !! userAvatar && (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={ userAvatar }\n\t\t\t\t\t\talt={ __( 'Avatar' ) }\n\t\t\t\t\t\tclassName=\"editor-post-locked-modal__avatar\"\n\t\t\t\t\t\twidth={ 64 }\n\t\t\t\t\t\theight={ 64 }\n\t\t\t\t\t/>\n\t\t\t\t) }\n\t\t\t\t<div>\n\t\t\t\t\t{ !! isTakeover && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t{ createInterpolateElement(\n\t\t\t\t\t\t\t\t\tuserDisplayName\n\t\t\t\t\t\t\t\t\t\t? sprintf(\n\t\t\t\t\t\t\t\t\t\t\t\t/* translators: %s: user's display name */\n\t\t\t\t\t\t\t\t\t\t\t\t__(\n\t\t\t\t\t\t\t\t\t\t\t\t\t'<strong>%s</strong> now has editing control of this post (<PreviewLink />). Don\u2019t worry, your changes up to this moment have been saved.'\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\tuserDisplayName\n\t\t\t\t\t\t\t\t\t\t )\n\t\t\t\t\t\t\t\t\t\t: __(\n\t\t\t\t\t\t\t\t\t\t\t\t'Another user now has editing control of this post (<PreviewLink />). Don\u2019t worry, your changes up to this moment have been saved.'\n\t\t\t\t\t\t\t\t\t\t ),\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstrong: <strong />,\n\t\t\t\t\t\t\t\t\t\tPreviewLink: (\n\t\t\t\t\t\t\t\t\t\t\t<ExternalLink href={ previewLink }>\n\t\t\t\t\t\t\t\t\t\t\t\t{ __( 'preview' ) }\n\t\t\t\t\t\t\t\t\t\t\t</ExternalLink>\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<CollaborationContext />\n\t\t\t\t\t\t</>\n\t\t\t\t\t) }\n\t\t\t\t\t{ ! isTakeover && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t{ createInterpolateElement(\n\t\t\t\t\t\t\t\t\tuserDisplayName\n\t\t\t\t\t\t\t\t\t\t? sprintf(\n\t\t\t\t\t\t\t\t\t\t\t\t/* translators: %s: user's display name */\n\t\t\t\t\t\t\t\t\t\t\t\t__(\n\t\t\t\t\t\t\t\t\t\t\t\t\t'<strong>%s</strong> is currently working on this post (<PreviewLink />), which means you cannot make changes, unless you take over.'\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\tuserDisplayName\n\t\t\t\t\t\t\t\t\t\t )\n\t\t\t\t\t\t\t\t\t\t: __(\n\t\t\t\t\t\t\t\t\t\t\t\t'Another user is currently working on this post (<PreviewLink />), which means you cannot make changes, unless you take over.'\n\t\t\t\t\t\t\t\t\t\t ),\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstrong: <strong />,\n\t\t\t\t\t\t\t\t\t\tPreviewLink: (\n\t\t\t\t\t\t\t\t\t\t\t<ExternalLink href={ previewLink }>\n\t\t\t\t\t\t\t\t\t\t\t\t{ __( 'preview' ) }\n\t\t\t\t\t\t\t\t\t\t\t</ExternalLink>\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<CollaborationContext />\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t{ __(\n\t\t\t\t\t\t\t\t\t'If you take over, the other user will lose editing control to the post, but their changes will be saved.'\n\t\t\t\t\t\t\t\t) }\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) }\n\n\t\t\t\t\t<HStack\n\t\t\t\t\t\tclassName=\"editor-post-locked-modal__buttons\"\n\t\t\t\t\t\tjustify=\"flex-end\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{ ! isTakeover && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t\thref={ unlockUrl }\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{ __( 'Take over' ) }\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t) }\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t\thref={ allPostsUrl }\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{ allPostsLabel }\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</HStack>\n\t\t\t\t</div>\n\t\t\t</HStack>\n\t\t</Modal>\n\t);\n}\n\n/**\n * A modal component that is displayed when a post is locked for editing by another user.\n * The modal provides information about the lock status and options to take over or exit the editor.\n *\n * @return {React.ReactNode} The rendered PostLockedModal component.\n */\nexport default globalThis.IS_GUTENBERG_PLUGIN\n\t? withFilters( 'editor.PostLockedModal' )( PostLockedModal )\n\t: PostLockedModal;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["coreStore", "editorStore", "HStack"]
|
|
7
7
|
}
|
|
@@ -31,6 +31,15 @@ var import_rich_text = require("@wordpress/rich-text");
|
|
|
31
31
|
var import_i18n = require("@wordpress/i18n");
|
|
32
32
|
var import_lock_unlock = require("../../lock-unlock.cjs");
|
|
33
33
|
var { parseRawBlock } = (0, import_lock_unlock.unlock)(import_blocks.privateApis);
|
|
34
|
+
function stringifyValue(value) {
|
|
35
|
+
if (value === null || value === void 0) {
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === "object") {
|
|
39
|
+
return JSON.stringify(value, null, 2);
|
|
40
|
+
}
|
|
41
|
+
return String(value);
|
|
42
|
+
}
|
|
34
43
|
function textSimilarity(text1, text2) {
|
|
35
44
|
if (!text1 && !text2) {
|
|
36
45
|
return 1;
|
|
@@ -47,7 +56,7 @@ function pairSimilarBlocks(blocks) {
|
|
|
47
56
|
const removed = [];
|
|
48
57
|
const added = [];
|
|
49
58
|
blocks.forEach((block, index) => {
|
|
50
|
-
const status = block.__revisionDiffStatus;
|
|
59
|
+
const status = block.__revisionDiffStatus?.status;
|
|
51
60
|
if (status === "removed") {
|
|
52
61
|
removed.push({ block, index });
|
|
53
62
|
} else if (status === "added") {
|
|
@@ -84,7 +93,7 @@ function pairSimilarBlocks(blocks) {
|
|
|
84
93
|
pairedRemoved.add(rem.index);
|
|
85
94
|
modifications.set(bestMatch.index, {
|
|
86
95
|
...bestMatch.block,
|
|
87
|
-
__revisionDiffStatus: "modified",
|
|
96
|
+
__revisionDiffStatus: { status: "modified" },
|
|
88
97
|
__previousRawBlock: rem.block
|
|
89
98
|
});
|
|
90
99
|
}
|
|
@@ -120,14 +129,14 @@ function diffRawBlocks(currentRaw, previousRaw) {
|
|
|
120
129
|
for (let i = 0; i < part.count; i++) {
|
|
121
130
|
result.push({
|
|
122
131
|
...currentRaw[currIdx++],
|
|
123
|
-
__revisionDiffStatus: "added"
|
|
132
|
+
__revisionDiffStatus: { status: "added" }
|
|
124
133
|
});
|
|
125
134
|
}
|
|
126
135
|
} else if (part.removed) {
|
|
127
136
|
for (let i = 0; i < part.count; i++) {
|
|
128
137
|
result.push({
|
|
129
138
|
...previousRaw[prevIdx++],
|
|
130
|
-
__revisionDiffStatus: "removed"
|
|
139
|
+
__revisionDiffStatus: { status: "removed" }
|
|
131
140
|
});
|
|
132
141
|
}
|
|
133
142
|
} else {
|
|
@@ -333,11 +342,12 @@ function applyRichTextDiff(currentRichText, previousRichText) {
|
|
|
333
342
|
}
|
|
334
343
|
return new import_rich_text.RichTextData(result);
|
|
335
344
|
}
|
|
336
|
-
function
|
|
345
|
+
function applyDiffToBlock(currentBlock, previousBlock, diffStatus) {
|
|
337
346
|
const blockType = (0, import_blocks.getBlockType)(currentBlock.name);
|
|
338
347
|
if (!blockType) {
|
|
339
348
|
return;
|
|
340
349
|
}
|
|
350
|
+
const changedAttributes = {};
|
|
341
351
|
for (const [attrName, attrDef] of Object.entries(
|
|
342
352
|
blockType.attributes
|
|
343
353
|
)) {
|
|
@@ -350,18 +360,36 @@ function applyRichTextDiffToBlock(currentBlock, previousBlock) {
|
|
|
350
360
|
previousRichText
|
|
351
361
|
);
|
|
352
362
|
}
|
|
363
|
+
} else {
|
|
364
|
+
const currStr = stringifyValue(
|
|
365
|
+
currentBlock.attributes[attrName]
|
|
366
|
+
);
|
|
367
|
+
const prevStr = stringifyValue(
|
|
368
|
+
previousBlock.attributes[attrName]
|
|
369
|
+
);
|
|
370
|
+
if (currStr !== prevStr) {
|
|
371
|
+
changedAttributes[attrName] = (0, import_word.diffWords)(prevStr, currStr);
|
|
372
|
+
}
|
|
353
373
|
}
|
|
354
374
|
}
|
|
375
|
+
if (Object.keys(changedAttributes).length > 0) {
|
|
376
|
+
diffStatus.changedAttributes = changedAttributes;
|
|
377
|
+
}
|
|
355
378
|
}
|
|
356
379
|
function applyDiffRecursively(parsedBlock, rawBlock) {
|
|
357
380
|
if (rawBlock.__revisionDiffStatus) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
381
|
+
if (rawBlock.__revisionDiffStatus.status === "modified" && rawBlock.__previousRawBlock) {
|
|
382
|
+
const previousParsed = parseRawBlock(rawBlock.__previousRawBlock);
|
|
383
|
+
if (previousParsed) {
|
|
384
|
+
applyDiffToBlock(
|
|
385
|
+
parsedBlock,
|
|
386
|
+
previousParsed,
|
|
387
|
+
rawBlock.__revisionDiffStatus
|
|
388
|
+
);
|
|
389
|
+
}
|
|
364
390
|
}
|
|
391
|
+
parsedBlock.__revisionDiffStatus = rawBlock.__revisionDiffStatus;
|
|
392
|
+
parsedBlock.attributes.__revisionDiffStatus = rawBlock.__revisionDiffStatus;
|
|
365
393
|
}
|
|
366
394
|
if (parsedBlock.innerBlocks && rawBlock.innerBlocks) {
|
|
367
395
|
for (let i = 0; i < parsedBlock.innerBlocks.length; i++) {
|