@atlaskit/editor-plugin-grid
Version:
Grid plugin for @atlaskit/editor-core
220 lines (218 loc) • 7.6 kB
JavaScript
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
});
}
};
};