@atlaskit/editor-plugin-media 12.4.1 → 12.5.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 +17 -0
- package/dist/cjs/nodeviews/helpers.js +5 -1
- package/dist/cjs/nodeviews/mediaNodeUpdater.js +72 -3
- package/dist/cjs/nodeviews/mediaSingleNext.js +11 -5
- package/dist/cjs/nodeviews/nodeviewHelpers.js +26 -0
- package/dist/cjs/pm-plugins/main.js +213 -20
- package/dist/cjs/ui/toolbar/index.js +13 -1
- package/dist/es2019/nodeviews/helpers.js +5 -1
- package/dist/es2019/nodeviews/mediaNodeUpdater.js +77 -1
- package/dist/es2019/nodeviews/mediaSingleNext.js +11 -5
- package/dist/es2019/nodeviews/nodeviewHelpers.js +20 -0
- package/dist/es2019/pm-plugins/main.js +211 -4
- package/dist/es2019/ui/toolbar/index.js +13 -1
- package/dist/esm/nodeviews/helpers.js +5 -1
- package/dist/esm/nodeviews/mediaNodeUpdater.js +70 -1
- package/dist/esm/nodeviews/mediaSingleNext.js +11 -5
- package/dist/esm/nodeviews/nodeviewHelpers.js +20 -0
- package/dist/esm/pm-plugins/main.js +203 -10
- package/dist/esm/ui/toolbar/index.js +13 -1
- package/dist/types/nodeviews/mediaNodeUpdater.d.ts +3 -1
- package/dist/types/nodeviews/nodeviewHelpers.d.ts +11 -0
- package/dist/types/pm-plugins/main.d.ts +12 -0
- package/dist/types/pm-plugins/types.d.ts +3 -0
- package/dist/types-ts4.5/nodeviews/mediaNodeUpdater.d.ts +3 -1
- package/dist/types-ts4.5/nodeviews/nodeviewHelpers.d.ts +11 -0
- package/dist/types-ts4.5/pm-plugins/main.d.ts +12 -0
- package/dist/types-ts4.5/pm-plugins/types.d.ts +3 -0
- package/nodeviewHelpers/package.json +17 -0
- package/package.json +5 -5
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { akEditorFullWidthLayoutWidth, akEditorWideLayoutWidth } from '@atlaskit/editor-shared-styles';
|
|
2
|
+
var MIN_MEDIA_DISPLAY_WIDTH = 24;
|
|
3
|
+
/**
|
|
4
|
+
* Computes the new mediaSingle display width that preserves the original display height
|
|
5
|
+
* when a media node is replaced with a file of a different aspect ratio, clamped to valid bounds.
|
|
6
|
+
*
|
|
7
|
+
* @param targetDisplayHeight - The display height to preserve (from the old image)
|
|
8
|
+
* @param newIntrinsicWidth - The new file's intrinsic pixel width
|
|
9
|
+
* @param newIntrinsicHeight - The new file's intrinsic pixel height
|
|
10
|
+
* @param layout - The mediaSingle layout (affects max width)
|
|
11
|
+
* @param lineLength - The editor content column width in pixels
|
|
12
|
+
*/
|
|
13
|
+
export var computeReplacementDisplayWidth = function computeReplacementDisplayWidth(targetDisplayHeight, newIntrinsicWidth, newIntrinsicHeight, layout, lineLength) {
|
|
14
|
+
if (newIntrinsicHeight <= 0) {
|
|
15
|
+
return MIN_MEDIA_DISPLAY_WIDTH;
|
|
16
|
+
}
|
|
17
|
+
var unclamped = targetDisplayHeight * (newIntrinsicWidth / newIntrinsicHeight);
|
|
18
|
+
var maxWidth = layout === 'full-width' ? akEditorFullWidthLayoutWidth : layout === 'wide' ? akEditorWideLayoutWidth : lineLength;
|
|
19
|
+
return Math.max(MIN_MEDIA_DISPLAY_WIDTH, Math.min(unclamped, maxWidth));
|
|
20
|
+
};
|
|
@@ -13,6 +13,7 @@ import React from 'react';
|
|
|
13
13
|
import { RawIntlProvider } from 'react-intl';
|
|
14
14
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
15
15
|
import uuid from 'uuid';
|
|
16
|
+
import { SetAttrsStep } from '@atlaskit/adf-schema/steps';
|
|
16
17
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
17
18
|
import { getBrowserInfo } from '@atlaskit/editor-common/browser';
|
|
18
19
|
import { mediaInlineImagesEnabled } from '@atlaskit/editor-common/media-inline';
|
|
@@ -27,6 +28,7 @@ import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
|
|
|
27
28
|
import { isFileIdentifier } from '@atlaskit/media-client';
|
|
28
29
|
import { getMediaFeatureFlag } from '@atlaskit/media-common';
|
|
29
30
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
31
|
+
import { createMediaNodeUpdater } from '../nodeviews/mediaNodeUpdater';
|
|
30
32
|
// Ignored via go/ees005
|
|
31
33
|
// eslint-disable-next-line import/no-namespace
|
|
32
34
|
import * as helpers from '../pm-plugins/commands/helpers';
|
|
@@ -87,6 +89,15 @@ export var MediaPluginStateImplementation = /*#__PURE__*/function () {
|
|
|
87
89
|
_defineProperty(this, "destroyed", false);
|
|
88
90
|
_defineProperty(this, "removeOnCloseListener", function () {});
|
|
89
91
|
_defineProperty(this, "onPopupToggleCallback", function () {});
|
|
92
|
+
// When non-null, holds the file ID of the media node being replaced. Used both as a
|
|
93
|
+
// flag (non-null = replace mode) and as a cross-check to ensure the correct node is
|
|
94
|
+
// updated if the selection moves between the picker opening and the file being picked.
|
|
95
|
+
_defineProperty(this, "replaceMediaFileId", null);
|
|
96
|
+
// The display height (in pixels) of the mediaSingle being replaced, computed at replace time
|
|
97
|
+
// from its display width and the old media node's intrinsic aspect ratio.
|
|
98
|
+
// Used by the nodeview to recompute display width from the new file's aspect ratio after
|
|
99
|
+
// dimensions are fetched, preserving visual height rather than visual width.
|
|
100
|
+
_defineProperty(this, "replaceMediaTargetDisplayHeight", null);
|
|
90
101
|
_defineProperty(this, "identifierCount", new Map());
|
|
91
102
|
// This is to enable mediaShallowCopySope to enable only shallow copying media referenced within the edtior
|
|
92
103
|
// see: trackOutOfScopeIdentifier
|
|
@@ -122,7 +133,7 @@ export var MediaPluginStateImplementation = /*#__PURE__*/function () {
|
|
|
122
133
|
* called when we insert a new file via the picker (connected via pickerfacade)
|
|
123
134
|
*/
|
|
124
135
|
_defineProperty(this, "insertFile", function (mediaState, onMediaStateChanged, pickerType, insertMediaVia) {
|
|
125
|
-
var _this$pluginInjection, _mediaState$collectio, _this$
|
|
136
|
+
var _this$pluginInjection, _mediaState$collectio, _this$pluginInjection3;
|
|
126
137
|
var state = _this.view.state;
|
|
127
138
|
var editorAnalyticsAPI = (_this$pluginInjection = _this.pluginInjectionApi) === null || _this$pluginInjection === void 0 || (_this$pluginInjection = _this$pluginInjection.analytics) === null || _this$pluginInjection === void 0 ? void 0 : _this$pluginInjection.actions;
|
|
128
139
|
var mediaStateWithContext = _objectSpread(_objectSpread({}, mediaState), {}, {
|
|
@@ -133,6 +144,19 @@ export var MediaPluginStateImplementation = /*#__PURE__*/function () {
|
|
|
133
144
|
return;
|
|
134
145
|
}
|
|
135
146
|
|
|
147
|
+
// If replace mode was set but the selection has moved away from a mediaSingle
|
|
148
|
+
// (e.g. the user cancelled the replace picker and then inserted media elsewhere),
|
|
149
|
+
// clear replace state so this insertion proceeds as a normal insert.
|
|
150
|
+
if (_this.replaceMediaFileId !== null) {
|
|
151
|
+
var _state$selection$node;
|
|
152
|
+
var mediaSingle = state.schema.nodes.mediaSingle;
|
|
153
|
+
var isStillOnTargetMedia = state.selection instanceof NodeSelection && state.selection.node.type === mediaSingle && ((_state$selection$node = state.selection.node.firstChild) === null || _state$selection$node === void 0 ? void 0 : _state$selection$node.attrs.id) === _this.replaceMediaFileId;
|
|
154
|
+
if (!isStillOnTargetMedia) {
|
|
155
|
+
_this.replaceMediaFileId = null;
|
|
156
|
+
_this.replaceMediaTargetDisplayHeight = null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
136
160
|
// We need to dispatch the change to event dispatcher only for successful files
|
|
137
161
|
if (mediaState.status !== 'error') {
|
|
138
162
|
_this.updateAndDispatch({
|
|
@@ -145,13 +169,124 @@ export var MediaPluginStateImplementation = /*#__PURE__*/function () {
|
|
|
145
169
|
});
|
|
146
170
|
_this.uploadInProgressSubscriptionsNotified = true;
|
|
147
171
|
}
|
|
172
|
+
|
|
173
|
+
// Replace mode: if a media node is being replaced, update its attrs in-place
|
|
174
|
+
// rather than inserting a new node. This preserves layout, width, and caption.
|
|
175
|
+
if (_this.replaceMediaFileId !== null) {
|
|
176
|
+
var _mediaState$fileMimeT, _mediaState$fileName, _mediaState$fileSize;
|
|
177
|
+
// Clear replace mode immediately so subsequent insertions behave normally
|
|
178
|
+
_this.replaceMediaFileId = null;
|
|
179
|
+
var currentState = _this.view.state;
|
|
180
|
+
var mediaSinglePos = currentState.selection.from;
|
|
181
|
+
var mediaPos = mediaSinglePos + 1;
|
|
182
|
+
var mediaNode = currentState.doc.nodeAt(mediaPos);
|
|
183
|
+
if (!mediaNode || mediaNode.type.name !== 'media') {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Build a single transaction that:
|
|
188
|
+
// 1. Updates the media child node attrs (new file identity + cleared dimensions)
|
|
189
|
+
// 2. Re-creates the NodeSelection so the toolbar picks up the fresh node
|
|
190
|
+
var tr = currentState.tr;
|
|
191
|
+
tr.step(new SetAttrsStep(mediaPos, {
|
|
192
|
+
id: mediaState.id,
|
|
193
|
+
collection: collection,
|
|
194
|
+
type: 'file',
|
|
195
|
+
__fileMimeType: (_mediaState$fileMimeT = mediaState.fileMimeType) !== null && _mediaState$fileMimeT !== void 0 ? _mediaState$fileMimeT : null,
|
|
196
|
+
__fileName: (_mediaState$fileName = mediaState.fileName) !== null && _mediaState$fileName !== void 0 ? _mediaState$fileName : null,
|
|
197
|
+
__fileSize: (_mediaState$fileSize = mediaState.fileSize) !== null && _mediaState$fileSize !== void 0 ? _mediaState$fileSize : null,
|
|
198
|
+
__mediaTraceId: null,
|
|
199
|
+
// Clear intrinsic dimensions — they'll be fetched once the file
|
|
200
|
+
// is processed and applied via updateDimensions in a single tx
|
|
201
|
+
// with the height-preserving mediaSingle width adjustment.
|
|
202
|
+
width: null,
|
|
203
|
+
height: null
|
|
204
|
+
}));
|
|
205
|
+
// Re-create the selection so the floating toolbar picks up
|
|
206
|
+
// the updated node and renders the full set of controls.
|
|
207
|
+
tr.setSelection(NodeSelection.create(tr.doc, mediaSinglePos));
|
|
208
|
+
tr.setMeta('scrollIntoView', false);
|
|
209
|
+
_this.view.dispatch(tr);
|
|
210
|
+
|
|
211
|
+
// Still register the state-change listener so upload completion is tracked
|
|
212
|
+
onMediaStateChanged(_this.handleMediaState);
|
|
213
|
+
var _isEndState = function _isEndState(state) {
|
|
214
|
+
return state.status && MEDIA_RESOLVED_STATES.includes(state.status);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// After the file finishes uploading/processing, trigger a dimension fetch.
|
|
218
|
+
// getRemoteDimensions may fail if called too early (isImageRepresentationReady
|
|
219
|
+
// returns false while processing), so we wait for the ready state first.
|
|
220
|
+
var triggerDimensionFetch = function triggerDimensionFetch() {
|
|
221
|
+
// Find the media node in the doc by id and create a temporary
|
|
222
|
+
// MediaNodeUpdater to fetch and apply dimensions
|
|
223
|
+
var editorState = _this.view.state;
|
|
224
|
+
var mediaSingleType = editorState.schema.nodes.mediaSingle;
|
|
225
|
+
var mediaChildNode = null;
|
|
226
|
+
editorState.doc.descendants(function (node) {
|
|
227
|
+
if (mediaChildNode) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
if (node.type === mediaSingleType) {
|
|
231
|
+
var child = node.firstChild;
|
|
232
|
+
if (child && child.attrs.id === mediaState.id) {
|
|
233
|
+
mediaChildNode = child;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return true;
|
|
237
|
+
});
|
|
238
|
+
if (mediaChildNode) {
|
|
239
|
+
var _this$pluginInjection2;
|
|
240
|
+
var updater = createMediaNodeUpdater({
|
|
241
|
+
view: _this.view,
|
|
242
|
+
mediaProvider: _this.mediaProvider ? Promise.resolve(_this.mediaProvider) : undefined,
|
|
243
|
+
contextIdentifierProvider: _this.contextIdentifierProvider ? Promise.resolve(_this.contextIdentifierProvider) : undefined,
|
|
244
|
+
node: mediaChildNode,
|
|
245
|
+
isMediaSingle: true,
|
|
246
|
+
lineLength: (_this$pluginInjection2 = _this.pluginInjectionApi) === null || _this$pluginInjection2 === void 0 || (_this$pluginInjection2 = _this$pluginInjection2.width) === null || _this$pluginInjection2 === void 0 || (_this$pluginInjection2 = _this$pluginInjection2.sharedState.currentState()) === null || _this$pluginInjection2 === void 0 ? void 0 : _this$pluginInjection2.lineLength
|
|
247
|
+
});
|
|
248
|
+
updater.getRemoteDimensions().then(function (dims) {
|
|
249
|
+
if (dims) {
|
|
250
|
+
updater.updateDimensions(dims);
|
|
251
|
+
}
|
|
252
|
+
}).catch(function () {
|
|
253
|
+
// Silently ignore — if dimensions can't be fetched (e.g. network error),
|
|
254
|
+
// the image will render at its current size without the height-preserving
|
|
255
|
+
// width adjustment. This is an acceptable degraded experience.
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
if (!_isEndState(mediaStateWithContext)) {
|
|
260
|
+
var uploadingPromise = new Promise(function (resolve) {
|
|
261
|
+
onMediaStateChanged(function (newState) {
|
|
262
|
+
if (_isEndState(newState)) {
|
|
263
|
+
resolve(newState);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
_this.taskManager.addPendingTask(uploadingPromise, mediaStateWithContext.id).then(function () {
|
|
268
|
+
_this.updateAndDispatch({
|
|
269
|
+
allUploadsFinished: true
|
|
270
|
+
});
|
|
271
|
+
triggerDimensionFetch();
|
|
272
|
+
});
|
|
273
|
+
} else {
|
|
274
|
+
// File is already in a resolved state — fetch dimensions immediately
|
|
275
|
+
triggerDimensionFetch();
|
|
276
|
+
}
|
|
277
|
+
var _view = _this.view;
|
|
278
|
+
if (!_view.hasFocus()) {
|
|
279
|
+
_view.focus();
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
148
283
|
switch (getMediaNodeInsertionType(state, _this.mediaOptions, mediaStateWithContext.fileMimeType)) {
|
|
149
284
|
case 'inline':
|
|
150
285
|
insertMediaInlineNode(editorAnalyticsAPI)(_this.view, mediaStateWithContext, collection, _this.allowInlineImages, _this.getInputMethod(pickerType), insertMediaVia);
|
|
151
286
|
break;
|
|
152
287
|
case 'block':
|
|
153
288
|
// read width state right before inserting to get up-to-date and define values
|
|
154
|
-
var widthPluginState = (_this$
|
|
289
|
+
var widthPluginState = (_this$pluginInjection3 = _this.pluginInjectionApi) === null || _this$pluginInjection3 === void 0 || (_this$pluginInjection3 = _this$pluginInjection3.width) === null || _this$pluginInjection3 === void 0 ? void 0 : _this$pluginInjection3.sharedState.currentState();
|
|
155
290
|
insertMediaSingleNode(_this.view, mediaStateWithContext, _this.getInputMethod(pickerType), collection, _this.mediaOptions && _this.mediaOptions.alignLeftOnInsert, widthPluginState, editorAnalyticsAPI, _this.onNodeInserted, insertMediaVia, _this.mediaOptions && _this.mediaOptions.allowPixelResizing);
|
|
156
291
|
break;
|
|
157
292
|
case 'group':
|
|
@@ -167,7 +302,7 @@ export var MediaPluginStateImplementation = /*#__PURE__*/function () {
|
|
|
167
302
|
return state.status && MEDIA_RESOLVED_STATES.indexOf(state.status) !== -1;
|
|
168
303
|
};
|
|
169
304
|
if (!isEndState(mediaStateWithContext)) {
|
|
170
|
-
var
|
|
305
|
+
var _uploadingPromise = new Promise(function (resolve) {
|
|
171
306
|
onMediaStateChanged(function (newState) {
|
|
172
307
|
// When media item reaches its final state, remove listener and resolve
|
|
173
308
|
if (isEndState(newState)) {
|
|
@@ -176,7 +311,7 @@ export var MediaPluginStateImplementation = /*#__PURE__*/function () {
|
|
|
176
311
|
});
|
|
177
312
|
});
|
|
178
313
|
if (fg('platform_editor_media_disable_save_during_upload')) {
|
|
179
|
-
_this.taskManager.addPendingTask(
|
|
314
|
+
_this.taskManager.addPendingTask(_uploadingPromise, mediaStateWithContext.id).then(function () {
|
|
180
315
|
_this.updateAndDispatch({
|
|
181
316
|
allUploadsFinished: true
|
|
182
317
|
});
|
|
@@ -190,7 +325,7 @@ export var MediaPluginStateImplementation = /*#__PURE__*/function () {
|
|
|
190
325
|
});
|
|
191
326
|
});
|
|
192
327
|
} else {
|
|
193
|
-
_this.taskManager.addPendingTask(
|
|
328
|
+
_this.taskManager.addPendingTask(_uploadingPromise, mediaStateWithContext.id).then(function () {
|
|
194
329
|
_this.updateAndDispatch({
|
|
195
330
|
allUploadsFinished: true
|
|
196
331
|
});
|
|
@@ -232,6 +367,64 @@ export var MediaPluginStateImplementation = /*#__PURE__*/function () {
|
|
|
232
367
|
}
|
|
233
368
|
_this.onPopupToggleCallback(true);
|
|
234
369
|
});
|
|
370
|
+
/**
|
|
371
|
+
* Opens the media picker in "replace" mode. The next file selected/uploaded
|
|
372
|
+
* will replace the currently selected mediaSingle node's media child in-place,
|
|
373
|
+
* preserving layout, width, and caption.
|
|
374
|
+
*
|
|
375
|
+
* The display height is computed and stored so that after the new file's intrinsic
|
|
376
|
+
* dimensions are fetched, the mediaSingle display width can be adjusted to maintain
|
|
377
|
+
* visual height stability rather than width stability.
|
|
378
|
+
*/
|
|
379
|
+
_defineProperty(this, "showMediaPickerForReplace", function () {
|
|
380
|
+
var _this$pluginInjection4, _this$pluginInjection5;
|
|
381
|
+
var state = _this.view.state;
|
|
382
|
+
var mediaSingle = state.schema.nodes.mediaSingle;
|
|
383
|
+
var selection = state.selection;
|
|
384
|
+
|
|
385
|
+
// Only activate replace mode when a mediaSingle is selected
|
|
386
|
+
if (!(selection instanceof NodeSelection) || selection.node.type !== mediaSingle) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
var mediaSingleNode = selection.node;
|
|
390
|
+
var mediaNode = mediaSingleNode.firstChild;
|
|
391
|
+
if (!mediaNode) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Store the current media node's id so insertFile can identify and replace it
|
|
396
|
+
_this.replaceMediaFileId = mediaNode.attrs.id;
|
|
397
|
+
|
|
398
|
+
// Compute and store the current display height so we can preserve it after
|
|
399
|
+
// the new file's intrinsic dimensions are known.
|
|
400
|
+
// displayHeight = displayWidth * (intrinsicHeight / intrinsicWidth)
|
|
401
|
+
var widthAttr = mediaSingleNode.attrs.width;
|
|
402
|
+
var widthType = mediaSingleNode.attrs.widthType;
|
|
403
|
+
var intrinsicWidth = mediaNode.attrs.width;
|
|
404
|
+
var intrinsicHeight = mediaNode.attrs.height;
|
|
405
|
+
|
|
406
|
+
// Resolve actual pixel display width from mediaSingle attrs.
|
|
407
|
+
var lineLength = (_this$pluginInjection4 = (_this$pluginInjection5 = _this.pluginInjectionApi) === null || _this$pluginInjection5 === void 0 || (_this$pluginInjection5 = _this$pluginInjection5.width) === null || _this$pluginInjection5 === void 0 || (_this$pluginInjection5 = _this$pluginInjection5.sharedState.currentState()) === null || _this$pluginInjection5 === void 0 ? void 0 : _this$pluginInjection5.lineLength) !== null && _this$pluginInjection4 !== void 0 ? _this$pluginInjection4 : 760;
|
|
408
|
+
var displayWidth = null;
|
|
409
|
+
if (widthAttr && widthType === 'pixel') {
|
|
410
|
+
displayWidth = widthAttr;
|
|
411
|
+
} else if (widthAttr) {
|
|
412
|
+
// Default widthType is 'percentage' — convert to pixels
|
|
413
|
+
displayWidth = widthAttr / 100 * lineLength;
|
|
414
|
+
} else if (intrinsicWidth) {
|
|
415
|
+
// No width set at all (never resized) — fall back to intrinsic width
|
|
416
|
+
displayWidth = intrinsicWidth;
|
|
417
|
+
}
|
|
418
|
+
if (displayWidth && intrinsicWidth && intrinsicHeight && intrinsicWidth > 0) {
|
|
419
|
+
_this.replaceMediaTargetDisplayHeight = displayWidth * (intrinsicHeight / intrinsicWidth);
|
|
420
|
+
} else {
|
|
421
|
+
// Can't compute display height — fall back to preserving width
|
|
422
|
+
_this.replaceMediaTargetDisplayHeight = null;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Finally, show the media picker
|
|
426
|
+
_this.showMediaPicker();
|
|
427
|
+
});
|
|
235
428
|
_defineProperty(this, "setBrowseFn", function (browseFn) {
|
|
236
429
|
_this.openMediaPickerBrowser = browseFn;
|
|
237
430
|
});
|
|
@@ -687,8 +880,8 @@ export var MediaPluginStateImplementation = /*#__PURE__*/function () {
|
|
|
687
880
|
}, {
|
|
688
881
|
key: "contextIdentifierProvider",
|
|
689
882
|
get: function get() {
|
|
690
|
-
var _this$
|
|
691
|
-
return (_this$
|
|
883
|
+
var _this$pluginInjection6;
|
|
884
|
+
return (_this$pluginInjection6 = this.pluginInjectionApi) === null || _this$pluginInjection6 === void 0 || (_this$pluginInjection6 = _this$pluginInjection6.contextIdentifier) === null || _this$pluginInjection6 === void 0 || (_this$pluginInjection6 = _this$pluginInjection6.sharedState.currentState()) === null || _this$pluginInjection6 === void 0 ? void 0 : _this$pluginInjection6.contextIdentifierProvider;
|
|
692
885
|
}
|
|
693
886
|
}, {
|
|
694
887
|
key: "selectLastAddedMediaNode",
|
|
@@ -923,9 +1116,9 @@ export var createPlugin = function createPlugin(_schema, options, getIntl, plugi
|
|
|
923
1116
|
return;
|
|
924
1117
|
},
|
|
925
1118
|
key: stateKey,
|
|
926
|
-
view: function view(
|
|
927
|
-
var pluginState = getMediaPluginState(
|
|
928
|
-
pluginState.setView(
|
|
1119
|
+
view: function view(_view2) {
|
|
1120
|
+
var pluginState = getMediaPluginState(_view2.state);
|
|
1121
|
+
pluginState.setView(_view2);
|
|
929
1122
|
pluginState.updateElement();
|
|
930
1123
|
return {
|
|
931
1124
|
update: function update() {
|
|
@@ -23,6 +23,7 @@ import ImageFullscreenIcon from '@atlaskit/icon/core/image-fullscreen';
|
|
|
23
23
|
import ImageInlineIcon from '@atlaskit/icon/core/image-inline';
|
|
24
24
|
import MaximizeIcon from '@atlaskit/icon/core/maximize';
|
|
25
25
|
import SmartLinkCardIcon from '@atlaskit/icon/core/smart-link-card';
|
|
26
|
+
import UploadIcon from '@atlaskit/icon/core/upload';
|
|
26
27
|
import { mediaFilmstripItemDOMSelector } from '@atlaskit/media-filmstrip';
|
|
27
28
|
import { messages } from '@atlaskit/media-ui';
|
|
28
29
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
@@ -883,6 +884,7 @@ export var floatingToolbar = function floatingToolbar(state, intl) {
|
|
|
883
884
|
if (!mediaPluginState.isResizing && areToolbarFlagsEnabled(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar))) {
|
|
884
885
|
var _pluginInjectionApi$a0, _pluginInjectionApi$a1, _pluginInjectionApi$a10;
|
|
885
886
|
updateToFullHeightSeparator(items);
|
|
887
|
+
var showReplaceOption = !isViewOnly && mediaPluginState.allowsUploads && expValEquals('platform_editor_inline_media_replacement', 'isEnabled', true) && selectedNodeType === mediaSingle;
|
|
886
888
|
var customOptions = [].concat(_toConsumableArray(getLinkingDropdownOptions(state, intl, mediaLinkingState, allowMediaInline && selectedNodeType && selectedNodeType === mediaInline, allowLinking, isViewOnly)), _toConsumableArray(getAltTextDropdownOption(state, intl.formatMessage, allowAltTextOnImages, selectedNodeType, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a0 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a0 === void 0 ? void 0 : _pluginInjectionApi$a0.actions)), _toConsumableArray(getResizeDropdownOption(options, state, intl.formatMessage, selectedNodeType)));
|
|
887
889
|
if (customOptions.length) {
|
|
888
890
|
customOptions.push({
|
|
@@ -901,7 +903,17 @@ export var floatingToolbar = function floatingToolbar(state, intl) {
|
|
|
901
903
|
type: 'overflow-dropdown',
|
|
902
904
|
id: 'media',
|
|
903
905
|
testId: overflowDropdwonBtnTriggerTestId,
|
|
904
|
-
options: [].concat(_toConsumableArray(customOptions), [
|
|
906
|
+
options: [].concat(_toConsumableArray(customOptions), _toConsumableArray(showReplaceOption ? [{
|
|
907
|
+
title: intl.formatMessage(mediaAndEmbedToolbarMessages.replaceMedia),
|
|
908
|
+
onClick: function onClick() {
|
|
909
|
+
mediaPluginState.showMediaPickerForReplace();
|
|
910
|
+
return true;
|
|
911
|
+
},
|
|
912
|
+
icon: /*#__PURE__*/React.createElement(UploadIcon, {
|
|
913
|
+
label: ""
|
|
914
|
+
}),
|
|
915
|
+
testId: 'media-replace-toolbar-button'
|
|
916
|
+
}] : []), [_objectSpread({
|
|
905
917
|
title: intl === null || intl === void 0 ? void 0 : intl.formatMessage(commonMessages.copyToClipboard),
|
|
906
918
|
onClick: function onClick() {
|
|
907
919
|
var _pluginInjectionApi$c4, _pluginInjectionApi$f3;
|
|
@@ -3,7 +3,7 @@ import type { ContextIdentifierProvider, MediaProvider } from '@atlaskit/editor-
|
|
|
3
3
|
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
4
4
|
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
5
5
|
import { type MediaTraceContext } from '@atlaskit/media-common';
|
|
6
|
-
import type {
|
|
6
|
+
import type { getPosHandler as ProsemirrorGetPosHandler, MediaOptions, MediaPluginState, SupportedMediaAttributes } from '../types';
|
|
7
7
|
type RemoteDimensions = {
|
|
8
8
|
height: number;
|
|
9
9
|
id: string;
|
|
@@ -13,6 +13,8 @@ export interface MediaNodeUpdaterProps {
|
|
|
13
13
|
contextIdentifierProvider?: Promise<ContextIdentifierProvider>;
|
|
14
14
|
dispatchAnalyticsEvent?: DispatchAnalyticsEvent;
|
|
15
15
|
isMediaSingle: boolean;
|
|
16
|
+
/** Content column width in pixels, used to clamp display width on media replacement. */
|
|
17
|
+
lineLength?: number;
|
|
16
18
|
mediaOptions?: MediaOptions;
|
|
17
19
|
mediaProvider?: Promise<MediaProvider>;
|
|
18
20
|
node: PMNode;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Computes the new mediaSingle display width that preserves the original display height
|
|
3
|
+
* when a media node is replaced with a file of a different aspect ratio, clamped to valid bounds.
|
|
4
|
+
*
|
|
5
|
+
* @param targetDisplayHeight - The display height to preserve (from the old image)
|
|
6
|
+
* @param newIntrinsicWidth - The new file's intrinsic pixel width
|
|
7
|
+
* @param newIntrinsicHeight - The new file's intrinsic pixel height
|
|
8
|
+
* @param layout - The mediaSingle layout (affects max width)
|
|
9
|
+
* @param lineLength - The editor content column width in pixels
|
|
10
|
+
*/
|
|
11
|
+
export declare const computeReplacementDisplayWidth: (targetDisplayHeight: number, newIntrinsicWidth: number, newIntrinsicHeight: number, layout: string, lineLength: number) => number;
|
|
@@ -52,6 +52,8 @@ export declare class MediaPluginStateImplementation implements MediaPluginState
|
|
|
52
52
|
private removeOnCloseListener;
|
|
53
53
|
private openMediaPickerBrowser?;
|
|
54
54
|
private onPopupToggleCallback;
|
|
55
|
+
replaceMediaFileId: string | null;
|
|
56
|
+
replaceMediaTargetDisplayHeight: number | null;
|
|
55
57
|
private identifierCount;
|
|
56
58
|
private outOfEditorScopeIdentifierMap;
|
|
57
59
|
private taskManager;
|
|
@@ -88,6 +90,16 @@ export declare class MediaPluginStateImplementation implements MediaPluginState
|
|
|
88
90
|
splitMediaGroup: () => boolean;
|
|
89
91
|
onPopupPickerClose: () => void;
|
|
90
92
|
showMediaPicker: () => void;
|
|
93
|
+
/**
|
|
94
|
+
* Opens the media picker in "replace" mode. The next file selected/uploaded
|
|
95
|
+
* will replace the currently selected mediaSingle node's media child in-place,
|
|
96
|
+
* preserving layout, width, and caption.
|
|
97
|
+
*
|
|
98
|
+
* The display height is computed and stored so that after the new file's intrinsic
|
|
99
|
+
* dimensions are fetched, the mediaSingle display width can be adjusted to maintain
|
|
100
|
+
* visual height stability rather than width stability.
|
|
101
|
+
*/
|
|
102
|
+
showMediaPickerForReplace: () => void;
|
|
91
103
|
setBrowseFn: (browseFn: () => void) => void;
|
|
92
104
|
onPopupToggle: (onPopupToggleCallback: (isOpen: boolean) => void) => void;
|
|
93
105
|
/**
|
|
@@ -53,6 +53,8 @@ export interface MediaPluginState {
|
|
|
53
53
|
pickerPromises: Array<Promise<PickerFacade>>;
|
|
54
54
|
pickers: PickerFacade[];
|
|
55
55
|
removeSelectedMediaContainer: () => boolean;
|
|
56
|
+
replaceMediaFileId: string | null;
|
|
57
|
+
replaceMediaTargetDisplayHeight: number | null;
|
|
56
58
|
resizingWidth: number;
|
|
57
59
|
selectedMediaContainerNode: () => PMNode | undefined;
|
|
58
60
|
setBrowseFn: (browseFn: () => void) => void;
|
|
@@ -63,6 +65,7 @@ export interface MediaPluginState {
|
|
|
63
65
|
showDropzone: boolean;
|
|
64
66
|
showEditingDialog?: boolean;
|
|
65
67
|
showMediaPicker: () => void;
|
|
68
|
+
showMediaPickerForReplace: () => void;
|
|
66
69
|
splitMediaGroup: () => boolean;
|
|
67
70
|
subscribeToUploadInProgressState: (fn: (isUploading: boolean) => void) => void;
|
|
68
71
|
trackOutOfScopeIdentifier: (identifier: Identifier) => void;
|
|
@@ -3,7 +3,7 @@ import type { ContextIdentifierProvider, MediaProvider } from '@atlaskit/editor-
|
|
|
3
3
|
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
4
4
|
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
5
5
|
import { type MediaTraceContext } from '@atlaskit/media-common';
|
|
6
|
-
import type {
|
|
6
|
+
import type { getPosHandler as ProsemirrorGetPosHandler, MediaOptions, MediaPluginState, SupportedMediaAttributes } from '../types';
|
|
7
7
|
type RemoteDimensions = {
|
|
8
8
|
height: number;
|
|
9
9
|
id: string;
|
|
@@ -13,6 +13,8 @@ export interface MediaNodeUpdaterProps {
|
|
|
13
13
|
contextIdentifierProvider?: Promise<ContextIdentifierProvider>;
|
|
14
14
|
dispatchAnalyticsEvent?: DispatchAnalyticsEvent;
|
|
15
15
|
isMediaSingle: boolean;
|
|
16
|
+
/** Content column width in pixels, used to clamp display width on media replacement. */
|
|
17
|
+
lineLength?: number;
|
|
16
18
|
mediaOptions?: MediaOptions;
|
|
17
19
|
mediaProvider?: Promise<MediaProvider>;
|
|
18
20
|
node: PMNode;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Computes the new mediaSingle display width that preserves the original display height
|
|
3
|
+
* when a media node is replaced with a file of a different aspect ratio, clamped to valid bounds.
|
|
4
|
+
*
|
|
5
|
+
* @param targetDisplayHeight - The display height to preserve (from the old image)
|
|
6
|
+
* @param newIntrinsicWidth - The new file's intrinsic pixel width
|
|
7
|
+
* @param newIntrinsicHeight - The new file's intrinsic pixel height
|
|
8
|
+
* @param layout - The mediaSingle layout (affects max width)
|
|
9
|
+
* @param lineLength - The editor content column width in pixels
|
|
10
|
+
*/
|
|
11
|
+
export declare const computeReplacementDisplayWidth: (targetDisplayHeight: number, newIntrinsicWidth: number, newIntrinsicHeight: number, layout: string, lineLength: number) => number;
|
|
@@ -52,6 +52,8 @@ export declare class MediaPluginStateImplementation implements MediaPluginState
|
|
|
52
52
|
private removeOnCloseListener;
|
|
53
53
|
private openMediaPickerBrowser?;
|
|
54
54
|
private onPopupToggleCallback;
|
|
55
|
+
replaceMediaFileId: string | null;
|
|
56
|
+
replaceMediaTargetDisplayHeight: number | null;
|
|
55
57
|
private identifierCount;
|
|
56
58
|
private outOfEditorScopeIdentifierMap;
|
|
57
59
|
private taskManager;
|
|
@@ -88,6 +90,16 @@ export declare class MediaPluginStateImplementation implements MediaPluginState
|
|
|
88
90
|
splitMediaGroup: () => boolean;
|
|
89
91
|
onPopupPickerClose: () => void;
|
|
90
92
|
showMediaPicker: () => void;
|
|
93
|
+
/**
|
|
94
|
+
* Opens the media picker in "replace" mode. The next file selected/uploaded
|
|
95
|
+
* will replace the currently selected mediaSingle node's media child in-place,
|
|
96
|
+
* preserving layout, width, and caption.
|
|
97
|
+
*
|
|
98
|
+
* The display height is computed and stored so that after the new file's intrinsic
|
|
99
|
+
* dimensions are fetched, the mediaSingle display width can be adjusted to maintain
|
|
100
|
+
* visual height stability rather than width stability.
|
|
101
|
+
*/
|
|
102
|
+
showMediaPickerForReplace: () => void;
|
|
91
103
|
setBrowseFn: (browseFn: () => void) => void;
|
|
92
104
|
onPopupToggle: (onPopupToggleCallback: (isOpen: boolean) => void) => void;
|
|
93
105
|
/**
|
|
@@ -53,6 +53,8 @@ export interface MediaPluginState {
|
|
|
53
53
|
pickerPromises: Array<Promise<PickerFacade>>;
|
|
54
54
|
pickers: PickerFacade[];
|
|
55
55
|
removeSelectedMediaContainer: () => boolean;
|
|
56
|
+
replaceMediaFileId: string | null;
|
|
57
|
+
replaceMediaTargetDisplayHeight: number | null;
|
|
56
58
|
resizingWidth: number;
|
|
57
59
|
selectedMediaContainerNode: () => PMNode | undefined;
|
|
58
60
|
setBrowseFn: (browseFn: () => void) => void;
|
|
@@ -63,6 +65,7 @@ export interface MediaPluginState {
|
|
|
63
65
|
showDropzone: boolean;
|
|
64
66
|
showEditingDialog?: boolean;
|
|
65
67
|
showMediaPicker: () => void;
|
|
68
|
+
showMediaPickerForReplace: () => void;
|
|
66
69
|
splitMediaGroup: () => boolean;
|
|
67
70
|
subscribeToUploadInProgressState: (fn: (isUploading: boolean) => void) => void;
|
|
68
71
|
trackOutOfScopeIdentifier: (identifier: Identifier) => void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atlaskit/editor-plugin-media/nodeviewHelpers",
|
|
3
|
+
"main": "../dist/cjs/nodeviews/nodeviewHelpers.js",
|
|
4
|
+
"module": "../dist/esm/nodeviews/nodeviewHelpers.js",
|
|
5
|
+
"module:es2019": "../dist/es2019/nodeviews/nodeviewHelpers.js",
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"*.compiled.css"
|
|
8
|
+
],
|
|
9
|
+
"types": "../dist/types/nodeviews/nodeviewHelpers.d.ts",
|
|
10
|
+
"typesVersions": {
|
|
11
|
+
">=4.5 <5.9": {
|
|
12
|
+
"*": [
|
|
13
|
+
"../dist/types-ts4.5/nodeviews/nodeviewHelpers.d.ts"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-media",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.5.1",
|
|
4
4
|
"description": "Media plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@atlaskit/button": "^23.11.0",
|
|
36
36
|
"@atlaskit/editor-palette": "^2.1.0",
|
|
37
37
|
"@atlaskit/editor-plugin-analytics": "^10.0.0",
|
|
38
|
-
"@atlaskit/editor-plugin-annotation": "^10.
|
|
38
|
+
"@atlaskit/editor-plugin-annotation": "^10.2.0",
|
|
39
39
|
"@atlaskit/editor-plugin-connectivity": "^10.0.0",
|
|
40
40
|
"@atlaskit/editor-plugin-decorations": "^10.0.0",
|
|
41
41
|
"@atlaskit/editor-plugin-editor-disabled": "^10.0.0",
|
|
@@ -61,12 +61,12 @@
|
|
|
61
61
|
"@atlaskit/media-common": "^13.2.0",
|
|
62
62
|
"@atlaskit/media-filmstrip": "^51.3.0",
|
|
63
63
|
"@atlaskit/media-picker": "^71.2.0",
|
|
64
|
-
"@atlaskit/media-ui": "^29.
|
|
64
|
+
"@atlaskit/media-ui": "^29.2.0",
|
|
65
65
|
"@atlaskit/media-viewer": "^53.1.0",
|
|
66
66
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
67
67
|
"@atlaskit/primitives": "^19.0.0",
|
|
68
68
|
"@atlaskit/textfield": "^8.3.0",
|
|
69
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
69
|
+
"@atlaskit/tmp-editor-statsig": "^75.0.0",
|
|
70
70
|
"@atlaskit/tokens": "^13.0.0",
|
|
71
71
|
"@atlaskit/tooltip": "^22.0.0",
|
|
72
72
|
"@babel/runtime": "^7.0.0",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"uuid": "^3.1.0"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"@atlaskit/editor-common": "^114.
|
|
81
|
+
"@atlaskit/editor-common": "^114.19.0",
|
|
82
82
|
"@atlaskit/media-core": "^37.0.0",
|
|
83
83
|
"react": "^18.2.0",
|
|
84
84
|
"react-dom": "^18.2.0",
|