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 方法
├── mocks // mocks 数据
├── plugins // 自定义的 Vue 插件
├── prosemirror // ProseMirror 富文本编辑器相关的文件
├── types // 类型定义文件
├── store // Vuex store参考https://vuex.vuejs.org/zh/guide/
├── utils // 通用的工具方法

View File

@ -1,62 +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)
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)
}

View File

@ -1,40 +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)
}
}
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)
}
}

View File

@ -1,20 +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(),
]
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(),
]
}

View File

@ -1,38 +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 })
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 })
}

View File

@ -1,33 +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
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
}

View File

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

View File

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

View File

@ -1,58 +1,58 @@
import { nodes } from 'prosemirror-schema-basic'
import { Node, NodeSpec } from 'prosemirror-model'
import { orderedList, bulletList, listItem } from 'prosemirror-schema-list'
const _orderedList: NodeSpec = {
...orderedList,
content: 'list_item+',
group: 'block',
}
const _bulletList: NodeSpec = {
...bulletList,
content: 'list_item+',
group: 'block',
}
const _listItem: NodeSpec = {
...listItem,
content: 'paragraph block*',
group: 'block',
}
const paragraph: NodeSpec = {
attrs: {
align: {
default: '',
},
},
content: 'inline*',
group: 'block',
parseDOM: [
{
tag: 'p',
getAttrs: dom => {
const { textAlign } = (dom as HTMLElement).style
let align = (dom as HTMLElement).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,
'ordered_list': _orderedList,
'bullet_list': _bulletList,
'list_item': _listItem,
paragraph,
}
import { nodes } from 'prosemirror-schema-basic'
import { Node, NodeSpec } from 'prosemirror-model'
import { orderedList, bulletList, listItem } from 'prosemirror-schema-list'
const _orderedList: NodeSpec = {
...orderedList,
content: 'list_item+',
group: 'block',
}
const _bulletList: NodeSpec = {
...bulletList,
content: 'list_item+',
group: 'block',
}
const _listItem: NodeSpec = {
...listItem,
content: 'paragraph block*',
group: 'block',
}
const paragraph: NodeSpec = {
attrs: {
align: {
default: '',
},
},
content: 'inline*',
group: 'block',
parseDOM: [
{
tag: 'p',
getAttrs: dom => {
const { textAlign } = (dom as HTMLElement).style
let align = (dom as HTMLElement).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,
'ordered_list': _orderedList,
'bullet_list': _bulletList,
'list_item': _listItem,
paragraph,
}

View File

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

View File

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