@atlaskit/editor-plugin-paste-options-toolbar 13.0.20 → 13.1.1

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 CHANGED
@@ -1,5 +1,25 @@
1
1
  # @atlaskit/editor-plugin-paste-options-toolbar
2
2
 
3
+ ## 13.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 13.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`779a0020403de`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/779a0020403de) -
14
+ Add async hidden support to paste menu registered components, including timeout/error handling and
15
+ single Smart Link URL context from the paste options toolbar. Use Smart Card access checks to hide
16
+ Smart Link Display as options and AI Add Summary / Ask Rovo paste actions for inaccessible
17
+ single-link pastes in Confluence.
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies
22
+
3
23
  ## 13.0.20
4
24
 
5
25
  ### Patch Changes
@@ -428,6 +428,7 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
428
428
  onMouseDown: preventEditorFocusLoss,
429
429
  onMouseEnter: handleMouseEnter,
430
430
  components: pasteMenuComponents,
431
- contentRef: popupContentRef
431
+ contentRef: popupContentRef,
432
+ pastedSlice: lastContentPasted === null || lastContentPasted === void 0 ? void 0 : lastContentPasted.pastedSlice
432
433
  }))));
433
434
  };
@@ -1,6 +1,7 @@
1
1
  /* PasteActionsMenuContent.tsx generated by @compiled/babel-plugin v0.39.1 */
2
2
  "use strict";
3
3
 
4
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5
  var _typeof = require("@babel/runtime/helpers/typeof");
