@atlaskit/editor-plugin-block-controls 1.12.10 → 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/cjs/commands/move-node.js +18 -10
  3. package/dist/cjs/plugin.js +16 -5
  4. package/dist/cjs/pm-plugins/decorations.js +3 -2
  5. package/dist/cjs/pm-plugins/empty-block-experiment.js +41 -0
  6. package/dist/cjs/pm-plugins/handle-mouse-over.js +4 -3
  7. package/dist/cjs/ui/drop-target.js +7 -7
  8. package/dist/cjs/ui/empty-block-experiment/global-styles.js +51 -0
  9. package/dist/cjs/ui/empty-block-experiment/widget.js +121 -0
  10. package/dist/cjs/ui/global-styles.js +12 -3
  11. package/dist/es2019/commands/move-node.js +11 -3
  12. package/dist/es2019/plugin.js +10 -1
  13. package/dist/es2019/pm-plugins/decorations.js +3 -2
  14. package/dist/es2019/pm-plugins/empty-block-experiment.js +33 -0
  15. package/dist/es2019/pm-plugins/handle-mouse-over.js +4 -3
  16. package/dist/es2019/ui/drop-target.js +6 -6
  17. package/dist/es2019/ui/empty-block-experiment/global-styles.js +48 -0
  18. package/dist/es2019/ui/empty-block-experiment/widget.js +117 -0
  19. package/dist/es2019/ui/global-styles.js +12 -3
  20. package/dist/esm/commands/move-node.js +11 -3
  21. package/dist/esm/plugin.js +16 -5
  22. package/dist/esm/pm-plugins/decorations.js +3 -2
  23. package/dist/esm/pm-plugins/empty-block-experiment.js +35 -0
  24. package/dist/esm/pm-plugins/handle-mouse-over.js +4 -3
  25. package/dist/esm/ui/drop-target.js +7 -7
  26. package/dist/esm/ui/empty-block-experiment/global-styles.js +43 -0
  27. package/dist/esm/ui/empty-block-experiment/widget.js +111 -0
  28. package/dist/esm/ui/global-styles.js +12 -3
  29. package/dist/types/pm-plugins/empty-block-experiment.d.ts +13 -0
  30. package/dist/types/types.d.ts +7 -1
  31. package/dist/types/ui/empty-block-experiment/global-styles.d.ts +4 -0
  32. package/dist/types/ui/empty-block-experiment/widget.d.ts +13 -0
  33. package/dist/types-ts4.5/pm-plugins/empty-block-experiment.d.ts +13 -0
  34. package/dist/types-ts4.5/types.d.ts +7 -1
  35. package/dist/types-ts4.5/ui/empty-block-experiment/global-styles.d.ts +4 -0
  36. package/dist/types-ts4.5/ui/empty-block-experiment/widget.d.ts +13 -0
  37. package/package.json +5 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 1.13.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#137849](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/137849)
