UNPKG

@atlaskit/editor-plugin-grid

Version:

Grid plugin for @atlaskit/editor-core

220 lines (218 loc) 7.6 kB
import React from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766 import { withTheme } from '@emotion/react'; import classnames from 'classnames'; import { isSSR } from '@atlaskit/editor-common/core-utils'; import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import { SafePlugin } from '@atlaskit/editor-common/safe-plugin'; import { PluginKey } from '@atlaskit/editor-prosemirror/state'; import { akEditorBreakoutPadding, akEditorFullPageMaxWidth, breakoutWideScaleRatio } from '@atlaskit/editor-shared-styles'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; export const GRID_SIZE = 12; const key = new PluginKey('gridPlugin'); const createDisplayGrid = view => props => { const { dispatch, state } = view; const tr = state.tr.setMeta(key, props); dispatch(tr); return true; }; const sides = ['left', 'right']; const overflowHighlight = (highlights, side, start, size) => { if (!highlights.length) { return false; } const numericHighlights = highlights.filter(highlight => typeof highlight === 'number'); const minHighlight = Math.min(...numericHighlights); const maxHighlight = Math.max(...numericHighlights); if (side === 'left') { return minHighlight < 0 && minHighlight <= -start && (typeof size === 'number' ? minHighlight >= -(start + size) : true); } else { return maxHighlight > GRID_SIZE && maxHighlight >= GRID_SIZE + start && (typeof size === 'number' ? maxHighlight <= GRID_SIZE + size : true); } }; const gutterGridLines = (editorMaxWidth, editorWidth, highlights, shouldCalcBreakoutGridLines) => { const gridLines = []; if (!shouldCalcBreakoutGridLines) { return gridLines; } const wideSpacing = (editorMaxWidth * breakoutWideScaleRatio - editorMaxWidth) / 2; sides.forEach(side => { gridLines.push( /*#__PURE__*/React.createElement("div", { key: side // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: classnames('gridLine', overflowHighlight(highlights, side, 0, 4) ? 'highlight' : '') // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 , style: { position: 'absolute', [side]: `-${wideSpacing}px` } })); gridLines.push( /*#__PURE__*/React.createElement("div", { key: side + '-bk' // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: classnames('gridLine', highlights.indexOf('full-width') > -1 ? 'highlight' : ''), style: { // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 position: 'absolute', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 [side]: `-${(editorWidth - editorMaxWidth - akEditorBreakoutPadding) / 2}px` } })); }); return gridLines; }; const lineLengthGridLines = highlights => { const gridLines = []; const gridSpacing = 100 / GRID_SIZE; for (let i = 0; i <= GRID_SIZE; i++) { const style = { paddingLeft: `${gridSpacing}%` }; gridLines.push( /*#__PURE__*/React.createElement("div", { key: i // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: classnames('gridLine', highlights.indexOf(i) > -1 ? 'highlight' : '') // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 , style: i < GRID_SIZE ? style : undefined })); } return gridLines; }; const Grid = ({ highlight, shouldCalcBreakoutGridLines, theme, containerElement, editorWidth, gridType, visible }) => { const editorMaxWidth = theme.layoutMaxWidth; const gridLines = [...lineLengthGridLines(highlight), ...gutterGridLines(editorMaxWidth, editorWidth, highlight, shouldCalcBreakoutGridLines)]; return ( /*#__PURE__*/ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 React.createElement("div", { className: "gridParent" }, /*#__PURE__*/React.createElement("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: classnames('gridContainer', gridType), style: { height: `${containerElement.scrollHeight}px`, display: visible ? 'block' : 'none' }, "data-testid": "gridContainer" }, gridLines)) ); }; const ThemedGrid = withTheme(Grid); const selector = states => { var _states$widthState, _states$gridState, _states$gridState2, _states$gridState3; return { width: (_states$widthState = states.widthState) === null || _states$widthState === void 0 ? void 0 : _states$widthState.width, visible: (_states$gridState = states.gridState) === null || _states$gridState === void 0 ? void 0 : _states$gridState.visible, gridType: (_states$gridState2 = states.gridState) === null || _states$gridState2 === void 0 ? void 0 : _states$gridState2.gridType, highlight: (_states$gridState3 = states.gridState) === null || _states$gridState3 === void 0 ? void 0 : _states$gridState3.highlight }; }; const ContentComponent = ({ api, editorView, options }) => { const { width, visible, gridType, highlight } = useSharedPluginStateWithSelector(api, ['width', 'grid'], selector); if (visible === undefined || !highlight) { return null; } if (expValEquals('platform_editor_remove_grid_init_reflow', 'isEnabled', true)) { if (!visible || !highlight) { return null; } } else { if (visible === undefined || !highlight) { return null; } } return /*#__PURE__*/React.createElement(ThemedGrid, { shouldCalcBreakoutGridLines: options && options.shouldCalcBreakoutGridLines, editorWidth: width !== null && width !== void 0 ? width : akEditorFullPageMaxWidth // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting , containerElement: editorView.dom, visible: visible, gridType: gridType !== null && gridType !== void 0 ? gridType : 'full', highlight: highlight }); }; const EMPTY_STATE = { visible: false, highlight: [] }; const gridPMPlugin = new SafePlugin({ key, state: { init() { return EMPTY_STATE; }, apply(tr, currentPluginState) { const nextPluginState = tr.getMeta(key); if (nextPluginState) { return nextPluginState; } return currentPluginState; } } }); /** * Grid plugin to be added to an `EditorPresetBuilder` and used with `ComposableEditor` * from `@atlaskit/editor-core`. */ export const gridPlugin = ({ config: options, api }) => { return { name: 'grid', getSharedState(editorState) { if (!editorState) { return null; } return key.getState(editorState) || null; }, actions: { displayGrid: createDisplayGrid }, pmPlugins() { return [{ name: 'grid', plugin: () => gridPMPlugin }]; }, contentComponent: ({ editorView }) => { if (!editorView || isSSR()) { return null; } return /*#__PURE__*/React.createElement(ContentComponent, { editorView: editorView, options: options, api: api }); } }; };