5
6
  Object.defineProperty(exports, "__esModule", {
6
7
  value: true
@@ -8,6 +9,8 @@ Object.defineProperty(exports, "__esModule", {
8
9
  exports.PasteActionsMenuContent = void 0;
9
10
  require("./PasteActionsMenuContent.compiled.css");
10
11
  var _runtime = require("@compiled/react/runtime");
12
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
14
  var _react = _interopRequireWildcard(require("react"));
12
15
  var _toolbar = require("@atlaskit/editor-common/toolbar");
13
16
  var _uiReact = require("@atlaskit/editor-common/ui-react");
@@ -15,7 +18,14 @@ var _toolbarMenuContainer = require("@atlaskit/editor-toolbar/toolbar-menu-conta
15
18
  var _editorUiControlModel = require("@atlaskit/editor-ui-control-model");
16
19
  var _compiled = require("@atlaskit/primitives/compiled");
17
20
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
21
+ var _currentPastedSmartLink = require("../utils/current-pasted-smart-link");
22
+ var _asyncHidden = require("./async-hidden");
18
23
  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); }
24
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
25
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
26
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
27
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
28
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
19
29
  var styles = {
20
30
  container: "_2rko12b0 _bfhk1bhr _16qs130s"
21
31
  };
@@ -27,8 +37,82 @@ var PasteActionsMenuContent = exports.PasteActionsMenuContent = function PasteAc
27
37
  var onMouseDown = _ref.onMouseDown,
28
38
  onMouseEnter = _ref.onMouseEnter,
29
39
  components = _ref.components,
30
- contentRef = _ref.contentRef;
40
+ contentRef = _ref.contentRef,
41
+ pastedSlice = _ref.pastedSlice;
31
42
  var setOutsideClickTargetRef = (0, _react.useContext)(_uiReact.OutsideClickTargetRefContext);
43
+
44
+ // Extract the pasted URL (if the pasted content was a single smart link).
45
+ // Uses the reactive pasted slice from the paste plugin state so pastedUrl
46
+ // updates when paste state changes.
47
+ var pastedUrl = (0, _currentPastedSmartLink.getSingleSmartLinkUrlFromSlice)(pastedSlice);
48
+
49
+ // Components with isAsyncHidden are hidden until the async check resolves.
50
+ // This prevents flash-of-visible content for items that
51
+ // should ultimately be hidden. A generation counter discards stale results.
52
+ var _useState = (0, _react.useState)(function () {
53
+ return Object.fromEntries(components.map(function (component) {
54
+ return [component.key, true];
55
+ }));
56
+ }),
57
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
58
+ asyncHiddenResults = _useState2[0],
59
+ setAsyncHiddenResults = _useState2[1];
60
+ var generationRef = (0, _react.useRef)(0);
61
+ (0, _react.useEffect)(function () {
62
+ var asyncComponents = [];
63
+ var _iterator = _createForOfIteratorHelper(components),
64
+ _step;
65
+ try {
66
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
67
+ var component = _step.value;
68
+ if (typeof component.isAsyncHidden === 'function') {
69
+ asyncComponents.push(component);
70
+ }
71
+ }
72
+ } catch (err) {
73
+ _iterator.e(err);
74
+ } finally {
75
+ _iterator.f();
76
+ }
77
+ if (asyncComponents.length === 0) {
78
+ setAsyncHiddenResults({});
79
+ return;
80
+ }
81
+
82
+ // Reset: mark all async components as hidden until this round resolves.
83
+ var pending = {};
84
+ for (var _i = 0, _asyncComponents = asyncComponents; _i < _asyncComponents.length; _i++) {
85
+ var c = _asyncComponents[_i];
86
+ pending[c.key] = true;
87
+ }
88
+ setAsyncHiddenResults(pending);
89
+ var generation = ++generationRef.current;
90
+ var context = {
91
+ pastedUrl: pastedUrl
92
+ };
93
+ void (0, _asyncHidden.evaluateAsyncHidden)(asyncComponents, context).then(function (results) {
94
+ // Discard if a newer effect has already started.
95
+ if (generation === generationRef.current) {
96
+ setAsyncHiddenResults(results);
97
+ }
98
+ });
99
+ }, [components, pastedUrl]);
100
+
101
+ // SurfaceRenderer evaluates isHidden() itself on each component. Override
102
+ // isHidden on async components so SurfaceRenderer sees the resolved value.
103
+ var visibleComponents = components.map(function (component) {
104
+ if (typeof component.isAsyncHidden !== 'function') {
105
+ // No async check — pass through unchanged.
106
+ return component;
107
+ }
108
+ var asyncResult = asyncHiddenResults[component.key];
109
+ return _objectSpread(_objectSpread({}, component), {}, {
110
+ isHidden: function isHidden() {
111
+ var _component$isHidden;
112
+ return asyncResult === true || Boolean((_component$isHidden = component.isHidden) === null || _component$isHidden === void 0 ? void 0 : _component$isHidden.call(component));
113
+ }
114
+ });
115
+ });
32
116
  var mergedRef = (0, _react.useCallback)(function (node) {
33
117
  setOutsideClickTargetRef === null || setOutsideClickTargetRef === void 0 || setOutsideClickTargetRef(node);
34
118
  if (contentRef) {
@@ -42,7 +126,7 @@ var PasteActionsMenuContent = exports.PasteActionsMenuContent = function PasteAc
42
126
  onMouseEnter: onMouseEnter
43
127
  }, /*#__PURE__*/_react.default.createElement(_editorUiControlModel.SurfaceRenderer, {
44
128
  surface: pasteMenuSurface,
45
- components: components
129
+ components: visibleComponents
46
130
  }));
47
131
  }
48
132
  return /*#__PURE__*/_react.default.createElement(_compiled.Box, {
@@ -52,6 +136,6 @@ var PasteActionsMenuContent = exports.PasteActionsMenuContent = function PasteAc
52
136
  onMouseEnter: onMouseEnter
53
137
  }, /*#__PURE__*/_react.default.createElement(_editorUiControlModel.SurfaceRenderer, {
54
138
  surface: pasteMenuSurface,
55
- components: components
139
+ components: visibleComponents
56
140
  }));
57
141
  };
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.ASYNC_HIDDEN_TIMEOUT_MS = void 0;
8
+ exports.evaluateAsyncHidden = evaluateAsyncHidden;
9
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
10
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
+ /** How long to wait for each `isAsyncHidden` check before treating it as hidden. */
12
+ var ASYNC_HIDDEN_TIMEOUT_MS = exports.ASYNC_HIDDEN_TIMEOUT_MS = 3000;
13
+ var hasAsyncHidden = function hasAsyncHidden(component) {
14
+ return typeof component.isAsyncHidden === 'function';
15
+ };
16
+ var rejectOnTimeout = function rejectOnTimeout(promise, timeoutMs) {
17
+ var timeoutId;
18
+ var timeoutPromise = new Promise(function (_, reject) {
19
+ timeoutId = setTimeout(function () {
20
+ return reject(new Error('isAsyncHidden timeout'));
21
+ }, timeoutMs);
22
+ });
23
+ return Promise.race([promise.finally(function () {
24
+ if (timeoutId !== undefined) {
25
+ clearTimeout(timeoutId);
26
+ }
27
+ }), timeoutPromise]);
28
+ };
29
+ var evaluateComponentAsyncHidden = /*#__PURE__*/function () {
30
+ var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(component, context) {
31
+ var hidden, _t;
32
+ return _regenerator.default.wrap(function (_context) {
33
+ while (1) switch (_context.prev = _context.next) {
34
+ case 0:
35
+ _context.prev = 0;
36
+ _context.next = 1;
37
+ return rejectOnTimeout(component.isAsyncHidden(context), ASYNC_HIDDEN_TIMEOUT_MS);
38
+ case 1:
39
+ hidden = _context.sent;
40
+ return _context.abrupt("return", {
41
+ key: component.key,
42
+ hidden: hidden
43
+ });
44
+ case 2:
45
+ _context.prev = 2;
46
+ _t = _context["catch"](0);
47
+ return _context.abrupt("return", {
48
+ key: component.key,
49
+ hidden: true
50
+ });
51
+ case 3:
52
+ case "end":
53
+ return _context.stop();
54
+ }
55
+ }, _callee, null, [[0, 2]]);
56
+ }));
57
+ return function evaluateComponentAsyncHidden(_x, _x2) {
58
+ return _ref.apply(this, arguments);
59
+ };
60
+ }();
61
+
62
+ /**
63
+ * Evaluates all `isAsyncHidden` functions in parallel.
64
+ * Each check is raced against a timeout; timeouts and errors are hidden.
65
+ *
66
+ * Returns a plain object that maps each component key to its hidden state.
67
+ * Components without `isAsyncHidden` are not present in the result.
68
+ */
69
+ function evaluateAsyncHidden(_x3, _x4) {
70
+ return _evaluateAsyncHidden.apply(this, arguments);
71
+ }
72
+ function _evaluateAsyncHidden() {
73
+ _evaluateAsyncHidden = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(components, context) {
74
+ var results;
75
+ return _regenerator.default.wrap(function (_context2) {
76
+ while (1) switch (_context2.prev = _context2.next) {
77
+ case 0:
78
+ _context2.next = 1;
79
+ return Promise.all(components.filter(hasAsyncHidden).map(function (component) {
80
+ return evaluateComponentAsyncHidden(component, context);
81
+ }));
82
+ case 1:
83
+ results = _context2.sent;
84
+ return _context2.abrupt("return", Object.fromEntries(results.map(function (_ref2) {
85
+ var key = _ref2.key,
86
+ hidden = _ref2.hidden;
87
+ return [key, hidden];
88
+ })));
89
+ case 2:
90
+ case "end":
91
+ return _context2.stop();
92
+ }
93
+ }, _callee2);
94
+ }));
95
+ return _evaluateAsyncHidden.apply(this, arguments);
96
+ }
@@ -405,6 +405,7 @@ export const PasteActionsMenu = ({
405
405
  onMouseDown: preventEditorFocusLoss,
406
406
  onMouseEnter: handleMouseEnter,
407
407
  components: pasteMenuComponents,
408
- contentRef: popupContentRef
408
+ contentRef: popupContentRef,
409
+ pastedSlice: lastContentPasted === null || lastContentPasted === void 0 ? void 0 : lastContentPasted.pastedSlice
409
410
  }))));
