@atlaskit/editor-plugin-paste-options-toolbar 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/LICENSE.md +13 -0
- package/README.md +9 -0
- package/dist/cjs/actions.js +15 -0
- package/dist/cjs/commands.js +146 -0
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/messages.js +29 -0
- package/dist/cjs/plugin.js +67 -0
- package/dist/cjs/pm-plugins/constants.js +13 -0
- package/dist/cjs/pm-plugins/main.js +66 -0
- package/dist/cjs/pm-plugins/plugin-factory.js +31 -0
- package/dist/cjs/reducer.js +50 -0
- package/dist/cjs/styles.js +12 -0
- package/dist/cjs/toolbar.js +95 -0
- package/dist/cjs/types.js +15 -0
- package/dist/cjs/ui/paste-icon.js +22 -0
- package/dist/cjs/ui/styles.js +12 -0
- package/dist/cjs/util/format-handlers.js +186 -0
- package/dist/cjs/util/index.js +45 -0
- package/dist/es2019/actions.js +9 -0
- package/dist/es2019/commands.js +134 -0
- package/dist/es2019/index.js +1 -0
- package/dist/es2019/messages.js +23 -0
- package/dist/es2019/plugin.js +64 -0
- package/dist/es2019/pm-plugins/constants.js +7 -0
- package/dist/es2019/pm-plugins/main.js +63 -0
- package/dist/es2019/pm-plugins/plugin-factory.js +23 -0
- package/dist/es2019/reducer.js +44 -0
- package/dist/es2019/styles.js +1 -0
- package/dist/es2019/toolbar.js +92 -0
- package/dist/es2019/types.js +9 -0
- package/dist/es2019/ui/paste-icon.js +14 -0
- package/dist/es2019/ui/styles.js +10 -0
- package/dist/es2019/util/format-handlers.js +196 -0
- package/dist/es2019/util/index.js +37 -0
- package/dist/esm/actions.js +9 -0
- package/dist/esm/commands.js +140 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/messages.js +23 -0
- package/dist/esm/plugin.js +61 -0
- package/dist/esm/pm-plugins/constants.js +7 -0
- package/dist/esm/pm-plugins/main.js +60 -0
- package/dist/esm/pm-plugins/plugin-factory.js +25 -0
- package/dist/esm/reducer.js +43 -0
- package/dist/esm/styles.js +1 -0
- package/dist/esm/toolbar.js +88 -0
- package/dist/esm/types.js +9 -0
- package/dist/esm/ui/paste-icon.js +16 -0
- package/dist/esm/ui/styles.js +5 -0
- package/dist/esm/util/format-handlers.js +178 -0
- package/dist/esm/util/index.js +37 -0
- package/dist/types/actions.d.ts +34 -0
- package/dist/types/commands.d.ts +16 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/messages.d.ts +22 -0
- package/dist/types/plugin.d.ts +6 -0
- package/dist/types/pm-plugins/constants.d.ts +7 -0
- package/dist/types/pm-plugins/main.d.ts +3 -0
- package/dist/types/pm-plugins/plugin-factory.d.ts +2 -0
- package/dist/types/reducer.d.ts +3 -0
- package/dist/types/styles.d.ts +1 -0
- package/dist/types/toolbar.d.ts +9 -0
- package/dist/types/types.d.ts +25 -0
- package/dist/types/ui/paste-icon.d.ts +5 -0
- package/dist/types/ui/styles.d.ts +1 -0
- package/dist/types/util/format-handlers.d.ts +9 -0
- package/dist/types/util/index.d.ts +7 -0
- package/dist/types-ts4.5/actions.d.ts +34 -0
- package/dist/types-ts4.5/commands.d.ts +16 -0
- package/dist/types-ts4.5/index.d.ts +1 -0
- package/dist/types-ts4.5/messages.d.ts +22 -0
- package/dist/types-ts4.5/plugin.d.ts +9 -0
- package/dist/types-ts4.5/pm-plugins/constants.d.ts +7 -0
- package/dist/types-ts4.5/pm-plugins/main.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/plugin-factory.d.ts +2 -0
- package/dist/types-ts4.5/reducer.d.ts +3 -0
- package/dist/types-ts4.5/styles.d.ts +1 -0
- package/dist/types-ts4.5/toolbar.d.ts +9 -0
- package/dist/types-ts4.5/types.d.ts +25 -0
- package/dist/types-ts4.5/ui/paste-icon.d.ts +5 -0
- package/dist/types-ts4.5/ui/styles.d.ts +1 -0
- package/dist/types-ts4.5/util/format-handlers.d.ts +9 -0
- package/dist/types-ts4.5/util/index.d.ts +7 -0
- package/package.json +114 -0
- package/report.api.md +46 -0
- package/styles/package.json +15 -0
- package/tmp/api-report-tmp.d.ts +19 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.formatRichText = exports.formatPlainText = exports.formatMarkdown = void 0;
|
|
7
|
+
exports.getMarkdownSlice = getMarkdownSlice;
|
|
8
|
+
exports.getRichTextSlice = void 0;
|
|
9
|
+
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
10
|
+
var _paste = require("@atlaskit/editor-common/paste");
|
|
11
|
+
var _editorMarkdownTransformer = require("@atlaskit/editor-markdown-transformer");
|
|
12
|
+
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
13
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
14
|
+
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
15
|
+
var _index = require("./index");
|
|
16
|
+
var formatMarkdown = exports.formatMarkdown = function formatMarkdown(state, pasteStartPos, plaintext) {
|
|
17
|
+
var schema = state.schema;
|
|
18
|
+
var tr = state.tr;
|
|
19
|
+
var selection = tr.selection;
|
|
20
|
+
if (pasteStartPos < 0) {
|
|
21
|
+
return tr;
|
|
22
|
+
}
|
|
23
|
+
var resolvedPasteStartPos = tr.doc.resolve(pasteStartPos);
|
|
24
|
+
var parentOffset = resolvedPasteStartPos.parentOffset;
|
|
25
|
+
if (parentOffset === 0 && resolvedPasteStartPos.depth > 0) {
|
|
26
|
+
pasteStartPos = resolvedPasteStartPos.before();
|
|
27
|
+
}
|
|
28
|
+
var markdownSlice = getMarkdownSlice(plaintext, schema, selection);
|
|
29
|
+
if (!markdownSlice) {
|
|
30
|
+
return tr;
|
|
31
|
+
}
|
|
32
|
+
var pasteEndPos = selection.$to.pos;
|
|
33
|
+
pasteSliceIntoTransactionWithSelectionAdjust({
|
|
34
|
+
tr: tr,
|
|
35
|
+
pasteStartPos: pasteStartPos,
|
|
36
|
+
pasteEndPos: pasteEndPos,
|
|
37
|
+
slice: markdownSlice
|
|
38
|
+
});
|
|
39
|
+
return tr;
|
|
40
|
+
};
|
|
41
|
+
var getRichTextSlice = exports.getRichTextSlice = function getRichTextSlice(state, pasteStartPos) {
|
|
42
|
+
var tr = state.tr;
|
|
43
|
+
var selection = tr.selection;
|
|
44
|
+
var pasteEndPos = selection.$to.pos;
|
|
45
|
+
return state.doc.slice(pasteStartPos, pasteEndPos);
|
|
46
|
+
};
|
|
47
|
+
var formatRichText = exports.formatRichText = function formatRichText(state, pasteStartPos, richTextSlice) {
|
|
48
|
+
var tr = state.tr;
|
|
49
|
+
var selection = tr.selection;
|
|
50
|
+
if (pasteStartPos < 0) {
|
|
51
|
+
return tr;
|
|
52
|
+
}
|
|
53
|
+
if (richTextSlice === _model.Slice.empty) {
|
|
54
|
+
return tr;
|
|
55
|
+
}
|
|
56
|
+
var pasteEndPos = selection.$to.pos;
|
|
57
|
+
var resolvedPasteStartPos = tr.doc.resolve(pasteStartPos);
|
|
58
|
+
var parentOffset = resolvedPasteStartPos.parentOffset;
|
|
59
|
+
if (parentOffset === 0 && resolvedPasteStartPos.depth > 0) {
|
|
60
|
+
pasteStartPos = resolvedPasteStartPos.before();
|
|
61
|
+
}
|
|
62
|
+
richTextSliceTransactionWithSelectionAdjust({
|
|
63
|
+
tr: tr,
|
|
64
|
+
pasteStartPos: pasteStartPos,
|
|
65
|
+
pasteEndPos: pasteEndPos,
|
|
66
|
+
slice: richTextSlice
|
|
67
|
+
});
|
|
68
|
+
return tr;
|
|
69
|
+
};
|
|
70
|
+
var formatPlainText = exports.formatPlainText = function formatPlainText(state, pasteStartPos, plaintext) {
|
|
71
|
+
var tr = state.tr,
|
|
72
|
+
schema = state.schema;
|
|
73
|
+
|
|
74
|
+
//not possible to create plain text slice with empty string
|
|
75
|
+
if (pasteStartPos < 0 || plaintext === '') {
|
|
76
|
+
return tr;
|
|
77
|
+
}
|
|
78
|
+
var selection = tr.selection;
|
|
79
|
+
var pasteEndPos = selection.$to.pos;
|
|
80
|
+
var resolvedPasteStartPos = tr.doc.resolve(pasteStartPos);
|
|
81
|
+
var parentOffset = resolvedPasteStartPos.parentOffset;
|
|
82
|
+
if (parentOffset === 0 && resolvedPasteStartPos.depth > 0) {
|
|
83
|
+
pasteStartPos = resolvedPasteStartPos.before();
|
|
84
|
+
}
|
|
85
|
+
var plainTextNode = schema.text(plaintext);
|
|
86
|
+
var plainTextFragment = _model.Fragment.from(schema.nodes.paragraph.createAndFill(null, plainTextNode));
|
|
87
|
+
var plainTextSlice = new _model.Slice(plainTextFragment, resolvedPasteStartPos.depth, resolvedPasteStartPos.depth);
|
|
88
|
+
pasteSliceIntoTransactionWithSelectionAdjust({
|
|
89
|
+
tr: tr,
|
|
90
|
+
pasteStartPos: pasteStartPos,
|
|
91
|
+
pasteEndPos: pasteEndPos,
|
|
92
|
+
slice: plainTextSlice
|
|
93
|
+
});
|
|
94
|
+
return tr;
|
|
95
|
+
};
|
|
96
|
+
function pasteSliceIntoTransactionWithSelectionAdjust(_ref) {
|
|
97
|
+
var tr = _ref.tr,
|
|
98
|
+
pasteStartPos = _ref.pasteStartPos,
|
|
99
|
+
pasteEndPos = _ref.pasteEndPos,
|
|
100
|
+
slice = _ref.slice;
|
|
101
|
+
tr.replaceRange(pasteStartPos, pasteEndPos, slice);
|
|
102
|
+
|
|
103
|
+
// ProseMirror doesn't give a proper way to tell us where something was inserted.
|
|
104
|
+
// However, we can know "how" it inserted something.
|
|
105
|
+
//
|
|
106
|
+
// So, instead of weird depth calculations, we can use the step produced by the transform.
|
|
107
|
+
// For instance:
|
|
108
|
+
// The `replaceStep.to and replaceStep.from`, tell us the real position
|
|
109
|
+
// where the content will be insert.
|
|
110
|
+
// Then, we can use the `tr.mapping.map` to the updated position after the replace operation
|
|
111
|
+
var replaceStep = tr.steps[0];
|
|
112
|
+
if (!(replaceStep instanceof _transform.ReplaceStep)) {
|
|
113
|
+
return tr;
|
|
114
|
+
}
|
|
115
|
+
var lastInsertNode = replaceStep.slice.content.lastChild;
|
|
116
|
+
var emptyNodeReference = lastInsertNode === null || lastInsertNode === void 0 ? void 0 : lastInsertNode.type.createAndFill();
|
|
117
|
+
var isLastNodeEmpty = (emptyNodeReference === null || emptyNodeReference === void 0 ? void 0 : emptyNodeReference.nodeSize) === (lastInsertNode === null || lastInsertNode === void 0 ? void 0 : lastInsertNode.nodeSize);
|
|
118
|
+
var isStepSplitingTarget = !(lastInsertNode !== null && lastInsertNode !== void 0 && lastInsertNode.isLeaf) && isLastNodeEmpty;
|
|
119
|
+
var $nextHead = tr.doc.resolve(tr.mapping.map(replaceStep.to));
|
|
120
|
+
var $nextPosition = isStepSplitingTarget && $nextHead.depth > 0 ? tr.doc.resolve($nextHead.before()) : $nextHead;
|
|
121
|
+
|
|
122
|
+
// The findFrom will make search for both: TextSelection and NodeSelections.
|
|
123
|
+
var nextSelection = _state.Selection.findFrom($nextPosition, -1);
|
|
124
|
+
if (nextSelection) {
|
|
125
|
+
tr.setSelection(nextSelection);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function richTextSliceTransactionWithSelectionAdjust(_ref2) {
|
|
129
|
+
var tr = _ref2.tr,
|
|
130
|
+
pasteStartPos = _ref2.pasteStartPos,
|
|
131
|
+
pasteEndPos = _ref2.pasteEndPos,
|
|
132
|
+
slice = _ref2.slice;
|
|
133
|
+
tr.replaceRange(pasteStartPos, pasteEndPos, slice);
|
|
134
|
+
|
|
135
|
+
// ProseMirror doesn't give a proper way to tell us where something was inserted.
|
|
136
|
+
// However, we can know "how" it inserted something.
|
|
137
|
+
//
|
|
138
|
+
// So, instead of weird depth calculations, we can use the step produced by the transform.
|
|
139
|
+
// For instance:
|
|
140
|
+
// The `replaceStep.to and replaceStep.from`, tell us the real position
|
|
141
|
+
// where the content will be insert.
|
|
142
|
+
// Then, we can use the `tr.mapping.map` to the updated position after the replace operation
|
|
143
|
+
var replaceStep = tr.steps[0];
|
|
144
|
+
if (!(replaceStep instanceof _transform.ReplaceStep)) {
|
|
145
|
+
return tr;
|
|
146
|
+
}
|
|
147
|
+
var nextPosition = tr.mapping.map(replaceStep.to);
|
|
148
|
+
|
|
149
|
+
// The findFrom will make search for both: TextSelection and NodeSelections.
|
|
150
|
+
var nextSelection = _state.Selection.findFrom(tr.doc.resolve(Math.min(nextPosition, tr.doc.content.size)), -1);
|
|
151
|
+
if (nextSelection) {
|
|
152
|
+
tr.setSelection(nextSelection);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function getMarkdownSlice(text, schema, selection) {
|
|
156
|
+
var targetOpenStartNode = selection.$from.parent;
|
|
157
|
+
var targetOpenEndNode = selection.$to.parent;
|
|
158
|
+
try {
|
|
159
|
+
var _doc$content$firstChi, _doc$content$lastChil;
|
|
160
|
+
var textInput = text;
|
|
161
|
+
var textSplitByCodeBlock = textInput.split(/```/);
|
|
162
|
+
for (var i = 0; i < textSplitByCodeBlock.length; i++) {
|
|
163
|
+
if (i % 2 === 0) {
|
|
164
|
+
textSplitByCodeBlock[i] = textSplitByCodeBlock[i].replace(/\\/g, '\\\\');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
textInput = textSplitByCodeBlock.join('```');
|
|
168
|
+
var atlassianMarkDownParser = new _editorMarkdownTransformer.MarkdownTransformer(schema, _paste.md);
|
|
169
|
+
var doc = atlassianMarkDownParser.parse((0, _index.escapeLinks)(textInput));
|
|
170
|
+
if (!doc || !doc.content) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
var canMergeOpenStart = targetOpenStartNode.type === ((_doc$content$firstChi = doc.content.firstChild) === null || _doc$content$firstChi === void 0 ? void 0 : _doc$content$firstChi.type);
|
|
174
|
+
var canMergeOpenEnd = targetOpenEndNode.type === ((_doc$content$lastChil = doc.content.lastChild) === null || _doc$content$lastChil === void 0 ? void 0 : _doc$content$lastChil.type);
|
|
175
|
+
var $start = _state.Selection.atStart(doc).$from;
|
|
176
|
+
var $end = _state.Selection.atEnd(doc).$from;
|
|
177
|
+
var openStart = canMergeOpenStart ? $start.depth : 0;
|
|
178
|
+
var openEnd = canMergeOpenEnd ? $end.depth : 0;
|
|
179
|
+
return new _model.Slice(doc.content, openStart, openEnd);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
(0, _monitoring.logException)(error, {
|
|
182
|
+
location: 'editor-plugin-paste-options-toolbar/util'
|
|
183
|
+
});
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.escapeLinks = escapeLinks;
|
|
7
|
+
exports.hasRuleNode = exports.hasMediaNode = exports.hasLinkMark = void 0;
|
|
8
|
+
exports.isPastedFromFabricEditor = isPastedFromFabricEditor;
|
|
9
|
+
function isPastedFromFabricEditor(pastedFrom) {
|
|
10
|
+
return pastedFrom === 'fabric-editor';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// @see https://product-fabric.atlassian.net/browse/ED-3159
|
|
14
|
+
// @see https://github.com/markdown-it/markdown-it/issues/38
|
|
15
|
+
function escapeLinks(text) {
|
|
16
|
+
return text.replace(/(\[([^\]]+)\]\()?((https?|ftp|jamfselfservice):\/\/[^\s"'>]+)/g, function (str) {
|
|
17
|
+
return str.match(/^(https?|ftp|jamfselfservice):\/\/[^\s"'>]+$/) ? "<".concat(str, ">") : str;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
var hasMediaNode = exports.hasMediaNode = function hasMediaNode(slice) {
|
|
21
|
+
if (!slice) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
var hasMedia = false;
|
|
25
|
+
slice.content.descendants(function (node) {
|
|
26
|
+
if (['media', 'mediaInline', 'mediaGroup', 'mediaSingle'].includes(node.type.name)) {
|
|
27
|
+
hasMedia = true;
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
});
|
|
32
|
+
return hasMedia;
|
|
33
|
+
};
|
|
34
|
+
var hasRuleNode = exports.hasRuleNode = function hasRuleNode(slice, schema) {
|
|
35
|
+
var hasRuleNode = false;
|
|
36
|
+
slice.content.nodesBetween(0, slice.content.size, function (node, start) {
|
|
37
|
+
if (node.type === schema.nodes.rule) {
|
|
38
|
+
hasRuleNode = true;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return hasRuleNode;
|
|
42
|
+
};
|
|
43
|
+
var hasLinkMark = exports.hasLinkMark = function hasLinkMark(state, pasteStartPos, pasteEndPos) {
|
|
44
|
+
return state.doc.rangeHasMark(pasteStartPos, pasteEndPos, state.schema.marks.link);
|
|
45
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export let PastePluginActionTypes = /*#__PURE__*/function (PastePluginActionTypes) {
|
|
2
|
+
PastePluginActionTypes["START_TRACKING_PASTED_MACRO_POSITIONS"] = "START_TRACKING_PASTED_MACRO_POSITIONS";
|
|
3
|
+
PastePluginActionTypes["STOP_TRACKING_PASTED_MACRO_POSITIONS"] = "STOP_TRACKING_PASTED_MACRO_POSITIONS";
|
|
4
|
+
PastePluginActionTypes["SHOW_PASTE_OPTIONS"] = "SHOW_PASTE_OPTIONS";
|
|
5
|
+
PastePluginActionTypes["HIDE_PASTE_OPTIONS"] = "HIDE_PASTE_OPTIONS";
|
|
6
|
+
PastePluginActionTypes["HIGHLIGHT_CONTENT"] = "HIGHLIGHT_CONTENT";
|
|
7
|
+
PastePluginActionTypes["CHANGE_FORMAT"] = "CHANGE_FORMAT";
|
|
8
|
+
return PastePluginActionTypes;
|
|
9
|
+
}({});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD, PasteContents, PasteTypes } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { withAnalytics } from '@atlaskit/editor-common/editor-analytics';
|
|
3
|
+
import { PastePluginActionTypes as ActionTypes } from './actions';
|
|
4
|
+
import { createCommand } from './pm-plugins/plugin-factory';
|
|
5
|
+
import { pasteOptionsPluginKey, ToolbarDropdownOption } from './types';
|
|
6
|
+
import { formatMarkdown, formatPlainText, formatRichText } from './util/format-handlers';
|
|
7
|
+
export const showToolbar = (lastContentPasted, selectedOption) => {
|
|
8
|
+
const commandAction = editorState => {
|
|
9
|
+
return {
|
|
10
|
+
type: ActionTypes.SHOW_PASTE_OPTIONS,
|
|
11
|
+
data: {
|
|
12
|
+
selectedOption,
|
|
13
|
+
plaintext: lastContentPasted.text,
|
|
14
|
+
isPlainText: lastContentPasted.isPlainText,
|
|
15
|
+
richTextSlice: lastContentPasted.pastedSlice,
|
|
16
|
+
pasteStartPos: lastContentPasted.pasteStartPos,
|
|
17
|
+
pasteEndPos: lastContentPasted.pasteEndPos
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
return createCommand(commandAction);
|
|
22
|
+
};
|
|
23
|
+
export const changeToPlainText = (pasteStartPos, plaintext) => {
|
|
24
|
+
const plaintextTransformer = (tr, state) => {
|
|
25
|
+
return formatPlainText(state, pasteStartPos, plaintext);
|
|
26
|
+
};
|
|
27
|
+
const commandAction = editorState => {
|
|
28
|
+
return {
|
|
29
|
+
type: ActionTypes.CHANGE_FORMAT,
|
|
30
|
+
data: {
|
|
31
|
+
selectedOption: ToolbarDropdownOption.PlainText
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
return createCommand(commandAction, plaintextTransformer);
|
|
36
|
+
};
|
|
37
|
+
export const changeToPlainTextWithAnalytics = (editorAnalyticsAPI, sliceSize) => (pasteStartPos, plaintext) => {
|
|
38
|
+
return withAnalytics(editorAnalyticsAPI, {
|
|
39
|
+
action: ACTION.PASTED,
|
|
40
|
+
actionSubject: ACTION_SUBJECT.DOCUMENT,
|
|
41
|
+
eventType: EVENT_TYPE.TRACK,
|
|
42
|
+
attributes: {
|
|
43
|
+
inputMethod: INPUT_METHOD.TOOLBAR,
|
|
44
|
+
type: PasteTypes.plain,
|
|
45
|
+
content: PasteContents.text,
|
|
46
|
+
pasteSize: sliceSize
|
|
47
|
+
}
|
|
48
|
+
})(changeToPlainText(pasteStartPos, plaintext));
|
|
49
|
+
};
|
|
50
|
+
export const dropdownClickHandler = () => {
|
|
51
|
+
return highlightContent();
|
|
52
|
+
};
|
|
53
|
+
export const changeToRichText = pasteStartPos => {
|
|
54
|
+
const transformer = (tr, state) => {
|
|
55
|
+
const pastePluginState = pasteOptionsPluginKey.getState(state);
|
|
56
|
+
return formatRichText(state, pasteStartPos, pastePluginState.richTextSlice);
|
|
57
|
+
};
|
|
58
|
+
const commandAction = editorState => {
|
|
59
|
+
return {
|
|
60
|
+
type: ActionTypes.CHANGE_FORMAT,
|
|
61
|
+
data: {
|
|
62
|
+
selectedOption: ToolbarDropdownOption.RichText
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
return createCommand(commandAction, transformer);
|
|
67
|
+
};
|
|
68
|
+
export const changeToRichTextWithAnalytics = editorAnalyticsAPI => pasteStartPos => {
|
|
69
|
+
const payloadCallback = state => {
|
|
70
|
+
var _pastePluginState$ric;
|
|
71
|
+
const pastePluginState = pasteOptionsPluginKey.getState(state);
|
|
72
|
+
return {
|
|
73
|
+
action: ACTION.PASTED,
|
|
74
|
+
actionSubject: ACTION_SUBJECT.DOCUMENT,
|
|
75
|
+
eventType: EVENT_TYPE.TRACK,
|
|
76
|
+
attributes: {
|
|
77
|
+
inputMethod: INPUT_METHOD.TOOLBAR,
|
|
78
|
+
type: PasteTypes.richText,
|
|
79
|
+
content: PasteContents.text,
|
|
80
|
+
pasteSize: ((_pastePluginState$ric = pastePluginState.richTextSlice) === null || _pastePluginState$ric === void 0 ? void 0 : _pastePluginState$ric.size) || 0
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
return withAnalytics(editorAnalyticsAPI, payloadCallback)(changeToRichText(pasteStartPos));
|
|
85
|
+
};
|
|
86
|
+
export const changeToMarkDown = (pasteStartPos, plaintext) => {
|
|
87
|
+
const markdownTransformer = (tr, state) => {
|
|
88
|
+
return formatMarkdown(state, pasteStartPos, plaintext);
|
|
89
|
+
};
|
|
90
|
+
const commandAction = editorState => {
|
|
91
|
+
return {
|
|
92
|
+
type: ActionTypes.CHANGE_FORMAT,
|
|
93
|
+
data: {
|
|
94
|
+
selectedOption: ToolbarDropdownOption.Markdown
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
return createCommand(commandAction, markdownTransformer);
|
|
99
|
+
};
|
|
100
|
+
export const changeToMarkdownWithAnalytics = (editorAnalyticsAPI, sliceSize) => (pasteStartPos, plaintext) => {
|
|
101
|
+
return withAnalytics(editorAnalyticsAPI, {
|
|
102
|
+
action: ACTION.PASTED,
|
|
103
|
+
actionSubject: ACTION_SUBJECT.DOCUMENT,
|
|
104
|
+
eventType: EVENT_TYPE.TRACK,
|
|
105
|
+
attributes: {
|
|
106
|
+
inputMethod: INPUT_METHOD.TOOLBAR,
|
|
107
|
+
type: PasteTypes.markdown,
|
|
108
|
+
content: PasteContents.text,
|
|
109
|
+
pasteSize: sliceSize
|
|
110
|
+
}
|
|
111
|
+
})(changeToMarkDown(pasteStartPos, plaintext));
|
|
112
|
+
};
|
|
113
|
+
export const highlightContent = () => {
|
|
114
|
+
const commandAction = editorState => {
|
|
115
|
+
return {
|
|
116
|
+
type: ActionTypes.HIGHLIGHT_CONTENT
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
return createCommand(commandAction);
|
|
120
|
+
};
|
|
121
|
+
export const hideToolbar = () => {
|
|
122
|
+
const commandAction = editorState => {
|
|
123
|
+
return {
|
|
124
|
+
type: ActionTypes.HIDE_PASTE_OPTIONS
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
return createCommand(commandAction);
|
|
128
|
+
};
|
|
129
|
+
export const checkAndHideToolbar = view => {
|
|
130
|
+
const pluginState = pasteOptionsPluginKey.getState(view.state);
|
|
131
|
+
if (pluginState.showToolbar) {
|
|
132
|
+
hideToolbar()(view.state, view.dispatch);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { pasteOptionsToolbarPlugin } from './plugin';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl-next';
|
|
2
|
+
export const messages = defineMessages({
|
|
3
|
+
pasteOptions: {
|
|
4
|
+
id: 'fabric.editor.pasteOptions',
|
|
5
|
+
defaultMessage: 'Paste options floating controls',
|
|
6
|
+
description: 'Opens a menu with additional paste options'
|
|
7
|
+
},
|
|
8
|
+
plainText: {
|
|
9
|
+
id: 'fabric.editor.plainText',
|
|
10
|
+
defaultMessage: 'Use plain text',
|
|
11
|
+
description: 'Converts pasted text into plain text'
|
|
12
|
+
},
|
|
13
|
+
markdown: {
|
|
14
|
+
id: 'fabric.editor.useMarkdown',
|
|
15
|
+
defaultMessage: 'Use Markdown',
|
|
16
|
+
description: 'Converts pasted text into Markdown'
|
|
17
|
+
},
|
|
18
|
+
richText: {
|
|
19
|
+
id: 'fabric.editor.richText',
|
|
20
|
+
defaultMessage: 'Use rich text',
|
|
21
|
+
description: 'Converts pasted text into Rich text'
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
|
|
3
|
+
import { hideToolbar, showToolbar } from './commands';
|
|
4
|
+
import { createPlugin } from './pm-plugins/main';
|
|
5
|
+
import { buildToolbar, isToolbarVisible } from './toolbar';
|
|
6
|
+
import { pasteOptionsPluginKey, ToolbarDropdownOption } from './types';
|
|
7
|
+
export const pasteOptionsToolbarPlugin = ({
|
|
8
|
+
config,
|
|
9
|
+
api
|
|
10
|
+
}) => {
|
|
11
|
+
var _api$analytics;
|
|
12
|
+
const editorAnalyticsAPI = api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions;
|
|
13
|
+
return {
|
|
14
|
+
name: 'pasteOptionsToolbarPlugin',
|
|
15
|
+
pmPlugins() {
|
|
16
|
+
return [{
|
|
17
|
+
name: 'pasteOptionsToolbarPlugin',
|
|
18
|
+
plugin: ({
|
|
19
|
+
dispatch
|
|
20
|
+
}) => createPlugin(dispatch)
|
|
21
|
+
}];
|
|
22
|
+
},
|
|
23
|
+
pluginsOptions: {
|
|
24
|
+
floatingToolbar(state, intl) {
|
|
25
|
+
const pastePluginState = pasteOptionsPluginKey.getState(state);
|
|
26
|
+
const {
|
|
27
|
+
showToolbar,
|
|
28
|
+
pasteStartPos,
|
|
29
|
+
plaintext
|
|
30
|
+
} = pastePluginState || {};
|
|
31
|
+
if (showToolbar) {
|
|
32
|
+
return buildToolbar(state, pasteStartPos, plaintext, intl, editorAnalyticsAPI);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
usePluginHook({
|
|
38
|
+
editorView
|
|
39
|
+
}) {
|
|
40
|
+
const {
|
|
41
|
+
pasteState
|
|
42
|
+
} = useSharedPluginState(api, ['paste']);
|
|
43
|
+
const lastContentPasted = pasteState === null || pasteState === void 0 ? void 0 : pasteState.lastContentPasted;
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!lastContentPasted) {
|
|
46
|
+
hideToolbar()(editorView.state, editorView.dispatch);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let selectedOption = ToolbarDropdownOption.None;
|
|
50
|
+
if (!lastContentPasted.isPlainText) {
|
|
51
|
+
selectedOption = ToolbarDropdownOption.RichText;
|
|
52
|
+
} else if (lastContentPasted.isShiftPressed) {
|
|
53
|
+
selectedOption = ToolbarDropdownOption.PlainText;
|
|
54
|
+
} else {
|
|
55
|
+
selectedOption = ToolbarDropdownOption.Markdown;
|
|
56
|
+
}
|
|
57
|
+
if (!isToolbarVisible(editorView.state, lastContentPasted)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
showToolbar(lastContentPasted, selectedOption)(editorView.state, editorView.dispatch);
|
|
61
|
+
}, [lastContentPasted, editorView]);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const PASTE_TOOLBAR_CLASS = 'ak-editor-paste-toolbar';
|
|
2
|
+
export const PASTE_TOOLBAR_MENU_ID = 'ak-editor-paste-toolbar-item-dropdownList';
|
|
3
|
+
export const TEXT_HIGHLIGHT_CLASS = 'text-highlight';
|
|
4
|
+
export const PASTE_HIGHLIGHT_DECORATION_KEY = 'paste-highlight-decoration-key';
|
|
5
|
+
export const PASTE_TOOLBAR_ITEM_CLASS = 'ak-editor-paste-toolbar-item';
|
|
6
|
+
export const EDITOR_WRAPPER_CLASS = 'akEditor';
|
|
7
|
+
export const PASTE_OPTIONS_TEST_ID = 'paste-options-testid';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
+
import { Slice } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
4
|
+
import { checkAndHideToolbar } from '../commands';
|
|
5
|
+
import { pasteOptionsPluginKey, ToolbarDropdownOption } from '../types';
|
|
6
|
+
import { PASTE_HIGHLIGHT_DECORATION_KEY, TEXT_HIGHLIGHT_CLASS } from './constants';
|
|
7
|
+
import { createPluginState } from './plugin-factory';
|
|
8
|
+
export function createPlugin(dispatch) {
|
|
9
|
+
return new SafePlugin({
|
|
10
|
+
key: pasteOptionsPluginKey,
|
|
11
|
+
state: createPluginState(dispatch, {
|
|
12
|
+
showToolbar: false,
|
|
13
|
+
pasteStartPos: 0,
|
|
14
|
+
pasteEndPos: 0,
|
|
15
|
+
plaintext: '',
|
|
16
|
+
isPlainText: false,
|
|
17
|
+
highlightContent: false,
|
|
18
|
+
highlightDecorationSet: DecorationSet.empty,
|
|
19
|
+
richTextSlice: Slice.empty,
|
|
20
|
+
selectedOption: ToolbarDropdownOption.None
|
|
21
|
+
}),
|
|
22
|
+
view(editorView) {
|
|
23
|
+
return {
|
|
24
|
+
update(view, prevState) {
|
|
25
|
+
return prevState;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
props: {
|
|
30
|
+
handleDOMEvents: {
|
|
31
|
+
// Hide toolbar when clicked outside the editor
|
|
32
|
+
blur: checkAndHideToolbar,
|
|
33
|
+
// Hide toolbar when clicked anywhere within the editor, tr.getMeta('pointer') does not work if clicked on the same line after pasting so relying on mousedown event
|
|
34
|
+
mousedown: checkAndHideToolbar
|
|
35
|
+
},
|
|
36
|
+
handleKeyDown: view => {
|
|
37
|
+
checkAndHideToolbar(view);
|
|
38
|
+
return false;
|
|
39
|
+
},
|
|
40
|
+
decorations: state => {
|
|
41
|
+
var _pasteOptionsPluginKe, _pasteOptionsPluginKe2;
|
|
42
|
+
const {
|
|
43
|
+
highlightContent,
|
|
44
|
+
pasteStartPos
|
|
45
|
+
} = pasteOptionsPluginKey.getState(state) || {};
|
|
46
|
+
let decorationSet = (_pasteOptionsPluginKe = (_pasteOptionsPluginKe2 = pasteOptionsPluginKey.getState(state)) === null || _pasteOptionsPluginKe2 === void 0 ? void 0 : _pasteOptionsPluginKe2.highlightDecorationSet) !== null && _pasteOptionsPluginKe !== void 0 ? _pasteOptionsPluginKe : DecorationSet.empty;
|
|
47
|
+
if (!highlightContent) {
|
|
48
|
+
return decorationSet;
|
|
49
|
+
}
|
|
50
|
+
const {
|
|
51
|
+
selection
|
|
52
|
+
} = state.tr;
|
|
53
|
+
const pasteEndPos = selection.$anchor.pos;
|
|
54
|
+
const highlightDecoration = Decoration.inline(pasteStartPos, pasteEndPos, {
|
|
55
|
+
class: TEXT_HIGHLIGHT_CLASS
|
|
56
|
+
}, {
|
|
57
|
+
key: PASTE_HIGHLIGHT_DECORATION_KEY
|
|
58
|
+
});
|
|
59
|
+
return decorationSet.add(state.doc, [highlightDecoration]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { pluginFactory } from '@atlaskit/editor-common/utils';
|
|
2
|
+
import { reducer } from '../reducer';
|
|
3
|
+
import { pasteOptionsPluginKey } from '../types';
|
|
4
|
+
export const {
|
|
5
|
+
createPluginState,
|
|
6
|
+
createCommand,
|
|
7
|
+
getPluginState
|
|
8
|
+
} = pluginFactory(pasteOptionsPluginKey, reducer, {
|
|
9
|
+
mapping: (tr, pluginState) => {
|
|
10
|
+
return pluginState;
|
|
11
|
+
},
|
|
12
|
+
onSelectionChanged: (tr, pluginState) => {
|
|
13
|
+
// Detect click outside the editor
|
|
14
|
+
if (tr.getMeta('outsideProsemirrorEditorClicked')) {
|
|
15
|
+
return {
|
|
16
|
+
...pluginState,
|
|
17
|
+
showToolbar: false,
|
|
18
|
+
highlightContent: false
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return pluginState;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PastePluginActionTypes as ActionTypes } from './actions';
|
|
2
|
+
export const reducer = (state, action) => {
|
|
3
|
+
switch (action.type) {
|
|
4
|
+
case ActionTypes.SHOW_PASTE_OPTIONS:
|
|
5
|
+
{
|
|
6
|
+
return {
|
|
7
|
+
...state,
|
|
8
|
+
showToolbar: true,
|
|
9
|
+
highlightContent: false,
|
|
10
|
+
isPlainText: action.data.isPlainText,
|
|
11
|
+
plaintext: action.data.plaintext,
|
|
12
|
+
selectedOption: action.data.selectedOption,
|
|
13
|
+
richTextSlice: action.data.richTextSlice,
|
|
14
|
+
pasteStartPos: action.data.pasteStartPos,
|
|
15
|
+
pasteEndPos: action.data.pasteEndPos
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
case ActionTypes.HIDE_PASTE_OPTIONS:
|
|
19
|
+
{
|
|
20
|
+
return {
|
|
21
|
+
...state,
|
|
22
|
+
highlightContent: false,
|
|
23
|
+
showToolbar: false
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
case ActionTypes.HIGHLIGHT_CONTENT:
|
|
27
|
+
{
|
|
28
|
+
return {
|
|
29
|
+
...state,
|
|
30
|
+
highlightContent: true
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
case ActionTypes.CHANGE_FORMAT:
|
|
34
|
+
{
|
|
35
|
+
return {
|
|
36
|
+
...state,
|
|
37
|
+
highlightContent: true,
|
|
38
|
+
selectedOption: action.data.selectedOption
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
default:
|
|
42
|
+
return state;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { textHighlightStyle } from './ui/styles';
|