82 lines
2.7 KiB
TypeScript
82 lines
2.7 KiB
TypeScript
import type {
|
|
BlockNoteEditor,
|
|
BlockSchema,
|
|
InlineContentSchema,
|
|
StyleSchema,
|
|
} from '@blocknote/core'
|
|
|
|
export type TolariaBlockNoteEditor = BlockNoteEditor<BlockSchema, InlineContentSchema, StyleSchema>
|
|
export type DropPlacement = 'before' | 'after'
|
|
|
|
export const BLOCK_CONTAINER_SELECTOR = '[data-node-type="blockContainer"][data-id]'
|
|
export const BLOCK_OUTER_SELECTOR = '[data-node-type="blockOuter"][data-id], .bn-block-outer[data-id]'
|
|
|
|
function clamp(value: number, min: number, max: number) {
|
|
return Math.min(Math.max(value, min), max)
|
|
}
|
|
|
|
export function editorBlockElement(editor: TolariaBlockNoteEditor): HTMLElement | null {
|
|
const element = editor.domElement
|
|
if (!(element instanceof HTMLElement)) return null
|
|
return element.matches('.bn-editor')
|
|
? element
|
|
: element.querySelector('.bn-editor')
|
|
}
|
|
|
|
export function blockElementFromPoint({
|
|
editorElement,
|
|
ownerDocument,
|
|
x,
|
|
y,
|
|
}: {
|
|
editorElement: HTMLElement
|
|
ownerDocument: Document
|
|
x: number
|
|
y: number
|
|
}): HTMLElement | null {
|
|
if (typeof ownerDocument.elementsFromPoint !== 'function') return null
|
|
|
|
const editorRect = editorElement.getBoundingClientRect()
|
|
if (editorRect.width <= 0 || editorRect.height <= 0) return null
|
|
|
|
const hitX = clamp(x, editorRect.left + 10, editorRect.right - 10)
|
|
const hitY = clamp(y, editorRect.top + 1, editorRect.bottom - 1)
|
|
|
|
for (const element of ownerDocument.elementsFromPoint(hitX, hitY)) {
|
|
if (!editorElement.contains(element)) continue
|
|
|
|
const blockElement = element.closest(BLOCK_CONTAINER_SELECTOR)
|
|
if (blockElement instanceof HTMLElement && editorElement.contains(blockElement)) {
|
|
return blockElement
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function dropPlacementForPoint(blockElement: HTMLElement, y: number): DropPlacement {
|
|
const rect = blockElement.getBoundingClientRect()
|
|
return y < rect.top + rect.height / 2 ? 'before' : 'after'
|
|
}
|
|
|
|
export function blockIdFromElement(blockElement: HTMLElement): string | null {
|
|
return blockElement.dataset.id ?? null
|
|
}
|
|
|
|
export function blockElementById(editorElement: HTMLElement, blockId: string): HTMLElement | null {
|
|
for (const element of editorElement.querySelectorAll(BLOCK_CONTAINER_SELECTOR)) {
|
|
if (element instanceof HTMLElement && element.dataset.id === blockId) return element
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function renderedSectionBlockElements(editorElement: HTMLElement): HTMLElement[] {
|
|
const outerBlocks = Array.from(editorElement.querySelectorAll(BLOCK_OUTER_SELECTOR))
|
|
.filter((element): element is HTMLElement => element instanceof HTMLElement)
|
|
if (outerBlocks.length > 0) return outerBlocks
|
|
|
|
return Array.from(editorElement.querySelectorAll(BLOCK_CONTAINER_SELECTOR))
|
|
.filter((element): element is HTMLElement => element instanceof HTMLElement)
|
|
}
|