410
411
  };
@@ -1,13 +1,15 @@
1
1
  /* PasteActionsMenuContent.tsx generated by @compiled/babel-plugin v0.39.1 */
2
2
  import "./PasteActionsMenuContent.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
- import React, { useCallback, useContext } from 'react';
4
+ import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
5
5
  import { PASTE_MENU } from '@atlaskit/editor-common/toolbar';
6
6
  import { OutsideClickTargetRefContext } from '@atlaskit/editor-common/ui-react';
7
7
  import { ToolbarMenuContainer } from '@atlaskit/editor-toolbar/toolbar-menu-container';
8
8
  import { SurfaceRenderer } from '@atlaskit/editor-ui-control-model';
9
9
  import { Box } from '@atlaskit/primitives/compiled';
10
10
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
11
+ import { getSingleSmartLinkUrlFromSlice } from '../utils/current-pasted-smart-link';
12
+ import { evaluateAsyncHidden } from './async-hidden';
11
13
  const styles = {
12
14
  container: "_2rko12b0 _bfhk1bhr _16qs130s"
13
15
  };
@@ -19,9 +21,69 @@ export const PasteActionsMenuContent = ({
19
21
  onMouseDown,
20
22
  onMouseEnter,
21
23
  components,
22
- contentRef
24
+ contentRef,
25
+ pastedSlice
23
26
  }) => {
24
27
  const setOutsideClickTargetRef = useContext(OutsideClickTargetRefContext);
28
+
29
+ // Extract the pasted URL (if the pasted content was a single smart link).
30
+ // Uses the reactive pasted slice from the paste plugin state so pastedUrl
31
+ // updates when paste state changes.
32
+ const pastedUrl = getSingleSmartLinkUrlFromSlice(pastedSlice);
33
+
34
+ // Components with isAsyncHidden are hidden until the async check resolves.
35
+ // This prevents flash-of-visible content for items that
36
+ // should ultimately be hidden. A generation counter discards stale results.
37
+ const [asyncHiddenResults, setAsyncHiddenResults] = useState(() => {
38
+ return Object.fromEntries(components.map(component => [component.key, true]));
39
+ });
40
+ const generationRef = useRef(0);
41
+ useEffect(() => {
42
+ const asyncComponents = [];
43
+ for (const component of components) {
44
+ if (typeof component.isAsyncHidden === 'function') {
45
+ asyncComponents.push(component);
46
+ }
47
+ }
48
+ if (asyncComponents.length === 0) {
49
+ setAsyncHiddenResults({});
50
+ return;
51
+ }
52
+
53
+ // Reset: mark all async components as hidden until this round resolves.
54
+ const pending = {};
55
+ for (const c of asyncComponents) {
56
+ pending[c.key] = true;
57
+ }
58
+ setAsyncHiddenResults(pending);
59
+ const generation = ++generationRef.current;
60
+ const context = {
61
+ pastedUrl
62
+ };
63
+ void evaluateAsyncHidden(asyncComponents, context).then(results => {
64
+ // Discard if a newer effect has already started.
65
+ if (generation === generationRef.current) {
66
+ setAsyncHiddenResults(results);
67
+ }
68
+ });
69
+ }, [components, pastedUrl]);
70
+
71
+ // SurfaceRenderer evaluates isHidden() itself on each component. Override
72
+ // isHidden on async components so SurfaceRenderer sees the resolved value.
73
+ const visibleComponents = components.map(component => {
74
+ if (typeof component.isAsyncHidden !== 'function') {
75
+ // No async check — pass through unchanged.
76
+ return component;
77
+ }
78
+ const asyncResult = asyncHiddenResults[component.key];
79
+ return {
80
+ ...component,
81
+ isHidden: () => {
82
+ var _component$isHidden;
83
+ return asyncResult === true || Boolean((_component$isHidden = component.isHidden) === null || _component$isHidden === void 0 ? void 0 : _component$isHidden.call(component));
84
+ }
85
+ };
86
+ });
25
87
  const mergedRef = useCallback(node => {
26
88
  setOutsideClickTargetRef === null || setOutsideClickTargetRef === void 0 ? void 0 : setOutsideClickTargetRef(node);
27
89
  if (contentRef) {
@@ -35,7 +97,7 @@ export const PasteActionsMenuContent = ({
35
97
  onMouseEnter: onMouseEnter
36
98
  }, /*#__PURE__*/React.createElement(SurfaceRenderer, {
37
99
  surface: pasteMenuSurface,
38
- components: components
100
+ components: visibleComponents
39
101
  }));
40
102
  }
41
103
  return /*#__PURE__*/React.createElement(Box, {
@@ -45,6 +107,6 @@ export const PasteActionsMenuContent = ({
45
107
  onMouseEnter: onMouseEnter
46
108
  }, /*#__PURE__*/React.createElement(SurfaceRenderer, {
47
109
  surface: pasteMenuSurface,
48
- components: components
110
+ components: visibleComponents
49
111
  }));
50
112
  };
@@ -0,0 +1,43 @@
1
+ /** How long to wait for each `isAsyncHidden` check before treating it as hidden. */
2
+ export const ASYNC_HIDDEN_TIMEOUT_MS = 3000;
3
+ const hasAsyncHidden = component => typeof component.isAsyncHidden === 'function';
4
+ const rejectOnTimeout = (promise, timeoutMs) => {
5
+ let timeoutId;
6
+ const timeoutPromise = new Promise((_, reject) => {
7
+ timeoutId = setTimeout(() => reject(new Error('isAsyncHidden timeout')), timeoutMs);
8
+ });
9
+ return Promise.race([promise.finally(() => {
10
+ if (timeoutId !== undefined) {
11
+ clearTimeout(timeoutId);
12
+ }
13
+ }), timeoutPromise]);
14
+ };
15
+ const evaluateComponentAsyncHidden = async (component, context) => {
16
+ try {
17
+ const hidden = await rejectOnTimeout(component.isAsyncHidden(context), ASYNC_HIDDEN_TIMEOUT_MS);
18
+ return {
19
+ key: component.key,
20
+ hidden
21
+ };
22
+ } catch {
23
+ return {
24
+ key: component.key,
25
+ hidden: true
26
+ };
27
+ }
28
+ };
29
+
30
+ /**
31
+ * Evaluates all `isAsyncHidden` functions in parallel.
32
+ * Each check is raced against a timeout; timeouts and errors are hidden.
33
+ *
34
+ * Returns a plain object that maps each component key to its hidden state.
35
+ * Components without `isAsyncHidden` are not present in the result.
36
+ */
37
+ export async function evaluateAsyncHidden(components, context) {
38
+ const results = await Promise.all(components.filter(hasAsyncHidden).map(component => evaluateComponentAsyncHidden(component, context)));
39
+ return Object.fromEntries(results.map(({
40
+ key,
41
+ hidden
42
+ }) => [key, hidden]));
43
+ }
@@ -412,6 +412,7 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
412
412
  onMouseDown: preventEditorFocusLoss,
413
413
  onMouseEnter: handleMouseEnter,
414
414
  components: pasteMenuComponents,
415
- contentRef: popupContentRef
415
+ contentRef: popupContentRef,
416
+ pastedSlice: lastContentPasted === null || lastContentPasted === void 0 ? void 0 : lastContentPasted.pastedSlice
416
417
  }))));
