mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
fix: 处理文本列表项目符号的字号和颜色问题(#84)
This commit is contained in:
parent
9b8a742a37
commit
23fc06462b
@ -23,7 +23,7 @@
|
||||
|
||||
ul {
|
||||
list-style-type: disc;
|
||||
padding-inline-start: 20px;
|
||||
padding-inline-start: 1.25em;
|
||||
|
||||
li {
|
||||
list-style-type: inherit;
|
||||
@ -33,7 +33,7 @@
|
||||
|
||||
ol {
|
||||
list-style-type: decimal;
|
||||
padding-inline-start: 20px;
|
||||
padding-inline-start: 1.25em;
|
||||
|
||||
li {
|
||||
list-style-type: inherit;
|
||||
|
@ -193,12 +193,12 @@ export default () => {
|
||||
if (styleObj['href']) options.hyperlink = { url: styleObj['href'] }
|
||||
|
||||
if (bulletFlag && styleObj['list-type'] === 'ol') {
|
||||
options.bullet = { type: 'number', indent: 20 * PT_PX_RATIO }
|
||||
options.bullet = { type: 'number', indent: (options.fontSize || 20) * 1.25 }
|
||||
options.paraSpaceBefore = 0.1
|
||||
bulletFlag = false
|
||||
}
|
||||
if (bulletFlag && styleObj['list-type'] === 'ul') {
|
||||
options.bullet = { indent: 20 * PT_PX_RATIO }
|
||||
options.bullet = { indent: (options.fontSize || 20) * 1.25 }
|
||||
options.paraSpaceBefore = 0.1
|
||||
bulletFlag = false
|
||||
}
|
||||
|
31
src/utils/prosemirror/commands/setListStyle.ts
Normal file
31
src/utils/prosemirror/commands/setListStyle.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import type { EditorView } from 'prosemirror-view'
|
||||
import { isList } from '../utils'
|
||||
|
||||
interface Style {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
export const setListStyle = (view: EditorView, style: Style | Style[]) => {
|
||||
const { state } = view
|
||||
const { schema, selection } = state
|
||||
const tr = state.tr.setSelection(selection)
|
||||
|
||||
const { doc } = tr
|
||||
if (!doc) return tr
|
||||
|
||||
const { from, to } = selection
|
||||
doc.nodesBetween(from, to, (node, pos) => {
|
||||
if (isList(node, schema)) {
|
||||
if (from - 3 <= pos && to + 3 >= pos + node.nodeSize) {
|
||||
const styles = Array.isArray(style) ? style : [style]
|
||||
|
||||
for (const style of styles) {
|
||||
tr.setNodeAttribute(pos, style.key, style.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
view.dispatch(tr)
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import type { Schema } from 'prosemirror-model'
|
||||
import { type Transaction, TextSelection, AllSelection } from 'prosemirror-state'
|
||||
import type { EditorView } from 'prosemirror-view'
|
||||
import { isList } from './toggleList'
|
||||
import { isList } from '../utils'
|
||||
|
||||
type IndentKey = 'indent' | 'textIndent'
|
||||
|
||||
|
@ -1,16 +1,18 @@
|
||||
import { wrapInList, liftListItem } from 'prosemirror-schema-list'
|
||||
import type { Schema, Node, NodeType } from 'prosemirror-model'
|
||||
import type { Node, NodeType } from 'prosemirror-model'
|
||||
import type { Transaction, EditorState } from 'prosemirror-state'
|
||||
import { findParentNode } from '../utils'
|
||||
import { findParentNode, isList } from '../utils'
|
||||
|
||||
export const isList = (node: Node, schema: Schema) => {
|
||||
return (
|
||||
node.type === schema.nodes.bullet_list ||
|
||||
node.type === schema.nodes.ordered_list
|
||||
)
|
||||
interface Attr {
|
||||
[key: string]: number | string
|
||||
}
|
||||
|
||||
export const toggleList = (listType: NodeType, itemType: NodeType, listStyleType?: string) => {
|
||||
interface TextStyleAttr {
|
||||
color?: string
|
||||
fontsize?: string
|
||||
}
|
||||
|
||||
export const toggleList = (listType: NodeType, itemType: NodeType, listStyleType: string, textStyleAttr: TextStyleAttr = {}) => {
|
||||
return (state: EditorState, dispatch: (tr: Transaction) => void) => {
|
||||
const { schema, selection } = state
|
||||
const { $from, $to } = selection
|
||||
@ -27,14 +29,14 @@ export const toggleList = (listType: NodeType, itemType: NodeType, listStyleType
|
||||
|
||||
if (isList(parentList.node, schema) && listType.validContent(parentList.node.content)) {
|
||||
const { tr } = state
|
||||
if (listStyleType) {
|
||||
const nodeAttrs = {
|
||||
|
||||
const nodeAttrs: Attr = {
|
||||
...parentList.node.attrs,
|
||||
listStyleType: listStyleType,
|
||||
...textStyleAttr,
|
||||
}
|
||||
if (listStyleType) nodeAttrs.listStyleType = listStyleType
|
||||
|
||||
tr.setNodeMarkup(parentList.pos, listType, nodeAttrs)
|
||||
}
|
||||
else tr.setNodeMarkup(parentList.pos, listType)
|
||||
|
||||
if (dispatch) dispatch(tr)
|
||||
|
||||
@ -42,7 +44,11 @@ export const toggleList = (listType: NodeType, itemType: NodeType, listStyleType
|
||||
}
|
||||
}
|
||||
|
||||
if (listStyleType) return wrapInList(listType, { listStyleType })(state, dispatch)
|
||||
return wrapInList(listType)(state, dispatch)
|
||||
const nodeAttrs: Attr = {
|
||||
...textStyleAttr,
|
||||
}
|
||||
if (listStyleType) nodeAttrs.listStyleType = listStyleType
|
||||
|
||||
return wrapInList(listType, nodeAttrs)(state, dispatch)
|
||||
}
|
||||
}
|
@ -14,6 +14,12 @@ const orderedList: NodeSpec = {
|
||||
listStyleType: {
|
||||
default: '',
|
||||
},
|
||||
fontsize: {
|
||||
default: '',
|
||||
},
|
||||
color: {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
content: 'list_item+',
|
||||
group: 'block',
|
||||
@ -24,17 +30,21 @@ const orderedList: NodeSpec = {
|
||||
const order = ((dom as HTMLElement).hasAttribute('start') ? (dom as HTMLElement).getAttribute('start') : 1) || 1
|
||||
const attr: Attr = { order: +order }
|
||||
|
||||
const { listStyleType } = (dom as HTMLElement).style
|
||||
const { listStyleType, fontSize, color } = (dom as HTMLElement).style
|
||||
if (listStyleType) attr['listStyleType'] = listStyleType
|
||||
if (fontSize) attr['fontsize'] = fontSize
|
||||
if (color) attr['color'] = color
|
||||
|
||||
return attr
|
||||
}
|
||||
}
|
||||
],
|
||||
toDOM: (node: Node) => {
|
||||
const { order, listStyleType } = node.attrs
|
||||
const { order, listStyleType, fontsize, color } = node.attrs
|
||||
let style = ''
|
||||
if (listStyleType) style += `list-style-type: ${listStyleType};`
|
||||
if (fontsize) style += `font-size: ${fontsize};`
|
||||
if (color) style += `color: ${color};`
|
||||
|
||||
const attr: Attr = { style }
|
||||
if (order !== 1) attr['start'] = order
|
||||
@ -49,6 +59,12 @@ const bulletList: NodeSpec = {
|
||||
listStyleType: {
|
||||
default: '',
|
||||
},
|
||||
fontsize: {
|
||||
default: '',
|
||||
},
|
||||
color: {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
content: 'list_item+',
|
||||
group: 'block',
|
||||
@ -56,15 +72,23 @@ const bulletList: NodeSpec = {
|
||||
{
|
||||
tag: 'ul',
|
||||
getAttrs: dom => {
|
||||
const { listStyleType } = (dom as HTMLElement).style
|
||||
return listStyleType ? { listStyleType } : {}
|
||||
const attr: Attr = {}
|
||||
|
||||
const { listStyleType, fontSize, color } = (dom as HTMLElement).style
|
||||
if (listStyleType) attr['listStyleType'] = listStyleType
|
||||
if (fontSize) attr['fontsize'] = fontSize
|
||||
if (color) attr['color'] = color
|
||||
|
||||
return attr
|
||||
}
|
||||
}
|
||||
],
|
||||
toDOM: (node: Node) => {
|
||||
const { listStyleType } = node.attrs
|
||||
const { listStyleType, fontsize, color } = node.attrs
|
||||
let style = ''
|
||||
if (listStyleType) style += `list-style-type: ${listStyleType};`
|
||||
if (fontsize) style += `font-size: ${fontsize};`
|
||||
if (color) style += `color: ${color};`
|
||||
|
||||
return ['ul', { style }, 0]
|
||||
},
|
||||
|
@ -1,8 +1,15 @@
|
||||
import type { Node, NodeType, ResolvedPos, Mark, MarkType } from 'prosemirror-model'
|
||||
import type { Node, NodeType, ResolvedPos, Mark, MarkType, Schema } from 'prosemirror-model'
|
||||
import type { EditorState, Selection } from 'prosemirror-state'
|
||||
import type { EditorView } from 'prosemirror-view'
|
||||
import { selectAll } from 'prosemirror-commands'
|
||||
|
||||
export const isList = (node: Node, schema: Schema) => {
|
||||
return (
|
||||
node.type === schema.nodes.bullet_list ||
|
||||
node.type === schema.nodes.ordered_list
|
||||
)
|
||||
}
|
||||
|
||||
export const autoSelectAll = (view: EditorView) => {
|
||||
const { empty } = view.state.selection
|
||||
if (empty) selectAll(view.state, view.dispatch)
|
||||
@ -113,12 +120,20 @@ export const isActiveOfParentNodeType = (nodeType: string, state: EditorState) =
|
||||
return !!findParentNodeOfType(node)(state.selection)
|
||||
}
|
||||
|
||||
export const getLastTextNode = (node: Node | null): Node | null => {
|
||||
if (!node) return null
|
||||
if (node.type.name === 'text') return node
|
||||
if (!node.lastChild) return null
|
||||
|
||||
return getLastTextNode(node.lastChild)
|
||||
}
|
||||
|
||||
export const getMarkAttrs = (view: EditorView) => {
|
||||
const { selection, doc } = view.state
|
||||
const { from } = selection
|
||||
|
||||
let node = doc.nodeAt(from) || doc.nodeAt(from - 1)
|
||||
if (node?.lastChild) node = node.lastChild
|
||||
node = getLastTextNode(node)
|
||||
|
||||
return node?.marks || []
|
||||
}
|
||||
|
@ -146,11 +146,11 @@ const execCommand = (command: string, value?: string) => {
|
||||
}
|
||||
else if (command === 'bulletList') {
|
||||
const { bullet_list: bulletList, list_item: listItem } = editorView.state.schema.nodes
|
||||
toggleList(bulletList, listItem)(editorView.state, editorView.dispatch)
|
||||
toggleList(bulletList, listItem, '')(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if (command === 'orderedList') {
|
||||
const { ordered_list: orderedList, list_item: listItem } = editorView.state.schema.nodes
|
||||
toggleList(orderedList, listItem)(editorView.state, editorView.dispatch)
|
||||
toggleList(orderedList, listItem, '')(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if (command === 'clear') {
|
||||
autoSelectAll(editorView)
|
||||
|
@ -20,6 +20,7 @@ import emitter, { EmitterEvents, type RichTextAction, type RichTextCommand } fro
|
||||
import { alignmentCommand } from '@/utils/prosemirror/commands/setTextAlign'
|
||||
import { indentCommand, textIndentCommand } from '@/utils/prosemirror/commands/setTextIndent'
|
||||
import { toggleList } from '@/utils/prosemirror/commands/toggleList'
|
||||
import { setListStyle } from '@/utils/prosemirror/commands/setListStyle'
|
||||
import type { TextFormatPainterKeys } from '@/types/edit'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
@ -42,7 +43,7 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const mainStore = useMainStore()
|
||||
const { handleElementId, textFormatPainter } = storeToRefs(mainStore)
|
||||
const { handleElementId, textFormatPainter, richTextAttrs } = storeToRefs(mainStore)
|
||||
|
||||
const editorViewRef = ref<HTMLElement>()
|
||||
let editorView: EditorView
|
||||
@ -115,6 +116,7 @@ const execCommand = ({ target, action }: RichTextCommand) => {
|
||||
const mark = editorView.state.schema.marks.fontsize.create({ fontsize: item.value })
|
||||
autoSelectAll(editorView)
|
||||
addMark(editorView, mark)
|
||||
setListStyle(editorView, { key: 'fontsize', value: item.value })
|
||||
}
|
||||
else if (item.command === 'fontsize-add') {
|
||||
const step = item.value ? +item.value : 2
|
||||
@ -122,6 +124,7 @@ const execCommand = ({ target, action }: RichTextCommand) => {
|
||||
const fontsize = getFontsize(editorView) + step + 'px'
|
||||
const mark = editorView.state.schema.marks.fontsize.create({ fontsize })
|
||||
addMark(editorView, mark)
|
||||
setListStyle(editorView, { key: 'fontsize', value: fontsize })
|
||||
}
|
||||
else if (item.command === 'fontsize-reduce') {
|
||||
const step = item.value ? +item.value : 2
|
||||
@ -130,11 +133,13 @@ const execCommand = ({ target, action }: RichTextCommand) => {
|
||||
if (fontsize < 12) fontsize = 12
|
||||
const mark = editorView.state.schema.marks.fontsize.create({ fontsize: fontsize + 'px' })
|
||||
addMark(editorView, mark)
|
||||
setListStyle(editorView, { key: 'fontsize', value: fontsize + 'px' })
|
||||
}
|
||||
else if (item.command === 'color' && item.value) {
|
||||
const mark = editorView.state.schema.marks.forecolor.create({ color: item.value })
|
||||
autoSelectAll(editorView)
|
||||
addMark(editorView, mark)
|
||||
setListStyle(editorView, { key: 'color', value: item.value })
|
||||
}
|
||||
else if (item.command === 'backcolor' && item.value) {
|
||||
const mark = editorView.state.schema.marks.backcolor.create({ backcolor: item.value })
|
||||
@ -183,17 +188,29 @@ const execCommand = ({ target, action }: RichTextCommand) => {
|
||||
else if (item.command === 'bulletList') {
|
||||
const listStyleType = item.value || ''
|
||||
const { bullet_list: bulletList, list_item: listItem } = editorView.state.schema.nodes
|
||||
toggleList(bulletList, listItem, listStyleType)(editorView.state, editorView.dispatch)
|
||||
const textStyle = {
|
||||
color: richTextAttrs.value.color,
|
||||
fontsize: richTextAttrs.value.fontsize
|
||||
}
|
||||
toggleList(bulletList, listItem, listStyleType, textStyle)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if (item.command === 'orderedList') {
|
||||
const listStyleType = item.value || ''
|
||||
const { ordered_list: orderedList, list_item: listItem } = editorView.state.schema.nodes
|
||||
toggleList(orderedList, listItem, listStyleType)(editorView.state, editorView.dispatch)
|
||||
const textStyle = {
|
||||
color: richTextAttrs.value.color,
|
||||
fontsize: richTextAttrs.value.fontsize
|
||||
}
|
||||
toggleList(orderedList, listItem, listStyleType, textStyle)(editorView.state, editorView.dispatch)
|
||||
}
|
||||
else if (item.command === 'clear') {
|
||||
autoSelectAll(editorView)
|
||||
const { $from, $to } = editorView.state.selection
|
||||
editorView.dispatch(editorView.state.tr.removeMark($from.pos, $to.pos))
|
||||
setListStyle(editorView, [
|
||||
{ key: 'fontsize', value: '' },
|
||||
{ key: 'color', value: '' },
|
||||
])
|
||||
}
|
||||
else if (item.command === 'link') {
|
||||
const markType = editorView.state.schema.marks.link
|
||||
|
Loading…
x
Reference in New Issue
Block a user