diff --git a/src/assets/styles/global.scss b/src/assets/styles/global.scss index 1b39dae8..c1cd7580 100644 --- a/src/assets/styles/global.scss +++ b/src/assets/styles/global.scss @@ -48,8 +48,7 @@ body { } ol, -ul, -li { +ul { list-style: none; } diff --git a/src/assets/styles/prosemirror.scss b/src/assets/styles/prosemirror.scss index 4da2cd42..3d4247bb 100644 --- a/src/assets/styles/prosemirror.scss +++ b/src/assets/styles/prosemirror.scss @@ -29,7 +29,7 @@ padding-inline-start: 20px; li { - list-style-type: disc; + list-style-type: inherit; } } @@ -38,7 +38,7 @@ padding-inline-start: 20px; li { - list-style-type: decimal; + list-style-type: inherit; } } diff --git a/src/utils/prosemirror/commands/toggleList.ts b/src/utils/prosemirror/commands/toggleList.ts index 1f621619..5cdd17ca 100644 --- a/src/utils/prosemirror/commands/toggleList.ts +++ b/src/utils/prosemirror/commands/toggleList.ts @@ -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) => { const { schema, selection } = state const { $from, $to } = selection @@ -21,13 +21,20 @@ export const toggleList = (listType: NodeType, itemType: NodeType) => { const parentList = findParentNode((node: Node) => isList(node, schema))(selection) 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) } if (isList(parentList.node, schema) && listType.validContent(parentList.node.content)) { 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) @@ -35,6 +42,7 @@ export const toggleList = (listType: NodeType, itemType: NodeType) => { } } + if (listStyleType) return wrapInList(listType, { listStyleType })(state, dispatch) return wrapInList(listType)(state, dispatch) } -} +} \ No newline at end of file diff --git a/src/utils/prosemirror/schema/nodes.ts b/src/utils/prosemirror/schema/nodes.ts index ae90bda7..0ba6f4fa 100644 --- a/src/utils/prosemirror/schema/nodes.ts +++ b/src/utils/prosemirror/schema/nodes.ts @@ -1,21 +1,73 @@ import { nodes } from 'prosemirror-schema-basic' 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 = { - ...orderedList, +const orderedList: NodeSpec = { + attrs: { + order: { + default: 1, + }, + listStyleType: { + default: '', + }, + }, content: 'list_item+', 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 = { - ...bulletList, +const bulletList: NodeSpec = { + attrs: { + listStyleType: { + default: '', + }, + }, content: 'list_item+', 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 = { - ...listItem, +const listItem: NodeSpec = { + ..._listItem, content: 'paragraph block*', group: 'block', } @@ -63,8 +115,8 @@ const { hard_break, ...otherNodes } = nodes export default { ...otherNodes, - 'ordered_list': _orderedList, - 'bullet_list': _bulletList, - 'list_item': _listItem, + 'ordered_list': orderedList, + 'bullet_list': bulletList, + 'list_item': listItem, paragraph, } diff --git a/src/views/Editor/Toolbar/ElementStylePanel/TextStylePanel.vue b/src/views/Editor/Toolbar/ElementStylePanel/TextStylePanel.vue index 7aecd7e7..36b3dc1b 100644 --- a/src/views/Editor/Toolbar/ElementStylePanel/TextStylePanel.vue +++ b/src/views/Editor/Toolbar/ElementStylePanel/TextStylePanel.vue @@ -200,22 +200,57 @@ - - - - - - - - +
+ + + + + + + + + +
+ + + + + + + + + +
@@ -383,6 +418,12 @@ const updateElement = (props: Partial) => { 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('#000') const lineHeight = ref() const wordSpace = ref() @@ -515,4 +556,55 @@ const updateLink = (link?: string) => { 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; +} \ No newline at end of file diff --git a/src/views/components/element/ProsemirrorEditor.vue b/src/views/components/element/ProsemirrorEditor.vue index c1a2c092..5f92d3f3 100644 --- a/src/views/components/element/ProsemirrorEditor.vue +++ b/src/views/components/element/ProsemirrorEditor.vue @@ -190,12 +190,14 @@ const execCommand = ({ target, action }: RichTextCommand) => { indentCommand(editorView, +item.value) } else if (item.command === 'bulletList') { + const listStyleType = item.value || '' 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') { + const listStyleType = item.value || '' 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') { autoSelectAll(editorView)