417
418
  };
@@ -1,13 +1,22 @@
1
1
  /* PasteActionsMenuContent.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
4
  import "./PasteActionsMenuContent.compiled.css";
3
5
  import { ax, ix } from "@compiled/react/runtime";
4
- import React, { useCallback, useContext } from 'react';
6
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
7
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
8
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
9
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
10
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
11
+ import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
5
12
  import { PASTE_MENU } from '@atlaskit/editor-common/toolbar';
6
13
  import { OutsideClickTargetRefContext } from '@atlaskit/editor-common/ui-react';
7
14
  import { ToolbarMenuContainer } from '@atlaskit/editor-toolbar/toolbar-menu-container';
8
15
  import { SurfaceRenderer } from '@atlaskit/editor-ui-control-model';
9
16
  import { Box } from '@atlaskit/primitives/compiled';
10
17
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
18
+ import { getSingleSmartLinkUrlFromSlice } from '../utils/current-pasted-smart-link';
19
+ import { evaluateAsyncHidden } from './async-hidden';
11
20
  var styles = {
12
21
  container: "_2rko12b0 _bfhk1bhr _16qs130s"
13
22
  };
@@ -19,8 +28,82 @@ export var PasteActionsMenuContent = function PasteActionsMenuContent(_ref) {
19
28
  var onMouseDown = _ref.onMouseDown,
20
29
  onMouseEnter = _ref.onMouseEnter,
21
30
  components = _ref.components,
22
- contentRef = _ref.contentRef;
31
+ contentRef = _ref.contentRef,
32
+ pastedSlice = _ref.pastedSlice;
23
33
  var setOutsideClickTargetRef = useContext(OutsideClickTargetRefContext);
34
+
35
+ // Extract the pasted URL (if the pasted content was a single smart link).
36
+ // Uses the reactive pasted slice from the paste plugin state so pastedUrl
37
+ // updates when paste state changes.
38
+ var pastedUrl = getSingleSmartLinkUrlFromSlice(pastedSlice);
39
+
40
+ // Components with isAsyncHidden are hidden until the async check resolves.
41
+ // This prevents flash-of-visible content for items that
42
+ // should ultimately be hidden. A generation counter discards stale results.
43
+ var _useState = useState(function () {
44
+ return Object.fromEntries(components.map(function (component) {
45
+ return [component.key, true];
46
+ }));
47
+ }),
48
+ _useState2 = _slicedToArray(_useState, 2),
49
+ asyncHiddenResults = _useState2[0],
50
+ setAsyncHiddenResults = _useState2[1];
51
+ var generationRef = useRef(0);
52
+ useEffect(function () {
53
+ var asyncComponents = [];
54
+ var _iterator = _createForOfIteratorHelper(components),
55
+ _step;
56
+ try {
57
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
58
+ var component = _step.value;
59
+ if (typeof component.isAsyncHidden === 'function') {
60
+ asyncComponents.push(component);
61
+ }
62
+ }
63
+ } catch (err) {
64
+ _iterator.e(err);
65
+ } finally {
66
+ _iterator.f();
67
+ }
68
+ if (asyncComponents.length === 0) {
69
+ setAsyncHiddenResults({});
70
+ return;
71
+ }
72
+
73
+ // Reset: mark all async components as hidden until this round resolves.
74
+ var pending = {};
75
+ for (var _i = 0, _asyncComponents = asyncComponents; _i < _asyncComponents.length; _i++) {
76
+ var c = _asyncComponents[_i];
77
+ pending[c.key] = true;
78
+ }
79
+ setAsyncHiddenResults(pending);
80
+ var generation = ++generationRef.current;
81
+ var context = {
82
+ pastedUrl: pastedUrl
83
+ };
84
+ void evaluateAsyncHidden(asyncComponents, context).then(function (results) {
85
+ // Discard if a newer effect has already started.
86
+ if (generation === generationRef.current) {
87
+ setAsyncHiddenResults(results);
88
+ }
89
+ });
90
+ }, [components, pastedUrl]);
91
+
92
+ // SurfaceRenderer evaluates isHidden() itself on each component. Override
93
+ // isHidden on async components so SurfaceRenderer sees the resolved value.
94
+ var visibleComponents = components.map(function (component) {
95
+ if (typeof component.isAsyncHidden !== 'function') {
96
+ // No async check — pass through unchanged.
97
+ return component;
98
+ }
99
+ var asyncResult = asyncHiddenResults[component.key];
100
+ return _objectSpread(_objectSpread({}, component), {}, {
101
+ isHidden: function isHidden() {
102
+ var _component$isHidden;
103
+ return asyncResult === true || Boolean((_component$isHidden = component.isHidden) === null || _component$isHidden === void 0 ? void 0 : _component$isHidden.call(component));
104
+ }
105
+ });
106
+ });
24
107
  var mergedRef = useCallback(function (node) {
25
108
  setOutsideClickTargetRef === null || setOutsideClickTargetRef === void 0 || setOutsideClickTargetRef(node);
26
109
  if (contentRef) {
@@ -34,7 +117,7 @@ export var PasteActionsMenuContent = function PasteActionsMenuContent(_ref) {
34
117
  onMouseEnter: onMouseEnter
35
118
  }, /*#__PURE__*/React.createElement(SurfaceRenderer, {
36
119
  surface: pasteMenuSurface,
37
- components: components
120
+ components: visibleComponents
38
121
  }));
