@atlaskit/editor-plugin-paste 9.0.15 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @atlaskit/editor-plugin-paste
2
2
 
3
+ ## 9.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`b749ce678d575`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b749ce678d575) -
8
+ Preserve small text formatting across list and task list edits
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies
13
+
14
+ ## 9.0.16
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies
19
+
3
20
  ## 9.0.15
4
21
 
5
22
  ### Patch Changes
@@ -32,7 +32,9 @@ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/
32
32
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
33
33
  var _v = _interopRequireDefault(require("uuid/v4"));
34
34
  var _analytics = require("@atlaskit/editor-common/analytics");
35
+ var _card = require("@atlaskit/editor-common/card");
35
36
  var _coreUtils = require("@atlaskit/editor-common/core-utils");
37
+ var _lists = require("@atlaskit/editor-common/lists");
36
38
  var _mark = require("@atlaskit/editor-common/mark");
37
39
  var _nesting = require("@atlaskit/editor-common/nesting");
38
40
  var _selection = require("@atlaskit/editor-common/selection");
@@ -48,7 +50,7 @@ var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equa
48
50
  var _commands = require("../../editor-commands/commands");
49
51
  var _pluginFactory = require("../plugin-factory");
50
52
  var _edgeCases = require("./edge-cases");
51
- var _lists = require("./edge-cases/lists");
53
+ var _lists2 = require("./edge-cases/lists");
52
54
  var _index = require("./index");
53
55
  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; } } }; }
54
56
  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; } }
@@ -172,7 +174,7 @@ function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
172
174
  var isFirstChildTaskNode = transformedSlice.content.firstChild.type === taskList || transformedSlice.content.firstChild.type === taskItem;
173
175
  var tr = (0, _prosemirrorHistory.closeHistory)(state.tr);
