@atlaskit/editor-plugin-block-controls 20.1.2 → 20.1.4

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 (32) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/pm-plugins/selection-preservation/pm-plugin.js +17 -34
  3. package/dist/cjs/ui/block-controls-left-surface.compiled.css +7 -1
  4. package/dist/cjs/ui/block-controls-left-surface.js +41 -27
  5. package/dist/cjs/ui/block-controls-left-surfaces.js +32 -12
  6. package/dist/cjs/ui/block-controls-right-surface.js +32 -24
  7. package/dist/cjs/ui/block-controls-right-surfaces.js +6 -4
  8. package/dist/cjs/ui/block-controls-surface-components.js +142 -0
  9. package/dist/cjs/ui/utils/get-surface-placement.js +63 -16
  10. package/dist/cjs/ui/visibility-container.js +42 -7
  11. package/dist/es2019/pm-plugins/selection-preservation/pm-plugin.js +17 -34
  12. package/dist/es2019/ui/block-controls-left-surface.compiled.css +7 -1
  13. package/dist/es2019/ui/block-controls-left-surface.js +44 -29
  14. package/dist/es2019/ui/block-controls-left-surfaces.js +41 -16
  15. package/dist/es2019/ui/block-controls-right-surface.js +35 -26
  16. package/dist/es2019/ui/block-controls-right-surfaces.js +6 -4
  17. package/dist/es2019/ui/block-controls-surface-components.js +129 -0
  18. package/dist/es2019/ui/utils/get-surface-placement.js +59 -7
  19. package/dist/es2019/ui/visibility-container.js +44 -6
  20. package/dist/esm/pm-plugins/selection-preservation/pm-plugin.js +17 -34
  21. package/dist/esm/ui/block-controls-left-surface.compiled.css +7 -1
  22. package/dist/esm/ui/block-controls-left-surface.js +43 -29
  23. package/dist/esm/ui/block-controls-left-surfaces.js +33 -13
  24. package/dist/esm/ui/block-controls-right-surface.js +34 -26
  25. package/dist/esm/ui/block-controls-right-surfaces.js +6 -4
  26. package/dist/esm/ui/block-controls-surface-components.js +133 -0
  27. package/dist/esm/ui/utils/get-surface-placement.js +62 -15
  28. package/dist/esm/ui/visibility-container.js +41 -6
  29. package/dist/types/ui/block-controls-surface-components.d.ts +13 -0
  30. package/dist/types/ui/utils/get-surface-placement.d.ts +13 -0
  31. package/dist/types/ui/visibility-container.d.ts +14 -0
  32. package/package.json +3 -3
@@ -8,14 +8,13 @@ import { BLOCK_CONTROLS_LEFT_SURFACE } from '@atlaskit/editor-common/block-contr
8
8
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
9
9
  import { isCSSAnchorSupported } from '@atlaskit/editor-common/styles';
10
10
  import { createSurfaceContext } from '@atlaskit/editor-ui-control-model/create-surface-context';
11
- import { willSurfaceRender } from '@atlaskit/editor-ui-control-model/surface-renderer';
12
11
  import { getNodeTypeWithLevel } from '../pm-plugins/decorations-common';
13
12
  import { BlockControlsLeftSurface } from './block-controls-left-surface';
13
+ import { getBlockControlsLeftSurfaceComponents } from './block-controls-surface-components';
14
14
  import { createBlockControlsSurfaceContextForPosition } from './block-controls-surface-context';
15
15
  import { getBlockControlsSurfaceTargets } from './block-controls-surface-targets';
16
16
  import { getNodeContentElement } from './utils/get-node-content-element';
17
- import { getAbsoluteSurfacePlacement, getAnchoredSurfacePlacement, getSurfacePlacement, toMeasuredSurfaceWrapperPlacement } from './utils/get-surface-placement';
18
- import { hasSurfaceControls } from './utils/has-surface-controls';
17
+ import { getAbsoluteSurfacePlacement, getAnchoredLeftGutterSurfacePlacement, getAnchoredSurfacePlacement, getLeftGutterSurfacePlacement, getSurfacePlacement, toMeasuredSurfaceWrapperPlacement } from './utils/get-surface-placement';
19
18
  const EMPTY_SURFACE_POSITIONS = [];
20
19
  const EMPTY_SURFACE_ANCHORS = new Map();
21
20
  const EMPTY_SURFACE_COMPONENTS = [];
