chore: 目录结构调整

This commit is contained in:
pipipi-pikachu 2021-06-04 17:19:09 +08:00
parent a10704c0ba
commit b86ff7dfd3
13 changed files with 404 additions and 405 deletions

View File

@ -142,7 +142,6 @@ A. 设置在线字体时会下载对应的字体文件,该文件较大,需
├── hooks // 供多个组件(模块)使用的 hooks 方法 ├── hooks // 供多个组件(模块)使用的 hooks 方法
├── mocks // mocks 数据 ├── mocks // mocks 数据
├── plugins // 自定义的 Vue 插件 ├── plugins // 自定义的 Vue 插件
├── prosemirror // ProseMirror 富文本编辑器相关的文件
├── types // 类型定义文件 ├── types // 类型定义文件
├── store // Vuex store参考https://vuex.vuejs.org/zh/guide/ ├── store // Vuex store参考https://vuex.vuejs.org/zh/guide/
├── utils // 通用的工具方法 ├── utils // 通用的工具方法

View File

@ -1,62 +1,62 @@
import { Schema, Node, NodeType } from 'prosemirror-model' import { Schema, Node, NodeType } from 'prosemirror-model'
import { Transaction } from 'prosemirror-state' import { Transaction } from 'prosemirror-state'
import { EditorView } from 'prosemirror-view' import { EditorView } from 'prosemirror-view'
export const setTextAlign = (tr: Transaction, schema: Schema, alignment: string) => { export const setTextAlign = (tr: Transaction, schema: Schema, alignment: string) => {
const { selection, doc } = tr const { selection, doc } = tr
if (!selection || !doc) return tr if (!selection || !doc) return tr
const { from, to } = selection const { from, to } = selection
const { nodes } = schema const { nodes } = schema
const blockquote = nodes.blockquote const blockquote = nodes.blockquote
const listItem = nodes.list_item const listItem = nodes.list_item
const paragraph = nodes.paragraph const paragraph = nodes.paragraph
interface Task { interface Task {
node: Node; node: Node;
pos: number; pos: number;
nodeType: NodeType; nodeType: NodeType;
} }
const tasks: Task[] = [] const tasks: Task[] = []
alignment = alignment || '' alignment = alignment || ''
const allowedNodeTypes = new Set([blockquote, listItem, paragraph]) const allowedNodeTypes = new Set([blockquote, listItem, paragraph])
doc.nodesBetween(from, to, (node, pos) => { doc.nodesBetween(from, to, (node, pos) => {
const nodeType = node.type const nodeType = node.type
const align = node.attrs.align || '' const align = node.attrs.align || ''
if (align !== alignment && allowedNodeTypes.has(nodeType)) { if (align !== alignment && allowedNodeTypes.has(nodeType)) {
tasks.push({ tasks.push({
node, node,
pos, pos,
nodeType, nodeType,
}) })
} }
return true return true
}) })
if (!tasks.length) return tr if (!tasks.length) return tr
tasks.forEach(task => { tasks.forEach(task => {
const { node, pos, nodeType } = task const { node, pos, nodeType } = task
let { attrs } = node let { attrs } = node
if (alignment) attrs = { ...attrs, align: alignment } if (alignment) attrs = { ...attrs, align: alignment }
else attrs = { ...attrs, align: null } else attrs = { ...attrs, align: null }
tr = tr.setNodeMarkup(pos, nodeType, attrs, node.marks) tr = tr.setNodeMarkup(pos, nodeType, attrs, node.marks)
}) })
return tr return tr
} }
export const alignmentCommand = (view: EditorView, alignment: string) => { export const alignmentCommand = (view: EditorView, alignment: string) => {
const { state } = view const { state } = view
const { schema, selection } = state const { schema, selection } = state
const tr = setTextAlign( const tr = setTextAlign(
state.tr.setSelection(selection), state.tr.setSelection(selection),
schema, schema,
alignment, alignment,
) )
view.dispatch(tr) view.dispatch(tr)
} }

