mirror of
https://github.com/docmost/docmost.git
synced 2026-07-25 21:44:46 +10:00
feat: table row/column drag and drop (#1467)
* chore: add dev container * feat: add drag handle when hovering cell * feat: add column drag and drop * feat: add support for row drag and drop * refactor: extract preview controllers * fix: hover issue * refactor: add handle controller * chore: f * chore: remove log * chore: remove dev files * feat: hide other drop indicators when table dnd working * feat: add auto scroll and bug fix * chore: f * fix: firefox
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import type {
|
||||
Node,
|
||||
ResolvedPos,
|
||||
} from '@tiptap/pm/model'
|
||||
import type { Selection } from '@tiptap/pm/state'
|
||||
import {
|
||||
cellAround,
|
||||
cellNear,
|
||||
CellSelection,
|
||||
inSameTable,
|
||||
} from '@tiptap/pm/tables'
|
||||
|
||||
/**
|
||||
* Checks if the given object is a `CellSelection` instance.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function isCellSelection(value: unknown): value is CellSelection {
|
||||
return value instanceof CellSelection
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest table node.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function findTable($pos: ResolvedPos): FindParentNodeResult | undefined {
|
||||
return findParentNode((node) => node.type.spec.tableRole === 'table', $pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find the anchor and head cell in the same table by using the given
|
||||
* anchor and head as hit points, or fallback to the selection's anchor and
|
||||
* head.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function findCellRange(
|
||||
selection: Selection,
|
||||
anchorHit?: number,
|
||||
headHit?: number,
|
||||
): [ResolvedPos, ResolvedPos] | undefined {
|
||||
if (anchorHit == null && headHit == null && isCellSelection(selection)) {
|
||||
return [selection.$anchorCell, selection.$headCell]
|
||||
}
|
||||
|
||||
const anchor: number = anchorHit ?? headHit ?? selection.anchor
|
||||
const head: number = headHit ?? anchorHit ?? selection.head
|
||||
|
||||
const doc = selection.$head.doc
|
||||
|
||||
const $anchorCell = findCellPos(doc, anchor)
|
||||
const $headCell = findCellPos(doc, head)
|
||||
|
||||
if ($anchorCell && $headCell && inSameTable($anchorCell, $headCell)) {
|
||||
return [$anchorCell, $headCell]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find a resolved pos of a cell by using the given pos as a hit point.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function findCellPos(
|
||||
doc: Node,
|
||||
pos: number,
|
||||
): ResolvedPos | undefined {
|
||||
const $pos = doc.resolve(pos)
|
||||
return cellAround($pos) || cellNear($pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface FindParentNodeResult {
|
||||
/**
|
||||
* The closest parent node that satisfies the predicate.
|
||||
*/
|
||||
node: Node
|
||||
|
||||
/**
|
||||
* The position directly before the node.
|
||||
*/
|
||||
pos: number
|
||||
|
||||
/**
|
||||
* The position at the start of the node.
|
||||
*/
|
||||
start: number
|
||||
|
||||
/**
|
||||
* The depth of the node.
|
||||
*/
|
||||
depth: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest parent node that satisfies the predicate.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function findParentNode(
|
||||
/**
|
||||
* The predicate to test the parent node.
|
||||
*/
|
||||
predicate: (node: Node) => boolean,
|
||||
/**
|
||||
* The position to start searching from.
|
||||
*/
|
||||
$pos: ResolvedPos,
|
||||
): FindParentNodeResult | undefined {
|
||||
for (let depth = $pos.depth; depth >= 0; depth -= 1) {
|
||||
const node = $pos.node(depth)
|
||||
|
||||
if (predicate(node)) {
|
||||
const pos = depth === 0 ? 0 : $pos.before(depth)
|
||||
const start = $pos.start(depth)
|
||||
return { node, pos, start, depth }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user