@@ -53,15 +52,21 @@ export const BlockControlsLeftSurfaces = ({
53
52
  return [];
54
53
  }
55
54
  const surfaceContext = createSurfaceContext(BLOCK_CONTROL_UI_CONTEXT, context);
56
- if (!hasSurfaceControls(components) || !willSurfaceRender(components, BLOCK_CONTROLS_LEFT_SURFACE, surfaceContext)) {
55
+ const surfaceComponents = getBlockControlsLeftSurfaceComponents(components, surfaceContext);
56
+ if (!surfaceComponents) {
57
57
  return [];
58
58
  }
59
59
  // The cache supplies the same anchor names used by ProseMirror decorations.
60
60
  const anchorName = isCSSAnchorSupported() ? surfaceAnchors.get(target.position) : undefined;
61
+ // Top-level controls can respond to the available left gutter without the surface
62
+ // host knowing which controls are registered or how they adapt.
63
+ const isTopLevel = context.targetNode.parentType === 'doc';
61
64
  return [{
62
65
  ...target,
63
66
  anchorName,
64
67
  blockControlsContext: context,
68
+ isTopLevel,
69
+ surfaceComponents,
65
70
  surfaceContext
66
71
  }];
67
72
  }), [activeNode, components, editorView, targets, surfaceAnchors, surfaceActiveNodes]);
@@ -72,15 +77,25 @@ export const BlockControlsLeftSurfaces = ({
72
77
  const anchoredPlacements = useMemo(() => new Map(surfaces.flatMap(({
73
78
  anchorName,
74
79
  blockControlsContext,
80
+ isTopLevel,
75
81
  position
76
- }) => anchorName ? [[position, getAnchoredSurfacePlacement({
77
- anchorName,
78
- nodeType: blockControlsContext.targetNode.type.name,
79
- nodeTypeWithLevel: getNodeTypeWithLevel(blockControlsContext.targetNode.node),
80
- layout: blockControlsContext.targetNode.node.attrs.layout,
81
- parentNodeType: blockControlsContext.targetNode.parentType === 'doc' ? undefined : blockControlsContext.targetNode.parentType,
82
- side: 'left'
83
- })]] : [])), [surfaces]);
82
+ }) => {
83
+ if (!anchorName) {
84
+ return [];
85
+ }
86
+ const options = {
87
+ anchorName,
88
+ layout: blockControlsContext.targetNode.node.attrs.layout,
89
+ nodeType: blockControlsContext.targetNode.type.name,
90
+ nodeTypeWithLevel: getNodeTypeWithLevel(blockControlsContext.targetNode.node),
91
+ parentNodeType: blockControlsContext.targetNode.parentType === 'doc' ? undefined : blockControlsContext.targetNode.parentType
92
+ };
93
+ const placement = isTopLevel ? getAnchoredLeftGutterSurfacePlacement(options) : getAnchoredSurfacePlacement({
94
+ ...options,
95
+ side: 'left'
96
+ });
97
+ return [[position, placement]];
98
+ })), [surfaces]);
84
99
  // Only the surfaces that still need measuring drive the effect below, so a document of anchored
85
100
  // blocks binds no scroll or resize listeners at all.
86
101
  //
