@atlaskit/editor-plugin-text-formatting
Version:
Text-formatting plugin for @atlaskit/editor-core
202 lines (201 loc) • 7.74 kB
JavaScript
import { INPUT_METHOD, ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
import { liftTarget } from '@atlaskit/editor-prosemirror/transform';
import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
import { cellSelectionNodesBetween } from './utils/cell-selection';
export const FORMATTING_NODE_TYPES = ['heading', 'blockquote'];
export const FORMATTING_MARK_TYPES = ['em', 'code', 'strike', 'strong', 'underline', 'textColor', 'subsup', 'backgroundColor'];
const formatTypes = {
em: ACTION_SUBJECT_ID.FORMAT_ITALIC,
code: ACTION_SUBJECT_ID.FORMAT_CODE,
strike: ACTION_SUBJECT_ID.FORMAT_STRIKE,
strong: ACTION_SUBJECT_ID.FORMAT_STRONG,
underline: ACTION_SUBJECT_ID.FORMAT_UNDERLINE,
textColor: ACTION_SUBJECT_ID.FORMAT_COLOR,
subsup: 'subsup',
backgroundColor: ACTION_SUBJECT_ID.FORMAT_BACKGROUND_COLOR
};
// eslint-disable-next-line @repo/internal/deprecations/deprecation-ticket-required
/**
* Consider removing this function when cleaning up platform_editor–toolbar_aifc
* @deprecated use `clearFormattingWithAnalyticsNext` instead, which returns EditorCommand
*/
export function clearFormattingWithAnalytics(inputMethod, editorAnalyticsAPI) {
return clearFormatting(inputMethod, editorAnalyticsAPI);
}
const clearNodeFormattingOnSelectionNext = (schema, tr, formattedNodeType, nodeName, formattingCleared) => {
return function (node, pos) {
if (node.type === formattedNodeType) {
if (formattedNodeType.isTextblock) {
tr.setNodeMarkup(pos, schema.nodes.paragraph);
formattingCleared.push(nodeName);
return false;
} else {
// In case of panel or blockquote
const fromPos = tr.doc.resolve(pos + 1);
const toPos = tr.doc.resolve(pos + node.nodeSize - 1);
const nodeRange = fromPos.blockRange(toPos);
if (nodeRange) {
const targetLiftDepth = liftTarget(nodeRange);
if (targetLiftDepth || targetLiftDepth === 0) {
formattingCleared.push(nodeName);
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
tr.lift(nodeRange, targetLiftDepth);
}
}
}
}
return true;
};
};
function clearNodeFormattingOnSelection(state, tr, formattedNodeType, nodeName, formattingCleared) {
return function (node, pos) {
if (node.type === formattedNodeType) {
if (formattedNodeType.isTextblock) {
tr.setNodeMarkup(pos, state.schema.nodes.paragraph);
formattingCleared.push(nodeName);
return false;
} else {
// In case of panel or blockquote
const fromPos = tr.doc.resolve(pos + 1);
const toPos = tr.doc.resolve(pos + node.nodeSize - 1);
const nodeRange = fromPos.blockRange(toPos);
if (nodeRange) {
const targetLiftDepth = liftTarget(nodeRange);
if (targetLiftDepth || targetLiftDepth === 0) {
formattingCleared.push(nodeName);
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
tr.lift(nodeRange, targetLiftDepth);
}
}
}
}
return true;
};
}
export function clearFormatting(inputMethod, editorAnalyticsAPI) {
return function (state, dispatch) {
const {
tr
} = state;
const formattingCleared = [];
FORMATTING_MARK_TYPES.forEach(mark => {
const {
from,
to
} = tr.selection;
const markType = state.schema.marks[mark];
if (!markType) {
return;
}
if (tr.selection instanceof CellSelection) {
cellSelectionNodesBetween(tr.selection, tr.doc, (node, pos) => {
const isTableCell = node.type === state.schema.nodes.tableCell || node.type === state.schema.nodes.tableHeader;
if (!isTableCell) {
return true;
}
if (tr.doc.rangeHasMark(pos, pos + node.nodeSize, markType)) {
formattingCleared.push(formatTypes[mark]);
tr.removeMark(pos, pos + node.nodeSize, markType);
}
return false;
});
} else if (tr.doc.rangeHasMark(from, to, markType)) {
formattingCleared.push(formatTypes[mark]);
tr.removeMark(from, to, markType);
}
});
FORMATTING_NODE_TYPES.forEach(nodeName => {
const formattedNodeType = state.schema.nodes[nodeName];
const {
$from,
$to
} = tr.selection;
if (tr.selection instanceof CellSelection) {
cellSelectionNodesBetween(tr.selection, tr.doc, clearNodeFormattingOnSelection(state, tr, formattedNodeType, nodeName, formattingCleared));
} else {
tr.doc.nodesBetween($from.pos, $to.pos, clearNodeFormattingOnSelection(state, tr, formattedNodeType, nodeName, formattingCleared));
}
});
tr.setStoredMarks([]);
if (formattingCleared.length && inputMethod) {
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent({
action: ACTION.FORMATTED,
eventType: EVENT_TYPE.TRACK,
actionSubject: ACTION_SUBJECT.TEXT,
actionSubjectId: ACTION_SUBJECT_ID.FORMAT_CLEAR,
attributes: {
inputMethod,
formattingCleared,
dropdownMenu: inputMethod === INPUT_METHOD.TOOLBAR || inputMethod === INPUT_METHOD.FLOATING_TB ? 'textFormatting' : undefined
}
})(tr);
}
if (dispatch) {
dispatch(tr);
}
return true;
};
}
export const clearFormattingWithAnalyticsNext = editorAnalyticsApi => inputMethod => ({
tr
}) => {
const formattingCleared = [];
const {
schema
} = tr.doc.type;
FORMATTING_MARK_TYPES.forEach(mark => {
const {
from,
to
} = tr.selection;
const markType = schema.marks[mark];
if (!markType) {
return;
}
if (tr.selection instanceof CellSelection) {
cellSelectionNodesBetween(tr.selection, tr.doc, (node, pos) => {
const isTableCell = node.type === tr.doc.type.schema.nodes.tableCell || node.type === schema.nodes.tableHeader;
if (!isTableCell) {
return true;
}
if (tr.doc.rangeHasMark(pos, pos + node.nodeSize, markType)) {
formattingCleared.push(formatTypes[mark]);
tr.removeMark(pos, pos + node.nodeSize, markType);
}
return false;
});
} else if (tr.doc.rangeHasMark(from, to, markType)) {
formattingCleared.push(formatTypes[mark]);
tr.removeMark(from, to, markType);
}
});
FORMATTING_NODE_TYPES.forEach(nodeName => {
const formattedNodeType = schema.nodes[nodeName];
const {
$from,
$to
} = tr.selection;
if (tr.selection instanceof CellSelection) {
cellSelectionNodesBetween(tr.selection, tr.doc, clearNodeFormattingOnSelectionNext(schema, tr, formattedNodeType, nodeName, formattingCleared));
} else {
tr.doc.nodesBetween($from.pos, $to.pos, clearNodeFormattingOnSelectionNext(schema, tr, formattedNodeType, nodeName, formattingCleared));
}
});
tr.setStoredMarks([]);
if (formattingCleared.length && inputMethod) {
editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent({
action: ACTION.FORMATTED,
eventType: EVENT_TYPE.TRACK,
actionSubject: ACTION_SUBJECT.TEXT,
actionSubjectId: ACTION_SUBJECT_ID.FORMAT_CLEAR,
attributes: {
inputMethod,
formattingCleared,
dropdownMenu: inputMethod === INPUT_METHOD.TOOLBAR || inputMethod === INPUT_METHOD.FLOATING_TB ? 'textFormatting' : undefined
}
})(tr);
}
return tr;
};