UNPKG

@atlaskit/editor-plugin-text-formatting

Version:

Text-formatting plugin for @atlaskit/editor-core

144 lines (138 loc) 5.32 kB
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics'; import { withAnalytics } from '@atlaskit/editor-common/editor-analytics'; import { applyMarkOnRange } from '@atlaskit/editor-common/mark'; import { Selection } from '@atlaskit/editor-prosemirror/state'; import { hasCode, markActive } from './utils'; export const moveRight = () => { return (state, dispatch) => { const { code } = state.schema.marks; const { empty, $cursor } = state.selection; if (!empty || !$cursor) { return false; } const { storedMarks } = state.tr; if (code) { const insideCode = markActive(state, code.create()); const currentPosHasCode = state.doc.rangeHasMark($cursor.pos, $cursor.pos, code); const nextPosHasCode = state.doc.rangeHasMark($cursor.pos, $cursor.pos + 1, code); const exitingCode = !currentPosHasCode && !nextPosHasCode && (!storedMarks || !!storedMarks.length); const enteringCode = !currentPosHasCode && nextPosHasCode && (!storedMarks || !storedMarks.length); // entering code mark (from the left edge): don't move the cursor, just add the mark if (!insideCode && enteringCode) { if (dispatch) { dispatch(state.tr.addStoredMark(code.create())); } return true; } // exiting code mark: don't move the cursor, just remove the mark if (insideCode && exitingCode) { if (dispatch) { dispatch(state.tr.removeStoredMark(code)); } return true; } } return false; }; }; export const moveLeft = () => { return (state, dispatch) => { const { code } = state.schema.marks; const { empty, $cursor } = state.selection; if (!empty || !$cursor) { return false; } const { storedMarks } = state.tr; if (code) { const insideCode = code && markActive(state, code.create()); const currentPosHasCode = hasCode(state, $cursor.pos); const nextPosHasCode = hasCode(state, $cursor.pos - 1); const nextNextPosHasCode = hasCode(state, $cursor.pos - 2); const exitingCode = currentPosHasCode && !nextPosHasCode && Array.isArray(storedMarks); const atLeftEdge = nextPosHasCode && !nextNextPosHasCode && (storedMarks === null || Array.isArray(storedMarks) && !!storedMarks.length); const atRightEdge = (exitingCode && Array.isArray(storedMarks) && !storedMarks.length || !exitingCode && storedMarks === null) && !nextPosHasCode && nextNextPosHasCode; const enteringCode = !currentPosHasCode && nextPosHasCode && Array.isArray(storedMarks) && !storedMarks.length; // at the right edge: remove code mark and move the cursor to the left if (!insideCode && atRightEdge) { const tr = state.tr.setSelection(Selection.near(state.doc.resolve($cursor.pos - 1))); if (dispatch) { dispatch(tr.removeStoredMark(code)); } return true; } // entering code mark (from right edge): don't move the cursor, just add the mark if (!insideCode && enteringCode) { if (dispatch) { dispatch(state.tr.addStoredMark(code.create())); } return true; } // at the left edge: add code mark and move the cursor to the left if (insideCode && atLeftEdge) { const tr = state.tr.setSelection(Selection.near(state.doc.resolve($cursor.pos - 1))); if (dispatch) { dispatch(tr.addStoredMark(code.create())); } return true; } // exiting code mark (or at the beginning of the line): don't move the cursor, just remove the mark const isFirstChild = $cursor.index($cursor.depth - 1) === 0; if (insideCode && (exitingCode || !$cursor.nodeBefore && isFirstChild)) { if (dispatch) { dispatch(state.tr.removeStoredMark(code)); } return true; } } return false; }; }; const createInlineCodeFromTextInput = (from, to, text) => { return (state, dispatch) => { if (state.selection.empty) { const { nodeBefore: before } = state.doc.resolve(from); const { nodeAfter: after } = state.doc.resolve(to); const hasTickBefore = before && before.text && before.text.endsWith('`'); const hasTickAfter = after && after.text && after.text.startsWith('`'); if (hasTickBefore && hasTickAfter) { let tr = state.tr.replaceRangeWith(from - 1, to + 1, state.schema.text(text)); if (dispatch) { const codeMark = state.schema.marks.code.create(); tr = applyMarkOnRange(tr.mapping.map(from - 1), tr.mapping.map(to + 1), false, codeMark, tr).setStoredMarks([codeMark]); dispatch(tr); } return true; } } return false; }; }; export const createInlineCodeFromTextInputWithAnalytics = editorAnalyticsAPI => (from, to, text) => { return withAnalytics(editorAnalyticsAPI, { action: ACTION.FORMATTED, actionSubject: ACTION_SUBJECT.TEXT, eventType: EVENT_TYPE.TRACK, actionSubjectId: ACTION_SUBJECT_ID.FORMAT_CODE, attributes: { inputMethod: INPUT_METHOD.FORMATTING } })(createInlineCodeFromTextInput(from, to, text)); };