174
176
  if (panelNode && sliceHasTask && ((_slice$content$firstC3 = slice.content.firstChild) === null || _slice$content$firstC3 === void 0 ? void 0 : _slice$content$firstC3.type) === panel && (0, _index.isEmptyNode)(panelNode) && selection.$from.node() === selection.$to.node()) {
175
- return Boolean((0, _lists.insertSliceInsideOfPanelNodeSelected)(panelNode)({
177
+ return Boolean((0, _lists2.insertSliceInsideOfPanelNodeSelected)(panelNode)({
176
178
  tr: tr,
177
179
  slice: slice
178
180
  }));
@@ -627,7 +629,11 @@ function insertAutoMacro(slice, macro, view, from, to) {
627
629
 
628
630
  // replace the text with the macro as a separate transaction
629
631
  // so the autoconversion generates 2 undo steps
630
- view.dispatch((0, _prosemirrorHistory.closeHistory)(view.state.tr).replaceRangeWith(before, before + slice.size, macro).scrollIntoView());
632
+ var macroTr = (0, _prosemirrorHistory.closeHistory)(view.state.tr).replaceRangeWith(before, before + slice.size, macro).scrollIntoView();
633
+ (0, _card.addLinkMetadata)(view.state.selection, macroTr, {
634
+ inputMethod: _analytics.INPUT_METHOD.CLIPBOARD
635
+ });
636
+ view.dispatch(macroTr);
631
637
  return true;
632
638
  }
633
639
  return false;
@@ -907,21 +913,32 @@ function getTopLevelMarkTypesInSlice(slice) {
907
913
  return markTypes;
908
914
  }
909
915
  function handleParagraphBlockMarks(state, slice) {
916
+ var _findParentNodeOfType2;
910
917
  if (slice.content.size === 0) {
911
918
  return slice;
912
919
  }
913
920
  var schema = state.schema,
921
+ selection = state.selection,
914
922
  $from = state.selection.$from;
923
+ var destinationListNode = (_findParentNodeOfType2 = (0, _utils2.findParentNodeOfType)([schema.nodes.bulletList, schema.nodes.orderedList])(selection)) === null || _findParentNodeOfType2 === void 0 ? void 0 : _findParentNodeOfType2.node;
924
+ var currentNode = typeof $from.node === 'function' ? $from.node() : undefined;
925
+ var isInNormalTaskContext = (currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === schema.nodes.taskItem || $from.parent.type === schema.nodes.taskItem;
926
+ var isInSmallTaskContext = schema.nodes.blockTaskItem && (currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === schema.nodes.blockTaskItem || schema.nodes.blockTaskItem && $from.parent.type === schema.nodes.blockTaskItem || schema.nodes.blockTaskItem && $from.parent.type === schema.nodes.paragraph && $from.depth > 0 && $from.node($from.depth - 1).type === schema.nodes.blockTaskItem;
927
+ var destinationBlockMarkAttrs = (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true) && schema.marks.fontSize ? destinationListNode ? (0, _lists.getFirstParagraphBlockMarkAttrs)(destinationListNode, schema.marks.fontSize) : isInSmallTaskContext ? (0, _lists.getBlockMarkAttrs)($from.parent, schema.marks.fontSize) || (0, _lists.getFirstParagraphBlockMarkAttrs)(currentNode, schema.marks.fontSize) : false : false;
915
928
 
916
929
  // If no paragraph in the slice contains marks, there's no need for special handling
930
+ // unless we're pasting into a small-text list and need to add the destination block mark.
917
931
  // Note: this doesn't check for marks applied to lower level nodes such as text
918
- if (!sliceHasTopLevelMarks(slice)) {
932
+ if (!sliceHasTopLevelMarks(slice) && !destinationBlockMarkAttrs) {
919
933
  return slice;
920
934
  }
935
+ var shouldNormalizeFontSizeForTarget = (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true) && !!schema.marks.fontSize && (!!destinationListNode || isInNormalTaskContext || !!isInSmallTaskContext);
921
936
 
922
- // If pasting a single paragraph into pre-existing content, match destination formatting
937
+ // If pasting a single paragraph into pre-existing content, match destination formatting.
938
+ // For bullet/ordered lists under small-text, we still need to normalize the paragraph block mark
939
+ // so pasted content adopts the destination list state.
923
940
  var destinationHasContent = $from.parent.textContent.length > 0;
924
- if (slice.content.childCount === 1 && destinationHasContent) {
941
+ if (slice.content.childCount === 1 && destinationHasContent && !shouldNormalizeFontSizeForTarget) {
925
942
  return slice;
926
943
  }
927
944
 
@@ -944,23 +961,16 @@ function handleParagraphBlockMarks(state, slice) {
944
961
  } finally {
945
962
  _iterator.f();
946
963
  }
947
- if (forbiddenMarkTypes.length === 0) {
948
- // In a slice containing one or more paragraphs at the document level (not wrapped in
949
- // another node), the first paragraph will only have its text content captured and pasted
950
- // since openStart is 1. We decrement the open depth of the slice so it retains any block
951
- // marks applied to it. We only care about the depth at the start of the selection so
952
- // there's no need to change openEnd - the rest of the slice gets pasted correctly.
953
- var openStart = Math.max(0, slice.openStart - 1);
954
- return new _model.Slice(slice.content, openStart, slice.openEnd);
955
- }
956
-
957
- // If the paragraph or heading contains marks forbidden by the parent node
958
- // (e.g. alignment/indentation), drop those marks from the slice
959
- return (0, _utils.mapSlice)(slice, function (node) {
964
+ var fontSizeMarkType = shouldNormalizeFontSizeForTarget ? schema.marks.fontSize : undefined;
965
+ var normalizedContent = (0, _utils.mapSlice)(slice, function (node) {
960
966
  if (node.type === schema.nodes.paragraph) {
961
- return schema.nodes.paragraph.createChecked(undefined, node.content, node.marks.filter(function (mark) {
967
+ var paragraphMarks = node.marks.filter(function (mark) {
962
968
  return !forbiddenMarkTypes.includes(mark.type);
963
- }));
969
+ });
970
+ var normalizedMarks = fontSizeMarkType ? paragraphMarks.filter(function (mark) {
971
+ return mark.type !== fontSizeMarkType;
972
+ }) : paragraphMarks;
973
+ return schema.nodes.paragraph.createChecked(undefined, node.content, destinationBlockMarkAttrs && fontSizeMarkType ? normalizedMarks.concat(fontSizeMarkType.create(destinationBlockMarkAttrs)) : normalizedMarks);
964
974
  } else if (node.type === schema.nodes.heading) {
965
975
  // Preserve heading attributes to keep formatting
966
976
  return schema.nodes.heading.createChecked(node.attrs, node.content, node.marks.filter(function (mark) {
@@ -969,6 +979,24 @@ function handleParagraphBlockMarks(state, slice) {
969
979
  }
970
980
  return node;
971
981
  });
982
+ if (forbiddenMarkTypes.length === 0 && !shouldNormalizeFontSizeForTarget) {
983
+ // In a slice containing one or more paragraphs at the document level (not wrapped in
984
+ // another node), the first paragraph will only have its text content captured and pasted
985
+ // since openStart is 1. We decrement the open depth of the slice so it retains any block
986
+ // marks applied to it. We only care about the depth at the start of the selection so
987
+ // there's no need to change openEnd - the rest of the slice gets pasted correctly.
988
+ var openStart = Math.max(0, slice.openStart - 1);
989
+ return new _model.Slice(slice.content, openStart, slice.openEnd);
990
+ }
991
+ if (forbiddenMarkTypes.length === 0 && shouldNormalizeFontSizeForTarget) {
992
+ var _openStart = Math.max(0, slice.openStart - 1);
993
+ return new _model.Slice(normalizedContent.content, _openStart, slice.openEnd);
994
+ }
995
+
996
+ // If the paragraph or heading contains marks forbidden by the parent node
997
+ // (e.g. alignment/indentation), drop those marks from the slice. For lists under the small
998
+ // text experiment, also normalize fontSize to the destination list state.
999
+ return new _model.Slice(normalizedContent.content, slice.openStart, slice.openEnd);
972
1000
  }
973
1001
 
974
1002
  /**
@@ -1010,7 +1038,7 @@ function flattenNestedListInSlice(slice) {
1010
1038
  }
1011
1039
  function handleRichText(slice, queueCardsFromChangedTr) {
1012
1040
  return function (state, dispatch) {
1013
- var _slice$content, _slice$content2, _firstChildOfSlice$ty, _lastChildOfSlice$typ, _panelParentOverCurre;
1041
+ var _slice$content, _slice$content2, _findParentNodeOfType3, _firstChildOfSlice$ty, _lastChildOfSlice$typ, _panelParentOverCurre;
1014
1042
  var _state$schema$nodes4 = state.schema.nodes,
1015
1043
  codeBlock = _state$schema$nodes4.codeBlock,
1016
1044
  heading = _state$schema$nodes4.heading,
@@ -1020,6 +1048,7 @@ function handleRichText(slice, queueCardsFromChangedTr) {
1020
1048
  schema = state.schema;
1021
1049
  var firstChildOfSlice = (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.firstChild;
1022
1050
  var lastChildOfSlice = (_slice$content2 = slice.content) === null || _slice$content2 === void 0 ? void 0 : _slice$content2.lastChild;
1051
+ var destinationListFontSizeAttrs = (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true) ? (0, _lists.getFirstParagraphBlockMarkAttrs)((_findParentNodeOfType3 = (0, _utils2.findParentNodeOfType)([schema.nodes.bulletList, schema.nodes.orderedList])(selection)) === null || _findParentNodeOfType3 === void 0 ? void 0 : _findParentNodeOfType3.node, schema.marks.fontSize) : false;
1023
1052
 
1024
1053
  // In case user is pasting inline code,
1025
1054
  // any backtick ` immediately preceding it should be removed.
@@ -1104,6 +1133,12 @@ function handleRichText(slice, queueCardsFromChangedTr) {
1104
1133
  }
1105
1134
  }
1106
1135
  }
1136
+ if ((0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true) && isSliceContentListNodes) {
1137
+ var containingList = (0, _utils2.findParentNodeOfTypeClosestToPos)(tr.doc.resolve(tr.mapping.map(selection.from)), [schema.nodes.bulletList, schema.nodes.orderedList]);
1138
+ if (containingList) {
1139
+ (0, _lists.reconcileBlockMarkForContainerAtPos)(tr, containingList.pos, schema.marks.fontSize, destinationListFontSizeAttrs);
1140
+ }
1141
+ }
1107
1142
  tr.setStoredMarks([]);
1108
1143
  if (tr.selection.empty && tr.selection.$from.parent.type === codeBlock) {
1109
1144
  tr.setSelection(_state.TextSelection.near(tr.selection.$from, 1));
@@ -1,7 +1,9 @@
1
1
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
2
2
  import uuid from 'uuid/v4';
3
3
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
+ import { addLinkMetadata } from '@atlaskit/editor-common/card';
4
5
  import { insideTable } from '@atlaskit/editor-common/core-utils';
6
+ import { getBlockMarkAttrs, getFirstParagraphBlockMarkAttrs, reconcileBlockMarkForContainerAtPos } from '@atlaskit/editor-common/lists';
5
7
  import { anyMarkActive } from '@atlaskit/editor-common/mark';
6
8
  import { getParentOfTypeCount, getPositionAfterTopParentNodeOfType } from '@atlaskit/editor-common/nesting';
7
9
  import { GapCursorSelection, Side } from '@atlaskit/editor-common/selection';
@@ -596,7 +598,11 @@ function insertAutoMacro(slice, macro, view, from, to) {
596
598
 
597
599
  // replace the text with the macro as a separate transaction
598
600
  // so the autoconversion generates 2 undo steps
599
- view.dispatch(closeHistory(view.state.tr).replaceRangeWith(before, before + slice.size, macro).scrollIntoView());
601
+ const macroTr = closeHistory(view.state.tr).replaceRangeWith(before, before + slice.size, macro).scrollIntoView();
602
+ addLinkMetadata(view.state.selection, macroTr, {
603
+ inputMethod: INPUT_METHOD.CLIPBOARD
604
+ });
605
+ view.dispatch(macroTr);
600
606
  return true;
601
607
  }
602
608
  return false;
@@ -893,25 +899,36 @@ function getTopLevelMarkTypesInSlice(slice) {
893
899
  return markTypes;
894
900
  }
895
901
  export function handleParagraphBlockMarks(state, slice) {
902
+ var _findParentNodeOfType2;
896
903
  if (slice.content.size === 0) {
897
904
  return slice;
898
905
  }
899
906
  const {
900
907
  schema,
908
+ selection,
901
909
  selection: {
902
910
  $from
903
911
  }
904
912
  } = state;
913
+ const destinationListNode = (_findParentNodeOfType2 = findParentNodeOfType([schema.nodes.bulletList, schema.nodes.orderedList])(selection)) === null || _findParentNodeOfType2 === void 0 ? void 0 : _findParentNodeOfType2.node;
914
+ const currentNode = typeof $from.node === 'function' ? $from.node() : undefined;
915
+ const isInNormalTaskContext = (currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === schema.nodes.taskItem || $from.parent.type === schema.nodes.taskItem;
916
+ const isInSmallTaskContext = schema.nodes.blockTaskItem && (currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === schema.nodes.blockTaskItem || schema.nodes.blockTaskItem && $from.parent.type === schema.nodes.blockTaskItem || schema.nodes.blockTaskItem && $from.parent.type === schema.nodes.paragraph && $from.depth > 0 && $from.node($from.depth - 1).type === schema.nodes.blockTaskItem;
917
+ const destinationBlockMarkAttrs = expValEquals('platform_editor_small_font_size', 'isEnabled', true) && schema.marks.fontSize ? destinationListNode ? getFirstParagraphBlockMarkAttrs(destinationListNode, schema.marks.fontSize) : isInSmallTaskContext ? getBlockMarkAttrs($from.parent, schema.marks.fontSize) || getFirstParagraphBlockMarkAttrs(currentNode, schema.marks.fontSize) : false : false;
905
918
 
906
919
  // If no paragraph in the slice contains marks, there's no need for special handling
920
+ // unless we're pasting into a small-text list and need to add the destination block mark.
907
921
  // Note: this doesn't check for marks applied to lower level nodes such as text
908
- if (!sliceHasTopLevelMarks(slice)) {
922
+ if (!sliceHasTopLevelMarks(slice) && !destinationBlockMarkAttrs) {
909
923
  return slice;
910
924
  }
925
+ const shouldNormalizeFontSizeForTarget = expValEquals('platform_editor_small_font_size', 'isEnabled', true) && !!schema.marks.fontSize && (!!destinationListNode || isInNormalTaskContext || !!isInSmallTaskContext);
911
926
 
912
- // If pasting a single paragraph into pre-existing content, match destination formatting
927
+ // If pasting a single paragraph into pre-existing content, match destination formatting.
928
+ // For bullet/ordered lists under small-text, we still need to normalize the paragraph block mark
929
+ // so pasted content adopts the destination list state.
913
930
  const destinationHasContent = $from.parent.textContent.length > 0;
914
- if (slice.content.childCount === 1 && destinationHasContent) {
931
+ if (slice.content.childCount === 1 && destinationHasContent && !shouldNormalizeFontSizeForTarget) {
915
932
  return slice;
916
933
  }
917
934
 
@@ -925,7 +942,19 @@ export function handleParagraphBlockMarks(state, slice) {
925
942
  forbiddenMarkTypes.push(markType);
926
943
  }
927
944
  }
928
- if (forbiddenMarkTypes.length === 0) {
945
+ const fontSizeMarkType = shouldNormalizeFontSizeForTarget ? schema.marks.fontSize : undefined;
946
+ const normalizedContent = mapSlice(slice, node => {
947
+ if (node.type === schema.nodes.paragraph) {
948
+ const paragraphMarks = node.marks.filter(mark => !forbiddenMarkTypes.includes(mark.type));
949
+ const normalizedMarks = fontSizeMarkType ? paragraphMarks.filter(mark => mark.type !== fontSizeMarkType) : paragraphMarks;
950
+ return schema.nodes.paragraph.createChecked(undefined, node.content, destinationBlockMarkAttrs && fontSizeMarkType ? normalizedMarks.concat(fontSizeMarkType.create(destinationBlockMarkAttrs)) : normalizedMarks);
951
+ } else if (node.type === schema.nodes.heading) {
952
+ // Preserve heading attributes to keep formatting
953
+ return schema.nodes.heading.createChecked(node.attrs, node.content, node.marks.filter(mark => !forbiddenMarkTypes.includes(mark.type)));
954
+ }
955
+ return node;
956
+ });
957
+ if (forbiddenMarkTypes.length === 0 && !shouldNormalizeFontSizeForTarget) {
929
958
  // In a slice containing one or more paragraphs at the document level (not wrapped in
930
959
  // another node), the first paragraph will only have its text content captured and pasted
931
960
  // since openStart is 1. We decrement the open depth of the slice so it retains any block
@@ -934,18 +963,15 @@ export function handleParagraphBlockMarks(state, slice) {
934
963
  const openStart = Math.max(0, slice.openStart - 1);
935
964
  return new Slice(slice.content, openStart, slice.openEnd);
936
965
  }
966
+ if (forbiddenMarkTypes.length === 0 && shouldNormalizeFontSizeForTarget) {
967
+ const openStart = Math.max(0, slice.openStart - 1);
968
+ return new Slice(normalizedContent.content, openStart, slice.openEnd);
969
+ }
937
970
 
938
971
  // If the paragraph or heading contains marks forbidden by the parent node
939
- // (e.g. alignment/indentation), drop those marks from the slice
940
- return mapSlice(slice, node => {
941
- if (node.type === schema.nodes.paragraph) {
942
- return schema.nodes.paragraph.createChecked(undefined, node.content, node.marks.filter(mark => !forbiddenMarkTypes.includes(mark.type)));
943
- } else if (node.type === schema.nodes.heading) {
944
- // Preserve heading attributes to keep formatting
945
- return schema.nodes.heading.createChecked(node.attrs, node.content, node.marks.filter(mark => !forbiddenMarkTypes.includes(mark.type)));
946
- }
947
- return node;
948
- });
972
+ // (e.g. alignment/indentation), drop those marks from the slice. For lists under the small
973
+ // text experiment, also normalize fontSize to the destination list state.
974
+ return new Slice(normalizedContent.content, slice.openStart, slice.openEnd);
949
975
  }
950
976
 
951
977
  /**
@@ -987,7 +1013,7 @@ export function flattenNestedListInSlice(slice) {
987
1013
  }
988
1014
  export function handleRichText(slice, queueCardsFromChangedTr) {
989
1015
  return (state, dispatch) => {
990
- var _slice$content, _slice$content2, _firstChildOfSlice$ty, _lastChildOfSlice$typ, _panelParentOverCurre;
1016
+ var _slice$content, _slice$content2, _findParentNodeOfType3, _firstChildOfSlice$ty, _lastChildOfSlice$typ, _panelParentOverCurre;
991
1017
  const {
992
1018
  codeBlock,
993
1019
  heading,
@@ -1000,6 +1026,7 @@ export function handleRichText(slice, queueCardsFromChangedTr) {
1000
1026
  } = state;
1001
1027
  const firstChildOfSlice = (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.firstChild;
1002
1028
  const lastChildOfSlice = (_slice$content2 = slice.content) === null || _slice$content2 === void 0 ? void 0 : _slice$content2.lastChild;
1029
+ const destinationListFontSizeAttrs = expValEquals('platform_editor_small_font_size', 'isEnabled', true) ? getFirstParagraphBlockMarkAttrs((_findParentNodeOfType3 = findParentNodeOfType([schema.nodes.bulletList, schema.nodes.orderedList])(selection)) === null || _findParentNodeOfType3 === void 0 ? void 0 : _findParentNodeOfType3.node, schema.marks.fontSize) : false;
1003
1030
 
1004
1031
  // In case user is pasting inline code,
1005
1032
  // any backtick ` immediately preceding it should be removed.
@@ -1084,6 +1111,12 @@ export function handleRichText(slice, queueCardsFromChangedTr) {
1084
1111
  }
1085
1112
  }
1086
1113
  }
1114
+ if (expValEquals('platform_editor_small_font_size', 'isEnabled', true) && isSliceContentListNodes) {
1115
+ const containingList = findParentNodeOfTypeClosestToPos(tr.doc.resolve(tr.mapping.map(selection.from)), [schema.nodes.bulletList, schema.nodes.orderedList]);
1116
+ if (containingList) {
1117
+ reconcileBlockMarkForContainerAtPos(tr, containingList.pos, schema.marks.fontSize, destinationListFontSizeAttrs);
1118
+ }
1119
+ }
1087
1120
  tr.setStoredMarks([]);
1088
1121
  if (tr.selection.empty && tr.selection.$from.parent.type === codeBlock) {
1089
1122
  tr.setSelection(TextSelection.near(tr.selection.$from, 1));
@@ -9,7 +9,9 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
9
9
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
10
10
  import uuid from 'uuid/v4';
11
11
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
12
+ import { addLinkMetadata } from '@atlaskit/editor-common/card';
12
13
  import { insideTable } from '@atlaskit/editor-common/core-utils';
14
+ import { getBlockMarkAttrs, getFirstParagraphBlockMarkAttrs, reconcileBlockMarkForContainerAtPos } from '@atlaskit/editor-common/lists';
13
15
  import { anyMarkActive } from '@atlaskit/editor-common/mark';
14
16
  import { getParentOfTypeCount, getPositionAfterTopParentNodeOfType } from '@atlaskit/editor-common/nesting';
15
17
  import { GapCursorSelection, Side } from '@atlaskit/editor-common/selection';
@@ -600,7 +602,11 @@ function insertAutoMacro(slice, macro, view, from, to) {
600
602
 
601
603
  // replace the text with the macro as a separate transaction
602
604
  // so the autoconversion generates 2 undo steps
603
- view.dispatch(closeHistory(view.state.tr).replaceRangeWith(before, before + slice.size, macro).scrollIntoView());
605
+ var macroTr = closeHistory(view.state.tr).replaceRangeWith(before, before + slice.size, macro).scrollIntoView();
606
+ addLinkMetadata(view.state.selection, macroTr, {
607
+ inputMethod: INPUT_METHOD.CLIPBOARD
608
+ });
609
+ view.dispatch(macroTr);
604
610
  return true;
605
611
  }
606
612
  return false;
@@ -880,21 +886,32 @@ function getTopLevelMarkTypesInSlice(slice) {
880
886
  return markTypes;
881
887
  }
882
888
  export function handleParagraphBlockMarks(state, slice) {
889
+ var _findParentNodeOfType2;
883
890
  if (slice.content.size === 0) {
884
891
  return slice;
885
892
  }
886
893
  var schema = state.schema,
894
+ selection = state.selection,
887
895
  $from = state.selection.$from;
896
+ var destinationListNode = (_findParentNodeOfType2 = findParentNodeOfType([schema.nodes.bulletList, schema.nodes.orderedList])(selection)) === null || _findParentNodeOfType2 === void 0 ? void 0 : _findParentNodeOfType2.node;
897
+ var currentNode = typeof $from.node === 'function' ? $from.node() : undefined;
898
+ var isInNormalTaskContext = (currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === schema.nodes.taskItem || $from.parent.type === schema.nodes.taskItem;
899
+ var isInSmallTaskContext = schema.nodes.blockTaskItem && (currentNode === null || currentNode === void 0 ? void 0 : currentNode.type) === schema.nodes.blockTaskItem || schema.nodes.blockTaskItem && $from.parent.type === schema.nodes.blockTaskItem || schema.nodes.blockTaskItem && $from.parent.type === schema.nodes.paragraph && $from.depth > 0 && $from.node($from.depth - 1).type === schema.nodes.blockTaskItem;
900
+ var destinationBlockMarkAttrs = expValEquals('platform_editor_small_font_size', 'isEnabled', true) && schema.marks.fontSize ? destinationListNode ? getFirstParagraphBlockMarkAttrs(destinationListNode, schema.marks.fontSize) : isInSmallTaskContext ? getBlockMarkAttrs($from.parent, schema.marks.fontSize) || getFirstParagraphBlockMarkAttrs(currentNode, schema.marks.fontSize) : false : false;
888
901
 
889
902
  // If no paragraph in the slice contains marks, there's no need for special handling
903
+ // unless we're pasting into a small-text list and need to add the destination block mark.
890
904
  // Note: this doesn't check for marks applied to lower level nodes such as text
891
- if (!sliceHasTopLevelMarks(slice)) {
905
+ if (!sliceHasTopLevelMarks(slice) && !destinationBlockMarkAttrs) {
892
906
  return slice;
893
907
  }
908
+ var shouldNormalizeFontSizeForTarget = expValEquals('platform_editor_small_font_size', 'isEnabled', true) && !!schema.marks.fontSize && (!!destinationListNode || isInNormalTaskContext || !!isInSmallTaskContext);
894
909
 
895
- // If pasting a single paragraph into pre-existing content, match destination formatting
910
+ // If pasting a single paragraph into pre-existing content, match destination formatting.
911
+ // For bullet/ordered lists under small-text, we still need to normalize the paragraph block mark
912
+ // so pasted content adopts the destination list state.
896
913
  var destinationHasContent = $from.parent.textContent.length > 0;
897
- if (slice.content.childCount === 1 && destinationHasContent) {
914
+ if (slice.content.childCount === 1 && destinationHasContent && !shouldNormalizeFontSizeForTarget) {
898
915
  return slice;
899
916
  }
900
917
 
@@ -917,23 +934,16 @@ export function handleParagraphBlockMarks(state, slice) {
917
934
  } finally {
918
935
  _iterator.f();
919
936
  }
920
- if (forbiddenMarkTypes.length === 0) {
921
- // In a slice containing one or more paragraphs at the document level (not wrapped in
922
- // another node), the first paragraph will only have its text content captured and pasted
923
- // since openStart is 1. We decrement the open depth of the slice so it retains any block
924
- // marks applied to it. We only care about the depth at the start of the selection so
925
- // there's no need to change openEnd - the rest of the slice gets pasted correctly.
926
- var openStart = Math.max(0, slice.openStart - 1);
927
- return new Slice(slice.content, openStart, slice.openEnd);
928
- }
929
-
930
- // If the paragraph or heading contains marks forbidden by the parent node
931
- // (e.g. alignment/indentation), drop those marks from the slice
932
- return mapSlice(slice, function (node) {
937
+ var fontSizeMarkType = shouldNormalizeFontSizeForTarget ? schema.marks.fontSize : undefined;
938
+ var normalizedContent = mapSlice(slice, function (node) {
933
939
  if (node.type === schema.nodes.paragraph) {
934
- return schema.nodes.paragraph.createChecked(undefined, node.content, node.marks.filter(function (mark) {
940
+ var paragraphMarks = node.marks.filter(function (mark) {
935
941
  return !forbiddenMarkTypes.includes(mark.type);
936
- }));
942
+ });
943
+ var normalizedMarks = fontSizeMarkType ? paragraphMarks.filter(function (mark) {
944
+ return mark.type !== fontSizeMarkType;
945
+ }) : paragraphMarks;
946
+ return schema.nodes.paragraph.createChecked(undefined, node.content, destinationBlockMarkAttrs && fontSizeMarkType ? normalizedMarks.concat(fontSizeMarkType.create(destinationBlockMarkAttrs)) : normalizedMarks);
937
947
  } else if (node.type === schema.nodes.heading) {
938
948
  // Preserve heading attributes to keep formatting
939
949
  return schema.nodes.heading.createChecked(node.attrs, node.content, node.marks.filter(function (mark) {
@@ -942,6 +952,24 @@ export function handleParagraphBlockMarks(state, slice) {
942
952
  }
943
953
  return node;
944
954
  });
955
+ if (forbiddenMarkTypes.length === 0 && !shouldNormalizeFontSizeForTarget) {
956
+ // In a slice containing one or more paragraphs at the document level (not wrapped in
957
+ // another node), the first paragraph will only have its text content captured and pasted
958
+ // since openStart is 1. We decrement the open depth of the slice so it retains any block
959
+ // marks applied to it. We only care about the depth at the start of the selection so
960
+ // there's no need to change openEnd - the rest of the slice gets pasted correctly.
961
+ var openStart = Math.max(0, slice.openStart - 1);
962
+ return new Slice(slice.content, openStart, slice.openEnd);
963
+ }
964
+ if (forbiddenMarkTypes.length === 0 && shouldNormalizeFontSizeForTarget) {
965
+ var _openStart = Math.max(0, slice.openStart - 1);
966
+ return new Slice(normalizedContent.content, _openStart, slice.openEnd);
967
+ }
968
+
969
+ // If the paragraph or heading contains marks forbidden by the parent node
970
+ // (e.g. alignment/indentation), drop those marks from the slice. For lists under the small
971
+ // text experiment, also normalize fontSize to the destination list state.
972
+ return new Slice(normalizedContent.content, slice.openStart, slice.openEnd);
945
973
  }
946
974
 
947
975
  /**
@@ -983,7 +1011,7 @@ export function flattenNestedListInSlice(slice) {
983
1011
  }
984
1012
  export function handleRichText(slice, queueCardsFromChangedTr) {
985
1013
  return function (state, dispatch) {
986
- var _slice$content, _slice$content2, _firstChildOfSlice$ty, _lastChildOfSlice$typ, _panelParentOverCurre;
1014
+ var _slice$content, _slice$content2, _findParentNodeOfType3, _firstChildOfSlice$ty, _lastChildOfSlice$typ, _panelParentOverCurre;
987
1015
  var _state$schema$nodes4 = state.schema.nodes,
988
1016
  codeBlock = _state$schema$nodes4.codeBlock,
989
1017
  heading = _state$schema$nodes4.heading,
@@ -993,6 +1021,7 @@ export function handleRichText(slice, queueCardsFromChangedTr) {
993
1021
  schema = state.schema;
994
1022
  var firstChildOfSlice = (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.firstChild;
995
1023
  var lastChildOfSlice = (_slice$content2 = slice.content) === null || _slice$content2 === void 0 ? void 0 : _slice$content2.lastChild;
1024
+ var destinationListFontSizeAttrs = expValEquals('platform_editor_small_font_size', 'isEnabled', true) ? getFirstParagraphBlockMarkAttrs((_findParentNodeOfType3 = findParentNodeOfType([schema.nodes.bulletList, schema.nodes.orderedList])(selection)) === null || _findParentNodeOfType3 === void 0 ? void 0 : _findParentNodeOfType3.node, schema.marks.fontSize) : false;
996
1025
 
997
1026
  // In case user is pasting inline code,
998
1027
  // any backtick ` immediately preceding it should be removed.
@@ -1077,6 +1106,12 @@ export function handleRichText(slice, queueCardsFromChangedTr) {
1077
1106
  }
1078
1107
  }
1079
1108
  }
1109
+ if (expValEquals('platform_editor_small_font_size', 'isEnabled', true) && isSliceContentListNodes) {
1110
+ var containingList = findParentNodeOfTypeClosestToPos(tr.doc.resolve(tr.mapping.map(selection.from)), [schema.nodes.bulletList, schema.nodes.orderedList]);
1111
+ if (containingList) {
1112
+ reconcileBlockMarkForContainerAtPos(tr, containingList.pos, schema.marks.fontSize, destinationListFontSizeAttrs);
1113
+ }
1114
+ }
1080
1115
  tr.setStoredMarks([]);
1081
1116
  if (tr.selection.empty && tr.selection.$from.parent.type === codeBlock) {
1082
1117
  tr.setSelection(TextSelection.near(tr.selection.$from, 1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-paste",
3
- "version": "9.0.15",
3
+ "version": "9.1.0",
4
4
  "description": "Paste plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -36,27 +36,27 @@
36
36
  "@atlaskit/editor-plugin-card": "^13.1.0",
37
37
  "@atlaskit/editor-plugin-expand": "^9.1.0",
38
38
  "@atlaskit/editor-plugin-feature-flags": "^7.0.0",
39
- "@atlaskit/editor-plugin-list": "^10.1.0",
39
+ "@atlaskit/editor-plugin-list": "^10.2.0",
40
40
  "@atlaskit/editor-plugin-media": "^10.1.0",
41
41
  "@atlaskit/editor-plugin-mentions": "^10.0.0",
42
42
  "@atlaskit/editor-prosemirror": "^7.3.0",
43
43
  "@atlaskit/editor-tables": "^2.9.0",
44
44
  "@atlaskit/flag": "^17.9.0",
45
45
  "@atlaskit/icon": "^33.1.0",
46
- "@atlaskit/insm": "^0.3.0",
46
+ "@atlaskit/insm": "^0.4.0",
47
47
  "@atlaskit/media-client": "^36.0.0",
48
48
  "@atlaskit/media-common": "^13.0.0",
49
49
  "@atlaskit/platform-feature-flags": "^1.1.0",
50
50
  "@atlaskit/prosemirror-history": "^0.2.0",
51
- "@atlaskit/tmp-editor-statsig": "^48.0.0",
52
- "@atlaskit/tokens": "^11.3.0",
51
+ "@atlaskit/tmp-editor-statsig": "^49.0.0",
52
+ "@atlaskit/tokens": "^11.4.0",
53
53
  "@babel/runtime": "^7.0.0",
54
54
  "lodash": "^4.17.21",
55
55
  "react-intl-next": "npm:react-intl@^5.18.1",
56
56
  "uuid": "^3.1.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "@atlaskit/editor-common": "^112.11.0",
59
+ "@atlaskit/editor-common": "^112.13.0",
60
60
  "react": "^18.2.0",
61
61
  "react-dom": "^18.2.0"
62
62
  },