@atlaskit/editor-common 111.11.7 → 111.11.9
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 +13 -0
- package/dist/cjs/analytics/types/enums.js +8 -0
- package/dist/cjs/block-menu/block-link.js +27 -13
- package/dist/cjs/block-menu/index.js +44 -1
- package/dist/cjs/block-menu/scroll-to-block-utils.js +330 -0
- package/dist/es2019/analytics/types/enums.js +8 -0
- package/dist/es2019/block-menu/block-link.js +27 -13
- package/dist/es2019/block-menu/index.js +2 -1
- package/dist/es2019/block-menu/scroll-to-block-utils.js +306 -0
- package/dist/esm/analytics/types/enums.js +8 -0
- package/dist/esm/block-menu/block-link.js +27 -13
- package/dist/esm/block-menu/index.js +2 -1
- package/dist/esm/block-menu/scroll-to-block-utils.js +322 -0
- package/dist/types/analytics/types/enums.d.ts +8 -0
- package/dist/types/analytics/types/sync-block-events.d.ts +12 -2
- package/dist/types/block-menu/block-link.d.ts +22 -10
- package/dist/types/block-menu/index.d.ts +2 -0
- package/dist/types/block-menu/scroll-to-block-utils.d.ts +94 -0
- package/dist/types-ts4.5/analytics/types/enums.d.ts +8 -0
- package/dist/types-ts4.5/analytics/types/sync-block-events.d.ts +12 -2
- package/dist/types-ts4.5/block-menu/block-link.d.ts +22 -10
- package/dist/types-ts4.5/block-menu/index.d.ts +2 -0
- package/dist/types-ts4.5/block-menu/scroll-to-block-utils.d.ts +94 -0
- package/package.json +3 -3
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for scrolling to block elements with expand node support.
|
|
3
|
+
* Used by both Confluence's useScrollOnUrlChange and platform renderer's useScrollToBlock.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Timing constants for expand animation and DOM update delays.
|
|
8
|
+
* These values are tuned for the expand component's behavior and React's update cycle.
|
|
9
|
+
*/
|
|
10
|
+
export const SCROLL_TO_BLOCK_TIMING = {
|
|
11
|
+
/** Minimal delay for DOM/React updates after expanding (no animation in expand component). */
|
|
12
|
+
DOM_UPDATE_DELAY: 50,
|
|
13
|
+
/** Delay when expand operation fails and needs retry. */
|
|
14
|
+
RETRY_DELAY: 100,
|
|
15
|
+
/** Maximum number of retry attempts before giving up and scrolling anyway. */
|
|
16
|
+
MAX_ATTEMPTS: 5,
|
|
17
|
+
/** Maximum depth of nested expands to search (prevents infinite loops). */
|
|
18
|
+
MAX_EXPAND_DEPTH: 2
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Find a node by its localId in an ADF document and determine if it has expand or nestedExpand parents.
|
|
22
|
+
* This is used to identify nodes that may be hidden inside collapsed expands.
|
|
23
|
+
*
|
|
24
|
+
* @param adfDoc - The ADF document to search
|
|
25
|
+
* @param targetLocalId - The localId to search for
|
|
26
|
+
* @returns NodeWithExpandParents if found, undefined otherwise
|
|
27
|
+
*/
|
|
28
|
+
export function findNodeWithExpandParents(adfDoc, targetLocalId) {
|
|
29
|
+
let result;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Recursive helper to traverse the ADF document and track expand ancestors.
|
|
33
|
+
* We use recursion to properly maintain the expand ancestor stack.
|
|
34
|
+
*/
|
|
35
|
+
const traverse = (node, expandAncestors) => {
|
|
36
|
+
var _node$attrs, _node$attrs2;
|
|
37
|
+
// If we already found the target, stop traversing
|
|
38
|
+
if (result) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check if this is the target node by localId
|
|
43
|
+
if ((node === null || node === void 0 ? void 0 : (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.localId) === targetLocalId) {
|
|
44
|
+
result = {
|
|
45
|
+
expandParentLocalIds: [...expandAncestors] // Copy the array
|
|
46
|
+
};
|
|
47
|
+
return false; // Stop traversing
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check if this node is an expand or nestedExpand
|
|
51
|
+
const isExpand = (node === null || node === void 0 ? void 0 : node.type) === 'expand' || (node === null || node === void 0 ? void 0 : node.type) === 'nestedExpand';
|
|
52
|
+
const newExpandAncestors = isExpand && node !== null && node !== void 0 && (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 && _node$attrs2.localId ? [...expandAncestors, node.attrs.localId] : expandAncestors;
|
|
53
|
+
|
|
54
|
+
// Traverse children if they exist
|
|
55
|
+
if (node !== null && node !== void 0 && node.content && Array.isArray(node.content)) {
|
|
56
|
+
for (const child of node.content) {
|
|
57
|
+
if (result || !child) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
traverse(child, newExpandAncestors);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return !result; // Continue if we haven't found it yet
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Start traversal from the root
|
|
67
|
+
traverse(adfDoc, []);
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Find all parent expand elements that contain the target element.
|
|
73
|
+
* Searches up the DOM tree to find expand or nestedExpand containers.
|
|
74
|
+
*
|
|
75
|
+
* This function limits the search depth to prevent infinite loops and performance issues.
|
|
76
|
+
* The default maxDepth of 2 is chosen because:
|
|
77
|
+
* - Most use cases have 0-2 levels of nesting
|
|
78
|
+
* - Searching deeper can impact performance
|
|
79
|
+
* - Users rarely nest expands more than 2 levels deep
|
|
80
|
+
*
|
|
81
|
+
* @param element - The target element to find parents for.
|
|
82
|
+
* @param maxDepth - Maximum depth to search (default: 2). This is the maximum number of
|
|
83
|
+
* expand ancestors to find, starting from the target element.
|
|
84
|
+
* @returns Array of expand containers ordered from outermost to innermost.
|
|
85
|
+
* For example, if element is inside expand B which is inside expand A,
|
|
86
|
+
* this returns [expandA, expandB].
|
|
87
|
+
*/
|
|
88
|
+
export const findParentExpands = (element, maxDepth = SCROLL_TO_BLOCK_TIMING.MAX_EXPAND_DEPTH) => {
|
|
89
|
+
const expands = [];
|
|
90
|
+
let currentElement = element;
|
|
91
|
+
let depth = 0;
|
|
92
|
+
while (currentElement && depth < maxDepth) {
|
|
93
|
+
// Look for expand container - handles both "expand" and "nestedExpand" types
|
|
94
|
+
const expandContainer = currentElement.closest('[data-node-type="expand"], [data-node-type="nestedExpand"]');
|
|
95
|
+
if (!expandContainer || expands.includes(expandContainer)) {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
expands.push(expandContainer);
|
|
99
|
+
currentElement = expandContainer.parentElement;
|
|
100
|
+
depth++;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Return in reverse order so we expand from outermost to innermost
|
|
104
|
+
return expands.reverse();
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check if an expand node is currently collapsed.
|
|
109
|
+
*
|
|
110
|
+
* Uses two methods to determine collapse state:
|
|
111
|
+
* 1. First checks aria-expanded attribute on the toggle button (most reliable).
|
|
112
|
+
* 2. Falls back to checking content div visibility via computed styles.
|
|
113
|
+
*
|
|
114
|
+
* @param expandContainer - The expand container element.
|
|
115
|
+
* @returns True if the expand is collapsed, false if expanded or state cannot be determined.
|
|
116
|
+
*/
|
|
117
|
+
export const isExpandCollapsed = expandContainer => {
|
|
118
|
+
// Check for aria-expanded attribute on the toggle button
|
|
119
|
+
const toggleButton = expandContainer.querySelector('[aria-expanded]');
|
|
120
|
+
if (toggleButton && toggleButton instanceof HTMLElement) {
|
|
121
|
+
return toggleButton.getAttribute('aria-expanded') === 'false';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Fallback: check if content div is hidden using the actual class name
|
|
125
|
+
const contentDiv = expandContainer.querySelector('.ak-editor-expand__content');
|
|
126
|
+
if (contentDiv && contentDiv instanceof HTMLElement) {
|
|
127
|
+
const computedStyle = window.getComputedStyle(contentDiv);
|
|
128
|
+
return computedStyle.display === 'none' || computedStyle.visibility === 'hidden' || contentDiv.hidden;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Expand a collapsed expand node by clicking its toggle button.
|
|
135
|
+
*
|
|
136
|
+
* This function finds the toggle button with aria-expanded="false" and programmatically
|
|
137
|
+
* clicks it to expand the node. It does not wait for the expansion to complete.
|
|
138
|
+
*
|
|
139
|
+
* @param expandContainer - The expand container element.
|
|
140
|
+
* @returns True if the toggle button was found and clicked, false otherwise.
|
|
141
|
+
*/
|
|
142
|
+
export const expandElement = expandContainer => {
|
|
143
|
+
// Find and click the toggle button
|
|
144
|
+
const toggleButton = expandContainer.querySelector('[aria-expanded="false"]');
|
|
145
|
+
if (toggleButton && toggleButton instanceof HTMLElement) {
|
|
146
|
+
toggleButton.click();
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Expand all parent expands then scroll to the element.
|
|
154
|
+
*
|
|
155
|
+
* This is the main entry point for scrolling to elements that may be hidden inside collapsed expands.
|
|
156
|
+
* It handles nested expands by expanding them one at a time from outermost to innermost,
|
|
157
|
+
* waiting for DOM updates between each expansion.
|
|
158
|
+
*
|
|
159
|
+
* The function uses a recursive approach with retry logic to handle:
|
|
160
|
+
* - Nested expands that need sequential expansion
|
|
161
|
+
* - DOM updates that may take time to reflect
|
|
162
|
+
* - Failed expand operations (e.g., if toggle button is temporarily unavailable)
|
|
163
|
+
* - Disconnected elements (removed from DOM)
|
|
164
|
+
*
|
|
165
|
+
* After all parent expands are open (or max attempts reached), scrolls the element into view
|
|
166
|
+
* with smooth behavior and centered in the viewport.
|
|
167
|
+
*
|
|
168
|
+
* @param element - The target element to scroll to.
|
|
169
|
+
* @param attempt - Current attempt number (used internally for retry logic, starts at 0).
|
|
170
|
+
* @returns A cleanup function that cancels any pending timeouts. Call this when the operation
|
|
171
|
+
* should be aborted (e.g., component unmount, navigation, or new scroll request).
|
|
172
|
+
*/
|
|
173
|
+
export const expandAllParentsThenScroll = (element, attempt = 0) => {
|
|
174
|
+
const {
|
|
175
|
+
MAX_EXPAND_DEPTH,
|
|
176
|
+
MAX_ATTEMPTS,
|
|
177
|
+
DOM_UPDATE_DELAY,
|
|
178
|
+
RETRY_DELAY
|
|
179
|
+
} = SCROLL_TO_BLOCK_TIMING;
|
|
180
|
+
|
|
181
|
+
// Store timeout ID and nested cleanup function so they can be cancelled.
|
|
182
|
+
let timeoutId = null;
|
|
183
|
+
let nestedCleanup = null;
|
|
184
|
+
|
|
185
|
+
// Cleanup function that cancels pending timeout and any nested operations.
|
|
186
|
+
const cleanup = () => {
|
|
187
|
+
if (timeoutId !== null) {
|
|
188
|
+
clearTimeout(timeoutId);
|
|
189
|
+
timeoutId = null;
|
|
190
|
+
}
|
|
191
|
+
if (nestedCleanup) {
|
|
192
|
+
nestedCleanup();
|
|
193
|
+
nestedCleanup = null;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Guard against element being disconnected from DOM or exceeding max attempts.
|
|
198
|
+
if (attempt >= MAX_ATTEMPTS || !element.isConnected) {
|
|
199
|
+
// Max attempts reached or element disconnected, scroll anyway.
|
|
200
|
+
if (element.isConnected) {
|
|
201
|
+
element.scrollIntoView({
|
|
202
|
+
behavior: 'smooth',
|
|
203
|
+
block: 'center'
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return cleanup;
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
// Step 1: Find all parent expands (outermost to innermost).
|
|
210
|
+
const parentExpands = findParentExpands(element, MAX_EXPAND_DEPTH);
|
|
211
|
+
|
|
212
|
+
// Step 2: Find any collapsed expands (filter out disconnected elements).
|
|
213
|
+
const collapsedExpands = parentExpands.filter(expandContainer => expandContainer.isConnected && isExpandCollapsed(expandContainer));
|
|
214
|
+
if (collapsedExpands.length === 0) {
|
|
215
|
+
// All expands are open (or there are no expands), scroll to element.
|
|
216
|
+
element.scrollIntoView({
|
|
217
|
+
behavior: 'smooth',
|
|
218
|
+
block: 'center'
|
|
219
|
+
});
|
|
220
|
+
return cleanup;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Step 3: Expand ONLY the outermost collapsed expand first.
|
|
224
|
+
// This is critical for nested expands - we must expand parent before child.
|
|
225
|
+
const outermostCollapsed = collapsedExpands[0];
|
|
226
|
+
try {
|
|
227
|
+
const expanded = expandElement(outermostCollapsed);
|
|
228
|
+
if (expanded) {
|
|
229
|
+
// Successfully expanded, wait briefly for DOM update then recurse to handle any nested expands.
|
|
230
|
+
// Note: There's no animation, but we need a minimal delay for DOM/React updates.
|
|
231
|
+
timeoutId = setTimeout(() => {
|
|
232
|
+
try {
|
|
233
|
+
// Verify element is still connected before proceeding.
|
|
234
|
+
if (!element.isConnected) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Recurse to handle any nested collapsed expands or retry if still collapsed.
|
|
239
|
+
nestedCleanup = expandAllParentsThenScroll(element, attempt + 1);
|
|
240
|
+
} catch {
|
|
241
|
+
// Fallback to simple scroll on error.
|
|
242
|
+
if (element.isConnected) {
|
|
243
|
+
element.scrollIntoView({
|
|
244
|
+
behavior: 'smooth',
|
|
245
|
+
block: 'center'
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}, DOM_UPDATE_DELAY);
|
|
250
|
+
} else {
|
|
251
|
+
// Failed to expand, retry with longer delay.
|
|
252
|
+
timeoutId = setTimeout(() => {
|
|
253
|
+
if (element.isConnected) {
|
|
254
|
+
nestedCleanup = expandAllParentsThenScroll(element, attempt + 1);
|
|
255
|
+
}
|
|
256
|
+
}, RETRY_DELAY);
|
|
257
|
+
}
|
|
258
|
+
} catch {
|
|
259
|
+
// Retry on error.
|
|
260
|
+
timeoutId = setTimeout(() => {
|
|
261
|
+
if (element.isConnected) {
|
|
262
|
+
nestedCleanup = expandAllParentsThenScroll(element, attempt + 1);
|
|
263
|
+
}
|
|
264
|
+
}, RETRY_DELAY);
|
|
265
|
+
}
|
|
266
|
+
} catch {
|
|
267
|
+
// Fallback to simple scroll on error.
|
|
268
|
+
if (element.isConnected) {
|
|
269
|
+
element.scrollIntoView({
|
|
270
|
+
behavior: 'smooth',
|
|
271
|
+
block: 'center'
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return cleanup;
|
|
276
|
+
};
|
|
277
|
+
export const getLocalIdSelector = (localId, container) => {
|
|
278
|
+
// Check if the element with data-local-id exists
|
|
279
|
+
let element = container.querySelector(`[data-local-id="${localId}"]`);
|
|
280
|
+
if (element) {
|
|
281
|
+
return element;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Special case for decision lists and task lists which already have localId
|
|
285
|
+
element = container.querySelector(`[data-decision-list-local-id="${localId}"]`);
|
|
286
|
+
if (element) {
|
|
287
|
+
return element;
|
|
288
|
+
}
|
|
289
|
+
element = container.querySelector(`[data-task-list-local-id="${localId}"]`);
|
|
290
|
+
if (element) {
|
|
291
|
+
return element;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Special case for tables which use data-table-local-id
|
|
295
|
+
element = container.querySelector(`[data-table-local-id="${localId}"]`);
|
|
296
|
+
if (element) {
|
|
297
|
+
return element;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Special case for extension, smart cards and media which use lowercase localid
|
|
301
|
+
element = container.querySelector(`[localid="${localId}"]`);
|
|
302
|
+
if (element) {
|
|
303
|
+
return element;
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
};
|
|
@@ -105,6 +105,8 @@ export var ACTION = /*#__PURE__*/function (ACTION) {
|
|
|
105
105
|
ACTION["REACT_NODEVIEW_RENDERED"] = "reactNodeViewRendered";
|
|
106
106
|
ACTION["REFERENCE_SYNCED_BLOCK_DELETE"] = "referenceSyncedBlockDelete";
|
|
107
107
|
ACTION["REFERENCE_SYNCED_BLOCK_UPDATE"] = "referenceSyncedBlockUpdate";
|
|
108
|
+
ACTION["REFERENCE_SYNCED_BLOCK_UNSYNC"] = "referenceSyncedBlockUnsync";
|
|
109
|
+
ACTION["REFERENCE_SYNCED_BLOCK_COPY"] = "referenceSyncedBlockCopy";
|
|
108
110
|
ACTION["REPLACED_ALL"] = "replacedAll";
|
|
109
111
|
ACTION["REPLACED_ONE"] = "replacedOne";
|
|
110
112
|
ACTION["RESOLVED"] = "resolved";
|
|
@@ -122,6 +124,9 @@ export var ACTION = /*#__PURE__*/function (ACTION) {
|
|
|
122
124
|
ACTION["SYNCED_BLOCK_FETCH_REFERENCES"] = "syncedBlockFetchReferences";
|
|
123
125
|
ACTION["SYNCED_BLOCK_DELETE"] = "syncedBlockDelete";
|
|
124
126
|
ACTION["SYNCED_BLOCK_GET_SOURCE_INFO"] = "syncedBlockGetSourceInfo";
|
|
127
|
+
ACTION["SYNCED_BLOCK_UNSYNC"] = "syncedBlockUnsync";
|
|
128
|
+
ACTION["SYNCED_BLOCK_EDIT_SOURCE"] = "syncedBlockEditSource";
|
|
129
|
+
ACTION["SYNCED_BLOCK_VIEW_SYNCED_LOCATIONS"] = "syncedBlockViewSyncedLocations";
|
|
125
130
|
ACTION["SYNCHRONY_DISCONNECTED"] = "synchronyDisconnected";
|
|
126
131
|
ACTION["SYNCHRONY_ENTITY_ERROR"] = "synchronyEntityError";
|
|
127
132
|
ACTION["SYNCHRONY_ERROR"] = "synchronyError";
|
|
@@ -249,6 +254,7 @@ export var INPUT_METHOD = /*#__PURE__*/function (INPUT_METHOD) {
|
|
|
249
254
|
INPUT_METHOD["DOUBLE_CLICK"] = "doubleClick";
|
|
250
255
|
INPUT_METHOD["META_CLICK"] = "metaClick";
|
|
251
256
|
INPUT_METHOD["INLINE_SUGGESTION_FLOATING_TB"] = "inlineSuggestionFloatingToolbar";
|
|
257
|
+
INPUT_METHOD["SYNCED_BLOCK_TB"] = "syncedBlockToolbar";
|
|
252
258
|
INPUT_METHOD["BLOCK_MENU"] = "blockMenu";
|
|
253
259
|
INPUT_METHOD["SMART_LINK"] = "smartLink";
|
|
254
260
|
return INPUT_METHOD;
|
|
@@ -535,6 +541,7 @@ export var ACTION_SUBJECT_ID = /*#__PURE__*/function (ACTION_SUBJECT_ID) {
|
|
|
535
541
|
ACTION_SUBJECT_ID["EDITOR_PLUGIN_SELECTION_EXTENSION_COMPONENT"] = "editorPluginSelectionExtensionComponent";
|
|
536
542
|
ACTION_SUBJECT_ID["TRANSFORM"] = "transform";
|
|
537
543
|
ACTION_SUBJECT_ID["SYNCED_BLOCK_TOOLBAR"] = "syncedBlockToolbar";
|
|
544
|
+
ACTION_SUBJECT_ID["SYNCED_BLOCK_COPY"] = "syncedBlockCopy";
|
|
538
545
|
ACTION_SUBJECT_ID["SYNCED_BLOCK_SOURCE_URL"] = "syncedBlockSourceUrl";
|
|
539
546
|
ACTION_SUBJECT_ID["SYNCED_BLOCK_UPDATE_CACHE"] = "syncedBlockUpdateCache";
|
|
540
547
|
ACTION_SUBJECT_ID["SYNCED_BLOCK_UPDATE"] = "syncedBlockUpdate";
|
|
@@ -546,6 +553,7 @@ export var ACTION_SUBJECT_ID = /*#__PURE__*/function (ACTION_SUBJECT_ID) {
|
|
|
546
553
|
ACTION_SUBJECT_ID["SYNCED_BLOCK_GET_SOURCE_INFO"] = "syncedBlockGetSourceInfo";
|
|
547
554
|
ACTION_SUBJECT_ID["SYNCED_BLOCK_FETCH"] = "syncedBlockFetch";
|
|
548
555
|
ACTION_SUBJECT_ID["SYNCED_BLOCK_FETCH_REFERENCES"] = "syncedBlockFetchReferences";
|
|
556
|
+
ACTION_SUBJECT_ID["SYNCED_BLOCK_CLICK_SYNCED_LOCATION"] = "syncedBlockClickSyncedLocation";
|
|
549
557
|
ACTION_SUBJECT_ID["TABLE_STICKY_HEADER"] = "tableStickyHeader";
|
|
550
558
|
return ACTION_SUBJECT_ID;
|
|
551
559
|
}({});
|
|
@@ -1,34 +1,48 @@
|
|
|
1
1
|
export var DEFAULT_BLOCK_LINK_HASH_PREFIX = 'block-';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Matches hashes that start with the block link prefix followed by
|
|
4
|
+
* Matches hashes that start with the block link prefix followed by either:
|
|
5
|
+
* - A UUID (e.g., '123e4567-e89b-12d3-a456-426614174000')
|
|
6
|
+
* - A short hex ID (e.g., 'ab2366c43b52')
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* Short hex IDs are 12-character hexadecimal strings without dashes, used in some contexts
|
|
9
|
+
* as a compact alternative to full UUIDs. Both formats are valid block identifiers.
|
|
10
|
+
*
|
|
11
|
+
* Note: The short ID pattern matches exactly 12 hex characters. While this could theoretically
|
|
12
|
+
* match heading IDs or other anchors, block links are typically generated programmatically
|
|
13
|
+
* with known ID formats, minimizing collision risk.
|
|
14
|
+
*
|
|
15
|
+
* @param hash - The hash string to check (e.g., '#block-123e4567-e89b-12d3-a456-426614174000', '#block-ab2366c43b52', or without '#').
|
|
16
|
+
* @param prefix - The prefix to look for (default is 'block-').
|
|
17
|
+
* @returns True if the hash matches the block link pattern, false otherwise.
|
|
9
18
|
*/
|
|
10
19
|
export var isBlockLinkHash = function isBlockLinkHash(hash) {
|
|
11
20
|
var prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_BLOCK_LINK_HASH_PREFIX;
|
|
12
21
|
if (!hash || !prefix) {
|
|
13
22
|
return false;
|
|
14
23
|
}
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
// Match either UUID format (8-4-4-4-12) or short hex ID format (exactly 12 hex chars without dashes).
|
|
25
|
+
var uuidRegex = new RegExp("^#?".concat(prefix, "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"), 'iu');
|
|
26
|
+
var shortIdRegex = new RegExp("^#?".concat(prefix, "[0-9a-f]{12}$"), 'iu');
|
|
27
|
+
return uuidRegex.test(hash) || shortIdRegex.test(hash);
|
|
17
28
|
};
|
|
18
29
|
|
|
19
30
|
/**
|
|
20
31
|
* Extracts the block ID from a block link hash.
|
|
21
32
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
33
|
+
* Supports both UUID format (e.g., '123e4567-e89b-12d3-a456-426614174000') and
|
|
34
|
+
* short hex ID format (e.g., 'ab2366c43b52').
|
|
35
|
+
*
|
|
36
|
+
* @param hash - The hash string to extract the block ID from (e.g., '#block-123e4567-e89b-12d3-a456-426614174000', '#block-ab2366c43b52', or without '#').
|
|
37
|
+
* @param prefix - The prefix to look for (default is 'block-').
|
|
38
|
+
* @returns The extracted block ID if the hash is valid, null otherwise.
|
|
25
39
|
*/
|
|
26
40
|
export var extractBlockIdFromLinkHash = function extractBlockIdFromLinkHash(hash) {
|
|
27
41
|
var prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_BLOCK_LINK_HASH_PREFIX;
|
|
28
42
|
if (!isBlockLinkHash(hash, prefix)) {
|
|
29
43
|
return null;
|
|
30
44
|
}
|
|
31
|
-
// Remove leading # if present, then remove the prefix
|
|
45
|
+
// Remove leading # if present, then remove the prefix.
|
|
32
46
|
var normalized = hash.startsWith('#') ? hash.slice(1) : hash;
|
|
33
47
|
return normalized.slice(prefix.length);
|
|
34
48
|
};
|
|
@@ -36,9 +50,9 @@ export var extractBlockIdFromLinkHash = function extractBlockIdFromLinkHash(hash
|
|
|
36
50
|
/**
|
|
37
51
|
* Creates a block link hash from a given block ID.
|
|
38
52
|
*
|
|
39
|
-
* @param blockId The block ID to create the hash from (e.g., '123e4567-e89b-12d3-a456-426614174000')
|
|
40
|
-
* @param prefix The prefix to use (default is 'block-')
|
|
41
|
-
* @returns The constructed block link hash value (e.g., 'block-123e4567-e89b-12d3-a456-426614174000')
|
|
53
|
+
* @param blockId - The block ID to create the hash from (e.g., '123e4567-e89b-12d3-a456-426614174000').
|
|
54
|
+
* @param prefix - The prefix to use (default is 'block-').
|
|
55
|
+
* @returns The constructed block link hash value (e.g., 'block-123e4567-e89b-12d3-a456-426614174000').
|
|
42
56
|
*/
|
|
43
57
|
export var createBlockLinkHashValue = function createBlockLinkHashValue(blockId) {
|
|
44
58
|
var prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_BLOCK_LINK_HASH_PREFIX;
|
|
@@ -4,4 +4,5 @@ export { BLOCK_ACTIONS_MENU_SECTION, BLOCK_ACTIONS_FEATURED_EXTENSION_SLOT_MENU_
|
|
|
4
4
|
export { FORMAT_HEADING_1_MENU_ITEM, FORMAT_HEADING_2_MENU_ITEM, FORMAT_HEADING_3_MENU_ITEM, FORMAT_HEADING_4_MENU_ITEM, FORMAT_HEADING_5_MENU_ITEM, FORMAT_HEADING_6_MENU_ITEM, FORMAT_PARAGRAPH_MENU_ITEM, FORMAT_QUOTE_MENU_ITEM, FORMAT_EXPAND_MENU_ITEM, FORMAT_LAYOUT_MENU_ITEM, FORMAT_PANEL_MENU_ITEM, FORMAT_CODE_BLOCK_MENU_ITEM, FORMAT_BULLETED_LIST_MENU_ITEM, FORMAT_NUMBERED_LIST_MENU_ITEM, FORMAT_TASK_LIST_MENU_ITEM, COPY_MENU_SECTION, MOVE_UP_DOWN_MENU_SECTION, COPY_BLOCK_MENU_ITEM, COPY_LINK_MENU_ITEM, MOVE_UP_MENU_ITEM, MOVE_DOWN_MENU_ITEM, NESTED_FORMAT_MENU_SECTION, FORMAT_MENU_ITEM, NESTED_FORMAT_MENU, PRIMARY_MENU_SECTION, ADD_BLOCKS_MENU_SECTION, CREATE_SYNCED_BLOCK_MENU_ITEM } from './key-deprecated';
|
|
5
5
|
export { MAIN_BLOCK_MENU_SECTION_RANK, TRANSFORM_MENU_SECTION_RANK, TRANSFORM_MENU_ITEM_RANK, TRANSFORM_HEADINGS_MENU_SECTION_RANK, TRANSFORM_STRUCTURE_MENU_SECTION_RANK, TRANSFORM_CLEAR_MENU_SECTION_RANK, BLOCK_ACTIONS_MENU_SECTION_RANK, POSITION_MENU_SECTION_RANK, DELETE_MENU_SECTION_RANK, TRANSFORM_SUGGESTED_MENU_SECTION_RANK, TRANSFORM_CREATE_MENU_SECTION_RANK } from './rank';
|
|
6
6
|
export { FORMAT_NESTED_MENU_RANK, FORMAT_NESTED_MENU_RANK_REVISED, BLOCK_MENU_SECTION_RANK, PRIMARY_MENU_SECTION_RANK, COPY_MENU_SECTION_RANK, DELETE_SECTION_RANK, MOVE_BLOCK_SECTION_RANK, ADD_BLOCKS_MENU_SECTION_RANK } from './rank-deprecated';
|
|
7
|
-
export { createBlockLinkHashValue, DEFAULT_BLOCK_LINK_HASH_PREFIX, extractBlockIdFromLinkHash, isBlockLinkHash } from './block-link';
|
|
7
|
+
export { createBlockLinkHashValue, DEFAULT_BLOCK_LINK_HASH_PREFIX, extractBlockIdFromLinkHash, isBlockLinkHash } from './block-link';
|
|
8
|
+
export { expandAllParentsThenScroll, findParentExpands, isExpandCollapsed, expandElement, findNodeWithExpandParents, SCROLL_TO_BLOCK_TIMING, getLocalIdSelector } from './scroll-to-block-utils';
|