UNPKG

@atlaskit/editor-plugin-breakout

Version:

Breakout plugin for @atlaskit/editor-core

199 lines (197 loc) 9.29 kB
import { akEditorGutterPaddingDynamic, akEditorGutterPadding, akEditorGutterPaddingReduced, akEditorFullPageNarrowBreakout, akEditorDefaultLayoutWidth, akEditorFullWidthLayoutWidth, akEditorCalculatedWideLayoutWidth, akEditorMaxLayoutWidth } from '@atlaskit/editor-shared-styles'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { setBreakoutWidth } from '../editor-commands/set-breakout-width'; import { getGuidelines } from './get-guidelines'; import { LOCAL_RESIZE_PROPERTY } from './resizing-mark-view'; import { resizingPluginKey } from './resizing-plugin'; import { generateResizeFrameRatePayloads, generateResizedEventPayload } from './utils/analytics'; import { measureFramerate, reduceResizeFrameRateSamples } from './utils/measure-framerate'; const RESIZE_RATIO = 2; const SNAP_GAP = 10; const WIDTHS = { MIN: akEditorDefaultLayoutWidth, WIDE: akEditorCalculatedWideLayoutWidth, FULL: akEditorFullWidthLayoutWidth, MAX: akEditorMaxLayoutWidth }; export function getProposedWidth({ initialWidth, location, api, source }) { var _api$width$sharedStat, _api$width$sharedStat2; const directionMultiplier = source.data.handleSide === 'left' ? -1 : 1; const diffX = (location.current.input.clientX - location.initial.input.clientX) * RESIZE_RATIO * directionMultiplier; const width = (api === null || api === void 0 ? void 0 : (_api$width$sharedStat = api.width.sharedState) === null || _api$width$sharedStat === void 0 ? void 0 : (_api$width$sharedStat2 = _api$width$sharedStat.currentState()) === null || _api$width$sharedStat2 === void 0 ? void 0 : _api$width$sharedStat2.width) || 0; const padding = width <= akEditorFullPageNarrowBreakout && editorExperiment('platform_editor_preview_panel_responsiveness', true, { exposure: true }) ? akEditorGutterPaddingReduced : akEditorGutterPaddingDynamic(); const containerWidth = width - 2 * padding - akEditorGutterPadding; // the node width may be greater than the container width so we resize using the smaller value const proposedWidth = Math.min(initialWidth, containerWidth) + diffX; const snapPoints = [WIDTHS.MIN, WIDTHS.WIDE, Math.min(containerWidth, WIDTHS.FULL)]; if (expValEquals('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEquals('confluence_max_width_content_appearance', 'isEnabled', true)) { snapPoints.push(Math.min(containerWidth, WIDTHS.MAX)); } for (const snapPoint of snapPoints) { if (snapPoint - SNAP_GAP < proposedWidth && snapPoint + SNAP_GAP > proposedWidth) { return snapPoint; } } const hardMax = expValEquals('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEquals('confluence_max_width_content_appearance', 'isEnabled', true) ? Math.min(containerWidth, WIDTHS.MAX) : Math.min(containerWidth, WIDTHS.FULL); return Math.max(WIDTHS.MIN, Math.min(proposedWidth, hardMax)); } export function createResizerCallbacks({ dom, view, mark, api }) { let node = null; let guidelines = []; const { startMeasure, endMeasure, countFrames } = measureFramerate(); const getEditorWidth = () => { var _api$width; return api === null || api === void 0 ? void 0 : (_api$width = api.width) === null || _api$width === void 0 ? void 0 : _api$width.sharedState.currentState(); }; return { onDragStart: () => { startMeasure(); const pos = view.posAtDOM(dom, 0); node = view.state.doc.nodeAt(pos); api === null || api === void 0 ? void 0 : api.core.actions.execute(({ tr }) => { var _api$userIntent; (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('dragging')({ tr }); tr.setMeta('is-resizer-resizing', true); tr.setMeta(resizingPluginKey, { type: 'UPDATE_BREAKOUT_NODE', data: { node, pos, start: pos, depth: 0 } }); return tr; }); }, onDrag: ({ location, source }) => { var _node, _api$guideline, _api$guideline$action, _api$breakout$sharedS; countFrames(); const initialWidth = mark.attrs.width; const newWidth = getProposedWidth({ initialWidth, location, api, source }); guidelines = getGuidelines(true, newWidth, getEditorWidth, (_node = node) === null || _node === void 0 ? void 0 : _node.type); api === null || api === void 0 ? void 0 : (_api$guideline = api.guideline) === null || _api$guideline === void 0 ? void 0 : (_api$guideline$action = _api$guideline.actions) === null || _api$guideline$action === void 0 ? void 0 : _api$guideline$action.displayGuideline(view)({ guidelines }); const activeGuideline = guidelines.find(guideline => guideline.active && !guideline.key.startsWith('grid')); if (activeGuideline) { api === null || api === void 0 ? void 0 : api.core.actions.execute(({ tr }) => { tr.setMeta(resizingPluginKey, { type: 'UPDATE_ACTIVE_GUIDELINE_KEY', data: { activeGuidelineKey: activeGuideline.key } }); return tr; }); } if (!activeGuideline && api !== null && api !== void 0 && (_api$breakout$sharedS = api.breakout.sharedState.currentState()) !== null && _api$breakout$sharedS !== void 0 && _api$breakout$sharedS.activeGuidelineKey) { api === null || api === void 0 ? void 0 : api.core.actions.execute(({ tr }) => { tr.setMeta(resizingPluginKey, { type: 'CLEAR_ACTIVE_GUIDELINE_KEY' }); return tr; }); } // dom is used for width calculations dom.style.setProperty(LOCAL_RESIZE_PROPERTY, `${newWidth}px`); }, onDrop({ location, source }) { var _api$guideline2, _api$guideline2$actio, _api$editorViewMode, _api$editorViewMode$s; let payloads = []; const frameRateSamples = endMeasure(); payloads = generateResizeFrameRatePayloads({ docSize: view.state.doc.nodeSize, frameRateSamples: reduceResizeFrameRateSamples(frameRateSamples), originalNode: node }); const isResizedToFullWidth = !!guidelines.find(guideline => guideline.key.startsWith('full_width') && guideline.active); let isResizedToMaxWidth = false; if (expValEquals('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEquals('confluence_max_width_content_appearance', 'isEnabled', true)) { isResizedToMaxWidth = !!guidelines.find(guideline => guideline.key.startsWith('max_width') && guideline.active); } guidelines = getGuidelines(false, 0, getEditorWidth); api === null || api === void 0 ? void 0 : (_api$guideline2 = api.guideline) === null || _api$guideline2 === void 0 ? void 0 : (_api$guideline2$actio = _api$guideline2.actions) === null || _api$guideline2$actio === void 0 ? void 0 : _api$guideline2$actio.displayGuideline(view)({ guidelines }); const pos = view.posAtDOM(dom, 0); const mode = mark.attrs.mode; const initialWidth = mark.attrs.width; let newWidth = isResizedToFullWidth ? WIDTHS.FULL : getProposedWidth({ initialWidth, location, api, source }); if (expValEquals('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEquals('confluence_max_width_content_appearance', 'isEnabled', true)) { if (isResizedToMaxWidth) { newWidth = WIDTHS.MAX; } } const isEditMode = (api === null || api === void 0 ? void 0 : (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : (_api$editorViewMode$s = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode$s === void 0 ? void 0 : _api$editorViewMode$s.mode) === 'edit'; setBreakoutWidth(newWidth, mode, pos, isEditMode)(view.state, view.dispatch); dom.style.removeProperty(LOCAL_RESIZE_PROPERTY); if (node) { const resizedPayload = generateResizedEventPayload({ node, prevWidth: initialWidth, newWidth }); payloads.push(resizedPayload); } api === null || api === void 0 ? void 0 : api.core.actions.execute(({ tr }) => { var _api$userIntent2; (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.commands.setCurrentUserIntent('default')({ tr }); payloads.forEach(payload => { var _api$analytics, _api$analytics$action; (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.attachAnalyticsEvent(payload)(tr); }); tr.setMeta('is-resizer-resizing', false).setMeta('scrollIntoView', false); tr.setMeta(resizingPluginKey, { type: 'RESET_STATE' }); return tr; }); } }; }