@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
215 lines (214 loc) • 8.46 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import React, { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';
import classnames from 'classnames';
import { Resizable } from 're-resizable';
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
import Tooltip from '@atlaskit/tooltip';
import { handleWrapperClass, resizerDangerClassName, resizerExtendedZone, resizerHandleClassName, resizerHandleThumbClassName, resizerHandleTrackClassName, resizerHandleZIndex, resizerHoverZoneClassName, resizerItemClassName } from '../styles/shared/resizer';
const SUPPORTED_HANDLES = ['left', 'right'];
const inheritedCSS = {
position: 'inherit',
height: 'inherit',
width: 'inherit',
display: 'inherit',
flexDirection: 'inherit',
justifyContent: 'inherit',
alignItems: 'inherit'
};
const ResizerNext = (props, ref) => {
const [isResizing, setIsResizing] = useState(false);
const resizable = useRef(null);
const resizeHandleThumbRef = useRef(null);
useImperativeHandle(ref, () => {
return {
getResizerThumbEl() {
return resizeHandleThumbRef.current;
}
};
}, [resizeHandleThumbRef]);
const {
width,
children,
handleClassName,
className,
handleResize,
handleResizeStart,
handleResizeStop,
handleSize = 'medium',
handleAlignmentMethod = 'center',
handlePositioning = 'overlap',
appearance,
handleStyles,
resizeRatio = 1,
snap,
snapGap,
isHandleVisible = false,
handleHighlight = 'none',
handleTooltipContent,
needExtendedResizeZone = true,
...otherProps
} = props;
const onResizeStart = useCallback(event => {
// prevent creating a drag event on Firefox
event.preventDefault();
setIsResizing(true);
handleResizeStart();
}, [handleResizeStart]);
const onResize = useCallback((_event, _direction, _elementRef, delta) => {
const resizableCurrent = resizable.current;
if (!resizableCurrent || !resizableCurrent.state.original) {
return;
}
const originalState = {
x: resizableCurrent.state.original.x,
y: resizableCurrent.state.original.y,
width: resizableCurrent.state.original.width,
height: resizableCurrent.state.original.height
};
handleResize(originalState, delta);
}, [handleResize]);
const onResizeStop = useCallback((_event, _direction, _elementRef, delta) => {
const resizableCurrent = resizable.current;
if (!resizableCurrent || !resizableCurrent.state.original) {
return;
}
const originalState = {
x: resizableCurrent.state.original.x,
y: resizableCurrent.state.original.y,
width: resizableCurrent.state.original.width,
height: resizableCurrent.state.original.height
};
setIsResizing(false);
handleResizeStop(originalState, delta);
}, [handleResizeStop]);
const handles = {
left: classnames(handleClassName !== null && handleClassName !== void 0 ? handleClassName : resizerHandleClassName, 'left', handleSize, handleAlignmentMethod),
right: classnames(handleClassName !== null && handleClassName !== void 0 ? handleClassName : resizerHandleClassName, 'right', handleSize, handleAlignmentMethod)
};
const baseHandleStyles = {
width: handlePositioning === 'adjacent' ? "var(--ds-space-100, 8px)" : "var(--ds-space-300, 24px)",
zIndex: resizerHandleZIndex,
pointerEvents: 'auto',
alignItems: handlePositioning === 'adjacent' ? 'center' : undefined
};
const offset = handlePositioning === 'adjacent' ? `calc(${baseHandleStyles.width} * -1)` : `calc(${baseHandleStyles.width} * -0.5)`;
const nextHandleStyles = SUPPORTED_HANDLES.reduce((result, position) => ({
...result,
[position]: {
...baseHandleStyles,
[position]: offset,
...(handleStyles === null || handleStyles === void 0 ? void 0 : handleStyles[position])
}
}), {});
const resizerClassName = classnames(className, resizerItemClassName, {
'is-resizing': isResizing,
'display-handle': isHandleVisible,
[resizerDangerClassName]: appearance === 'danger'
});
const resizerZoneClassName = classnames(resizerHoverZoneClassName, {
[resizerExtendedZone]: needExtendedResizeZone
});
const handleComponent = useMemo(() => {
return SUPPORTED_HANDLES.reduce((result, position) => {
const thumb = /*#__PURE__*/React.createElement("button", {
className: resizerHandleThumbClassName,
"data-testid": `resizer-handle-${position}-thumb`,
contentEditable: false,
ref: resizeHandleThumbRef,
type: "button",
tabIndex: -1 //We want to control focus on this button ourselves
});
if ((!handleHighlight || handleHighlight === 'none') && !handleTooltipContent) {
return {
...result,
[position]: thumb
};
}
if (getBooleanFF('platform.editor.resizer.prevent-contenteditable')) {
const thumbWithTrack =
/*#__PURE__*/
//It's important to have {thumb} element before the div, the thumb element is the one that gets focus and only the 1st element recives aria-descibedby attribute which is important for screen reader users
React.createElement(React.Fragment, null, thumb, /*#__PURE__*/React.createElement("div", {
className: classnames(resizerHandleTrackClassName, handleHighlight),
"data-testid": `resizer-handle-${position}-track`
}));
if (!!handleTooltipContent) {
return {
...result,
[position]: /*#__PURE__*/React.createElement("div", {
contentEditable: false,
style: inheritedCSS
}, /*#__PURE__*/React.createElement(Tooltip, {
content: handleTooltipContent,
hideTooltipOnClick: true,
position: "mouse",
mousePosition: "auto-start",
testId: `resizer-handle-${position}-tooltip`
}, thumbWithTrack))
};
}
return {
...result,
[position]: /*#__PURE__*/React.createElement("div", {
contentEditable: false,
style: inheritedCSS
}, thumbWithTrack)
};
} else {
const thumbWithTrack =
/*#__PURE__*/
//It's important to have {thumb} element before the div, the thumb element is the one that gets focus and only the 1st element recives aria-descibedby attribute which is important for screen reader users
React.createElement(React.Fragment, null, thumb, /*#__PURE__*/React.createElement("div", {
className: classnames(resizerHandleTrackClassName, handleHighlight),
"data-testid": `resizer-handle-${position}-track`
}));
if (!!handleTooltipContent) {
return {
...result,
[position]: /*#__PURE__*/React.createElement(Tooltip, {
content: handleTooltipContent,
hideTooltipOnClick: true,
position: "mouse",
mousePosition: "auto-start",
testId: `resizer-handle-${position}-tooltip`
}, thumbWithTrack)
};
}
return {
...result,
[position]: thumbWithTrack
};
}
}, {});
}, [handleHighlight, handleTooltipContent]);
// snapGap is usually a constant, if snap.x?.length is 0 and snapGap has a value resizer cannot be resized
const snapGapActual = useMemo(() => {
var _snap$x, _snap$y;
if (!snap || ((_snap$x = snap.x) === null || _snap$x === void 0 ? void 0 : _snap$x.length) === 0 && ((_snap$y = snap.y) === null || _snap$y === void 0 ? void 0 : _snap$y.length) === 0) {
return undefined;
}
return snapGap;
}, [snap, snapGap]);
return /*#__PURE__*/React.createElement(Resizable, _extends({
ref: resizable,
size: {
width,
// just content itself (no paddings)
height: 'auto'
},
className: resizerClassName,
handleClasses: handles,
handleWrapperClass: handleWrapperClass,
handleStyles: nextHandleStyles,
onResizeStart: onResizeStart,
onResize: onResize,
onResizeStop: onResizeStop,
resizeRatio: resizeRatio,
snapGap: snapGapActual,
snap: snap,
handleComponent: handleComponent
}, otherProps), /*#__PURE__*/React.createElement("span", {
className: resizerZoneClassName
}, children));
};
export default /*#__PURE__*/forwardRef(ResizerNext);