@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.
Files changed (87) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE.md +13 -0
  3. package/README.md +9 -0
  4. package/dist/cjs/actions.js +15 -0
  5. package/dist/cjs/commands.js +146 -0
  6. package/dist/cjs/index.js +12 -0
  7. package/dist/cjs/messages.js +29 -0
  8. package/dist/cjs/plugin.js +67 -0
  9. package/dist/cjs/pm-plugins/constants.js +13 -0
  10. package/dist/cjs/pm-plugins/main.js +66 -0
  11. package/dist/cjs/pm-plugins/plugin-factory.js +31 -0
  12. package/dist/cjs/reducer.js +50 -0
  13. package/dist/cjs/styles.js +12 -0
  14. package/dist/cjs/toolbar.js +95 -0
  15. package/dist/cjs/types.js +15 -0
  16. package/dist/cjs/ui/paste-icon.js +22 -0
  17. package/dist/cjs/ui/styles.js +12 -0
  18. package/dist/cjs/util/format-handlers.js +186 -0
  19. package/dist/cjs/util/index.js +45 -0
  20. package/dist/es2019/actions.js +9 -0
  21. package/dist/es2019/commands.js +134 -0
  22. package/dist/es2019/index.js +1 -0
  23. package/dist/es2019/messages.js +23 -0
  24. package/dist/es2019/plugin.js +64 -0
  25. package/dist/es2019/pm-plugins/constants.js +7 -0
  26. package/dist/es2019/pm-plugins/main.js +63 -0
  27. package/dist/es2019/pm-plugins/plugin-factory.js +23 -0
  28. package/dist/es2019/reducer.js +44 -0
  29. package/dist/es2019/styles.js +1 -0
  30. package/dist/es2019/toolbar.js +92 -0
  31. package/dist/es2019/types.js +9 -0
  32. package/dist/es2019/ui/paste-icon.js +14 -0
  33. package/dist/es2019/ui/styles.js +10 -0
  34. package/dist/es2019/util/format-handlers.js +196 -0
  35. package/dist/es2019/util/index.js +37 -0
  36. package/dist/esm/actions.js +9 -0
  37. package/dist/esm/commands.js +140 -0
  38. package/dist/esm/index.js +1 -0
  39. package/dist/esm/messages.js +23 -0
  40. package/dist/esm/plugin.js +61 -0
  41. package/dist/esm/pm-plugins/constants.js +7 -0
  42. package/dist/esm/pm-plugins/main.js +60 -0
  43. package/dist/esm/pm-plugins/plugin-factory.js +25 -0
  44. package/dist/esm/reducer.js +43 -0
  45. package/dist/esm/styles.js +1 -0
  46. package/dist/esm/toolbar.js +88 -0
  47. package/dist/esm/types.js +9 -0
  48. package/dist/esm/ui/paste-icon.js +16 -0
  49. package/dist/esm/ui/styles.js +5 -0
  50. package/dist/esm/util/format-handlers.js +178 -0
  51. package/dist/esm/util/index.js +37 -0
  52. package/dist/types/actions.d.ts +34 -0
  53. package/dist/types/commands.d.ts +16 -0
  54. package/dist/types/index.d.ts +1 -0
  55. package/dist/types/messages.d.ts +22 -0
  56. package/dist/types/plugin.d.ts +6 -0
  57. package/dist/types/pm-plugins/constants.d.ts +7 -0
  58. package/dist/types/pm-plugins/main.d.ts +3 -0
  59. package/dist/types/pm-plugins/plugin-factory.d.ts +2 -0
  60. package/dist/types/reducer.d.ts +3 -0
  61. package/dist/types/styles.d.ts +1 -0
  62. package/dist/types/toolbar.d.ts +9 -0
  63. package/dist/types/types.d.ts +25 -0
  64. package/dist/types/ui/paste-icon.d.ts +5 -0
  65. package/dist/types/ui/styles.d.ts +1 -0
  66. package/dist/types/util/format-handlers.d.ts +9 -0
  67. package/dist/types/util/index.d.ts +7 -0
  68. package/dist/types-ts4.5/actions.d.ts +34 -0
  69. package/dist/types-ts4.5/commands.d.ts +16 -0
  70. package/dist/types-ts4.5/index.d.ts +1 -0
  71. package/dist/types-ts4.5/messages.d.ts +22 -0
  72. package/dist/types-ts4.5/plugin.d.ts +9 -0
  73. package/dist/types-ts4.5/pm-plugins/constants.d.ts +7 -0
  74. package/dist/types-ts4.5/pm-plugins/main.d.ts +3 -0
  75. package/dist/types-ts4.5/pm-plugins/plugin-factory.d.ts +2 -0
  76. package/dist/types-ts4.5/reducer.d.ts +3 -0
  77. package/dist/types-ts4.5/styles.d.ts +1 -0
  78. package/dist/types-ts4.5/toolbar.d.ts +9 -0
  79. package/dist/types-ts4.5/types.d.ts +25 -0
  80. package/dist/types-ts4.5/ui/paste-icon.d.ts +5 -0
  81. package/dist/types-ts4.5/ui/styles.d.ts +1 -0
  82. package/dist/types-ts4.5/util/format-handlers.d.ts +9 -0
  83. package/dist/types-ts4.5/util/index.d.ts +7 -0
  84. package/package.json +114 -0
  85. package/report.api.md +46 -0
  86. package/styles/package.json +15 -0
  87. 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';