fix: 处理文本列表项目符号的字号和颜色问题(#84)

This commit is contained in:
pipipi-pikachu 2023-12-06 23:07:05 +08:00
parent 9b8a742a37
commit 23fc06462b
9 changed files with 127 additions and 34 deletions

View File

@ -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;

View File

@ -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
}

View 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)
}

View File

@ -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'

View File

@ -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 = {
...parentList.node.attrs,
listStyleType: listStyleType,
}
tr.setNodeMarkup(parentList.pos, listType, nodeAttrs)
const nodeAttrs: Attr = {
...parentList.node.attrs,
...textStyleAttr,
}
else tr.setNodeMarkup(parentList.pos, listType)
if (listStyleType) nodeAttrs.listStyleType = listStyleType
tr.setNodeMarkup(parentList.pos, listType, nodeAttrs)
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)
}
}

View File

@ -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]
},

View File

@ -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 || []
}

View File

@ -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)

View File

@ -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