@atlaskit/editor-core 187.8.11 → 187.9.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/plugins/floating-toolbar/index.js +1 -3
  3. package/dist/cjs/plugins/media/nodeviews/mediaSingle.js +33 -23
  4. package/dist/cjs/plugins/media/toolbar/index.js +16 -2
  5. package/dist/cjs/plugins/media/toolbar/utils.js +7 -2
  6. package/dist/cjs/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.js +574 -0
  7. package/dist/cjs/plugins/media/ui/ResizableMediaSingle/guidelines.js +117 -0
  8. package/dist/cjs/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.js +15 -0
  9. package/dist/cjs/ui/ElementBrowser/components/ElementSearch.js +5 -3
  10. package/dist/cjs/ui/ElementBrowser/components/StatelessElementBrowser.js +8 -6
  11. package/dist/cjs/ui/Resizer/ResizeLabel/index.js +20 -0
  12. package/dist/cjs/ui/Resizer/ResizeLabel/styles.js +17 -0
  13. package/dist/cjs/version-wrapper.js +1 -1
  14. package/dist/cjs/version.json +1 -1
  15. package/dist/es2019/plugins/floating-toolbar/index.js +1 -1
  16. package/dist/es2019/plugins/media/nodeviews/mediaSingle.js +34 -23
  17. package/dist/es2019/plugins/media/toolbar/index.js +19 -3
  18. package/dist/es2019/plugins/media/toolbar/utils.js +7 -1
  19. package/dist/es2019/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.js +493 -0
  20. package/dist/es2019/plugins/media/ui/ResizableMediaSingle/guidelines.js +108 -0
  21. package/dist/es2019/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.js +8 -0
  22. package/dist/es2019/ui/ElementBrowser/components/ElementSearch.js +5 -3
  23. package/dist/es2019/ui/ElementBrowser/components/StatelessElementBrowser.js +9 -6
  24. package/dist/es2019/ui/Resizer/ResizeLabel/index.js +12 -0
  25. package/dist/es2019/ui/Resizer/ResizeLabel/styles.js +25 -0
  26. package/dist/es2019/version-wrapper.js +1 -1
  27. package/dist/es2019/version.json +1 -1
  28. package/dist/esm/plugins/floating-toolbar/index.js +1 -3
  29. package/dist/esm/plugins/media/nodeviews/mediaSingle.js +33 -23
  30. package/dist/esm/plugins/media/toolbar/index.js +17 -3
  31. package/dist/esm/plugins/media/toolbar/utils.js +5 -1
  32. package/dist/esm/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.js +564 -0
  33. package/dist/esm/plugins/media/ui/ResizableMediaSingle/guidelines.js +108 -0
  34. package/dist/esm/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.js +8 -0
  35. package/dist/esm/ui/ElementBrowser/components/ElementSearch.js +5 -3
  36. package/dist/esm/ui/ElementBrowser/components/StatelessElementBrowser.js +8 -6
  37. package/dist/esm/ui/Resizer/ResizeLabel/index.js +12 -0
  38. package/dist/esm/ui/Resizer/ResizeLabel/styles.js +7 -0
  39. package/dist/esm/version-wrapper.js +1 -1
  40. package/dist/esm/version.json +1 -1
  41. package/dist/types/labs/next/presets/universal.d.ts +1 -2
  42. package/dist/types/plugins/floating-toolbar/index.d.ts +3 -1
  43. package/dist/types/plugins/media/index.d.ts +2 -0
  44. package/dist/types/plugins/media/nodeviews/styles.d.ts +1 -1
  45. package/dist/types/plugins/media/pm-plugins/main.d.ts +1 -1
  46. package/dist/types/plugins/media/toolbar/utils.d.ts +1 -0
  47. package/dist/types/plugins/media/types.d.ts +1 -0
  48. package/dist/types/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.d.ts +61 -0
  49. package/dist/types/plugins/media/ui/ResizableMediaSingle/guidelines.d.ts +4 -0
  50. package/dist/types/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.d.ts +7 -0
  51. package/dist/types/plugins/media/ui/ResizableMediaSingle/types.d.ts +1 -0
  52. package/dist/types/ui/ElementBrowser/components/ElementSearch.d.ts +2 -1
  53. package/dist/types/ui/ElementBrowser/components/StatelessElementBrowser.d.ts +4 -4
  54. package/dist/types/ui/Resizer/ResizeLabel/index.d.ts +8 -0
  55. package/dist/types/ui/Resizer/ResizeLabel/styles.d.ts +3 -0
  56. package/dist/types-ts4.5/labs/next/presets/universal.d.ts +1 -2
  57. package/dist/types-ts4.5/plugins/floating-toolbar/index.d.ts +3 -1
  58. package/dist/types-ts4.5/plugins/media/index.d.ts +2 -0
  59. package/dist/types-ts4.5/plugins/media/nodeviews/styles.d.ts +1 -1
  60. package/dist/types-ts4.5/plugins/media/pm-plugins/main.d.ts +1 -1
  61. package/dist/types-ts4.5/plugins/media/toolbar/utils.d.ts +1 -0
  62. package/dist/types-ts4.5/plugins/media/types.d.ts +1 -0
  63. package/dist/types-ts4.5/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.d.ts +61 -0
  64. package/dist/types-ts4.5/plugins/media/ui/ResizableMediaSingle/guidelines.d.ts +4 -0
  65. package/dist/types-ts4.5/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.d.ts +7 -0
  66. package/dist/types-ts4.5/plugins/media/ui/ResizableMediaSingle/types.d.ts +1 -0
  67. package/dist/types-ts4.5/ui/ElementBrowser/components/ElementSearch.d.ts +2 -1
  68. package/dist/types-ts4.5/ui/ElementBrowser/components/StatelessElementBrowser.d.ts +4 -4
  69. package/dist/types-ts4.5/ui/Resizer/ResizeLabel/index.d.ts +8 -0
  70. package/dist/types-ts4.5/ui/Resizer/ResizeLabel/styles.d.ts +3 -0
  71. package/package.json +2 -2
  72. package/report.api.md +7 -4
  73. package/tmp/api-report-tmp.d.ts +5 -1
