fix: Indent/outdent list controls, closes #6974

This commit is contained in:
Tom Moor
2024-06-08 21:51:52 -04:00
parent 2f495f0add
commit 808415b906
34 changed files with 80 additions and 62 deletions

View File

@@ -1,5 +1,5 @@
import { Command, TextSelection } from "prosemirror-state";
import isInCode from "../queries/isInCode";
import { isInCode } from "../queries/isInCode";
/**
* Moves the current selection to the previous newline, this is used inside

View File

@@ -1,5 +1,5 @@
import { Command } from "prosemirror-state";
import isNodeActive from "../queries/isNodeActive";
import { isNodeActive } from "../queries/isNodeActive";
/**
* Deletes the first paragraph node if it is empty and the cursor is at the

View File

@@ -1,7 +1,7 @@
import { NodeType } from "prosemirror-model";
import { Command, TextSelection } from "prosemirror-state";
import { findBlockNodes } from "../queries/findChildren";
import findCollapsedNodes from "../queries/findCollapsedNodes";
import { findCollapsedNodes } from "../queries/findCollapsedNodes";
export default function splitHeading(type: NodeType): Command {
return (state, dispatch): boolean => {

View File

@@ -1,7 +1,7 @@
import { setBlockType } from "prosemirror-commands";
import { NodeType } from "prosemirror-model";
import { Command } from "prosemirror-state";
import isNodeActive from "../queries/isNodeActive";
import { isNodeActive } from "../queries/isNodeActive";
/**
* Toggles the block type of the current selection between the given type and the toggle type.

View File

@@ -3,7 +3,7 @@ import { wrapInList, liftListItem } from "prosemirror-schema-list";
import { Command } from "prosemirror-state";
import { chainTransactions } from "../lib/chainTransactions";
import { findParentNode } from "../queries/findParentNode";
import isList from "../queries/isList";
import { isList } from "../queries/isList";
import clearNodes from "./clearNodes";
export default function toggleList(

View File

@@ -2,7 +2,7 @@ import { wrapIn, lift } from "prosemirror-commands";
import { NodeType } from "prosemirror-model";
import { Command } from "prosemirror-state";
import { Primitive } from "utility-types";
import isNodeActive from "../queries/isNodeActive";
import { isNodeActive } from "../queries/isNodeActive";
export default function toggleWrap(
type: NodeType,

View File

@@ -1,6 +1,6 @@
import { InputRule as ProsemirrorInputRule } from "prosemirror-inputrules";
import { EditorState } from "prosemirror-state";
import isInCode from "../queries/isInCode";
import { isInCode } from "../queries/isInCode";
/**
* A factory function for creating Prosemirror input rules that automatically insert text

View File

@@ -4,7 +4,7 @@ import { Command, Plugin } from "prosemirror-state";
import { v4 as uuidv4 } from "uuid";
import collapseSelection from "../commands/collapseSelection";
import { chainTransactions } from "../lib/chainTransactions";
import isMarkActive from "../queries/isMarkActive";
import { isMarkActive } from "../queries/isMarkActive";
import Mark from "./Mark";
export default class Comment extends Mark {

View File

@@ -12,8 +12,8 @@ import { Command, EditorState, Plugin, TextSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { toast } from "sonner";
import { sanitizeUrl } from "../../utils/urls";
import getMarkRange from "../queries/getMarkRange";
import isMarkActive from "../queries/isMarkActive";
import { getMarkRange } from "../queries/getMarkRange";
import { isMarkActive } from "../queries/isMarkActive";
import { EventType } from "../types";
import Mark from "./Mark";

View File

@@ -1,6 +1,6 @@
import { MarkSpec } from "prosemirror-model";
import { Plugin, TextSelection } from "prosemirror-state";
import getMarkRange from "../queries/getMarkRange";
import { getMarkRange } from "../queries/getMarkRange";
import markRule from "../rules/mark";
import Mark from "./Mark";

View File

@@ -3,7 +3,7 @@ import { NodeSpec, Node as ProsemirrorNode, NodeType } from "prosemirror-model";
import { Command } from "prosemirror-state";
import toggleWrap from "../commands/toggleWrap";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import isNodeActive from "../queries/isNodeActive";
import { isNodeActive } from "../queries/isNodeActive";
import Node from "./Node";
export default class Blockquote extends Node {

View File

@@ -88,6 +88,13 @@ export default class CheckboxItem extends Node {
}
};
commands({ type }: { type: NodeType }) {
return {
indentCheckboxList: () => sinkListItem(type),
outdentCheckboxList: () => liftListItem(type),
};
}
keys({ type }: { type: NodeType }) {
return {
Enter: splitListItem(type, {

View File

@@ -76,8 +76,8 @@ import Prism from "../extensions/Prism";
import { isCode } from "../lib/isCode";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import { findParentNode } from "../queries/findParentNode";
import getMarkRange from "../queries/getMarkRange";
import isInCode from "../queries/isInCode";
import { getMarkRange } from "../queries/getMarkRange";
import { isInCode } from "../queries/isInCode";
import Node from "./Node";
const PERSISTENCE_KEY = "rme-code-language";

View File

@@ -2,8 +2,8 @@ import { NodeSpec, NodeType } from "prosemirror-model";
import { Command } from "prosemirror-state";
import { isInTable } from "prosemirror-tables";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import isInCode from "../queries/isInCode";
import isNodeActive from "../queries/isNodeActive";
import { isInCode } from "../queries/isInCode";
import { isNodeActive } from "../queries/isNodeActive";
import breakRule from "../rules/breaks";
import Node from "./Node";

View File

@@ -14,9 +14,9 @@ import {
import { DecorationSet, Decoration } from "prosemirror-view";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import { findParentNodeClosestToPos } from "../queries/findParentNode";
import getParentListItem from "../queries/getParentListItem";
import isInList from "../queries/isInList";
import isList from "../queries/isList";
import { getParentListItem } from "../queries/getParentListItem";
import { isInList } from "../queries/isInList";
import { isList } from "../queries/isList";
import Node from "./Node";
export default class ListItem extends Node {

View File

@@ -3,7 +3,7 @@ import { Decoration, DecorationSet } from "prosemirror-view";
import Storage from "../../utils/Storage";
import { headingToPersistenceKey } from "../lib/headingToSlug";
import { findBlockNodes } from "../queries/findChildren";
import findCollapsedNodes from "../queries/findCollapsedNodes";
import { findCollapsedNodes } from "../queries/findCollapsedNodes";
export class FoldingHeadersPlugin extends Plugin {
constructor(documentId: string | undefined) {

View File

@@ -1,7 +1,7 @@
import { Node } from "prosemirror-model";
import { findBlockNodes, NodeWithPos } from "./findChildren";
export default function findCollapsedNodes(doc: Node): NodeWithPos[] {
export function findCollapsedNodes(doc: Node): NodeWithPos[] {
const blocks = findBlockNodes(doc);
const nodes: NodeWithPos[] = [];

View File

@@ -1,6 +1,6 @@
import { ResolvedPos, MarkType } from "prosemirror-model";
export default function getMarkRange($pos?: ResolvedPos, type?: MarkType) {
export function getMarkRange($pos?: ResolvedPos, type?: MarkType) {
if (!$pos || !type) {
return false;
}

View File

@@ -1,9 +1,7 @@
import { Node } from "prosemirror-model";
import { EditorState } from "prosemirror-state";
export default function getParentListItem(
state: EditorState
): [Node, number] | void {
export function getParentListItem(state: EditorState): [Node, number] | void {
const $head = state.selection.$head;
for (let d = $head.depth; d > 0; d--) {
const node = $head.node(d);

View File

@@ -1,6 +1,6 @@
import { EditorState } from "prosemirror-state";
import isMarkActive from "./isMarkActive";
import isNodeActive from "./isNodeActive";
import { isMarkActive } from "./isMarkActive";
import { isNodeActive } from "./isNodeActive";
type Options = {
/** Only check if the selection is inside a code block. */
@@ -16,10 +16,7 @@ type Options = {
* @param options The options.
* @returns True if the selection is inside a code block or code mark.
*/
export default function isInCode(
state: EditorState,
options?: Options
): boolean {
export function isInCode(state: EditorState, options?: Options): boolean {
const { nodes, marks } = state.schema;
if (!options?.onlyMark) {

View File

@@ -1,12 +1,19 @@
import { EditorState } from "prosemirror-state";
export default function isInList(state: EditorState) {
/**
* Check if the current selection is in a list
*
* @param state - The current editor state
* @param options - Optionally check for specific list types
*/
export function isInList(state: EditorState, options?: { types: string[] }) {
const $head = state.selection.$head;
for (let d = $head.depth; d > 0; d--) {
if (
["ordered_list", "bullet_list", "checkbox_list"].includes(
$head.node(d).type.name
)
(options?.types
? options.types
: ["ordered_list", "bullet_list", "checkbox_list"]
).includes($head.node(d).type.name)
) {
return true;
}

View File

@@ -1,6 +1,6 @@
import { Node, Schema } from "prosemirror-model";
export default function isList(node: Node, schema: Schema) {
export function isList(node: Node, schema: Schema) {
return (
node.type === schema.nodes.bullet_list ||
node.type === schema.nodes.ordered_list ||

View File

@@ -1,7 +1,7 @@
import { MarkType } from "prosemirror-model";
import { EditorState } from "prosemirror-state";
const isMarkActive =
export const isMarkActive =
(type: MarkType) =>
(state: EditorState): boolean => {
if (!type) {
@@ -14,5 +14,3 @@ const isMarkActive =
? type.isInSet(state.storedMarks || $from.marks())
: state.doc.rangeHasMark(from, to, type));
};
export default isMarkActive;

View File

@@ -3,7 +3,7 @@ import { EditorState } from "prosemirror-state";
import { Primitive } from "utility-types";
import { findParentNode } from "./findParentNode";
const isNodeActive =
export const isNodeActive =
(type: NodeType, attrs: Record<string, Primitive> = {}) =>
(state: EditorState) => {
if (!type) {
@@ -26,5 +26,3 @@ const isNodeActive =
return node.hasMarkup(type, { ...node.attrs, ...attrs });
};
export default isNodeActive;