@atlaskit/editor-plugin-media-insert 2.3.1 → 2.4.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atlaskit/editor-plugin-media-insert
2
2
 
3
+ ## 2.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#136903](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/136903)
8
+ [`2a9928406d4d3`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/2a9928406d4d3) -
9
+ [ux] [ED-24756] **@atlaskit/editor-plugin-media-insert**: Added a hook to correctly focus on the
10
+ Upload button when user opens media insert (preventing scrolling to the top of the editor)
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+
16
+ ## 2.3.2
17
+
18
+ ### Patch Changes
19
+
20
+ - [#137217](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/137217)
21
+ [`90e304a10f0b2`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/90e304a10f0b2) -
22
+ [ED-24843] Fix external flag on media-insert external node creation
23
+ - Updated dependencies
24
+
3
25
  ## 2.3.1
4
26
 
5
27
  ### Patch Changes
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+
3
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.useUnholyAutofocus = void 0;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
10
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
11
+ /**
12
+ * Autofocuses the first interactive element in the first tab panel
13
+ * when the media picker is opened.
14
+ *
15
+ * This is to mitigate the issue where the PopupWithListeners component
16
+ * renders initially at the top of the editor and then repositioned.
17
+ *
18
+ * We want to autofocus after the repositioning to ensure we don't scroll
19
+ * to the top of the editor when the media picker is opened.
20
+ */
21
+ var useUnholyAutofocus = exports.useUnholyAutofocus = function useUnholyAutofocus() {
22
+ var autofocusRef = _react.default.useRef(null);
23
+ var positionRef = _react.default.useRef(null);
24
+ var onPositionCalculated = _react.default.useCallback(function (position) {
25
+ if (positionRef.current === null) {
26
+ // Initial position is _always incorrect, so the first time this is set
27
+ // we're going to ignore it.
28
+ positionRef.current = position;
29
+ } else if (positionRef.current !== position) {
30
+ var _autofocusRef$current;
31
+ // If it isn't the first position and it has changed, we're likely in
32
+ // the actual position we want. We'll call focus and update the position.
33
+ (_autofocusRef$current = autofocusRef.current) === null || _autofocusRef$current === void 0 || _autofocusRef$current.focus();
34
+ positionRef.current = position;
35
+ }
36
+
37
+ // Important to return this as the popup uses the returned position
38
+ return position;
39
+ }, [autofocusRef]);
40
+
41
+ /**
42
+ * If we don't clear the ref, then reopening the media picker will
43
+ * not correctly focus the button.
44
+ *
45
+ * WARNING: If the component re-renders the ref will be cleared and
46
+ * the button will be focused again.
47
+ *
48
+ * This is a trade-off, we prefer that the button is correctly focused
49
+ * if the media picker is re-opened, rather than re-focusing the button
50
+ * if the component re-renders/ the browser window is resized.
51
+ *
52
+ * This is a temporary solution until we can find a better way to do it.
53
+ */
54
+ (0, _react.useEffect)(function () {
55
+ return function () {
56
+ positionRef.current = null;
57
+ };
58
+ });
59
+ return {
60
+ autofocusRef: autofocusRef,
61
+ onPositionCalculated: onPositionCalculated
62
+ };
63
+ };
@@ -81,7 +81,8 @@ var mediaInsertPlugin = exports.mediaInsertPlugin = function mediaInsertPlugin(_
81
81
  var node = editorView.state.schema.nodes.media.create({
82
82
  type: 'external',
83
83
  url: url,
84
- alt: alt
84
+ alt: alt,
85
+ __external: true
85
86
  });
86
87
  return (_api$media$actions$in2 = api === null || api === void 0 ? void 0 : api.media.actions.insertMediaAsMediaSingle(editorView, node, inputMethod)) !== null && _api$media$actions$in2 !== void 0 ? _api$media$actions$in2 : false;
87
88
  };
@@ -47,7 +47,7 @@ var uploadReducer = function uploadReducer(state, action) {
47
47
  var isImagePreview = function isImagePreview(preview) {
48
48
  return 'dimensions' in preview;
49
49
  };
50
- var LocalMedia = exports.LocalMedia = function LocalMedia(_ref) {
50
+ var LocalMedia = exports.LocalMedia = /*#__PURE__*/_react.default.forwardRef(function (_ref, ref) {
51
51
  var mediaProvider = _ref.mediaProvider,
52
52
  insertMediaSingle = _ref.insertMediaSingle,
53
53
  dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent,
@@ -106,6 +106,7 @@ var LocalMedia = exports.LocalMedia = function LocalMedia(_ref) {
106
106
  appearance: "error"
107
107
  }, uploadState.error === 'upload_fail' ? strings.networkError : strings.genericError), /*#__PURE__*/_react.default.createElement(_new.default, {
108
108
  iconBefore: _upload.default,
109
+ ref: ref,
109
110
  shouldFitContainer: true,
110
111
  isDisabled: !uploadMediaClientConfig || !uploadParams,
111
112
  onClick: function onClick() {
@@ -113,8 +114,7 @@ var LocalMedia = exports.LocalMedia = function LocalMedia(_ref) {
113
114
  dispatch({
114
115
  type: 'open'
115
116
  });
116
- },
117
- autoFocus: true
117
+ }
118
118
  }, strings.upload), uploadMediaClientConfig && uploadParams && /*#__PURE__*/_react.default.createElement(_mediaPicker.Browser, {
119
119
  isOpen: uploadState.isOpen,
120
120
  config: {
@@ -142,4 +142,4 @@ var LocalMedia = exports.LocalMedia = function LocalMedia(_ref) {
142
142
  });
143
143
  }
144
144
  }));
145
- };
145
+ });
@@ -267,7 +267,6 @@ function MediaFromURL(_ref) {
267
267
  space: "space.150",
268
268
  grow: "fill"
269
269
  }, /*#__PURE__*/_react.default.createElement(_textfield.default, {
270
- autoFocus: true,
271
270
  value: inputUrl,
272
271
  placeholder: strings.pasteLinkToUpload,
273
272
  onChange: onURLChange,
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
5
  Object.defineProperty(exports, "__esModule", {
5
6
  value: true
6
7
  });
@@ -13,9 +14,15 @@ var _messages = require("@atlaskit/editor-common/messages");
13
14
  var _ui = require("@atlaskit/editor-common/ui");
14
15
  var _utils = require("@atlaskit/editor-prosemirror/utils");
15
16
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
17
+ var _primitives = require("@atlaskit/primitives");
18
+ var _tabs = _interopRequireWildcard(require("@atlaskit/tabs"));
16
19
  var _useFocusEditor = require("../hooks/use-focus-editor");
17
- var _MediaInsertContent = require("./MediaInsertContent");
20
+ var _useUnholyAutofocus2 = require("../hooks/use-unholy-autofocus");
21
+ var _LocalMedia = require("./LocalMedia");
22
+ var _MediaFromURL = require("./MediaFromURL");
18
23
  var _MediaInsertWrapper = require("./MediaInsertWrapper");
24
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
25
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
19
26
  var PopupWithListeners = (0, _ui.withOuterListeners)(_ui.Popup);
20
27
  var getDomRefFromSelection = function getDomRefFromSelection(view, dispatchAnalyticsEvent) {
21
28
  try {
@@ -58,6 +65,9 @@ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_
58
65
  var focusEditor = (0, _useFocusEditor.useFocusEditor)({
59
66
  editorView: editorView
60
67
  });
68
+ var _useUnholyAutofocus = (0, _useUnholyAutofocus2.useUnholyAutofocus)(),
69
+ autofocusRef = _useUnholyAutofocus.autofocusRef,
70
+ onPositionCalculated = _useUnholyAutofocus.onPositionCalculated;
61
71
  if (!isOpen || !mediaProvider) {
62
72
  return null;
63
73
  }
@@ -93,8 +103,22 @@ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_
93
103
  handleEscapeKeydown: handleClose(_analytics.INPUT_METHOD.KEYBOARD),
94
104
  scrollableElement: popupsScrollableElement,
95
105
  preventOverflow: true,
106
+ onPositionCalculated: onPositionCalculated,
96
107
  focusTrap: true
97
- }, /*#__PURE__*/_react.default.createElement(_MediaInsertWrapper.MediaInsertWrapper, null, /*#__PURE__*/_react.default.createElement(_MediaInsertContent.MediaInsertContent, {
108
+ }, /*#__PURE__*/_react.default.createElement(_MediaInsertWrapper.MediaInsertWrapper, null, /*#__PURE__*/_react.default.createElement(_tabs.default, {
109
+ id: "media-insert-tab-navigation"
110
+ }, /*#__PURE__*/_react.default.createElement(_primitives.Box, {
111
+ paddingBlockEnd: "space.150"
112
+ }, /*#__PURE__*/_react.default.createElement(_tabs.TabList, null, /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.fileTabTitle)), /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.linkTabTitle)))), /*#__PURE__*/_react.default.createElement(_tabs.TabPanel, null, /*#__PURE__*/_react.default.createElement(_LocalMedia.LocalMedia, {
113
+ ref: autofocusRef,
114
+ mediaProvider: mediaProvider,
115
+ insertMediaSingle: insertMediaSingle,
116
+ closeMediaInsertPicker: function closeMediaInsertPicker() {
117
+ _closeMediaInsertPicker();
118
+ focusEditor();
119
+ },
120
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent
121
+ })), /*#__PURE__*/_react.default.createElement(_tabs.TabPanel, null, /*#__PURE__*/_react.default.createElement(_MediaFromURL.MediaFromURL, {
98
122
  mediaProvider: mediaProvider,
99
123
  dispatchAnalyticsEvent: dispatchAnalyticsEvent,
100
124
  closeMediaInsertPicker: function closeMediaInsertPicker() {
@@ -103,5 +127,5 @@ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_
103
127
  },
104
128
  insertMediaSingle: insertMediaSingle,
105
129
  insertExternalMediaSingle: insertExternalMediaSingle
106
- })));
130
+ })))));
107
131
  };
