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