39
122
  }
40
123
  return /*#__PURE__*/React.createElement(Box, {
@@ -44,6 +127,6 @@ export var PasteActionsMenuContent = function PasteActionsMenuContent(_ref) {
44
127
  onMouseEnter: onMouseEnter
45
128
  }, /*#__PURE__*/React.createElement(SurfaceRenderer, {
46
129
  surface: pasteMenuSurface,
47
- components: components
130
+ components: visibleComponents
48
131
  }));
49
132
  };
@@ -0,0 +1,88 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
3
+ /** How long to wait for each `isAsyncHidden` check before treating it as hidden. */
4
+ export var ASYNC_HIDDEN_TIMEOUT_MS = 3000;
5
+ var hasAsyncHidden = function hasAsyncHidden(component) {
6
+ return typeof component.isAsyncHidden === 'function';
7
+ };
8
+ var rejectOnTimeout = function rejectOnTimeout(promise, timeoutMs) {
9
+ var timeoutId;
10
+ var timeoutPromise = new Promise(function (_, reject) {
11
+ timeoutId = setTimeout(function () {
12
+ return reject(new Error('isAsyncHidden timeout'));
13
+ }, timeoutMs);
14
+ });
15
+ return Promise.race([promise.finally(function () {
16
+ if (timeoutId !== undefined) {
17
+ clearTimeout(timeoutId);
18
+ }
19
+ }), timeoutPromise]);
20
+ };
21
+ var evaluateComponentAsyncHidden = /*#__PURE__*/function () {
22
+ var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(component, context) {
23
+ var hidden, _t;
24
+ return _regeneratorRuntime.wrap(function (_context) {
25
+ while (1) switch (_context.prev = _context.next) {
26
+ case 0:
27
+ _context.prev = 0;
28
+ _context.next = 1;
29
+ return rejectOnTimeout(component.isAsyncHidden(context), ASYNC_HIDDEN_TIMEOUT_MS);
30
+ case 1:
31
+ hidden = _context.sent;
32
+ return _context.abrupt("return", {
33
+ key: component.key,
34
+ hidden: hidden
35
+ });
36
+ case 2:
37
+ _context.prev = 2;
38
+ _t = _context["catch"](0);
39
+ return _context.abrupt("return", {
40
+ key: component.key,
41
+ hidden: true
42
+ });
43
+ case 3:
44
+ case "end":
45
+ return _context.stop();
46
+ }
47
+ }, _callee, null, [[0, 2]]);
48
+ }));
49
+ return function evaluateComponentAsyncHidden(_x, _x2) {
50
+ return _ref.apply(this, arguments);
51
+ };
52
+ }();
53
+
54
+ /**
55
+ * Evaluates all `isAsyncHidden` functions in parallel.
56
+ * Each check is raced against a timeout; timeouts and errors are hidden.
57
+ *
58
+ * Returns a plain object that maps each component key to its hidden state.
59
+ * Components without `isAsyncHidden` are not present in the result.
60
+ */
61
+ export function evaluateAsyncHidden(_x3, _x4) {
62
+ return _evaluateAsyncHidden.apply(this, arguments);
63
+ }
64
+ function _evaluateAsyncHidden() {
65
+ _evaluateAsyncHidden = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(components, context) {
66
+ var results;
67
+ return _regeneratorRuntime.wrap(function (_context2) {
68
+ while (1) switch (_context2.prev = _context2.next) {
69
+ case 0:
70
+ _context2.next = 1;
71
+ return Promise.all(components.filter(hasAsyncHidden).map(function (component) {
72
+ return evaluateComponentAsyncHidden(component, context);
73
+ }));
74
+ case 1:
75
+ results = _context2.sent;
76
+ return _context2.abrupt("return", Object.fromEntries(results.map(function (_ref2) {
77
+ var key = _ref2.key,
78
+ hidden = _ref2.hidden;
79
+ return [key, hidden];
80
+ })));
81
+ case 2:
82
+ case "end":
83
+ return _context2.stop();
84
+ }
85
+ }, _callee2);
86
+ }));
87
+ return _evaluateAsyncHidden.apply(this, arguments);
88
+ }
@@ -1,10 +1,13 @@
1
1
  import React from 'react';