View File

@ -1,40 +1,40 @@
import { wrapInList, liftListItem } from 'prosemirror-schema-list' import { wrapInList, liftListItem } from 'prosemirror-schema-list'
import { Schema, Node, NodeType } from 'prosemirror-model' import { Schema, Node, NodeType } from 'prosemirror-model'
import { Transaction, EditorState } from 'prosemirror-state' import { Transaction, EditorState } from 'prosemirror-state'
import { findParentNode } from '../utils' import { findParentNode } from '../utils'
const isList = (node: Node, schema: Schema) => { const isList = (node: Node, schema: Schema) => {
return ( return (
node.type === schema.nodes.bullet_list || node.type === schema.nodes.bullet_list ||
node.type === schema.nodes.ordered_list node.type === schema.nodes.ordered_list
) )
} }
export const toggleList = (listType: NodeType, itemType: NodeType) => { export const toggleList = (listType: NodeType, itemType: NodeType) => {
return (state: EditorState, dispatch: (tr: Transaction) => void) => { return (state: EditorState, dispatch: (tr: Transaction) => void) => {
const { schema, selection } = state const { schema, selection } = state
const { $from, $to } = selection const { $from, $to } = selection
const range = $from.blockRange($to) const range = $from.blockRange($to)
if (!range) return false if (!range) return false
const parentList = findParentNode((node: Node) => isList(node, schema))(selection) const parentList = findParentNode((node: Node) => isList(node, schema))(selection)
if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) { if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) {
if (parentList.node.type === listType) { if (parentList.node.type === listType) {
return liftListItem(itemType)(state, dispatch) return liftListItem(itemType)(state, dispatch)
} }
if (isList(parentList.node, schema) && listType.validContent(parentList.node.content)) { if (isList(parentList.node, schema) && listType.validContent(parentList.node.content)) {
const { tr } = state const { tr } = state
tr.setNodeMarkup(parentList.pos, listType) tr.setNodeMarkup(parentList.pos, listType)
if (dispatch) dispatch(tr) if (dispatch) dispatch(tr)
return false return false
} }
} }
return wrapInList(listType)(state, dispatch) return wrapInList(listType)(state, dispatch)
} }
} }

View File

@ -1,20 +1,20 @@
import { keymap } from 'prosemirror-keymap' import { keymap } from 'prosemirror-keymap'
import { Schema } from 'prosemirror-model' import { Schema } from 'prosemirror-model'
import { history } from 'prosemirror-history' import { history } from 'prosemirror-history'
import { baseKeymap } from 'prosemirror-commands' import { baseKeymap } from 'prosemirror-commands'
import { dropCursor } from 'prosemirror-dropcursor' import { dropCursor } from 'prosemirror-dropcursor'
import { gapCursor } from 'prosemirror-gapcursor' import { gapCursor } from 'prosemirror-gapcursor'
import { buildKeymap } from './keymap' import { buildKeymap } from './keymap'
import { buildInputRules } from './inputrules' import { buildInputRules } from './inputrules'
export const buildPlugins = (schema: Schema) => { export const buildPlugins = (schema: Schema) => {
return [ return [
buildInputRules(schema), buildInputRules(schema),
keymap(buildKeymap(schema)), keymap(buildKeymap(schema)),
keymap(baseKeymap), keymap(baseKeymap),
dropCursor(), dropCursor(),
gapCursor(), gapCursor(),
history(), history(),
] ]
} }

View File

