UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

215 lines (214 loc) • 8.46 kB
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);