@atlaskit/editor-plugin-paste-options-toolbar 11.1.0 → 11.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 +11 -0
- package/dist/cjs/pasteOptionsToolbarPlugin.js +36 -0
- package/dist/cjs/ui/on-paste-actions-menu/exposure-v2.js +4 -32
- package/dist/cjs/ui/utils/paste-menu-rules/hasTableNode.js +20 -0
- package/dist/cjs/ui/utils/paste-menu-rules/isNotProse.js +29 -0
- package/dist/cjs/ui/utils/paste-menu-rules/rules.js +102 -0
- package/dist/cjs/ui/utils/paste-menu-rules/types.js +5 -0
- package/dist/es2019/pasteOptionsToolbarPlugin.js +34 -0
- package/dist/es2019/ui/on-paste-actions-menu/exposure-v2.js +2 -30
- package/dist/es2019/ui/utils/paste-menu-rules/hasTableNode.js +14 -0
- package/dist/es2019/ui/utils/paste-menu-rules/isNotProse.js +23 -0
- package/dist/es2019/ui/utils/paste-menu-rules/rules.js +66 -0
- package/dist/es2019/ui/utils/paste-menu-rules/types.js +1 -0
- package/dist/esm/pasteOptionsToolbarPlugin.js +36 -0
- package/dist/esm/ui/on-paste-actions-menu/exposure-v2.js +2 -30
- package/dist/esm/ui/utils/paste-menu-rules/hasTableNode.js +14 -0
- package/dist/esm/ui/utils/paste-menu-rules/isNotProse.js +23 -0
- package/dist/esm/ui/utils/paste-menu-rules/rules.js +96 -0
- package/dist/esm/ui/utils/paste-menu-rules/types.js +1 -0
- package/dist/types/pasteOptionsToolbarPluginType.d.ts +14 -0
- package/dist/types/ui/utils/paste-menu-rules/hasTableNode.d.ts +2 -0
- package/dist/types/ui/utils/paste-menu-rules/isNotProse.d.ts +1 -0
- package/dist/types/ui/utils/paste-menu-rules/rules.d.ts +8 -0
- package/dist/types/ui/utils/paste-menu-rules/types.d.ts +73 -0
- package/dist/types-ts4.5/pasteOptionsToolbarPluginType.d.ts +14 -0
- package/dist/types-ts4.5/ui/utils/paste-menu-rules/hasTableNode.d.ts +2 -0
- package/dist/types-ts4.5/ui/utils/paste-menu-rules/isNotProse.d.ts +1 -0
- package/dist/types-ts4.5/ui/utils/paste-menu-rules/rules.d.ts +8 -0
- package/dist/types-ts4.5/ui/utils/paste-menu-rules/types.d.ts +73 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-paste-options-toolbar
|
|
2
2
|
|
|
3
|
+
## 11.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`bbc429637a494`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/bbc429637a494) -
|
|
8
|
+
[EDITOR-6941] Add way to fetch rules for people to compose rules simply per button
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- Updated dependencies
|
|
13
|
+
|
|
3
14
|
## 11.1.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
|
@@ -17,6 +17,7 @@ var _exposureV = require("./ui/on-paste-actions-menu/exposure-v2");
|
|
|
17
17
|
var _PasteActionsMenu = require("./ui/on-paste-actions-menu/PasteActionsMenu");
|
|
18
18
|
var _PasteMenuComponents = require("./ui/on-paste-actions-menu/PasteMenuComponents");
|
|
19
19
|
var _toolbar = require("./ui/toolbar");
|
|
20
|
+
var _rules = require("./ui/utils/paste-menu-rules/rules");
|
|
20
21
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
21
22
|
var pasteOptionsToolbarPlugin = exports.pasteOptionsToolbarPlugin = function pasteOptionsToolbarPlugin(_ref) {
|
|
22
23
|
var _api$analytics;
|
|
@@ -31,6 +32,41 @@ var pasteOptionsToolbarPlugin = exports.pasteOptionsToolbarPlugin = function pas
|
|
|
31
32
|
}
|
|
32
33
|
return {
|
|
33
34
|
name: 'pasteOptionsToolbarPlugin',
|
|
35
|
+
actions: {
|
|
36
|
+
getPasteMenuRules: function getPasteMenuRules() {
|
|
37
|
+
var getContext = function getContext() {
|
|
38
|
+
var _api$pasteOptionsTool, _api$paste;
|
|
39
|
+
var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool === void 0 ? void 0 : _api$pasteOptionsTool.sharedState.currentState();
|
|
40
|
+
var pasteState = api === null || api === void 0 || (_api$paste = api.paste) === null || _api$paste === void 0 ? void 0 : _api$paste.sharedState.currentState();
|
|
41
|
+
return {
|
|
42
|
+
getPlaintextLength: function getPlaintextLength() {
|
|
43
|
+
var _pasteOptsState$plain;
|
|
44
|
+
return (_pasteOptsState$plain = pasteOptsState === null || pasteOptsState === void 0 ? void 0 : pasteOptsState.plaintextLength) !== null && _pasteOptsState$plain !== void 0 ? _pasteOptsState$plain : 0;
|
|
45
|
+
},
|
|
46
|
+
getAncestorNodeNames: function getAncestorNodeNames() {
|
|
47
|
+
var _pasteOptsState$paste;
|
|
48
|
+
return (_pasteOptsState$paste = pasteOptsState === null || pasteOptsState === void 0 ? void 0 : pasteOptsState.pasteAncestorNodeNames) !== null && _pasteOptsState$paste !== void 0 ? _pasteOptsState$paste : [];
|
|
49
|
+
},
|
|
50
|
+
getPastedText: function getPastedText() {
|
|
51
|
+
var _pasteState$lastConte, _pasteState$lastConte2;
|
|
52
|
+
return (_pasteState$lastConte = pasteState === null || pasteState === void 0 || (_pasteState$lastConte2 = pasteState.lastContentPasted) === null || _pasteState$lastConte2 === void 0 ? void 0 : _pasteState$lastConte2.text) !== null && _pasteState$lastConte !== void 0 ? _pasteState$lastConte : '';
|
|
53
|
+
},
|
|
54
|
+
getPastedSlice: function getPastedSlice() {
|
|
55
|
+
var _pasteState$lastConte3;
|
|
56
|
+
return pasteState === null || pasteState === void 0 || (_pasteState$lastConte3 = pasteState.lastContentPasted) === null || _pasteState$lastConte3 === void 0 ? void 0 : _pasteState$lastConte3.pastedSlice;
|
|
57
|
+
},
|
|
58
|
+
getNodeTypes: function getNodeTypes() {
|
|
59
|
+
return [];
|
|
60
|
+
},
|
|
61
|
+
getPasteSource: function getPasteSource() {
|
|
62
|
+
var _pasteState$lastConte4;
|
|
63
|
+
return pasteState === null || pasteState === void 0 || (_pasteState$lastConte4 = pasteState.lastContentPasted) === null || _pasteState$lastConte4 === void 0 ? void 0 : _pasteState$lastConte4.pasteSource;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
return (0, _rules.createPasteMenuRuleFactories)(getContext);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
34
70
|
pmPlugins: function pmPlugins() {
|
|
35
71
|
return [{
|
|
36
72
|
name: 'pasteOptionsToolbarPlugin',
|
|
@@ -5,46 +5,18 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.firePasteActionsMenuV2ExperimentExposure = void 0;
|
|
7
7
|
var _expVal = require("@atlaskit/tmp-editor-statsig/expVal");
|
|
8
|
+
var _hasTableNode = require("../utils/paste-menu-rules/hasTableNode");
|
|
9
|
+
var _isNotProse = require("../utils/paste-menu-rules/isNotProse");
|
|
8
10
|
// Remove this file when experiment 'platform_editor_paste_actions_menu_v2' is cleaned up.
|
|
9
11
|
|
|
10
|
-
var hasTableNode = function hasTableNode(slice) {
|
|
11
|
-
if (!slice) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
var found = false;
|
|
15
|
-
slice.content.descendants(function (node) {
|
|
16
|
-
if (node.type.name === 'table') {
|
|
17
|
-
found = true;
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
return true;
|
|
21
|
-
});
|
|
22
|
-
return found;
|
|
23
|
-
};
|
|
24
|
-
var isNotProse = function isNotProse(text) {
|
|
25
|
-
var trimmed = text.trim();
|
|
26
|
-
if (!trimmed) {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
for (var i = 0; i < trimmed.length; i++) {
|
|
30
|
-
var code = trimmed.charCodeAt(i);
|
|
31
|
-
if (code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
if (code > 0x7f) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return true;
|
|
39
|
-
};
|
|
40
12
|
var firePasteActionsMenuV2ExperimentExposure = exports.firePasteActionsMenuV2ExperimentExposure = function firePasteActionsMenuV2ExperimentExposure(contentLength, state, pasteStartPos, pasteEndPos, pastedText, pastedSlice) {
|
|
41
13
|
if (contentLength < 100 || !pasteStartPos || !pasteEndPos || !pastedText) {
|
|
42
14
|
return;
|
|
43
15
|
}
|
|
44
|
-
if (isNotProse(pastedText)) {
|
|
16
|
+
if ((0, _isNotProse.isNotProse)(pastedText)) {
|
|
45
17
|
return;
|
|
46
18
|
}
|
|
47
|
-
if (hasTableNode(pastedSlice)) {
|
|
19
|
+
if ((0, _hasTableNode.hasTableNode)(pastedSlice)) {
|
|
48
20
|
return;
|
|
49
21
|
}
|
|
50
22
|
try {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.hasTableNode = void 0;
|
|
7
|
+
var hasTableNode = exports.hasTableNode = function hasTableNode(slice) {
|
|
8
|
+
if (!slice) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
var found = false;
|
|
12
|
+
slice.content.descendants(function (node) {
|
|
13
|
+
if (node.type.name === 'table') {
|
|
14
|
+
found = true;
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
});
|
|
19
|
+
return found;
|
|
20
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isNotProse = void 0;
|
|
7
|
+
var isNotProse = exports.isNotProse = function isNotProse(text) {
|
|
8
|
+
var trimmed = text.trim();
|
|
9
|
+
if (!trimmed) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Check each character: if we find whitespace it's prose-like,
|
|
14
|
+
// if we find a non-ASCII character it's likely CJK/Thai/etc.
|
|
15
|
+
for (var i = 0; i < trimmed.length; i++) {
|
|
16
|
+
var code = trimmed.charCodeAt(i);
|
|
17
|
+
// Whitespace (space, tab, newline, etc.) → prose-like
|
|
18
|
+
if (code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
// Non-ASCII character → likely a non-Latin script (CJK, Thai, etc.)
|
|
22
|
+
if (code > 0x7f) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// No whitespace and all ASCII → URL, token, path, etc.
|
|
28
|
+
return true;
|
|
29
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createPasteMenuRuleFactories = void 0;
|
|
7
|
+
var _hasTableNode = require("./hasTableNode");
|
|
8
|
+
var _isNotProse = require("./isNotProse");
|
|
9
|
+
/**
|
|
10
|
+
* Returns a rule that hides the paste-menu button when the pasted plain-text
|
|
11
|
+
* is shorter than `minChars` characters.
|
|
12
|
+
*/
|
|
13
|
+
var _minCharsRule = function minCharsRule(minChars) {
|
|
14
|
+
return function (context) {
|
|
15
|
+
return context.getPlaintextLength() < minChars;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A rule that hides the paste-menu button when the pasted content is plain
|
|
21
|
+
* text (i.e. not rich-text / prose).
|
|
22
|
+
*/
|
|
23
|
+
var notProseRule = function notProseRule(context) {
|
|
24
|
+
// The pasted slice being empty or missing indicates plain-text paste.
|
|
25
|
+
var slice = context.getPastedSlice();
|
|
26
|
+
var pastedText = context.getPastedText();
|
|
27
|
+
return slice === undefined && (0, _isNotProse.isNotProse)(pastedText);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A rule that hides the paste-menu button when the pasted content contains a
|
|
32
|
+
* table node as an ancestor at the insertion point.
|
|
33
|
+
*/
|
|
34
|
+
var containsTableRule = function containsTableRule(context) {
|
|
35
|
+
return (0, _hasTableNode.hasTableNode)(context.getPastedSlice());
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns a rule that hides the paste-menu button when any of the provided
|
|
40
|
+
* node names appear in the ancestor list at the insertion point.
|
|
41
|
+
*/
|
|
42
|
+
var _excludedAncestorRule = function excludedAncestorRule(excludedNames) {
|
|
43
|
+
return function (context) {
|
|
44
|
+
return context.getAncestorNodeNames().some(function (name) {
|
|
45
|
+
return excludedNames.includes(name);
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Combines multiple context-aware rules with short-circuit evaluation.
|
|
52
|
+
* Returns `true` (hidden) as soon as the first rule returns `true`; returns
|
|
53
|
+
* `false` only when all rules return `false`.
|
|
54
|
+
* Context is injected by `createPasteMenuRuleFactories` — callers never
|
|
55
|
+
* construct or pass a context object.
|
|
56
|
+
*/
|
|
57
|
+
var _allRules = function allRules() {
|
|
58
|
+
for (var _len = arguments.length, rules = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
59
|
+
rules[_key] = arguments[_key];
|
|
60
|
+
}
|
|
61
|
+
return function (context) {
|
|
62
|
+
for (var _i = 0, _rules = rules; _i < _rules.length; _i++) {
|
|
63
|
+
var rule = _rules[_i];
|
|
64
|
+
if (rule(context)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Binds a `PasteMenuRuleWithContext` to a context supplier, returning a
|
|
74
|
+
* no-argument `PasteMenuRule` that re-reads the context on every invocation.
|
|
75
|
+
*/
|
|
76
|
+
var bind = function bind(getContext, rule) {
|
|
77
|
+
return function () {
|
|
78
|
+
return rule(getContext());
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Creates the `PasteMenuRuleFactories` object with every rule pre-bound to the
|
|
84
|
+
* given context supplier. Called inside `getPasteMenuRules()` so the plugin
|
|
85
|
+
* API and paste state are already available in the closure — external callers
|
|
86
|
+
* never need to construct or pass a context object.
|
|
87
|
+
*/
|
|
88
|
+
var createPasteMenuRuleFactories = exports.createPasteMenuRuleFactories = function createPasteMenuRuleFactories(getContext) {
|
|
89
|
+
return {
|
|
90
|
+
allRules: function allRules() {
|
|
91
|
+
return bind(getContext, _allRules.apply(void 0, arguments));
|
|
92
|
+
},
|
|
93
|
+
containsTableRule: bind(getContext, containsTableRule),
|
|
94
|
+
excludedAncestorRule: function excludedAncestorRule(excludedNames) {
|
|
95
|
+
return bind(getContext, _excludedAncestorRule(excludedNames));
|
|
96
|
+
},
|
|
97
|
+
minCharsRule: function minCharsRule(minChars) {
|
|
98
|
+
return bind(getContext, _minCharsRule(minChars));
|
|
99
|
+
},
|
|
100
|
+
notProseRule: bind(getContext, notProseRule)
|
|
101
|
+
};
|
|
102
|
+
};
|
|
@@ -10,6 +10,7 @@ import { firePasteActionsMenuV2ExperimentExposure } from './ui/on-paste-actions-
|
|
|
10
10
|
import { PasteActionsMenu } from './ui/on-paste-actions-menu/PasteActionsMenu';
|
|
11
11
|
import { getPasteMenuComponents } from './ui/on-paste-actions-menu/PasteMenuComponents';
|
|
12
12
|
import { buildToolbar, isToolbarVisible } from './ui/toolbar';
|
|
13
|
+
import { createPasteMenuRuleFactories } from './ui/utils/paste-menu-rules/rules';
|
|
13
14
|
export const pasteOptionsToolbarPlugin = ({
|
|
14
15
|
config,
|
|
15
16
|
api
|
|
@@ -24,6 +25,39 @@ export const pasteOptionsToolbarPlugin = ({
|
|
|
24
25
|
}
|
|
25
26
|
return {
|
|
26
27
|
name: 'pasteOptionsToolbarPlugin',
|
|
28
|
+
actions: {
|
|
29
|
+
getPasteMenuRules: () => {
|
|
30
|
+
const getContext = () => {
|
|
31
|
+
var _api$pasteOptionsTool, _api$paste;
|
|
32
|
+
const pasteOptsState = api === null || api === void 0 ? void 0 : (_api$pasteOptionsTool = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool === void 0 ? void 0 : _api$pasteOptionsTool.sharedState.currentState();
|
|
33
|
+
const pasteState = api === null || api === void 0 ? void 0 : (_api$paste = api.paste) === null || _api$paste === void 0 ? void 0 : _api$paste.sharedState.currentState();
|
|
34
|
+
return {
|
|
35
|
+
getPlaintextLength: () => {
|
|
36
|
+
var _pasteOptsState$plain;
|
|
37
|
+
return (_pasteOptsState$plain = pasteOptsState === null || pasteOptsState === void 0 ? void 0 : pasteOptsState.plaintextLength) !== null && _pasteOptsState$plain !== void 0 ? _pasteOptsState$plain : 0;
|
|
38
|
+
},
|
|
39
|
+
getAncestorNodeNames: () => {
|
|
40
|
+
var _pasteOptsState$paste;
|
|
41
|
+
return (_pasteOptsState$paste = pasteOptsState === null || pasteOptsState === void 0 ? void 0 : pasteOptsState.pasteAncestorNodeNames) !== null && _pasteOptsState$paste !== void 0 ? _pasteOptsState$paste : [];
|
|
42
|
+
},
|
|
43
|
+
getPastedText: () => {
|
|
44
|
+
var _pasteState$lastConte, _pasteState$lastConte2;
|
|
45
|
+
return (_pasteState$lastConte = pasteState === null || pasteState === void 0 ? void 0 : (_pasteState$lastConte2 = pasteState.lastContentPasted) === null || _pasteState$lastConte2 === void 0 ? void 0 : _pasteState$lastConte2.text) !== null && _pasteState$lastConte !== void 0 ? _pasteState$lastConte : '';
|
|
46
|
+
},
|
|
47
|
+
getPastedSlice: () => {
|
|
48
|
+
var _pasteState$lastConte3;
|
|
49
|
+
return pasteState === null || pasteState === void 0 ? void 0 : (_pasteState$lastConte3 = pasteState.lastContentPasted) === null || _pasteState$lastConte3 === void 0 ? void 0 : _pasteState$lastConte3.pastedSlice;
|
|
50
|
+
},
|
|
51
|
+
getNodeTypes: () => [],
|
|
52
|
+
getPasteSource: () => {
|
|
53
|
+
var _pasteState$lastConte4;
|
|
54
|
+
return pasteState === null || pasteState === void 0 ? void 0 : (_pasteState$lastConte4 = pasteState.lastContentPasted) === null || _pasteState$lastConte4 === void 0 ? void 0 : _pasteState$lastConte4.pasteSource;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
return createPasteMenuRuleFactories(getContext);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
27
61
|
pmPlugins() {
|
|
28
62
|
return [{
|
|
29
63
|
name: 'pasteOptionsToolbarPlugin',
|
|
@@ -1,36 +1,8 @@
|
|
|
1
1
|
// Remove this file when experiment 'platform_editor_paste_actions_menu_v2' is cleaned up.
|
|
2
2
|
|
|
3
3
|
import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return false;
|
|
7
|
-
}
|
|
8
|
-
let found = false;
|
|
9
|
-
slice.content.descendants(node => {
|
|
10
|
-
if (node.type.name === 'table') {
|
|
11
|
-
found = true;
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
return true;
|
|
15
|
-
});
|
|
16
|
-
return found;
|
|
17
|
-
};
|
|
18
|
-
const isNotProse = text => {
|
|
19
|
-
const trimmed = text.trim();
|
|
20
|
-
if (!trimmed) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
for (let i = 0; i < trimmed.length; i++) {
|
|
24
|
-
const code = trimmed.charCodeAt(i);
|
|
25
|
-
if (code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
if (code > 0x7f) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return true;
|
|
33
|
-
};
|
|
4
|
+
import { hasTableNode } from '../utils/paste-menu-rules/hasTableNode';
|
|
5
|
+
import { isNotProse } from '../utils/paste-menu-rules/isNotProse';
|
|
34
6
|
export const firePasteActionsMenuV2ExperimentExposure = (contentLength, state, pasteStartPos, pasteEndPos, pastedText, pastedSlice) => {
|
|
35
7
|
if (contentLength < 100 || !pasteStartPos || !pasteEndPos || !pastedText) {
|
|
36
8
|
return;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const isNotProse = text => {
|
|
2
|
+
const trimmed = text.trim();
|
|
3
|
+
if (!trimmed) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Check each character: if we find whitespace it's prose-like,
|
|
8
|
+
// if we find a non-ASCII character it's likely CJK/Thai/etc.
|
|
9
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
10
|
+
const code = trimmed.charCodeAt(i);
|
|
11
|
+
// Whitespace (space, tab, newline, etc.) → prose-like
|
|
12
|
+
if (code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
// Non-ASCII character → likely a non-Latin script (CJK, Thai, etc.)
|
|
16
|
+
if (code > 0x7f) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// No whitespace and all ASCII → URL, token, path, etc.
|
|
22
|
+
return true;
|
|
23
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { hasTableNode } from './hasTableNode';
|
|
2
|
+
import { isNotProse } from './isNotProse';
|
|
3
|
+
/**
|
|
4
|
+
* Returns a rule that hides the paste-menu button when the pasted plain-text
|
|
5
|
+
* is shorter than `minChars` characters.
|
|
6
|
+
*/
|
|
7
|
+
const minCharsRule = minChars => context => context.getPlaintextLength() < minChars;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A rule that hides the paste-menu button when the pasted content is plain
|
|
11
|
+
* text (i.e. not rich-text / prose).
|
|
12
|
+
*/
|
|
13
|
+
const notProseRule = context => {
|
|
14
|
+
// The pasted slice being empty or missing indicates plain-text paste.
|
|
15
|
+
const slice = context.getPastedSlice();
|
|
16
|
+
const pastedText = context.getPastedText();
|
|
17
|
+
return slice === undefined && isNotProse(pastedText);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A rule that hides the paste-menu button when the pasted content contains a
|
|
22
|
+
* table node as an ancestor at the insertion point.
|
|
23
|
+
*/
|
|
24
|
+
const containsTableRule = context => hasTableNode(context.getPastedSlice());
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns a rule that hides the paste-menu button when any of the provided
|
|
28
|
+
* node names appear in the ancestor list at the insertion point.
|
|
29
|
+
*/
|
|
30
|
+
const excludedAncestorRule = excludedNames => context => context.getAncestorNodeNames().some(name => excludedNames.includes(name));
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Combines multiple context-aware rules with short-circuit evaluation.
|
|
34
|
+
* Returns `true` (hidden) as soon as the first rule returns `true`; returns
|
|
35
|
+
* `false` only when all rules return `false`.
|
|
36
|
+
* Context is injected by `createPasteMenuRuleFactories` — callers never
|
|
37
|
+
* construct or pass a context object.
|
|
38
|
+
*/
|
|
39
|
+
const allRules = (...rules) => context => {
|
|
40
|
+
for (const rule of rules) {
|
|
41
|
+
if (rule(context)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Binds a `PasteMenuRuleWithContext` to a context supplier, returning a
|
|
50
|
+
* no-argument `PasteMenuRule` that re-reads the context on every invocation.
|
|
51
|
+
*/
|
|
52
|
+
const bind = (getContext, rule) => () => rule(getContext());
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates the `PasteMenuRuleFactories` object with every rule pre-bound to the
|
|
56
|
+
* given context supplier. Called inside `getPasteMenuRules()` so the plugin
|
|
57
|
+
* API and paste state are already available in the closure — external callers
|
|
58
|
+
* never need to construct or pass a context object.
|
|
59
|
+
*/
|
|
60
|
+
export const createPasteMenuRuleFactories = getContext => ({
|
|
61
|
+
allRules: (...rules) => bind(getContext, allRules(...rules)),
|
|
62
|
+
containsTableRule: bind(getContext, containsTableRule),
|
|
63
|
+
excludedAncestorRule: excludedNames => bind(getContext, excludedAncestorRule(excludedNames)),
|
|
64
|
+
minCharsRule: minChars => bind(getContext, minCharsRule(minChars)),
|
|
65
|
+
notProseRule: bind(getContext, notProseRule)
|
|
66
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -10,6 +10,7 @@ import { firePasteActionsMenuV2ExperimentExposure } from './ui/on-paste-actions-
|
|
|
10
10
|
import { PasteActionsMenu } from './ui/on-paste-actions-menu/PasteActionsMenu';
|
|
11
11
|
import { getPasteMenuComponents } from './ui/on-paste-actions-menu/PasteMenuComponents';
|
|
12
12
|
import { buildToolbar, isToolbarVisible } from './ui/toolbar';
|
|
13
|
+
import { createPasteMenuRuleFactories } from './ui/utils/paste-menu-rules/rules';
|
|
13
14
|
export var pasteOptionsToolbarPlugin = function pasteOptionsToolbarPlugin(_ref) {
|
|
14
15
|
var _api$analytics;
|
|
15
16
|
var config = _ref.config,
|
|
@@ -23,6 +24,41 @@ export var pasteOptionsToolbarPlugin = function pasteOptionsToolbarPlugin(_ref)
|
|
|
23
24
|
}
|
|
24
25
|
return {
|
|
25
26
|
name: 'pasteOptionsToolbarPlugin',
|
|
27
|
+
actions: {
|
|
28
|
+
getPasteMenuRules: function getPasteMenuRules() {
|
|
29
|
+
var getContext = function getContext() {
|
|
30
|
+
var _api$pasteOptionsTool, _api$paste;
|
|
31
|
+
var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool === void 0 ? void 0 : _api$pasteOptionsTool.sharedState.currentState();
|
|
32
|
+
var pasteState = api === null || api === void 0 || (_api$paste = api.paste) === null || _api$paste === void 0 ? void 0 : _api$paste.sharedState.currentState();
|
|
33
|
+
return {
|
|
34
|
+
getPlaintextLength: function getPlaintextLength() {
|
|
35
|
+
var _pasteOptsState$plain;
|
|
36
|
+
return (_pasteOptsState$plain = pasteOptsState === null || pasteOptsState === void 0 ? void 0 : pasteOptsState.plaintextLength) !== null && _pasteOptsState$plain !== void 0 ? _pasteOptsState$plain : 0;
|
|
37
|
+
},
|
|
38
|
+
getAncestorNodeNames: function getAncestorNodeNames() {
|
|
39
|
+
var _pasteOptsState$paste;
|
|
40
|
+
return (_pasteOptsState$paste = pasteOptsState === null || pasteOptsState === void 0 ? void 0 : pasteOptsState.pasteAncestorNodeNames) !== null && _pasteOptsState$paste !== void 0 ? _pasteOptsState$paste : [];
|
|
41
|
+
},
|
|
42
|
+
getPastedText: function getPastedText() {
|
|
43
|
+
var _pasteState$lastConte, _pasteState$lastConte2;
|
|
44
|
+
return (_pasteState$lastConte = pasteState === null || pasteState === void 0 || (_pasteState$lastConte2 = pasteState.lastContentPasted) === null || _pasteState$lastConte2 === void 0 ? void 0 : _pasteState$lastConte2.text) !== null && _pasteState$lastConte !== void 0 ? _pasteState$lastConte : '';
|
|
45
|
+
},
|
|
46
|
+
getPastedSlice: function getPastedSlice() {
|
|
47
|
+
var _pasteState$lastConte3;
|
|
48
|
+
return pasteState === null || pasteState === void 0 || (_pasteState$lastConte3 = pasteState.lastContentPasted) === null || _pasteState$lastConte3 === void 0 ? void 0 : _pasteState$lastConte3.pastedSlice;
|
|
49
|
+
},
|
|
50
|
+
getNodeTypes: function getNodeTypes() {
|
|
51
|
+
return [];
|
|
52
|
+
},
|
|
53
|
+
getPasteSource: function getPasteSource() {
|
|
54
|
+
var _pasteState$lastConte4;
|
|
55
|
+
return pasteState === null || pasteState === void 0 || (_pasteState$lastConte4 = pasteState.lastContentPasted) === null || _pasteState$lastConte4 === void 0 ? void 0 : _pasteState$lastConte4.pasteSource;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
return createPasteMenuRuleFactories(getContext);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
26
62
|
pmPlugins: function pmPlugins() {
|
|
27
63
|
return [{
|
|
28
64
|
name: 'pasteOptionsToolbarPlugin',
|
|
@@ -1,36 +1,8 @@
|
|
|
1
1
|
// Remove this file when experiment 'platform_editor_paste_actions_menu_v2' is cleaned up.
|
|
2
2
|
|
|
3
3
|
import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return false;
|
|
7
|
-
}
|
|
8
|
-
var found = false;
|
|
9
|
-
slice.content.descendants(function (node) {
|
|
10
|
-
if (node.type.name === 'table') {
|
|
11
|
-
found = true;
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
return true;
|
|
15
|
-
});
|
|
16
|
-
return found;
|
|
17
|
-
};
|
|
18
|
-
var isNotProse = function isNotProse(text) {
|
|
19
|
-
var trimmed = text.trim();
|
|
20
|
-
if (!trimmed) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
for (var i = 0; i < trimmed.length; i++) {
|
|
24
|
-
var code = trimmed.charCodeAt(i);
|
|
25
|
-
if (code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
if (code > 0x7f) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return true;
|
|
33
|
-
};
|
|
4
|
+
import { hasTableNode } from '../utils/paste-menu-rules/hasTableNode';
|
|
5
|
+
import { isNotProse } from '../utils/paste-menu-rules/isNotProse';
|
|
34
6
|
export var firePasteActionsMenuV2ExperimentExposure = function firePasteActionsMenuV2ExperimentExposure(contentLength, state, pasteStartPos, pasteEndPos, pastedText, pastedSlice) {
|
|
35
7
|
if (contentLength < 100 || !pasteStartPos || !pasteEndPos || !pastedText) {
|
|
36
8
|
return;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export var hasTableNode = function hasTableNode(slice) {
|
|
2
|
+
if (!slice) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
var found = false;
|
|
6
|
+
slice.content.descendants(function (node) {
|
|
7
|
+
if (node.type.name === 'table') {
|
|
8
|
+
found = true;
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return true;
|
|
12
|
+
});
|
|
13
|
+
return found;
|
|
14
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export var isNotProse = function isNotProse(text) {
|
|
2
|
+
var trimmed = text.trim();
|
|
3
|
+
if (!trimmed) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Check each character: if we find whitespace it's prose-like,
|
|
8
|
+
// if we find a non-ASCII character it's likely CJK/Thai/etc.
|
|
9
|
+
for (var i = 0; i < trimmed.length; i++) {
|
|
10
|
+
var code = trimmed.charCodeAt(i);
|
|
11
|
+
// Whitespace (space, tab, newline, etc.) → prose-like
|
|
12
|
+
if (code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
// Non-ASCII character → likely a non-Latin script (CJK, Thai, etc.)
|
|
16
|
+
if (code > 0x7f) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// No whitespace and all ASCII → URL, token, path, etc.
|
|
22
|
+
return true;
|
|
23
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { hasTableNode } from './hasTableNode';
|
|
2
|
+
import { isNotProse } from './isNotProse';
|
|
3
|
+
/**
|
|
4
|
+
* Returns a rule that hides the paste-menu button when the pasted plain-text
|
|
5
|
+
* is shorter than `minChars` characters.
|
|
6
|
+
*/
|
|
7
|
+
var _minCharsRule = function minCharsRule(minChars) {
|
|
8
|
+
return function (context) {
|
|
9
|
+
return context.getPlaintextLength() < minChars;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A rule that hides the paste-menu button when the pasted content is plain
|
|
15
|
+
* text (i.e. not rich-text / prose).
|
|
16
|
+
*/
|
|
17
|
+
var notProseRule = function notProseRule(context) {
|
|
18
|
+
// The pasted slice being empty or missing indicates plain-text paste.
|
|
19
|
+
var slice = context.getPastedSlice();
|
|
20
|
+
var pastedText = context.getPastedText();
|
|
21
|
+
return slice === undefined && isNotProse(pastedText);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A rule that hides the paste-menu button when the pasted content contains a
|
|
26
|
+
* table node as an ancestor at the insertion point.
|
|
27
|
+
*/
|
|
28
|
+
var containsTableRule = function containsTableRule(context) {
|
|
29
|
+
return hasTableNode(context.getPastedSlice());
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns a rule that hides the paste-menu button when any of the provided
|
|
34
|
+
* node names appear in the ancestor list at the insertion point.
|
|
35
|
+
*/
|
|
36
|
+
var _excludedAncestorRule = function excludedAncestorRule(excludedNames) {
|
|
37
|
+
return function (context) {
|
|
38
|
+
return context.getAncestorNodeNames().some(function (name) {
|
|
39
|
+
return excludedNames.includes(name);
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Combines multiple context-aware rules with short-circuit evaluation.
|
|
46
|
+
* Returns `true` (hidden) as soon as the first rule returns `true`; returns
|
|
47
|
+
* `false` only when all rules return `false`.
|
|
48
|
+
* Context is injected by `createPasteMenuRuleFactories` — callers never
|
|
49
|
+
* construct or pass a context object.
|
|
50
|
+
*/
|
|
51
|
+
var _allRules = function allRules() {
|
|
52
|
+
for (var _len = arguments.length, rules = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
53
|
+
rules[_key] = arguments[_key];
|
|
54
|
+
}
|
|
55
|
+
return function (context) {
|
|
56
|
+
for (var _i = 0, _rules = rules; _i < _rules.length; _i++) {
|
|
57
|
+
var rule = _rules[_i];
|
|
58
|
+
if (rule(context)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Binds a `PasteMenuRuleWithContext` to a context supplier, returning a
|
|
68
|
+
* no-argument `PasteMenuRule` that re-reads the context on every invocation.
|
|
69
|
+
*/
|
|
70
|
+
var bind = function bind(getContext, rule) {
|
|
71
|
+
return function () {
|
|
72
|
+
return rule(getContext());
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Creates the `PasteMenuRuleFactories` object with every rule pre-bound to the
|
|
78
|
+
* given context supplier. Called inside `getPasteMenuRules()` so the plugin
|
|
79
|
+
* API and paste state are already available in the closure — external callers
|
|
80
|
+
* never need to construct or pass a context object.
|
|
81
|
+
*/
|
|
82
|
+
export var createPasteMenuRuleFactories = function createPasteMenuRuleFactories(getContext) {
|
|
83
|
+
return {
|
|
84
|
+
allRules: function allRules() {
|
|
85
|
+
return bind(getContext, _allRules.apply(void 0, arguments));
|
|
86
|
+
},
|
|
87
|
+
containsTableRule: bind(getContext, containsTableRule),
|
|
88
|
+
excludedAncestorRule: function excludedAncestorRule(excludedNames) {
|
|
89
|
+
return bind(getContext, _excludedAncestorRule(excludedNames));
|
|
90
|
+
},
|
|
91
|
+
minCharsRule: function minCharsRule(minChars) {
|
|
92
|
+
return bind(getContext, _minCharsRule(minChars));
|
|
93
|
+
},
|
|
94
|
+
notProseRule: bind(getContext, notProseRule)
|
|
95
|
+
};
|
|
96
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -3,6 +3,7 @@ import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
|
|
|
3
3
|
import type { PastePlugin } from '@atlaskit/editor-plugin-paste';
|
|
4
4
|
import type { UiControlRegistryPlugin } from '@atlaskit/editor-plugin-ui-control-registry';
|
|
5
5
|
import type { ToolbarDropdownOption } from './types/types';
|
|
6
|
+
import type { PasteMenuRuleFactories } from './ui/utils/paste-menu-rules/types';
|
|
6
7
|
export type PasteOptionsToolbarPluginDependencies = [
|
|
7
8
|
OptionalPlugin<AnalyticsPlugin>,
|
|
8
9
|
PastePlugin,
|
|
@@ -19,6 +20,19 @@ export interface PasteOptionsToolbarSharedState {
|
|
|
19
20
|
showToolbar: boolean;
|
|
20
21
|
}
|
|
21
22
|
export type PasteOptionsToolbarPlugin = NextEditorPlugin<'pasteOptionsToolbarPlugin', {
|
|
23
|
+
actions: {
|
|
24
|
+
/**
|
|
25
|
+
* Returns the collection of paste-menu rule factories.
|
|
26
|
+
* External plugins (e.g. `editor-plugin-ai`, `editor-plugin-card`) use
|
|
27
|
+
* this to compose `isHidden` callbacks without static cross-package
|
|
28
|
+
* imports.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const { minCharsRule, allRules } =
|
|
32
|
+
* api.pasteOptionsToolbarPlugin.actions.getPasteMenuRules();
|
|
33
|
+
*/
|
|
34
|
+
getPasteMenuRules: () => PasteMenuRuleFactories;
|
|
35
|
+
};
|
|
22
36
|
dependencies: PasteOptionsToolbarPluginDependencies;
|
|
23
37
|
pluginConfiguration?: {
|
|
24
38
|
usePopupBasedPasteActionsMenu?: boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isNotProse: (text: string) => boolean;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PasteMenuRuleContext, PasteMenuRuleFactories } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates the `PasteMenuRuleFactories` object with every rule pre-bound to the
|
|
4
|
+
* given context supplier. Called inside `getPasteMenuRules()` so the plugin
|
|
5
|
+
* API and paste state are already available in the closure — external callers
|
|
6
|
+
* never need to construct or pass a context object.
|
|
7
|
+
*/
|
|
8
|
+
export declare const createPasteMenuRuleFactories: (getContext: () => PasteMenuRuleContext) => PasteMenuRuleFactories;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Slice } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
/**
|
|
3
|
+
* Context object passed to each `PasteMenuRule`.
|
|
4
|
+
* Provides lazy accessors over the current paste state so that rule
|
|
5
|
+
* implementations remain decoupled from the plugin's internal state shape.
|
|
6
|
+
*/
|
|
7
|
+
export interface PasteMenuRuleContext {
|
|
8
|
+
/**
|
|
9
|
+
* Returns the names of ancestor nodes at the paste insertion point
|
|
10
|
+
* (e.g. `['table', 'tableRow', 'tableCell']`).
|
|
11
|
+
*/
|
|
12
|
+
getAncestorNodeNames: () => string[];
|
|
13
|
+
/**
|
|
14
|
+
* Returns the set of ProseMirror node-type names present in the pasted
|
|
15
|
+
* content (derived from the rich-text slice).
|
|
16
|
+
*/
|
|
17
|
+
getNodeTypes: () => string[];
|
|
18
|
+
/** Returns the rich-text `Slice` that was pasted, if available. */
|
|
19
|
+
getPastedSlice: () => Slice | undefined;
|
|
20
|
+
/** Returns the raw pasted text content. */
|
|
21
|
+
getPastedText: () => string;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the paste source identifier, e.g. `'external'`, `'internal'`,
|
|
24
|
+
* `'markdown'`, etc. Returns `undefined` when the source is unknown.
|
|
25
|
+
*/
|
|
26
|
+
getPasteSource: () => string | undefined;
|
|
27
|
+
/** Returns the character length of the pasted plain-text string. */
|
|
28
|
+
getPlaintextLength: () => number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Internal rule function that receives an explicit context object.
|
|
32
|
+
* Used only within this package — external callers see `PasteMenuRule`.
|
|
33
|
+
*/
|
|
34
|
+
export type PasteMenuRuleWithContext = (context: PasteMenuRuleContext) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* A rule function returned by `getPasteMenuRules()`. The context is already
|
|
37
|
+
* bound by the plugin, so callers simply invoke it with no arguments.
|
|
38
|
+
*/
|
|
39
|
+
export type PasteMenuRule = () => boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The collection of rule factories exposed via the plugin API.
|
|
42
|
+
* External plugins consume this object through
|
|
43
|
+
* `api?.pasteOptionsToolbarPlugin?.actions.getPasteMenuRules()`.
|
|
44
|
+
* Every rule already has the paste context pre-bound — callers do not need to
|
|
45
|
+
* construct or pass a context object.
|
|
46
|
+
*/
|
|
47
|
+
export interface PasteMenuRuleFactories {
|
|
48
|
+
/**
|
|
49
|
+
* Combines multiple rules with short-circuit evaluation: returns `true`
|
|
50
|
+
* (hidden) as soon as the first rule returns `true`.
|
|
51
|
+
*/
|
|
52
|
+
allRules: (...rules: PasteMenuRuleWithContext[]) => PasteMenuRule;
|
|
53
|
+
/**
|
|
54
|
+
* A rule that hides the button when the pasted content contains a table
|
|
55
|
+
* node as an ancestor at the insertion point.
|
|
56
|
+
*/
|
|
57
|
+
containsTableRule: PasteMenuRule;
|
|
58
|
+
/**
|
|
59
|
+
* Returns a rule that hides the button when any of the specified node
|
|
60
|
+
* names appear in the ancestor list at the insertion point.
|
|
61
|
+
*/
|
|
62
|
+
excludedAncestorRule: (excludedNames: string[]) => PasteMenuRule;
|
|
63
|
+
/**
|
|
64
|
+
* Returns a rule that hides the button when the pasted plain-text is
|
|
65
|
+
* shorter than `minChars` characters.
|
|
66
|
+
*/
|
|
67
|
+
minCharsRule: (minChars: number) => PasteMenuRule;
|
|
68
|
+
/**
|
|
69
|
+
* A rule that hides the button when the pasted content is plain text
|
|
70
|
+
* (i.e. not rich-text / prose).
|
|
71
|
+
*/
|
|
72
|
+
notProseRule: PasteMenuRule;
|
|
73
|
+
}
|
|
@@ -3,6 +3,7 @@ import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
|
|
|
3
3
|
import type { PastePlugin } from '@atlaskit/editor-plugin-paste';
|
|
4
4
|
import type { UiControlRegistryPlugin } from '@atlaskit/editor-plugin-ui-control-registry';
|
|
5
5
|
import type { ToolbarDropdownOption } from './types/types';
|
|
6
|
+
import type { PasteMenuRuleFactories } from './ui/utils/paste-menu-rules/types';
|
|
6
7
|
export type PasteOptionsToolbarPluginDependencies = [
|
|
7
8
|
OptionalPlugin<AnalyticsPlugin>,
|
|
8
9
|
PastePlugin,
|
|
@@ -19,6 +20,19 @@ export interface PasteOptionsToolbarSharedState {
|
|
|
19
20
|
showToolbar: boolean;
|
|
20
21
|
}
|
|
21
22
|
export type PasteOptionsToolbarPlugin = NextEditorPlugin<'pasteOptionsToolbarPlugin', {
|
|
23
|
+
actions: {
|
|
24
|
+
/**
|
|
25
|
+
* Returns the collection of paste-menu rule factories.
|
|
26
|
+
* External plugins (e.g. `editor-plugin-ai`, `editor-plugin-card`) use
|
|
27
|
+
* this to compose `isHidden` callbacks without static cross-package
|
|
28
|
+
* imports.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const { minCharsRule, allRules } =
|
|
32
|
+
* api.pasteOptionsToolbarPlugin.actions.getPasteMenuRules();
|
|
33
|
+
*/
|
|
34
|
+
getPasteMenuRules: () => PasteMenuRuleFactories;
|
|
35
|
+
};
|
|
22
36
|
dependencies: PasteOptionsToolbarPluginDependencies;
|
|
23
37
|
pluginConfiguration?: {
|
|
24
38
|
usePopupBasedPasteActionsMenu?: boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isNotProse: (text: string) => boolean;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PasteMenuRuleContext, PasteMenuRuleFactories } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates the `PasteMenuRuleFactories` object with every rule pre-bound to the
|
|
4
|
+
* given context supplier. Called inside `getPasteMenuRules()` so the plugin
|
|
5
|
+
* API and paste state are already available in the closure — external callers
|
|
6
|
+
* never need to construct or pass a context object.
|
|
7
|
+
*/
|
|
8
|
+
export declare const createPasteMenuRuleFactories: (getContext: () => PasteMenuRuleContext) => PasteMenuRuleFactories;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Slice } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
/**
|
|
3
|
+
* Context object passed to each `PasteMenuRule`.
|
|
4
|
+
* Provides lazy accessors over the current paste state so that rule
|
|
5
|
+
* implementations remain decoupled from the plugin's internal state shape.
|
|
6
|
+
*/
|
|
7
|
+
export interface PasteMenuRuleContext {
|
|
8
|
+
/**
|
|
9
|
+
* Returns the names of ancestor nodes at the paste insertion point
|
|
10
|
+
* (e.g. `['table', 'tableRow', 'tableCell']`).
|
|
11
|
+
*/
|
|
12
|
+
getAncestorNodeNames: () => string[];
|
|
13
|
+
/**
|
|
14
|
+
* Returns the set of ProseMirror node-type names present in the pasted
|
|
15
|
+
* content (derived from the rich-text slice).
|
|
16
|
+
*/
|
|
17
|
+
getNodeTypes: () => string[];
|
|
18
|
+
/** Returns the rich-text `Slice` that was pasted, if available. */
|
|
19
|
+
getPastedSlice: () => Slice | undefined;
|
|
20
|
+
/** Returns the raw pasted text content. */
|
|
21
|
+
getPastedText: () => string;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the paste source identifier, e.g. `'external'`, `'internal'`,
|
|
24
|
+
* `'markdown'`, etc. Returns `undefined` when the source is unknown.
|
|
25
|
+
*/
|
|
26
|
+
getPasteSource: () => string | undefined;
|
|
27
|
+
/** Returns the character length of the pasted plain-text string. */
|
|
28
|
+
getPlaintextLength: () => number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Internal rule function that receives an explicit context object.
|
|
32
|
+
* Used only within this package — external callers see `PasteMenuRule`.
|
|
33
|
+
*/
|
|
34
|
+
export type PasteMenuRuleWithContext = (context: PasteMenuRuleContext) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* A rule function returned by `getPasteMenuRules()`. The context is already
|
|
37
|
+
* bound by the plugin, so callers simply invoke it with no arguments.
|
|
38
|
+
*/
|
|
39
|
+
export type PasteMenuRule = () => boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The collection of rule factories exposed via the plugin API.
|
|
42
|
+
* External plugins consume this object through
|
|
43
|
+
* `api?.pasteOptionsToolbarPlugin?.actions.getPasteMenuRules()`.
|
|
44
|
+
* Every rule already has the paste context pre-bound — callers do not need to
|
|
45
|
+
* construct or pass a context object.
|
|
46
|
+
*/
|
|
47
|
+
export interface PasteMenuRuleFactories {
|
|
48
|
+
/**
|
|
49
|
+
* Combines multiple rules with short-circuit evaluation: returns `true`
|
|
50
|
+
* (hidden) as soon as the first rule returns `true`.
|
|
51
|
+
*/
|
|
52
|
+
allRules: (...rules: PasteMenuRuleWithContext[]) => PasteMenuRule;
|
|
53
|
+
/**
|
|
54
|
+
* A rule that hides the button when the pasted content contains a table
|
|
55
|
+
* node as an ancestor at the insertion point.
|
|
56
|
+
*/
|
|
57
|
+
containsTableRule: PasteMenuRule;
|
|
58
|
+
/**
|
|
59
|
+
* Returns a rule that hides the button when any of the specified node
|
|
60
|
+
* names appear in the ancestor list at the insertion point.
|
|
61
|
+
*/
|
|
62
|
+
excludedAncestorRule: (excludedNames: string[]) => PasteMenuRule;
|
|
63
|
+
/**
|
|
64
|
+
* Returns a rule that hides the button when the pasted plain-text is
|
|
65
|
+
* shorter than `minChars` characters.
|
|
66
|
+
*/
|
|
67
|
+
minCharsRule: (minChars: number) => PasteMenuRule;
|
|
68
|
+
/**
|
|
69
|
+
* A rule that hides the button when the pasted content is plain text
|
|
70
|
+
* (i.e. not rich-text / prose).
|
|
71
|
+
*/
|
|
72
|
+
notProseRule: PasteMenuRule;
|
|
73
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-paste-options-toolbar",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.2.0",
|
|
4
4
|
"description": "Paste options toolbar for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -34,15 +34,15 @@
|
|
|
34
34
|
"@atlaskit/editor-markdown-transformer": "^5.20.0",
|
|
35
35
|
"@atlaskit/editor-plugin-analytics": "^10.0.0",
|
|
36
36
|
"@atlaskit/editor-plugin-paste": "^11.2.0",
|
|
37
|
-
"@atlaskit/editor-plugin-ui-control-registry": "^4.
|
|
37
|
+
"@atlaskit/editor-plugin-ui-control-registry": "^4.1.0",
|
|
38
38
|
"@atlaskit/editor-prosemirror": "^7.3.0",
|
|
39
|
-
"@atlaskit/editor-shared-styles": "^3.
|
|
40
|
-
"@atlaskit/editor-toolbar": "^1.
|
|
41
|
-
"@atlaskit/editor-ui-control-model": "^1.
|
|
39
|
+
"@atlaskit/editor-shared-styles": "^3.11.0",
|
|
40
|
+
"@atlaskit/editor-toolbar": "^1.4.0",
|
|
41
|
+
"@atlaskit/editor-ui-control-model": "^1.2.0",
|
|
42
42
|
"@atlaskit/icon": "^34.5.0",
|
|
43
43
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
44
44
|
"@atlaskit/primitives": "^19.0.0",
|
|
45
|
-
"@atlaskit/tmp-editor-statsig": "^80.
|
|
45
|
+
"@atlaskit/tmp-editor-statsig": "^80.2.0",
|
|
46
46
|
"@atlaskit/tokens": "^13.0.0",
|
|
47
47
|
"@babel/runtime": "^7.0.0",
|
|
48
48
|
"@compiled/react": "^0.20.0",
|