@atlaskit/editor-plugin-media-insert 23.0.0 → 23.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 CHANGED
@@ -1,5 +1,29 @@
1
1
  # @atlaskit/editor-plugin-media-insert
2
2
 
3
+ ## 23.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`09301dc1b0c55`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/09301dc1b0c55) -
8
+ [ux] Switches the order of the image generation tab in the media insert picker to the first
9
+ position and renames the tab to Create. All changes behind an experiment.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
15
+ ## 23.1.0
16
+
17
+ ### Minor Changes
18
+
19
+ - [`1b208e1f7d8f7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1b208e1f7d8f7) -
20
+ [ux] Adds the entry point for ai image generation to the media insert picker plugin as a new tab.
21
+ This feature is fully behind an experiment gate.
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies
26
+
3
27
  ## 23.0.0
4
28
 
5
29
  ### Major Changes
@@ -13,9 +13,34 @@ var _actions = require("./pm-plugins/actions");
13
13
  var _main = require("./pm-plugins/main");
14
14
  var _pluginKey = require("./pm-plugins/plugin-key");
15
15
  var _MediaInsertPicker = require("./ui/MediaInsertPicker");
16
+ /**
17
+ * Per-editor-instance registry of insert tabs registered via
18
+ * `actions.registerInsertTab(...)`. Idempotent on `key` so that re-registering
19
+ * the same tab (e.g. on plugin re-init in dev / StrictMode) replaces rather
20
+ * than duplicates.
21
+ */
22
+ var createInsertTabRegistry = function createInsertTabRegistry() {
23
+ var tabs = [];
24
+ return {
25
+ register: function register(tab) {
26
+ var existingIndex = tabs.findIndex(function (t) {
27
+ return t.key === tab.key;
28
+ });
29
+ if (existingIndex >= 0) {
30
+ tabs[existingIndex] = tab;
31
+ } else {
32
+ tabs.push(tab);
33
+ }
34
+ },
35
+ getAll: function getAll() {
36
+ return tabs;
37
+ }
38
+ };
39
+ };
16
40
  var mediaInsertPlugin = exports.mediaInsertPlugin = function mediaInsertPlugin(_ref) {
17
41
  var api = _ref.api,
18
42
  config = _ref.config;
43
+ var insertTabRegistry = createInsertTabRegistry();
19
44
  return {
20
45
  name: 'mediaInsert',
21
46
  pmPlugins: function pmPlugins() {
@@ -48,6 +73,14 @@ var mediaInsertPlugin = exports.mediaInsertPlugin = function mediaInsertPlugin(_
48
73
  };
49
74
  }
50
75
  },
76
+ actions: {
77
+ registerInsertTab: function registerInsertTab(tab) {
78
+ return insertTabRegistry.register(tab);
79
+ },
80
+ getInsertTabs: function getInsertTabs() {
81
+ return insertTabRegistry.getAll();
82
+ }
83
+ },
51
84
  contentComponent: function contentComponent(_ref4) {
52
85
  var editorView = _ref4.editorView,
53
86
  dispatchAnalyticsEvent = _ref4.dispatchAnalyticsEvent,
@@ -43,6 +43,7 @@ var CustomTabPanel = function CustomTabPanel(_ref) {
43
43
  );
44
44
  };
45
45
  var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_ref2) {
46
+ var _api$mediaInsert$acti, _api$mediaInsert, _api$mediaInsert$getI;
46
47
  var api = _ref2.api,
47
48
  editorView = _ref2.editorView,
48
49
  dispatchAnalyticsEvent = _ref2.dispatchAnalyticsEvent,
@@ -57,6 +58,10 @@ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_
57
58
  isOnlyExternalLinks = _ref2$isOnlyExternalL === void 0 ? false : _ref2$isOnlyExternalL,
58
59
  customizedUrlValidation = _ref2.customizedUrlValidation,
59
60
  customizedHelperMessage = _ref2.customizedHelperMessage;
61
+ // Tabs registered by other plugins via `api.mediaInsert.actions.registerInsertTab(...)`.
62
+ // Read once per render; the registry is mutated only at plugin setup time so this is stable
63
+ // for the lifetime of an editor instance.
64
+ var registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 || (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 || (_api$mediaInsert = _api$mediaInsert.actions) === null || _api$mediaInsert === void 0 || (_api$mediaInsert$getI = _api$mediaInsert.getInsertTabs) === null || _api$mediaInsert$getI === void 0 ? void 0 : _api$mediaInsert$getI.call(_api$mediaInsert)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : [];
60
65
  var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['media', 'mediaInsert'], function (states) {
61
66
  var _states$mediaState, _states$mediaInsertSt, _states$mediaInsertSt2;
62
67
  return {
@@ -91,6 +96,11 @@ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_
91
96
  }
92
97
  var handleClose = function handleClose(exitMethod) {
93
98
  return function (event) {
99
+ // Same as AIImageGenerationPopup: react-select can detach the option
100
+ // before `click` fires, so withOuterListeners treats it as outside.
101
+ if (exitMethod === _analytics.INPUT_METHOD.MOUSE && event.target instanceof Node && !event.target.isConnected) {
102
+ return;
103
+ }
94
104
  event.preventDefault();
95
105
  if (dispatchAnalyticsEvent) {
96
106
  var payload = {
@@ -132,7 +142,27 @@ var MediaInsertPicker = exports.MediaInsertPicker = function MediaInsertPicker(_
132
142
  id: "media-insert-tab-navigation"
133
143
  }, /*#__PURE__*/_react.default.createElement(_compiled.Box, {
134
144
  paddingBlockEnd: "space.150"
135
- }, /*#__PURE__*/_react.default.createElement(_tabs.TabList, null, !isOnlyExternalLinks && /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.fileTabTitle)), /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.linkTabTitle)))), !isOnlyExternalLinks && /*#__PURE__*/_react.default.createElement(CustomTabPanel, null, /*#__PURE__*/_react.default.createElement(_LocalMedia.LocalMedia, {
145
+ }, /*#__PURE__*/_react.default.createElement(_tabs.TabList, null, registeredTabs.map(function (tab) {
146
+ return /*#__PURE__*/_react.default.createElement(_tabs.Tab, {
147
+ key: tab.key
148
+ }, tab.label);
149
+ }), !isOnlyExternalLinks && /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.fileTabTitle)), /*#__PURE__*/_react.default.createElement(_tabs.Tab, null, intl.formatMessage(_messages.mediaInsertMessages.linkTabTitle)))), registeredTabs.map(function (_ref3) {
150
+ var key = _ref3.key,
151
+ TabComponent = _ref3.component;
152
+ return /*#__PURE__*/_react.default.createElement(CustomTabPanel, {
153
+ key: key
154
+ }, /*#__PURE__*/_react.default.createElement(TabComponent
155
+ // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
156
+ , {
157
+ closeMediaInsertPicker: function closeMediaInsertPicker() {
158
+ _closeMediaInsertPicker();
159
+ focusEditor();
160
+ },
161
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
162
+ insertMediaSingle: insertMediaSingle,
163
+ mediaProvider: mediaProvider
164
+ }));
165
+ }), !isOnlyExternalLinks && /*#__PURE__*/_react.default.createElement(CustomTabPanel, null, /*#__PURE__*/_react.default.createElement(_LocalMedia.LocalMedia, {
136
166
  ref: autofocusRef,
137
167
  mediaProvider: mediaProvider
138
168
  // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
@@ -6,10 +6,32 @@ import { closeMediaInsertPicker, showMediaInsertPopup } from './pm-plugins/actio
6
6
  import { createPlugin } from './pm-plugins/main';
7
7
  import { pluginKey } from './pm-plugins/plugin-key';
8
8
  import { MediaInsertPicker } from './ui/MediaInsertPicker';
9
+
10
+ /**
11
+ * Per-editor-instance registry of insert tabs registered via
12
+ * `actions.registerInsertTab(...)`. Idempotent on `key` so that re-registering
13
+ * the same tab (e.g. on plugin re-init in dev / StrictMode) replaces rather
14
+ * than duplicates.
15
+ */
16
+ const createInsertTabRegistry = () => {
17
+ const tabs = [];
18
+ return {
19
+ register: tab => {
20
+ const existingIndex = tabs.findIndex(t => t.key === tab.key);
21
+ if (existingIndex >= 0) {
22
+ tabs[existingIndex] = tab;
23
+ } else {
24
+ tabs.push(tab);
25
+ }
26
+ },
27
+ getAll: () => tabs
28
+ };
29
+ };
9
30
  export const mediaInsertPlugin = ({
10
31
  api,
11
32
  config
12
33
  }) => {
34
+ const insertTabRegistry = createInsertTabRegistry();
13
35
  return {
14
36
  name: 'mediaInsert',
15
37
  pmPlugins() {
@@ -38,6 +60,10 @@ export const mediaInsertPlugin = ({
38
60
  tr
39
61
  }) => showMediaInsertPopup(tr, mountInfo)
40
62
  },
63
+ actions: {
64
+ registerInsertTab: tab => insertTabRegistry.register(tab),
65
+ getInsertTabs: () => insertTabRegistry.getAll()
66
+ },
41
67
  contentComponent: ({
42
68
  editorView,
43
69
  dispatchAnalyticsEvent,
@@ -49,6 +49,11 @@ export const MediaInsertPicker = ({
49
49
  customizedUrlValidation,
50
50
  customizedHelperMessage
51
51
  }) => {
52
+ var _api$mediaInsert$acti, _api$mediaInsert, _api$mediaInsert$acti2, _api$mediaInsert$acti3;
53
+ // Tabs registered by other plugins via `api.mediaInsert.actions.registerInsertTab(...)`.
54
+ // Read once per render; the registry is mutated only at plugin setup time so this is stable
55
+ // for the lifetime of an editor instance.
56
+ const registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 ? void 0 : (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 ? void 0 : (_api$mediaInsert$acti2 = _api$mediaInsert.actions) === null || _api$mediaInsert$acti2 === void 0 ? void 0 : (_api$mediaInsert$acti3 = _api$mediaInsert$acti2.getInsertTabs) === null || _api$mediaInsert$acti3 === void 0 ? void 0 : _api$mediaInsert$acti3.call(_api$mediaInsert$acti2)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : [];
52
57
  const {
53
58
  mediaProvider,
54
59
  isOpen,
@@ -84,6 +89,11 @@ export const MediaInsertPicker = ({
84
89
  return null;
85
90
  }
86
91
  const handleClose = exitMethod => event => {
92
+ // Same as AIImageGenerationPopup: react-select can detach the option
93
+ // before `click` fires, so withOuterListeners treats it as outside.
94
+ if (exitMethod === INPUT_METHOD.MOUSE && event.target instanceof Node && !event.target.isConnected) {
95
+ return;
96
+ }
87
97
  event.preventDefault();
88
98
  if (dispatchAnalyticsEvent) {
89
99
  const payload = {
@@ -123,7 +133,24 @@ export const MediaInsertPicker = ({
123
133
  id: "media-insert-tab-navigation"
124
134
  }, /*#__PURE__*/React.createElement(Box, {
125
135
  paddingBlockEnd: "space.150"
126
- }, /*#__PURE__*/React.createElement(TabList, null, !isOnlyExternalLinks && /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.fileTabTitle)), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), !isOnlyExternalLinks && /*#__PURE__*/React.createElement(CustomTabPanel, null, /*#__PURE__*/React.createElement(LocalMedia, {
136
+ }, /*#__PURE__*/React.createElement(TabList, null, registeredTabs.map(tab => /*#__PURE__*/React.createElement(Tab, {
137
+ key: tab.key
138
+ }, tab.label)), !isOnlyExternalLinks && /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.fileTabTitle)), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), registeredTabs.map(({
139
+ key,
140
+ component: TabComponent
141
+ }) => /*#__PURE__*/React.createElement(CustomTabPanel, {
142
+ key: key
143
+ }, /*#__PURE__*/React.createElement(TabComponent
144
+ // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
145
+ , {
146
+ closeMediaInsertPicker: () => {
147
+ closeMediaInsertPicker();
148
+ focusEditor();
149
+ },
150
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
151
+ insertMediaSingle: insertMediaSingle,
152
+ mediaProvider: mediaProvider
153
+ }))), !isOnlyExternalLinks && /*#__PURE__*/React.createElement(CustomTabPanel, null, /*#__PURE__*/React.createElement(LocalMedia, {
127
154
  ref: autofocusRef,
128
155
  mediaProvider: mediaProvider
129
156
  // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
@@ -6,9 +6,35 @@ import { closeMediaInsertPicker as _closeMediaInsertPicker, showMediaInsertPopup
6
6
  import { createPlugin } from './pm-plugins/main';
7
7
  import { pluginKey } from './pm-plugins/plugin-key';
8
8
  import { MediaInsertPicker } from './ui/MediaInsertPicker';
9
+
10
+ /**
11
+ * Per-editor-instance registry of insert tabs registered via
12
+ * `actions.registerInsertTab(...)`. Idempotent on `key` so that re-registering
13
+ * the same tab (e.g. on plugin re-init in dev / StrictMode) replaces rather
14
+ * than duplicates.
15
+ */
16
+ var createInsertTabRegistry = function createInsertTabRegistry() {
17
+ var tabs = [];
18
+ return {
19
+ register: function register(tab) {
20
+ var existingIndex = tabs.findIndex(function (t) {
21
+ return t.key === tab.key;
22
+ });
23
+ if (existingIndex >= 0) {
24
+ tabs[existingIndex] = tab;
25
+ } else {
26
+ tabs.push(tab);
27
+ }
28
+ },
29
+ getAll: function getAll() {
30
+ return tabs;
31
+ }
32
+ };
33
+ };
9
34
  export var mediaInsertPlugin = function mediaInsertPlugin(_ref) {
10
35
  var api = _ref.api,
11
36
  config = _ref.config;
37
+ var insertTabRegistry = createInsertTabRegistry();
12
38
  return {
13
39
  name: 'mediaInsert',
14
40
  pmPlugins: function pmPlugins() {
@@ -41,6 +67,14 @@ export var mediaInsertPlugin = function mediaInsertPlugin(_ref) {
41
67
  };
42
68
  }
43
69
  },
70
+ actions: {
71
+ registerInsertTab: function registerInsertTab(tab) {
72
+ return insertTabRegistry.register(tab);
73
+ },
74
+ getInsertTabs: function getInsertTabs() {
75
+ return insertTabRegistry.getAll();
76
+ }
77
+ },
44
78
  contentComponent: function contentComponent(_ref4) {
45
79
  var editorView = _ref4.editorView,
46
80
  dispatchAnalyticsEvent = _ref4.dispatchAnalyticsEvent,
@@ -34,6 +34,7 @@ var CustomTabPanel = function CustomTabPanel(_ref) {
34
34
  );
35
35
  };
36
36
  export var MediaInsertPicker = function MediaInsertPicker(_ref2) {
37
+ var _api$mediaInsert$acti, _api$mediaInsert, _api$mediaInsert$getI;
37
38
  var api = _ref2.api,
38
39
  editorView = _ref2.editorView,
39
40
  dispatchAnalyticsEvent = _ref2.dispatchAnalyticsEvent,
@@ -48,6 +49,10 @@ export var MediaInsertPicker = function MediaInsertPicker(_ref2) {
48
49
  isOnlyExternalLinks = _ref2$isOnlyExternalL === void 0 ? false : _ref2$isOnlyExternalL,
49
50
  customizedUrlValidation = _ref2.customizedUrlValidation,
50
51
  customizedHelperMessage = _ref2.customizedHelperMessage;
52
+ // Tabs registered by other plugins via `api.mediaInsert.actions.registerInsertTab(...)`.
53
+ // Read once per render; the registry is mutated only at plugin setup time so this is stable
54
+ // for the lifetime of an editor instance.
55
+ var registeredTabs = (_api$mediaInsert$acti = api === null || api === void 0 || (_api$mediaInsert = api.mediaInsert) === null || _api$mediaInsert === void 0 || (_api$mediaInsert = _api$mediaInsert.actions) === null || _api$mediaInsert === void 0 || (_api$mediaInsert$getI = _api$mediaInsert.getInsertTabs) === null || _api$mediaInsert$getI === void 0 ? void 0 : _api$mediaInsert$getI.call(_api$mediaInsert)) !== null && _api$mediaInsert$acti !== void 0 ? _api$mediaInsert$acti : [];
51
56
  var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['media', 'mediaInsert'], function (states) {
52
57
  var _states$mediaState, _states$mediaInsertSt, _states$mediaInsertSt2;
53
58
  return {
@@ -82,6 +87,11 @@ export var MediaInsertPicker = function MediaInsertPicker(_ref2) {
82
87
  }
83
88
  var handleClose = function handleClose(exitMethod) {
84
89
  return function (event) {
90
+ // Same as AIImageGenerationPopup: react-select can detach the option
91
+ // before `click` fires, so withOuterListeners treats it as outside.
92
+ if (exitMethod === INPUT_METHOD.MOUSE && event.target instanceof Node && !event.target.isConnected) {
93
+ return;
94
+ }
85
95
  event.preventDefault();
86
96
  if (dispatchAnalyticsEvent) {
87
97
  var payload = {
@@ -123,7 +133,27 @@ export var MediaInsertPicker = function MediaInsertPicker(_ref2) {
123
133
  id: "media-insert-tab-navigation"
124
134
  }, /*#__PURE__*/React.createElement(Box, {
125
135
  paddingBlockEnd: "space.150"
126
- }, /*#__PURE__*/React.createElement(TabList, null, !isOnlyExternalLinks && /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.fileTabTitle)), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), !isOnlyExternalLinks && /*#__PURE__*/React.createElement(CustomTabPanel, null, /*#__PURE__*/React.createElement(LocalMedia, {
136
+ }, /*#__PURE__*/React.createElement(TabList, null, registeredTabs.map(function (tab) {
137
+ return /*#__PURE__*/React.createElement(Tab, {
138
+ key: tab.key
139
+ }, tab.label);
140
+ }), !isOnlyExternalLinks && /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.fileTabTitle)), /*#__PURE__*/React.createElement(Tab, null, intl.formatMessage(mediaInsertMessages.linkTabTitle)))), registeredTabs.map(function (_ref3) {
141
+ var key = _ref3.key,
142
+ TabComponent = _ref3.component;
143
+ return /*#__PURE__*/React.createElement(CustomTabPanel, {
144
+ key: key
145
+ }, /*#__PURE__*/React.createElement(TabComponent
146
+ // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
147
+ , {
148
+ closeMediaInsertPicker: function closeMediaInsertPicker() {
149
+ _closeMediaInsertPicker();
150
+ focusEditor();
151
+ },
152
+ dispatchAnalyticsEvent: dispatchAnalyticsEvent,
153
+ insertMediaSingle: insertMediaSingle,
154
+ mediaProvider: mediaProvider
155
+ }));
156
+ }), !isOnlyExternalLinks && /*#__PURE__*/React.createElement(CustomTabPanel, null, /*#__PURE__*/React.createElement(LocalMedia, {
127
157
  ref: autofocusRef,
128
158
  mediaProvider: mediaProvider
129
159
  // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
@@ -1,2 +1,2 @@
1
1
  export { mediaInsertPlugin } from './mediaInsertPlugin';
2
- export type { MediaInsertPlugin, MediaInsertPluginState, MediaInsertPluginDependencies, MediaInsertPluginCommands, MediaInsertPluginConfig, } from './mediaInsertPluginType';
2
+ export type { MediaInsertPlugin, MediaInsertPluginState, MediaInsertPluginDependencies, MediaInsertPluginCommands, MediaInsertPluginConfig, MediaInsertPluginActions, MediaInsertTabProps, RegisterInsertTab, } from './mediaInsertPluginType';
@@ -1,8 +1,49 @@
1
+ import type { ComponentType, ReactNode } from 'react';
2
+ import type { AnalyticsEventPayload } from '@atlaskit/editor-common/analytics';
3
+ import type { MediaProvider } from '@atlaskit/editor-common/provider-factory';
1
4
  import type { EditorCommand, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
2
5
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
3
6
  import type { FeatureFlagsPlugin } from '@atlaskit/editor-plugin-feature-flags';
4
7
  import type { MediaPlugin } from '@atlaskit/editor-plugin-media';
5
- import type { CustomizedHelperMessage } from './types';
8
+ import type { CustomizedHelperMessage, InsertMediaSingle } from './types';
9
+ /**
10
+ * Props provided to a registered insert tab inside the media insert picker.
11
+ *
12
+ * The picker owns the popup chrome and provides the editor-side context
13
+ * needed to insert media; the registering plugin provides the actual UI.
14
+ */
15
+ export type MediaInsertTabProps = {
16
+ closeMediaInsertPicker: () => void;
17
+ dispatchAnalyticsEvent?: (payload: AnalyticsEventPayload) => void;
18
+ insertMediaSingle: InsertMediaSingle;
19
+ mediaProvider: MediaProvider;
20
+ };
21
+ /**
22
+ * Descriptor for an insert tab registered onto the media insert picker via
23
+ * `api.mediaInsert.actions.registerInsertTab(...)`.
24
+ *
25
+ * Other plugins (typically private `@atlassian/*` plugins like
26
+ * `editor-plugin-ai-image-generation`) call `registerInsertTab` from inside
27
+ * their own setup so the public `editor-plugin-media-insert` package never
28
+ * needs to import them directly.
29
+ */
30
+ export type RegisterInsertTab = {
31
+ /**
32
+ * Stable identifier for this tab. Used to de-duplicate registrations so
33
+ * calling `registerInsertTab` with the same key twice replaces the prior
34
+ * registration rather than appending a duplicate tab.
35
+ */
36
+ key: string;
37
+ /**
38
+ * Label rendered inside the tab. Typically a `<FormattedMessage />` so the
39
+ * registering plugin owns its own i18n.
40
+ */
41
+ label: ReactNode;
42
+ /**
43
+ * Component rendered inside the picker when this tab is active.
44
+ */
45
+ component: ComponentType<MediaInsertTabProps>;
46
+ };
6
47
  export type MediaInsertPluginState = {
7
48
  isOpen?: boolean;
8
49
  mountInfo?: {
@@ -21,6 +62,29 @@ export type MediaInsertPluginCommands = {
21
62
  ref: HTMLElement;
22
63
  }) => EditorCommand;
23
64
  };
65
+ export type MediaInsertPluginActions = {
66
+ /**
67
+ * Returns the list of insert tabs that have been registered with the
68
+ * picker. Order matches registration order.
69
+ */
70
+ getInsertTabs: () => RegisterInsertTab[];
71
+ /**
72
+ * Register an additional tab inside the media insert picker. Idempotent
73
+ * by `key`: re-registering with the same key replaces the prior entry.
74
+ *
75
+ * Intended to be called from another plugin's setup, e.g.:
76
+ *
77
+ * ```tsx
78
+ * // inside aiImageGenerationPlugin
79
+ * api?.mediaInsert?.actions.registerInsertTab({
80
+ * key: 'ai-image-generation',
81
+ * label: <FormattedMessage {...messages.generateTabTitle} />,
82
+ * component: MediaInsertImageGenerationTab,
83
+ * });
84
+ * ```
85
+ */
86
+ registerInsertTab: (tab: RegisterInsertTab) => void;
87
+ };
24
88
  export type MediaInsertPluginConfig = {
25
89
  customizedHelperMessage?: CustomizedHelperMessage;
26
90
  customizedUrlValidation?: (input: string) => boolean;
@@ -55,6 +119,7 @@ export type MediaInsertPluginConfig = {
55
119
  isOnlyExternalLinks?: boolean;
56
120
  };
57
121
  export type MediaInsertPlugin = NextEditorPlugin<'mediaInsert', {
122
+ actions: MediaInsertPluginActions;
58
123
  commands: MediaInsertPluginCommands;
59
124
  dependencies: MediaInsertPluginDependencies;
60
125
  pluginConfiguration: MediaInsertPluginConfig | undefined;
@@ -1,2 +1,2 @@
1
1
  export { mediaInsertPlugin } from './mediaInsertPlugin';
2
- export type { MediaInsertPlugin, MediaInsertPluginState, MediaInsertPluginDependencies, MediaInsertPluginCommands, MediaInsertPluginConfig, } from './mediaInsertPluginType';
2
+ export type { MediaInsertPlugin, MediaInsertPluginState, MediaInsertPluginDependencies, MediaInsertPluginCommands, MediaInsertPluginConfig, MediaInsertPluginActions, MediaInsertTabProps, RegisterInsertTab, } from './mediaInsertPluginType';
@@ -1,8 +1,49 @@
1
+ import type { ComponentType, ReactNode } from 'react';
2
+ import type { AnalyticsEventPayload } from '@atlaskit/editor-common/analytics';
3
+ import type { MediaProvider } from '@atlaskit/editor-common/provider-factory';
1
4
  import type { EditorCommand, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
2
5
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
3
6
  import type { FeatureFlagsPlugin } from '@atlaskit/editor-plugin-feature-flags';
4
7
  import type { MediaPlugin } from '@atlaskit/editor-plugin-media';
5
- import type { CustomizedHelperMessage } from './types';
8
+ import type { CustomizedHelperMessage, InsertMediaSingle } from './types';
9
+ /**
10
+ * Props provided to a registered insert tab inside the media insert picker.
11
+ *
12
+ * The picker owns the popup chrome and provides the editor-side context
13
+ * needed to insert media; the registering plugin provides the actual UI.
14
+ */
15
+ export type MediaInsertTabProps = {
16
+ closeMediaInsertPicker: () => void;
17
+ dispatchAnalyticsEvent?: (payload: AnalyticsEventPayload) => void;
18
+ insertMediaSingle: InsertMediaSingle;
19
+ mediaProvider: MediaProvider;
20
+ };
21
+ /**
22
+ * Descriptor for an insert tab registered onto the media insert picker via
23
+ * `api.mediaInsert.actions.registerInsertTab(...)`.
24
+ *
25
+ * Other plugins (typically private `@atlassian/*` plugins like
26
+ * `editor-plugin-ai-image-generation`) call `registerInsertTab` from inside
27
+ * their own setup so the public `editor-plugin-media-insert` package never
28
+ * needs to import them directly.
29
+ */
30
+ export type RegisterInsertTab = {
31
+ /**
32
+ * Stable identifier for this tab. Used to de-duplicate registrations so
33
+ * calling `registerInsertTab` with the same key twice replaces the prior
34
+ * registration rather than appending a duplicate tab.
35
+ */
36
+ key: string;
37
+ /**
38
+ * Label rendered inside the tab. Typically a `<FormattedMessage />` so the
39
+ * registering plugin owns its own i18n.
40
+ */
41
+ label: ReactNode;
42
+ /**
43
+ * Component rendered inside the picker when this tab is active.
44
+ */
45
+ component: ComponentType<MediaInsertTabProps>;
46
+ };
6
47
  export type MediaInsertPluginState = {
7
48
  isOpen?: boolean;
8
49
  mountInfo?: {
@@ -21,6 +62,29 @@ export type MediaInsertPluginCommands = {
21
62
  ref: HTMLElement;
22
63
  }) => EditorCommand;
23
64
  };
65
+ export type MediaInsertPluginActions = {
66
+ /**
67
+ * Returns the list of insert tabs that have been registered with the
68
+ * picker. Order matches registration order.
69
+ */
70
+ getInsertTabs: () => RegisterInsertTab[];
71
+ /**
72
+ * Register an additional tab inside the media insert picker. Idempotent
73
+ * by `key`: re-registering with the same key replaces the prior entry.
74
+ *
75
+ * Intended to be called from another plugin's setup, e.g.:
76
+ *
77
+ * ```tsx
78
+ * // inside aiImageGenerationPlugin
79
+ * api?.mediaInsert?.actions.registerInsertTab({
80
+ * key: 'ai-image-generation',
81
+ * label: <FormattedMessage {...messages.generateTabTitle} />,
82
+ * component: MediaInsertImageGenerationTab,
83
+ * });
84
+ * ```
85
+ */
86
+ registerInsertTab: (tab: RegisterInsertTab) => void;
87
+ };
24
88
  export type MediaInsertPluginConfig = {
25
89
  customizedHelperMessage?: CustomizedHelperMessage;
26
90
  customizedUrlValidation?: (input: string) => boolean;
@@ -55,6 +119,7 @@ export type MediaInsertPluginConfig = {
55
119
  isOnlyExternalLinks?: boolean;
56
120
  };
57
121
  export type MediaInsertPlugin = NextEditorPlugin<'mediaInsert', {
122
+ actions: MediaInsertPluginActions;
58
123
  commands: MediaInsertPluginCommands;
59
124
  dependencies: MediaInsertPluginDependencies;
60
125
  pluginConfiguration: MediaInsertPluginConfig | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-media-insert",
3
- "version": "23.0.0",
3
+ "version": "23.2.0",
4
4
  "description": "Media Insert plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -21,18 +21,18 @@
21
21
  ],
22
22
  "atlaskit:src": "src/index.ts",
23
23
  "dependencies": {
24
- "@atlaskit/adf-schema": "^52.5.0",
24
+ "@atlaskit/adf-schema": "^52.7.0",
25
25
  "@atlaskit/button": "^23.11.0",
26
26
  "@atlaskit/editor-plugin-analytics": "^10.0.0",
27
- "@atlaskit/editor-plugin-media": "^12.0.0",
27
+ "@atlaskit/editor-plugin-media": "^12.3.0",
28
28
  "@atlaskit/editor-prosemirror": "^7.3.0",
29
29
  "@atlaskit/editor-shared-styles": "^3.10.0",
30
30
  "@atlaskit/form": "^15.5.0",
31
- "@atlaskit/icon": "^34.2.0",
32
- "@atlaskit/media-card": "^80.0.0",
33
- "@atlaskit/media-client": "^36.0.0",
34
- "@atlaskit/media-client-react": "^5.0.0",
35
- "@atlaskit/media-picker": "^71.0.0",
31
+ "@atlaskit/icon": "^34.3.0",
32
+ "@atlaskit/media-card": "^80.4.0",
33
+ "@atlaskit/media-client": "^36.1.0",
34
+ "@atlaskit/media-client-react": "^5.1.0",
35
+ "@atlaskit/media-picker": "^71.2.0",
36
36
  "@atlaskit/platform-feature-flags": "^1.1.0",
37
37
  "@atlaskit/primitives": "^19.0.0",
38
38
  "@atlaskit/section-message": "^8.12.0",
@@ -41,7 +41,7 @@
41
41
  "@babel/runtime": "^7.0.0"
42
42
  },
43
43
  "peerDependencies": {
44
- "@atlaskit/editor-common": "^114.0.0",
44
+ "@atlaskit/editor-common": "^114.17.0",
45
45
  "@atlaskit/tokens": "^13.0.0",
46
46
  "react": "^18.2.0",
47
47
  "react-dom": "^18.2.0",