mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
添加prosemirror封装
This commit is contained in:
parent
0988cbae90
commit
52d5a6ba47
4136
package-lock.json
generated
4136
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@ -17,6 +17,16 @@
|
|||||||
"dexie": "^3.0.3",
|
"dexie": "^3.0.3",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"mitt": "^2.1.0",
|
"mitt": "^2.1.0",
|
||||||
|
"prosemirror-commands": "^1.1.4",
|
||||||
|
"prosemirror-dropcursor": "^1.3.2",
|
||||||
|
"prosemirror-gapcursor": "^1.1.5",
|
||||||
|
"prosemirror-history": "^1.1.3",
|
||||||
|
"prosemirror-inputrules": "^1.1.3",
|
||||||
|
"prosemirror-model": "^1.13.1",
|
||||||
|
"prosemirror-schema-basic": "^1.1.2",
|
||||||
|
"prosemirror-schema-list": "^1.1.4",
|
||||||
|
"prosemirror-state": "^1.3.3",
|
||||||
|
"prosemirror-view": "^1.16.4",
|
||||||
"store2": "^2.12.0",
|
"store2": "^2.12.0",
|
||||||
"vue": "^3.0.0",
|
"vue": "^3.0.0",
|
||||||
"vuedraggable": "^4.0.1",
|
"vuedraggable": "^4.0.1",
|
||||||
@ -26,6 +36,14 @@
|
|||||||
"@types/clipboard": "^2.0.1",
|
"@types/clipboard": "^2.0.1",
|
||||||
"@types/crypto-js": "^4.0.1",
|
"@types/crypto-js": "^4.0.1",
|
||||||
"@types/jest": "^24.0.19",
|
"@types/jest": "^24.0.19",
|
||||||
|
"@types/prosemirror-commands": "^1.0.3",
|
||||||
|
"@types/prosemirror-dropcursor": "^1.0.0",
|
||||||
|
"@types/prosemirror-gapcursor": "^1.0.1",
|
||||||
|
"@types/prosemirror-history": "^1.0.1",
|
||||||
|
"@types/prosemirror-inputrules": "^1.0.3",
|
||||||
|
"@types/prosemirror-keymap": "^1.0.3",
|
||||||
|
"@types/prosemirror-schema-basic": "^1.0.1",
|
||||||
|
"@types/prosemirror-schema-list": "^1.0.1",
|
||||||
"@types/resize-observer-browser": "^0.1.4",
|
"@types/resize-observer-browser": "^0.1.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||||
"@typescript-eslint/parser": "^2.33.0",
|
"@typescript-eslint/parser": "^2.33.0",
|
||||||
@ -41,8 +59,10 @@
|
|||||||
"babel-plugin-import": "^1.13.3",
|
"babel-plugin-import": "^1.13.3",
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^6.7.2",
|
||||||
"eslint-plugin-vue": "^7.0.0-0",
|
"eslint-plugin-vue": "^7.0.0-0",
|
||||||
|
"i": "^0.3.6",
|
||||||
"less": "^3.12.2",
|
"less": "^3.12.2",
|
||||||
"less-loader": "^7.1.0",
|
"less-loader": "^7.1.0",
|
||||||
|
"npm": "^6.14.10",
|
||||||
"sass": "^1.26.5",
|
"sass": "^1.26.5",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"stylelint": "^13.8.0",
|
"stylelint": "^13.8.0",
|
||||||
|
40
src/prosemirror/commands/applyMark.ts
Normal file
40
src/prosemirror/commands/applyMark.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { MarkType } from 'prosemirror-model'
|
||||||
|
import { SelectionRange, Transaction } from 'prosemirror-state'
|
||||||
|
|
||||||
|
const markApplies = (tr: Transaction, ranges: SelectionRange[], type: MarkType) => {
|
||||||
|
for (let i = 0; i < ranges.length; i++) {
|
||||||
|
let {$from, $to} = ranges[i]
|
||||||
|
let can = $from.depth == 0 ? tr.doc.type.allowsMarkType(type) : false
|
||||||
|
tr.doc.nodesBetween($from.pos, $to.pos, node => {
|
||||||
|
if (can) return false
|
||||||
|
can = node.inlineContent && node.type.allowsMarkType(type)
|
||||||
|
})
|
||||||
|
if (can) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const applyMark = (tr: Transaction, markType: MarkType, attrs: { [key: string]: string; } | undefined) => {
|
||||||
|
if(!tr.selection || !tr.doc || !markType) return tr
|
||||||
|
|
||||||
|
const { empty, $anchor, ranges } = tr.selection
|
||||||
|
if(empty && !$anchor || !markApplies(tr, ranges, markType)) return tr
|
||||||
|
|
||||||
|
if($anchor) {
|
||||||
|
tr = tr.removeStoredMark(markType)
|
||||||
|
return attrs ? tr.addStoredMark(markType.create(attrs)) : tr
|
||||||
|
}
|
||||||
|
|
||||||
|
let has = false
|
||||||
|
for(let i = 0; !has && i < ranges.length; i++) {
|
||||||
|
const { $from, $to } = ranges[i]
|
||||||
|
has = tr.doc.rangeHasMark($from.pos, $to.pos, markType)
|
||||||
|
}
|
||||||
|
for(let j = 0; j < ranges.length; j++) {
|
||||||
|
const { $from, $to } = ranges[j]
|
||||||
|
if(has) tr = tr.removeMark($from.pos, $to.pos, markType)
|
||||||
|
if(attrs) tr = tr.addMark($from.pos, $to.pos, markType.create(attrs))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr
|
||||||
|
}
|
62
src/prosemirror/commands/setTextAlign.ts
Normal file
62
src/prosemirror/commands/setTextAlign.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { Schema, Node, NodeType } from 'prosemirror-model'
|
||||||
|
import { Transaction } from 'prosemirror-state'
|
||||||
|
import { EditorView } from 'prosemirror-view'
|
||||||
|
|
||||||
|
export const setTextAlign = (tr: Transaction, schema: Schema, alignment: string) => {
|
||||||
|
const { selection, doc } = tr
|
||||||
|
if(!selection || !doc) return tr
|
||||||
|
|
||||||
|
const { from, to } = selection
|
||||||
|
const { nodes } = schema
|
||||||
|
|
||||||
|
const blockquote = nodes.blockquote
|
||||||
|
const listItem = nodes.list_item
|
||||||
|
const paragraph = nodes.paragraph
|
||||||
|
|
||||||
|
interface Task {
|
||||||
|
node: Node;
|
||||||
|
pos: number;
|
||||||
|
nodeType: NodeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tasks: Task[] = []
|
||||||
|
alignment = alignment || ''
|
||||||
|
|
||||||
|
const allowedNodeTypes = new Set([blockquote, listItem, paragraph])
|
||||||
|
|
||||||
|
doc.nodesBetween(from, to, (node, pos) => {
|
||||||
|
const nodeType = node.type
|
||||||
|
const align = node.attrs.align || ''
|
||||||
|
if(align !== alignment && allowedNodeTypes.has(nodeType)) {
|
||||||
|
tasks.push({
|
||||||
|
node,
|
||||||
|
pos,
|
||||||
|
nodeType,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if(!tasks.length) return tr
|
||||||
|
|
||||||
|
tasks.forEach(task => {
|
||||||
|
const { node, pos, nodeType } = task
|
||||||
|
let { attrs } = node
|
||||||
|
if(alignment) attrs = { ...attrs, align: alignment }
|
||||||
|
else attrs = { ...attrs, align: null }
|
||||||
|
tr = tr.setNodeMarkup(pos, nodeType, attrs, node.marks);
|
||||||
|
})
|
||||||
|
|
||||||
|
return tr
|
||||||
|
}
|
||||||
|
|
||||||
|
export const alignmentCommand = (view: EditorView, alignment: string) => {
|
||||||
|
const { state } = view
|
||||||
|
const { schema, selection } = state
|
||||||
|
const tr = setTextAlign(
|
||||||
|
state.tr.setSelection(selection),
|
||||||
|
schema,
|
||||||
|
alignment,
|
||||||
|
)
|
||||||
|
view.dispatch(tr)
|
||||||
|
}
|
40
src/prosemirror/commands/toggleList.ts
Normal file
40
src/prosemirror/commands/toggleList.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { wrapInList, liftListItem } from 'prosemirror-schema-list'
|
||||||
|
import { Schema, Node, NodeType } from 'prosemirror-model'
|
||||||
|
import { Transaction, EditorState } from 'prosemirror-state'
|
||||||
|
import { findParentNode } from '../utils'
|
||||||
|
|
||||||
|
const isList = (node: Node, schema: Schema) => {
|
||||||
|
return (
|
||||||
|
node.type === schema.nodes.bullet_list ||
|
||||||
|
node.type === schema.nodes.ordered_list
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toggleList = (listType: NodeType, itemType: NodeType) => {
|
||||||
|
return (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
||||||
|
const { schema, selection } = state
|
||||||
|
const { $from, $to } = selection
|
||||||
|
const range = $from.blockRange($to)
|
||||||
|
|
||||||
|
if(!range) return false
|
||||||
|
|
||||||
|
const parentList = findParentNode((node: Node) => isList(node, schema))(selection)
|
||||||
|
|
||||||
|
if(range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) {
|
||||||
|
if(parentList.node.type === listType) {
|
||||||
|
return liftListItem(itemType)(state, dispatch)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isList(parentList.node, schema) && listType.validContent(parentList.node.content)) {
|
||||||
|
const { tr } = state
|
||||||
|
tr.setNodeMarkup(parentList.pos, listType)
|
||||||
|
|
||||||
|
if(dispatch) dispatch(tr)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapInList(listType)(state, dispatch)
|
||||||
|
}
|
||||||
|
}
|
20
src/prosemirror/plugins/index.ts
Normal file
20
src/prosemirror/plugins/index.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { keymap } from 'prosemirror-keymap'
|
||||||
|
import { Schema } from 'prosemirror-model'
|
||||||
|
import { history } from 'prosemirror-history'
|
||||||
|
import { baseKeymap } from 'prosemirror-commands'
|
||||||
|
import { dropCursor } from 'prosemirror-dropcursor'
|
||||||
|
import { gapCursor } from 'prosemirror-gapcursor'
|
||||||
|
|
||||||
|
import { buildKeymap } from './keymap'
|
||||||
|
import { buildInputRules } from './inputrules'
|
||||||
|
|
||||||
|
export const buildPlugins = (schema: Schema) => {
|
||||||
|
return [
|
||||||
|
buildInputRules(schema),
|
||||||
|
keymap(buildKeymap(schema)),
|
||||||
|
keymap(baseKeymap),
|
||||||
|
dropCursor(),
|
||||||
|
gapCursor(),
|
||||||
|
history(),
|
||||||
|
]
|
||||||
|
}
|
38
src/prosemirror/plugins/inputrules.ts
Normal file
38
src/prosemirror/plugins/inputrules.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { NodeType, Schema } from 'prosemirror-model'
|
||||||
|
import {
|
||||||
|
inputRules,
|
||||||
|
wrappingInputRule,
|
||||||
|
textblockTypeInputRule,
|
||||||
|
smartQuotes,
|
||||||
|
emDash,
|
||||||
|
ellipsis,
|
||||||
|
} from 'prosemirror-inputrules'
|
||||||
|
|
||||||
|
const blockQuoteRule = (nodeType: NodeType) => wrappingInputRule(/^\s*>\s$/, nodeType)
|
||||||
|
|
||||||
|
const orderedListRule = (nodeType: NodeType) => (
|
||||||
|
wrappingInputRule(
|
||||||
|
/^(\d+)\.\s$/,
|
||||||
|
nodeType,
|
||||||
|
match => ({order: +match[1]}),
|
||||||
|
(match, node) => node.childCount + node.attrs.order == +match[1],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const bulletListRule = (nodeType: NodeType) => wrappingInputRule(/^\s*([-+*])\s$/, nodeType)
|
||||||
|
|
||||||
|
const codeBlockRule = (nodeType: NodeType) => textblockTypeInputRule(/^```$/, nodeType)
|
||||||
|
|
||||||
|
export const buildInputRules = (schema: Schema) => {
|
||||||
|
const rules = [
|
||||||
|
...smartQuotes,
|
||||||
|
ellipsis,
|
||||||
|
emDash,
|
||||||
|
]
|
||||||
|
rules.push(blockQuoteRule(schema.nodes.blockquote))
|
||||||
|
rules.push(orderedListRule(schema.nodes.ordered_list))
|
||||||
|
rules.push(bulletListRule(schema.nodes.bullet_list))
|
||||||
|
rules.push(codeBlockRule(schema.nodes.code_block))
|
||||||
|
|
||||||
|
return inputRules({ rules })
|
||||||
|
}
|
33
src/prosemirror/plugins/keymap.ts
Normal file
33
src/prosemirror/plugins/keymap.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { splitListItem, liftListItem, sinkListItem } from 'prosemirror-schema-list'
|
||||||
|
import { Schema } from 'prosemirror-model'
|
||||||
|
import { undo, redo } from 'prosemirror-history'
|
||||||
|
import { undoInputRule } from 'prosemirror-inputrules'
|
||||||
|
import {
|
||||||
|
toggleMark,
|
||||||
|
selectParentNode,
|
||||||
|
joinUp,
|
||||||
|
joinDown,
|
||||||
|
Command,
|
||||||
|
} from 'prosemirror-commands'
|
||||||
|
|
||||||
|
export const buildKeymap = (schema: Schema) => {
|
||||||
|
const keys = {}
|
||||||
|
const bind = (key: string, cmd: Command) => keys[key] = cmd
|
||||||
|
|
||||||
|
bind('Alt-ArrowUp', joinUp)
|
||||||
|
bind('Alt-ArrowDown', joinDown)
|
||||||
|
bind('Ctrl-z', undo)
|
||||||
|
bind('Ctrl-y', redo)
|
||||||
|
bind('Backspace', undoInputRule)
|
||||||
|
bind('Escape', selectParentNode)
|
||||||
|
bind('Ctrl-b', toggleMark(schema.marks.strong))
|
||||||
|
bind('Ctrl-i', toggleMark(schema.marks.em))
|
||||||
|
bind('Ctrl-u', toggleMark(schema.marks.underline))
|
||||||
|
bind('Ctrl-d', toggleMark(schema.marks.strikethrough))
|
||||||
|
|
||||||
|
bind('Enter', splitListItem(schema.nodes.list_item))
|
||||||
|
bind('Mod-[', liftListItem(schema.nodes.list_item))
|
||||||
|
bind('Mod-]', sinkListItem(schema.nodes.list_item))
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
5
src/prosemirror/schema/index.ts
Normal file
5
src/prosemirror/schema/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import nodes from './nodes'
|
||||||
|
import marks from './marks'
|
||||||
|
|
||||||
|
export const schemaNodes = nodes
|
||||||
|
export const schemaMarks = marks
|
146
src/prosemirror/schema/marks.ts
Normal file
146
src/prosemirror/schema/marks.ts
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import { marks } from 'prosemirror-schema-basic'
|
||||||
|
import { Node } from 'prosemirror-model'
|
||||||
|
|
||||||
|
const subscript = {
|
||||||
|
excludes: 'subscript',
|
||||||
|
parseDOM: [
|
||||||
|
{ tag: 'sub' },
|
||||||
|
{
|
||||||
|
style: 'vertical-align',
|
||||||
|
getAttrs: (value: string) => value === 'sub' && null
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM: () => ['sub', 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
const superscript = {
|
||||||
|
excludes: 'superscript',
|
||||||
|
parseDOM: [
|
||||||
|
{ tag: 'sup' },
|
||||||
|
{
|
||||||
|
style: 'vertical-align',
|
||||||
|
getAttrs: (value: string) => value === 'super' && null
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM: () => ['sup', 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
const strikethrough = {
|
||||||
|
parseDOM: [
|
||||||
|
{ tag: 'strike' },
|
||||||
|
{
|
||||||
|
style: 'text-decoration',
|
||||||
|
getAttrs: (value: string) => value === 'line-through' && null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
style: 'text-decoration-line',
|
||||||
|
getAttrs: (value: string) => value === 'line-through' && null
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM: () => ['span', { style: 'text-decoration-line: line-through' }, 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
const underline = {
|
||||||
|
parseDOM: [
|
||||||
|
{ tag: 'u' },
|
||||||
|
{
|
||||||
|
style: 'text-decoration',
|
||||||
|
getAttrs: (value: string) => value === 'underline' && null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
style: 'text-decoration-line',
|
||||||
|
getAttrs: (value: string) => value === 'underline' && null
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM: () => ['span', { style: 'text-decoration: underline' }, 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
const forecolor = {
|
||||||
|
attrs: {
|
||||||
|
color: {},
|
||||||
|
},
|
||||||
|
parseDOM: [
|
||||||
|
{
|
||||||
|
style: 'color',
|
||||||
|
getAttrs: (color: string) => color ? { color } : {}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM: (node: Node) => {
|
||||||
|
const { color } = node.attrs
|
||||||
|
let style = ''
|
||||||
|
if(color) style += `color: ${color};`
|
||||||
|
return ['span', { style }, 0]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const backcolor = {
|
||||||
|
attrs: {
|
||||||
|
backcolor: {},
|
||||||
|
},
|
||||||
|
inline: true,
|
||||||
|
group: 'inline',
|
||||||
|
parseDOM: [
|
||||||
|
{
|
||||||
|
tag: 'span[style*=background-color]',
|
||||||
|
getAttrs: (backcolor: string) => backcolor ? { backcolor } : {}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM: (node: Node) => {
|
||||||
|
const { backcolor } = node.attrs
|
||||||
|
let style = ''
|
||||||
|
if(backcolor) style += `background-color: ${backcolor};`
|
||||||
|
return ['span', { style }, 0]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const fontsize = {
|
||||||
|
attrs: {
|
||||||
|
fontsize: {},
|
||||||
|
},
|
||||||
|
inline: true,
|
||||||
|
group: 'inline',
|
||||||
|
parseDOM: [
|
||||||
|
{
|
||||||
|
style: 'font-size',
|
||||||
|
getAttrs: (fontsize: string) => fontsize ? { fontsize } : {}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM: (node: Node) => {
|
||||||
|
const { fontsize } = node.attrs
|
||||||
|
let style = ''
|
||||||
|
if(fontsize) style += `font-size: ${fontsize}`
|
||||||
|
return ['span', { style }, 0]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const fontname = {
|
||||||
|
attrs: {
|
||||||
|
fontname: '',
|
||||||
|
},
|
||||||
|
inline: true,
|
||||||
|
group: 'inline',
|
||||||
|
parseDOM: [
|
||||||
|
{
|
||||||
|
style: 'font-family',
|
||||||
|
getAttrs: (fontname: string) => ({ fontname: fontname ? fontname.replace(/[\"\']/g, '') : '' })
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toDOM: (node: Node) => {
|
||||||
|
const { fontname } = node.attrs
|
||||||
|
let style = ''
|
||||||
|
if(fontname) style += `font-family: ${fontname}`
|
||||||
|
return ['span', { style }, 0]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...marks,
|
||||||
|
subscript,
|
||||||
|
superscript,
|
||||||
|
strikethrough,
|
||||||
|
underline,
|
||||||
|
forecolor,
|
||||||
|
backcolor,
|
||||||
|
fontsize,
|
||||||
|
fontname,
|
||||||
|
}
|
53
src/prosemirror/schema/nodes.ts
Normal file
53
src/prosemirror/schema/nodes.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { nodes } from 'prosemirror-schema-basic'
|
||||||
|
import { Node } from 'prosemirror-model'
|
||||||
|
import { orderedList, bulletList, listItem } from 'prosemirror-schema-list'
|
||||||
|
|
||||||
|
const listNodes = {
|
||||||
|
ordered_list: {
|
||||||
|
...orderedList,
|
||||||
|
content: 'list_item+',
|
||||||
|
group: 'block',
|
||||||
|
},
|
||||||
|
bullet_list: {
|
||||||
|
...bulletList,
|
||||||
|
content: 'list_item+',
|
||||||
|
group: 'block',
|
||||||
|
},
|
||||||
|
list_item: {
|
||||||
|
...listItem,
|
||||||
|
content: 'paragraph block*',
|
||||||
|
group: 'block',
|
||||||
|
},
|
||||||
|
|
||||||
|
paragraph: {
|
||||||
|
attrs: {
|
||||||
|
align: {default: null},
|
||||||
|
},
|
||||||
|
content: 'inline*',
|
||||||
|
group: 'block',
|
||||||
|
parseDOM: [
|
||||||
|
{
|
||||||
|
tag: 'p',
|
||||||
|
getAttrs: (dom: HTMLElement) => {
|
||||||
|
const { textAlign } = dom.style
|
||||||
|
let align = dom.getAttribute('align') || textAlign || ''
|
||||||
|
align = /(left|right|center|justify)/.test(align) ? align : ''
|
||||||
|
|
||||||
|
return { align }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toDOM: (node: Node) => {
|
||||||
|
const { align } = node.attrs
|
||||||
|
let style = ''
|
||||||
|
if(align && align !== 'left') style += `text-align: ${align};`
|
||||||
|
|
||||||
|
return ['p', { style }, 0]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...nodes,
|
||||||
|
...listNodes,
|
||||||
|
}
|
77
src/prosemirror/utils.ts
Normal file
77
src/prosemirror/utils.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { Node, NodeType, ResolvedPos } from 'prosemirror-model'
|
||||||
|
import { EditorState, Selection } from 'prosemirror-state'
|
||||||
|
import { EditorView } from 'prosemirror-view'
|
||||||
|
|
||||||
|
const equalNodeType = (nodeType: NodeType, node: Node) => {
|
||||||
|
return Array.isArray(nodeType) && nodeType.indexOf(node.type) > -1 || node.type === nodeType
|
||||||
|
}
|
||||||
|
|
||||||
|
const findParentNodeClosestToPos = ($pos: ResolvedPos, predicate: (node: Node) => boolean) => {
|
||||||
|
for(let i = $pos.depth; i > 0; i--) {
|
||||||
|
const node = $pos.node(i)
|
||||||
|
if(predicate(node)) {
|
||||||
|
return {
|
||||||
|
pos: i > 0 ? $pos.before(i) : 0,
|
||||||
|
start: $pos.start(i),
|
||||||
|
depth: i,
|
||||||
|
node,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findParentNode = (predicate: (node: Node) => boolean) => {
|
||||||
|
return (_ref: Selection) => findParentNodeClosestToPos(_ref.$from, predicate)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findParentNodeOfType = (nodeType: NodeType) => {
|
||||||
|
return (selection: Selection) => {
|
||||||
|
return findParentNode((node: Node) => {
|
||||||
|
return equalNodeType(nodeType, node)
|
||||||
|
})(selection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isActiveOfParentNodeType = (nodeType: string, state: EditorState) => {
|
||||||
|
const node = state.schema.nodes[nodeType]
|
||||||
|
return !!findParentNodeOfType(node)(state.selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMarkAttrs = (view: EditorView) => {
|
||||||
|
const { selection, doc } = view.state
|
||||||
|
const { from } = selection
|
||||||
|
const node = doc.nodeAt(from)
|
||||||
|
return node?.marks || []
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getAttrValue = (view: EditorView, markType: string, attr: string) => {
|
||||||
|
const marks = getMarkAttrs(view)
|
||||||
|
for(const mark of marks) {
|
||||||
|
if(mark.type.name === markType && mark.attrs[attr]) return mark.attrs[attr]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isActiveMark = (view: EditorView, markType: string) => {
|
||||||
|
const marks = getMarkAttrs(view)
|
||||||
|
for(const mark of marks) {
|
||||||
|
if(mark.type.name === markType) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getAttrValueInSelection = (view: EditorView, attr: string) => {
|
||||||
|
const { selection, doc } = view.state
|
||||||
|
const { from, to } = selection
|
||||||
|
|
||||||
|
let keepChecking = true
|
||||||
|
let value = ''
|
||||||
|
doc.nodesBetween(from, to, node => {
|
||||||
|
if(keepChecking && node.attrs[attr]) {
|
||||||
|
keepChecking = false
|
||||||
|
value = node.attrs[attr]
|
||||||
|
}
|
||||||
|
return keepChecking
|
||||||
|
})
|
||||||
|
return value
|
||||||
|
}
|
@ -11,7 +11,6 @@ export const actions: ActionTree<State, State> = {
|
|||||||
|
|
||||||
if(lastSnapshot) {
|
if(lastSnapshot) {
|
||||||
db.snapshots.clear()
|
db.snapshots.clear()
|
||||||
// commit(MutationTypes.SET_SLIDES, lastSnapshot.slides)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const newFirstSnapshot = {
|
const newFirstSnapshot = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user