2
- import type { RegisterComponent } from '@atlaskit/editor-ui-control-model';
2
+ import type { Slice } from '@atlaskit/editor-prosemirror/model';
3
+ import type { RegisterComponent } from '@atlaskit/editor-ui-control-model/types';
3
4
  interface PasteActionsMenuContentProps {
4
5
  components: RegisterComponent[];
5
6
  contentRef?: React.RefObject<HTMLDivElement | null>;
6
7
  onMouseDown: (e: React.MouseEvent) => void;
7
8
  onMouseEnter: () => void;
9
+ /** The pasted slice, used to extract a single smart link URL for `isAsyncHidden` checks. */
10
+ pastedSlice?: Slice;
8
11
  }
9
- export declare const PasteActionsMenuContent: ({ onMouseDown, onMouseEnter, components, contentRef, }: PasteActionsMenuContentProps) => React.JSX.Element;
12
+ export declare const PasteActionsMenuContent: ({ onMouseDown, onMouseEnter, components, contentRef, pastedSlice, }: PasteActionsMenuContentProps) => React.JSX.Element;
10
13
  export {};
@@ -0,0 +1,12 @@
1
+ import type { AsyncHiddenContext, RegisterComponent } from '@atlaskit/editor-ui-control-model/types';
2
+ /** How long to wait for each `isAsyncHidden` check before treating it as hidden. */
3
+ export declare const ASYNC_HIDDEN_TIMEOUT_MS = 3000;
4
+ export type AsyncHiddenResults = Record<string, boolean>;
5
+ /**
6
+ * Evaluates all `isAsyncHidden` functions in parallel.
7
+ * Each check is raced against a timeout; timeouts and errors are hidden.
8
+ *
9
+ * Returns a plain object that maps each component key to its hidden state.
10
+ * Components without `isAsyncHidden` are not present in the result.
11
+ */
12
+ export declare function evaluateAsyncHidden(components: RegisterComponent[], context: AsyncHiddenContext): Promise<AsyncHiddenResults>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-paste-options-toolbar",
3
- "version": "13.0.20",
3
+ "version": "13.1.1",
4
4
  "description": "Paste options toolbar for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -30,18 +30,18 @@
