feat: 支持文本缩进

This commit is contained in:
pipipi-pikachu 2022-05-28 00:02:41 +08:00
parent 7b07629a58
commit 533b76ffee
8 changed files with 129 additions and 4 deletions

View File

@ -54,6 +54,28 @@
font-style: italic;
border-left: 5px solid #ccc;
}
[data-indent='1'] {
padding-left: 48px;
}
[data-indent='2'] {
padding-left: 96px;
}
[data-indent='3'] {
padding-left: 144px;
}
[data-indent='4'] {
padding-left: 192px;
}
[data-indent='5'] {
padding-left: 240px;
}
[data-indent='6'] {
padding-left: 288px;
}
[data-indent='7'] {
padding-left: 336px;
}
}
.ProseMirror-selectednode {

View File

@ -74,6 +74,7 @@ export default () => {
const formatHTML = (html: string) => {
const ast = toAST(html)
let bulletFlag = false
let indent = 0
const slices: pptxgen.TextProps[] = []
const parse = (obj: AST[], baseStyleObj = {}) => {
@ -124,6 +125,12 @@ export default () => {
if (item.tagName === 'li') {
bulletFlag = true
}
if (item.tagName === 'p') {
if ('attributes' in item) {
const dataIndentAttr = item.attributes.find(attr => attr.key === 'data-indent')
if (dataIndentAttr && dataIndentAttr.value) indent = +dataIndentAttr.value
}
}
}
if ('tagName' in item && item.tagName === 'br') {
@ -184,6 +191,10 @@ export default () => {
options.paraSpaceBefore = 0.1
bulletFlag = false
}
if (indent) {
options.indentLevel = indent
indent = 0
}
slices.push({ text, options })
}

View File

@ -98,6 +98,8 @@ import {
Magic,
HighLight,
Share,
IndentLeft,
IndentRight,
} from '@icon-park/vue-next'
const icons = {
@ -197,6 +199,8 @@ const icons = {
Magic,
HighLight,
Share,
IndentLeft,
IndentRight,
}
export default {

View File

@ -0,0 +1,66 @@
import { Schema } from 'prosemirror-model'
import { TextSelection, AllSelection, Transaction } from 'prosemirror-state'
import { EditorView } from 'prosemirror-view'
import { isList } from './toggleList'
function setNodeIndentMarkup(tr: Transaction, pos: number, delta: number): Transaction {
if (!tr.doc) return tr
const node = tr.doc.nodeAt(pos)
if (!node) return tr
const minIndent = 0
const maxIndent = 7
let indent = (node.attrs.indent || 0) + delta
if (indent < minIndent) indent = minIndent
if (indent > maxIndent) indent = maxIndent
if (indent === node.attrs.indent) return tr
const nodeAttrs = {
...node.attrs,
indent,
}
return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks)
}
const setTextIndent = (tr: Transaction, schema: Schema, delta: number): Transaction => {
const { selection, doc } = tr
if (!selection || !doc) return tr
if (!(selection instanceof TextSelection || selection instanceof AllSelection)) return tr
const { from, to } = selection
doc.nodesBetween(from, to, (node, pos) => {
const nodeType = node.type
if (nodeType.name === 'paragraph' || nodeType.name === 'blockquote') {
tr = setNodeIndentMarkup(tr, pos, delta)
return false
}
else if (isList(node, schema)) return false
return true
})
return tr
}
export const indentCommand = (view: EditorView, delta: number) => {
const { state } = view
const { schema, selection } = state
const tr = setTextIndent(
state.tr.setSelection(selection),
schema,
delta,
)
if (tr.docChanged) {
view.dispatch(tr)
return true
}
return false
}

View File

@ -3,7 +3,7 @@ import { Schema, Node, NodeType } from 'prosemirror-model'
import { Transaction, EditorState } from 'prosemirror-state'
import { findParentNode } from '../utils'
const isList = (node: Node, schema: Schema) => {
export const isList = (node: Node, schema: Schema) => {
return (
node.type === schema.nodes.bullet_list ||
node.type === schema.nodes.ordered_list

View File

@ -25,6 +25,9 @@ const paragraph: NodeSpec = {
align: {
default: '',
},
indent: {
default: 0,
},
},
content: 'inline*',
group: 'block',
@ -33,19 +36,25 @@ const paragraph: NodeSpec = {
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 }
const indent = +((dom as HTMLElement).getAttribute('data-indent') || 0)
return { align, indent }
}
}
],
toDOM: (node: Node) => {
const { align } = node.attrs
const { align, indent } = node.attrs
let style = ''
if (align && align !== 'left') style += `text-align: ${align};`
return ['p', { style }, 0]
const attr = { style }
if (indent) attr['data-indent'] = indent
return ['p', attr, 0]
},
}

View File

@ -209,6 +209,15 @@
</Tooltip>
</CheckboxButtonGroup>
<ButtonGroup class="row">
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="减小缩进">
<Button style="flex: 1;" @click="emitRichTextCommand('indent', '-1')"><IconIndentLeft /></Button>
</Tooltip>
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="增大缩进">
<Button style="flex: 1;" @click="emitRichTextCommand('indent', '+1')"><IconIndentRight /></Button>
</Tooltip>
</ButtonGroup>
<Divider />
<div class="row">

View File

@ -16,6 +16,7 @@ import { initProsemirrorEditor, createDocument } from '@/utils/prosemirror'
import { findNodesWithSameMark, getTextAttrs, autoSelectAll, addMark, markActive, getFontsize } from '@/utils/prosemirror/utils'
import emitter, { EmitterEvents, RichTextCommand } from '@/utils/emitter'
import { alignmentCommand } from '@/utils/prosemirror/commands/setTextAlign'
import { indentCommand } from '@/utils/prosemirror/commands/setTextIndent'
import { toggleList } from '@/utils/prosemirror/commands/toggleList'
export default defineComponent({
@ -192,6 +193,9 @@ export default defineComponent({
else if (item.command === 'align' && item.value) {
alignmentCommand(editorView, item.value)
}
else if (item.command === 'indent' && item.value) {
indentCommand(editorView, +item.value)
}
else if (item.command === 'bulletList') {
const { bullet_list: bulletList, list_item: listItem } = editorView.state.schema.nodes
toggleList(bulletList, listItem)(editorView.state, editorView.dispatch)