@atlaskit/editor-plugin-block-controls 17.2.5 → 17.3.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/blockControlsPlugin.js +22 -2
  3. package/dist/cjs/pm-plugins/main.js +9 -7
  4. package/dist/cjs/pm-plugins/utils/surface-node-positions.js +45 -19
  5. package/dist/cjs/ui/block-controls-left-surfaces.js +5 -4
  6. package/dist/cjs/ui/block-controls-right-surface.compiled.css +4 -0
  7. package/dist/cjs/ui/block-controls-right-surface.js +45 -0
  8. package/dist/cjs/ui/block-controls-right-surfaces.js +222 -0
  9. package/dist/{esm/ui/block-controls-left-surface-targets.js → cjs/ui/block-controls-surface-targets.js} +14 -2
  10. package/dist/cjs/ui/utils/get-surface-placement.js +20 -6
  11. package/dist/es2019/blockControlsPlugin.js +27 -3
  12. package/dist/es2019/pm-plugins/main.js +9 -9
  13. package/dist/es2019/pm-plugins/utils/surface-node-positions.js +34 -15
  14. package/dist/es2019/ui/block-controls-left-surfaces.js +5 -4
  15. package/dist/es2019/ui/block-controls-right-surface.compiled.css +4 -0
  16. package/dist/es2019/ui/block-controls-right-surface.js +39 -0
  17. package/dist/es2019/ui/block-controls-right-surfaces.js +187 -0
  18. package/dist/es2019/ui/{block-controls-left-surface-targets.js → block-controls-surface-targets.js} +8 -2
  19. package/dist/es2019/ui/utils/get-surface-placement.js +20 -6
  20. package/dist/esm/blockControlsPlugin.js +23 -3
  21. package/dist/esm/pm-plugins/main.js +10 -8
  22. package/dist/esm/pm-plugins/utils/surface-node-positions.js +45 -19
  23. package/dist/esm/ui/block-controls-left-surfaces.js +5 -4
  24. package/dist/esm/ui/block-controls-right-surface.compiled.css +4 -0
  25. package/dist/esm/ui/block-controls-right-surface.js +38 -0
  26. package/dist/esm/ui/block-controls-right-surfaces.js +213 -0
  27. package/dist/{cjs/ui/block-controls-left-surface-targets.js → esm/ui/block-controls-surface-targets.js} +8 -8
  28. package/dist/esm/ui/utils/get-surface-placement.js +20 -6
  29. package/dist/types/pm-plugins/main.d.ts +2 -2
  30. package/dist/types/pm-plugins/utils/surface-node-positions.d.ts +3 -3
  31. package/dist/types/ui/block-controls-left-surface.d.ts +2 -2
  32. package/dist/types/ui/block-controls-right-surface.d.ts +23 -0
  33. package/dist/types/ui/block-controls-right-surfaces.d.ts +20 -0
  34. package/dist/types/ui/block-controls-surface-targets.d.ts +13 -0
  35. package/dist/types/ui/utils/get-surface-placement.d.ts +12 -4
  36. package/package.json +22 -22
  37. package/dist/types/ui/block-controls-left-surface-targets.d.ts +0 -7
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { BLOCK_CONTROLS_LEFT_GROUP, BLOCK_CONTROLS_LEFT_SECTION, BLOCK_CONTROLS_LEFT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
2
+ import { BLOCK_CONTROLS_LEFT_GROUP, BLOCK_CONTROLS_LEFT_SECTION, BLOCK_CONTROLS_LEFT_SURFACE, BLOCK_CONTROLS_RIGHT_GROUP, BLOCK_CONTROLS_RIGHT_SECTION, BLOCK_CONTROLS_RIGHT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
3
3
  import { expandSelectionBounds } from '@atlaskit/editor-common/selection';
4
4
  import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check';
5
5
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
@@ -23,6 +23,7 @@ import { createSelectionPreservationPlugin } from './pm-plugins/selection-preser
23
23
  import { expandAndUpdateSelection } from './pm-plugins/utils/expand-and-update-selection';
24
24
  import { selectNode } from './pm-plugins/utils/getSelection';
25
25
  import { BlockControlsLeftSurfaces } from './ui/block-controls-left-surfaces';
26
+ import { BlockControlsRightSurfaces } from './ui/block-controls-right-surfaces';
26
27
  import { getBlockControlsSurfaceDragHandleComponents } from './ui/block-controls-surface-drag-handle-registration';
27
28
  import { GlobalStylesWrapper } from './ui/global-styles';
28
29
  import { SurfaceEditorViewContext } from './ui/surface-editor-view-context';
@@ -37,6 +38,7 @@ export const blockControlsPlugin = ({
37
38
  const registryBlockControlsEnabled = isExperimentEnabled('platform_editor_block_control_migration');
38
39
  const surfaceNodePositionsEnabled = registryBlockControlsEnabled && isExperimentEnabled('platform_editor_collapsible_headings');
39
40
  const legacyBlockControlsEnabled = !registryBlockControlsEnabled;
41
+ const rightSurfaceEnabled = registryBlockControlsEnabled && isExperimentEnabled('platform_editor_block_control_right_surface');
40
42
  if (registryBlockControlsEnabled) {
41
43
  var _api$uiControlRegistr;
42
44
  api === null || api === void 0 ? void 0 : (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 ? void 0 : _api$uiControlRegistr.actions.register([BLOCK_CONTROLS_LEFT_SURFACE, {
@@ -54,6 +56,25 @@ export const blockControlsPlugin = ({
54
56
  }, ...getBlockControlsSurfaceDragHandleComponents({
55
57
  api
56
58
  })]);
59
+ if (rightSurfaceEnabled) {
60
+ var _api$uiControlRegistr2;
61
+ // The right surface host stays generic: it only registers the toolbar/section/group
62
+ // hierarchy. Individual features contribute their own buttons into
63
+ // BLOCK_CONTROLS_RIGHT_GROUP from their own plugins, same as the left surface.
64
+ api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.register([BLOCK_CONTROLS_RIGHT_SURFACE, {
65
+ ...BLOCK_CONTROLS_RIGHT_SECTION,
66
+ parents: [{
67
+ ...BLOCK_CONTROLS_RIGHT_SURFACE,
68
+ rank: 100
69
+ }]
70
+ }, {
71
+ ...BLOCK_CONTROLS_RIGHT_GROUP,
72
+ parents: [{
73
+ ...BLOCK_CONTROLS_RIGHT_SECTION,
74
+ rank: 100
75
+ }]
76
+ }]);
77
+ }
57
78
  }
58
79
  return {
59
80
  name: 'blockControls',
@@ -123,7 +144,7 @@ export const blockControlsPlugin = ({
123
144
  plugin: ({
124
145
  getIntl,
125
146
  nodeViewPortalProviderAPI
126
- }) => createPlugin(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled, quickInsertButtonEnabled && legacyBlockControlsEnabled, legacyBlockControlsEnabled, surfaceNodePositionsEnabled)
147
+ }) => createPlugin(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled, quickInsertButtonEnabled && legacyBlockControlsEnabled, legacyBlockControlsEnabled, surfaceNodePositionsEnabled, rightSurfaceEnabled)
127
148
  }];
128
149
  if (editorExperiment('platform_editor_controls', 'variant1')) {
129
150
  pmPlugins.push({
@@ -446,7 +467,10 @@ export const blockControlsPlugin = ({
446
467
  }, /*#__PURE__*/React.createElement(BlockControlsLeftSurfaces, {
447
468
  api: api,
448
469
  editorView: editorView
449
- })) : null);
470
+ }), rightSurfaceEnabled ? /*#__PURE__*/React.createElement(BlockControlsRightSurfaces, {
471
+ api: api,
472
+ editorView: editorView
473
+ }) : null) : null);
450
474
  }
451
475
  };
452
476
  };
@@ -1,7 +1,7 @@
1
1
  import rafSchedule from 'raf-schd';
2
2
  import { getDocument } from '@atlaskit/browser-apis';
3
3
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
4
- import { BLOCK_CONTROLS_LEFT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
4
+ import { BLOCK_CONTROLS_LEFT_SURFACE, BLOCK_CONTROLS_RIGHT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
5
5
  import { getBrowserInfo } from '@atlaskit/editor-common/browser';
6
6
  import { getNodeIdProvider } from '@atlaskit/editor-common/node-anchor';
7
7
  import { isMeasuring, startMeasure, stopMeasure } from '@atlaskit/editor-common/performance-measures';
@@ -231,7 +231,7 @@ const getDecorationAtPos = (state, decorations, pos, to) => {
231
231
  const nodeDecAtActivePos = nodeDecsAtActivePos.pop();
232
232
  return nodeDecAtActivePos;
233
233
  };
234
- export const apply = (api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled = false, quickInsertButtonEnabled = true, anchorRectCache, resizeObserverWidth, pragmaticCleanup, resolvedSurface) => {
234
+ export const apply = (api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled = false, quickInsertButtonEnabled = true, anchorRectCache, resizeObserverWidth, pragmaticCleanup, resolvedSurfaces = []) => {
235
235
  var _api$limitedMode, _api$limitedMode$shar, _api$limitedMode$shar2, _meta$multiSelectDnD, _api$editorViewMode, _api$editorViewMode$s, _activeNode, _activeNode2, _meta$activeNode$hand, _activeNode3, _activeNode4, _meta$isDragging2, _meta$isDragging3, _meta$toggleMenu, _meta$toggleMenu2, _meta$toggleMenu3, _meta$toggleMenu4, _meta$toggleMenu5, _meta$toggleMenu6, _meta$toggleMenu7, _meta$toggleMenu8, _meta$editorHeight, _meta$editorWidthLeft, _meta$editorWidthRigh, _meta$isPMDragging, _meta$isShiftDown, _meta$lastDragCancell;
236
236
  let {
237
237
  activeNode,
@@ -717,14 +717,14 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
717
717
  var _latestActiveNode13, _latestActiveNode14;
718
718
  newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && flags.legacyDragHandleEnabled && findHandleDec(decorations, (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.pos, (_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.pos).length === 0 ? null : latestActiveNode;
719
719
  }
720
- if (flags.surfaceNodePositionsEnabled && resolvedSurface) {
720
+ if (flags.surfaceNodePositionsEnabled && resolvedSurfaces.length > 0) {
721
721
  surfaceNodePositions = updateSurfaceNodePositions({
722
722
  activeNode: newActiveNode,
723
723
  currentPositions: surfaceNodePositions,
724
724
  from,
725
725
  newState,
726
726
  previousActiveNode,
727
- resolvedSurface,
727
+ resolvedSurfaces,
728
728
  to,
729
729
  tr
730
730
  });
@@ -772,8 +772,8 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
772
772
  isSelectedViaDragHandle: isSelectedViaDragHandleNew
773
773
  };
774
774
  };
775
- export const createPlugin = (api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled = false, quickInsertButtonEnabled = true, legacyDragHandleEnabled = true, surfaceNodePositionsEnabled = false) => {
776
- var _api$uiControlRegistr, _api$uiControlRegistr2;
775
+ export const createPlugin = (api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled = false, quickInsertButtonEnabled = true, legacyDragHandleEnabled = true, surfaceNodePositionsEnabled = false, rightSurfaceEnabled = false) => {
776
+ var _api$uiControlRegistr, _api$uiControlRegistr2, _api$uiControlRegistr3, _api$uiControlRegistr4;
777
777
  const {
778
778
  formatMessage
779
779
  } = getIntl();
@@ -786,7 +786,7 @@ export const createPlugin = (api, getIntl, nodeViewPortalProviderAPI, nodeDecora
786
786
  surfaceNodePositionsEnabled,
787
787
  toolbarFlagsEnabled
788
788
  };
789
- const resolvedSurface = surfaceNodePositionsEnabled ? resolveSurface((_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(BLOCK_CONTROLS_LEFT_SURFACE)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [], BLOCK_CONTROLS_LEFT_SURFACE) : undefined;
789
+ const resolvedSurfaces = surfaceNodePositionsEnabled ? [resolveSurface((_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(BLOCK_CONTROLS_LEFT_SURFACE)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [], BLOCK_CONTROLS_LEFT_SURFACE), ...(rightSurfaceEnabled ? [resolveSurface((_api$uiControlRegistr3 = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr4 = api.uiControlRegistry) === null || _api$uiControlRegistr4 === void 0 ? void 0 : _api$uiControlRegistr4.actions.getComponents(BLOCK_CONTROLS_RIGHT_SURFACE)) !== null && _api$uiControlRegistr3 !== void 0 ? _api$uiControlRegistr3 : [], BLOCK_CONTROLS_RIGHT_SURFACE)] : [])] : [];
790
790
  let anchorRectCache;
791
791
  if (!isAnchorSupported()) {
792
792
  anchorRectCache = new AnchorRectCache();
@@ -799,10 +799,10 @@ export const createPlugin = (api, getIntl, nodeViewPortalProviderAPI, nodeDecora
799
799
  init(_config, editorState) {
800
800
  return {
801
801
  ...initialState,
802
- surfaceNodePositions: resolvedSurface ? getSurfaceNodePositions(editorState, resolvedSurface) : []
802
+ surfaceNodePositions: resolvedSurfaces.length > 0 ? getSurfaceNodePositions(editorState, resolvedSurfaces) : []
803
803
  };
804
804
  },
805
- apply: (tr, currentState, _, newState) => apply(api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled, quickInsertButtonEnabled, anchorRectCache, resizeObserverWidth, pragmaticCleanup, resolvedSurface)
805
+ apply: (tr, currentState, _, newState) => apply(api, formatMessage, tr, currentState, newState, flags, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled, quickInsertButtonEnabled, anchorRectCache, resizeObserverWidth, pragmaticCleanup, resolvedSurfaces)
806
806
  },
807
807
  props: {
808
808
  decorations: state => {
@@ -6,19 +6,35 @@ const createSurfaceContextAtPosition = (state, position, activeNode) => {
6
6
  const blockControlsContext = createBlockControlsSurfaceContextForPosition(state, position, activeNode);
7
7
  return blockControlsContext ? createSurfaceContext(BLOCK_CONTROL_UI_CONTEXT, blockControlsContext) : undefined;
8
8
  };
9
- export const getSurfaceNodePositions = (state, resolvedSurface, activeNode, from = 0, to = state.doc.content.size) => {
10
- const {
11
- root,
12
- childrenMap
13
- } = resolvedSurface;
14
- if (!root) {
9
+
10
+ /**
11
+ * A position qualifies as soon as ANY of the given surfaces would render something for it.
12
+ * The surface context depends only on state/position/activeNode, not on which surface is being
13
+ * checked, so it's built once here rather than once per surface inside the `.some()`.
14
+ */
15
+ const willAnySurfaceRenderAt = (surfaces, state, position, activeNode) => {
16
+ const surfaceContext = createSurfaceContextAtPosition(state, position, activeNode);
17
+ return surfaces.some(({
18
+ childrenMap,
19
+ root
20
+ }) => willComponentRender(root, childrenMap, surfaceContext));
21
+ };
22
+ export const getSurfaceNodePositions = (state, resolvedSurfaces, activeNode, from = 0, to = state.doc.content.size) => {
23
+ const surfaces = resolvedSurfaces.flatMap(({
24
+ childrenMap,
25
+ root
26
+ }) => root ? [{
27
+ childrenMap,
28
+ root
29
+ }] : []);
30
+ if (surfaces.length === 0) {
15
31
  return [];
16
32
  }
17
33
  const positions = [];
18
34
  const start = Math.max(0, from);
19
35
  const end = Math.min(state.doc.content.size, Math.max(start, to));
20
36
  state.doc.nodesBetween(start, end, (node, position) => {
21
- if (node.isBlock && willComponentRender(root, childrenMap, createSurfaceContextAtPosition(state, position, activeNode))) {
37
+ if (node.isBlock && willAnySurfaceRenderAt(surfaces, state, position, activeNode)) {
22
38
  positions.push(position);
23
39
  }
24
40
  });
@@ -31,7 +47,7 @@ export const updateSurfaceNodePositions = ({
31
47
  from,
32
48
  newState,
33
49
  previousActiveNode,
34
- resolvedSurface,
50
+ resolvedSurfaces,
35
51
  to,
36
52
  tr
37
53
  }) => {
@@ -41,16 +57,19 @@ export const updateSurfaceNodePositions = ({
41
57
  }
42
58
  const nextActiveNode = activeNode !== null && activeNode !== void 0 ? activeNode : undefined;
43
59
  if (activeNodeChanged) {
44
- return getSurfaceNodePositions(newState, resolvedSurface, nextActiveNode);
60
+ return getSurfaceNodePositions(newState, resolvedSurfaces, nextActiveNode);
45
61
  }
46
- const {
47
- root,
48
- childrenMap
49
- } = resolvedSurface;
62
+ const surfaces = resolvedSurfaces.flatMap(({
63
+ childrenMap,
64
+ root
65
+ }) => root ? [{
66
+ childrenMap,
67
+ root
68
+ }] : []);
50
69
  const mappedPositions = currentPositions.map(position => tr.mapping.mapResult(position, -1)).filter(result => !result.deleted).map(result => result.pos).filter(position => {
51
70
  const node = newState.doc.nodeAt(position);
52
- return Boolean((node === null || node === void 0 ? void 0 : node.isBlock) && root && willComponentRender(root, childrenMap, createSurfaceContextAtPosition(newState, position, nextActiveNode)));
71
+ return Boolean((node === null || node === void 0 ? void 0 : node.isBlock) && willAnySurfaceRenderAt(surfaces, newState, position, nextActiveNode));
53
72
  });
54
- const changedPositions = getSurfaceNodePositions(newState, resolvedSurface, nextActiveNode, from - 1, to + 1);
73
+ const changedPositions = getSurfaceNodePositions(newState, resolvedSurfaces, nextActiveNode, from - 1, to + 1);
55
74
  return Array.from(new Set([...mappedPositions, ...changedPositions])).sort((first, second) => first - second);
56
75
  };
@@ -8,9 +8,9 @@ import { BLOCK_CONTROLS_LEFT_SURFACE } from '@atlaskit/editor-common/block-contr
8
8
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
9
9
  import { createSurfaceContext } from '@atlaskit/editor-ui-control-model/create-surface-context';
10
10
  import { getNodeTypeWithLevel } from '../pm-plugins/decorations-common';
11
- import { createBlockControlsSurfaceContext, createBlockControlsSurfaceContextForPosition } from './block-controls-surface-context';
12
11
  import { BlockControlsLeftSurface } from './block-controls-left-surface';
13
- import { getBlockControlsLeftSurfaceTargets } from './block-controls-left-surface-targets';
12
+ import { createBlockControlsSurfaceContext, createBlockControlsSurfaceContextForPosition } from './block-controls-surface-context';
13
+ import { getBlockControlsSurfaceTargets } from './block-controls-surface-targets';
14
14
  import { getNodeContentElement } from './utils/get-node-content-element';
15
15
  import { getAbsoluteSurfacePlacement, getSurfacePlacement } from './utils/get-surface-placement';
16
16
  const EMPTY_SURFACE_POSITIONS = [];
@@ -31,7 +31,7 @@ export const BlockControlsLeftSurfaces = ({
31
31
  };
32
32
  });
33
33
  const components = (_api$uiControlRegistr = (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(BLOCK_CONTROLS_LEFT_SURFACE)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : EMPTY_SURFACE_COMPONENTS;
34
- const targets = useMemo(() => getBlockControlsLeftSurfaceTargets(activeNode, surfaceNodePositions), [activeNode, surfaceNodePositions]);
34
+ const targets = useMemo(() => getBlockControlsSurfaceTargets(activeNode, surfaceNodePositions), [activeNode, surfaceNodePositions]);
35
35
  const surfaces = useMemo(() => targets.flatMap(target => {
36
36
  const context = target.source === 'stored' ? createBlockControlsSurfaceContextForPosition(editorView.state, target.position, activeNode) : createBlockControlsSurfaceContext(editorView, activeNode, target.source);
37
37
  if (!context) {
@@ -93,7 +93,8 @@ export const BlockControlsLeftSurfaces = ({
93
93
  nodeRect: targetElement.getBoundingClientRect(),
94
94
  nodeType: blockControlsContext.targetNode.type.name,
95
95
  nodeTypeWithLevel: getNodeTypeWithLevel(blockControlsContext.targetNode.node),
96
- parentNodeType: blockControlsContext.targetNode.parentType === 'doc' ? undefined : blockControlsContext.targetNode.parentType
96
+ parentNodeType: blockControlsContext.targetNode.parentType === 'doc' ? undefined : blockControlsContext.targetNode.parentType,
97
+ side: 'left'
97
98
  });
98
99
  nextPlacements.set(position, getAbsoluteSurfacePlacement({
99
100
  offsetParentRect,
@@ -0,0 +1,4 @@
1
+ ._zulpv77o{gap:var(--ds-space-025,2px)}._1e0c1txw{display:flex}
2
+ ._1pbyb4wl{z-index:100}
3
+ ._4cvr1h6o{align-items:center}
4
+ ._kqswstnw{position:absolute}
@@ -0,0 +1,39 @@
1
+ /* block-controls-right-surface.tsx generated by @compiled/babel-plugin v2.0.0 */
2
+ import "./block-controls-right-surface.compiled.css";
3
+ import { ax, ix } from "@compiled/react/runtime";
4
+ import React from 'react';
5
+ import { BLOCK_CONTROLS_RIGHT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
6
+ import { SurfaceRenderer, willSurfaceRender } from '@atlaskit/editor-ui-control-model/surface-renderer';
7
+ import { VisibilityContainer } from './visibility-container';
8
+ const rightSurfaceStyles = null;
9
+ /** Renders one right block-controls surface at a document position. */
10
+ export const BlockControlsRightSurface = ({
11
+ api,
12
+ components,
13
+ forceVisibleOnMouseOut,
14
+ placement,
15
+ source,
16
+ surfaceContext
17
+ }) => {
18
+ if (!placement || !willSurfaceRender(components, BLOCK_CONTROLS_RIGHT_SURFACE, surfaceContext)) {
19
+ return null;
20
+ }
21
+ const surface = /*#__PURE__*/React.createElement(SurfaceRenderer, {
22
+ components: components,
23
+ surface: BLOCK_CONTROLS_RIGHT_SURFACE,
24
+ surfaceContext: surfaceContext
25
+ });
26
+ return /*#__PURE__*/React.createElement("div", {
27
+ "data-editor-block-controls-surface": true,
28
+ "data-testid": source === 'stored' ? 'block-controls-right-surface-stored' : source === 'active' ? 'block-controls-right-surface-active' : 'block-controls-right-surface'
29
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Placement is resolved from the target node's runtime DOM geometry.
30
+ ,
31
+ style: placement,
32
+ className: ax(["_zulpv77o _4cvr1h6o _1e0c1txw _kqswstnw _1pbyb4wl"])
33
+ }, /*#__PURE__*/React.createElement(VisibilityContainer, {
34
+ api: api,
35
+ controlSide: "right",
36
+ forceVisibleOnMouseOut: forceVisibleOnMouseOut,
37
+ shouldUseDisplayContents: true
38
+ }, surface));
39
+ };
@@ -0,0 +1,187 @@
1
+ /* block-controls-right-surfaces.tsx generated by @compiled/babel-plugin v2.0.0 */
2
+ import { ax, ix } from "@compiled/react/runtime";
3
+ import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
4
+ import { bind } from 'bind-event-listener';
5
+ import { getDocument } from '@atlaskit/browser-apis';
6
+ import { BLOCK_CONTROL_UI_CONTEXT } from '@atlaskit/editor-common/block-controls/block-control-ui-context';
7
+ import { BLOCK_CONTROLS_RIGHT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
8
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
9
+ import { createSurfaceContext } from '@atlaskit/editor-ui-control-model/types';
10
+ import { getNodeTypeWithLevel } from '../pm-plugins/decorations-common';
11
+ import { BlockControlsRightSurface } from './block-controls-right-surface';
12
+ import { createBlockControlsSurfaceContext, createBlockControlsSurfaceContextForPosition } from './block-controls-surface-context';
13
+ import { getBlockControlsSurfaceTargets } from './block-controls-surface-targets';
14
+ import { getNodeContentElement } from './utils/get-node-content-element';
15
+ import { getAbsoluteSurfacePlacement, getSurfacePlacement } from './utils/get-surface-placement';
16
+ const EMPTY_SURFACE_POSITIONS = [];
17
+ const EMPTY_SURFACE_COMPONENTS = [];
18
+ /**
19
+ * Registry-backed right surface tree. Mirrors the left surface's single-tree, measured-placement
20
+ * approach, but keeps its own components lookup, targets, placements and measurement lifecycle so
21
+ * the right surface never depends on the left surface's render pass.
22
+ */
23
+ export const BlockControlsRightSurfaces = ({
24
+ api,
25
+ editorView
26
+ }) => {
27
+ var _api$uiControlRegistr, _api$uiControlRegistr2;
28
+ const {
29
+ activeNode,
30
+ surfaceNodePositions
31
+ } = useSharedPluginStateWithSelector(api, ['blockControls'], states => {
32
+ var _states$blockControls, _states$blockControls2, _states$blockControls3;
33
+ return {
34
+ activeNode: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.activeNode,
35
+ surfaceNodePositions: (_states$blockControls2 = (_states$blockControls3 = states.blockControlsState) === null || _states$blockControls3 === void 0 ? void 0 : _states$blockControls3.surfaceNodePositions) !== null && _states$blockControls2 !== void 0 ? _states$blockControls2 : EMPTY_SURFACE_POSITIONS
36
+ };
37
+ });
38
+ const components = (_api$uiControlRegistr = (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(BLOCK_CONTROLS_RIGHT_SURFACE)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : EMPTY_SURFACE_COMPONENTS;
39
+ const targets = useMemo(() => getBlockControlsSurfaceTargets(activeNode, surfaceNodePositions), [activeNode, surfaceNodePositions]);
40
+ const surfaces = useMemo(() => targets.flatMap(target => {
41
+ const context = target.source === 'stored' ? createBlockControlsSurfaceContextForPosition(editorView.state, target.position, activeNode) : createBlockControlsSurfaceContext(editorView, activeNode, target.source);
42
+ if (!context) {
43
+ return [];
44
+ }
45
+ const surfaceContext = createSurfaceContext(BLOCK_CONTROL_UI_CONTEXT, context);
46
+ return [{
47
+ ...target,
48
+ blockControlsContext: context,
49
+ surfaceContext
50
+ }];
51
+ }), [activeNode, editorView, targets]);
52
+ const [placements, setPlacements] = useState(new Map());
53
+ const animationFrameRef = useRef(undefined);
54
+ useLayoutEffect(() => {
55
+ var _getDocument;
56
+ if (surfaces.length === 0) {
57
+ setPlacements(new Map());
58
+ return;
59
+ }
60
+ const measurementTargets = surfaces.flatMap(({
61
+ blockControlsContext,
62
+ position
63
+ }) => {
64
+ const targetNodeElement = editorView.nodeDOM(blockControlsContext.targetNode.pos);
65
+ const targetElementRoot = targetNodeElement instanceof HTMLElement ? targetNodeElement : targetNodeElement === null || targetNodeElement === void 0 ? void 0 : targetNodeElement.parentElement;
66
+ if (!targetElementRoot) {
67
+ return [];
68
+ }
69
+ return [{
70
+ blockControlsContext,
71
+ position,
72
+ targetElement: getNodeContentElement(targetElementRoot, blockControlsContext.targetNode.type.name),
73
+ targetElementRoot
74
+ }];
75
+ });
76
+ const offsetParent = editorView.dom.offsetParent;
77
+ const isDocumentOffsetParent = !offsetParent || offsetParent === ((_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.body);
78
+ const scrollingOffsetParent = offsetParent instanceof HTMLElement && !isDocumentOffsetParent ? offsetParent : undefined;
79
+ const updatePositions = () => {
80
+ var _scrollingOffsetParen, _scrollingOffsetParen2;
81
+ animationFrameRef.current = undefined;
82
+ const offsetParentRect = offsetParent === null || offsetParent === void 0 ? void 0 : offsetParent.getBoundingClientRect();
83
+ const offsetParentScrollTop = (_scrollingOffsetParen = scrollingOffsetParent === null || scrollingOffsetParent === void 0 ? void 0 : scrollingOffsetParent.scrollTop) !== null && _scrollingOffsetParen !== void 0 ? _scrollingOffsetParen : window.scrollY;
84
+ const offsetParentScrollLeft = (_scrollingOffsetParen2 = scrollingOffsetParent === null || scrollingOffsetParent === void 0 ? void 0 : scrollingOffsetParent.scrollLeft) !== null && _scrollingOffsetParen2 !== void 0 ? _scrollingOffsetParen2 : window.scrollX;
85
+ const nextPlacements = new Map();
86
+ for (const {
87
+ blockControlsContext,
88
+ position,
89
+ targetElement,
90
+ targetElementRoot
91
+ } of measurementTargets) {
92
+ var _targetElementRoot$ge;
93
+ if (!targetElementRoot.isConnected || !targetElement.isConnected) {
94
+ continue;
95
+ }
96
+ const placement = getSurfacePlacement({
97
+ layout: (_targetElementRoot$ge = targetElementRoot.getAttribute('layout')) !== null && _targetElementRoot$ge !== void 0 ? _targetElementRoot$ge : '',
98
+ nodeRect: targetElement.getBoundingClientRect(),
99
+ nodeType: blockControlsContext.targetNode.type.name,
100
+ nodeTypeWithLevel: getNodeTypeWithLevel(blockControlsContext.targetNode.node),
101
+ parentNodeType: blockControlsContext.targetNode.parentType === 'doc' ? undefined : blockControlsContext.targetNode.parentType,
102
+ side: 'right'
103
+ });
104
+ nextPlacements.set(position, getAbsoluteSurfacePlacement({
105
+ offsetParentRect,
106
+ offsetParentScrollLeft,
107
+ offsetParentScrollTop,
108
+ placement
109
+ }));
110
+ }
111
+ setPlacements(nextPlacements);
112
+ };
113
+ const schedulePositionUpdate = () => {
114
+ if (animationFrameRef.current === undefined) {
115
+ animationFrameRef.current = requestAnimationFrame(updatePositions);
116
+ }
117
+ };
118
+ updatePositions();
119
+ const resizeObserver = new ResizeObserver(schedulePositionUpdate);
120
+ const observedElements = new Set([editorView.dom]);
121
+ for (const {
122
+ targetElement
123
+ } of measurementTargets) {
124
+ observedElements.add(targetElement);
125
+ }
126
+ if (offsetParent instanceof HTMLElement) {
127
+ observedElements.add(offsetParent);
128
+ }
129
+ observedElements.forEach(element => resizeObserver.observe(element));
130
+ const unbindWindowResize = bind(window, {
131
+ type: 'resize',
132
+ listener: schedulePositionUpdate
133
+ });
134
+ const unbindWindowScroll = bind(window, {
135
+ type: 'scroll',
136
+ listener: schedulePositionUpdate,
137
+ options: {
138
+ capture: true,
139
+ passive: true
140
+ }
141
+ });
142
+ const visualViewport = window.visualViewport;
143
+ const unbindVisualViewportResize = visualViewport ? bind(visualViewport, {
144
+ type: 'resize',
145
+ listener: schedulePositionUpdate
146
+ }) : undefined;
147
+ const unbindVisualViewportScroll = visualViewport ? bind(visualViewport, {
148
+ type: 'scroll',
149
+ listener: schedulePositionUpdate
150
+ }) : undefined;
151
+ const unbindTransitionEnd = bind(editorView.dom, {
152
+ type: 'transitionend',
153
+ listener: schedulePositionUpdate,
154
+ options: {
155
+ capture: true
156
+ }
157
+ });
158
+ return () => {
159
+ if (animationFrameRef.current !== undefined) {
160
+ cancelAnimationFrame(animationFrameRef.current);
161
+ animationFrameRef.current = undefined;
162
+ }
163
+ resizeObserver.disconnect();
164
+ unbindWindowResize();
165
+ unbindWindowScroll();
166
+ unbindVisualViewportResize === null || unbindVisualViewportResize === void 0 ? void 0 : unbindVisualViewportResize();
167
+ unbindVisualViewportScroll === null || unbindVisualViewportScroll === void 0 ? void 0 : unbindVisualViewportScroll();
168
+ unbindTransitionEnd();
169
+ };
170
+ }, [editorView, surfaces]);
171
+ return /*#__PURE__*/React.createElement(React.Fragment, null, surfaces.map(({
172
+ position,
173
+ source,
174
+ surfaceContext
175
+ }) => {
176
+ var _activeNode$handleOpt;
177
+ return /*#__PURE__*/React.createElement(BlockControlsRightSurface, {
178
+ api: api,
179
+ components: components,
180
+ forceVisibleOnMouseOut: Boolean(activeNode === null || activeNode === void 0 ? void 0 : (_activeNode$handleOpt = activeNode.handleOptions) === null || _activeNode$handleOpt === void 0 ? void 0 : _activeNode$handleOpt.isFocused),
181
+ key: position,
182
+ placement: placements.get(position),
183
+ source: source,
184
+ surfaceContext: surfaceContext
185
+ });
186
+ }));
187
+ };
@@ -1,5 +1,11 @@
1
- /** Combines hover-driven and stored surface positions into one deduplicated list. */
2
- export const getBlockControlsLeftSurfaceTargets = (activeNode, storedPositions) => {
1
+ /**
2
+ * Combines hover-driven and stored surface positions into one deduplicated list.
3
+ * Shared by both the left and right surfaces: this is pure position bookkeeping with no
4
+ * surface-specific behavior, so there's nothing to keep independent between them. What *is*
5
+ * kept independent is each surface's own React tree (component lookup, measurement effects,
6
+ * observers) — see block-controls-left-surfaces.tsx / block-controls-right-surfaces.tsx.
7
+ */
8
+ export const getBlockControlsSurfaceTargets = (activeNode, storedPositions) => {
3
9
  const targetsByPosition = new Map();
4
10
  for (const position of storedPositions) {
5
11
  targetsByPosition.set(position, {
@@ -14,26 +14,40 @@ export const getAbsoluteSurfacePlacement = ({
14
14
  };
15
15
 
16
16
  /**
17
- * Positions the Task 1 left surface beside the legacy drag-handle slot.
18
- * The surface's own width is handled by the transform, so later controls do not require measuring it.
17
+ * Positions the surface beside the legacy drag-handle slot, on either the left or right edge
18
+ * of the target node. Node-type-specific gap/top-adjustment maths (layouts, tables, media, cards,
19
+ * extensions, etc.) live in one place so left/right edge cases can't drift out of sync.
20
+ *
21
+ * The left side grows away from the node without knowing its own width, so it needs
22
+ * `translateX(-100%)` to anchor its right edge at `nodeRect.left` and extend backward. The right
23
+ * side grows in the normal box-flow direction (away from the node, into the margin), so the gap
24
+ * can be folded straight into `left` with no transform needed.
19
25
  */
20
26
  export const getSurfacePlacement = ({
21
27
  layout,
22
28
  nodeRect,
23
29
  nodeType,
24
30
  nodeTypeWithLevel,
25
- parentNodeType
31
+ parentNodeType,
32
+ side
26
33
  }) => {
27
34
  if (nodeType === 'layoutColumn') {
35
+ // nodeRect.left + width/2 === nodeRect.right - width/2 — side-independent.
28
36
  return {
29
37
  left: nodeRect.left + nodeRect.width / 2,
30
38
  top: nodeRect.top,
31
39
  transform: 'translate(-50%, -50%)'
32
40
  };
33
41
  }
34
- return {
42
+ const gap = dragHandleGap(nodeType, parentNodeType);
43
+ const top = nodeRect.top + topPositionAdjustment(nodeTypeWithLevel, layout);
44
+ return side === 'right' ? {
45
+ left: nodeRect.right + gap,
46
+ top,
47
+ transform: ''
48
+ } : {
35
49
  left: nodeRect.left,
36
- top: nodeRect.top + topPositionAdjustment(nodeTypeWithLevel, layout),
37
- transform: `translateX(calc(-100% - ${dragHandleGap(nodeType, parentNodeType)}px))`
50
+ top,
51
+ transform: `translateX(calc(-100% - ${gap}px))`
38
52
  };
39
53
  };
@@ -3,7 +3,7 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
3
  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
4
  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
5
  import React from 'react';
6
- import { BLOCK_CONTROLS_LEFT_GROUP, BLOCK_CONTROLS_LEFT_SECTION, BLOCK_CONTROLS_LEFT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
6
+ import { BLOCK_CONTROLS_LEFT_GROUP, BLOCK_CONTROLS_LEFT_SECTION, BLOCK_CONTROLS_LEFT_SURFACE, BLOCK_CONTROLS_RIGHT_GROUP, BLOCK_CONTROLS_RIGHT_SECTION, BLOCK_CONTROLS_RIGHT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
7
7
  import { expandSelectionBounds } from '@atlaskit/editor-common/selection';
8
8
  import { areToolbarFlagsEnabled } from '@atlaskit/editor-common/toolbar-flag-check';
9
9
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
@@ -27,6 +27,7 @@ import { createSelectionPreservationPlugin } from './pm-plugins/selection-preser
27
27
  import { expandAndUpdateSelection as _expandAndUpdateSelection } from './pm-plugins/utils/expand-and-update-selection';
28
28
  import { selectNode } from './pm-plugins/utils/getSelection';
29
29
  import { BlockControlsLeftSurfaces } from './ui/block-controls-left-surfaces';
30
+ import { BlockControlsRightSurfaces } from './ui/block-controls-right-surfaces';
30
31
  import { getBlockControlsSurfaceDragHandleComponents } from './ui/block-controls-surface-drag-handle-registration';
31
32
  import { GlobalStylesWrapper } from './ui/global-styles';
32
33
  import { SurfaceEditorViewContext } from './ui/surface-editor-view-context';
@@ -40,6 +41,7 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
40
41
  var registryBlockControlsEnabled = isExperimentEnabled('platform_editor_block_control_migration');
41
42
  var surfaceNodePositionsEnabled = registryBlockControlsEnabled && isExperimentEnabled('platform_editor_collapsible_headings');
42
43
  var legacyBlockControlsEnabled = !registryBlockControlsEnabled;
44
+ var rightSurfaceEnabled = registryBlockControlsEnabled && isExperimentEnabled('platform_editor_block_control_right_surface');
43
45
  if (registryBlockControlsEnabled) {
44
46
  var _api$uiControlRegistr;
45
47
  api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register([BLOCK_CONTROLS_LEFT_SURFACE, _objectSpread(_objectSpread({}, BLOCK_CONTROLS_LEFT_SECTION), {}, {
@@ -53,6 +55,21 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
53
55
  })].concat(_toConsumableArray(getBlockControlsSurfaceDragHandleComponents({
54
56
  api: api
55
57
  }))));
58
+ if (rightSurfaceEnabled) {
59
+ var _api$uiControlRegistr2;
60
+ // The right surface host stays generic: it only registers the toolbar/section/group
61
+ // hierarchy. Individual features contribute their own buttons into
62
+ // BLOCK_CONTROLS_RIGHT_GROUP from their own plugins, same as the left surface.
63
+ api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 || _api$uiControlRegistr2.actions.register([BLOCK_CONTROLS_RIGHT_SURFACE, _objectSpread(_objectSpread({}, BLOCK_CONTROLS_RIGHT_SECTION), {}, {
64
+ parents: [_objectSpread(_objectSpread({}, BLOCK_CONTROLS_RIGHT_SURFACE), {}, {
65
+ rank: 100
66
+ })]
67
+ }), _objectSpread(_objectSpread({}, BLOCK_CONTROLS_RIGHT_GROUP), {}, {
68
+ parents: [_objectSpread(_objectSpread({}, BLOCK_CONTROLS_RIGHT_SECTION), {}, {
69
+ rank: 100
70
+ })]
71
+ })]);
72
+ }
56
73
  }
57
74
  return {
58
75
  name: 'blockControls',
@@ -122,7 +139,7 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
122
139
  plugin: function plugin(_ref2) {
123
140
  var getIntl = _ref2.getIntl,
124
141
  nodeViewPortalProviderAPI = _ref2.nodeViewPortalProviderAPI;
125
- return createPlugin(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled, quickInsertButtonEnabled && legacyBlockControlsEnabled, legacyBlockControlsEnabled, surfaceNodePositionsEnabled);
142
+ return createPlugin(api, getIntl, nodeViewPortalProviderAPI, nodeDecorationRegistry, rightSideControlsEnabled, quickInsertButtonEnabled && legacyBlockControlsEnabled, legacyBlockControlsEnabled, surfaceNodePositionsEnabled, rightSurfaceEnabled);
126
143
  }
127
144
  }];
128
145
  if (editorExperiment('platform_editor_controls', 'variant1')) {
@@ -450,7 +467,10 @@ export var blockControlsPlugin = function blockControlsPlugin(_ref) {
450
467
  }, /*#__PURE__*/React.createElement(BlockControlsLeftSurfaces, {
451
468
  api: api,
452
469
  editorView: editorView
453
- })) : null);
470
+ }), rightSurfaceEnabled ? /*#__PURE__*/React.createElement(BlockControlsRightSurfaces, {
471
+ api: api,
472
+ editorView: editorView
473
+ }) : null) : null);
454
474
  }
455
475
  };
456
476
  };