@atlaskit/editor-plugin-selection 1.4.2 → 1.4.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.
- package/CHANGELOG.md +17 -0
- package/dist/cjs/mark-boundary-cursor/marks-side.js +12 -0
- package/dist/cjs/mark-boundary-cursor/ui/mark-boundary-cursor-decoration.js +38 -0
- package/dist/cjs/mark-boundary-cursor/utils/active-marks-side.js +40 -0
- package/dist/cjs/mark-boundary-cursor/utils/inline-code-side.js +45 -0
- package/dist/cjs/plugin.js +9 -1
- package/dist/cjs/pm-plugins/mark-boundary-cursor-main.js +51 -0
- package/dist/cjs/pm-plugins/mark-boundary-cursor-plugin-key.js +8 -0
- package/dist/es2019/mark-boundary-cursor/marks-side.js +6 -0
- package/dist/es2019/mark-boundary-cursor/ui/mark-boundary-cursor-decoration.js +41 -0
- package/dist/es2019/mark-boundary-cursor/utils/active-marks-side.js +36 -0
- package/dist/es2019/mark-boundary-cursor/utils/inline-code-side.js +34 -0
- package/dist/es2019/plugin.js +6 -1
- package/dist/es2019/pm-plugins/mark-boundary-cursor-main.js +45 -0
- package/dist/es2019/pm-plugins/mark-boundary-cursor-plugin-key.js +2 -0
- package/dist/esm/mark-boundary-cursor/marks-side.js +6 -0
- package/dist/esm/mark-boundary-cursor/ui/mark-boundary-cursor-decoration.js +32 -0
- package/dist/esm/mark-boundary-cursor/utils/active-marks-side.js +34 -0
- package/dist/esm/mark-boundary-cursor/utils/inline-code-side.js +39 -0
- package/dist/esm/plugin.js +9 -1
- package/dist/esm/pm-plugins/mark-boundary-cursor-main.js +45 -0
- package/dist/esm/pm-plugins/mark-boundary-cursor-plugin-key.js +2 -0
- package/dist/types/mark-boundary-cursor/marks-side.d.ts +5 -0
- package/dist/types/mark-boundary-cursor/ui/mark-boundary-cursor-decoration.d.ts +2 -0
- package/dist/types/mark-boundary-cursor/utils/active-marks-side.d.ts +18 -0
- package/dist/types/mark-boundary-cursor/utils/inline-code-side.d.ts +18 -0
- package/dist/types/pm-plugins/mark-boundary-cursor-main.d.ts +5 -0
- package/dist/types/pm-plugins/mark-boundary-cursor-plugin-key.d.ts +2 -0
- package/dist/types-ts4.5/mark-boundary-cursor/marks-side.d.ts +5 -0
- package/dist/types-ts4.5/mark-boundary-cursor/ui/mark-boundary-cursor-decoration.d.ts +2 -0
- package/dist/types-ts4.5/mark-boundary-cursor/utils/active-marks-side.d.ts +18 -0
- package/dist/types-ts4.5/mark-boundary-cursor/utils/inline-code-side.d.ts +18 -0
- package/dist/types-ts4.5/pm-plugins/mark-boundary-cursor-main.d.ts +5 -0
- package/dist/types-ts4.5/pm-plugins/mark-boundary-cursor-plugin-key.d.ts +2 -0
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-selection
|
|
2
2
|
|
|
3
|
+
## 1.4.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#140867](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/140867)
|
|
8
|
+
[`17b58500a94e3`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/17b58500a94e3) -
|
|
9
|
+
[ux] ED-24255 Adding a custom mark boundary cursor to fix the cursor position in inline code
|
|
10
|
+
|
|
11
|
+
## 1.4.3
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#139334](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/139334)
|
|
16
|
+
[`30793649657c0`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/30793649657c0) -
|
|
17
|
+
[HOT-111629] We had an incident where the last character disappears when hitting the enter key on
|
|
18
|
+
windows OS for Korean characters. Bumping to prosemirror-view@1.34.2 for the fix.
|
|
19
|
+
|
|
3
20
|
## 1.4.2
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.MarksSide = void 0;
|
|
7
|
+
var MarksSide = exports.MarksSide = /*#__PURE__*/function (MarksSide) {
|
|
8
|
+
MarksSide[MarksSide["None"] = 0] = "None";
|
|
9
|
+
MarksSide[MarksSide["Left"] = -1] = "Left";
|
|
10
|
+
MarksSide[MarksSide["Right"] = 1] = "Right";
|
|
11
|
+
return MarksSide;
|
|
12
|
+
}({});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createMarkBoundaryCursorDecoration = void 0;
|
|
7
|
+
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
8
|
+
var containerStyle = "\n\tposition: relative;\n";
|
|
9
|
+
var caretStyle = "\n\tposition: absolute;\n\theight: 100%;\n\twidth: 1px;\n\tleft: -0.5px;\n\tbottom: 0;\n\tbackground-color: ".concat("var(--ds-text, #172B4D)", ";\n");
|
|
10
|
+
var toDOM = function toDOM() {
|
|
11
|
+
var container = document.createElement('span');
|
|
12
|
+
container.className = 'ProseMirror-mark-boundary-cursor';
|
|
13
|
+
container.setAttribute('style', containerStyle);
|
|
14
|
+
var caret = document.createElement('div');
|
|
15
|
+
caret.setAttribute('style', caretStyle);
|
|
16
|
+
caret.animate([{
|
|
17
|
+
opacity: 1
|
|
18
|
+
}, {
|
|
19
|
+
opacity: 1,
|
|
20
|
+
offset: 0.5
|
|
21
|
+
}, {
|
|
22
|
+
opacity: 0,
|
|
23
|
+
offset: 0.5
|
|
24
|
+
}, {
|
|
25
|
+
opacity: 0
|
|
26
|
+
}], {
|
|
27
|
+
duration: 1000,
|
|
28
|
+
iterations: Infinity
|
|
29
|
+
});
|
|
30
|
+
container.appendChild(caret);
|
|
31
|
+
return container;
|
|
32
|
+
};
|
|
33
|
+
var createMarkBoundaryCursorDecoration = exports.createMarkBoundaryCursorDecoration = function createMarkBoundaryCursorDecoration(pos, side) {
|
|
34
|
+
return _view.Decoration.widget(pos, toDOM, {
|
|
35
|
+
key: 'mark-boundary-cursor',
|
|
36
|
+
side: side
|
|
37
|
+
});
|
|
38
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getActiveMarksSide = getActiveMarksSide;
|
|
7
|
+
var _utils = require("@atlaskit/editor-common/utils");
|
|
8
|
+
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
9
|
+
var _marksSide = require("../marks-side");
|
|
10
|
+
var _inlineCodeSide = require("./inline-code-side");
|
|
11
|
+
/**
|
|
12
|
+
* Will determine which side of the current selection the mark-boundary-cursor should
|
|
13
|
+
* favour / point towards, based on which marks are currently active and which marks
|
|
14
|
+
* are applied to the nodes to the left and right side of the current selection.
|
|
15
|
+
*
|
|
16
|
+
* @param {Selection} options.selection - The current selection.
|
|
17
|
+
* @param {readonly Mark[] | null} options.storedMarks - Current stored marks, if any.
|
|
18
|
+
* @returns {MarksSide} - The side (left -1, right 1, or none 0) the mark-boundary-cursor should favour.
|
|
19
|
+
*/
|
|
20
|
+
function getActiveMarksSide(_ref) {
|
|
21
|
+
var _nodeBefore$marks, _nodeAfter$marks;
|
|
22
|
+
var selection = _ref.selection,
|
|
23
|
+
storedMarks = _ref.storedMarks;
|
|
24
|
+
if (!(0, _utils.isTextSelection)(selection) || !selection.empty) {
|
|
25
|
+
return _marksSide.MarksSide.None;
|
|
26
|
+
}
|
|
27
|
+
var _selection$$head = selection.$head,
|
|
28
|
+
nodeBefore = _selection$$head.nodeBefore,
|
|
29
|
+
nodeAfter = _selection$$head.nodeAfter;
|
|
30
|
+
var leftMarks = (_nodeBefore$marks = nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.marks) !== null && _nodeBefore$marks !== void 0 ? _nodeBefore$marks : [];
|
|
31
|
+
var rightMarks = (_nodeAfter$marks = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.marks) !== null && _nodeAfter$marks !== void 0 ? _nodeAfter$marks : [];
|
|
32
|
+
if (_model.Mark.sameSet(leftMarks, rightMarks)) {
|
|
33
|
+
return _marksSide.MarksSide.None;
|
|
34
|
+
}
|
|
35
|
+
return (0, _inlineCodeSide.getInlineCodeCursorSide)({
|
|
36
|
+
leftMarks: leftMarks,
|
|
37
|
+
rightMarks: rightMarks,
|
|
38
|
+
storedMarks: storedMarks
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getInlineCodeCursorSide = getInlineCodeCursorSide;
|
|
7
|
+
var _marksSide = require("../marks-side");
|
|
8
|
+
/**
|
|
9
|
+
* Given the marks on the left and right side of the current selection, and the stored
|
|
10
|
+
* marks being updated during a transaction, this function returns the side (-1 = left,
|
|
11
|
+
* 1 = right) for which node the mark boundary cursor decoration should be added
|
|
12
|
+
*
|
|
13
|
+
* This is intended to correct an issue where the default cursor does not render in the
|
|
14
|
+
* expected location when navigating via arrow keys past the boundary of an inline code
|
|
15
|
+
* node
|
|
16
|
+
*/
|
|
17
|
+
function getInlineCodeCursorSide(_ref) {
|
|
18
|
+
var leftMarks = _ref.leftMarks,
|
|
19
|
+
rightMarks = _ref.rightMarks,
|
|
20
|
+
storedMarks = _ref.storedMarks;
|
|
21
|
+
var isInlineCodeToLeft = leftMarks.some(function (mark) {
|
|
22
|
+
return mark.type.name === 'code';
|
|
23
|
+
});
|
|
24
|
+
var isInlineCodeToRight = rightMarks.some(function (mark) {
|
|
25
|
+
return mark.type.name === 'code';
|
|
26
|
+
});
|
|
27
|
+
var isInlineCodeBeingStored = !!storedMarks && (storedMarks === null || storedMarks === void 0 ? void 0 : storedMarks.some(function (mark) {
|
|
28
|
+
return mark.type.name === 'code';
|
|
29
|
+
}));
|
|
30
|
+
var isStoredMarksBeingCleared = !!storedMarks && storedMarks.length === 0;
|
|
31
|
+
|
|
32
|
+
// This condition covers two scenarios:
|
|
33
|
+
// a) When the cursor is on the left side of the inline code, and is expected to be
|
|
34
|
+
// positioned within the inline code after pressing arrow right
|
|
35
|
+
// b) When the cursor is on the right side of the inline code, and is expected to be
|
|
36
|
+
// positioned outside of the inline code after pressing arrow right
|
|
37
|
+
// in both of these cases the cursor needs to be corrected to be positioned inline
|
|
38
|
+
// with the node to the right
|
|
39
|
+
// NOTE: In editor-plugin-text-formatting there is logic on left/right key press
|
|
40
|
+
// which dispatches a transaction to update the stored marks
|
|
41
|
+
if (isInlineCodeToRight && isInlineCodeBeingStored || isInlineCodeToLeft && isStoredMarksBeingCleared) {
|
|
42
|
+
return _marksSide.MarksSide.Right;
|
|
43
|
+
}
|
|
44
|
+
return _marksSide.MarksSide.None;
|
|
45
|
+
}
|
package/dist/cjs/plugin.js
CHANGED
|
@@ -5,11 +5,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.selectionPlugin = exports.default = void 0;
|
|
8
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
8
10
|
var _commands = require("./commands");
|
|
9
11
|
var _gapCursorKeymap = _interopRequireDefault(require("./pm-plugins/gap-cursor-keymap"));
|
|
10
12
|
var _gapCursorMain = _interopRequireDefault(require("./pm-plugins/gap-cursor-main"));
|
|
11
13
|
var _gapCursorPluginKey = require("./pm-plugins/gap-cursor-plugin-key");
|
|
12
14
|
var _keymap = _interopRequireDefault(require("./pm-plugins/keymap"));
|
|
15
|
+
var _markBoundaryCursorMain = require("./pm-plugins/mark-boundary-cursor-main");
|
|
13
16
|
var _selectionMain = require("./pm-plugins/selection-main");
|
|
14
17
|
var _types = require("./types");
|
|
15
18
|
var displayGapCursor = function displayGapCursor(toggle) {
|
|
@@ -69,7 +72,12 @@ var selectionPlugin = exports.selectionPlugin = function selectionPlugin(_ref2)
|
|
|
69
72
|
plugin: function plugin() {
|
|
70
73
|
return _gapCursorMain.default;
|
|
71
74
|
}
|
|
72
|
-
}]
|
|
75
|
+
}].concat((0, _toConsumableArray2.default)((0, _platformFeatureFlags.fg)('platform_editor_mark_boundary_cursor') ? [{
|
|
76
|
+
name: 'markBoundaryCursor',
|
|
77
|
+
plugin: function plugin() {
|
|
78
|
+
return (0, _markBoundaryCursorMain.createMarkBoundaryCursorPlugin)();
|
|
79
|
+
}
|
|
80
|
+
}] : []));
|
|
73
81
|
}
|
|
74
82
|
};
|
|
75
83
|
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createMarkBoundaryCursorPlugin = void 0;
|
|
7
|
+
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
|
|
8
|
+
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
9
|
+
var _markBoundaryCursorDecoration = require("../mark-boundary-cursor/ui/mark-boundary-cursor-decoration");
|
|
10
|
+
var _activeMarksSide = require("../mark-boundary-cursor/utils/active-marks-side");
|
|
11
|
+
var _markBoundaryCursorPluginKey = require("./mark-boundary-cursor-plugin-key");
|
|
12
|
+
var createMarkBoundaryCursorPlugin = exports.createMarkBoundaryCursorPlugin = function createMarkBoundaryCursorPlugin() {
|
|
13
|
+
return new _safePlugin.SafePlugin({
|
|
14
|
+
key: _markBoundaryCursorPluginKey.markBoundaryCursorPluginKey,
|
|
15
|
+
state: {
|
|
16
|
+
init: function init() {
|
|
17
|
+
return {
|
|
18
|
+
decorations: _view.DecorationSet.empty
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
apply: function apply(tr, currentState) {
|
|
22
|
+
var selection = tr.selection,
|
|
23
|
+
storedMarks = tr.storedMarks,
|
|
24
|
+
doc = tr.doc,
|
|
25
|
+
selectionSet = tr.selectionSet,
|
|
26
|
+
storedMarksSet = tr.storedMarksSet;
|
|
27
|
+
if (!selectionSet && !storedMarksSet) {
|
|
28
|
+
return currentState;
|
|
29
|
+
}
|
|
30
|
+
var side = (0, _activeMarksSide.getActiveMarksSide)({
|
|
31
|
+
selection: selection,
|
|
32
|
+
storedMarks: storedMarks
|
|
33
|
+
});
|
|
34
|
+
if (!side) {
|
|
35
|
+
return {
|
|
36
|
+
decorations: _view.DecorationSet.empty
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
decorations: _view.DecorationSet.create(doc, [(0, _markBoundaryCursorDecoration.createMarkBoundaryCursorDecoration)(selection.head, side)])
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
props: {
|
|
45
|
+
decorations: function decorations(state) {
|
|
46
|
+
var _markBoundaryCursorPl;
|
|
47
|
+
return (_markBoundaryCursorPl = _markBoundaryCursorPluginKey.markBoundaryCursorPluginKey.getState(state)) === null || _markBoundaryCursorPl === void 0 ? void 0 : _markBoundaryCursorPl.decorations;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.markBoundaryCursorPluginKey = void 0;
|
|
7
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
8
|
+
var markBoundaryCursorPluginKey = exports.markBoundaryCursorPluginKey = new _state.PluginKey('markBoundaryCursorPlugin');
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
2
|
+
const containerStyle = `
|
|
3
|
+
position: relative;
|
|
4
|
+
`;
|
|
5
|
+
const caretStyle = `
|
|
6
|
+
position: absolute;
|
|
7
|
+
height: 100%;
|
|
8
|
+
width: 1px;
|
|
9
|
+
left: -0.5px;
|
|
10
|
+
bottom: 0;
|
|
11
|
+
background-color: ${"var(--ds-text, #172B4D)"};
|
|
12
|
+
`;
|
|
13
|
+
const toDOM = () => {
|
|
14
|
+
const container = document.createElement('span');
|
|
15
|
+
container.className = 'ProseMirror-mark-boundary-cursor';
|
|
16
|
+
container.setAttribute('style', containerStyle);
|
|
17
|
+
const caret = document.createElement('div');
|
|
18
|
+
caret.setAttribute('style', caretStyle);
|
|
19
|
+
caret.animate([{
|
|
20
|
+
opacity: 1
|
|
21
|
+
}, {
|
|
22
|
+
opacity: 1,
|
|
23
|
+
offset: 0.5
|
|
24
|
+
}, {
|
|
25
|
+
opacity: 0,
|
|
26
|
+
offset: 0.5
|
|
27
|
+
}, {
|
|
28
|
+
opacity: 0
|
|
29
|
+
}], {
|
|
30
|
+
duration: 1000,
|
|
31
|
+
iterations: Infinity
|
|
32
|
+
});
|
|
33
|
+
container.appendChild(caret);
|
|
34
|
+
return container;
|
|
35
|
+
};
|
|
36
|
+
export const createMarkBoundaryCursorDecoration = (pos, side) => {
|
|
37
|
+
return Decoration.widget(pos, toDOM, {
|
|
38
|
+
key: 'mark-boundary-cursor',
|
|
39
|
+
side
|
|
40
|
+
});
|
|
41
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isTextSelection } from '@atlaskit/editor-common/utils';
|
|
2
|
+
import { Mark } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { MarksSide } from '../marks-side';
|
|
4
|
+
import { getInlineCodeCursorSide } from './inline-code-side';
|
|
5
|
+
/**
|
|
6
|
+
* Will determine which side of the current selection the mark-boundary-cursor should
|
|
7
|
+
* favour / point towards, based on which marks are currently active and which marks
|
|
8
|
+
* are applied to the nodes to the left and right side of the current selection.
|
|
9
|
+
*
|
|
10
|
+
* @param {Selection} options.selection - The current selection.
|
|
11
|
+
* @param {readonly Mark[] | null} options.storedMarks - Current stored marks, if any.
|
|
12
|
+
* @returns {MarksSide} - The side (left -1, right 1, or none 0) the mark-boundary-cursor should favour.
|
|
13
|
+
*/
|
|
14
|
+
export function getActiveMarksSide({
|
|
15
|
+
selection,
|
|
16
|
+
storedMarks
|
|
17
|
+
}) {
|
|
18
|
+
var _nodeBefore$marks, _nodeAfter$marks;
|
|
19
|
+
if (!isTextSelection(selection) || !selection.empty) {
|
|
20
|
+
return MarksSide.None;
|
|
21
|
+
}
|
|
22
|
+
const {
|
|
23
|
+
nodeBefore,
|
|
24
|
+
nodeAfter
|
|
25
|
+
} = selection.$head;
|
|
26
|
+
const leftMarks = (_nodeBefore$marks = nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.marks) !== null && _nodeBefore$marks !== void 0 ? _nodeBefore$marks : [];
|
|
27
|
+
const rightMarks = (_nodeAfter$marks = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.marks) !== null && _nodeAfter$marks !== void 0 ? _nodeAfter$marks : [];
|
|
28
|
+
if (Mark.sameSet(leftMarks, rightMarks)) {
|
|
29
|
+
return MarksSide.None;
|
|
30
|
+
}
|
|
31
|
+
return getInlineCodeCursorSide({
|
|
32
|
+
leftMarks,
|
|
33
|
+
rightMarks,
|
|
34
|
+
storedMarks
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { MarksSide } from '../marks-side';
|
|
2
|
+
/**
|
|
3
|
+
* Given the marks on the left and right side of the current selection, and the stored
|
|
4
|
+
* marks being updated during a transaction, this function returns the side (-1 = left,
|
|
5
|
+
* 1 = right) for which node the mark boundary cursor decoration should be added
|
|
6
|
+
*
|
|
7
|
+
* This is intended to correct an issue where the default cursor does not render in the
|
|
8
|
+
* expected location when navigating via arrow keys past the boundary of an inline code
|
|
9
|
+
* node
|
|
10
|
+
*/
|
|
11
|
+
export function getInlineCodeCursorSide({
|
|
12
|
+
leftMarks,
|
|
13
|
+
rightMarks,
|
|
14
|
+
storedMarks
|
|
15
|
+
}) {
|
|
16
|
+
const isInlineCodeToLeft = leftMarks.some(mark => mark.type.name === 'code');
|
|
17
|
+
const isInlineCodeToRight = rightMarks.some(mark => mark.type.name === 'code');
|
|
18
|
+
const isInlineCodeBeingStored = !!storedMarks && (storedMarks === null || storedMarks === void 0 ? void 0 : storedMarks.some(mark => mark.type.name === 'code'));
|
|
19
|
+
const isStoredMarksBeingCleared = !!storedMarks && storedMarks.length === 0;
|
|
20
|
+
|
|
21
|
+
// This condition covers two scenarios:
|
|
22
|
+
// a) When the cursor is on the left side of the inline code, and is expected to be
|
|
23
|
+
// positioned within the inline code after pressing arrow right
|
|
24
|
+
// b) When the cursor is on the right side of the inline code, and is expected to be
|
|
25
|
+
// positioned outside of the inline code after pressing arrow right
|
|
26
|
+
// in both of these cases the cursor needs to be corrected to be positioned inline
|
|
27
|
+
// with the node to the right
|
|
28
|
+
// NOTE: In editor-plugin-text-formatting there is logic on left/right key press
|
|
29
|
+
// which dispatches a transaction to update the stored marks
|
|
30
|
+
if (isInlineCodeToRight && isInlineCodeBeingStored || isInlineCodeToLeft && isStoredMarksBeingCleared) {
|
|
31
|
+
return MarksSide.Right;
|
|
32
|
+
}
|
|
33
|
+
return MarksSide.None;
|
|
34
|
+
}
|
package/dist/es2019/plugin.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
1
2
|
import { selectNearNode } from './commands';
|
|
2
3
|
import gapCursorKeymapPlugin from './pm-plugins/gap-cursor-keymap';
|
|
3
4
|
import gapCursorPlugin from './pm-plugins/gap-cursor-main';
|
|
4
5
|
import { gapCursorPluginKey } from './pm-plugins/gap-cursor-plugin-key';
|
|
5
6
|
import selectionKeymapPlugin from './pm-plugins/keymap';
|
|
7
|
+
import { createMarkBoundaryCursorPlugin } from './pm-plugins/mark-boundary-cursor-main';
|
|
6
8
|
import { createPlugin } from './pm-plugins/selection-main';
|
|
7
9
|
import { selectionPluginKey } from './types';
|
|
8
10
|
const displayGapCursor = toggle => ({
|
|
@@ -55,7 +57,10 @@ export const selectionPlugin = ({
|
|
|
55
57
|
}, {
|
|
56
58
|
name: 'gapCursor',
|
|
57
59
|
plugin: () => gapCursorPlugin
|
|
58
|
-
}
|
|
60
|
+
}, ...(fg('platform_editor_mark_boundary_cursor') ? [{
|
|
61
|
+
name: 'markBoundaryCursor',
|
|
62
|
+
plugin: () => createMarkBoundaryCursorPlugin()
|
|
63
|
+
}] : [])];
|
|
59
64
|
}
|
|
60
65
|
});
|
|
61
66
|
export default selectionPlugin;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
+
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
+
import { createMarkBoundaryCursorDecoration } from '../mark-boundary-cursor/ui/mark-boundary-cursor-decoration';
|
|
4
|
+
import { getActiveMarksSide } from '../mark-boundary-cursor/utils/active-marks-side';
|
|
5
|
+
import { markBoundaryCursorPluginKey } from './mark-boundary-cursor-plugin-key';
|
|
6
|
+
export const createMarkBoundaryCursorPlugin = () => {
|
|
7
|
+
return new SafePlugin({
|
|
8
|
+
key: markBoundaryCursorPluginKey,
|
|
9
|
+
state: {
|
|
10
|
+
init: () => ({
|
|
11
|
+
decorations: DecorationSet.empty
|
|
12
|
+
}),
|
|
13
|
+
apply(tr, currentState) {
|
|
14
|
+
const {
|
|
15
|
+
selection,
|
|
16
|
+
storedMarks,
|
|
17
|
+
doc,
|
|
18
|
+
selectionSet,
|
|
19
|
+
storedMarksSet
|
|
20
|
+
} = tr;
|
|
21
|
+
if (!selectionSet && !storedMarksSet) {
|
|
22
|
+
return currentState;
|
|
23
|
+
}
|
|
24
|
+
const side = getActiveMarksSide({
|
|
25
|
+
selection,
|
|
26
|
+
storedMarks
|
|
27
|
+
});
|
|
28
|
+
if (!side) {
|
|
29
|
+
return {
|
|
30
|
+
decorations: DecorationSet.empty
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
decorations: DecorationSet.create(doc, [createMarkBoundaryCursorDecoration(selection.head, side)])
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
props: {
|
|
39
|
+
decorations: state => {
|
|
40
|
+
var _markBoundaryCursorPl;
|
|
41
|
+
return (_markBoundaryCursorPl = markBoundaryCursorPluginKey.getState(state)) === null || _markBoundaryCursorPl === void 0 ? void 0 : _markBoundaryCursorPl.decorations;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
2
|
+
var containerStyle = "\n\tposition: relative;\n";
|
|
3
|
+
var caretStyle = "\n\tposition: absolute;\n\theight: 100%;\n\twidth: 1px;\n\tleft: -0.5px;\n\tbottom: 0;\n\tbackground-color: ".concat("var(--ds-text, #172B4D)", ";\n");
|
|
4
|
+
var toDOM = function toDOM() {
|
|
5
|
+
var container = document.createElement('span');
|
|
6
|
+
container.className = 'ProseMirror-mark-boundary-cursor';
|
|
7
|
+
container.setAttribute('style', containerStyle);
|
|
8
|
+
var caret = document.createElement('div');
|
|
9
|
+
caret.setAttribute('style', caretStyle);
|
|
10
|
+
caret.animate([{
|
|
11
|
+
opacity: 1
|
|
12
|
+
}, {
|
|
13
|
+
opacity: 1,
|
|
14
|
+
offset: 0.5
|
|
15
|
+
}, {
|
|
16
|
+
opacity: 0,
|
|
17
|
+
offset: 0.5
|
|
18
|
+
}, {
|
|
19
|
+
opacity: 0
|
|
20
|
+
}], {
|
|
21
|
+
duration: 1000,
|
|
22
|
+
iterations: Infinity
|
|
23
|
+
});
|
|
24
|
+
container.appendChild(caret);
|
|
25
|
+
return container;
|
|
26
|
+
};
|
|
27
|
+
export var createMarkBoundaryCursorDecoration = function createMarkBoundaryCursorDecoration(pos, side) {
|
|
28
|
+
return Decoration.widget(pos, toDOM, {
|
|
29
|
+
key: 'mark-boundary-cursor',
|
|
30
|
+
side: side
|
|
31
|
+
});
|
|
32
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { isTextSelection } from '@atlaskit/editor-common/utils';
|
|
2
|
+
import { Mark } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { MarksSide } from '../marks-side';
|
|
4
|
+
import { getInlineCodeCursorSide } from './inline-code-side';
|
|
5
|
+
/**
|
|
6
|
+
* Will determine which side of the current selection the mark-boundary-cursor should
|
|
7
|
+
* favour / point towards, based on which marks are currently active and which marks
|
|
8
|
+
* are applied to the nodes to the left and right side of the current selection.
|
|
9
|
+
*
|
|
10
|
+
* @param {Selection} options.selection - The current selection.
|
|
11
|
+
* @param {readonly Mark[] | null} options.storedMarks - Current stored marks, if any.
|
|
12
|
+
* @returns {MarksSide} - The side (left -1, right 1, or none 0) the mark-boundary-cursor should favour.
|
|
13
|
+
*/
|
|
14
|
+
export function getActiveMarksSide(_ref) {
|
|
15
|
+
var _nodeBefore$marks, _nodeAfter$marks;
|
|
16
|
+
var selection = _ref.selection,
|
|
17
|
+
storedMarks = _ref.storedMarks;
|
|
18
|
+
if (!isTextSelection(selection) || !selection.empty) {
|
|
19
|
+
return MarksSide.None;
|
|
20
|
+
}
|
|
21
|
+
var _selection$$head = selection.$head,
|
|
22
|
+
nodeBefore = _selection$$head.nodeBefore,
|
|
23
|
+
nodeAfter = _selection$$head.nodeAfter;
|
|
24
|
+
var leftMarks = (_nodeBefore$marks = nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.marks) !== null && _nodeBefore$marks !== void 0 ? _nodeBefore$marks : [];
|
|
25
|
+
var rightMarks = (_nodeAfter$marks = nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.marks) !== null && _nodeAfter$marks !== void 0 ? _nodeAfter$marks : [];
|
|
26
|
+
if (Mark.sameSet(leftMarks, rightMarks)) {
|
|
27
|
+
return MarksSide.None;
|
|
28
|
+
}
|
|
29
|
+
return getInlineCodeCursorSide({
|
|
30
|
+
leftMarks: leftMarks,
|
|
31
|
+
rightMarks: rightMarks,
|
|
32
|
+
storedMarks: storedMarks
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { MarksSide } from '../marks-side';
|
|
2
|
+
/**
|
|
3
|
+
* Given the marks on the left and right side of the current selection, and the stored
|
|
4
|
+
* marks being updated during a transaction, this function returns the side (-1 = left,
|
|
5
|
+
* 1 = right) for which node the mark boundary cursor decoration should be added
|
|
6
|
+
*
|
|
7
|
+
* This is intended to correct an issue where the default cursor does not render in the
|
|
8
|
+
* expected location when navigating via arrow keys past the boundary of an inline code
|
|
9
|
+
* node
|
|
10
|
+
*/
|
|
11
|
+
export function getInlineCodeCursorSide(_ref) {
|
|
12
|
+
var leftMarks = _ref.leftMarks,
|
|
13
|
+
rightMarks = _ref.rightMarks,
|
|
14
|
+
storedMarks = _ref.storedMarks;
|
|
15
|
+
var isInlineCodeToLeft = leftMarks.some(function (mark) {
|
|
16
|
+
return mark.type.name === 'code';
|
|
17
|
+
});
|
|
18
|
+
var isInlineCodeToRight = rightMarks.some(function (mark) {
|
|
19
|
+
return mark.type.name === 'code';
|
|
20
|
+
});
|
|
21
|
+
var isInlineCodeBeingStored = !!storedMarks && (storedMarks === null || storedMarks === void 0 ? void 0 : storedMarks.some(function (mark) {
|
|
22
|
+
return mark.type.name === 'code';
|
|
23
|
+
}));
|
|
24
|
+
var isStoredMarksBeingCleared = !!storedMarks && storedMarks.length === 0;
|
|
25
|
+
|
|
26
|
+
// This condition covers two scenarios:
|
|
27
|
+
// a) When the cursor is on the left side of the inline code, and is expected to be
|
|
28
|
+
// positioned within the inline code after pressing arrow right
|
|
29
|
+
// b) When the cursor is on the right side of the inline code, and is expected to be
|
|
30
|
+
// positioned outside of the inline code after pressing arrow right
|
|
31
|
+
// in both of these cases the cursor needs to be corrected to be positioned inline
|
|
32
|
+
// with the node to the right
|
|
33
|
+
// NOTE: In editor-plugin-text-formatting there is logic on left/right key press
|
|
34
|
+
// which dispatches a transaction to update the stored marks
|
|
35
|
+
if (isInlineCodeToRight && isInlineCodeBeingStored || isInlineCodeToLeft && isStoredMarksBeingCleared) {
|
|
36
|
+
return MarksSide.Right;
|
|
37
|
+
}
|
|
38
|
+
return MarksSide.None;
|
|
39
|
+
}
|
package/dist/esm/plugin.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
1
3
|
import { selectNearNode as _selectNearNode } from './commands';
|
|
2
4
|
import gapCursorKeymapPlugin from './pm-plugins/gap-cursor-keymap';
|
|
3
5
|
import gapCursorPlugin from './pm-plugins/gap-cursor-main';
|
|
4
6
|
import { gapCursorPluginKey } from './pm-plugins/gap-cursor-plugin-key';
|
|
5
7
|
import selectionKeymapPlugin from './pm-plugins/keymap';
|
|
8
|
+
import { createMarkBoundaryCursorPlugin } from './pm-plugins/mark-boundary-cursor-main';
|
|
6
9
|
import { createPlugin } from './pm-plugins/selection-main';
|
|
7
10
|
import { selectionPluginKey } from './types';
|
|
8
11
|
var displayGapCursor = function displayGapCursor(toggle) {
|
|
@@ -62,7 +65,12 @@ export var selectionPlugin = function selectionPlugin(_ref2) {
|
|
|
62
65
|
plugin: function plugin() {
|
|
63
66
|
return gapCursorPlugin;
|
|
64
67
|
}
|
|
65
|
-
}]
|
|
68
|
+
}].concat(_toConsumableArray(fg('platform_editor_mark_boundary_cursor') ? [{
|
|
69
|
+
name: 'markBoundaryCursor',
|
|
70
|
+
plugin: function plugin() {
|
|
71
|
+
return createMarkBoundaryCursorPlugin();
|
|
72
|
+
}
|
|
73
|
+
}] : []));
|
|
66
74
|
}
|
|
67
75
|
};
|
|
68
76
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
+
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
+
import { createMarkBoundaryCursorDecoration } from '../mark-boundary-cursor/ui/mark-boundary-cursor-decoration';
|
|
4
|
+
import { getActiveMarksSide } from '../mark-boundary-cursor/utils/active-marks-side';
|
|
5
|
+
import { markBoundaryCursorPluginKey } from './mark-boundary-cursor-plugin-key';
|
|
6
|
+
export var createMarkBoundaryCursorPlugin = function createMarkBoundaryCursorPlugin() {
|
|
7
|
+
return new SafePlugin({
|
|
8
|
+
key: markBoundaryCursorPluginKey,
|
|
9
|
+
state: {
|
|
10
|
+
init: function init() {
|
|
11
|
+
return {
|
|
12
|
+
decorations: DecorationSet.empty
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
apply: function apply(tr, currentState) {
|
|
16
|
+
var selection = tr.selection,
|
|
17
|
+
storedMarks = tr.storedMarks,
|
|
18
|
+
doc = tr.doc,
|
|
19
|
+
selectionSet = tr.selectionSet,
|
|
20
|
+
storedMarksSet = tr.storedMarksSet;
|
|
21
|
+
if (!selectionSet && !storedMarksSet) {
|
|
22
|
+
return currentState;
|
|
23
|
+
}
|
|
24
|
+
var side = getActiveMarksSide({
|
|
25
|
+
selection: selection,
|
|
26
|
+
storedMarks: storedMarks
|
|
27
|
+
});
|
|
28
|
+
if (!side) {
|
|
29
|
+
return {
|
|
30
|
+
decorations: DecorationSet.empty
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
decorations: DecorationSet.create(doc, [createMarkBoundaryCursorDecoration(selection.head, side)])
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
props: {
|
|
39
|
+
decorations: function decorations(state) {
|
|
40
|
+
var _markBoundaryCursorPl;
|
|
41
|
+
return (_markBoundaryCursorPl = markBoundaryCursorPluginKey.getState(state)) === null || _markBoundaryCursorPl === void 0 ? void 0 : _markBoundaryCursorPl.decorations;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Mark } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import type { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import { MarksSide } from '../marks-side';
|
|
4
|
+
type GetActiveMarksSideOptions = {
|
|
5
|
+
selection: Selection;
|
|
6
|
+
storedMarks?: readonly Mark[] | null;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Will determine which side of the current selection the mark-boundary-cursor should
|
|
10
|
+
* favour / point towards, based on which marks are currently active and which marks
|
|
11
|
+
* are applied to the nodes to the left and right side of the current selection.
|
|
12
|
+
*
|
|
13
|
+
* @param {Selection} options.selection - The current selection.
|
|
14
|
+
* @param {readonly Mark[] | null} options.storedMarks - Current stored marks, if any.
|
|
15
|
+
* @returns {MarksSide} - The side (left -1, right 1, or none 0) the mark-boundary-cursor should favour.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getActiveMarksSide({ selection, storedMarks, }: GetActiveMarksSideOptions): MarksSide;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Mark } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import { MarksSide } from '../marks-side';
|
|
3
|
+
type GetInlineCodeCursorSideOptions = {
|
|
4
|
+
leftMarks: readonly Mark[];
|
|
5
|
+
rightMarks: readonly Mark[];
|
|
6
|
+
storedMarks?: readonly Mark[] | null;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Given the marks on the left and right side of the current selection, and the stored
|
|
10
|
+
* marks being updated during a transaction, this function returns the side (-1 = left,
|
|
11
|
+
* 1 = right) for which node the mark boundary cursor decoration should be added
|
|
12
|
+
*
|
|
13
|
+
* This is intended to correct an issue where the default cursor does not render in the
|
|
14
|
+
* expected location when navigating via arrow keys past the boundary of an inline code
|
|
15
|
+
* node
|
|
16
|
+
*/
|
|
17
|
+
export declare function getInlineCodeCursorSide({ leftMarks, rightMarks, storedMarks, }: GetInlineCodeCursorSideOptions): MarksSide;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Mark } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import type { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import { MarksSide } from '../marks-side';
|
|
4
|
+
type GetActiveMarksSideOptions = {
|
|
5
|
+
selection: Selection;
|
|
6
|
+
storedMarks?: readonly Mark[] | null;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Will determine which side of the current selection the mark-boundary-cursor should
|
|
10
|
+
* favour / point towards, based on which marks are currently active and which marks
|
|
11
|
+
* are applied to the nodes to the left and right side of the current selection.
|
|
12
|
+
*
|
|
13
|
+
* @param {Selection} options.selection - The current selection.
|
|
14
|
+
* @param {readonly Mark[] | null} options.storedMarks - Current stored marks, if any.
|
|
15
|
+
* @returns {MarksSide} - The side (left -1, right 1, or none 0) the mark-boundary-cursor should favour.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getActiveMarksSide({ selection, storedMarks, }: GetActiveMarksSideOptions): MarksSide;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type Mark } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import { MarksSide } from '../marks-side';
|
|
3
|
+
type GetInlineCodeCursorSideOptions = {
|
|
4
|
+
leftMarks: readonly Mark[];
|
|
5
|
+
rightMarks: readonly Mark[];
|
|
6
|
+
storedMarks?: readonly Mark[] | null;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Given the marks on the left and right side of the current selection, and the stored
|
|
10
|
+
* marks being updated during a transaction, this function returns the side (-1 = left,
|
|
11
|
+
* 1 = right) for which node the mark boundary cursor decoration should be added
|
|
12
|
+
*
|
|
13
|
+
* This is intended to correct an issue where the default cursor does not render in the
|
|
14
|
+
* expected location when navigating via arrow keys past the boundary of an inline code
|
|
15
|
+
* node
|
|
16
|
+
*/
|
|
17
|
+
export declare function getInlineCodeCursorSide({ leftMarks, rightMarks, storedMarks, }: GetInlineCodeCursorSideOptions): MarksSide;
|
|
18
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-selection",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "Selection plugin for @atlaskit/editor-core",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -20,11 +20,12 @@
|
|
|
20
20
|
"runReact18": false
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@atlaskit/editor-common": "^89.
|
|
24
|
-
"@atlaskit/editor-prosemirror": "
|
|
23
|
+
"@atlaskit/editor-common": "^89.2.0",
|
|
24
|
+
"@atlaskit/editor-prosemirror": "6.0.0",
|
|
25
25
|
"@atlaskit/editor-shared-styles": "^2.13.0",
|
|
26
26
|
"@atlaskit/editor-tables": "^2.8.0",
|
|
27
27
|
"@atlaskit/platform-feature-flags": "^0.3.0",
|
|
28
|
+
"@atlaskit/tokens": "^1.59.0",
|
|
28
29
|
"@babel/runtime": "^7.0.0"
|
|
29
30
|
},
|
|
30
31
|
"peerDependencies": {
|
|
@@ -78,6 +79,9 @@
|
|
|
78
79
|
"platform-feature-flags": {
|
|
79
80
|
"platform.editor.single-player-expand": {
|
|
80
81
|
"type": "boolean"
|
|
82
|
+
},
|
|
83
|
+
"platform_editor_mark_boundary_cursor": {
|
|
84
|
+
"type": "boolean"
|
|
81
85
|
}
|
|
82
86
|
}
|
|
83
87
|
}
|