
Depending on the browser in different situations the root node itself is selected and new text ends up in a text node on root level instead of a new paragraph. This happens in: * Firefox: after inserting a closed block like a horizontal rule * Chromium: after inserting or selecting such a closed block Now instead of inserting a paragraph directly after inserting an HR, the editor simply checks for normal text input inside the root node and wraps the newly written text with a paragraph (and moves the caret to the end of the paragraph because chromium moves it to the beginning of the line)
79 lines
2 KiB
TypeScript
79 lines
2 KiB
TypeScript
import { elementNameToMenuState, marks, blocks } from './constants'
|
|
|
|
export {
|
|
isRootNode,
|
|
isRootChild,
|
|
isElementNode,
|
|
isTextNode,
|
|
isEmptyTextNode
|
|
} from './node'
|
|
|
|
export {
|
|
moveCaretToBOL,
|
|
moveCaretToEOL
|
|
} from './caret'
|
|
|
|
export type State = KV<boolean>
|
|
export {
|
|
movementKeys,
|
|
controlSequenceKeys,
|
|
marks,
|
|
blocks
|
|
} from './constants'
|
|
|
|
function simpleAction (cmd: string, arg?: string): () => boolean {
|
|
return () => {
|
|
return document.execCommand(cmd, false, arg)
|
|
}
|
|
}
|
|
|
|
export const menuActionToCommand: KV<() => boolean> = {
|
|
paragraph: simpleAction('formatblock', 'P'),
|
|
heading1: simpleAction('formatblock', 'H1'),
|
|
heading2: simpleAction('formatblock', 'H2'),
|
|
heading3: simpleAction('formatblock', 'H3'),
|
|
bulletList: simpleAction('insertUnorderedList'),
|
|
numberedList: simpleAction('insertOrderedList'),
|
|
separator: simpleAction('insertHorizontalRule'),
|
|
bold: simpleAction('bold'),
|
|
italic: simpleAction('italic')
|
|
}
|
|
|
|
export function getActiveMarksAndBlocks (el: HTMLElement): {
|
|
marks: string[];
|
|
block: string;
|
|
} {
|
|
let activeBlock = 'paragraph'
|
|
const activeMarks: string[] = []
|
|
|
|
const focussedEl = el.nodeName === '#text' ? el.parentElement : el
|
|
if (!focussedEl) return { marks: activeMarks, block: activeBlock }
|
|
|
|
const focussedState = elementNameToMenuState[focussedEl.nodeName]
|
|
if (!focussedState) return { marks: activeMarks, block: activeBlock }
|
|
|
|
if (blocks.indexOf(focussedState) >= 0) {
|
|
activeBlock = focussedState
|
|
return { marks: activeMarks, block: activeBlock }
|
|
}
|
|
|
|
let wrappingEl = focussedEl.parentElement
|
|
let wrappingState: string
|
|
|
|
if (marks.indexOf(focussedState) >= 0) {
|
|
activeMarks.push(focussedState)
|
|
|
|
while (wrappingEl) {
|
|
wrappingState = elementNameToMenuState[wrappingEl.nodeName]
|
|
if (marks.indexOf(wrappingState) < 0) {
|
|
if (blocks.indexOf(wrappingState) >= 0) activeBlock = wrappingState
|
|
break
|
|
}
|
|
|
|
activeMarks.push(wrappingState)
|
|
wrappingEl = wrappingEl.parentElement
|
|
}
|
|
}
|
|
|
|
return { marks: activeMarks, block: activeBlock }
|
|
}
|