mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 文字项目符号支持多种样式
This commit is contained in:
parent
f4af2405dc
commit
10c6b3daa3
@ -48,8 +48,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ol,
|
ol,
|
||||||
ul,
|
ul {
|
||||||
li {
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
padding-inline-start: 20px;
|
padding-inline-start: 20px;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
list-style-type: disc;
|
list-style-type: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +38,7 @@
|
|||||||
padding-inline-start: 20px;
|
padding-inline-start: 20px;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
list-style-type: decimal;
|
list-style-type: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ export const isList = (node: Node, schema: Schema) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const toggleList = (listType: NodeType, itemType: NodeType) => {
|
export const toggleList = (listType: NodeType, itemType: NodeType, listStyleType?: string) => {
|
||||||
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
|
||||||
@ -21,13 +21,20 @@ export const toggleList = (listType: NodeType, itemType: NodeType) => {
|
|||||||
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 && !listStyleType) {
|
||||||
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)
|
if (listStyleType) {
|
||||||
|
const nodeAttrs = {
|
||||||
|
...parentList.node.attrs,
|
||||||
|
listStyleType: listStyleType,
|
||||||
|
}
|
||||||
|
tr.setNodeMarkup(parentList.pos, listType, nodeAttrs)
|
||||||
|
}
|
||||||
|
else tr.setNodeMarkup(parentList.pos, listType)
|
||||||
|
|
||||||
if (dispatch) dispatch(tr)
|
if (dispatch) dispatch(tr)
|
||||||
|
|
||||||
@ -35,6 +42,7 @@ export const toggleList = (listType: NodeType, itemType: NodeType) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (listStyleType) return wrapInList(listType, { listStyleType })(state, dispatch)
|
||||||
return wrapInList(listType)(state, dispatch)
|
return wrapInList(listType)(state, dispatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,21 +1,73 @@
|
|||||||
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 { listItem as _listItem } from 'prosemirror-schema-list'
|
||||||
|
|
||||||
const _orderedList: NodeSpec = {
|
const orderedList: NodeSpec = {
|
||||||
...orderedList,
|
attrs: {
|
||||||
|
order: {
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
listStyleType: {
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
content: 'list_item+',
|
content: 'list_item+',
|
||||||
group: 'block',
|
group: 'block',
|
||||||
|
parseDOM: [
|
||||||
|
{
|
||||||
|
tag: 'ol',
|
||||||
|
getAttrs: dom => {
|
||||||
|
const order = ((dom as HTMLElement).hasAttribute('start') ? (dom as HTMLElement).getAttribute('start') : 1) || 1
|
||||||
|
const attr = { order: +order }
|
||||||
|
|
||||||
|
const { listStyleType } = (dom as HTMLElement).style
|
||||||
|
if (listStyleType) attr['listStyleType'] = listStyleType
|
||||||
|
|
||||||
|
return attr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toDOM: (node: Node) => {
|
||||||
|
const { order, listStyleType } = node.attrs
|
||||||
|
let style = ''
|
||||||
|
if (listStyleType) style += `list-style-type: ${listStyleType};`
|
||||||
|
|
||||||
|
const attr = { style }
|
||||||
|
if (order !== 1) attr['start'] = order
|
||||||
|
|
||||||
|
|
||||||
|
return ['ol', attr, 0]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const _bulletList: NodeSpec = {
|
const bulletList: NodeSpec = {
|
||||||
...bulletList,
|
attrs: {
|
||||||
|
listStyleType: {
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
content: 'list_item+',
|
content: 'list_item+',
|
||||||
group: 'block',
|
group: 'block',
|
||||||
|
parseDOM: [
|
||||||
|
{
|
||||||
|
tag: 'ul',
|
||||||
|
getAttrs: dom => {
|
||||||
|
const { listStyleType } = (dom as HTMLElement).style
|
||||||
|
return listStyleType ? { listStyleType } : {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toDOM: (node: Node) => {
|
||||||
|
const { listStyleType } = node.attrs
|
||||||
|
let style = ''
|
||||||
|
if (listStyleType) style += `list-style-type: ${listStyleType};`
|
||||||
|
|
||||||
|
return ['ul', { style }, 0]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const _listItem: NodeSpec = {
|
const listItem: NodeSpec = {
|
||||||
...listItem,
|
..._listItem,
|
||||||
content: 'paragraph block*',
|
content: 'paragraph block*',
|
||||||
group: 'block',
|
group: 'block',
|
||||||
}
|
}
|
||||||
@ -63,8 +115,8 @@ const { hard_break, ...otherNodes } = nodes
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
...otherNodes,
|
...otherNodes,
|
||||||
'ordered_list': _orderedList,
|
'ordered_list': orderedList,
|
||||||
'bullet_list': _bulletList,
|
'bullet_list': bulletList,
|
||||||
'list_item': _listItem,
|
'list_item': listItem,
|
||||||
paragraph,
|
paragraph,
|
||||||
}
|
}
|
||||||
|
@ -200,22 +200,57 @@
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
<CheckboxButtonGroup class="row">
|
<div class="row">
|
||||||
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="项目符号">
|
<ButtonGroup style="flex: 15;">
|
||||||
<CheckboxButton
|
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="项目符号">
|
||||||
style="flex: 1;"
|
<Button
|
||||||
:checked="richTextAttrs.bulletList"
|
:type="richTextAttrs.bulletList ? 'primary' : 'default'"
|
||||||
@click="emitRichTextCommand('bulletList')"
|
style="flex: 1;"
|
||||||
><IconList /></CheckboxButton>
|
@click="emitRichTextCommand('bulletList')"
|
||||||
</Tooltip>
|
><IconList /></Button>
|
||||||
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="编号">
|
</Tooltip>
|
||||||
<CheckboxButton
|
<Popover trigger="click" v-model:visible="bulletListPanelVisible">
|
||||||
style="flex: 1;"
|
<template #content>
|
||||||
:checked="richTextAttrs.orderedList"
|
<div class="list-wrap">
|
||||||
@click="emitRichTextCommand('orderedList')"
|
<ul class="list"
|
||||||
><IconOrderedList /></CheckboxButton>
|
v-for="item in bulletListStyleTypeOption"
|
||||||
</Tooltip>
|
:key="item"
|
||||||
</CheckboxButtonGroup>
|
:style="{ listStyleType: item }"
|
||||||
|
@click="emitRichTextCommand('bulletList', item)"
|
||||||
|
>
|
||||||
|
<li class="list-item" v-for="key in 3" :key="key"><span></span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Button class="popover-btn"><IconDown /></Button>
|
||||||
|
</Popover>
|
||||||
|
</ButtonGroup>
|
||||||
|
<div style="flex: 1;"></div>
|
||||||
|
<ButtonGroup style="flex: 15;">
|
||||||
|
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="编号">
|
||||||
|
<Button
|
||||||
|
:type="richTextAttrs.orderedList ? 'primary' : 'default'"
|
||||||
|
style="flex: 1;"
|
||||||
|
@click="emitRichTextCommand('orderedList')"
|
||||||
|
><IconOrderedList /></Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Popover trigger="click" v-model:visible="orderedListPanelVisible">
|
||||||
|
<template #content>
|
||||||
|
<div class="list-wrap">
|
||||||
|
<ul class="list"
|
||||||
|
v-for="item in orderedListStyleTypeOption"
|
||||||
|
:key="item"
|
||||||
|
:style="{ listStyleType: item }"
|
||||||
|
@click="emitRichTextCommand('orderedList', item)"
|
||||||
|
>
|
||||||
|
<li class="list-item" v-for="key in 3" :key="key"><span></span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Button class="popover-btn"><IconDown /></Button>
|
||||||
|
</Popover>
|
||||||
|
</ButtonGroup>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ButtonGroup class="row">
|
<ButtonGroup class="row">
|
||||||
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="减小缩进">
|
<Tooltip :mouseLeaveDelay="0" :mouseEnterDelay="0.5" title="减小缩进">
|
||||||
@ -383,6 +418,12 @@ const updateElement = (props: Partial<PPTTextElement>) => {
|
|||||||
addHistorySnapshot()
|
addHistorySnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bulletListPanelVisible = ref(false)
|
||||||
|
const orderedListPanelVisible = ref(false)
|
||||||
|
|
||||||
|
const bulletListStyleTypeOption = ref(['disc', 'circle', 'square'])
|
||||||
|
const orderedListStyleTypeOption = ref(['decimal', 'lower-roman', 'upper-roman', 'lower-alpha', 'upper-alpha', 'lower-greek'])
|
||||||
|
|
||||||
const fill = ref<string>('#000')
|
const fill = ref<string>('#000')
|
||||||
const lineHeight = ref<number>()
|
const lineHeight = ref<number>()
|
||||||
const wordSpace = ref<number>()
|
const wordSpace = ref<number>()
|
||||||
@ -515,4 +556,55 @@ const updateLink = (link?: string) => {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-wrap {
|
||||||
|
width: 176px;
|
||||||
|
color: #666;
|
||||||
|
padding: 8px;
|
||||||
|
margin: -12px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
background-color: $lightGray;
|
||||||
|
padding: 4px 4px 4px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:not(:nth-child(3n)) {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(4),
|
||||||
|
&:nth-child(5),
|
||||||
|
&:nth-child(6) {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $themeColor;
|
||||||
|
|
||||||
|
span {
|
||||||
|
background-color: $themeColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-item {
|
||||||
|
width: 24px;
|
||||||
|
height: 12px;
|
||||||
|
position: relative;
|
||||||
|
top: -5px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.popover-btn {
|
||||||
|
padding: 0 3px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -190,12 +190,14 @@ const execCommand = ({ target, action }: RichTextCommand) => {
|
|||||||
indentCommand(editorView, +item.value)
|
indentCommand(editorView, +item.value)
|
||||||
}
|
}
|
||||||
else if (item.command === 'bulletList') {
|
else if (item.command === 'bulletList') {
|
||||||
|
const listStyleType = item.value || ''
|
||||||
const { bullet_list: bulletList, list_item: listItem } = editorView.state.schema.nodes
|
const { bullet_list: bulletList, list_item: listItem } = editorView.state.schema.nodes
|
||||||
toggleList(bulletList, listItem)(editorView.state, editorView.dispatch)
|
toggleList(bulletList, listItem, listStyleType)(editorView.state, editorView.dispatch)
|
||||||
}
|
}
|
||||||
else if (item.command === 'orderedList') {
|
else if (item.command === 'orderedList') {
|
||||||
|
const listStyleType = item.value || ''
|
||||||
const { ordered_list: orderedList, list_item: listItem } = editorView.state.schema.nodes
|
const { ordered_list: orderedList, list_item: listItem } = editorView.state.schema.nodes
|
||||||
toggleList(orderedList, listItem)(editorView.state, editorView.dispatch)
|
toggleList(orderedList, listItem, listStyleType)(editorView.state, editorView.dispatch)
|
||||||
}
|
}
|
||||||
else if (item.command === 'clear') {
|
else if (item.command === 'clear') {
|
||||||
autoSelectAll(editorView)
|
autoSelectAll(editorView)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user