@@ -0,0 +1,54 @@
1
+ import React, { useEffect } from 'react';
2
+ /**
3
+ * Autofocuses the first interactive element in the first tab panel
4
+ * when the media picker is opened.
5
+ *
6
+ * This is to mitigate the issue where the PopupWithListeners component
7
+ * renders initially at the top of the editor and then repositioned.
8
+ *
9
+ * We want to autofocus after the repositioning to ensure we don't scroll
10
+ * to the top of the editor when the media picker is opened.
11
+ */
12
+ export const useUnholyAutofocus = () => {
13
+ const autofocusRef = React.useRef(null);
14
+ const positionRef = React.useRef(null);
15
+ const onPositionCalculated = React.useCallback(position => {
16
+ if (positionRef.current === null) {
17
+ // Initial position is _always incorrect, so the first time this is set
18
+ // we're going to ignore it.
19
+ positionRef.current = position;
20
+ } else if (positionRef.current !== position) {
21
+ var _autofocusRef$current;
22
+ // If it isn't the first position and it has changed, we're likely in
23
+ // the actual position we want. We'll call focus and update the position.
24
+ (_autofocusRef$current = autofocusRef.current) === null || _autofocusRef$current === void 0 ? void 0 : _autofocusRef$current.focus();
25
+ positionRef.current = position;
26
+ }
27
+
28
+ // Important to return this as the popup uses the returned position
29
+ return position;
30
+ }, [autofocusRef]);
31
+
32
+ /**
33
+ * If we don't clear the ref, then reopening the media picker will
34
+ * not correctly focus the button.
35
+ *
36
+ * WARNING: If the component re-renders the ref will be cleared and
37
+ * the button will be focused again.
38
+ *
39
+ * This is a trade-off, we prefer that the button is correctly focused
40
+ * if the media picker is re-opened, rather than re-focusing the button
41
+ * if the component re-renders/ the browser window is resized.
42
+ *
43
+ * This is a temporary solution until we can find a better way to do it.
44
+ */
45
+ useEffect(() => {
46
+ return () => {
47
+ positionRef.current = null;
48
+ };
49
+ });
50
+ return {
51
+ autofocusRef,
52
+ onPositionCalculated
53
+ };
54
+ };
@@ -79,7 +79,8 @@ export const mediaInsertPlugin = ({
79
79
  const node = editorView.state.schema.nodes.media.create({
80
80
  type: 'external',
81
81
  url,
82
- alt
82
+ alt,
83
+ __external: true
83
84
  });
84
85
  return (_api$media$actions$in2 = api === null || api === void 0 ? void 0 : api.media.actions.insertMediaAsMediaSingle(editorView, node, inputMethod)) !== null && _api$media$actions$in2 !== void 0 ? _api$media$actions$in2 : false;
85
86
  };
@@ -39,12 +39,12 @@ const uploadReducer = (state, action) => {
39
39
  const isImagePreview = preview => {
40
40
  return 'dimensions' in preview;
41
41
  };
42
- export const LocalMedia = ({
42
+ export const LocalMedia = /*#__PURE__*/React.forwardRef(({
43
43
  mediaProvider,
44
44
  insertMediaSingle,
45
45
  dispatchAnalyticsEvent,
46
46
  closeMediaInsertPicker
47
- }) => {
47
+ }, ref) => {
48
48
  const intl = useIntl();
49
49
  const strings = {
50
50
  upload: intl.formatMessage(mediaInsertMessages.upload),
@@ -100,6 +100,7 @@ export const LocalMedia = ({
100
100
  appearance: "error"
101
101
  }, uploadState.error === 'upload_fail' ? strings.networkError : strings.genericError), /*#__PURE__*/React.createElement(Button, {
102
102
  iconBefore: UploadIcon,
103
+ ref: ref,
103
104
  shouldFitContainer: true,
104
105
  isDisabled: !uploadMediaClientConfig || !uploadParams,
105
106
  onClick: () => {
@@ -107,8 +108,7 @@ export const LocalMedia = ({
107
108
  dispatch({
108
109
  type: 'open'
109
110
  });
110
- },
111
- autoFocus: true
111
+ }
112
112
  }, strings.upload), uploadMediaClientConfig && uploadParams && /*#__PURE__*/React.createElement(Browser, {
113
113
  isOpen: uploadState.isOpen,
114
114
  config: {
@@ -136,4 +136,4 @@ export const LocalMedia = ({
136
136
  });
137
137
  }
138
138
  }));
139
- };
139
+ });
@@ -235,7 +235,6 @@ export function MediaFromURL({
235
235
  space: "space.150",
236
236
  grow: "fill"
237
237
  }, /*#__PURE__*/React.createElement(TextField, {
238
- autoFocus: true,
239
238
  value: inputUrl,
240
239
  placeholder: strings.pasteLinkToUpload,
241
240
  onChange: onURLChange,
@@ -6,8 +6,12 @@ import { mediaInsertMessages } from '@atlaskit/editor-common/messages';
6
6
  import { Popup, withOuterListeners } from '@atlaskit/editor-common/ui';
7
7
  import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
8
8
  import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
9
+ import { Box } from '@atlaskit/primitives';
10
+ import Tabs, { Tab, TabList, TabPanel } from '@atlaskit/tabs';
9
11
  import { useFocusEditor } from '../hooks/use-focus-editor';
10
- import { MediaInsertContent } from './MediaInsertContent';
12
+ import { useUnholyAutofocus } from '../hooks/use-unholy-autofocus';
13
+ import { LocalMedia } from './LocalMedia';
14
+ import { MediaFromURL } from './MediaFromURL';
11
15
  import { MediaInsertWrapper } from './MediaInsertWrapper';
12
16
  const PopupWithListeners = withOuterListeners(Popup);
13
17
  const getDomRefFromSelection = (view, dispatchAnalyticsEvent) => {
@@ -52,6 +56,10 @@ export const MediaInsertPicker = ({
52
56
  const focusEditor = useFocusEditor({
53
57
  editorView
54
58
  });
59
+ const {
60
+ autofocusRef,
61
+ onPositionCalculated
62
+ } = useUnholyAutofocus();
55
63
  if (!isOpen || !mediaProvider) {
56
64
  return null;
57
65
  }
@@ -85,8 +93,22 @@ export const MediaInsertPicker = ({
85
93
  handleEscapeKeydown: handleClose(INPUT_METHOD.KEYBOARD),
86
94
  scrollableElement: popupsScrollableElement,
87
95
  preventOverflow: true,
96
+ onPositionCalculated: onPositionCalculated,
88
97
  focusTrap: true
89
- }, /*#__PURE__*/React.createElement(MediaInsertWrapper, null, /*#__PURE__*/React.createElement(MediaInsertContent, {
98
+ }, /*#__PURE__*/React.createElement(MediaInsertWrapper, null, /*#__PURE__*/React.createElement(Tabs, {
99
+ id: "media-insert-tab-navigation"
100
+ }, /*#__PURE__*/React.createElement(Box, {
101
+ paddingBlockEnd: "space.150"
102
+ }, /*#__PURE__*/React.createElement(TabList, null, /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.fileTabTitle)), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), /*#__PURE__*/React.createElement(TabPanel, null, /*#__PURE__*/React.createElement(LocalMedia, {
103
+ ref: autofocusRef,
104
+ mediaProvider: mediaProvider,
105
+ insertMediaSingle: insertMediaSingle,
106
+ closeMediaInsertPicker: () => {
107
+ closeMediaInsertPicker();
108
+ focusEditor();
109
+ },
110
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent
111
+ })), /*#__PURE__*/React.createElement(TabPanel, null, /*#__PURE__*/React.createElement(MediaFromURL, {
90
112
  mediaProvider: mediaProvider,
91
113
  dispatchAnalyticsEvent: dispatchAnalyticsEvent,
92
114
  closeMediaInsertPicker: () => {
@@ -95,5 +117,5 @@ export const MediaInsertPicker = ({
95
117
  },
96
118
  insertMediaSingle: insertMediaSingle,
97
119
  insertExternalMediaSingle: insertExternalMediaSingle
98
- })));
120
+ })))));
99
121
  };
@@ -0,0 +1,54 @@
1
+ import React, { useEffect } from 'react';
2
+ /**
3
+ * Autofocuses the first interactive element in the first tab panel
4
+ * when the media picker is opened.
5
+ *
6
+ * This is to mitigate the issue where the PopupWithListeners component
7
+ * renders initially at the top of the editor and then repositioned.
8
+ *
9
+ * We want to autofocus after the repositioning to ensure we don't scroll
10
+ * to the top of the editor when the media picker is opened.
11
+ */
12
+ export var useUnholyAutofocus = function useUnholyAutofocus() {
13
+ var autofocusRef = React.useRef(null);
14
+ var positionRef = React.useRef(null);
15
+ var onPositionCalculated = React.useCallback(function (position) {
16
+ if (positionRef.current === null) {
17
+ // Initial position is _always incorrect, so the first time this is set
18
+ // we're going to ignore it.
19
+ positionRef.current = position;
20
+ } else if (positionRef.current !== position) {
21
+ var _autofocusRef$current;
22
+ // If it isn't the first position and it has changed, we're likely in
23
+ // the actual position we want. We'll call focus and update the position.
24
+ (_autofocusRef$current = autofocusRef.current) === null || _autofocusRef$current === void 0 || _autofocusRef$current.focus();
25
+ positionRef.current = position;
26
+ }
27
+
28
+ // Important to return this as the popup uses the returned position
29
+ return position;
30
+ }, [autofocusRef]);
31
+
32
+ /**
33
+ * If we don't clear the ref, then reopening the media picker will
34
+ * not correctly focus the button.
35
+ *
36
+ * WARNING: If the component re-renders the ref will be cleared and
37
+ * the button will be focused again.
38
+ *
39
+ * This is a trade-off, we prefer that the button is correctly focused
40
+ * if the media picker is re-opened, rather than re-focusing the button
41
+ * if the component re-renders/ the browser window is resized.
42
+ *
43
+ * This is a temporary solution until we can find a better way to do it.
44
+ */
45
+ useEffect(function () {
46
+ return function () {
47
+ positionRef.current = null;
48
+ };
49
+ });
50
+ return {
51
+ autofocusRef: autofocusRef,
52
+ onPositionCalculated: onPositionCalculated
53
+ };
54
+ };
@@ -74,7 +74,8 @@ export var mediaInsertPlugin = function mediaInsertPlugin(_ref) {
74
74
  var node = editorView.state.schema.nodes.media.create({
75
75
  type: 'external',
76
76
  url: url,
77
- alt: alt
77
+ alt: alt,
78
+ __external: true
78
79
  });
79
80
  return (_api$media$actions$in2 = api === null || api === void 0 ? void 0 : api.media.actions.insertMediaAsMediaSingle(editorView, node, inputMethod)) !== null && _api$media$actions$in2 !== void 0 ? _api$media$actions$in2 : false;
80
81
  };
@@ -40,7 +40,7 @@ var uploadReducer = function uploadReducer(state, action) {
40
40
  var isImagePreview = function isImagePreview(preview) {
41
41
  return 'dimensions' in preview;
42
42
  };
43
- export var LocalMedia = function LocalMedia(_ref) {
43
+ export var LocalMedia = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
44
44
  var mediaProvider = _ref.mediaProvider,
45
45
  insertMediaSingle = _ref.insertMediaSingle,
46
46
  dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent,
@@ -99,6 +99,7 @@ export var LocalMedia = function LocalMedia(_ref) {
99
99
  appearance: "error"
100
100
  }, uploadState.error === 'upload_fail' ? strings.networkError : strings.genericError), /*#__PURE__*/React.createElement(Button, {
101
101
  iconBefore: UploadIcon,
102
+ ref: ref,
102
103
  shouldFitContainer: true,
103
104
  isDisabled: !uploadMediaClientConfig || !uploadParams,
104
105
  onClick: function onClick() {
@@ -106,8 +107,7 @@ export var LocalMedia = function LocalMedia(_ref) {
106
107
  dispatch({
107
108
  type: 'open'
108
109
  });
109
- },
110
- autoFocus: true
110
+ }
111
111
  }, strings.upload), uploadMediaClientConfig && uploadParams && /*#__PURE__*/React.createElement(Browser, {
112
112
  isOpen: uploadState.isOpen,
113
113
  config: {
@@ -135,4 +135,4 @@ export var LocalMedia = function LocalMedia(_ref) {
135
135
  });
136
136
  }
137
137
  }));
138
- };
138
+ });
@@ -260,7 +260,6 @@ export function MediaFromURL(_ref) {
260
260
  space: "space.150",
261
261
  grow: "fill"
262
262
  }, /*#__PURE__*/React.createElement(TextField, {
263
- autoFocus: true,
264
263
  value: inputUrl,
265
264
  placeholder: strings.pasteLinkToUpload,
266
265
  onChange: onURLChange,
@@ -6,8 +6,12 @@ import { mediaInsertMessages } from '@atlaskit/editor-common/messages';
6
6
  import { Popup, withOuterListeners } from '@atlaskit/editor-common/ui';
7
7
  import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
8
8
  import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
9
+ import { Box } from '@atlaskit/primitives';
10
+ import Tabs, { Tab, TabList, TabPanel } from '@atlaskit/tabs';
9
11
  import { useFocusEditor } from '../hooks/use-focus-editor';
10
- import { MediaInsertContent } from './MediaInsertContent';
12
+ import { useUnholyAutofocus } from '../hooks/use-unholy-autofocus';
13
+ import { LocalMedia } from './LocalMedia';
14
+ import { MediaFromURL } from './MediaFromURL';
11
15
  import { MediaInsertWrapper } from './MediaInsertWrapper';
12
16
  var PopupWithListeners = withOuterListeners(Popup);
13
17
  var getDomRefFromSelection = function getDomRefFromSelection(view, dispatchAnalyticsEvent) {
@@ -51,6 +55,9 @@ export var MediaInsertPicker = function MediaInsertPicker(_ref) {
51
55
  var focusEditor = useFocusEditor({
52
56
  editorView: editorView
53
57
  });
58
+ var _useUnholyAutofocus = useUnholyAutofocus(),
59
+ autofocusRef = _useUnholyAutofocus.autofocusRef,
60
+ onPositionCalculated = _useUnholyAutofocus.onPositionCalculated;
54
61
  if (!isOpen || !mediaProvider) {
55
62
  return null;
56
63
  }
@@ -86,8 +93,22 @@ export var MediaInsertPicker = function MediaInsertPicker(_ref) {
86
93
  handleEscapeKeydown: handleClose(INPUT_METHOD.KEYBOARD),
87
94
  scrollableElement: popupsScrollableElement,
88
95
  preventOverflow: true,
96
+ onPositionCalculated: onPositionCalculated,
89
97
  focusTrap: true
90
- }, /*#__PURE__*/React.createElement(MediaInsertWrapper, null, /*#__PURE__*/React.createElement(MediaInsertContent, {
98
+ }, /*#__PURE__*/React.createElement(MediaInsertWrapper, null, /*#__PURE__*/React.createElement(Tabs, {
99
+ id: "media-insert-tab-navigation"
100
+ }, /*#__PURE__*/React.createElement(Box, {
101
+ paddingBlockEnd: "space.150"
102
+ }, /*#__PURE__*/React.createElement(TabList, null, /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.fileTabTitle)), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), /*#__PURE__*/React.createElement(TabPanel, null, /*#__PURE__*/React.createElement(LocalMedia, {
103
+ ref: autofocusRef,
104
+ mediaProvider: mediaProvider,
105
+ insertMediaSingle: insertMediaSingle,
106
+ closeMediaInsertPicker: function closeMediaInsertPicker() {
107
+ _closeMediaInsertPicker();
108
+ focusEditor();
109
+ },
110
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent
111
+ })), /*#__PURE__*/React.createElement(TabPanel, null, /*#__PURE__*/React.createElement(MediaFromURL, {
91
112
  mediaProvider: mediaProvider,
92
113
  dispatchAnalyticsEvent: dispatchAnalyticsEvent,
93
114
  closeMediaInsertPicker: function closeMediaInsertPicker() {
@@ -96,5 +117,5 @@ export var MediaInsertPicker = function MediaInsertPicker(_ref) {
96
117
  },
97
118
  insertMediaSingle: insertMediaSingle,
98
119
  insertExternalMediaSingle: insertExternalMediaSingle
99
- })));
120
+ })))));
100
121
  };
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import type { Position as PopupPosition } from '@atlaskit/editor-common/src/ui/Popup/utils';
3
+ /**
4
+ * Autofocuses the first interactive element in the first tab panel
5
+ * when the media picker is opened.
6
+ *
7
+ * This is to mitigate the issue where the PopupWithListeners component
8
+ * renders initially at the top of the editor and then repositioned.
9
+ *
10
+ * We want to autofocus after the repositioning to ensure we don't scroll
11
+ * to the top of the editor when the media picker is opened.
12
+ */
13
+ export declare const useUnholyAutofocus: () => {
14
+ autofocusRef: React.RefObject<HTMLButtonElement>;
15
+ onPositionCalculated: (position: PopupPosition) => PopupPosition;
16
+ };
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { type DispatchAnalyticsEvent } from '@atlaskit/editor-common/analytics';
3
3
  import type { MediaProvider } from '@atlaskit/editor-common/provider-factory';
4
4
  import { type InsertMediaSingle } from '../types';
@@ -8,5 +8,5 @@ type Props = {
8
8
  closeMediaInsertPicker: () => void;
9
9
  dispatchAnalyticsEvent?: DispatchAnalyticsEvent;
10
10
  };
11
- export declare const LocalMedia: ({ mediaProvider, insertMediaSingle, dispatchAnalyticsEvent, closeMediaInsertPicker, }: Props) => JSX.Element;
11
+ export declare const LocalMedia: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLButtonElement>>;
12
12
  export {};
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import type { Position as PopupPosition } from '@atlaskit/editor-common/src/ui/Popup/utils';
3
+ /**
4
+ * Autofocuses the first interactive element in the first tab panel
5
+ * when the media picker is opened.
6
+ *
7
+ * This is to mitigate the issue where the PopupWithListeners component
8
+ * renders initially at the top of the editor and then repositioned.
9
+ *
10
+ * We want to autofocus after the repositioning to ensure we don't scroll
11
+ * to the top of the editor when the media picker is opened.
12
+ */
13
+ export declare const useUnholyAutofocus: () => {
14
+ autofocusRef: React.RefObject<HTMLButtonElement>;
15
+ onPositionCalculated: (position: PopupPosition) => PopupPosition;
16
+ };
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { type DispatchAnalyticsEvent } from '@atlaskit/editor-common/analytics';
3
3
  import type { MediaProvider } from '@atlaskit/editor-common/provider-factory';
4
4
  import { type InsertMediaSingle } from '../types';
@@ -8,5 +8,5 @@ type Props = {
8
8
  closeMediaInsertPicker: () => void;
9
9
  dispatchAnalyticsEvent?: DispatchAnalyticsEvent;
10
10
  };
11
- export declare const LocalMedia: ({ mediaProvider, insertMediaSingle, dispatchAnalyticsEvent, closeMediaInsertPicker, }: Props) => JSX.Element;
11
+ export declare const LocalMedia: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLButtonElement>>;
12
12
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-media-insert",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "Media Insert plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -24,12 +24,12 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@atlaskit/button": "^20.1.0",
27
- "@atlaskit/editor-common": "^88.7.0",
27
+ "@atlaskit/editor-common": "^88.12.0",
28
28
  "@atlaskit/editor-plugin-analytics": "^1.8.0",
29
- "@atlaskit/editor-plugin-media": "^1.29.0",
29
+ "@atlaskit/editor-plugin-media": "^1.30.0",
30
30
  "@atlaskit/editor-prosemirror": "5.0.1",
31
31
  "@atlaskit/editor-shared-styles": "^2.13.0",
32
- "@atlaskit/icon": "^22.15.0",
32
+ "@atlaskit/icon": "^22.16.0",
33
33
  "@atlaskit/media-card": "^78.2.0",
34
34
  "@atlaskit/media-client": "^27.6.0",
35
35
  "@atlaskit/media-client-react": "^2.2.0",
@@ -1,41 +0,0 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- var _typeof = require("@babel/runtime/helpers/typeof");
5
- Object.defineProperty(exports, "__esModule", {
6
- value: true
7
- });
8
- exports.MediaInsertContent = void 0;
9
- var _react = _interopRequireDefault(require("react"));
10
- var _reactIntlNext = require("react-intl-next");
11
- var _messages = require("@atlaskit/editor-common/messages");
12
- var _primitives = require("@atlaskit/primitives");
13
- var _tabs = _interopRequireWildcard(require("@atlaskit/tabs"));
14
- var _LocalMedia = require("./LocalMedia");
15
- var _MediaFromURL = require("./MediaFromURL");
16
- function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
17
- function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
18
- var MediaInsertContent = exports.MediaInsertContent = function MediaInsertContent(_ref) {
19
- var mediaProvider = _ref.mediaProvider,
20
- dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent,
21
- closeMediaInsertPicker = _ref.closeMediaInsertPicker,
22
- insertMediaSingle = _ref.insertMediaSingle,
23
- insertExternalMediaSingle = _ref.insertExternalMediaSingle;
24
- var intl = (0, _reactIntlNext.useIntl)();
25
- return /*#__PURE__*/_react.default.createElement(_tabs.default, {
26
- id: "media-insert-tab-navigation"
27
- }, /*#__PURE__*/_react.default.createElement(_primitives.Box, {
28
- paddingBlockEnd: "space.150"
29
- }, /*#__PURE__*/_react.default.createElement(_tabs.TabList, null, /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.fileTabTitle)), /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.linkTabTitle)))), /*#__PURE__*/_react.default.createElement(_tabs.TabPanel, null, /*#__PURE__*/_react.default.createElement(_LocalMedia.LocalMedia, {
30
- mediaProvider: mediaProvider,
31
- dispatchAnalyticsEvent: dispatchAnalyticsEvent,
32
- closeMediaInsertPicker: closeMediaInsertPicker,
33
- insertMediaSingle: insertMediaSingle
34
- })), /*#__PURE__*/_react.default.createElement(_tabs.TabPanel, null, /*#__PURE__*/_react.default.createElement(_MediaFromURL.MediaFromURL, {
35
- mediaProvider: mediaProvider,
36
- dispatchAnalyticsEvent: dispatchAnalyticsEvent,
37
- closeMediaInsertPicker: closeMediaInsertPicker,
38
- insertMediaSingle: insertMediaSingle,
39
- insertExternalMediaSingle: insertExternalMediaSingle
40
- })));
41
- };
@@ -1,32 +0,0 @@
1
- import React from 'react';
2
- import { useIntl } from 'react-intl-next';
3
- import { mediaInsertMessages } from '@atlaskit/editor-common/messages';
4
- import { Box } from '@atlaskit/primitives';
5
- import Tabs, { Tab, TabList, TabPanel } from '@atlaskit/tabs';
6
- import { LocalMedia } from './LocalMedia';
7
- import { MediaFromURL } from './MediaFromURL';
8
- export const MediaInsertContent = ({
9
- mediaProvider,
10
- dispatchAnalyticsEvent,
11
- closeMediaInsertPicker,
12
- insertMediaSingle,
13
- insertExternalMediaSingle
14
- }) => {
15
- const intl = useIntl();
16
- return /*#__PURE__*/React.createElement(Tabs, {
17
- id: "media-insert-tab-navigation"
18
- }, /*#__PURE__*/React.createElement(Box, {
19
- paddingBlockEnd: "space.150"
20
- }, /*#__PURE__*/React.createElement(TabList, null, /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.fileTabTitle)), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), /*#__PURE__*/React.createElement(TabPanel, null, /*#__PURE__*/React.createElement(LocalMedia, {
21
- mediaProvider: mediaProvider,
22
- dispatchAnalyticsEvent: dispatchAnalyticsEvent,
23
- closeMediaInsertPicker: closeMediaInsertPicker,
24
- insertMediaSingle: insertMediaSingle
25
- })), /*#__PURE__*/React.createElement(TabPanel, null, /*#__PURE__*/React.createElement(MediaFromURL, {
26
- mediaProvider: mediaProvider,
27
- dispatchAnalyticsEvent: dispatchAnalyticsEvent,
28
- closeMediaInsertPicker: closeMediaInsertPicker,
29
- insertMediaSingle: insertMediaSingle,
30
- insertExternalMediaSingle: insertExternalMediaSingle
31
- })));
32
- };
@@ -1,31 +0,0 @@
1
- import React from 'react';
2
- import { useIntl } from 'react-intl-next';
3
- import { mediaInsertMessages } from '@atlaskit/editor-common/messages';
4
- import { Box } from '@atlaskit/primitives';
5
- import Tabs, { Tab, TabList, TabPanel } from '@atlaskit/tabs';
6
- import { LocalMedia } from './LocalMedia';
7
- import { MediaFromURL } from './MediaFromURL';
8
- export var MediaInsertContent = function MediaInsertContent(_ref) {
9
- var mediaProvider = _ref.mediaProvider,
10
- dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent,
11
- closeMediaInsertPicker = _ref.closeMediaInsertPicker,
12
- insertMediaSingle = _ref.insertMediaSingle,
13
- insertExternalMediaSingle = _ref.insertExternalMediaSingle;
14
- var intl = useIntl();
15
- return /*#__PURE__*/React.createElement(Tabs, {
16
- id: "media-insert-tab-navigation"
17
- }, /*#__PURE__*/React.createElement(Box, {
18
- paddingBlockEnd: "space.150"
19
- }, /*#__PURE__*/React.createElement(TabList, null, /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.fileTabTitle)), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), /*#__PURE__*/React.createElement(TabPanel, null, /*#__PURE__*/React.createElement(LocalMedia, {
20
- mediaProvider: mediaProvider,
21
- dispatchAnalyticsEvent: dispatchAnalyticsEvent,
22
- closeMediaInsertPicker: closeMediaInsertPicker,
23
- insertMediaSingle: insertMediaSingle
24
- })), /*#__PURE__*/React.createElement(TabPanel, null, /*#__PURE__*/React.createElement(MediaFromURL, {
25
- mediaProvider: mediaProvider,
26
- dispatchAnalyticsEvent: dispatchAnalyticsEvent,
27
- closeMediaInsertPicker: closeMediaInsertPicker,
28
- insertMediaSingle: insertMediaSingle,
29
- insertExternalMediaSingle: insertExternalMediaSingle
30
- })));
31
- };
@@ -1,13 +0,0 @@
1
- /// <reference types="react" />
2
- import type { DispatchAnalyticsEvent } from '@atlaskit/editor-common/analytics';
3
- import type { MediaProvider } from '@atlaskit/editor-common/provider-factory';
4
- import { type InsertExternalMediaSingle, type InsertMediaSingle } from '../types';
5
- type Props = {
6
- mediaProvider: MediaProvider;
7
- dispatchAnalyticsEvent?: DispatchAnalyticsEvent;
8
- closeMediaInsertPicker: () => void;
9
- insertMediaSingle: InsertMediaSingle;
10
- insertExternalMediaSingle: InsertExternalMediaSingle;
11
- };
12
- export declare const MediaInsertContent: ({ mediaProvider, dispatchAnalyticsEvent, closeMediaInsertPicker, insertMediaSingle, insertExternalMediaSingle, }: Props) => JSX.Element;
13
- export {};
@@ -1,13 +0,0 @@
1
- /// <reference types="react" />
2
- import type { DispatchAnalyticsEvent } from '@atlaskit/editor-common/analytics';
3
- import type { MediaProvider } from '@atlaskit/editor-common/provider-factory';
4
- import { type InsertExternalMediaSingle, type InsertMediaSingle } from '../types';
5
- type Props = {
6
- mediaProvider: MediaProvider;
7
- dispatchAnalyticsEvent?: DispatchAnalyticsEvent;
8
- closeMediaInsertPicker: () => void;
9
- insertMediaSingle: InsertMediaSingle;
10
- insertExternalMediaSingle: InsertExternalMediaSingle;
11
- };
12
- export declare const MediaInsertContent: ({ mediaProvider, dispatchAnalyticsEvent, closeMediaInsertPicker, insertMediaSingle, insertExternalMediaSingle, }: Props) => JSX.Element;
13
- export {};