@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.
Files changed (118) hide show
  1. package/build/actions.cjs +23 -29
  2. package/build/actions.cjs.map +2 -2
  3. package/build/awareness/block-lookup.cjs +103 -0
  4. package/build/awareness/block-lookup.cjs.map +7 -0
  5. package/build/awareness/post-editor-awareness.cjs +45 -7
  6. package/build/awareness/post-editor-awareness.cjs.map +3 -3
  7. package/build/entities.cjs +60 -63
  8. package/build/entities.cjs.map +2 -2
  9. package/build/entity-types/icon.cjs +19 -0
  10. package/build/entity-types/icon.cjs.map +7 -0
  11. package/build/entity-types/index.cjs.map +1 -1
  12. package/build/hooks/use-post-editor-awareness-state.cjs +12 -8
  13. package/build/hooks/use-post-editor-awareness-state.cjs.map +2 -2
  14. package/build/private-actions.cjs +0 -8
  15. package/build/private-actions.cjs.map +2 -2
  16. package/build/private-apis.cjs +1 -1
  17. package/build/private-apis.cjs.map +1 -1
  18. package/build/private-selectors.cjs +1 -9
  19. package/build/private-selectors.cjs.map +2 -2
  20. package/build/reducer.cjs +0 -10
  21. package/build/reducer.cjs.map +2 -2
  22. package/build/resolvers.cjs +101 -113
  23. package/build/resolvers.cjs.map +2 -2
  24. package/build/selectors.cjs.map +2 -2
  25. package/build/sync.cjs +0 -3
  26. package/build/sync.cjs.map +2 -2
  27. package/build/types.cjs.map +1 -1
  28. package/build/utils/crdt-selection.cjs +1 -1
  29. package/build/utils/crdt-selection.cjs.map +2 -2
  30. package/build/utils/crdt-user-selections.cjs +78 -22
  31. package/build/utils/crdt-user-selections.cjs.map +3 -3
  32. package/build-module/actions.mjs +23 -29
  33. package/build-module/actions.mjs.map +2 -2
  34. package/build-module/awareness/block-lookup.mjs +77 -0
  35. package/build-module/awareness/block-lookup.mjs.map +7 -0
  36. package/build-module/awareness/post-editor-awareness.mjs +47 -8
  37. package/build-module/awareness/post-editor-awareness.mjs.map +3 -3
  38. package/build-module/entities.mjs +60 -63
  39. package/build-module/entities.mjs.map +2 -2
  40. package/build-module/entity-types/icon.mjs +1 -0
  41. package/build-module/entity-types/icon.mjs.map +7 -0
  42. package/build-module/hooks/use-post-editor-awareness-state.mjs +10 -6
  43. package/build-module/hooks/use-post-editor-awareness-state.mjs.map +2 -2
  44. package/build-module/private-actions.mjs +0 -7
  45. package/build-module/private-actions.mjs.map +2 -2
  46. package/build-module/private-apis.mjs +2 -2
  47. package/build-module/private-apis.mjs.map +1 -1
  48. package/build-module/private-selectors.mjs +2 -12
  49. package/build-module/private-selectors.mjs.map +2 -2
  50. package/build-module/reducer.mjs +0 -9
  51. package/build-module/reducer.mjs.map +2 -2
  52. package/build-module/resolvers.mjs +101 -112
  53. package/build-module/resolvers.mjs.map +2 -2
  54. package/build-module/selectors.mjs.map +2 -2
  55. package/build-module/sync.mjs +0 -2
  56. package/build-module/sync.mjs.map +2 -2
  57. package/build-module/utils/crdt-selection.mjs +1 -1
  58. package/build-module/utils/crdt-selection.mjs.map +2 -2
  59. package/build-module/utils/crdt-user-selections.mjs +77 -22
  60. package/build-module/utils/crdt-user-selections.mjs.map +2 -2
  61. package/build-types/actions.d.ts.map +1 -1
  62. package/build-types/awareness/block-lookup.d.ts +29 -0
  63. package/build-types/awareness/block-lookup.d.ts.map +1 -0
  64. package/build-types/awareness/post-editor-awareness.d.ts +18 -5
  65. package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
  66. package/build-types/awareness/test/block-lookup.d.ts +2 -0
  67. package/build-types/awareness/test/block-lookup.d.ts.map +1 -0
  68. package/build-types/entities.d.ts +16 -0
  69. package/build-types/entities.d.ts.map +1 -1
  70. package/build-types/entity-types/icon.d.ts +25 -0
  71. package/build-types/entity-types/icon.d.ts.map +1 -0
  72. package/build-types/entity-types/index.d.ts +3 -2
  73. package/build-types/entity-types/index.d.ts.map +1 -1
  74. package/build-types/hooks/use-post-editor-awareness-state.d.ts +11 -6
  75. package/build-types/hooks/use-post-editor-awareness-state.d.ts.map +1 -1
  76. package/build-types/index.d.ts.map +1 -1
  77. package/build-types/private-actions.d.ts +0 -8
  78. package/build-types/private-actions.d.ts.map +1 -1
  79. package/build-types/private-selectors.d.ts +1 -8
  80. package/build-types/private-selectors.d.ts.map +1 -1
  81. package/build-types/reducer.d.ts +0 -11
  82. package/build-types/reducer.d.ts.map +1 -1
  83. package/build-types/resolvers.d.ts +0 -3
  84. package/build-types/resolvers.d.ts.map +1 -1
  85. package/build-types/selectors.d.ts +0 -6
  86. package/build-types/selectors.d.ts.map +1 -1
  87. package/build-types/sync.d.ts +2 -2
  88. package/build-types/sync.d.ts.map +1 -1
  89. package/build-types/types.d.ts +13 -5
  90. package/build-types/types.d.ts.map +1 -1
  91. package/build-types/utils/crdt-selection.d.ts.map +1 -1
  92. package/build-types/utils/crdt-user-selections.d.ts +21 -4
  93. package/build-types/utils/crdt-user-selections.d.ts.map +1 -1
  94. package/build-types/utils/test/crdt-user-selections.d.ts +2 -0
  95. package/build-types/utils/test/crdt-user-selections.d.ts.map +1 -0
  96. package/package.json +18 -18
  97. package/src/actions.js +39 -45
  98. package/src/awareness/block-lookup.ts +169 -0
  99. package/src/awareness/post-editor-awareness.ts +68 -11
  100. package/src/awareness/test/block-lookup.ts +504 -0
  101. package/src/awareness/test/post-editor-awareness.ts +662 -38
  102. package/src/entities.js +63 -66
  103. package/src/entity-types/icon.ts +30 -0
  104. package/src/entity-types/index.ts +3 -0
  105. package/src/hooks/test/use-post-editor-awareness-state.ts +21 -14
  106. package/src/hooks/use-post-editor-awareness-state.ts +22 -13
  107. package/src/private-actions.js +0 -14
  108. package/src/private-apis.js +2 -2
  109. package/src/private-selectors.ts +3 -22
  110. package/src/reducer.js +0 -17
  111. package/src/resolvers.js +137 -156
  112. package/src/selectors.ts +0 -7
  113. package/src/sync.ts +0 -2
  114. package/src/test/resolvers.js +109 -1
  115. package/src/types.ts +22 -5
  116. package/src/utils/crdt-selection.ts +3 -1
  117. package/src/utils/crdt-user-selections.ts +129 -47
  118. 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 { SelectionCursor, WPBlockSelection } from '../types';
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
- * Get the absolute position index from a selection cursor.
178
+ * Resolve a selection state to a text index and block client ID.
176
179
  *
177
- * @param selection - The selection cursor.
178
- * @return The absolute position index, or null if not found.
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 getAbsolutePositionIndex(
181
- selection: SelectionCursor
182
- ): number | null {
183
- return (
184
- Y.createAbsolutePositionFromRelativePosition(
185
- selection.cursorPosition.relativePosition,
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
- )?.index ?? null
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
  /**