@ -1,38 +1,38 @@
import { NodeType, Schema } from 'prosemirror-model' import { NodeType, Schema } from 'prosemirror-model'
import { import {
inputRules, inputRules,
wrappingInputRule, wrappingInputRule,
textblockTypeInputRule, textblockTypeInputRule,
smartQuotes, smartQuotes,
emDash, emDash,
ellipsis, ellipsis,
} from 'prosemirror-inputrules' } from 'prosemirror-inputrules'
const blockQuoteRule = (nodeType: NodeType) => wrappingInputRule(/^\s*>\s$/, nodeType) const blockQuoteRule = (nodeType: NodeType) => wrappingInputRule(/^\s*>\s$/, nodeType)
const orderedListRule = (nodeType: NodeType) => ( const orderedListRule = (nodeType: NodeType) => (
wrappingInputRule( wrappingInputRule(
/^(\d+)\.\s$/, /^(\d+)\.\s$/,
nodeType, nodeType,
match => ({order: +match[1]}), match => ({order: +match[1]}),
(match, node) => node.childCount + node.attrs.order === +match[1], (match, node) => node.childCount + node.attrs.order === +match[1],
) )
) )
const bulletListRule = (nodeType: NodeType) => wrappingInputRule(/^\s*([-+*])\s$/, nodeType) const bulletListRule = (nodeType: NodeType) => wrappingInputRule(/^\s*([-+*])\s$/, nodeType)
const codeBlockRule = (nodeType: NodeType) => textblockTypeInputRule(/^```$/, nodeType) const codeBlockRule = (nodeType: NodeType) => textblockTypeInputRule(/^```$/, nodeType)
export const buildInputRules = (schema: Schema) => { export const buildInputRules = (schema: Schema) => {
const rules = [ const rules = [
...smartQuotes, ...smartQuotes,
ellipsis, ellipsis,
emDash, emDash,
] ]
rules.push(blockQuoteRule(schema.nodes.blockquote)) rules.push(blockQuoteRule(schema.nodes.blockquote))
rules.push(orderedListRule(schema.nodes.ordered_list)) rules.push(orderedListRule(schema.nodes.ordered_list))
rules.push(bulletListRule(schema.nodes.bullet_list)) rules.push(bulletListRule(schema.nodes.bullet_list))
rules.push(codeBlockRule(schema.nodes.code_block)) rules.push(codeBlockRule(schema.nodes.code_block))
return inputRules({ rules }) return inputRules({ rules })
} }

View File

@ -1,33 +1,33 @@
import { splitListItem, liftListItem, sinkListItem } from 'prosemirror-schema-list' import { splitListItem, liftListItem, sinkListItem } from 'prosemirror-schema-list'
import { Schema } from 'prosemirror-model' import { Schema } from 'prosemirror-model'
import { undo, redo } from 'prosemirror-history' import { undo, redo } from 'prosemirror-history'
import { undoInputRule } from 'prosemirror-inputrules' import { undoInputRule } from 'prosemirror-inputrules'
import { import {
toggleMark, toggleMark,
selectParentNode, selectParentNode,
joinUp, joinUp,
joinDown, joinDown,
Command, Command,
} from 'prosemirror-commands' } from 'prosemirror-commands'
export const buildKeymap = (schema: Schema) => { export const buildKeymap = (schema: Schema) => {
const keys = {} const keys = {}
const bind = (key: string, cmd: Command) => keys[key] = cmd const bind = (key: string, cmd: Command) => keys[key] = cmd
bind('Alt-ArrowUp', joinUp) bind('Alt-ArrowUp', joinUp)
bind('Alt-ArrowDown', joinDown) bind('Alt-ArrowDown', joinDown)
bind('Ctrl-z', undo) bind('Ctrl-z', undo)
bind('Ctrl-y', redo) bind('Ctrl-y', redo)
bind('Backspace', undoInputRule) bind('Backspace', undoInputRule)
bind('Escape', selectParentNode) bind('Escape', selectParentNode)
bind('Ctrl-b', toggleMark(schema.marks.strong)) bind('Ctrl-b', toggleMark(schema.marks.strong))
bind('Ctrl-i', toggleMark(schema.marks.em)) bind('Ctrl-i', toggleMark(schema.marks.em))
bind('Ctrl-u', toggleMark(schema.marks.underline)) bind('Ctrl-u', toggleMark(schema.marks.underline))
bind('Ctrl-d', toggleMark(schema.marks.strikethrough)) bind('Ctrl-d', toggleMark(schema.marks.strikethrough))
bind('Enter', splitListItem(schema.nodes.list_item)) bind('Enter', splitListItem(schema.nodes.list_item))
bind('Mod-[', liftListItem(schema.nodes.list_item)) bind('Mod-[', liftListItem(schema.nodes.list_item))
bind('Mod-]', sinkListItem(schema.nodes.list_item)) bind('Mod-]', sinkListItem(schema.nodes.list_item))
return keys return keys
} }

