Skip to content

Commit

Permalink
feat(Checkbox): improve checkbox insertion (#220)
Browse files Browse the repository at this point in the history
New behavior of inserting checkbox:
- Disabled:
  - when selecting any text
  - when cursor inside complex textblock (exclude checkboxes)
  - selection type is some other then TextSelection or NodeSelection
- If cursor is in paragraph: replace current paragraph with checkbox with contents of paragraph
- If node selection or if cursor is in another textblock or checkbox: insert new checkbox after current node, and move cursor inside new checkbox
  • Loading branch information
d3m1d0v authored Apr 8, 2024
1 parent 71813a2 commit cbcebf3
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 29 deletions.
67 changes: 39 additions & 28 deletions src/extensions/yfm/Checkbox/actions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Fragment, Node, Schema} from 'prosemirror-model';
import {Command, TextSelection} from 'prosemirror-state';
import {findChildrenByType, findParentNodeOfType} from 'prosemirror-utils';
import {Command, TextSelection, Transaction} from 'prosemirror-state';

import type {ActionSpec} from '../../../core';
import {get$Cursor, isNodeSelection} from '../../../utils/selection';
import {pType} from '../../base/BaseSchema';

import {checkboxInputType, checkboxLabelType, checkboxType} from './utils';
Expand All @@ -14,42 +14,48 @@ const createCheckbox = (schema: Schema, content?: Fragment | Node | Node[]) =>
]);

export const addCheckboxCmd: Command = (state, dispatch) => {
const paragraph = findParentNodeOfType(pType(state.schema))(state.selection);
const checkboxParent = findParentNodeOfType(checkboxType(state.schema))(state.selection);
const parent = paragraph || checkboxParent;

if (!parent) return false;

const checkboxChild = findChildrenByType(parent.node, checkboxLabelType(state.schema));

if (checkboxChild.length) {
if (dispatch) {
const {tr} = state;

tr.insert(parent.pos + parent.node.nodeSize, createCheckbox(state.schema, undefined));
function insertCheckbox(tr: Transaction, pos: number, content?: Fragment): Transaction {
tr.insert(pos, createCheckbox(state.schema, content));
return tr.setSelection(TextSelection.create(tr.doc, pos + 3)); // move cursor inside checkbox
}

tr.setSelection(new TextSelection(tr.doc.resolve(tr.selection.$from.after() + 4)));
if (isNodeSelection(state.selection) && rootOrNonComplex(state.selection.node)) {
const pos = state.selection.to;
dispatch?.(insertCheckbox(state.tr, pos).scrollIntoView());
return true;
}

dispatch(tr);
}
const $cursor = get$Cursor(state.selection);
if (!$cursor) return false;

const inCheckbox =
$cursor.parent.type === checkboxLabelType(state.schema) &&
$cursor.node($cursor.depth - 1).type === checkboxType(state.schema);
if (inCheckbox) {
const pos = $cursor.after($cursor.depth - 1);
dispatch?.(insertCheckbox(state.tr, pos).scrollIntoView());
return true;
}

const {tr} = state;

if (dispatch) {
tr.replaceWith(
parent.pos,
parent.pos + parent.node.nodeSize,
createCheckbox(state.schema, parent.node.content),
);
if (!rootOrNonComplex($cursor.parent)) return false;

tr.setSelection(new TextSelection(tr.doc.resolve(tr.selection.$from.after() - 1)));
if (!dispatch) return true;

dispatch?.(tr);
const {tr} = state;
const inParagraph = $cursor.parent.type === pType(state.schema);

if (inParagraph) {
const from = $cursor.before(),
to = $cursor.after();
// replace para with checkbox with same content
tr.replaceWith(from, to, createCheckbox(state.schema, $cursor.parent.content));
tr.setSelection(TextSelection.create(tr.doc, $cursor.pos + 2)); // save cursor position in text
} else {
const pos = $cursor.after();
insertCheckbox(tr, pos);
}

dispatch(tr.scrollIntoView());
return true;
};

Expand All @@ -59,3 +65,8 @@ export const addCheckbox = (): ActionSpec => {
run: addCheckboxCmd,
};
};

function rootOrNonComplex(node: Node): boolean {
const {complex} = node.type.spec;
return !complex || complex === 'root';
}
2 changes: 1 addition & 1 deletion src/utils/inputrules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {InputRule} from 'prosemirror-inputrules';
import {Fragment, Mark, MarkType, Node} from 'prosemirror-model';
import {EditorState, TextSelection} from 'prosemirror-state';

import {codeType} from '../extensions';
import {codeType} from '../extensions/markdown/specs';
import {isFunction} from '../lodash';
import {isMarkActive} from '../utils/marks';
// TODO: remove explicit import from code extension
Expand Down

0 comments on commit cbcebf3

Please sign in to comment.