UNPKG

prosemirror-flat-list

Version:
94 lines (78 loc) 2.2 kB
import type { NodeRange } from 'prosemirror-model' import type { Command } from 'prosemirror-state' import type { ListAttributes, ProsemirrorNode } from '../types' import { isListNode } from '../utils/is-list-node' import { isNodeSelection } from '../utils/is-node-selection' import { safeLiftFromTo } from '../utils/safe-lift' import { dedentOutOfList } from './dedent-list' /** * @public @group Commands */ export interface UnwrapListOptions { /** * If given, only this kind of list will be unwrap. */ kind?: string } /** * Returns a command function that unwraps the list around the selection. * * @public @group Commands */ export function createUnwrapListCommand(options?: UnwrapListOptions): Command { const kind = options?.kind const unwrapList: Command = (state, dispatch) => { const selection = state.selection if (isNodeSelection(selection) && isTargetList(selection.node, kind)) { if (dispatch) { const tr = state.tr safeLiftFromTo(tr, tr.selection.from + 1, tr.selection.to - 1) dispatch(tr.scrollIntoView()) } return true } const range = selection.$from.blockRange(selection.$to) if (range && isTargetListsRange(range, kind)) { const tr = state.tr if (dedentOutOfList(tr, range)) { dispatch?.(tr) return true } } if (range && isTargetList(range.parent, kind)) { if (dispatch) { const tr = state.tr safeLiftFromTo( tr, range.$from.start(range.depth), range.$to.end(range.depth), ) dispatch(tr.scrollIntoView()) } return true } return false } return unwrapList } function isTargetList(node: ProsemirrorNode, kind: string | undefined) { if (isListNode(node)) { if (kind) { return (node.attrs as ListAttributes).kind === kind } return true } return false } function isTargetListsRange( range: NodeRange, kind: string | undefined, ): boolean { const { startIndex, endIndex, parent } = range for (let i = startIndex; i < endIndex; i++) { if (!isTargetList(parent.child(i), kind)) { return false } } return true }