View File

@ -1,5 +1,5 @@
import nodes from './nodes' import nodes from './nodes'
import marks from './marks' import marks from './marks'
export const schemaNodes = nodes export const schemaNodes = nodes
export const schemaMarks = marks export const schemaMarks = marks

View File

@ -1,148 +1,148 @@
import { marks } from 'prosemirror-schema-basic' import { marks } from 'prosemirror-schema-basic'
import { MarkSpec } from 'prosemirror-model' import { MarkSpec } from 'prosemirror-model'
const subscript: MarkSpec = { const subscript: MarkSpec = {
excludes: 'subscript', excludes: 'subscript',
parseDOM: [ parseDOM: [
{ tag: 'sub' }, { tag: 'sub' },
{ {
style: 'vertical-align', style: 'vertical-align',
getAttrs: value => value === 'sub' && null getAttrs: value => value === 'sub' && null
}, },
], ],
toDOM: () => ['sub', 0], toDOM: () => ['sub', 0],
} }
const superscript: MarkSpec = { const superscript: MarkSpec = {
excludes: 'superscript', excludes: 'superscript',
parseDOM: [ parseDOM: [
{ tag: 'sup' }, { tag: 'sup' },
{ {
style: 'vertical-align', style: 'vertical-align',
getAttrs: value => value === 'super' && null getAttrs: value => value === 'super' && null
}, },
], ],
toDOM: () => ['sup', 0], toDOM: () => ['sup', 0],
} }
const strikethrough: MarkSpec = { const strikethrough: MarkSpec = {
parseDOM: [ parseDOM: [
{ tag: 'strike' }, { tag: 'strike' },
{ {
style: 'text-decoration', style: 'text-decoration',
getAttrs: value => value === 'line-through' && null getAttrs: value => value === 'line-through' && null
}, },
{ {
style: 'text-decoration-line', style: 'text-decoration-line',
getAttrs: value => value === 'line-through' && null getAttrs: value => value === 'line-through' && null
}, },
], ],
toDOM: () => ['span', { style: 'text-decoration-line: line-through' }, 0], toDOM: () => ['span', { style: 'text-decoration-line: line-through' }, 0],
} }
const underline: MarkSpec = { const underline: MarkSpec = {
parseDOM: [ parseDOM: [
{ tag: 'u' }, { tag: 'u' },
{ {
style: 'text-decoration', style: 'text-decoration',
getAttrs: value => value === 'underline' && null getAttrs: value => value === 'underline' && null
}, },
{ {
style: 'text-decoration-line', style: 'text-decoration-line',
getAttrs: value => value === 'underline' && null getAttrs: value => value === 'underline' && null
}, },
], ],
toDOM: () => ['span', { style: 'text-decoration: underline' }, 0], toDOM: () => ['span', { style: 'text-decoration: underline' }, 0],
} }
const forecolor: MarkSpec = { const forecolor: MarkSpec = {
attrs: { attrs: {
color: {}, color: {},
}, },
parseDOM: [ parseDOM: [
{ {
style: 'color', style: 'color',
getAttrs: color => color ? { color } : {} getAttrs: color => color ? { color } : {}
}, },
], ],
toDOM: mark => { toDOM: mark => {
const { color } = mark.attrs const { color } = mark.attrs
let style = '' let style = ''
if (color) style += `color: ${color};` if (color) style += `color: ${color};`
return ['span', { style }, 0] return ['span', { style }, 0]
}, },
} }
const backcolor: MarkSpec = { const backcolor: MarkSpec = {
attrs: { attrs: {
backcolor: {}, backcolor: {},
}, },
inline: true, inline: true,
group: 'inline', group: 'inline',
parseDOM: [ parseDOM: [
{ {
tag: 'span[style*=background-color]', tag: 'span[style*=background-color]',
getAttrs: backcolor => backcolor ? { backcolor } : {} getAttrs: backcolor => backcolor ? { backcolor } : {}
}, },
], ],
toDOM: mark => { toDOM: mark => {
const { backcolor } = mark.attrs const { backcolor } = mark.attrs
let style = '' let style = ''
if (backcolor) style += `background-color: ${backcolor};` if (backcolor) style += `background-color: ${backcolor};`
return ['span', { style }, 0] return ['span', { style }, 0]
}, },
} }
const fontsize: MarkSpec = { const fontsize: MarkSpec = {
attrs: { attrs: {
fontsize: {}, fontsize: {},
}, },
inline: true, inline: true,
group: 'inline', group: 'inline',
parseDOM: [ parseDOM: [
{ {
style: 'font-size', style: 'font-size',
getAttrs: fontsize => fontsize ? { fontsize } : {} getAttrs: fontsize => fontsize ? { fontsize } : {}
}, },
], ],
toDOM: mark => { toDOM: mark => {
const { fontsize } = mark.attrs const { fontsize } = mark.attrs
let style = '' let style = ''
if (fontsize) style += `font-size: ${fontsize}` if (fontsize) style += `font-size: ${fontsize}`
return ['span', { style }, 0] return ['span', { style }, 0]
}, },
} }
const fontname: MarkSpec = { const fontname: MarkSpec = {
attrs: { attrs: {
fontname: {}, fontname: {},
}, },
inline: true, inline: true,
group: 'inline', group: 'inline',
parseDOM: [ parseDOM: [
{ {
style: 'font-family', style: 'font-family',
getAttrs: fontname => { getAttrs: fontname => {
return { fontname: fontname && typeof fontname === 'string' ? fontname.replace(/[\"\']/g, '') : '' } return { fontname: fontname && typeof fontname === 'string' ? fontname.replace(/[\"\']/g, '') : '' }
} }
}, },
], ],
toDOM: mark => { toDOM: mark => {
const { fontname } = mark.attrs const { fontname } = mark.attrs
let style = '' let style = ''
if (fontname) style += `font-family: ${fontname}` if (fontname) style += `font-family: ${fontname}`
return ['span', { style }, 0] return ['span', { style }, 0]
}, },
} }
export default { export default {
...marks, ...marks,
subscript, subscript,
superscript, superscript,
strikethrough, strikethrough,
underline, underline,
forecolor, forecolor,
backcolor, backcolor,
fontsize, fontsize,
fontname, fontname,
} }

View File

@ -1,58 +1,58 @@
import { nodes } from 'prosemirror-schema-basic' import { nodes } from 'prosemirror-schema-basic'
import { Node, NodeSpec } from 'prosemirror-model' import { Node, NodeSpec } from 'prosemirror-model'
import { orderedList, bulletList, listItem } from 'prosemirror-schema-list' import { orderedList, bulletList, listItem } from 'prosemirror-schema-list'
const _orderedList: NodeSpec = { const _orderedList: NodeSpec = {
...orderedList, ...orderedList,
content: 'list_item+', content: 'list_item+',
group: 'block', group: 'block',
} }
const _bulletList: NodeSpec = { const _bulletList: NodeSpec = {
...bulletList, ...bulletList,
content: 'list_item+', content: 'list_item+',
group: 'block', group: 'block',
} }
const _listItem: NodeSpec = { const _listItem: NodeSpec = {
...listItem, ...listItem,
content: 'paragraph block*', content: 'paragraph block*',
group: 'block', group: 'block',
} }
const paragraph: NodeSpec = { const paragraph: NodeSpec = {
attrs: { attrs: {
align: { align: {
default: '', default: '',
}, },
}, },
content: 'inline*', content: 'inline*',
group: 'block', group: 'block',
parseDOM: [ parseDOM: [
{ {
tag: 'p', tag: 'p',
getAttrs: dom => { getAttrs: dom => {
const { textAlign } = (dom as HTMLElement).style const { textAlign } = (dom as HTMLElement).style
let align = (dom as HTMLElement).getAttribute('align') || textAlign || '' let align = (dom as HTMLElement).getAttribute('align') || textAlign || ''
align = /(left|right|center|justify)/.test(align) ? align : '' align = /(left|right|center|justify)/.test(align) ? align : ''
return { align } return { align }
} }
} }
], ],
toDOM: (node: Node) => { toDOM: (node: Node) => {
const { align } = node.attrs const { align } = node.attrs
let style = '' let style = ''
if (align && align !== 'left') style += `text-align: ${align};` if (align && align !== 'left') style += `text-align: ${align};`
return ['p', { style }, 0] return ['p', { style }, 0]
}, },
} }
export default { export default {
...nodes, ...nodes,
'ordered_list': _orderedList, 'ordered_list': _orderedList,
'bullet_list': _bulletList, 'bullet_list': _bulletList,
'list_item': _listItem, 'list_item': _listItem,
paragraph, paragraph,
} }

View File

@ -222,7 +222,7 @@ import { computed, defineComponent, onUnmounted, ref, watch } from 'vue'
import { MutationTypes, useStore } from '@/store' import { MutationTypes, useStore } from '@/store'
import { PPTTextElement } from '@/types/slides' import { PPTTextElement } from '@/types/slides'
import emitter, { EmitterEvents } from '@/utils/emitter' import emitter, { EmitterEvents } from '@/utils/emitter'
import { TextAttrs } from '@/prosemirror/utils' import { TextAttrs } from '@/utils/prosemirror/utils'
import { WEB_FONTS } from '@/configs/font' import { WEB_FONTS } from '@/configs/font'
import useHistorySnapshot from '@/hooks/useHistorySnapshot' import useHistorySnapshot from '@/hooks/useHistorySnapshot'

View File

@ -50,12 +50,12 @@ import { EditorView } from 'prosemirror-view'
import { toggleMark, wrapIn, selectAll } from 'prosemirror-commands' import { toggleMark, wrapIn, selectAll } from 'prosemirror-commands'
import { PPTTextElement } from '@/types/slides' import { PPTTextElement } from '@/types/slides'
import { ContextmenuItem } from '@/components/Contextmenu/types' import { ContextmenuItem } from '@/components/Contextmenu/types'
import { initProsemirrorEditor } from '@/prosemirror/' import { initProsemirrorEditor } from '@/utils/prosemirror/'
import { getTextAttrs } from '@/prosemirror/utils' import { getTextAttrs } from '@/utils/prosemirror/utils'
import emitter, { EmitterEvents } from '@/utils/emitter' import emitter, { EmitterEvents } from '@/utils/emitter'
import useElementShadow from '@/views/components/element/hooks/useElementShadow' import useElementShadow from '@/views/components/element/hooks/useElementShadow'
import { alignmentCommand } from '@/prosemirror/commands/setTextAlign' import { alignmentCommand } from '@/utils/prosemirror/commands/setTextAlign'
import { toggleList } from '@/prosemirror/commands/toggleList' import { toggleList } from '@/utils/prosemirror/commands/toggleList'
import useHistorySnapshot from '@/hooks/useHistorySnapshot' import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import ElementOutline from '@/views/components/element/ElementOutline.vue' import ElementOutline from '@/views/components/element/ElementOutline.vue'