@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.
- package/CHANGELOG.md +16 -0
- package/dist/cjs/plugins/floating-toolbar/index.js +1 -3
- package/dist/cjs/plugins/media/nodeviews/mediaSingle.js +33 -23
- package/dist/cjs/plugins/media/toolbar/index.js +16 -2
- package/dist/cjs/plugins/media/toolbar/utils.js +7 -2
- package/dist/cjs/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.js +574 -0
- package/dist/cjs/plugins/media/ui/ResizableMediaSingle/guidelines.js +117 -0
- package/dist/cjs/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.js +15 -0
- package/dist/cjs/ui/ElementBrowser/components/ElementSearch.js +5 -3
- package/dist/cjs/ui/ElementBrowser/components/StatelessElementBrowser.js +8 -6
- package/dist/cjs/ui/Resizer/ResizeLabel/index.js +20 -0
- package/dist/cjs/ui/Resizer/ResizeLabel/styles.js +17 -0
- package/dist/cjs/version-wrapper.js +1 -1
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/plugins/floating-toolbar/index.js +1 -1
- package/dist/es2019/plugins/media/nodeviews/mediaSingle.js +34 -23
- package/dist/es2019/plugins/media/toolbar/index.js +19 -3
- package/dist/es2019/plugins/media/toolbar/utils.js +7 -1
- package/dist/es2019/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.js +493 -0
- package/dist/es2019/plugins/media/ui/ResizableMediaSingle/guidelines.js +108 -0
- package/dist/es2019/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.js +8 -0
- package/dist/es2019/ui/ElementBrowser/components/ElementSearch.js +5 -3
- package/dist/es2019/ui/ElementBrowser/components/StatelessElementBrowser.js +9 -6
- package/dist/es2019/ui/Resizer/ResizeLabel/index.js +12 -0
- package/dist/es2019/ui/Resizer/ResizeLabel/styles.js +25 -0
- package/dist/es2019/version-wrapper.js +1 -1
- package/dist/es2019/version.json +1 -1
- package/dist/esm/plugins/floating-toolbar/index.js +1 -3
- package/dist/esm/plugins/media/nodeviews/mediaSingle.js +33 -23
- package/dist/esm/plugins/media/toolbar/index.js +17 -3
- package/dist/esm/plugins/media/toolbar/utils.js +5 -1
- package/dist/esm/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.js +564 -0
- package/dist/esm/plugins/media/ui/ResizableMediaSingle/guidelines.js +108 -0
- package/dist/esm/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.js +8 -0
- package/dist/esm/ui/ElementBrowser/components/ElementSearch.js +5 -3
- package/dist/esm/ui/ElementBrowser/components/StatelessElementBrowser.js +8 -6
- package/dist/esm/ui/Resizer/ResizeLabel/index.js +12 -0
- package/dist/esm/ui/Resizer/ResizeLabel/styles.js +7 -0
- package/dist/esm/version-wrapper.js +1 -1
- package/dist/esm/version.json +1 -1
- package/dist/types/labs/next/presets/universal.d.ts +1 -2
- package/dist/types/plugins/floating-toolbar/index.d.ts +3 -1
- package/dist/types/plugins/media/index.d.ts +2 -0
- package/dist/types/plugins/media/nodeviews/styles.d.ts +1 -1
- package/dist/types/plugins/media/pm-plugins/main.d.ts +1 -1
- package/dist/types/plugins/media/toolbar/utils.d.ts +1 -0
- package/dist/types/plugins/media/types.d.ts +1 -0
- package/dist/types/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.d.ts +61 -0
- package/dist/types/plugins/media/ui/ResizableMediaSingle/guidelines.d.ts +4 -0
- package/dist/types/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.d.ts +7 -0
- package/dist/types/plugins/media/ui/ResizableMediaSingle/types.d.ts +1 -0
- package/dist/types/ui/ElementBrowser/components/ElementSearch.d.ts +2 -1
- package/dist/types/ui/ElementBrowser/components/StatelessElementBrowser.d.ts +4 -4
- package/dist/types/ui/Resizer/ResizeLabel/index.d.ts +8 -0
- package/dist/types/ui/Resizer/ResizeLabel/styles.d.ts +3 -0
- package/dist/types-ts4.5/labs/next/presets/universal.d.ts +1 -2
- package/dist/types-ts4.5/plugins/floating-toolbar/index.d.ts +3 -1
- package/dist/types-ts4.5/plugins/media/index.d.ts +2 -0
- package/dist/types-ts4.5/plugins/media/nodeviews/styles.d.ts +1 -1
- package/dist/types-ts4.5/plugins/media/pm-plugins/main.d.ts +1 -1
- package/dist/types-ts4.5/plugins/media/toolbar/utils.d.ts +1 -0
- package/dist/types-ts4.5/plugins/media/types.d.ts +1 -0
- package/dist/types-ts4.5/plugins/media/ui/ResizableMediaSingle/ResizableMediaSingleNext.d.ts +61 -0
- package/dist/types-ts4.5/plugins/media/ui/ResizableMediaSingle/guidelines.d.ts +4 -0
- package/dist/types-ts4.5/plugins/media/ui/ResizableMediaSingle/resizable-media-single-messages.d.ts +7 -0
- package/dist/types-ts4.5/plugins/media/ui/ResizableMediaSingle/types.d.ts +1 -0
- package/dist/types-ts4.5/ui/ElementBrowser/components/ElementSearch.d.ts +2 -1
- package/dist/types-ts4.5/ui/ElementBrowser/components/StatelessElementBrowser.d.ts +4 -4
- package/dist/types-ts4.5/ui/Resizer/ResizeLabel/index.d.ts +8 -0
- package/dist/types-ts4.5/ui/Resizer/ResizeLabel/styles.d.ts +3 -0
- package/package.json +2 -2
- package/report.api.md +7 -4
- 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
|
+
};
|
|
@@ -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
|
`;
|