8
+ [`005c00c30884a`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/005c00c30884a) -
9
+ ED-24874 Only disable dragging top level nested nodes in tables
10
+ - [#137156](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/137156)
11
+ [`eccceae25389f`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/eccceae25389f) -
12
+ [ux] Don't show drop target in final container position if proceeded by empty paragraph, replace
13
+ it if only empty paragraph
14
+ - [#137755](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/137755)
15
+ [`f3d004d4e3a3e`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/f3d004d4e3a3e) -
16
+ ED-24650 fix nested node jittering issue
17
+ - Updated dependencies
18
+
19
+ ## 1.13.0
20
+
21
+ ### Minor Changes
22
+
23
+ - [#137234](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/137234)
24
+ [`e80c81de138e9`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/e80c81de138e9) -
25
+ [ux] [ED-24803] Experiment for editor block controls which adds a button to insert quickInsert
26
+ elements
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies
31
+
3
32
  ## 1.12.10
4
33
 
5
34
  ### Patch Changes
@@ -10,12 +10,13 @@ var _analytics = require("@atlaskit/editor-common/analytics");
10
10
  var _messages = require("@atlaskit/editor-common/messages");
11
11
  var _selection = require("@atlaskit/editor-common/selection");
12
12
  var _transforms = require("@atlaskit/editor-common/transforms");
13
- var _utils = require("@atlaskit/editor-tables/utils");
13
+ var _utils = require("@atlaskit/editor-common/utils");
14
+ var _utils2 = require("@atlaskit/editor-tables/utils");
14
15
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
15
16
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
16
17
  var _consts = require("../consts");
17
18
  var _main = require("../pm-plugins/main");
18
- var _utils2 = require("../utils");
19
+ var _utils3 = require("../utils");
19
20
  var _validation = require("../utils/validation");
20
21
  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; }
21
22
  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) { (0, _defineProperty2.default)(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; }
@@ -56,12 +57,12 @@ var getCurrentNodePos = function getCurrentNodePos(state) {
56
57
  if (activeNode && (_activeNode$handleOpt = activeNode.handleOptions) !== null && _activeNode$handleOpt !== void 0 && _activeNode$handleOpt.isFocused) {
57
58
  // 1. drag handle of the node is focused
58
59
  currentNodePos = activeNode.pos;
59
- } else if ((0, _utils.isInTable)(state)) {
60
- if ((0, _utils.isTableSelected)(selection)) {
60
+ } else if ((0, _utils2.isInTable)(state)) {
61
+ if ((0, _utils2.isTableSelected)(selection)) {
61
62
  var _findTable$pos, _findTable;
62
63
  // We only move table node if it's fully selected
63
64
  // to avoid shortcut collision with table drag and drop
64
- currentNodePos = (_findTable$pos = (_findTable = (0, _utils.findTable)(selection)) === null || _findTable === void 0 ? void 0 : _findTable.pos) !== null && _findTable$pos !== void 0 ? _findTable$pos : currentNodePos;
65
+ currentNodePos = (_findTable$pos = (_findTable = (0, _utils2.findTable)(selection)) === null || _findTable === void 0 ? void 0 : _findTable.pos) !== null && _findTable$pos !== void 0 ? _findTable$pos : currentNodePos;
65
66
  }
66
67
  } else if (!(state.selection instanceof _selection.GapCursorSelection)) {
67
68
  // 2. caret cursor is inside the node
@@ -108,7 +109,7 @@ var moveNodeViaShortcut = exports.moveNodeViaShortcut = function moveNodeViaShor
108
109
  // If the node is first/last one, only select the node
109
110
  api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (_ref3) {
110
111
  var tr = _ref3.tr;
111
- (0, _utils2.selectNode)(tr, currentNodePos, nodeType);
112
+ (0, _utils3.selectNode)(tr, currentNodePos, nodeType);
112
113
  tr.scrollIntoView();
113
114
  return tr;
114
115
  });
@@ -134,7 +135,6 @@ var moveNode = exports.moveNode = function moveNode(api) {
134
135
  var end = start + size;
135
136
  var mappedTo;
136
137
  if ((0, _experiments.editorExperiment)('nested-dnd', true)) {
137
- var _transformSourceSlice;
138
138
  var nodeCopy = tr.doc.slice(start, end, false); // cut the content
139
139
  var $to = tr.doc.resolve(to);
140
140
  var $from = tr.doc.resolve(start);
@@ -143,20 +143,28 @@ var moveNode = exports.moveNode = function moveNode(api) {
143
143
  if (!(0, _validation.canMoveNodeToIndex)(destParent, $to.index(), $from.node().child($from.index()))) {
144
144
  return tr;
145
145
  }
146
- var convertedNode = (_transformSourceSlice = transformSourceSlice(nodeCopy, destType)) === null || _transformSourceSlice === void 0 ? void 0 : _transformSourceSlice.content;
146
+ var convertedNodeSlice = transformSourceSlice(nodeCopy, destType);
147
+ var convertedNode = convertedNodeSlice === null || convertedNodeSlice === void 0 ? void 0 : convertedNodeSlice.content;
147
148
  if (!convertedNode) {
148
149
  return tr;
149
150
  }
150
151
  tr.delete(start, end); // delete the content from the original position
151
152
  mappedTo = tr.mapping.map(to);
152
- tr.insert(mappedTo, convertedNode); // insert the content at the new position
153
+ var isDestNestedLoneEmptyParagraph = destParent.type.name !== 'doc' && destParent.childCount === 1 && (0, _utils.isEmptyParagraph)($to.nodeAfter);
154
+ if (convertedNodeSlice && isDestNestedLoneEmptyParagraph) {
155
+ // if only a single empty paragraph within container, replace it
156
+ tr.replace(mappedTo, mappedTo + 1, convertedNodeSlice);
157
+ } else {
158
+ // otherwise just insert the content at the new position
159
+ tr.insert(mappedTo, convertedNode);
160
+ }
153
161
  } else {
154
162
  var _nodeCopy = tr.doc.content.cut(start, end); // cut the content
155
163
  tr.delete(start, end); // delete the content from the original position
156
164
  mappedTo = tr.mapping.map(to);
157
165
  tr.insert(mappedTo, _nodeCopy); // insert the content at the new position
158
166
  }
159
- tr = (0, _utils2.selectNode)(tr, mappedTo, node.type.name);
167
+ tr = (0, _utils3.selectNode)(tr, mappedTo, node.type.name);
160
168
  tr.setMeta(_main.key, {
161
169
  nodeMoved: true
162
170
  });
@@ -5,8 +5,11 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.blockControlsPlugin = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
8
9
  var _react = _interopRequireDefault(require("react"));
10
+ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
9
11
  var _moveNode = require("./commands/move-node");
12
+ var _emptyBlockExperiment = require("./pm-plugins/empty-block-experiment");
10
13
  var _main = require("./pm-plugins/main");
11
14
  var _dragHandleMenu = require("./ui/drag-handle-menu");
12
15
  var _globalStyles = require("./ui/global-styles");
@@ -22,13 +25,21 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
22
25
  var getIntl = _ref2.getIntl;
23
26
  return (0, _main.createPlugin)(api, getIntl);
24
27
  }
25
- }];
28
+ }].concat((0, _toConsumableArray2.default)((0, _experiments.editorExperiment)('platform_editor_empty_line_prompt', true, {
29
+ exposure: true
30
+ }) ? [{
31
+ name: 'emptyBlockExperimentPlugin',
32
+ plugin: function plugin(_ref3) {
33
+ var getIntl = _ref3.getIntl;
34
+ return (0, _emptyBlockExperiment.createEmptyBlockExperimentPlugin)(api, getIntl);
35
+ }
36
+ }] : []));
26
37
  },