@@ -0,0 +1,493 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ /** @jsx jsx */
3
+ import React from 'react';
4
+ import { jsx } from '@emotion/react';
5
+ import { findParentNodeOfTypeClosestToPos, hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
6
+ import { getMediaClient } from '@atlaskit/media-client';
7
+ import { calcPctFromPx, wrappedLayouts, handleSides, imageAlignmentMap, calcColumnsFromPx, DEFAULT_IMAGE_WIDTH } from '@atlaskit/editor-common/ui';
8
+ import { nonWrappedLayouts, setNodeSelection } from '@atlaskit/editor-common/utils';
9
+ import { akEditorFullWidthLayoutWidth, akEditorGutterPadding, akEditorDefaultLayoutWidth } from '@atlaskit/editor-shared-styles';
10
+ import { wrapperStyle } from './styled';
11
+ import { ResizerNext } from '@atlaskit/editor-common/resizer';
12
+ import ResizeLabel from '../../../../ui/Resizer/ResizeLabel';
13
+ import { injectIntl } from 'react-intl-next';
14
+ import { messages } from './resizable-media-single-messages';
15
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
16
+ import classnames from 'classnames';
17
+ import { richMediaClassName, resizerStyles } from '@atlaskit/editor-common/styles';
18
+ import { MEDIA_SINGLE_MIN_PIXEL_WIDTH, MEDIA_SINGLE_SNAP_GAP, MEDIA_SINGLE_HIGHLIGHT_GAP } from '@atlaskit/editor-common/media-single';
19
+ import { findClosestSnap, getSnapWidth, getGuidelinesWithHighlights } from '@atlaskit/editor-common/guideline';
20
+ import { generateDefaultGuidelines, generateDynamicGuidelines } from './guidelines';
21
+ export const resizerNextTestId = 'mediaSingle.resizerNext.testid';
22
+ export function calcOffsetLeft(insideInlineLike, insideLayout, pmViewDom, wrapper) {
23
+ if (wrapper && insideInlineLike && !insideLayout) {
24
+ const currentNode = wrapper;
25
+ const boundingRect = currentNode.getBoundingClientRect();
26
+ return boundingRect.left - pmViewDom.getBoundingClientRect().left;
27
+ }
28
+ return 0;
29
+ }
30
+
31
+ // TODO: Create new fixed image size event
32
+ const getResizeAnalyticsEvent = (type, size, layout) => {
33
+ const actionSubject = type === 'embed' ? ACTION_SUBJECT.EMBEDS : ACTION_SUBJECT.MEDIA_SINGLE;
34
+ return {
35
+ action: ACTION.EDITED,
36
+ actionSubject,
37
+ actionSubjectId: ACTION_SUBJECT_ID.RESIZED,
38
+ attributes: {
39
+ size,
40
+ layout
41
+ },
42
+ eventType: EVENT_TYPE.UI
43
+ };
44
+ };
45
+ class ResizableMediaSingleNext extends React.Component {
46
+ constructor(props) {
47
+ super(props);
48
+ _defineProperty(this, "calcNewSize", (newWidth, stop) => {
49
+ const {
50
+ layout,
51
+ view: {
52
+ state
53
+ },
54
+ containerWidth,
55
+ lineLength
56
+ } = this.props;
57
+ const newPct = calcPctFromPx(newWidth, this.props.lineLength) * 100;
58
+ this.setState({
59
+ resizedPctWidth: newPct
60
+ });
61
+ let newLayout = hasParentNodeOfType(state.schema.nodes.table)(state.selection) ? layout : this.calcUnwrappedLayout(newWidth, containerWidth, lineLength);
62
+ if (newPct <= 100) {
63
+ if (this.wrappedLayout && (stop ? newPct !== 100 : true)) {
64
+ newLayout = layout;
65
+ }
66
+ return {
67
+ width: newPct,
68
+ layout: newLayout
69
+ };
70
+ } else {
71
+ return {
72
+ width: this.props.pctWidth || null,
73
+ layout: newLayout
74
+ };
75
+ }
76
+ });
77
+ _defineProperty(this, "calcUnwrappedLayout", (width, containerWidth, contentWidth) => {
78
+ if (width <= contentWidth) {
79
+ return 'center';
80
+ }
81
+ if (width < Math.min(containerWidth - akEditorGutterPadding * 2, akEditorFullWidthLayoutWidth)) {
82
+ return 'wide';
83
+ }
84
+
85
+ // set full width to be containerWidth - akEditorGutterPadding * 2
86
+ // instead of containerWidth - akEditorBreakoutPadding,
87
+ // so that we have image aligned with text
88
+ return 'full-width';
89
+ });
90
+ _defineProperty(this, "calcColumnLeftOffset", () => {
91
+ const {
92
+ offsetLeft
93
+ } = this.state;
94
+ return this.insideInlineLike ? calcColumnsFromPx(offsetLeft, this.props.lineLength, this.props.gridSize) : 0;
95
+ });
96
+ _defineProperty(this, "calcPxHeight", newWidth => {
97
+ const {
98
+ width = newWidth,
99
+ height
100
+ } = this.props;
101
+ return Math.round(height / width * newWidth);
102
+ });
103
+ _defineProperty(this, "saveWrapper", wrapper => this.wrapper = wrapper);
104
+ _defineProperty(this, "displayGuideline", guidelines => {
105
+ var _pluginInjectionApi$d, _pluginInjectionApi$d2, _pluginInjectionApi$d3;
106
+ const {
107
+ pluginInjectionApi
108
+ } = this.props;
109
+ pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$d = pluginInjectionApi.dependencies) === null || _pluginInjectionApi$d === void 0 ? void 0 : (_pluginInjectionApi$d2 = _pluginInjectionApi$d.guideline) === null || _pluginInjectionApi$d2 === void 0 ? void 0 : (_pluginInjectionApi$d3 = _pluginInjectionApi$d2.actions) === null || _pluginInjectionApi$d3 === void 0 ? void 0 : _pluginInjectionApi$d3.displayGuideline(this.props.view)({
110
+ guidelines
111
+ });
112
+ });
113
+ _defineProperty(this, "setIsResizing", isResizing => {
114
+ const {
115
+ state,
116
+ dispatch
117
+ } = this.props.view;
118
+ const tr = state.tr;
119
+ tr.setMeta('mediaSinglePlugin.isResizing', isResizing);
120
+ return dispatch(tr);
121
+ });
122
+ const initialWidth = props.mediaSingleWidth || this.calcInitialWidth();
123
+ this.state = {
124
+ offsetLeft: calcOffsetLeft(this.insideInlineLike, this.insideLayout, this.props.view.dom, undefined),
125
+ // We default to true until we resolve the file type
126
+ isVideoFile: true,
127
+ isResizing: false,
128
+ size: {
129
+ width: initialWidth,
130
+ height: this.calcPxHeight(initialWidth)
131
+ }
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Calculate media single node initial width if props.mediaSingleWidth is undefined
137
+ * (Mainly when switching from lagacy experience to new experience).
138
+ *
139
+ * @returns initial width in pixel
140
+ */
141
+ calcInitialWidth() {
142
+ const {
143
+ width: origWidth,
144
+ lineLength: contentWidth,
145
+ containerWidth
146
+ } = this.props;
147
+ return Math.max(Math.min(origWidth || DEFAULT_IMAGE_WIDTH, contentWidth || containerWidth || akEditorDefaultLayoutWidth), MEDIA_SINGLE_MIN_PIXEL_WIDTH);
148
+ }
149
+ componentDidUpdate(prevProps, prevState) {
150
+ const offsetLeft = calcOffsetLeft(this.insideInlineLike, this.insideLayout, this.props.view.dom, this.wrapper);
151
+ if (offsetLeft !== this.state.offsetLeft && offsetLeft >= 0) {
152
+ this.setState({
153
+ offsetLeft
154
+ });
155
+ }
156
+
157
+ // Handle undo, when the actual pctWidth changed,
158
+ // we sync up with the internal state.
159
+ if (prevProps.pctWidth !== this.props.pctWidth) {
160
+ this.setState({
161
+ resizedPctWidth: this.props.pctWidth
162
+ });
163
+ }
164
+ if (prevState.isResizing !== this.state.isResizing && this.state.isResizing) {
165
+ const guidelines = this.getDefaultGuidelines();
166
+ this.displayGuideline(guidelines);
167
+ }
168
+ if (prevProps.lineLength === undefined && this.props.lineLength !== undefined && this.props.mediaSingleWidth === null) {
169
+ // re-initalises size when lineLength becomes defined later
170
+ // ensures extended experience renders legacy image with the same size as the legacy experience
171
+ const initialWidth = this.calcInitialWidth();
172
+ this.setState({
173
+ size: {
174
+ width: initialWidth,
175
+ height: this.calcPxHeight(initialWidth)
176
+ }
177
+ });
178
+ }
179
+ return true;
180
+ }
181
+
182
+ // check if is inside of layout, table, expand, nestedExpand and list item
183
+ isNestedNode() {
184
+ return !!(this.$pos && this.$pos.parent.type.name !== 'doc');
185
+ }
186
+ getDefaultGuidelines() {
187
+ const {
188
+ lineLength,
189
+ containerWidth,
190
+ fullWidthMode
191
+ } = this.props;
192
+
193
+ // disable guidelines for nested media single node
194
+ return this.isNestedNode() ? [] : generateDefaultGuidelines(lineLength, containerWidth, fullWidthMode);
195
+ }
196
+
197
+ // Calculate width of media nodes for snaps based on dynamic guidelines
198
+ // TODO: refactor this later, maybe use state to hold snaps array
199
+ getSnaps() {
200
+ const {
201
+ view
202
+ } = this.props;
203
+ const {
204
+ dom
205
+ } = view;
206
+ const defaultGuidelines = this.getDefaultGuidelines();
207
+ // disable guidelines for nested media single node
208
+ const dynamicGuidelines = this.isNestedNode() ? [] : generateDynamicGuidelines(view);
209
+ const guidelines = [...defaultGuidelines, ...dynamicGuidelines];
210
+ const mediaSingleSelector = 'div.mediaSingleView-content-wrap.ProseMirror-selectednode';
211
+ const {
212
+ width
213
+ } = dom.getBoundingClientRect();
214
+ const halfWidth = width / 2;
215
+ const selectedMedia = dom.querySelector(mediaSingleSelector);
216
+ if (selectedMedia) {
217
+ const pixelWidth = parseFloat(selectedMedia.getAttribute('width') || '') || 0;
218
+ const layout = selectedMedia.getAttribute('layout');
219
+ let snap = 0;
220
+ switch (layout) {
221
+ case 'align-start':
222
+ case 'wrap-left':
223
+ snap = pixelWidth - halfWidth;
224
+ break;
225
+ case 'align-end':
226
+ case 'wrap-right':
227
+ snap = width - pixelWidth - halfWidth;
228
+ break;
229
+ case 'center':
230
+ case 'wide':
231
+ case 'full-width':
232
+ snap = -pixelWidth / 2;
233
+ break;
234
+ // we ingnore full-width and wide
235
+ default:
236
+ snap = 0;
237
+ }
238
+ const snapWidths = getSnapWidth(guidelines, pixelWidth, snap, layout);
239
+ const snapToWidths = snapWidths.map(s => s && s.width);
240
+
241
+ // update guidelines
242
+ if (this.state.isResizing) {
243
+ const {
244
+ gap,
245
+ keys: activeGuidelineKeys
246
+ } = findClosestSnap(this.state.size.width, snapToWidths, snapWidths, MEDIA_SINGLE_HIGHLIGHT_GAP);
247
+ this.displayGuideline(getGuidelinesWithHighlights(gap, MEDIA_SINGLE_HIGHLIGHT_GAP, activeGuidelineKeys, guidelines));
248
+ }
249
+ return snapToWidths.length > 0 ? {
250
+ x: snapToWidths
251
+ } : {};
252
+ }
253
+ return {};
254
+ }
255
+ get wrappedLayout() {
256
+ return wrappedLayouts.indexOf(this.props.layout) > -1;
257
+ }
258
+ async componentDidMount() {
259
+ const {
260
+ viewMediaClientConfig
261
+ } = this.props;
262
+ if (viewMediaClientConfig) {
263
+ await this.checkVideoFile(viewMediaClientConfig);
264
+ }
265
+ }
266
+ UNSAFE_componentWillReceiveProps(nextProps) {
267
+ if (this.props.viewMediaClientConfig !== nextProps.viewMediaClientConfig) {
268
+ this.checkVideoFile(nextProps.viewMediaClientConfig);
269
+ }
270
+ }
271
+ async checkVideoFile(viewMediaClientConfig) {
272
+ const $pos = this.$pos;
273
+ if (!$pos || !viewMediaClientConfig) {
274
+ return;
275
+ }
276
+ const mediaNode = this.props.view.state.doc.nodeAt($pos.pos + 1);
277
+ if (!mediaNode || !mediaNode.attrs.id) {
278
+ return;
279
+ }
280
+ const mediaClient = getMediaClient(viewMediaClientConfig);
281
+ try {
282
+ const state = await mediaClient.file.getCurrentState(mediaNode.attrs.id, {
283
+ collectionName: mediaNode.attrs.collection
284
+ });
285
+ if (state && state.status !== 'error' && state.mediaType === 'image') {
286
+ this.setState({
287
+ isVideoFile: false
288
+ });
289
+ }
290
+ } catch (err) {
291
+ this.setState({
292
+ isVideoFile: false
293
+ });
294
+ }
295
+ }
296
+ get $pos() {
297
+ if (typeof this.props.getPos !== 'function') {
298
+ return null;
299
+ }
300
+ const pos = this.props.getPos();
301
+ if (Number.isNaN(pos) || typeof pos !== 'number') {
302
+ return null;
303
+ }
304
+
305
+ // need to pass view because we may not get updated props in time
306
+ return this.props.view.state.doc.resolve(pos);
307
+ }
308
+
309
+ /**
310
+ * The maxmimum number of grid columns this node can resize to.
311
+ */
312
+ get gridWidth() {
313
+ const {
314
+ gridSize
315
+ } = this.props;
316
+ return !(this.wrappedLayout || this.insideInlineLike) ? gridSize / 2 : gridSize;
317
+ }
318
+ get insideInlineLike() {
319
+ const $pos = this.$pos;
320
+ if (!$pos) {
321
+ return false;
322
+ }
323
+ const {
324
+ listItem
325
+ } = this.props.view.state.schema.nodes;
326
+ return !!findParentNodeOfTypeClosestToPos($pos, [listItem]);
327
+ }
328
+ get insideLayout() {
329
+ const $pos = this.$pos;
330
+ if (!$pos) {
331
+ return false;
332
+ }
333
+ const {
334
+ layoutColumn
335
+ } = this.props.view.state.schema.nodes;
336
+ return !!findParentNodeOfTypeClosestToPos($pos, [layoutColumn]);
337
+ }
338
+ render() {
339
+ const {
340
+ width: origWidth,
341
+ layout,
342
+ pctWidth,
343
+ containerWidth,
344
+ fullWidthMode,
345
+ selected,
346
+ children,
347
+ intl
348
+ } = this.props;
349
+ const {
350
+ isResizing,
351
+ size
352
+ } = this.state;
353
+ const enable = {};
354
+ handleSides.forEach(side => {
355
+ const oppositeSide = side === 'left' ? 'right' : 'left';
356
+ enable[side] = nonWrappedLayouts.concat(`wrap-${oppositeSide}`).concat(`align-${imageAlignmentMap[oppositeSide]}`).indexOf(layout) > -1;
357
+ if (side === 'left' && this.insideInlineLike) {
358
+ enable[side] = false;
359
+ }
360
+ });
361
+
362
+ /**
363
+ * NEW REFACTOR
364
+ */
365
+ const roundPixelValue = value => Math.round(value);
366
+ const getHeightFromNewWidth = (originalWidth, originalHeight, newWidth) => roundPixelValue(originalHeight / originalWidth * newWidth);
367
+ const calculateSizeState = (size, delta, originalWidth = 0, originalHeight, onResizeStop = false) => {
368
+ const calculatedWidth = roundPixelValue(size.width + delta.width);
369
+ const calculatedWidthWithLayout = this.calcNewSize(calculatedWidth, onResizeStop);
370
+ const calculatedHeightWithLayout = getHeightFromNewWidth(originalWidth, originalHeight, calculatedWidth);
371
+ return {
372
+ width: calculatedWidth,
373
+ height: calculatedHeightWithLayout,
374
+ calculatedWidthWithLayout
375
+ };
376
+ };
377
+ const selectCurrentMediaNode = () => {
378
+ if (!this.props.selected && typeof this.props.getPos === 'function') {
379
+ const propPos = this.props.getPos();
380
+ if (propPos !== undefined) {
381
+ setNodeSelection(this.props.view, propPos);
382
+ }
383
+ }
384
+ };
385
+ const handleResizeStart = () => {
386
+ this.setState({
387
+ isResizing: true
388
+ });
389
+ selectCurrentMediaNode();
390
+ this.setIsResizing(true);
391
+
392
+ // TODO: Update once type updated from editor common resizer
393
+ return 0;
394
+ };
395
+ const handleResize = (size, delta) => {
396
+ const {
397
+ width: originalWidth,
398
+ height: originalHeight,
399
+ layout,
400
+ updateSize
401
+ } = this.props;
402
+ const {
403
+ width,
404
+ height,
405
+ calculatedWidthWithLayout
406
+ } = calculateSizeState(size, delta, originalWidth, originalHeight);
407
+ this.setState({
408
+ size: {
409
+ width,
410
+ height
411
+ }
412
+ });
413
+ if (calculatedWidthWithLayout.layout !== layout) {
414
+ updateSize(width, calculatedWidthWithLayout.layout);
415
+ }
416
+
417
+ // TODO: Update once type updated from editor common resizer
418
+ return 0;
419
+ };
420
+ const handleResizeStop = (size, delta) => {
421
+ const {
422
+ width: originalWidth,
423
+ height: originalHeight,
424
+ updateSize,
425
+ dispatchAnalyticsEvent,
426
+ nodeType
427
+ } = this.props;
428
+ const {
429
+ width,
430
+ height,
431
+ calculatedWidthWithLayout
432
+ } = calculateSizeState(size, delta, originalWidth, originalHeight, true);
433
+ this.setIsResizing(false);
434
+ this.displayGuideline([]);
435
+ if (dispatchAnalyticsEvent) {
436
+ dispatchAnalyticsEvent(getResizeAnalyticsEvent(nodeType, calculatedWidthWithLayout.width, calculatedWidthWithLayout.layout));
437
+ }
438
+ this.setState({
439
+ isResizing: false,
440
+ size: {
441
+ width,
442
+ height
443
+ }
444
+ }, () => {
445
+ updateSize(width, calculatedWidthWithLayout.layout);
446
+ });
447
+
448
+ // TODO: Update once type updated from editor common resizer
449
+ return 0;
450
+ };
451
+
452
+ // TODO: Clean up where this lives and how it gets generated
453
+ const className = classnames(richMediaClassName, `image-${layout}`, isResizing ? 'is-resizing' : 'not-resizing', this.props.className, {
454
+ 'not-resized': !pctWidth,
455
+ 'richMedia-selected': selected,
456
+ 'rich-media-wrapped': layout === 'wrap-left' || layout === 'wrap-right'
457
+ });
458
+ const resizerNextClassName = classnames(className, resizerStyles);
459
+ const showSizeGuide = selected || isResizing;
460
+ const label = layout === 'full-width' ? intl.formatMessage(messages.fullWidthImage) : `${this.state.size.width} x ${this.state.size.height}`;
461
+ return jsx("div", {
462
+ ref: this.saveWrapper,
463
+ css: wrapperStyle({
464
+ layout,
465
+ isResized: !!pctWidth,
466
+ containerWidth: containerWidth || origWidth,
467
+ fullWidthMode,
468
+ mediaSingleWidth: this.state.size.width,
469
+ isNestedNode: this.isNestedNode(),
470
+ isExtendedResizeExperienceOn: true
471
+ })
472
+ }, jsx(ResizerNext, {
473
+ minWidth: MEDIA_SINGLE_MIN_PIXEL_WIDTH,
474
+ className: resizerNextClassName,
475
+ snapGap: MEDIA_SINGLE_SNAP_GAP,
476
+ enable: enable,
477
+ width: size.width,
478
+ handleResizeStart: handleResizeStart,
479
+ handleResize: handleResize,
480
+ handleResizeStop: handleResizeStop,
481
+ snap: this.getSnaps(),
482
+ resizeRatio: nonWrappedLayouts.includes(layout) ? 2 : 1,
483
+ "data-testid": resizerNextTestId
484
+ }, showSizeGuide && jsx(ResizeLabel, {
485
+ label: label,
486
+ containerWidth: this.state.size.width
487
+ }), children));
488
+ }
489
+ }
490
+
491
+ // for testing purpose
492
+ export { ResizableMediaSingleNext as UnwrappedResizableMediaSingleNext };
493
+ export default injectIntl(ResizableMediaSingleNext);
@@ -0,0 +1,108 @@
1
+ import memoizeOne from 'memoize-one';
2
+ import { akEditorGutterPadding, akEditorFullWidthLayoutWidth, breakoutWideScaleRatio } from '@atlaskit/editor-shared-styles';
3
+ import { MEDIA_DYNAMIC_GUIDELINE_PREFIX } from '@atlaskit/editor-common/guideline';
4
+ const mediaSingleSelector = 'div.mediaSingleView-content-wrap:not(.ProseMirror-selectednode)';
5
+ const getDefaultGuidelines = memoizeOne(editorWidth => {
6
+ return [-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6].map((val, index) => ({
7
+ key: `grid_${index}`,
8
+ position: {
9
+ x: val / 12 * editorWidth
10
+ }
11
+ }));
12
+ });
13
+ const getWideGuidelines = memoizeOne(editorWidth => {
14
+ const wideSpacing = editorWidth * breakoutWideScaleRatio / 2;
15
+ return [{
16
+ key: `wide_left`,
17
+ position: {
18
+ x: -wideSpacing
19
+ }
20
+ }, {
21
+ key: `wide_right`,
22
+ position: {
23
+ x: wideSpacing
24
+ }
25
+ }];
26
+ });
27
+ const getFullWidthGuidelines = memoizeOne(containerWidth => {
28
+ const fullWidth = Math.min(containerWidth - akEditorGutterPadding * 2, akEditorFullWidthLayoutWidth) / 2;
29
+ return [{
30
+ key: `full_width_left`,
31
+ position: {
32
+ x: -fullWidth
33
+ }
34
+ }, {
35
+ key: `full_width_right`,
36
+ position: {
37
+ x: fullWidth
38
+ }
39
+ }];
40
+ });
41
+ export const generateDefaultGuidelines = (editorWidth, containerWidth, isFullWidthMode = false) => {
42
+ const innerGrids = getDefaultGuidelines(editorWidth);
43
+ const wideGuidelines = !isFullWidthMode ? getWideGuidelines(editorWidth) : [];
44
+ const fullWidthGuidelines = !isFullWidthMode ? getFullWidthGuidelines(containerWidth) : [];
45
+ return [...innerGrids, ...wideGuidelines, ...fullWidthGuidelines];
46
+ };
47
+ export const generateDynamicGuidelines = view => {
48
+ const {
49
+ dom
50
+ } = view;
51
+ const {
52
+ width
53
+ } = dom.getBoundingClientRect();
54
+ const halfWidth = width / 2;
55
+ let keyIdx = 0;
56
+ const dynamicGuidelines = [...dom.querySelectorAll(mediaSingleSelector)].map((elem, _index) => {
57
+ const pixelWidthAttr = elem.getAttribute('widthType') === 'pixel' ? elem.getAttribute('width') : '';
58
+ const pixelWidth = parseFloat(pixelWidthAttr || '');
59
+ if (Number.isNaN(pixelWidth)) {
60
+ return [];
61
+ }
62
+ const commonStyles = {
63
+ styles: {
64
+ lineStyle: 'dashed'
65
+ },
66
+ show: false
67
+ };
68
+ const key = `${MEDIA_DYNAMIC_GUIDELINE_PREFIX}${++keyIdx}`;
69
+ switch (elem.getAttribute('layout')) {
70
+ case 'align-start':
71
+ case 'wrap-left':
72
+ return {
73
+ position: {
74
+ x: pixelWidth - halfWidth
75
+ },
76
+ key,
77
+ ...commonStyles
78
+ };
79
+ case 'align-end':
80
+ case 'wrap-right':
81
+ return {
82
+ position: {
83
+ x: width - pixelWidth - halfWidth
84
+ },
85
+ key,
86
+ ...commonStyles
87
+ };
88
+ case 'center':
89
+ return [{
90
+ position: {
91
+ x: pixelWidth / 2
92
+ },
93
+ key,
94
+ ...commonStyles
95
+ }, {
96
+ position: {
97
+ x: -pixelWidth / 2
98
+ },
99
+ key,
100
+ ...commonStyles
101
+ }];
102
+ // we ignore full-width and wide
103
+ default:
104
+ return [];
105
+ }
106
+ }).flat();
107
+ return dynamicGuidelines;
108
+ };
@@ -0,0 +1,8 @@
1
+ import { defineMessages } from 'react-intl-next';
2
+ export const messages = defineMessages({
3
+ fullWidthImage: {
4
+ id: 'fabric.editor.image.fullWidthImage',
5
+ defaultMessage: 'Full width image',
6
+ description: 'The image has reached its full width'
7
+ }
8
+ });
@@ -19,6 +19,7 @@ function ElementSearch({
19
19
  },
20
20
  focus,
21
21
  onClick,
22
+ onKeyDown,
22
23
  searchTerm
23
24
  }) {
24
25
  const ref = useFocus(focus);
@@ -38,6 +39,7 @@ function ElementSearch({
38
39
  onChange: onChange,
39
40
  onClick: onClick,
40
41
  onFocus: onFocus,
42
+ onKeyDown: onKeyDown,
41
43
  onBlur: onBlur,
42
44
  elemBeforeInput: jsx("div", {
43
45
  css: elementBeforeInput,
@@ -82,7 +84,7 @@ const wrapper = css`
82
84
  & > [data-ds--text-field--input] {
83
85
  margin-bottom: 3px;
84
86
  font-size: ${relativeFontSizeToBase16(14)};
85
- padding: ${GRID_SIZE}px 6px ${GRID_SIZE}px 0;
87
+ padding: ${GRID_SIZE}px ${"var(--ds-space-075, 6px)"} ${GRID_SIZE}px 0;
86
88
  }
87
89
  }
88
90
  `;
@@ -94,7 +96,7 @@ const wrapperInline = css`
94
96
  }
95
97
  `;
96
98
  const elementBeforeInput = css`
97
- margin: 1px 6px 0 8px;
99
+ margin: 1px ${"var(--ds-space-075, 6px)"} 0 ${"var(--ds-space-100, 8px)"};
98
100
  color: ${`var(--ds-icon, ${N200})`};
99
101
 
100
102
  // Custom SearchIcon style
@@ -105,7 +107,7 @@ const elementBeforeInput = css`
105
107
  }
106
108
  `;
107
109
  const elementAfterInput = css`
108
- margin: 0 8px;
110
+ margin: 0 ${"var(--ds-space-100, 8px)"};
109
111
  height: ${SEARCH_ITEM_HEIGHT_WIDTH};
110
112
  text-align: center;
111
113
  `;