@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
201 lines (200 loc) • 6.7 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import React from 'react';
import classnames from 'classnames';
import { Resizable } from 're-resizable';
import { akRichMediaResizeZIndex } from '@atlaskit/editor-shared-styles';
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '../../analytics';
import { richMediaClassName } from '../../styles';
import { gridTypeForLayout } from '../../utils';
import { handleSides, snapTo } from './utils';
const getResizeAnalyticsEvent = (type, size, layout) => {
const actionSubject = type === 'embed' ? ACTION_SUBJECT.EMBEDS : ACTION_SUBJECT.MEDIA_SINGLE;
return {
action: ACTION.EDITED,
actionSubject,
actionSubjectId: ACTION_SUBJECT_ID.RESIZED,
attributes: {
size,
layout
},
eventType: EVENT_TYPE.UI
};
};
const getWidthFromSnapPoints = (width, snapPoints) => {
if (snapPoints.length) {
return Math.min(Math.max(width, snapPoints[0]), snapPoints[snapPoints.length - 1]);
}
return width;
};
export default class Resizer extends React.Component {
constructor(...args) {
super(...args);
_defineProperty(this, "resizable", /*#__PURE__*/React.createRef());
_defineProperty(this, "state", {
isResizing: false
});
_defineProperty(this, "handleResizeStart", event => {
const {
innerPadding = 0,
highlights,
displayGrid,
layout,
width,
snapPoints
} = this.props;
// prevent creating a drag event on Firefox
event.preventDefault();
this.setState({
isResizing: true
}, () => {
const newHighlights = highlights(width + innerPadding, snapPoints);
displayGrid === null || displayGrid === void 0 ? void 0 : displayGrid(newHighlights.length > 0, gridTypeForLayout(layout), newHighlights);
});
});
_defineProperty(this, "handleResize", (_event, _direction, _elementRef, delta) => {
const {
highlights,
calcNewSize,
scaleFactor,
snapPoints,
displayGrid,
layout,
updateSize,
innerPadding = 0
} = this.props;
const resizable = this.resizable.current;
const {
isResizing
} = this.state;
if (!resizable || !resizable.state.original || !isResizing) {
return;
}
const newWidth = getWidthFromSnapPoints(resizable.state.original.width + delta.width * (scaleFactor || 1), snapPoints);
const newSize = calcNewSize(newWidth + innerPadding, false);
if (newSize.layout !== layout) {
updateSize(newSize.width, newSize.layout);
}
const newHighlights = highlights(newWidth, snapPoints);
displayGrid === null || displayGrid === void 0 ? void 0 : displayGrid(newHighlights.length > 0, gridTypeForLayout(newSize.layout), newHighlights);
resizable.updateSize({
width: newWidth,
height: 'auto'
});
resizable.setState({
isResizing: true
});
});
_defineProperty(this, "handleResizeStop", (_event, _direction, _elementRef, delta) => {
const {
highlights,
calcNewSize,
snapPoints,
displayGrid,
layout,
updateSize,
dispatchAnalyticsEvent,
nodeType
} = this.props;
const resizable = this.resizable.current;
const {
isResizing
} = this.state;
if (!resizable || !resizable.state.original || !isResizing) {
return;
}
const newWidth = getWidthFromSnapPoints(resizable.state.original.width + delta.width, snapPoints);
const snapWidth = snapTo(newWidth, snapPoints);
const newSize = calcNewSize(snapWidth, true);
const newHighlights = highlights(newWidth, snapPoints);
if (dispatchAnalyticsEvent) {
dispatchAnalyticsEvent(getResizeAnalyticsEvent(nodeType, newSize.width, newSize.layout));
}
// show committed grid size
displayGrid === null || displayGrid === void 0 ? void 0 : displayGrid(newHighlights.length > 0, gridTypeForLayout(newSize.layout), newHighlights);
this.setState({
isResizing: false
}, () => {
updateSize(newSize.width, newSize.layout);
displayGrid === null || displayGrid === void 0 ? void 0 : displayGrid(false, gridTypeForLayout(layout), []);
});
});
}
render() {
const baseHandleStyles = {};
const handles = {};
const handleComponent = {};
const {
innerPadding = 0,
width,
pctWidth,
selected,
layout,
enable,
children,
ratio,
handleComponentFunc,
handleStyles
} = this.props;
const {
isResizing
} = this.state;
handleSides.forEach(side => {
handles[side] = `richMedia-resize-handle-${side}`;
baseHandleStyles[side] = {
width: '24px',
[side]: `${-13 - innerPadding}px`,
zIndex: akRichMediaResizeZIndex,
pointerEvents: 'auto'
};
const sideHandleComponent = handleComponentFunc && handleComponentFunc(side);
if (sideHandleComponent) {
handleComponent[side] = sideHandleComponent;
}
});
const updatedHandleStyles = {
left: {
...baseHandleStyles.left,
...(handleStyles === null || handleStyles === void 0 ? void 0 : handleStyles.left)
},
right: {
...baseHandleStyles.right,
...(handleStyles === null || handleStyles === void 0 ? void 0 : handleStyles.right)
}
};
const className = classnames(richMediaClassName, `image-${layout}`, this.props.className, {
'is-resizing': isResizing,
'not-resized': !pctWidth,
'richMedia-selected': selected,
'rich-media-wrapped': layout === 'wrap-left' || layout === 'wrap-right'
});
let handleWrapperStyle;
if (ratio) {
handleWrapperStyle = {
position: 'absolute',
width: '100%',
paddingBottom: `${ratio}%`,
top: 0,
pointerEvents: 'none'
};
}
// Ideally, Resizable would let you pass in the component rather than
// the div. For now, we just apply the same styles using CSS
return /*#__PURE__*/React.createElement(Resizable, {
ref: this.resizable,
size: {
width,
// just content itself (no paddings)
height: 'auto'
},
className: className,
handleClasses: handles,
handleStyles: updatedHandleStyles,
handleWrapperStyle: handleWrapperStyle,
handleComponent: handleComponent,
enable: enable,
onResize: this.handleResize,
onResizeStop: this.handleResizeStop,
onResizeStart: this.handleResizeStart
}, children);
}
}