@atlaskit/editor-common 112.9.0 → 112.10.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.
@@ -1,5 +1,6 @@
1
1
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
2
2
  import { findChildrenByType, findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
3
4
  import { getSupportedListTypesSet, isBulletOrOrderedList, isTaskList, convertBlockToInlineContent } from './list-utils';
4
5
  const getContentSupportChecker = targetNodeType => {
5
6
  return node => {
@@ -10,6 +11,15 @@ const getContentSupportChecker = targetNodeType => {
10
11
  }
11
12
  };
12
13
  };
14
+ const createBlockTaskItemWithMarks = (content, marks, schema) => {
15
+ const {
16
+ blockTaskItem,
17
+ paragraph
18
+ } = schema.nodes;
19
+ const allowedMarks = marks.filter(mark => blockTaskItem.allowsMarkType(mark.type));
20
+ const newParagraph = paragraph.create(null, content.length > 0 ? content : null, allowedMarks);
21
+ return blockTaskItem.create(null, newParagraph);
22
+ };
13
23
  export const transformListRecursively = (props, onhandleUnsupportedContent) => {
14
24
  const transformedItems = [];
15
25
  const {
@@ -26,14 +36,33 @@ export const transformListRecursively = (props, onhandleUnsupportedContent) => {
26
36
  taskList,
27
37
  listItem,
28
38
  taskItem,
29
- paragraph
39
+ paragraph,
40
+ blockTaskItem
30
41
  } = schema.nodes;
42
+
43
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
44
+ // but keep this solution general
45
+ const isBlockTaskEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
46
+
47
+ /**
48
+ * Extracts paragraph children from a blockTaskItem, preserving their marks.
49
+ */
50
+ const extractParagraphsFromBlockTaskItem = node => {
51
+ const paragraphs = [];
52
+ node.forEach(child => {
53
+ if (child.type === paragraph) {
54
+ paragraphs.push(child);
55
+ }
56
+ });
57
+ return paragraphs;
58
+ };
31
59
  listNode.forEach(child => {
32
60
  if (isSourceBulletOrOrdered && isTargetTask) {
33
61
  // Convert bullet/ordered => task
34
62
  if (child.type === listItem) {
35
63
  const inlineContent = [];
36
64
  const nestedTaskLists = [];
65
+ let blockMarks = [];
37
66
  child.forEach(grandChild => {
38
67
  if (supportedListTypes.has(grandChild.type) && grandChild.type !== taskList) {
39
68
  nestedTaskLists.push(transformListRecursively({
@@ -43,18 +72,37 @@ export const transformListRecursively = (props, onhandleUnsupportedContent) => {
43
72
  } else if (!getContentSupportChecker(taskItem)(grandChild) && !grandChild.isTextblock) {
44
73
  onhandleUnsupportedContent === null || onhandleUnsupportedContent === void 0 ? void 0 : onhandleUnsupportedContent(grandChild);
45
74
  } else {
75
+ if (isBlockTaskEnabled && grandChild.type === paragraph && grandChild.marks.length > 0) {
76
+ blockMarks = grandChild.marks;
77
+ }
46
78
  inlineContent.push(...convertBlockToInlineContent(grandChild, schema));
47
79
  }
48
80
  });
49
- transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
81
+ if (isBlockTaskEnabled && blockMarks.length > 0) {
82
+ transformedItems.push(createBlockTaskItemWithMarks(inlineContent, blockMarks, schema));
83
+ } else {
84
+ transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
85
+ }
50
86
  transformedItems.push(...nestedTaskLists);
51
87
  }
52
88
  } else if (isSourceTask && isTargetBulletOrOrdered) {
53
89
  // Convert task => bullet/ordered
54
90
  if (child.type === taskItem) {
55
91
  const inlineContent = [...child.content.content];
56
- const paragraphNode = paragraph.create(null, inlineContent.length > 0 ? inlineContent : null);
92
+
93
+ // Transfer taskItem's block marks to the paragraph.
94
+ // Use listItem.allowsMarkType since the paragraph will be inside a listItem
95
+ // (which uses ParagraphWithFontSizeStage0 that allows fontSize).
96
+ const paragraphMarks = isBlockTaskEnabled && child.marks.length > 0 ? child.marks.filter(mark => listItem.allowsMarkType(mark.type)) : undefined;
97
+ const paragraphNode = paragraph.create(null, inlineContent.length > 0 ? inlineContent : null, paragraphMarks);
57
98
  transformedItems.push(listItem.create(null, [paragraphNode]));
99
+ } else if (isBlockTaskEnabled && child.type === blockTaskItem) {
100
+ // blockTaskItem wraps content in paragraphs — extract them directly,
101
+ // preserving their fontSize marks
102
+ const paragraphs = extractParagraphsFromBlockTaskItem(child);
103
+ if (paragraphs.length > 0) {
104
+ transformedItems.push(listItem.create(null, paragraphs));
105
+ }
58
106
  } else if (child.type === taskList) {
59
107
  const transformedNestedList = transformListRecursively({
60
108
  ...props,
@@ -199,22 +247,28 @@ export const transformBetweenListTypes = context => {
199
247
  export const transformToTaskList = (tr, range, targetNodeType, targetAttrs, nodes) => {
200
248
  try {
201
249
  const {
202
- taskItem
250
+ taskItem,
251
+ paragraph,
252
+ blockTaskItem
203
253
  } = nodes;
254
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
255
+ // but keep this solution general
256
+ const isBlockTaskItemEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
204
257
  const listItems = [];
205
258
 
206
259
  // Process each block in the range
207
260
  tr.doc.nodesBetween(range.start, range.end, node => {
208
261
  if (node.isBlock) {
209
- // For block nodes like paragraphs, directly use their inline content
210
262
  const inlineContent = [...node.content.content];
211
263
  if (inlineContent.length > 0) {
212
- // Create task item with inline content directly
213
- const listItem = taskItem.create(targetAttrs, inlineContent);
214
- listItems.push(listItem);
264
+ if (isBlockTaskItemEnabled && node.type === paragraph && node.marks.length > 0) {
265
+ listItems.push(createBlockTaskItemWithMarks(inlineContent, node.marks, tr.doc.type.schema));
266
+ } else {
267
+ listItems.push(taskItem.create(targetAttrs, inlineContent));
268
+ }
215
269
  }
216
270
  }
217
- return false; // Don't traverse into children
271
+ return false;
218
272
  });
219
273
  if (listItems.length === 0) {
220
274
  return null;
@@ -242,6 +296,39 @@ export const transformTaskListToBlockNodes = context => {
242
296
  selection
243
297
  } = tr;
244
298
  const schema = selection.$from.doc.type.schema;
299
+ const {
300
+ blockTaskItem
301
+ } = schema.nodes;
302
+
303
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
304
+ // but keep this solution general
305
+ const isBlockTaskItemEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
306
+ if (isBlockTaskItemEnabled) {
307
+ const blockTaskItemsResult = findChildrenByType(sourceNode, blockTaskItem);
308
+ if (blockTaskItemsResult.length > 0 && targetNodeType === schema.nodes.paragraph) {
309
+ // blockTaskItem content is (paragraph | extension)+
310
+ // Extract paragraph children directly — they may carry block marks (e.g. fontSize)
311
+ const targetNodes = [];
312
+ for (const {
313
+ node: blockItem
314
+ } of blockTaskItemsResult) {
315
+ blockItem.forEach(child => {
316
+ if (child.type === schema.nodes.paragraph) {
317
+ targetNodes.push(child);
318
+ }
319
+ });
320
+ }
321
+ if (targetNodes.length === 0) {
322
+ return null;
323
+ }
324
+ const slice = new Slice(Fragment.fromArray(targetNodes), 0, 0);
325
+ const rangeStart = sourcePos !== null ? sourcePos : selection.from;
326
+ tr.replaceRange(rangeStart, rangeStart + sourceNode.nodeSize, slice);
327
+ return tr;
328
+ }
329
+ }
330
+
331
+ // Original logic for regular taskItem children
245
332
  const taskItemsResult = findChildrenByType(sourceNode, schema.nodes.taskItem);
246
333
  const taskItems = taskItemsResult.map(item => item.node);
247
334
  const taskItemFragments = taskItems.map(taskItem => taskItem.content);
@@ -283,6 +370,10 @@ export const getFormattedNode = tr => {
283
370
  nodes
284
371
  } = tr.doc.type.schema;
285
372
 
373
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
374
+ // but keep this solution general
375
+ const isBlockTaskItemEnabled = !!nodes.blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
376
+
286
377
  // Find the node to format from the current selection
287
378
  let nodeToFormat;
288
379
  let nodePos = selection.from;
@@ -294,13 +385,17 @@ export const getFormattedNode = tr => {
294
385
  nodePos = selectedNode.pos;
295
386
  } else {
296
387
  // Try to find parent node (including list parents)
297
- const parentNode = findParentNodeOfType([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
388
+ const parentNodeTypes = [nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection];
389
+ if (isBlockTaskItemEnabled) {
390
+ parentNodeTypes.push(nodes.blockTaskItem);
391
+ }
392
+ const parentNode = findParentNodeOfType(parentNodeTypes)(selection);
298
393
  if (parentNode) {
299
394
  nodeToFormat = parentNode.node;
300
395
  nodePos = parentNode.pos;
301
396
  const paragraphOrHeadingNode = findParentNodeOfType([nodes.paragraph, nodes.heading])(selection);
302
- // Special case: if we found a listItem, check if we need the parent list instead
303
- if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
397
+ // Special case: if we found a listItem/taskItem/blockTaskItem, check if we need the parent list instead
398
+ if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem || isBlockTaskItemEnabled && parentNode.node.type === nodes.blockTaskItem) {
304
399
  const listParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
305
400
  if (listParent) {
306
401
  // For list transformations, we want the list parent, not the listItem
@@ -14,7 +14,7 @@ import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
14
14
  import { fg } from '@atlaskit/platform-feature-flags';
15
15
  import Layer from '../Layer';
16
16
  const packageName = "@atlaskit/editor-common";
17
- const packageVersion = "112.8.4";
17
+ const packageVersion = "112.9.1";
18
18
  const halfFocusRing = 1;
19
19
  const dropOffset = '0, 8';
20
20
  const fadeIn = keyframes({
@@ -8,6 +8,7 @@ export { getCommonListAnalyticsAttributes, countListItemsInSelection } from './a
8
8
  export { hasValidListIndentationLevel } from './indentation';
9
9
  export { restoreSelection, computeSelectionOffsets } from './restore-selection';
10
10
  export { buildReplacementFragment } from './build-replacement-fragment';
11
+ export { narrowReplacementRange } from './narrow-replacement-range';
11
12
  export { flattenList } from './flatten-list';
12
13
  export { isListNode, isListItemNode, isBulletList, isParagraphNode } from '../utils';
13
14
  export { messages } from './messages';
@@ -0,0 +1,76 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ /**
3
+ * Narrows a full-list replacement to the minimal changed range.
4
+ *
5
+ * Compares the old root list node with the new replacement fragment
6
+ * from both ends to find the first and last positions where they differ,
7
+ * then returns only the changed subrange.
8
+ *
9
+ * This reduces the scope of `tr.replaceWith()` so that remote cursors
10
+ * on unchanged items are preserved during collaborative editing.
11
+ */
12
+ export function narrowReplacementRange(doc, rootListStart, rootListEnd, fragment, contentStartOffsets) {
13
+ var oldNode = doc.nodeAt(rootListStart);
14
+ var newNode = fragment.childCount === 1 ? fragment.firstChild : null;
15
+ if (!oldNode || !newNode || newNode.type !== oldNode.type) {
16
+ return {
17
+ start: rootListStart,
18
+ end: rootListEnd,
19
+ fragment: fragment,
20
+ adjustedContentStartOffsets: contentStartOffsets
21
+ };
22
+ }
23
+ var minChildCount = Math.min(oldNode.childCount, newNode.childCount);
24
+ var commonPrefixChildren = 0;
25
+ var prefixSize = 0;
26
+ for (var i = 0; i < minChildCount; i++) {
27
+ var oldChild = oldNode.child(i);
28
+ var newChild = newNode.child(i);
29
+ if (oldChild.eq(newChild)) {
30
+ commonPrefixChildren++;
31
+ prefixSize += oldChild.nodeSize;
32
+ } else {
33
+ break;
34
+ }
35
+ }
36
+ var commonSuffixChildren = 0;
37
+ var suffixSize = 0;
38
+ for (var _i = 0; _i < minChildCount - commonPrefixChildren; _i++) {
39
+ var _oldChild = oldNode.child(oldNode.childCount - 1 - _i);
40
+ var _newChild = newNode.child(newNode.childCount - 1 - _i);
41
+ if (_oldChild.eq(_newChild)) {
42
+ commonSuffixChildren++;
43
+ suffixSize += _oldChild.nodeSize;
44
+ } else {
45
+ break;
46
+ }
47
+ }
48
+ var totalCommon = commonPrefixChildren + commonSuffixChildren;
49
+ if (totalCommon >= oldNode.childCount && totalCommon >= newNode.childCount) {
50
+ return {
51
+ start: rootListStart,
52
+ end: rootListStart,
53
+ fragment: Fragment.empty,
54
+ adjustedContentStartOffsets: contentStartOffsets
55
+ };
56
+ }
57
+ var narrowedStart = rootListStart + 1 + prefixSize;
58
+ var narrowedEnd = rootListEnd - 1 - suffixSize;
59
+ var changedChildStart = commonPrefixChildren;
60
+ var changedChildEnd = newNode.childCount - commonSuffixChildren;
61
+ var changedNodes = [];
62
+ for (var _i2 = changedChildStart; _i2 < changedChildEnd; _i2++) {
63
+ changedNodes.push(newNode.child(_i2));
64
+ }
65
+ var narrowedFragment = Fragment.from(changedNodes);
66
+ var prefixOffset = 1 + prefixSize;
67
+ var adjustedContentStartOffsets = contentStartOffsets.map(function (offset) {
68
+ return offset - prefixOffset;
69
+ });
70
+ return {
71
+ start: narrowedStart,
72
+ end: narrowedEnd,
73
+ fragment: narrowedFragment,
74
+ adjustedContentStartOffsets: adjustedContentStartOffsets
75
+ };
76
+ }
@@ -10,6 +10,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
10
10
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports -- Ignored via go/DSP-18766; jsx required at runtime for @jsxRuntime classic
11
11
  import { css, jsx } from '@emotion/react';
12
12
  import { hexToEditorBorderPaletteColor } from '@atlaskit/editor-palette';
13
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
13
14
  import { borderStyle, INLINE_IMAGE_ASPECT_RATIO_CSS_VAR_KEY, INLINE_IMAGE_BORDER_COLOR_CSS_VAR_KEY, INLINE_IMAGE_BORDER_SIZE_CSS_VAR_KEY, INLINE_IMAGE_WRAPPER_CLASS_NAME, selectedStyle, wrapperStyle } from './styles';
14
15
 
15
16
  // The MediaImage component needs to obtain its parent's dimensions.
@@ -32,8 +33,17 @@ export var InlineImageWrapper = function InlineImageWrapper(_ref) {
32
33
  onClick = _ref.onClick;
33
34
  var borderStyleVars = borderSize && borderColor ? _defineProperty(_defineProperty({}, INLINE_IMAGE_BORDER_SIZE_CSS_VAR_KEY, borderSize), INLINE_IMAGE_BORDER_COLOR_CSS_VAR_KEY, hexToEditorBorderPaletteColor(borderColor) || borderColor) : {};
34
35
  var aspectStyleVars = aspectRatio ? _defineProperty({}, INLINE_IMAGE_ASPECT_RATIO_CSS_VAR_KEY, aspectRatio) : {};
36
+ var onKeyDown = function onKeyDown(e) {
37
+ if (expValEquals('editor_a11y__enghealth-46814_fy26', 'isEnabled', true)) {
38
+ if (e.key === 'Enter' || e.key === ' ') {
39
+ e.preventDefault();
40
+ onClick(e);
41
+ }
42
+ }
43
+ return undefined;
44
+ };
35
45
  return (
36
- // eslint-disable-next-line @atlaskit/design-system/prefer-primitives, @atlassian/a11y/click-events-have-key-events, @atlassian/a11y/interactive-element-not-keyboard-focusable, @atlassian/a11y/no-static-element-interactions
46
+ // eslint-disable-next-line @atlaskit/design-system/prefer-primitives
37
47
  jsx("span", _extends({
38
48
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
39
49
  style: _objectSpread(_objectSpread({}, borderStyleVars), aspectStyleVars)
@@ -48,7 +58,10 @@ export var InlineImageWrapper = function InlineImageWrapper(_ref) {
48
58
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766
49
59
  isSelected && selectedStyle],
50
60
  "data-testid": "inline-image-wrapper",
51
- onClick: onClick
61
+ role: expValEquals('editor_a11y__enghealth-46814_fy26', 'isEnabled', true) ? 'button' : undefined,
62
+ tabIndex: expValEquals('editor_a11y__enghealth-46814_fy26', 'isEnabled', true) ? 0 : undefined,
63
+ onClick: onClick,
64
+ onKeyDown: onKeyDown
52
65
  // Ignored via go/ees005
53
66
  // eslint-disable-next-line react/jsx-props-no-spreading
54
67
  }, htmlAttrs), jsx("span", {
@@ -10,7 +10,7 @@ import { isFedRamp } from './environment';
10
10
  import { normaliseSentryBreadcrumbs, SERIALIZABLE_ATTRIBUTES } from './normalise-sentry-breadcrumbs';
11
11
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
12
12
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
13
- var packageVersion = "112.8.4";
13
+ var packageVersion = "112.9.1";
14
14
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
15
15
  // Remove URL as it has UGC
16
16
  // Ignored via go/ees007
@@ -4,6 +4,7 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
4
4
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
5
5
  import _createClass from "@babel/runtime/helpers/createClass";
6
6
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
7
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
8
  import { EventDispatcher } from '../event-dispatcher';
8
9
 
9
10
  /*********************
@@ -666,12 +667,28 @@ export var EditorPresetBuilder = /*#__PURE__*/function () {
666
667
  if (typeof fn !== 'function') {
667
668
  return null;
668
669
  }
669
- var plugin = pluginInjectionAPI ? fn({
670
- config: config,
671
- api: pluginInjectionAPI.api()
672
- }) : fn({
673
- config: config
674
- });
670
+ var plugin;
671
+ if (expValEquals('platform_editor_improve_preset_builder_logging', 'isEnabled', true)) {
672
+ try {
673
+ plugin = pluginInjectionAPI ? fn({
674
+ config: config,
675
+ api: pluginInjectionAPI.api()
676
+ }) : fn({
677
+ config: config
678
+ });
679
+ } catch (error) {
680
+ var pluginName = fn.name || 'unknown';
681
+ var enhancedError = error instanceof Error ? new Error("Failed to initialize plugin '".concat(pluginName, "': ").concat(error.message)) : new Error("Failed to initialize plugin '".concat(pluginName, "'"));
682
+ throw enhancedError;
683
+ }
684
+ } else {
685
+ plugin = pluginInjectionAPI ? fn({
686
+ config: config,
687
+ api: pluginInjectionAPI.api()
688
+ }) : fn({
689
+ config: config
690
+ });
691
+ }
675
692
  if (plugin && excludePlugins !== null && excludePlugins !== void 0 && excludePlugins.has(plugin.name)) {
676
693
  return null;
677
694
  }
@@ -1,9 +1,13 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ 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; } } }; }
4
+ 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; } }
5
+ 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; }
3
6
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
7
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
8
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
6
9
  import { findChildrenByType, findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
10
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
11
  import { getSupportedListTypesSet, isBulletOrOrderedList, isTaskList, convertBlockToInlineContent } from './list-utils';
8
12
  var getContentSupportChecker = function getContentSupportChecker(targetNodeType) {
9
13
  return function (node) {
@@ -14,6 +18,16 @@ var getContentSupportChecker = function getContentSupportChecker(targetNodeType)
14
18
  }
15
19
  };
16
20
  };
21
+ var createBlockTaskItemWithMarks = function createBlockTaskItemWithMarks(content, marks, schema) {
22
+ var _schema$nodes = schema.nodes,
23
+ blockTaskItem = _schema$nodes.blockTaskItem,
24
+ paragraph = _schema$nodes.paragraph;
25
+ var allowedMarks = marks.filter(function (mark) {
26
+ return blockTaskItem.allowsMarkType(mark.type);
27
+ });
28
+ var newParagraph = paragraph.create(null, content.length > 0 ? content : null, allowedMarks);
29
+ return blockTaskItem.create(null, newParagraph);
30
+ };
17
31
  var _transformListRecursively = function transformListRecursively(props, onhandleUnsupportedContent) {
18
32
  var transformedItems = [];
19
33
  var listNode = props.listNode,
@@ -24,17 +38,36 @@ var _transformListRecursively = function transformListRecursively(props, onhandl
24
38
  supportedListTypes = props.supportedListTypes,
25
39
  schema = props.schema,
26
40
  targetNodeType = props.targetNodeType;
27
- var _schema$nodes = schema.nodes,
28
- taskList = _schema$nodes.taskList,
29
- listItem = _schema$nodes.listItem,
30
- taskItem = _schema$nodes.taskItem,
31
- paragraph = _schema$nodes.paragraph;
41
+ var _schema$nodes2 = schema.nodes,
42
+ taskList = _schema$nodes2.taskList,
43
+ listItem = _schema$nodes2.listItem,
44
+ taskItem = _schema$nodes2.taskItem,
45
+ paragraph = _schema$nodes2.paragraph,
46
+ blockTaskItem = _schema$nodes2.blockTaskItem;
47
+
48
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
49
+ // but keep this solution general
50
+ var isBlockTaskEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
51
+
52
+ /**
53
+ * Extracts paragraph children from a blockTaskItem, preserving their marks.
54
+ */
55
+ var extractParagraphsFromBlockTaskItem = function extractParagraphsFromBlockTaskItem(node) {
56
+ var paragraphs = [];
57
+ node.forEach(function (child) {
58
+ if (child.type === paragraph) {
59
+ paragraphs.push(child);
60
+ }
61
+ });
62
+ return paragraphs;
63
+ };
32
64
  listNode.forEach(function (child) {
33
65
  if (isSourceBulletOrOrdered && isTargetTask) {
34
66
  // Convert bullet/ordered => task
35
67
  if (child.type === listItem) {
36
68
  var inlineContent = [];
37
69
  var nestedTaskLists = [];
70
+ var blockMarks = [];
38
71
  child.forEach(function (grandChild) {
39
72
  if (supportedListTypes.has(grandChild.type) && grandChild.type !== taskList) {
40
73
  nestedTaskLists.push(_transformListRecursively(_objectSpread(_objectSpread({}, props), {}, {
@@ -43,18 +76,39 @@ var _transformListRecursively = function transformListRecursively(props, onhandl
43
76
  } else if (!getContentSupportChecker(taskItem)(grandChild) && !grandChild.isTextblock) {
44
77
  onhandleUnsupportedContent === null || onhandleUnsupportedContent === void 0 || onhandleUnsupportedContent(grandChild);
45
78
  } else {
79
+ if (isBlockTaskEnabled && grandChild.type === paragraph && grandChild.marks.length > 0) {
80
+ blockMarks = grandChild.marks;
81
+ }
46
82
  inlineContent.push.apply(inlineContent, _toConsumableArray(convertBlockToInlineContent(grandChild, schema)));
47
83
  }
48
84
  });
49
- transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
85
+ if (isBlockTaskEnabled && blockMarks.length > 0) {
86
+ transformedItems.push(createBlockTaskItemWithMarks(inlineContent, blockMarks, schema));
87
+ } else {
88
+ transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
89
+ }
50
90
  transformedItems.push.apply(transformedItems, nestedTaskLists);
51
91
  }
52
92
  } else if (isSourceTask && isTargetBulletOrOrdered) {
53
93
  // Convert task => bullet/ordered
54
94
  if (child.type === taskItem) {
55
95
  var _inlineContent = _toConsumableArray(child.content.content);
56
- var paragraphNode = paragraph.create(null, _inlineContent.length > 0 ? _inlineContent : null);
96
+
97
+ // Transfer taskItem's block marks to the paragraph.
98
+ // Use listItem.allowsMarkType since the paragraph will be inside a listItem
99
+ // (which uses ParagraphWithFontSizeStage0 that allows fontSize).
100
+ var paragraphMarks = isBlockTaskEnabled && child.marks.length > 0 ? child.marks.filter(function (mark) {
101
+ return listItem.allowsMarkType(mark.type);
102
+ }) : undefined;
103
+ var paragraphNode = paragraph.create(null, _inlineContent.length > 0 ? _inlineContent : null, paragraphMarks);
57
104
  transformedItems.push(listItem.create(null, [paragraphNode]));
105
+ } else if (isBlockTaskEnabled && child.type === blockTaskItem) {
106
+ // blockTaskItem wraps content in paragraphs — extract them directly,
107
+ // preserving their fontSize marks
108
+ var paragraphs = extractParagraphsFromBlockTaskItem(child);
109
+ if (paragraphs.length > 0) {
110
+ transformedItems.push(listItem.create(null, paragraphs));
111
+ }
58
112
  } else if (child.type === taskList) {
59
113
  var transformedNestedList = _transformListRecursively(_objectSpread(_objectSpread({}, props), {}, {
60
114
  listNode: child
@@ -189,21 +243,27 @@ export var transformBetweenListTypes = function transformBetweenListTypes(contex
189
243
  */
190
244
  export var transformToTaskList = function transformToTaskList(tr, range, targetNodeType, targetAttrs, nodes) {
191
245
  try {
192
- var taskItem = nodes.taskItem;
246
+ var taskItem = nodes.taskItem,
247
+ paragraph = nodes.paragraph,
248
+ blockTaskItem = nodes.blockTaskItem;
249
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
250
+ // but keep this solution general
251
+ var isBlockTaskItemEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
193
252
  var listItems = [];
194
253
 
195
254
  // Process each block in the range
196
255
  tr.doc.nodesBetween(range.start, range.end, function (node) {
197
256
  if (node.isBlock) {
198
- // For block nodes like paragraphs, directly use their inline content
199
257
  var inlineContent = _toConsumableArray(node.content.content);
200
258
  if (inlineContent.length > 0) {
201
- // Create task item with inline content directly
202
- var listItem = taskItem.create(targetAttrs, inlineContent);
203
- listItems.push(listItem);
259
+ if (isBlockTaskItemEnabled && node.type === paragraph && node.marks.length > 0) {
260
+ listItems.push(createBlockTaskItemWithMarks(inlineContent, node.marks, tr.doc.type.schema));
261
+ } else {
262
+ listItems.push(taskItem.create(targetAttrs, inlineContent));
263
+ }
204
264
  }
205
265
  }
206
- return false; // Don't traverse into children
266
+ return false;
207
267
  });
208
268
  if (listItems.length === 0) {
209
269
  return null;
@@ -227,6 +287,44 @@ export var transformTaskListToBlockNodes = function transformTaskListToBlockNode
227
287
  sourcePos = context.sourcePos;
228
288
  var selection = tr.selection;
229
289
  var schema = selection.$from.doc.type.schema;
290
+ var blockTaskItem = schema.nodes.blockTaskItem;
291
+
292
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
293
+ // but keep this solution general
294
+ var isBlockTaskItemEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
295
+ if (isBlockTaskItemEnabled) {
296
+ var blockTaskItemsResult = findChildrenByType(sourceNode, blockTaskItem);
297
+ if (blockTaskItemsResult.length > 0 && targetNodeType === schema.nodes.paragraph) {
298
+ // blockTaskItem content is (paragraph | extension)+
299
+ // Extract paragraph children directly — they may carry block marks (e.g. fontSize)
300
+ var _targetNodes = [];
301
+ var _iterator = _createForOfIteratorHelper(blockTaskItemsResult),
302
+ _step;
303
+ try {
304
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
305
+ var blockItem = _step.value.node;
306
+ blockItem.forEach(function (child) {
307
+ if (child.type === schema.nodes.paragraph) {
308
+ _targetNodes.push(child);
309
+ }
310
+ });
311
+ }
312
+ } catch (err) {
313
+ _iterator.e(err);
314
+ } finally {
315
+ _iterator.f();
316
+ }
317
+ if (_targetNodes.length === 0) {
318
+ return null;
319
+ }
320
+ var _slice = new Slice(Fragment.fromArray(_targetNodes), 0, 0);
321
+ var _rangeStart = sourcePos !== null ? sourcePos : selection.from;
322
+ tr.replaceRange(_rangeStart, _rangeStart + sourceNode.nodeSize, _slice);
323
+ return tr;
324
+ }
325
+ }
326
+
327
+ // Original logic for regular taskItem children
230
328
  var taskItemsResult = findChildrenByType(sourceNode, schema.nodes.taskItem);
231
329
  var taskItems = taskItemsResult.map(function (item) {
232
330
  return item.node;
@@ -274,6 +372,10 @@ export var getFormattedNode = function getFormattedNode(tr) {
274
372
  var selection = tr.selection;
275
373
  var nodes = tr.doc.type.schema.nodes;
276
374
 
375
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
376
+ // but keep this solution general
377
+ var isBlockTaskItemEnabled = !!nodes.blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
378
+
277
379
  // Find the node to format from the current selection
278
380
  var nodeToFormat;
279
381
  var nodePos = selection.from;
@@ -285,13 +387,17 @@ export var getFormattedNode = function getFormattedNode(tr) {
285
387
  nodePos = selectedNode.pos;
286
388
  } else {
287
389
  // Try to find parent node (including list parents)
288
- var parentNode = findParentNodeOfType([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
390
+ var parentNodeTypes = [nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection];
391
+ if (isBlockTaskItemEnabled) {
392
+ parentNodeTypes.push(nodes.blockTaskItem);
393
+ }
394
+ var parentNode = findParentNodeOfType(parentNodeTypes)(selection);
289
395
  if (parentNode) {
290
396
  nodeToFormat = parentNode.node;
291
397
  nodePos = parentNode.pos;
292
398
  var paragraphOrHeadingNode = findParentNodeOfType([nodes.paragraph, nodes.heading])(selection);
293
- // Special case: if we found a listItem, check if we need the parent list instead
294
- if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
399
+ // Special case: if we found a listItem/taskItem/blockTaskItem, check if we need the parent list instead
400
+ if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem || isBlockTaskItemEnabled && parentNode.node.type === nodes.blockTaskItem) {
295
401
  var listParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
296
402
  if (listParent) {
297
403
  // For list transformations, we want the list parent, not the listItem
@@ -21,7 +21,7 @@ import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
21
21
  import { fg } from '@atlaskit/platform-feature-flags';
22
22
  import Layer from '../Layer';
23
23
  var packageName = "@atlaskit/editor-common";
24
- var packageVersion = "112.8.4";
24
+ var packageVersion = "112.9.1";
25
25
  var halfFocusRing = 1;
26
26
  var dropOffset = '0, 8';
27
27
  var fadeIn = keyframes({
@@ -6,6 +6,8 @@ export { hasValidListIndentationLevel } from './indentation';
6
6
  export { restoreSelection, computeSelectionOffsets } from './restore-selection';
7
7
  export { buildReplacementFragment } from './build-replacement-fragment';
8
8
  export type { BuildResult } from './build-replacement-fragment';
9
+ export { narrowReplacementRange } from './narrow-replacement-range';
10
+ export type { NarrowedReplacement } from './narrow-replacement-range';
9
11
  export type { FlattenedItem } from './flatten-list';
10
12
  export { flattenList } from './flatten-list';
11
13
  export type { FlattenListOptions, FlattenListResult } from './flatten-list';
@@ -0,0 +1,19 @@
1
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
3
+ export type NarrowedReplacement = {
4
+ adjustedContentStartOffsets: number[];
5
+ end: number;
6
+ fragment: Fragment;
7
+ start: number;
8
+ };
9
+ /**
10
+ * Narrows a full-list replacement to the minimal changed range.
11
+ *
12
+ * Compares the old root list node with the new replacement fragment
13
+ * from both ends to find the first and last positions where they differ,
14
+ * then returns only the changed subrange.
15
+ *
16
+ * This reduces the scope of `tr.replaceWith()` so that remote cursors
17
+ * on unchanged items are preserved during collaborative editing.
18
+ */
19
+ export declare function narrowReplacementRange(doc: PMNode, rootListStart: number, rootListEnd: number, fragment: Fragment, contentStartOffsets: number[]): NarrowedReplacement;