27
38
  commands: {
28
39
  moveNode: (0, _moveNode.moveNode)(api),
29
40
  showDragHandleAt: function showDragHandleAt(pos, anchorName, nodeType, handleOptions) {
30
- return function (_ref3) {
31
- var tr = _ref3.tr;
41
+ return function (_ref4) {
42
+ var tr = _ref4.tr;
32
43
  tr.setMeta(_main.key, {
33
44
  activeNode: {
34
45
  pos: pos,
@@ -41,8 +52,8 @@ var blockControlsPlugin = exports.blockControlsPlugin = function blockControlsPl
41
52
  };
42
53
  },
43
54
  setNodeDragged: function setNodeDragged(pos, anchorName, nodeType) {
44
- return function (_ref4) {
45
- var tr = _ref4.tr;
55
+ return function (_ref5) {
56
+ var tr = _ref5.tr;
46
57
  if (pos === undefined) {
47
58
  return tr;
48
59
  }
@@ -13,6 +13,7 @@ var _bindEventListener = require("bind-event-listener");
13
13
  var _reactDom = _interopRequireDefault(require("react-dom"));
14
14
  var _reactIntlNext = require("react-intl-next");
15
15
  var _uuid = _interopRequireDefault(require("uuid"));
16
+ var _utils = require("@atlaskit/editor-common/utils");
16
17
  var _view = require("@atlaskit/editor-prosemirror/view");
17
18
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
18
19
  var _dragHandle = require("../ui/drag-handle");
@@ -48,7 +49,7 @@ var dropTargetDecorations = exports.dropTargetDecorations = function dropTargetD
48
49
  var activePMNode = typeof activeNodePos === 'number' && newState.doc.resolve(activeNodePos).nodeAfter;
49
50
  newState.doc.nodesBetween(0, newState.doc.nodeSize - 2, function (node, pos, parent, index) {
50
51
  var depth = 0;
51
- // drop target dco at the end position
52
+ // drop target deco at the end position
52
53
  var endPosDeco = null;
53
54
  if ((0, _experiments.editorExperiment)('nested-dnd', true)) {
54
55
  depth = newState.doc.resolve(pos).depth;
@@ -71,7 +72,7 @@ var dropTargetDecorations = exports.dropTargetDecorations = function dropTargetD
71
72
  id: pos,
72
73
  pos: pos
73
74
  });
74
- if (parent.lastChild === node && PARENT_WITH_END_DROP_TARGET.includes(parent.type.name)) {
75
+ if (parent.lastChild === node && !(0, _utils.isEmptyParagraph)(node) && PARENT_WITH_END_DROP_TARGET.includes(parent.type.name)) {
75
76
  var endpos = pos + node.nodeSize;
76
77
  endPosDeco = {
77
78
  id: endpos,
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.emptyBlockExperimentPluginKey = exports.createEmptyBlockExperimentPlugin = void 0;
7
+ var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
8
+ var _state = require("@atlaskit/editor-prosemirror/state");
9
+ var _view = require("@atlaskit/editor-prosemirror/view");
10
+ var _widget = require("../ui/empty-block-experiment/widget");
11
+ var emptyBlockExperimentPluginKey = exports.emptyBlockExperimentPluginKey = new _state.PluginKey('emptyBlockExperiment');
12
+ var getDecorations = function getDecorations(tr, api, getIntl) {
13
+ var widget = (0, _widget.createEmptyBlockWidgetDecoration)(tr.selection, api, getIntl);
14
+ if (widget) {
15
+ return _view.DecorationSet.create(tr.doc, [widget]);
16
+ }
17
+ return _view.DecorationSet.empty;
18
+ };
19
+ var createEmptyBlockExperimentPlugin = exports.createEmptyBlockExperimentPlugin = function createEmptyBlockExperimentPlugin(api, getIntl) {
20
+ return new _safePlugin.SafePlugin({
21
+ key: emptyBlockExperimentPluginKey,
22
+ state: {
23
+ init: function init(_, _editorState) {
24
+ return {
25
+ decorations: _view.DecorationSet.empty
26
+ };
27
+ },
28
+ apply: function apply(tr, _currentState) {
29
+ return {
30
+ decorations: getDecorations(tr, api, getIntl)
31
+ };
32
+ }
33
+ },
34
+ props: {
35
+ decorations: function decorations(state) {
36
+ var _emptyBlockExperiment;
37
+ return (_emptyBlockExperiment = emptyBlockExperimentPluginKey.getState(state)) === null || _emptyBlockExperiment === void 0 ? void 0 : _emptyBlockExperiment.decorations;
38
+ }
39
+ }
40
+ });
41
+ };
@@ -22,11 +22,12 @@ var handleMouseOver = exports.handleMouseOver = function handleMouseOver(view, e
22
22
  }
23
23
  var rootElement = target === null || target === void 0 ? void 0 : target.closest('[data-drag-handler-anchor-name]');
24
24
  if (rootElement) {
25
- var tableElement = rootElement.closest('[data-drag-handler-node-type="table"]');
26
- if (tableElement && (0, _experiments.editorExperiment)('nested-dnd', true) && (0, _experiments.editorExperiment)('table-nested-dnd', false, {
25
+ var _rootElement$parentEl;
26
+ var parentElement = (_rootElement$parentEl = rootElement.parentElement) === null || _rootElement$parentEl === void 0 ? void 0 : _rootElement$parentEl.closest('[data-drag-handler-anchor-name]');
27
+ if (parentElement && parentElement.getAttribute('data-drag-handler-node-type') === 'table' && (0, _experiments.editorExperiment)('nested-dnd', true) && (0, _experiments.editorExperiment)('table-nested-dnd', false, {
27
28
  exposure: true
28
29
  })) {
29
- rootElement = tableElement;
30
+ rootElement = parentElement;
30
31
  }
31
32
  var anchorName = rootElement.getAttribute('data-drag-handler-anchor-name');
32
33
  var nodeType = rootElement.getAttribute('data-drag-handler-node-type');
@@ -24,12 +24,12 @@ var _consts = require("./consts");
24
24
 
25
25
  var DEFAULT_DROP_INDICATOR_WIDTH = 760;
26
26
  var EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH = '--editor-block-controls-drop-indicator-width';
27
- var EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_LEFT_MARGIN = '--editor-block-controls-drop-indicator-leftMargin';
27
+ var EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN = '--editor-block-controls-drop-indicator-leftMargin';
28
28
  var styleDropTarget = (0, _react2.css)({
29
29
  height: "var(--ds-space-100, 8px)",
30
30
  marginTop: "var(--ds-space-negative-100, -8px)",
31
- marginLeft: "calc(-1 * var(".concat(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_LEFT_MARGIN, ", 0))"),
32
- paddingLeft: "var(".concat(EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_LEFT_MARGIN, ", 0)"),
31
+ marginLeft: "calc(-1 * var(".concat(EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN, ", 0))"),
32
+ paddingLeft: "var(".concat(EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN, ", 0)"),
33
33
  position: 'absolute',
34
34
  left: '0',
35
35
  display: 'block',
@@ -78,7 +78,7 @@ var getDropTargetOffsetStyle = function getDropTargetOffsetStyle(prevNode, nextN
78
78
  return marginLookupMap[offset];
79
79
  };
80
80
  var DropTarget = exports.DropTarget = function DropTarget(_ref3) {
81
- var _widthStyle;
81
+ var _dynamicStyle;
82
82
  var api = _ref3.api,
83
83
  id = _ref3.id,
84
84
  prevNode = _ref3.prevNode,
@@ -139,9 +139,9 @@ var DropTarget = exports.DropTarget = function DropTarget(_ref3) {
139
139
  }
140
140
  return getDropTargetOffsetStyle(prevNode, nextNode);
141
141
  }, [prevNode, nextNode, parentNode]);
142
- var widthStyle = (_widthStyle = {
142
+ var dynamicStyle = (_dynamicStyle = {
143
143
  width: isNestedDropTarget ? 'unset' : '100%'
144
- }, (0, _defineProperty2.default)(_widthStyle, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH, isNestedDropTarget ? '100%' : "".concat((widthState === null || widthState === void 0 ? void 0 : widthState.lineLength) || DEFAULT_DROP_INDICATOR_WIDTH, "px")), (0, _defineProperty2.default)(_widthStyle, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_LEFT_MARGIN, isNestedDropTarget ? (0, _consts.getNestedNodeLeftPaddingMargin)(parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name) : '0'), _widthStyle);
144
+ }, (0, _defineProperty2.default)(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_INDICATOR_WIDTH, isNestedDropTarget ? '100%' : "".concat((widthState === null || widthState === void 0 ? void 0 : widthState.lineLength) || DEFAULT_DROP_INDICATOR_WIDTH, "px")), (0, _defineProperty2.default)(_dynamicStyle, EDITOR_BLOCK_CONTROLS_DROP_TARGET_LEFT_MARGIN, isNestedDropTarget ? (0, _consts.getNestedNodeLeftPaddingMargin)(parentNode === null || parentNode === void 0 ? void 0 : parentNode.type.name) : '0'), _dynamicStyle);
145
145
  return (
146
146
  // Note: Firefox has trouble with using a button element as the handle for drag and drop
147
147
  (0, _react2.jsx)("div", {
@@ -149,7 +149,7 @@ var DropTarget = exports.DropTarget = function DropTarget(_ref3) {
149
149
  css: [styleDropTarget, dropTargetOffsetStyle, isNestedDropTarget && nestedDropIndicatorStyle]
150
150
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
151
151
  ,
152
- style: widthStyle,
152
+ style: dynamicStyle,
153
153
  ref: ref,
154
154
  "data-testid": "block-ctrl-drop-target"
155
155
  },
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.emptyBlockExperimentGlobalStyles = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _react = require("@emotion/react");
10
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
11
+ /* eslint-disable @atlaskit/ui-styling-standard/no-exported-styles */
12
+ /* eslint-disable @atlaskit/ui-styling-standard/no-unsafe-values */
13
+ /* eslint-disable @atlaskit/ui-styling-standard/no-nested-selectors */
14
+ /* eslint-disable @atlaskit/ui-styling-standard/no-important-styles */
15
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled
16
+
17
+ var emptyBlockExperimentWidget = '.ProseMirror-widget[data-empty-block-experiment="true"]';
18
+ var quickInsertWidget = '.ProseMirror-widget[data-type-ahead="typeaheadDecoration"]';
19
+ var formattingElement = 'div.fabric-editor-block-mark';
20
+ var elementWithEmptyBlockExperiment = "+ p > ".concat(emptyBlockExperimentWidget, ", + h1 > ").concat(emptyBlockExperimentWidget, ", + h2 > ").concat(emptyBlockExperimentWidget, ", + h3 > ").concat(emptyBlockExperimentWidget, ", + h4 > ").concat(emptyBlockExperimentWidget, ", + h5 > ").concat(emptyBlockExperimentWidget, ", + h6 > ").concat(emptyBlockExperimentWidget);
21
+ // Selectors for when contained withing a formatting container mark (eg. indent, centering, right-align)
22
+ var elementWithEmptyBlockExperimentFormatted = "+ ".concat(formattingElement, " > p > ").concat(emptyBlockExperimentWidget, ", + ").concat(formattingElement, " > h1 > ").concat(emptyBlockExperimentWidget, ", + ").concat(formattingElement, " > h2 > ").concat(emptyBlockExperimentWidget, ", + ").concat(formattingElement, " > h3 > ").concat(emptyBlockExperimentWidget, ", + ").concat(formattingElement, " > h4 > ").concat(emptyBlockExperimentWidget, ", + ").concat(formattingElement, " > h5 > ").concat(emptyBlockExperimentWidget, ", + ").concat(formattingElement, " > h6 > ").concat(emptyBlockExperimentWidget);
23
+ var dragHandleContainer = '.ProseMirror-widget[data-blocks-drag-handle-container="true"]';
24
+ var dragHandleSelector = 'button[data-testid="block-ctrl-drag-handle"]';
25
+
26
+ // Hides the drag handle when the block contains the empty block experiment
27
+ // Override is consistent with how the drag handle is hidden when the block contains a placeholder
28
+ var dragHandleWithInlineNodeStyle = (0, _react.css)((0, _defineProperty2.default)({}, ".ProseMirror-widget[data-blocks-drag-handle-container=\"true\"]:has(".concat(elementWithEmptyBlockExperiment, "), .ProseMirror-widget[data-blocks-drag-handle-container=\"true\"]:has(").concat(elementWithEmptyBlockExperimentFormatted, ")"), {
29
+ display: 'none !important'
30
+ }));
31
+
32
+ // Alternate styling for hiding the drag handle when the block contains the empty block experiment
33
+ // Override is consistent with a feature-gated bugfix that hides the drag handle when the block contains a placeholder
34
+ /**
35
+ * Please do not change change transform to display:none, or visibility:hidden
36
+ * Otherwise it might break composition input for Chrome
37
+ * https://product-fabric.atlassian.net/browse/ED-24136
38
+ */
39
+ var dragHandleWithInlineNodeStyleWithChromeFix = (0, _react.css)((0, _defineProperty2.default)({}, "".concat(dragHandleContainer, ":has(").concat(elementWithEmptyBlockExperiment, ") ").concat(dragHandleSelector, ", ").concat(dragHandleContainer, ":has(").concat(elementWithEmptyBlockExperimentFormatted, ") ").concat(dragHandleSelector), {
40
+ transform: 'scale(0)'
41
+ }));
42
+ var getDragHandleOverrides = function getDragHandleOverrides() {
43
+ return (0, _platformFeatureFlags.fg)('platform_editor_element_controls_chrome_input_fix') ? dragHandleWithInlineNodeStyleWithChromeFix : dragHandleWithInlineNodeStyle;
44
+ };
45
+
46
+ /**
47
+ * Hide the experiment button when it has been activated. (contains quick-insert decoration widget)
48
+ */
49
+ var emptyBlockExperimentGlobalStyles = exports.emptyBlockExperimentGlobalStyles = (0, _react.css)((0, _defineProperty2.default)({}, "".concat(emptyBlockExperimentWidget, ":has(+ ").concat(quickInsertWidget, ") button"), {
50
+ transform: 'scale(0)'
51
+ }), getDragHandleOverrides());
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.createEmptyBlockWidgetDecoration = exports.TypeAheadControl = void 0;
9
+ var _react = _interopRequireWildcard(require("react"));
10
+ var _reactDom = _interopRequireDefault(require("react-dom"));
11
+ var _reactIntlNext = require("react-intl-next");
12
+ var _keymaps = require("@atlaskit/editor-common/keymaps");
13
+ var _messages = require("@atlaskit/editor-common/messages");
14
+ var _whitespace = require("@atlaskit/editor-common/whitespace");
15
+ var _state = require("@atlaskit/editor-prosemirror/state");
16
+ var _view2 = require("@atlaskit/editor-prosemirror/view");
17
+ var _add = _interopRequireDefault(require("@atlaskit/icon/glyph/editor/add"));
18
+ var _primitives = require("@atlaskit/primitives");
19
+ var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
20
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
21
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
22
+ var wrapperStyles = (0, _primitives.xcss)({
23
+ position: 'absolute',
24
+ top: "calc('50%' - ".concat("var(--ds-space-150, 12px)", ")"),
25
+ left: "calc(".concat("var(--ds-space-negative-300, -24px)", " + ", "var(--ds-space-negative-100, -8px)", ")")
26
+ });
27
+ var buttonStyles = (0, _primitives.xcss)({
28
+ boxSizing: 'border-box',
29
+ display: 'flex',
30
+ flexDirection: 'column',
31
+ justifyContent: 'center',
32
+ alignItems: 'center',
33
+ height: "var(--ds-space-300, 24px)",
34
+ width: "var(--ds-space-300, 24px)",
35
+ border: 'none',
36
+ backgroundColor: 'color.background.neutral',
37
+ borderRadius: '50%',
38
+ color: 'color.text',
39
+ zIndex: 'card',
40
+ outline: 'none',
41
+ ':hover': {
42
+ backgroundColor: 'color.background.neutral.hovered'
43
+ },
44
+ ':active': {
45
+ backgroundColor: 'color.background.neutral.pressed'
46
+ },
47
+ ':focus': {
48
+ outline: "2px solid ".concat("var(--ds-border-focused, #388BFF)")
49
+ }
50
+ });
51
+ var TypeAheadControl = exports.TypeAheadControl = function TypeAheadControl(_ref) {
52
+ var api = _ref.api,
53
+ getPos = _ref.getPos,
54
+ formatMessage = _ref.intl.formatMessage;
55
+ return /*#__PURE__*/_react.default.createElement(_primitives.Box, {
56
+ xcss: [wrapperStyles]
57
+ }, /*#__PURE__*/_react.default.createElement(_tooltip.default, {
58
+ content: /*#__PURE__*/_react.default.createElement(_keymaps.ToolTipContent, {
59
+ description: formatMessage(_messages.blockControlsMessages.insert),
60
+ shortcutOverride: "/"
61
+ })
62
+ }, /*#__PURE__*/_react.default.createElement(_primitives.Pressable, {
63
+ type: "button",
64
+ "aria-label": formatMessage(_messages.blockControlsMessages.insert),
65
+ xcss: [buttonStyles],
66
+ onClick: function onClick() {
67
+ var _api$core, _api$quickInsert;
68
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref2) {
69
+ var tr = _ref2.tr;
70
+ var start = getPos();
71
+ if (!start) {
72
+ return null;
73
+ }
74
+ return tr.setSelection(_state.TextSelection.create(tr.doc, start));
75
+ });
76
+ api === null || api === void 0 || (_api$quickInsert = api.quickInsert) === null || _api$quickInsert === void 0 || _api$quickInsert.actions.openTypeAhead('blockControl');
77
+ }
78
+ }, /*#__PURE__*/_react.default.createElement(_add.default, {
79
+ label: "add",
80
+ size: "medium"
81
+ }))));
82
+ };
83
+ var TypeAheadControlWithIntl = (0, _reactIntlNext.injectIntl)(TypeAheadControl);
84
+ var toDOM = function toDOM(api, getPos, getIntl) {
85
+ var element = document.createElement('span');
86
+ element.contentEditable = 'false';
87
+ element.setAttribute('style', 'position: relative');
88
+ element.setAttribute('class', 'empty-block-experiment');
89
+ element.setAttribute('data-empty-block-experiment', 'true');
90
+ _reactDom.default.render( /*#__PURE__*/(0, _react.createElement)(_reactIntlNext.RawIntlProvider, {
91
+ value: getIntl()
92
+ }, /*#__PURE__*/(0, _react.createElement)(TypeAheadControlWithIntl, {
93
+ api: api,
94
+ getPos: getPos
95
+ })), element);
96
+
97
+ // // This is a hack to ensure that the cursor in Chrome does not take on the height of the widget button.
98
+ // // Cursor height cannot be controlled via CSS and is handled by the browser.
99
+ // // see Prosemirror forum: https://discuss.prosemirror.net/t/chrome-caret-cursor-larger-than-the-text-with-inlined-items/5946
100
+ var cursorHack = document.createTextNode(_whitespace.ZERO_WIDTH_SPACE);
101
+ element.appendChild(cursorHack);
102
+ return element;
103
+ };
104
+ var createEmptyBlockWidgetDecoration = exports.createEmptyBlockWidgetDecoration = function createEmptyBlockWidgetDecoration(selection, api, getIntl) {
105
+ if (selection instanceof _state.TextSelection && selection.$cursor) {
106
+ var _$cursor$parent, _$cursor$parent2;
107
+ var $cursor = selection.$cursor;
108
+ var depth = $cursor.depth;
109
+ var cursorAtRoot = depth === 1;
110
+ var nodeIsEmpty = ((_$cursor$parent = $cursor.parent) === null || _$cursor$parent === void 0 ? void 0 : _$cursor$parent.nodeSize) === 2;
111
+ var supportedNodeTypes = ['paragraph', 'heading'];
112
+ if (cursorAtRoot && nodeIsEmpty && supportedNodeTypes.includes((_$cursor$parent2 = $cursor.parent) === null || _$cursor$parent2 === void 0 ? void 0 : _$cursor$parent2.type.name)) {
113
+ return _view2.Decoration.widget(selection.$cursor.posAtIndex($cursor.depth - 1), function (_view, getPos) {
114
+ return toDOM(api, getPos, getIntl);
115
+ }, {
116
+ key: 'emptyBlockWidgetDecoration',
117
+ side: -1
118
+ });
119
+ }
120
+ }
121
+ };
@@ -10,6 +10,7 @@ var _react = require("@emotion/react");
10
10
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
11
11
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
12
12
  var _consts = require("./consts");
13
+ var _globalStyles = require("./empty-block-experiment/global-styles");
13
14
  /**
14
15
  * @jsxRuntime classic
15
16
  * @jsx jsx
@@ -85,7 +86,7 @@ var extendedHoverZoneNested = (0, _react.css)({
85
86
  'hr[data-drag-handler-anchor-name]': {
86
87
  overflow: 'visible'
87
88
  },
88
- //Hide psudeo element at top depth level. Leave for nested depths to prevent mouseover loop.
89
+ //Hide pseudo element at top depth level. Leave for nested depths to prevent mouseover loop.
89
90
  //eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors
90
91
  '[data-blocks-drag-handle-container="true"] + [data-drag-handler-anchor-depth="0"]::after': {
91
92
  display: 'none'
@@ -108,7 +109,13 @@ var withInlineNodeStyle = (0, _react.css)((0, _defineProperty2.default)({}, ".Pr
108
109
  var withInlineNodeStyleWithChromeFix = (0, _react.css)((0, _defineProperty2.default)({}, "".concat(dragHandleContainer, ":has(").concat(paragraphWithTrailingBreakAsOnlyChild, ") ").concat(dragHandleSelector, ",\n\t ").concat(dragHandleContainer, ":has(").concat(paragraphWithPlaceholder, ") ").concat(dragHandleSelector), {
109
110
  transform: 'scale(0)'
110
111
  }));
111
- var globalStyles = (0, _react.css)({
112
+ var globalStyles = (0, _experiments.editorExperiment)('nested-dnd', true) ? (0, _react.css)({
113
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors -- Ignored via go/DSP-18766
114
+ '.ProseMirror-widget:first-child + *:not([data-panel-type], .code-block, [data-node-type="nestedExpand"])': {
115
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-important-styles -- Ignored via go/DSP-18766
116
+ marginTop: '0 !important'
117
+ }
118
+ }) : (0, _react.css)({
112
119
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors -- Ignored via go/DSP-18766
113
120
  '.ProseMirror-widget:first-child + *': {
114
121
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-important-styles -- Ignored via go/DSP-18766
@@ -131,6 +138,8 @@ var getTextNodeStyle = function getTextNodeStyle() {
131
138
  };
132
139
  var GlobalStylesWrapper = exports.GlobalStylesWrapper = function GlobalStylesWrapper() {
133
140
  return (0, _react.jsx)(_react.Global, {
134
- styles: [globalStyles, (0, _experiments.editorExperiment)('nested-dnd', true) ? extendedHoverZoneNested : extendedHoverZone, getTextNodeStyle(), withDeleteLinesStyleFix, withMediaSingleStyleFix]
141
+ styles: [globalStyles, (0, _experiments.editorExperiment)('nested-dnd', true) ? extendedHoverZoneNested : extendedHoverZone, getTextNodeStyle(), withDeleteLinesStyleFix, withMediaSingleStyleFix, (0, _experiments.editorExperiment)('platform_editor_empty_line_prompt', true, {
142
+ exposure: false
143
+ }) ? _globalStyles.emptyBlockExperimentGlobalStyles : undefined]
135
144
  });
136
145
  };
@@ -2,6 +2,7 @@ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } f
2
2
  import { blockControlsMessages } from '@atlaskit/editor-common/messages';
3
3
  import { GapCursorSelection } from '@atlaskit/editor-common/selection';
4
4
  import { transformSliceNestedExpandToExpand } from '@atlaskit/editor-common/transforms';
5
+ import { isEmptyParagraph } from '@atlaskit/editor-common/utils';
5
6
  import { findTable, isInTable, isTableSelected } from '@atlaskit/editor-tables/utils';
6
7
  import { fg } from '@atlaskit/platform-feature-flags';
7
8
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
@@ -128,7 +129,6 @@ export const moveNode = api => (start, to, inputMethod = INPUT_METHOD.DRAG_AND_D
128
129
  const end = start + size;
129
130
  let mappedTo;
130
131
  if (editorExperiment('nested-dnd', true)) {
131
- var _transformSourceSlice;
132
132
  const nodeCopy = tr.doc.slice(start, end, false); // cut the content
133
133
  const $to = tr.doc.resolve(to);
134
134
  const $from = tr.doc.resolve(start);
@@ -137,13 +137,21 @@ export const moveNode = api => (start, to, inputMethod = INPUT_METHOD.DRAG_AND_D
137
137
  if (!canMoveNodeToIndex(destParent, $to.index(), $from.node().child($from.index()))) {
138
138
  return tr;
139
139
  }
140
- const convertedNode = (_transformSourceSlice = transformSourceSlice(nodeCopy, destType)) === null || _transformSourceSlice === void 0 ? void 0 : _transformSourceSlice.content;
140
+ const convertedNodeSlice = transformSourceSlice(nodeCopy, destType);
141
+ const convertedNode = convertedNodeSlice === null || convertedNodeSlice === void 0 ? void 0 : convertedNodeSlice.content;
141
142
  if (!convertedNode) {
142
143
  return tr;
143
144
  }
144
145
  tr.delete(start, end); // delete the content from the original position
145
146
  mappedTo = tr.mapping.map(to);
146
- tr.insert(mappedTo, convertedNode); // insert the content at the new position
147
+ const isDestNestedLoneEmptyParagraph = destParent.type.name !== 'doc' && destParent.childCount === 1 && isEmptyParagraph($to.nodeAfter);
148
+ if (convertedNodeSlice && isDestNestedLoneEmptyParagraph) {
149
+ // if only a single empty paragraph within container, replace it
150
+ tr.replace(mappedTo, mappedTo + 1, convertedNodeSlice);
151
+ } else {
152
+ // otherwise just insert the content at the new position
153
+ tr.insert(mappedTo, convertedNode);
154
+ }
147
155
  } else {
148
156
  const nodeCopy = tr.doc.content.cut(start, end); // cut the content
149
157
  tr.delete(start, end); // delete the content from the original position
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
2
3
  import { moveNode } from './commands/move-node';
4
+ import { createEmptyBlockExperimentPlugin } from './pm-plugins/empty-block-experiment';
3
5
  import { createPlugin, key } from './pm-plugins/main';
4
6
  import { DragHandleMenu } from './ui/drag-handle-menu';
5
7
  import { GlobalStylesWrapper } from './ui/global-styles';
@@ -14,7 +16,14 @@ export const blockControlsPlugin = ({
14
16
  plugin: ({
15
17
  getIntl
16
18
  }) => createPlugin(api, getIntl)
17
- }];
19
+ }, ...(editorExperiment('platform_editor_empty_line_prompt', true, {
20
+ exposure: true
21
+ }) ? [{
22
+ name: 'emptyBlockExperimentPlugin',
23
+ plugin: ({
24
+ getIntl
25
+ }) => createEmptyBlockExperimentPlugin(api, getIntl)
26
+ }] : [])];
18
27
  },
19
28
  commands: {
20
29
  moveNode: moveNode(api),
@@ -4,6 +4,7 @@ import { bind } from 'bind-event-listener';
4
4
  import ReactDOM from 'react-dom';
5
5
  import { RawIntlProvider } from 'react-intl-next';
6
6
  import uuid from 'uuid';
7
+ import { isEmptyParagraph } from '@atlaskit/editor-common/utils';
7
8
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
8
9
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
9
10
  import { DragHandle } from '../ui/drag-handle';
@@ -37,7 +38,7 @@ export const dropTargetDecorations = (newState, api, formatMessage, activeNode)
37
38
  const activePMNode = typeof activeNodePos === 'number' && newState.doc.resolve(activeNodePos).nodeAfter;
38
39
  newState.doc.nodesBetween(0, newState.doc.nodeSize - 2, (node, pos, parent, index) => {
39
40
  let depth = 0;
40
- // drop target dco at the end position
41
+ // drop target deco at the end position
41
42
  let endPosDeco = null;
42
43
  if (editorExperiment('nested-dnd', true)) {
43
44
  depth = newState.doc.resolve(pos).depth;
@@ -60,7 +61,7 @@ export const dropTargetDecorations = (newState, api, formatMessage, activeNode)
60
61
  id: pos,
61
62
  pos
62
63
  });
63
- if (parent.lastChild === node && PARENT_WITH_END_DROP_TARGET.includes(parent.type.name)) {
64
+ if (parent.lastChild === node && !isEmptyParagraph(node) && PARENT_WITH_END_DROP_TARGET.includes(parent.type.name)) {
64
65
  const endpos = pos + node.nodeSize;
65
66
  endPosDeco = {
66
67
  id: endpos,
@@ -0,0 +1,33 @@
1
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
3
+ import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
4
+ import { createEmptyBlockWidgetDecoration } from '../ui/empty-block-experiment/widget';
5
+ export const emptyBlockExperimentPluginKey = new PluginKey('emptyBlockExperiment');
6
+ const getDecorations = (tr, api, getIntl) => {
7
+ const widget = createEmptyBlockWidgetDecoration(tr.selection, api, getIntl);
8
+ if (widget) {
9
+ return DecorationSet.create(tr.doc, [widget]);
10
+ }
11
+ return DecorationSet.empty;
12
+ };
13
+ export const createEmptyBlockExperimentPlugin = (api, getIntl) => {
14
+ return new SafePlugin({
15
+ key: emptyBlockExperimentPluginKey,
16
+ state: {
17
+ init: (_, _editorState) => ({
18
+ decorations: DecorationSet.empty
19
+ }),
20
+ apply: (tr, _currentState) => {
21
+ return {
22
+ decorations: getDecorations(tr, api, getIntl)
23
+ };
24
+ }
25
+ },
26
+ props: {
27
+ decorations: state => {
28
+ var _emptyBlockExperiment;
29
+ return (_emptyBlockExperiment = emptyBlockExperimentPluginKey.getState(state)) === null || _emptyBlockExperiment === void 0 ? void 0 : _emptyBlockExperiment.decorations;
30
+ }
31
+ }
32
+ });
33
+ };