@@ -106,6 +121,7 @@ export const BlockControlsLeftSurfaces = ({
106
121
  }
107
122
  const measurementTargets = measuredSurfaces.flatMap(({
108
123
  blockControlsContext,
124
+ isTopLevel,
109
125
  position
110
126
  }) => {
111
127
  const targetNodeElement = editorView.nodeDOM(blockControlsContext.targetNode.pos);
@@ -115,6 +131,7 @@ export const BlockControlsLeftSurfaces = ({
115
131
  }
116
132
  return [{
117
133
  blockControlsContext,
134
+ isTopLevel,
118
135
  position,
119
136
  targetElement: getNodeContentElement(targetElementRoot, blockControlsContext.targetNode.type.name),
120
137
  targetElementRoot
@@ -132,20 +149,27 @@ export const BlockControlsLeftSurfaces = ({
132
149
  const nextPlacements = new Map();
133
150
  for (const {
134
151
  blockControlsContext,
152
+ isTopLevel,
135
153
  position,
136
154
  targetElement,
137
155
  targetElementRoot
138
156
  } of measurementTargets) {
139
- var _targetElementRoot$ge;
157
+ var _targetElementRoot$ge, _offsetParentRect$lef;
140
158
  if (!targetElementRoot.isConnected || !targetElement.isConnected) {
141
159
  continue;
142
160
  }
143
- const placement = getSurfacePlacement({
161
+ const options = {
144
162
  layout: (_targetElementRoot$ge = targetElementRoot.getAttribute('layout')) !== null && _targetElementRoot$ge !== void 0 ? _targetElementRoot$ge : '',
145
163
  nodeRect: targetElement.getBoundingClientRect(),
146
164
  nodeType: blockControlsContext.targetNode.type.name,
147
165
  nodeTypeWithLevel: getNodeTypeWithLevel(blockControlsContext.targetNode.node),
148
- parentNodeType: blockControlsContext.targetNode.parentType === 'doc' ? undefined : blockControlsContext.targetNode.parentType,
166
+ parentNodeType: blockControlsContext.targetNode.parentType === 'doc' ? undefined : blockControlsContext.targetNode.parentType
167
+ };
168
+ const placement = isTopLevel ? getLeftGutterSurfacePlacement({
169
+ ...options,
170
+ leftGutterBoundary: (_offsetParentRect$lef = offsetParentRect === null || offsetParentRect === void 0 ? void 0 : offsetParentRect.left) !== null && _offsetParentRect$lef !== void 0 ? _offsetParentRect$lef : 0
171
+ }) : getSurfacePlacement({
172
+ ...options,
149
173
  side: 'left'
150
174
  });
151
175
  nextPlacements.set(position, toMeasuredSurfaceWrapperPlacement(getAbsoluteSurfacePlacement({
@@ -218,12 +242,13 @@ export const BlockControlsLeftSurfaces = ({
218
242
  return /*#__PURE__*/React.createElement(React.Fragment, null, surfaces.map(({
219
243
  position,
220
244
  source,
245
+ surfaceComponents,
221
246
  surfaceContext
222
247
  }) => {
223
248
  var _activeNode$handleOpt, _anchoredPlacements$g;
224
249
  return /*#__PURE__*/React.createElement(BlockControlsLeftSurface, {
225
250
  api: api,
226
- components: components,
251
+ components: surfaceComponents,
227
252
  forceVisibleOnMouseOut: Boolean(activeNode === null || activeNode === void 0 ? void 0 : (_activeNode$handleOpt = activeNode.handleOptions) === null || _activeNode$handleOpt === void 0 ? void 0 : _activeNode$handleOpt.isFocused),
228
253
  key: position,
229
254
  placement: (_anchoredPlacements$g = anchoredPlacements.get(position)) !== null && _anchoredPlacements$g !== void 0 ? _anchoredPlacements$g : placements.get(position),
@@ -3,9 +3,9 @@ import "./block-controls-right-surface.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
4
  import React from 'react';
5
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 { partitionComponentsByPersistence } from './utils/partition-components-by-persistence';
8
- import { VisibilityContainer } from './visibility-container';
6
+ import { SurfaceRenderer } from '@atlaskit/editor-ui-control-model/surface-renderer';
7
+ import { BlockControlsVisibilityProvider } from './block-controls-surface-components';
8
+ import { useBlockControlsVisibility } from './visibility-container';
9
9
  const rightSurfaceOuterStyles = null;
10
10
  const rightSurfaceRowStyles = null;
11
11
 
@@ -14,8 +14,7 @@ const rightSurfaceRowStyles = null;
14
14
  // can stick near the top of the viewport instead of scrolling off with the node's top edge.
15
15
  // Keep in sync with STICKY_CONTROLS_TOP_MARGIN in ./consts (must be a static literal here).
16
16
  const rightSurfaceStickyStyles = null;
17
- /** Renders one right block-controls surface at a document position. */
18
- export const BlockControlsRightSurface = ({
17
+ const BlockControlsRightSurfaceContent = ({
19
18
  api,
20
19
  components,
21
20
  forceVisibleOnMouseOut,
@@ -23,15 +22,14 @@ export const BlockControlsRightSurface = ({
23
22
  source,
24
23
  surfaceContext
25
24
  }) => {
26
- if (!placement || !willSurfaceRender(components, BLOCK_CONTROLS_RIGHT_SURFACE, surfaceContext)) {
27
- return null;
28
- }
29
25
  const {
30
- hoverOnlyComponents,
31
- persistentComponents
32
- } = partitionComponentsByPersistence(components, BLOCK_CONTROLS_RIGHT_SURFACE, surfaceContext);
33
- const willRenderPersistent = willSurfaceRender(persistentComponents, BLOCK_CONTROLS_RIGHT_SURFACE, surfaceContext);
34
- const willRenderHoverOnly = willSurfaceRender(hoverOnlyComponents, BLOCK_CONTROLS_RIGHT_SURFACE, surfaceContext);
26
+ shouldHide: shouldHideHoverControls,
27
+ useCssStyles
28
+ } = useBlockControlsVisibility({
29
+ api,
30
+ controlSide: 'right',
31
+ forceVisibleOnMouseOut
32
+ });
35
33
  const isSticky = placement.isSticky;
36
34
  return /*#__PURE__*/React.createElement("div", {
37
35
  "data-editor-block-controls-surface": true,
@@ -43,23 +41,34 @@ export const BlockControlsRightSurface = ({
43
41
  className: ax(["_kqswstnw _17c61i8t _1pbyb4wl"])
44
42
  }, /*#__PURE__*/React.createElement("div", {
45
43
  className: ax(["_zulpv77o _4cvr1h6o _1e0c1txw", isSticky && "_kqsw1if8 _154iu2gc"])
46
- }, willRenderPersistent && /*#__PURE__*/React.createElement(VisibilityContainer, {
47
- api: api,
48
- controlSide: "right",
49
- isPersistent: true,
50
- shouldUseDisplayContents: true
44
+ }, /*#__PURE__*/React.createElement(BlockControlsVisibilityProvider, {
45
+ shouldHideHoverControls: shouldHideHoverControls,
46
+ useCssStyles: useCssStyles
51
47
  }, /*#__PURE__*/React.createElement(SurfaceRenderer, {
52
- components: persistentComponents,
48
+ components: components,
53
49
  surface: BLOCK_CONTROLS_RIGHT_SURFACE,
54
50
  surfaceContext: surfaceContext
55
- })), willRenderHoverOnly && /*#__PURE__*/React.createElement(VisibilityContainer, {
51
+ }))));
52
+ };
53
+
54
+ /** Renders one right block-controls surface at a document position. */
55
+ export const BlockControlsRightSurface = ({
56
+ api,
57
+ components,
58
+ forceVisibleOnMouseOut,
59
+ placement,
60
+ source,
61
+ surfaceContext
62
+ }) => {
63
+ if (!placement) {
64
+ return null;
65
+ }
66
+ return /*#__PURE__*/React.createElement(BlockControlsRightSurfaceContent, {
56
67
  api: api,
57
- controlSide: "right",
68
+ components: components,
58
69
  forceVisibleOnMouseOut: forceVisibleOnMouseOut,
59
- shouldUseDisplayContents: true
60
- }, /*#__PURE__*/React.createElement(SurfaceRenderer, {
61
- components: hoverOnlyComponents,
62
- surface: BLOCK_CONTROLS_RIGHT_SURFACE,
70
+ placement: placement,
71
+ source: source,
63
72
  surfaceContext: surfaceContext
64
- }))));
73
+ });
65
74
  };
@@ -8,14 +8,13 @@ import { BLOCK_CONTROLS_RIGHT_SURFACE } from '@atlaskit/editor-common/block-cont
8
8
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
9
9
  import { isCSSAnchorSupported } from '@atlaskit/editor-common/styles';
10
10
  import { createSurfaceContext } from '@atlaskit/editor-ui-control-model/create-surface-context';
11
- import { willSurfaceRender } from '@atlaskit/editor-ui-control-model/surface-renderer';
12
11
  import { getNodeTypeWithLevel } from '../pm-plugins/decorations-common';
13
12
  import { BlockControlsRightSurface } from './block-controls-right-surface';
13
+ import { getBlockControlsRightSurfaceComponents } from './block-controls-surface-components';
14
14
  import { createBlockControlsSurfaceContextForPosition } from './block-controls-surface-context';
15
15
  import { getBlockControlsSurfaceTargets } from './block-controls-surface-targets';
16
16
  import { getNodeContentElement } from './utils/get-node-content-element';
17
17
  import { getAbsoluteSurfacePlacement, getAnchoredSurfacePlacement, getSurfacePlacement, toMeasuredSurfaceWrapperPlacement } from './utils/get-surface-placement';
18
- import { hasSurfaceControls } from './utils/has-surface-controls';
19
18
  const EMPTY_SURFACE_POSITIONS = [];
20
19
  const EMPTY_SURFACE_ANCHORS = new Map();
21
20
  const EMPTY_SURFACE_COMPONENTS = [];
@@ -62,7 +61,8 @@ export const BlockControlsRightSurfaces = ({
62
61
  return [];
63
62
  }
64
63
  const surfaceContext = createSurfaceContext(BLOCK_CONTROL_UI_CONTEXT, context);
65
- if (!hasSurfaceControls(components) || !willSurfaceRender(components, BLOCK_CONTROLS_RIGHT_SURFACE, surfaceContext)) {
64
+ const surfaceComponents = getBlockControlsRightSurfaceComponents(components, surfaceContext);
65
+ if (!surfaceComponents) {
66
66
  return [];
67
67
  }
68
68
  // The cache supplies the same anchor names used by ProseMirror decorations.
@@ -71,6 +71,7 @@ export const BlockControlsRightSurfaces = ({
71
71
  ...target,
72
72
  anchorName,
73
73
  blockControlsContext: context,
74
+ surfaceComponents,
74
75
  surfaceContext
75
76
  }];
76
77
  }), [activeNode, components, editorView, targets, surfaceAnchors, surfaceActiveNodes]);
@@ -227,12 +228,13 @@ export const BlockControlsRightSurfaces = ({
227
228
  return /*#__PURE__*/React.createElement(React.Fragment, null, surfaces.map(({
228
229
  position,
229
230
  source,
231
+ surfaceComponents,
230
232
  surfaceContext
231
233
  }) => {
232
234
  var _activeNode$handleOpt, _anchoredPlacements$g;
233
235
  return /*#__PURE__*/React.createElement(BlockControlsRightSurface, {
234
236
  api: api,
235
- components: components,
237
+ components: surfaceComponents,
236
238
  forceVisibleOnMouseOut: Boolean(activeNode === null || activeNode === void 0 ? void 0 : (_activeNode$handleOpt = activeNode.handleOptions) === null || _activeNode$handleOpt === void 0 ? void 0 : _activeNode$handleOpt.isFocused),
237
239
  key: position,
238
240
  placement: (_anchoredPlacements$g = anchoredPlacements.get(position)) !== null && _anchoredPlacements$g !== void 0 ? _anchoredPlacements$g : placements.get(position),
@@ -0,0 +1,129 @@
1
+ import React, { createContext, useContext, useMemo } from 'react';
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
+ import { getComponentIdentity, resolveSurface } from '@atlaskit/editor-ui-control-model/surface-renderer';
4
+ import { partitionComponentsByPersistence } from './utils/partition-components-by-persistence';
5
+ import { VisibilityWrapper } from './visibility-container';
6
+ const BlockControlsVisibilityContext = /*#__PURE__*/createContext({
7
+ shouldHideHoverControls: false,
8
+ useCssStyles: false
9
+ });
10
+ const BlockControlItem = ({
11
+ children
12
+ }) => /*#__PURE__*/React.createElement("div", {
13
+ "data-editor-block-control-item": true
14
+ }, children);
15
+ const HoverBlockControlItem = ({
16
+ children
17
+ }) => {
18
+ const {
19
+ shouldHideHoverControls,
20
+ useCssStyles
21
+ } = useContext(BlockControlsVisibilityContext);
22
+ return /*#__PURE__*/React.createElement(BlockControlItem, null, /*#__PURE__*/React.createElement(VisibilityWrapper, {
23
+ shouldHide: shouldHideHoverControls,
24
+ useCssStyles: useCssStyles
25
+ }, children));
26
+ };
27
+ const PersistentBlockControlItem = ({
28
+ children
29
+ }) => {
30
+ const {
31
+ useCssStyles
32
+ } = useContext(BlockControlsVisibilityContext);
33
+ return /*#__PURE__*/React.createElement(BlockControlItem, null, /*#__PURE__*/React.createElement(VisibilityWrapper, {
34
+ shouldHide: false,
35
+ useCssStyles: useCssStyles
36
+ }, children));
37
+ };
38
+ export const BlockControlsVisibilityProvider = ({
39
+ children,
40
+ shouldHideHoverControls,
41
+ useCssStyles
42
+ }) => {
43
+ const value = useMemo(() => ({
44
+ shouldHideHoverControls,
45
+ useCssStyles
46
+ }), [shouldHideHoverControls, useCssStyles]);
47
+ return /*#__PURE__*/React.createElement(BlockControlsVisibilityContext.Provider, {
48
+ value: value
49
+ }, children);
50
+ };
51
+ const withoutVisibilityPredicate = component => {
52
+ const {
53
+ isHidden: _isHidden,
54
+ ...visibleComponent
55
+ } = component;
56
+ return visibleComponent;
57
+ };
58
+
59
+ /**
60
+ * Replaces the shared surface group with one stable wrapper group per visible control. The wrappers
61
+ * inherit each control's rank, so SurfaceRenderer keeps the registry order without re-evaluating
62
+ * the controls' visibility predicates.
63
+ */
64
+ const getBlockControlsSurfaceComponents = (components, {
65
+ group,
66
+ section,
67
+ surface
68
+ }, surfaceContext) => {
69
+ var _resolvedSurface$chil;
70
+ const resolvedSurface = resolveSurface(components, surface);
71
+ const groupIdentity = getComponentIdentity(group);
72
+ const controls = (_resolvedSurface$chil = resolvedSurface.childrenMap.get(groupIdentity)) !== null && _resolvedSurface$chil !== void 0 ? _resolvedSurface$chil : [];
73
+ const visibleControls = controls.filter(control => {
74
+ var _control$isHidden, _control$isHidden2;
75
+ return !((_control$isHidden = (_control$isHidden2 = control.isHidden) === null || _control$isHidden2 === void 0 ? void 0 : _control$isHidden2.call(control, {
76
+ surfaceContext
77
+ })) !== null && _control$isHidden !== void 0 ? _control$isHidden : false);
78
+ });
79
+ if (!resolvedSurface.root || visibleControls.length === 0) {
80
+ return undefined;
81
+ }
82
+ const controlIdentities = new Set(controls.map(getComponentIdentity));
83
+ const visibleControlIdentities = new Set(visibleControls.map(getComponentIdentity));
84
+ const {
85
+ persistentComponents
86
+ } = partitionComponentsByPersistence(resolvedSurface.components.filter(component => {
87
+ const identity = getComponentIdentity(component);
88
+ return !controlIdentities.has(identity) || visibleControlIdentities.has(identity);
89
+ }), surface, surfaceContext);
90
+ const persistentIdentities = new Set(persistentComponents.map(getComponentIdentity));
91
+ const structuralComponents = resolvedSurface.components.filter(component => {
92
+ const identity = getComponentIdentity(component);
93
+ return identity !== groupIdentity && !controlIdentities.has(identity);
94
+ }).map(withoutVisibilityPredicate);
95
+ const wrappedControls = visibleControls.flatMap(control => {
96
+ var _control$parents$find, _control$parents, _control$parents$find2;
97
+ const identity = getComponentIdentity(control);
98
+ const rank = (_control$parents$find = (_control$parents = control.parents) === null || _control$parents === void 0 ? void 0 : (_control$parents$find2 = _control$parents.find(parent => getComponentIdentity(parent) === groupIdentity)) === null || _control$parents$find2 === void 0 ? void 0 : _control$parents$find2.rank) !== null && _control$parents$find !== void 0 ? _control$parents$find : 0;
99
+ const wrapper = {
100
+ component: persistentIdentities.has(identity) ? PersistentBlockControlItem : HoverBlockControlItem,
101
+ key: `block-control-item:${identity}`,
102
+ parents: [{
103
+ ...section,
104
+ rank
105
+ }],
106
+ type: 'group'
107
+ };
108
+ const visibleControl = {
109
+ ...withoutVisibilityPredicate(control),
110
+ parents: [{
111
+ key: wrapper.key,
112
+ rank: 0,
113
+ type: wrapper.type
114
+ }]
115
+ };
116
+ return [wrapper, visibleControl];
117
+ });
118
+ return [...structuralComponents, ...wrappedControls];
119
+ };
120
+ export const getBlockControlsLeftSurfaceComponents = (components, surfaceContext) => getBlockControlsSurfaceComponents(components, {
121
+ group: BLOCK_CONTROLS_LEFT_GROUP,
122
+ section: BLOCK_CONTROLS_LEFT_SECTION,
123
+ surface: BLOCK_CONTROLS_LEFT_SURFACE
124
+ }, surfaceContext);
125
+ export const getBlockControlsRightSurfaceComponents = (components, surfaceContext) => getBlockControlsSurfaceComponents(components, {
126
+ group: BLOCK_CONTROLS_RIGHT_GROUP,
127
+ section: BLOCK_CONTROLS_RIGHT_SECTION,
128
+ surface: BLOCK_CONTROLS_RIGHT_SURFACE
129
+ }, surfaceContext);
@@ -43,18 +43,43 @@ export const getSurfacePlacement = ({
43
43
  const gap = dragHandleGap(nodeType, parentNodeType);
44
44
  const top = nodeRect.top + topPositionAdjustment(nodeTypeWithLevel, layout);
45
45
  const height = shouldBeSticky(nodeType) ? nodeRect.height : undefined;
46
- return side === 'right' ? {
47
- left: nodeRect.right + gap,
48
- top,
49
- transform: '',
50
- height
51
- } : {
46
+ if (side === 'right') {
47
+ return {
48
+ left: nodeRect.right + gap,
49
+ top,
50
+ transform: '',
51
+ height
52
+ };
53
+ }
54
+ return {
52
55
  left: nodeRect.left,
53
56
  top,
54
57
  transform: `translateX(calc(-100% - ${gap}px))`,
55
58
  height
56
59
  };
57
60
  };
61
+ /**
62
+ * Expands a measured left surface from the available gutter boundary to the target node.
63
+ */
64
+ export const getLeftGutterSurfacePlacement = ({
65
+ leftGutterBoundary,
66
+ ...options
67
+ }) => {
68
+ const placement = getSurfacePlacement({
69
+ ...options,
70
+ side: 'left'
71
+ });
72
+ if (options.nodeType === 'layoutColumn') {
73
+ return placement;
74
+ }
75
+ const gap = dragHandleGap(options.nodeType, options.parentNodeType);
76
+ return {
77
+ ...placement,
78
+ left: leftGutterBoundary,
79
+ transform: '',
80
+ width: Math.max(0, options.nodeRect.left - leftGutterBoundary - gap)
81
+ };
82
+ };
58
83
 
59
84
  /**
60
85
  * The insets to apply to a surface wrapper, plus whether that wrapper spans its target node.
@@ -78,7 +103,10 @@ export const toMeasuredSurfaceWrapperPlacement = placement => ({
78
103
  height: placement.height,
79
104
  left: placement.left,
80
105
  top: placement.top,
81
- transform: placement.transform
106
+ transform: placement.transform,
107
+ ...(placement.width !== undefined ? {
108
+ width: placement.width
109
+ } : {})
82
110
  }
83
111
  });
84
112
 
@@ -135,4 +163,28 @@ export const getAnchoredSurfacePlacement = ({
135
163
  transform: side === 'right' ? '' : `translateX(calc(-100% - ${gap}px))`
136
164
  }
137
165
  };
166
+ };
167
+ /**
168
+ * Expands an anchored left surface across the available gutter without runtime measurement.
169
+ */
170
+ export const getAnchoredLeftGutterSurfacePlacement = options => {
171
+ const placement = getAnchoredSurfacePlacement({
172
+ ...options,
173
+ side: 'left'
174
+ });
175
+ if (options.nodeType === 'layoutColumn') {
176
+ return placement;
177
+ }
178
+ const gap = dragHandleGap(options.nodeType, options.parentNodeType);
179
+ return {
180
+ ...placement,
181
+ style: {
182
+ ...placement.style,
183
+ left: 0,
184
+ // In a `right` inset, `anchor(... left)` resolves from the containing block's right edge
185
+ // to the anchor's left edge. Adding the gap ends this wrapper just before the node.
186
+ right: `calc(anchor(${options.anchorName} left) + ${gap}px)`,
187
+ transform: ''
188
+ }
189
+ };
138
190
  };
@@ -46,13 +46,16 @@ const hiddenStylesCSS = css({
46
46
  const displayContentsStylesCSS = css({
47
47
  display: 'contents'
48
48
  });
49
- export const VisibilityContainer = ({
49
+
50
+ /**
51
+ * Resolves visibility once so a surface can apply the same state to each rendered control without
52
+ * creating a plugin-state subscription and hide-delay timer for every control wrapper.
53
+ */
54
+ export const useBlockControlsVisibility = ({
50
55
  api,
51
- children,
52
56
  controlSide,
53
57
  forceVisibleOnMouseOut,
54
- isPersistent,
55
- shouldUseDisplayContents
58
+ isPersistent
56
59
  }) => {
57
60
  const {
58
61
  isTypeAheadOpen,
@@ -127,9 +130,21 @@ export const VisibilityContainer = ({
127
130
  };
128
131
  }, [shouldHideImmediate, isRightControlViewMode]);
129
132
  const shouldHide = isRightControlViewMode ? delayedShouldHide : shouldHideImmediate;
130
- if (editorExperiment('platform_editor_preview_panel_responsiveness', true, {
133
+ const useCssStyles = editorExperiment('platform_editor_preview_panel_responsiveness', true, {
131
134
  exposure: true
132
- })) {
135
+ });
136
+ return {
137
+ shouldHide,
138
+ useCssStyles
139
+ };
140
+ };
141
+ export const VisibilityWrapper = ({
142
+ children,
143
+ shouldHide,
144
+ shouldUseDisplayContents,
145
+ useCssStyles
146
+ }) => {
147
+ if (useCssStyles) {
133
148
  return jsx("div", {
134
149
  css: [baseStylesCSS, shouldHide ? hiddenStylesCSS : visibleStylesCSS, shouldUseDisplayContents && displayContentsStylesCSS]
135
150
  }, children);
@@ -139,4 +154,27 @@ export const VisibilityContainer = ({
139
154
  , {
140
155
  xcss: [baseStyles, shouldHide ? hiddenStyles : visibleStyles, shouldUseDisplayContents && displayContentsStyles]
141
156
  }, children);
157
+ };
158
+ export const VisibilityContainer = ({
159
+ api,
160
+ children,
161
+ controlSide,
162
+ forceVisibleOnMouseOut,
163
+ isPersistent,
164
+ shouldUseDisplayContents
165
+ }) => {
166
+ const {
167
+ shouldHide,
168
+ useCssStyles
169
+ } = useBlockControlsVisibility({
170
+ api,
171
+ controlSide,
172
+ forceVisibleOnMouseOut,
173
+ isPersistent
174
+ });
175
+ return jsx(VisibilityWrapper, {
176
+ shouldHide: shouldHide,
177
+ shouldUseDisplayContents: shouldUseDisplayContents,
178
+ useCssStyles: useCssStyles
179
+ }, children);
142
180
  };
@@ -5,7 +5,6 @@ import { bind } from 'bind-event-listener';
5
5
  import { getDocument } from '@atlaskit/browser-apis';
6
6
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
7
7
  import { DRAG_HANDLE_SELECTOR } from '@atlaskit/editor-common/styles';
8
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
9
8
  import { key } from '../main';
10
9
  import { createPreservedSelection, mapPreservedSelection } from '../utils/selection';
11
10
  import { stopPreservingSelection } from './editor-commands';
@@ -48,37 +47,25 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
48
47
  key: selectionPreservationPluginKey,
49
48
  state: {
50
49
  init: function init() {
51
- if (expValEquals('platform_editor_fix_selection_text_color_change', 'isEnabled', true)) {
52
- return {
53
- preservedSelection: undefined,
54
- syncDomSelectionForDoc: undefined
55
- };
56
- } else {
57
- return {
58
- preservedSelection: undefined
59
- };
60
- }
50
+ return {
51
+ preservedSelection: undefined,
52
+ syncDomSelectionForDoc: undefined
53
+ };
61
54
  },
62
55
  apply: function apply(tr, pluginState) {
63
56
  var meta = getSelectionPreservationMeta(tr);
64
57
  var newState = _objectSpread({}, pluginState);
65
58
  if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'startPreserving') {
66
59
  newState.preservedSelection = createPreservedSelection(tr.doc.resolve(tr.selection.from), tr.doc.resolve(tr.selection.to));
67
- if (expValEquals('platform_editor_fix_selection_text_color_change', 'isEnabled', true)) {
68
- newState.syncDomSelectionForDoc = undefined;
69
- }
60
+ newState.syncDomSelectionForDoc = undefined;
70
61
  } else if ((meta === null || meta === void 0 ? void 0 : meta.type) === 'stopPreserving') {
71
62
  newState.preservedSelection = undefined;
72
- if (expValEquals('platform_editor_fix_selection_text_color_change', 'isEnabled', true)) {
73
- newState.syncDomSelectionForDoc = undefined;
74
- }
63
+ newState.syncDomSelectionForDoc = undefined;
75
64
  } else if (tr.docChanged) {
76
65
  if (newState.preservedSelection) {
77
66
  newState.preservedSelection = mapPreservedSelection(newState.preservedSelection, tr);
78
67
  }
79
- if (expValEquals('platform_editor_fix_selection_text_color_change', 'isEnabled', true)) {
80
- newState.syncDomSelectionForDoc = hasFormatSelectionSyncMeta(tr) ? tr.doc : undefined;
81
- }
68
+ newState.syncDomSelectionForDoc = hasFormatSelectionSyncMeta(tr) ? tr.doc : undefined;
82
69
  }
83
70
  if (!compareSelections(newState.preservedSelection, pluginState.preservedSelection)) {
84
71
  if (newState.preservedSelection) {
@@ -188,23 +175,19 @@ export var createSelectionPreservationPlugin = function createSelectionPreservat
188
175
  var activeNodeChanged = prevActiveNode !== currActiveNode;
189
176
  var docChanged = prevState.doc !== view.state.doc;
190
177
  var hasFormatSyncRequestForCurrentDoc = (currPluginState === null || currPluginState === void 0 ? void 0 : currPluginState.syncDomSelectionForDoc) === view.state.doc;
191
- var shouldSyncDOMSelection = (hasPreservedSelection && (preservedSelectionChanged || activeNodeChanged) || expValEquals('platform_editor_fix_selection_text_color_change', 'isEnabled', true) && hasFormatSyncRequestForCurrentDoc) && docChanged;
178
+ var shouldSyncDOMSelection = (hasPreservedSelection && (preservedSelectionChanged || activeNodeChanged) || hasFormatSyncRequestForCurrentDoc) && docChanged;
192
179
  if (shouldSyncDOMSelection) {
193
180
  var syncSelection = view.state.selection;
194
- if (expValEquals('platform_editor_fix_selection_text_color_change', 'isEnabled', true)) {
195
- if (hasFormatSyncRequestForCurrentDoc) {
196
- if (pendingFormatSelectionSyncFrame !== undefined) {
197
- cancelAnimationFrame(pendingFormatSelectionSyncFrame);
198
- }
199
- pendingFormatSelectionSyncFrame = requestAnimationFrame(function () {
200
- pendingFormatSelectionSyncFrame = undefined;
201
- syncDOMSelection(syncSelection, view, {
202
- focusEditor: true
203
- });
204
- });
205
- } else {
206
- syncDOMSelection(syncSelection, view);
181
+ if (hasFormatSyncRequestForCurrentDoc) {
182
+ if (pendingFormatSelectionSyncFrame !== undefined) {
183
+ cancelAnimationFrame(pendingFormatSelectionSyncFrame);
207
184
  }
185
+ pendingFormatSelectionSyncFrame = requestAnimationFrame(function () {
186
+ pendingFormatSelectionSyncFrame = undefined;
187
+ syncDOMSelection(syncSelection, view, {
188
+ focusEditor: true
189
+ });
190
+ });
208
191
  } else {
209
192
  syncDOMSelection(syncSelection, view);
210
193
  }