@atlaskit/editor-plugin-selection-toolbar 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/calculate-toolbar-position.js +117 -0
- package/dist/cjs/plugin.js +18 -13
- package/dist/es2019/calculate-toolbar-position.js +112 -0
- package/dist/es2019/plugin.js +18 -13
- package/dist/esm/calculate-toolbar-position.js +111 -0
- package/dist/esm/plugin.js +18 -13
- package/dist/types/calculate-toolbar-position.d.ts +9 -0
- package/dist/types-ts4.5/calculate-toolbar-position.d.ts +9 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-selection-toolbar
|
|
2
2
|
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#43081](https://bitbucket.org/atlassian/atlassian-frontend/pull-requests/43081) [`efe83787c45`](https://bitbucket.org/atlassian/atlassian-frontend/commits/efe83787c45) - [ux] ED-20762 Remove blur dom event handler to prevent the toolbar disappearing when using the keyboard to access it.
|
|
8
|
+
|
|
9
|
+
## 0.1.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#42935](https://bitbucket.org/atlassian/atlassian-frontend/pull-requests/42935) [`d9e2cafc03e`](https://bitbucket.org/atlassian/atlassian-frontend/commits/d9e2cafc03e) - [ux] ED-20664 Fix position of floating toolbar on non full-page editors when using editor-plugin-selection-toolbar
|
|
14
|
+
- [#42935](https://bitbucket.org/atlassian/atlassian-frontend/pull-requests/42935) [`31e453b325e`](https://bitbucket.org/atlassian/atlassian-frontend/commits/31e453b325e) - [ux] ED-20807 Prevents the selection toolbar from extending outside of the Editor.
|
|
15
|
+
- [#42935](https://bitbucket.org/atlassian/atlassian-frontend/pull-requests/42935) [`bc3880e7c3c`](https://bitbucket.org/atlassian/atlassian-frontend/commits/bc3880e7c3c) - [ux] ED-20806 Prevents the selection toolbar from overriding the table floating toolbar by preventing rendering on anything that's not a text selection.
|
|
16
|
+
|
|
3
17
|
## 0.1.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.calculateToolbarPositionTrackHead = void 0;
|
|
7
|
+
var getScrollParent = function getScrollParent(editorView) {
|
|
8
|
+
var _editorContentArea$pa;
|
|
9
|
+
// Find the nearest Editor
|
|
10
|
+
var editorContentArea = editorView.dom.closest('.ak-editor-content-area');
|
|
11
|
+
|
|
12
|
+
// Check if the Editor is a child of another, the annotation editor inside
|
|
13
|
+
// Confluence full-page editor for example.
|
|
14
|
+
// If so, we need to append the toolbar to the closest Editor
|
|
15
|
+
if (editorContentArea !== null && editorContentArea !== void 0 && (_editorContentArea$pa = editorContentArea.parentElement) !== null && _editorContentArea$pa !== void 0 && _editorContentArea$pa.closest('.ak-editor-content-area')) {
|
|
16
|
+
return {
|
|
17
|
+
scrollWrapper: editorContentArea,
|
|
18
|
+
offset: editorContentArea.offsetTop
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// If the Editor is full-page there should be a parent .fabric-editor-popup-scroll-parent
|
|
23
|
+
var scrollParent = editorView.dom.closest('.fabric-editor-popup-scroll-parent');
|
|
24
|
+
|
|
25
|
+
// If there is a scroll parent then we can assume the Editor is full-page
|
|
26
|
+
if (scrollParent) {
|
|
27
|
+
return {
|
|
28
|
+
scrollWrapper: scrollParent,
|
|
29
|
+
offset: scrollParent.scrollTop
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// If there is no scroll parent then we can assume the Editor is not full-page
|
|
34
|
+
if (editorContentArea && !scrollParent) {
|
|
35
|
+
return {
|
|
36
|
+
scrollWrapper: editorContentArea,
|
|
37
|
+
offset: editorContentArea.offsetTop
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Use the document body as a fallback
|
|
42
|
+
return {
|
|
43
|
+
scrollWrapper: document.body,
|
|
44
|
+
offset: 0
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
Calculates the position of the floating toolbar relative to the selection.
|
|
50
|
+
This is a re-implementation which closely matches the behaviour on Confluence renderer.
|
|
51
|
+
The main difference is the popup is always above the selection.
|
|
52
|
+
Things to consider:
|
|
53
|
+
- stick as close to the head X release coordinates as possible
|
|
54
|
+
- coordinates of head X and getBoundingClientRect() are absolute in client viewport (not including scroll offsets)
|
|
55
|
+
- popup may appear in '.fabric-editor-popup-scroll-parent' (or body)
|
|
56
|
+
- we use the toolbarRect to center align toolbar
|
|
57
|
+
- use wrapperBounds to clamp values
|
|
58
|
+
- editorView.dom bounds differ to wrapperBounds, convert at the end
|
|
59
|
+
*/
|
|
60
|
+
var calculateToolbarPositionTrackHead = exports.calculateToolbarPositionTrackHead = function calculateToolbarPositionTrackHead(toolbarTitle) {
|
|
61
|
+
return function (editorView, nextPos) {
|
|
62
|
+
var toolbar = document.querySelector("div[aria-label=\"".concat(toolbarTitle, "\"]"));
|
|
63
|
+
if (!toolbar) {
|
|
64
|
+
return nextPos;
|
|
65
|
+
}
|
|
66
|
+
var _getScrollParent = getScrollParent(editorView),
|
|
67
|
+
scrollWrapper = _getScrollParent.scrollWrapper,
|
|
68
|
+
offset = _getScrollParent.offset;
|
|
69
|
+
var wrapperBounds = scrollWrapper.getBoundingClientRect();
|
|
70
|
+
var selection = window && window.getSelection();
|
|
71
|
+
var range = selection && !selection.isCollapsed && selection.getRangeAt(0);
|
|
72
|
+
if (!range) {
|
|
73
|
+
return nextPos;
|
|
74
|
+
}
|
|
75
|
+
var toolbarRect = toolbar.getBoundingClientRect();
|
|
76
|
+
var _editorView$state$sel = editorView.state.selection,
|
|
77
|
+
head = _editorView$state$sel.head,
|
|
78
|
+
anchor = _editorView$state$sel.anchor;
|
|
79
|
+
var topCoords = editorView.coordsAtPos(Math.min(head, anchor));
|
|
80
|
+
var bottomCoords = editorView.coordsAtPos(Math.max(head, anchor) - Math.min(range.endOffset, 1));
|
|
81
|
+
var top;
|
|
82
|
+
// If not the same line, display toolbar below.
|
|
83
|
+
if (head > anchor && topCoords.top !== bottomCoords.top) {
|
|
84
|
+
// We are taking the previous pos to the maxium, so avoid end of line positions
|
|
85
|
+
// returning the next line's rect.
|
|
86
|
+
top = (bottomCoords.top || 0) + toolbarRect.height / 1.15;
|
|
87
|
+
} else {
|
|
88
|
+
top = (topCoords.top || 0) - toolbarRect.height * 1.5;
|
|
89
|
+
}
|
|
90
|
+
var left = (head > anchor ? bottomCoords.right : topCoords.left) - toolbarRect.width / 2;
|
|
91
|
+
// Place toolbar below selection if not sufficient space above
|
|
92
|
+
if (top < wrapperBounds.top) {
|
|
93
|
+
var _getCoordsBelowSelect = getCoordsBelowSelection(bottomCoords, toolbarRect);
|
|
94
|
+
top = _getCoordsBelowSelect.top;
|
|
95
|
+
left = _getCoordsBelowSelect.left;
|
|
96
|
+
}
|
|
97
|
+
// Make sure the toolbar doesn't extend out of the Editor
|
|
98
|
+
if (left + toolbarRect.width > wrapperBounds.right) {
|
|
99
|
+
left = wrapperBounds.right - toolbarRect.width;
|
|
100
|
+
}
|
|
101
|
+
// remap positions from browser document to wrapperBounds
|
|
102
|
+
return {
|
|
103
|
+
top: top - wrapperBounds.top + offset,
|
|
104
|
+
left: Math.max(0, left - wrapperBounds.left)
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Returns the coordintes at the bottom the selection.
|
|
111
|
+
*/
|
|
112
|
+
var getCoordsBelowSelection = function getCoordsBelowSelection(bottomCoords, toolbarRect) {
|
|
113
|
+
return {
|
|
114
|
+
top: (bottomCoords.top || 0) + toolbarRect.height / 1.15,
|
|
115
|
+
left: bottomCoords.right - toolbarRect.width / 2
|
|
116
|
+
};
|
|
117
|
+
};
|
package/dist/cjs/plugin.js
CHANGED
|
@@ -9,7 +9,8 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
|
|
|
9
9
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
10
10
|
var _bindEventListener = require("bind-event-listener");
|
|
11
11
|
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
|
|
12
|
-
var
|
|
12
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
13
|
+
var _calculateToolbarPosition = require("./calculate-toolbar-position");
|
|
13
14
|
var _pluginKey = require("./plugin-key");
|
|
14
15
|
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; }
|
|
15
16
|
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; }
|
|
@@ -65,12 +66,6 @@ var selectionToolbarPlugin = exports.selectionToolbarPlugin = function selection
|
|
|
65
66
|
selectionStable: false
|
|
66
67
|
}));
|
|
67
68
|
return false;
|
|
68
|
-
},
|
|
69
|
-
blur: function blur(view) {
|
|
70
|
-
view.dispatch(view.state.tr.setMeta(_pluginKey.selectionToolbarPluginKey, {
|
|
71
|
-
selectionStable: false
|
|
72
|
-
}));
|
|
73
|
-
return false;
|
|
74
69
|
}
|
|
75
70
|
}
|
|
76
71
|
}
|
|
@@ -82,7 +77,10 @@ var selectionToolbarPlugin = exports.selectionToolbarPlugin = function selection
|
|
|
82
77
|
floatingToolbar: function floatingToolbar(state, intl, providerFactory) {
|
|
83
78
|
var _selectionToolbarPlug = _pluginKey.selectionToolbarPluginKey.getState(state),
|
|
84
79
|
selectionStable = _selectionToolbarPlug.selectionStable;
|
|
85
|
-
if (state.selection.empty || !selectionStable ||
|
|
80
|
+
if (state.selection.empty || !selectionStable || state.selection instanceof _state.NodeSelection ||
|
|
81
|
+
// $anchorCell is only available in CellSelection, this check is to
|
|
82
|
+
// avoid importing CellSelection from @atlaskit/editor-tables
|
|
83
|
+
'$anchorCell' in state.selection) {
|
|
86
84
|
// If there is no active selection, or the selection is not stable, or the selection is a node selection,
|
|
87
85
|
// do not show the toolbar.
|
|
88
86
|
return;
|
|
@@ -109,12 +107,19 @@ var selectionToolbarPlugin = exports.selectionToolbarPlugin = function selection
|
|
|
109
107
|
}
|
|
110
108
|
return -1;
|
|
111
109
|
});
|
|
110
|
+
var items = [];
|
|
111
|
+
|
|
112
112
|
// This flattens the groups passed into the floating toolbar into a single list of items
|
|
113
|
-
var
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
for (var i = 0; i < resolved.length; i++) {
|
|
114
|
+
// add a seperator icon after each group except the last
|
|
115
|
+
items.push.apply(items, (0, _toConsumableArray2.default)(resolved[i].items));
|
|
116
|
+
if (i !== resolved.length - 1) {
|
|
117
|
+
items.push({
|
|
118
|
+
type: 'separator'
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
var calcToolbarPosition = _calculateToolbarPosition.calculateToolbarPositionTrackHead;
|
|
118
123
|
var toolbarTitle = 'Selection toolbar';
|
|
119
124
|
var onPositionCalculated = calcToolbarPosition(toolbarTitle);
|
|
120
125
|
var nodeType = getSelectionNodeTypes(state);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
const getScrollParent = editorView => {
|
|
2
|
+
var _editorContentArea$pa;
|
|
3
|
+
// Find the nearest Editor
|
|
4
|
+
const editorContentArea = editorView.dom.closest('.ak-editor-content-area');
|
|
5
|
+
|
|
6
|
+
// Check if the Editor is a child of another, the annotation editor inside
|
|
7
|
+
// Confluence full-page editor for example.
|
|
8
|
+
// If so, we need to append the toolbar to the closest Editor
|
|
9
|
+
if (editorContentArea !== null && editorContentArea !== void 0 && (_editorContentArea$pa = editorContentArea.parentElement) !== null && _editorContentArea$pa !== void 0 && _editorContentArea$pa.closest('.ak-editor-content-area')) {
|
|
10
|
+
return {
|
|
11
|
+
scrollWrapper: editorContentArea,
|
|
12
|
+
offset: editorContentArea.offsetTop
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// If the Editor is full-page there should be a parent .fabric-editor-popup-scroll-parent
|
|
17
|
+
const scrollParent = editorView.dom.closest('.fabric-editor-popup-scroll-parent');
|
|
18
|
+
|
|
19
|
+
// If there is a scroll parent then we can assume the Editor is full-page
|
|
20
|
+
if (scrollParent) {
|
|
21
|
+
return {
|
|
22
|
+
scrollWrapper: scrollParent,
|
|
23
|
+
offset: scrollParent.scrollTop
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If there is no scroll parent then we can assume the Editor is not full-page
|
|
28
|
+
if (editorContentArea && !scrollParent) {
|
|
29
|
+
return {
|
|
30
|
+
scrollWrapper: editorContentArea,
|
|
31
|
+
offset: editorContentArea.offsetTop
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Use the document body as a fallback
|
|
36
|
+
return {
|
|
37
|
+
scrollWrapper: document.body,
|
|
38
|
+
offset: 0
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
Calculates the position of the floating toolbar relative to the selection.
|
|
44
|
+
This is a re-implementation which closely matches the behaviour on Confluence renderer.
|
|
45
|
+
The main difference is the popup is always above the selection.
|
|
46
|
+
Things to consider:
|
|
47
|
+
- stick as close to the head X release coordinates as possible
|
|
48
|
+
- coordinates of head X and getBoundingClientRect() are absolute in client viewport (not including scroll offsets)
|
|
49
|
+
- popup may appear in '.fabric-editor-popup-scroll-parent' (or body)
|
|
50
|
+
- we use the toolbarRect to center align toolbar
|
|
51
|
+
- use wrapperBounds to clamp values
|
|
52
|
+
- editorView.dom bounds differ to wrapperBounds, convert at the end
|
|
53
|
+
*/
|
|
54
|
+
export const calculateToolbarPositionTrackHead = toolbarTitle => (editorView, nextPos) => {
|
|
55
|
+
const toolbar = document.querySelector(`div[aria-label="${toolbarTitle}"]`);
|
|
56
|
+
if (!toolbar) {
|
|
57
|
+
return nextPos;
|
|
58
|
+
}
|
|
59
|
+
const {
|
|
60
|
+
scrollWrapper,
|
|
61
|
+
offset
|
|
62
|
+
} = getScrollParent(editorView);
|
|
63
|
+
const wrapperBounds = scrollWrapper.getBoundingClientRect();
|
|
64
|
+
const selection = window && window.getSelection();
|
|
65
|
+
const range = selection && !selection.isCollapsed && selection.getRangeAt(0);
|
|
66
|
+
if (!range) {
|
|
67
|
+
return nextPos;
|
|
68
|
+
}
|
|
69
|
+
const toolbarRect = toolbar.getBoundingClientRect();
|
|
70
|
+
const {
|
|
71
|
+
head,
|
|
72
|
+
anchor
|
|
73
|
+
} = editorView.state.selection;
|
|
74
|
+
let topCoords = editorView.coordsAtPos(Math.min(head, anchor));
|
|
75
|
+
let bottomCoords = editorView.coordsAtPos(Math.max(head, anchor) - Math.min(range.endOffset, 1));
|
|
76
|
+
let top;
|
|
77
|
+
// If not the same line, display toolbar below.
|
|
78
|
+
if (head > anchor && topCoords.top !== bottomCoords.top) {
|
|
79
|
+
// We are taking the previous pos to the maxium, so avoid end of line positions
|
|
80
|
+
// returning the next line's rect.
|
|
81
|
+
top = (bottomCoords.top || 0) + toolbarRect.height / 1.15;
|
|
82
|
+
} else {
|
|
83
|
+
top = (topCoords.top || 0) - toolbarRect.height * 1.5;
|
|
84
|
+
}
|
|
85
|
+
let left = (head > anchor ? bottomCoords.right : topCoords.left) - toolbarRect.width / 2;
|
|
86
|
+
// Place toolbar below selection if not sufficient space above
|
|
87
|
+
if (top < wrapperBounds.top) {
|
|
88
|
+
({
|
|
89
|
+
top,
|
|
90
|
+
left
|
|
91
|
+
} = getCoordsBelowSelection(bottomCoords, toolbarRect));
|
|
92
|
+
}
|
|
93
|
+
// Make sure the toolbar doesn't extend out of the Editor
|
|
94
|
+
if (left + toolbarRect.width > wrapperBounds.right) {
|
|
95
|
+
left = wrapperBounds.right - toolbarRect.width;
|
|
96
|
+
}
|
|
97
|
+
// remap positions from browser document to wrapperBounds
|
|
98
|
+
return {
|
|
99
|
+
top: top - wrapperBounds.top + offset,
|
|
100
|
+
left: Math.max(0, left - wrapperBounds.left)
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Returns the coordintes at the bottom the selection.
|
|
106
|
+
*/
|
|
107
|
+
const getCoordsBelowSelection = (bottomCoords, toolbarRect) => {
|
|
108
|
+
return {
|
|
109
|
+
top: (bottomCoords.top || 0) + toolbarRect.height / 1.15,
|
|
110
|
+
left: bottomCoords.right - toolbarRect.width / 2
|
|
111
|
+
};
|
|
112
|
+
};
|
package/dist/es2019/plugin.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { bind } from 'bind-event-listener';
|
|
2
2
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
3
|
-
import {
|
|
3
|
+
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
4
|
+
import { calculateToolbarPositionTrackHead } from './calculate-toolbar-position';
|
|
4
5
|
import { selectionToolbarPluginKey } from './plugin-key';
|
|
5
6
|
export const selectionToolbarPlugin = options => {
|
|
6
7
|
let __selectionToolbarHandlers = [];
|
|
@@ -57,12 +58,6 @@ export const selectionToolbarPlugin = options => {
|
|
|
57
58
|
selectionStable: false
|
|
58
59
|
}));
|
|
59
60
|
return false;
|
|
60
|
-
},
|
|
61
|
-
blur: view => {
|
|
62
|
-
view.dispatch(view.state.tr.setMeta(selectionToolbarPluginKey, {
|
|
63
|
-
selectionStable: false
|
|
64
|
-
}));
|
|
65
|
-
return false;
|
|
66
61
|
}
|
|
67
62
|
}
|
|
68
63
|
}
|
|
@@ -75,7 +70,10 @@ export const selectionToolbarPlugin = options => {
|
|
|
75
70
|
const {
|
|
76
71
|
selectionStable
|
|
77
72
|
} = selectionToolbarPluginKey.getState(state);
|
|
78
|
-
if (state.selection.empty || !selectionStable ||
|
|
73
|
+
if (state.selection.empty || !selectionStable || state.selection instanceof NodeSelection ||
|
|
74
|
+
// $anchorCell is only available in CellSelection, this check is to
|
|
75
|
+
// avoid importing CellSelection from @atlaskit/editor-tables
|
|
76
|
+
'$anchorCell' in state.selection) {
|
|
79
77
|
// If there is no active selection, or the selection is not stable, or the selection is a node selection,
|
|
80
78
|
// do not show the toolbar.
|
|
81
79
|
return;
|
|
@@ -98,12 +96,19 @@ export const selectionToolbarPlugin = options => {
|
|
|
98
96
|
}
|
|
99
97
|
return -1;
|
|
100
98
|
});
|
|
99
|
+
const items = [];
|
|
100
|
+
|
|
101
101
|
// This flattens the groups passed into the floating toolbar into a single list of items
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
for (let i = 0; i < resolved.length; i++) {
|
|
103
|
+
// add a seperator icon after each group except the last
|
|
104
|
+
items.push(...resolved[i].items);
|
|
105
|
+
if (i !== resolved.length - 1) {
|
|
106
|
+
items.push({
|
|
107
|
+
type: 'separator'
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const calcToolbarPosition = calculateToolbarPositionTrackHead;
|
|
107
112
|
const toolbarTitle = 'Selection toolbar';
|
|
108
113
|
const onPositionCalculated = calcToolbarPosition(toolbarTitle);
|
|
109
114
|
const nodeType = getSelectionNodeTypes(state);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
var getScrollParent = function getScrollParent(editorView) {
|
|
2
|
+
var _editorContentArea$pa;
|
|
3
|
+
// Find the nearest Editor
|
|
4
|
+
var editorContentArea = editorView.dom.closest('.ak-editor-content-area');
|
|
5
|
+
|
|
6
|
+
// Check if the Editor is a child of another, the annotation editor inside
|
|
7
|
+
// Confluence full-page editor for example.
|
|
8
|
+
// If so, we need to append the toolbar to the closest Editor
|
|
9
|
+
if (editorContentArea !== null && editorContentArea !== void 0 && (_editorContentArea$pa = editorContentArea.parentElement) !== null && _editorContentArea$pa !== void 0 && _editorContentArea$pa.closest('.ak-editor-content-area')) {
|
|
10
|
+
return {
|
|
11
|
+
scrollWrapper: editorContentArea,
|
|
12
|
+
offset: editorContentArea.offsetTop
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// If the Editor is full-page there should be a parent .fabric-editor-popup-scroll-parent
|
|
17
|
+
var scrollParent = editorView.dom.closest('.fabric-editor-popup-scroll-parent');
|
|
18
|
+
|
|
19
|
+
// If there is a scroll parent then we can assume the Editor is full-page
|
|
20
|
+
if (scrollParent) {
|
|
21
|
+
return {
|
|
22
|
+
scrollWrapper: scrollParent,
|
|
23
|
+
offset: scrollParent.scrollTop
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If there is no scroll parent then we can assume the Editor is not full-page
|
|
28
|
+
if (editorContentArea && !scrollParent) {
|
|
29
|
+
return {
|
|
30
|
+
scrollWrapper: editorContentArea,
|
|
31
|
+
offset: editorContentArea.offsetTop
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Use the document body as a fallback
|
|
36
|
+
return {
|
|
37
|
+
scrollWrapper: document.body,
|
|
38
|
+
offset: 0
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
Calculates the position of the floating toolbar relative to the selection.
|
|
44
|
+
This is a re-implementation which closely matches the behaviour on Confluence renderer.
|
|
45
|
+
The main difference is the popup is always above the selection.
|
|
46
|
+
Things to consider:
|
|
47
|
+
- stick as close to the head X release coordinates as possible
|
|
48
|
+
- coordinates of head X and getBoundingClientRect() are absolute in client viewport (not including scroll offsets)
|
|
49
|
+
- popup may appear in '.fabric-editor-popup-scroll-parent' (or body)
|
|
50
|
+
- we use the toolbarRect to center align toolbar
|
|
51
|
+
- use wrapperBounds to clamp values
|
|
52
|
+
- editorView.dom bounds differ to wrapperBounds, convert at the end
|
|
53
|
+
*/
|
|
54
|
+
export var calculateToolbarPositionTrackHead = function calculateToolbarPositionTrackHead(toolbarTitle) {
|
|
55
|
+
return function (editorView, nextPos) {
|
|
56
|
+
var toolbar = document.querySelector("div[aria-label=\"".concat(toolbarTitle, "\"]"));
|
|
57
|
+
if (!toolbar) {
|
|
58
|
+
return nextPos;
|
|
59
|
+
}
|
|
60
|
+
var _getScrollParent = getScrollParent(editorView),
|
|
61
|
+
scrollWrapper = _getScrollParent.scrollWrapper,
|
|
62
|
+
offset = _getScrollParent.offset;
|
|
63
|
+
var wrapperBounds = scrollWrapper.getBoundingClientRect();
|
|
64
|
+
var selection = window && window.getSelection();
|
|
65
|
+
var range = selection && !selection.isCollapsed && selection.getRangeAt(0);
|
|
66
|
+
if (!range) {
|
|
67
|
+
return nextPos;
|
|
68
|
+
}
|
|
69
|
+
var toolbarRect = toolbar.getBoundingClientRect();
|
|
70
|
+
var _editorView$state$sel = editorView.state.selection,
|
|
71
|
+
head = _editorView$state$sel.head,
|
|
72
|
+
anchor = _editorView$state$sel.anchor;
|
|
73
|
+
var topCoords = editorView.coordsAtPos(Math.min(head, anchor));
|
|
74
|
+
var bottomCoords = editorView.coordsAtPos(Math.max(head, anchor) - Math.min(range.endOffset, 1));
|
|
75
|
+
var top;
|
|
76
|
+
// If not the same line, display toolbar below.
|
|
77
|
+
if (head > anchor && topCoords.top !== bottomCoords.top) {
|
|
78
|
+
// We are taking the previous pos to the maxium, so avoid end of line positions
|
|
79
|
+
// returning the next line's rect.
|
|
80
|
+
top = (bottomCoords.top || 0) + toolbarRect.height / 1.15;
|
|
81
|
+
} else {
|
|
82
|
+
top = (topCoords.top || 0) - toolbarRect.height * 1.5;
|
|
83
|
+
}
|
|
84
|
+
var left = (head > anchor ? bottomCoords.right : topCoords.left) - toolbarRect.width / 2;
|
|
85
|
+
// Place toolbar below selection if not sufficient space above
|
|
86
|
+
if (top < wrapperBounds.top) {
|
|
87
|
+
var _getCoordsBelowSelect = getCoordsBelowSelection(bottomCoords, toolbarRect);
|
|
88
|
+
top = _getCoordsBelowSelect.top;
|
|
89
|
+
left = _getCoordsBelowSelect.left;
|
|
90
|
+
}
|
|
91
|
+
// Make sure the toolbar doesn't extend out of the Editor
|
|
92
|
+
if (left + toolbarRect.width > wrapperBounds.right) {
|
|
93
|
+
left = wrapperBounds.right - toolbarRect.width;
|
|
94
|
+
}
|
|
95
|
+
// remap positions from browser document to wrapperBounds
|
|
96
|
+
return {
|
|
97
|
+
top: top - wrapperBounds.top + offset,
|
|
98
|
+
left: Math.max(0, left - wrapperBounds.left)
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Returns the coordintes at the bottom the selection.
|
|
105
|
+
*/
|
|
106
|
+
var getCoordsBelowSelection = function getCoordsBelowSelection(bottomCoords, toolbarRect) {
|
|
107
|
+
return {
|
|
108
|
+
top: (bottomCoords.top || 0) + toolbarRect.height / 1.15,
|
|
109
|
+
left: bottomCoords.right - toolbarRect.width / 2
|
|
110
|
+
};
|
|
111
|
+
};
|
package/dist/esm/plugin.js
CHANGED
|
@@ -4,7 +4,8 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
|
|
|
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 { bind } from 'bind-event-listener';
|
|
6
6
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
7
|
-
import {
|
|
7
|
+
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
8
|
+
import { calculateToolbarPositionTrackHead } from './calculate-toolbar-position';
|
|
8
9
|
import { selectionToolbarPluginKey } from './plugin-key';
|
|
9
10
|
export var selectionToolbarPlugin = function selectionToolbarPlugin(options) {
|
|
10
11
|
var __selectionToolbarHandlers = [];
|
|
@@ -58,12 +59,6 @@ export var selectionToolbarPlugin = function selectionToolbarPlugin(options) {
|
|
|
58
59
|
selectionStable: false
|
|
59
60
|
}));
|
|
60
61
|
return false;
|
|
61
|
-
},
|
|
62
|
-
blur: function blur(view) {
|
|
63
|
-
view.dispatch(view.state.tr.setMeta(selectionToolbarPluginKey, {
|
|
64
|
-
selectionStable: false
|
|
65
|
-
}));
|
|
66
|
-
return false;
|
|
67
62
|
}
|
|
68
63
|
}
|
|
69
64
|
}
|
|
@@ -75,7 +70,10 @@ export var selectionToolbarPlugin = function selectionToolbarPlugin(options) {
|
|
|
75
70
|
floatingToolbar: function floatingToolbar(state, intl, providerFactory) {
|
|
76
71
|
var _selectionToolbarPlug = selectionToolbarPluginKey.getState(state),
|
|
77
72
|
selectionStable = _selectionToolbarPlug.selectionStable;
|
|
78
|
-
if (state.selection.empty || !selectionStable ||
|
|
73
|
+
if (state.selection.empty || !selectionStable || state.selection instanceof NodeSelection ||
|
|
74
|
+
// $anchorCell is only available in CellSelection, this check is to
|
|
75
|
+
// avoid importing CellSelection from @atlaskit/editor-tables
|
|
76
|
+
'$anchorCell' in state.selection) {
|
|
79
77
|
// If there is no active selection, or the selection is not stable, or the selection is a node selection,
|
|
80
78
|
// do not show the toolbar.
|
|
81
79
|
return;
|
|
@@ -102,12 +100,19 @@ export var selectionToolbarPlugin = function selectionToolbarPlugin(options) {
|
|
|
102
100
|
}
|
|
103
101
|
return -1;
|
|
104
102
|
});
|
|
103
|
+
var items = [];
|
|
104
|
+
|
|
105
105
|
// This flattens the groups passed into the floating toolbar into a single list of items
|
|
106
|
-
var
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
for (var i = 0; i < resolved.length; i++) {
|
|
107
|
+
// add a seperator icon after each group except the last
|
|
108
|
+
items.push.apply(items, _toConsumableArray(resolved[i].items));
|
|
109
|
+
if (i !== resolved.length - 1) {
|
|
110
|
+
items.push({
|
|
111
|
+
type: 'separator'
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
var calcToolbarPosition = calculateToolbarPositionTrackHead;
|
|
111
116
|
var toolbarTitle = 'Selection toolbar';
|
|
112
117
|
var onPositionCalculated = calcToolbarPosition(toolbarTitle);
|
|
113
118
|
var nodeType = getSelectionNodeTypes(state);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PopupPosition as Position } from '@atlaskit/editor-common/ui';
|
|
2
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
+
export declare const calculateToolbarPositionTrackHead: (toolbarTitle: string) => (editorView: EditorView, nextPos: Position) => Position;
|
|
4
|
+
export type CoordsAtPos = {
|
|
5
|
+
top: number;
|
|
6
|
+
bottom: number;
|
|
7
|
+
left: number;
|
|
8
|
+
right: number;
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PopupPosition as Position } from '@atlaskit/editor-common/ui';
|
|
2
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
+
export declare const calculateToolbarPositionTrackHead: (toolbarTitle: string) => (editorView: EditorView, nextPos: Position) => Position;
|
|
4
|
+
export type CoordsAtPos = {
|
|
5
|
+
top: number;
|
|
6
|
+
bottom: number;
|
|
7
|
+
left: number;
|
|
8
|
+
right: number;
|
|
9
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-selection-toolbar",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "@atlaskit/editor-plugin-selection-toolbar for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
".": "./src/index.ts"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@atlaskit/editor-common": "^76.
|
|
38
|
+
"@atlaskit/editor-common": "^76.19.0",
|
|
39
39
|
"@atlaskit/editor-prosemirror": "1.1.0",
|
|
40
40
|
"@babel/runtime": "^7.0.0",
|
|
41
41
|
"bind-event-listener": "^2.1.1"
|