@wordpress/core-data 7.40.1 → 7.40.2-next.v.202602241322.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/build/actions.cjs +23 -29
- package/build/actions.cjs.map +2 -2
- package/build/awareness/block-lookup.cjs +103 -0
- package/build/awareness/block-lookup.cjs.map +7 -0
- package/build/awareness/post-editor-awareness.cjs +45 -7
- package/build/awareness/post-editor-awareness.cjs.map +3 -3
- package/build/entities.cjs +60 -63
- package/build/entities.cjs.map +2 -2
- package/build/entity-types/icon.cjs +19 -0
- package/build/entity-types/icon.cjs.map +7 -0
- package/build/entity-types/index.cjs.map +1 -1
- package/build/hooks/use-post-editor-awareness-state.cjs +12 -8
- package/build/hooks/use-post-editor-awareness-state.cjs.map +2 -2
- package/build/private-actions.cjs +0 -8
- package/build/private-actions.cjs.map +2 -2
- package/build/private-apis.cjs +1 -1
- package/build/private-apis.cjs.map +1 -1
- package/build/private-selectors.cjs +1 -9
- package/build/private-selectors.cjs.map +2 -2
- package/build/reducer.cjs +0 -10
- package/build/reducer.cjs.map +2 -2
- package/build/resolvers.cjs +101 -113
- package/build/resolvers.cjs.map +2 -2
- package/build/selectors.cjs.map +2 -2
- package/build/sync.cjs +0 -3
- package/build/sync.cjs.map +2 -2
- package/build/types.cjs.map +1 -1
- package/build/utils/crdt-selection.cjs +1 -1
- package/build/utils/crdt-selection.cjs.map +2 -2
- package/build/utils/crdt-user-selections.cjs +78 -22
- package/build/utils/crdt-user-selections.cjs.map +3 -3
- package/build-module/actions.mjs +23 -29
- package/build-module/actions.mjs.map +2 -2
- package/build-module/awareness/block-lookup.mjs +77 -0
- package/build-module/awareness/block-lookup.mjs.map +7 -0
- package/build-module/awareness/post-editor-awareness.mjs +47 -8
- package/build-module/awareness/post-editor-awareness.mjs.map +3 -3
- package/build-module/entities.mjs +60 -63
- package/build-module/entities.mjs.map +2 -2
- package/build-module/entity-types/icon.mjs +1 -0
- package/build-module/entity-types/icon.mjs.map +7 -0
- package/build-module/hooks/use-post-editor-awareness-state.mjs +10 -6
- package/build-module/hooks/use-post-editor-awareness-state.mjs.map +2 -2
- package/build-module/private-actions.mjs +0 -7
- package/build-module/private-actions.mjs.map +2 -2
- package/build-module/private-apis.mjs +2 -2
- package/build-module/private-apis.mjs.map +1 -1
- package/build-module/private-selectors.mjs +2 -12
- package/build-module/private-selectors.mjs.map +2 -2
- package/build-module/reducer.mjs +0 -9
- package/build-module/reducer.mjs.map +2 -2
- package/build-module/resolvers.mjs +101 -112
- package/build-module/resolvers.mjs.map +2 -2
- package/build-module/selectors.mjs.map +2 -2
- package/build-module/sync.mjs +0 -2
- package/build-module/sync.mjs.map +2 -2
- package/build-module/utils/crdt-selection.mjs +1 -1
- package/build-module/utils/crdt-selection.mjs.map +2 -2
- package/build-module/utils/crdt-user-selections.mjs +77 -22
- package/build-module/utils/crdt-user-selections.mjs.map +2 -2
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/awareness/block-lookup.d.ts +29 -0
- package/build-types/awareness/block-lookup.d.ts.map +1 -0
- package/build-types/awareness/post-editor-awareness.d.ts +18 -5
- package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
- package/build-types/awareness/test/block-lookup.d.ts +2 -0
- package/build-types/awareness/test/block-lookup.d.ts.map +1 -0
- package/build-types/entities.d.ts +16 -0
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/entity-types/icon.d.ts +25 -0
- package/build-types/entity-types/icon.d.ts.map +1 -0
- package/build-types/entity-types/index.d.ts +3 -2
- package/build-types/entity-types/index.d.ts.map +1 -1
- package/build-types/hooks/use-post-editor-awareness-state.d.ts +11 -6
- package/build-types/hooks/use-post-editor-awareness-state.d.ts.map +1 -1
- package/build-types/index.d.ts.map +1 -1
- package/build-types/private-actions.d.ts +0 -8
- package/build-types/private-actions.d.ts.map +1 -1
- package/build-types/private-selectors.d.ts +1 -8
- package/build-types/private-selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts +0 -11
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts +0 -3
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +0 -6
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/sync.d.ts +2 -2
- package/build-types/sync.d.ts.map +1 -1
- package/build-types/types.d.ts +13 -5
- package/build-types/types.d.ts.map +1 -1
- package/build-types/utils/crdt-selection.d.ts.map +1 -1
- package/build-types/utils/crdt-user-selections.d.ts +21 -4
- package/build-types/utils/crdt-user-selections.d.ts.map +1 -1
- package/build-types/utils/test/crdt-user-selections.d.ts +2 -0
- package/build-types/utils/test/crdt-user-selections.d.ts.map +1 -0
- package/package.json +18 -18
- package/src/actions.js +39 -45
- package/src/awareness/block-lookup.ts +169 -0
- package/src/awareness/post-editor-awareness.ts +68 -11
- package/src/awareness/test/block-lookup.ts +504 -0
- package/src/awareness/test/post-editor-awareness.ts +662 -38
- package/src/entities.js +63 -66
- package/src/entity-types/icon.ts +30 -0
- package/src/entity-types/index.ts +3 -0
- package/src/hooks/test/use-post-editor-awareness-state.ts +21 -14
- package/src/hooks/use-post-editor-awareness-state.ts +22 -13
- package/src/private-actions.js +0 -14
- package/src/private-apis.js +2 -2
- package/src/private-selectors.ts +3 -22
- package/src/reducer.js +0 -17
- package/src/resolvers.js +137 -156
- package/src/selectors.ts +0 -7
- package/src/sync.ts +0 -2
- package/src/test/resolvers.js +109 -1
- package/src/types.ts +22 -5
- package/src/utils/crdt-selection.ts +3 -1
- package/src/utils/crdt-user-selections.ts +129 -47
- package/src/utils/test/crdt-user-selections.ts +894 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { select } from '@wordpress/data';
|
|
5
|
+
import { Y } from '@wordpress/sync';
|
|
6
|
+
// @ts-ignore No exported types for block editor store selectors.
|
|
7
|
+
import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Internal dependencies
|
|
11
|
+
*/
|
|
12
|
+
import type { AbsoluteBlockIndexPath } from '../types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A block as represented in the block-editor store (from `getBlocks()`).
|
|
16
|
+
*
|
|
17
|
+
* This is a minimal interface covering only the fields used by RTC awareness.
|
|
18
|
+
*/
|
|
19
|
+
interface EditorStoreBlock {
|
|
20
|
+
clientId: string;
|
|
21
|
+
name: string;
|
|
22
|
+
innerBlocks: EditorStoreBlock[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Given a Y.Map within a Ydoc, traverse up the Yjs block tree to compute the
|
|
27
|
+
* index path from the root.
|
|
28
|
+
*
|
|
29
|
+
* For example, the second inner block of the first root block returns [0, 1].
|
|
30
|
+
*
|
|
31
|
+
* @param yType - The Yjs block Y.Map to start from.
|
|
32
|
+
* @return The index path from root, or null if traversal fails.
|
|
33
|
+
*/
|
|
34
|
+
export function getBlockPathInYdoc(
|
|
35
|
+
yType: Y.Map< unknown >
|
|
36
|
+
): AbsoluteBlockIndexPath | null {
|
|
37
|
+
const path: AbsoluteBlockIndexPath = [];
|
|
38
|
+
let current: Y.Map< unknown > = yType;
|
|
39
|
+
|
|
40
|
+
while ( current ) {
|
|
41
|
+
const parentArray = current.parent;
|
|
42
|
+
|
|
43
|
+
if ( ! parentArray || ! ( parentArray instanceof Y.Array ) ) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Find index of current block in its parent array.
|
|
48
|
+
let index = -1;
|
|
49
|
+
for ( let i = 0; i < parentArray.length; i++ ) {
|
|
50
|
+
if ( parentArray.get( i ) === current ) {
|
|
51
|
+
index = i;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if ( index === -1 ) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
path.unshift( index );
|
|
61
|
+
|
|
62
|
+
// Walk up: is the parent array's parent a block Y.Map or the root?
|
|
63
|
+
const grandparent = parentArray.parent;
|
|
64
|
+
if (
|
|
65
|
+
grandparent instanceof Y.Map &&
|
|
66
|
+
grandparent.get( 'clientId' ) !== undefined
|
|
67
|
+
) {
|
|
68
|
+
current = grandparent; // It's a block, keep going.
|
|
69
|
+
} else {
|
|
70
|
+
break; // It's the root map, done.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return path;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Navigate the block-editor store's block tree by an index path
|
|
79
|
+
* and return the local block's clientId.
|
|
80
|
+
*
|
|
81
|
+
* In template mode, getBlocks() returns the full template tree, but Yjs
|
|
82
|
+
* paths are relative to the post content. This method finds the
|
|
83
|
+
* core/post-content block (if present) and uses its inner blocks as the
|
|
84
|
+
* navigation root, so paths align with the Yjs document structure.
|
|
85
|
+
*
|
|
86
|
+
* @param path - The index path, e.g. [0, 1] for blocks[0].innerBlocks[1].
|
|
87
|
+
* @return The local block clientId, or null if the path is invalid.
|
|
88
|
+
*/
|
|
89
|
+
export function resolveBlockClientIdByPath(
|
|
90
|
+
path: AbsoluteBlockIndexPath
|
|
91
|
+
): string | null {
|
|
92
|
+
if ( path.length === 0 ) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const { getBlocks } = select( blockEditorStore );
|
|
97
|
+
const postContentBlocks = getPostContentBlocks( getBlocks(), getBlocks );
|
|
98
|
+
|
|
99
|
+
let blocks = postContentBlocks;
|
|
100
|
+
|
|
101
|
+
for ( let i = 0; i < path.length; i++ ) {
|
|
102
|
+
const block = blocks[ path[ i ] ];
|
|
103
|
+
if ( ! block ) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if ( i === path.length - 1 ) {
|
|
107
|
+
return block.clientId;
|
|
108
|
+
}
|
|
109
|
+
blocks = block.innerBlocks;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Find the post content blocks to use as the navigation root.
|
|
116
|
+
*
|
|
117
|
+
* In template mode, the block tree contains template parts wrapping a
|
|
118
|
+
* core/post-content block. The Yjs document only stores the post content
|
|
119
|
+
* blocks, so we need to find the core/post-content block and use
|
|
120
|
+
* getBlocks(clientId) to retrieve its inner blocks from the store.
|
|
121
|
+
*
|
|
122
|
+
* We must use getBlocks(clientId) rather than reading .innerBlocks from
|
|
123
|
+
* the block object because useBlockSync() injects post content as
|
|
124
|
+
* controlled inner blocks — they exist in the store's block order map
|
|
125
|
+
* but are not populated in the .innerBlocks property of the tree
|
|
126
|
+
* returned by getBlocks().
|
|
127
|
+
*
|
|
128
|
+
* @param rootBlocks - The root-level blocks from getBlocks().
|
|
129
|
+
* @param getBlocks - The getBlocks selector.
|
|
130
|
+
* @return The blocks that correspond to the Yjs document root.
|
|
131
|
+
*/
|
|
132
|
+
function getPostContentBlocks(
|
|
133
|
+
rootBlocks: EditorStoreBlock[],
|
|
134
|
+
getBlocks: ( rootClientId?: string ) => EditorStoreBlock[]
|
|
135
|
+
): EditorStoreBlock[] {
|
|
136
|
+
const postContentBlock = findBlockByName( rootBlocks, 'core/post-content' );
|
|
137
|
+
if ( postContentBlock ) {
|
|
138
|
+
// Use getBlocks(clientId) to read controlled inner blocks from
|
|
139
|
+
// the store, since postContentBlock.innerBlocks is empty.
|
|
140
|
+
return getBlocks( postContentBlock.clientId );
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return rootBlocks;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Recursively search the block tree for a block with a given name.
|
|
148
|
+
*
|
|
149
|
+
* @param blocks - The blocks to search.
|
|
150
|
+
* @param name - The block name to find.
|
|
151
|
+
* @return The first matching block, or null if not found.
|
|
152
|
+
*/
|
|
153
|
+
function findBlockByName(
|
|
154
|
+
blocks: EditorStoreBlock[],
|
|
155
|
+
name: string
|
|
156
|
+
): EditorStoreBlock | null {
|
|
157
|
+
for ( const block of blocks ) {
|
|
158
|
+
if ( block.name === name ) {
|
|
159
|
+
return block;
|
|
160
|
+
}
|
|
161
|
+
if ( block.innerBlocks?.length ) {
|
|
162
|
+
const found = findBlockByName( block.innerBlocks, name );
|
|
163
|
+
if ( found ) {
|
|
164
|
+
return found;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
@@ -10,6 +10,7 @@ import { store as blockEditorStore } from '@wordpress/block-editor';
|
|
|
10
10
|
* Internal dependencies
|
|
11
11
|
*/
|
|
12
12
|
import { BaseAwarenessState, baseEqualityFieldChecks } from './base-awareness';
|
|
13
|
+
import { getBlockPathInYdoc, resolveBlockClientIdByPath } from './block-lookup';
|
|
13
14
|
import {
|
|
14
15
|
AWARENESS_CURSOR_UPDATE_THROTTLE_IN_MS,
|
|
15
16
|
LOCAL_CURSOR_UPDATE_DEBOUNCE_IN_MS,
|
|
@@ -18,9 +19,11 @@ import { STORE_NAME as coreStore } from '../name';
|
|
|
18
19
|
import {
|
|
19
20
|
areSelectionsStatesEqual,
|
|
20
21
|
getSelectionState,
|
|
22
|
+
SelectionType,
|
|
21
23
|
} from '../utils/crdt-user-selections';
|
|
22
24
|
|
|
23
|
-
import type {
|
|
25
|
+
import type { SelectionState, WPBlockSelection } from '../types';
|
|
26
|
+
import type { YBlocks } from '../utils/crdt-blocks';
|
|
24
27
|
import type {
|
|
25
28
|
DebugCollaboratorData,
|
|
26
29
|
EditorState,
|
|
@@ -172,20 +175,74 @@ export class PostEditorAwareness extends BaseAwarenessState< PostEditorState > {
|
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
/**
|
|
175
|
-
*
|
|
178
|
+
* Resolve a selection state to a text index and block client ID.
|
|
176
179
|
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
180
|
+
* For text-based selections, navigates up from the resolved Y.Text via
|
|
181
|
+
* AbstractType.parent to find the containing block, then resolves the
|
|
182
|
+
* local clientId via the block's tree path.
|
|
183
|
+
* For WholeBlock selections, resolves the block's relative position and
|
|
184
|
+
* then finds the local clientId via tree path.
|
|
185
|
+
*
|
|
186
|
+
* Tree-path resolution is used instead of reading the clientId directly
|
|
187
|
+
* from the Yjs block because the local block-editor store may use different
|
|
188
|
+
* clientIds (e.g. in "Show Template" mode where blocks are cloned).
|
|
189
|
+
*
|
|
190
|
+
* @param selection - The selection state.
|
|
191
|
+
* @return The text index and block client ID, or nulls if not resolvable.
|
|
179
192
|
*/
|
|
180
|
-
public
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
public convertSelectionStateToAbsolute( selection: SelectionState ): {
|
|
194
|
+
textIndex: number | null;
|
|
195
|
+
localClientId: string | null;
|
|
196
|
+
} {
|
|
197
|
+
if ( selection.type === SelectionType.None ) {
|
|
198
|
+
return { textIndex: null, localClientId: null };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if ( selection.type === SelectionType.WholeBlock ) {
|
|
202
|
+
const absolutePos = Y.createAbsolutePositionFromRelativePosition(
|
|
203
|
+
selection.blockPosition,
|
|
186
204
|
this.doc
|
|
187
|
-
)
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
let localClientId: string | null = null;
|
|
208
|
+
|
|
209
|
+
if ( absolutePos && absolutePos.type instanceof Y.Array ) {
|
|
210
|
+
const parentArray = absolutePos.type as YBlocks;
|
|
211
|
+
const block = parentArray.get( absolutePos.index );
|
|
212
|
+
|
|
213
|
+
if ( block instanceof Y.Map ) {
|
|
214
|
+
const path = getBlockPathInYdoc( block );
|
|
215
|
+
localClientId = path
|
|
216
|
+
? resolveBlockClientIdByPath( path )
|
|
217
|
+
: null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return { textIndex: null, localClientId };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Text-based selections: resolve cursor position and navigate up.
|
|
225
|
+
const cursorPos =
|
|
226
|
+
'cursorPosition' in selection
|
|
227
|
+
? selection.cursorPosition
|
|
228
|
+
: selection.cursorStartPosition;
|
|
229
|
+
|
|
230
|
+
const absolutePosition = Y.createAbsolutePositionFromRelativePosition(
|
|
231
|
+
cursorPos.relativePosition,
|
|
232
|
+
this.doc
|
|
188
233
|
);
|
|
234
|
+
|
|
235
|
+
if ( ! absolutePosition ) {
|
|
236
|
+
return { textIndex: null, localClientId: null };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Navigate up: Y.Text -> attributes Y.Map -> block Y.Map
|
|
240
|
+
const yType = absolutePosition.type.parent?.parent;
|
|
241
|
+
const path =
|
|
242
|
+
yType instanceof Y.Map ? getBlockPathInYdoc( yType ) : null;
|
|
243
|
+
const localClientId = path ? resolveBlockClientIdByPath( path ) : null;
|
|
244
|
+
|
|
245
|
+
return { textIndex: absolutePosition.index, localClientId };
|
|
189
246
|
}
|
|
190
247
|
|
|
191
248
|
/**
|