30
30
  "@atlaskit/editor-prosemirror": "^8.0.0",
31
31
  "@atlaskit/editor-shared-styles": "^4.0.0",
32
32
  "@atlaskit/editor-toolbar": "^2.1.0",
33
- "@atlaskit/editor-ui-control-model": "^2.1.0",
33
+ "@atlaskit/editor-ui-control-model": "^2.2.0",
34
34
  "@atlaskit/icon": "^36.2.0",
35
35
  "@atlaskit/platform-feature-flags": "^2.0.0",
36
- "@atlaskit/primitives": "^20.3.0",
37
- "@atlaskit/tmp-editor-statsig": "^121.0.0",
38
- "@atlaskit/tokens": "^15.4.0",
36
+ "@atlaskit/primitives": "^20.5.0",
37
+ "@atlaskit/tmp-editor-statsig": "^123.0.0",
38
+ "@atlaskit/tokens": "^15.5.0",
39
39
  "@babel/runtime": "^7.0.0",
40
40
  "@compiled/react": "^0.20.0",
41
41
  "@emotion/react": "^11.7.1"
42
42
  },
43
43
  "peerDependencies": {
44
- "@atlaskit/editor-common": "^116.22.0",
44
+ "@atlaskit/editor-common": "^116.24.0",
45
45
  "react": "^18.2.0",
46
46
  "react-dom": "^18.2.0",
47
47
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"