diff --git a/docs/plugin/tutorials-card.md b/docs/plugin/tutorials-card.md index 2a5f26ed..e6ab7117 100644 --- a/docs/plugin/tutorials-card.md +++ b/docs/plugin/tutorials-card.md @@ -945,10 +945,6 @@ The style of the selected yes, the default is the border change, optional values - `border` border changes - `background` background color change -### `toolbarFollowMouse` - -Whether the card toolbar follows the mouse position, the default flase - ### `lazyRender` Whether to enable lazy loading, the rendering is triggered when the card node is visible in the view diff --git a/docs/plugin/tutorials-card.zh-CN.md b/docs/plugin/tutorials-card.zh-CN.md index 6fc41d1e..0f0449b3 100644 --- a/docs/plugin/tutorials-card.zh-CN.md +++ b/docs/plugin/tutorials-card.zh-CN.md @@ -946,10 +946,6 @@ export default class extends Plugin { - `border` 边框变化 - `background` 背景颜色变化 -### `toolbarFollowMouse` - -卡片工具栏是否跟随鼠标位置,默认 flase - ### `lazyRender` 是否启用懒加载,卡片节点在视图内可见时触发渲染 diff --git a/packages/engine/src/card/entry.ts b/packages/engine/src/card/entry.ts index e91ebb72..e7e441b1 100644 --- a/packages/engine/src/card/entry.ts +++ b/packages/engine/src/card/entry.ts @@ -50,7 +50,6 @@ abstract class CardEntry implements CardInterface { static readonly collab: boolean = true; static readonly focus: boolean; static readonly selectStyleType: SelectStyleType = SelectStyleType.BORDER; - static readonly toolbarFollowMouse: boolean = false; static readonly lazyRender: boolean = false; private defaultMaximize: MaximizeInterface; isMaximize: boolean = false; diff --git a/packages/engine/src/change/index.ts b/packages/engine/src/change/index.ts index bf7f896f..9032b6fc 100644 --- a/packages/engine/src/change/index.ts +++ b/packages/engine/src/change/index.ts @@ -479,14 +479,9 @@ class ChangeModel implements ChangeInterface { } // 被删除了重新设置开始节点位置 if (startRange && !startRange.node[0].parentNode) { - const children = node.children(); startRange = { - node: node, - offset: - children.length === 1 && - children[0].nodeName === 'BR' - ? 0 - : range.startOffset, + node: appendNodes[0], + offset: 0, }; } node = next; diff --git a/packages/engine/src/change/paste.ts b/packages/engine/src/change/paste.ts index 00e04b3c..c1c8baf2 100644 --- a/packages/engine/src/change/paste.ts +++ b/packages/engine/src/change/paste.ts @@ -162,6 +162,57 @@ export default class Paste { ul.append(node); return; } + if (nodeApi.isList(node) && parent && nodeApi.isList(parent)) { + // 分割付节点list + const leftList: NodeInterface[] = []; + const rightList: NodeInterface[] = []; + let isLeft = true; + const rootChildren = parent.children().toArray(); + let tempList = parent.clone(); + const appendToTemp = () => { + if (tempList.children().length > 0) { + if (isLeft) leftList.push(tempList); + else rightList.push(tempList); + tempList = parent!.clone(); + return true; + } + return false; + }; + rootChildren.forEach((child) => { + if (!child) return; + if (child.equal(node)) { + isLeft = false; + return; + } + if (child.name === 'li') { + tempList.append(child); + return; + } else { + appendToTemp(); + } + if (isLeft) leftList.push(child); + else rightList.push(child); + }); + appendToTemp(); + const indent = parent.attributes('data-indent') || '0'; + this.engine.list.addIndent(node, parseInt(indent, 10)); + leftList.push(node); + let prev = parent; + leftList.forEach((childNode) => { + const child = $(childNode); + if (!child || child.children().length === 0) return; + prev.after(child); + prev = child; + }); + rightList.forEach((childNode) => { + const child = $(childNode); + if (!child || child.children().length === 0) return; + prev.after(child); + prev = child; + }); + parent.remove(); + return node.next() || undefined; + } // 补齐 li if (node.name !== 'li' && parentIsList) { const li = $('
  • '); @@ -170,33 +221,92 @@ export default class Paste { return; } //
  • two
    1. three
    four
  • - if ( - nodeApi.isList(node) && - parent?.name === 'li' && - (node.prev() || node.next()) - ) { - let li: NodeInterface | null; + /** + * + */ + if (nodeApi.isList(node) && parent?.name === 'li') { + // li没有父节点就移除包裹 + const rootListElement = parent?.parent(); + if (!rootListElement) { + nodeApi.unwrap(parent); + return; + } + // 分割付节点list + const leftList = rootListElement.clone(); + const rightList = rootListElement.clone(); + let isLeft = true; + const rootChildren = rootListElement.children().toArray(); + + rootChildren.forEach((child) => { + if (!child) return; + if (child.equal(parent!)) { + isLeft = false; + return; + } + if (isLeft) leftList.append(child); + else rightList.append(child); + }); const isCustomizeList = parent?.parent()?.hasClass('data-list'); const children = parent?.children(); + let li: NodeInterface | null = null; + let next: NodeInterface | null = null; children.each((child, index) => { const node = children.eq(index); if (!node || nodeApi.isEmptyWithTrim(node)) { return; } const isList = nodeApi.isList(node); - if (!li || isList) { + const leftLast = leftList[leftList.length - 1]; + if (isList) { + const indent = + $(leftLast)?.attributes('data-indent') || '0'; + this.engine.list.addIndent(node, parseInt(indent, 10)); + leftList[leftList.length] = node[0]; + li = null; + return; + } + if (!li) { li = isCustomizeList ? $('
  • ') : $('
  • '); - parent?.before(li); + const last = $(leftLast)?.last(); + if (last) last?.after(li); + else $(leftLast).append(li); } li.append(child); - if (isList) { - li = null; + if (!next) { + next = li; } }); parent?.remove(); - return; + let prev = rootListElement; + leftList.each((childNode) => { + const child = $(childNode); + if (!child || child.children().length === 0) return; + prev.after(child); + prev = child; + }); + rightList.each((childNode) => { + const child = $(childNode); + if (!child || child.children().length === 0) return; + prev.after(child); + prev = child; + }); + rootListElement.remove(); + return ( + (next as NodeInterface | null)?.next() || + leftList.next() || + void 0 + ); } // p 改成 li if (node.name === 'p' && parentIsList) { diff --git a/packages/engine/src/node/entry.ts b/packages/engine/src/node/entry.ts index dd0b3999..daebe3b2 100644 --- a/packages/engine/src/node/entry.ts +++ b/packages/engine/src/node/entry.ts @@ -939,7 +939,7 @@ class NodeEntry implements NodeInterface { } traverse( - callback: (node: NodeInterface) => boolean | void, + callback: (node: NodeInterface) => boolean | void | NodeInterface, order: boolean = true, includeEditableCard: boolean = false, ) { @@ -962,8 +962,10 @@ class NodeEntry implements NodeInterface { if (result === false) { return; } - if (result !== true) { + if (result && typeof result !== 'boolean') { + child = result; + } if (includeEditableCard && child.isEditableCard()) { const editableElements = child.find(EDITABLE_SELECTOR); editableElements.each((_, index) => { diff --git a/packages/engine/src/types/card.ts b/packages/engine/src/types/card.ts index af640485..b1c4a75d 100644 --- a/packages/engine/src/types/card.ts +++ b/packages/engine/src/types/card.ts @@ -119,10 +119,6 @@ export interface CardEntry { * 卡片选中后的样式效果,默认为 border */ readonly selectStyleType: SelectStyleType; - /** - * toolbar 跟随鼠标点击位置 - */ - readonly toolbarFollowMouse: boolean; /** * 是否在卡片处于视图内时才渲染,默认 false */ diff --git a/packages/engine/src/types/node.ts b/packages/engine/src/types/node.ts index 08de6698..e29bf1d1 100644 --- a/packages/engine/src/types/node.ts +++ b/packages/engine/src/types/node.ts @@ -477,7 +477,7 @@ export interface NodeInterface { * @param includeEditableCard 是否包含可编辑器卡片 */ traverse( - callback: (node: NodeInterface) => boolean | void, + callback: (node: NodeInterface) => boolean | void | NodeInterface, order?: boolean, includeEditableCard?: boolean, ): void; diff --git a/packages/toolbar/src/plugin/index.ts b/packages/toolbar/src/plugin/index.ts index 6c2cdc6c..659a086c 100644 --- a/packages/toolbar/src/plugin/index.ts +++ b/packages/toolbar/src/plugin/index.ts @@ -51,10 +51,20 @@ class ToolbarPlugin extends Plugin { if (isEngine(this.editor)) { this.editor.on('keydown:slash', (event) => this.onSlash(event)); this.editor.on('parse:value', (node) => this.paserValue(node)); + //this.editor.on('select', this.onSelect) } this.editor.language.add(locales); } + // onSelect = () => { + // if (!isEngine(this.editor)) return; + // const { change, card } = this.editor; + // if(card.active) return + // const range = change.range.get().cloneRange().shrinkToTextNode(); + // if(range.collapsed) return + // const { startNode, endNode } = range + // } + paserValue(node: NodeInterface) { if ( node.isCard() && diff --git a/plugins/orderedlist/src/index.ts b/plugins/orderedlist/src/index.ts index 8e294568..bfeec30e 100644 --- a/plugins/orderedlist/src/index.ts +++ b/plugins/orderedlist/src/index.ts @@ -118,7 +118,7 @@ export default class extends ListPlugin { const text = node.text(); if (!text) return; - const reg = /(^|\r\n|\n)(\d{1,9}\.)/; + const reg = /(^|\r\n|\n)\s*(\d{1,9}\.)/; const match = reg.exec(text); return { reg, @@ -134,12 +134,20 @@ export default class extends ListPlugin { const { list } = this.editor; - const createList = (nodes: Array, start?: number) => { + const createList = ( + nodes: Array, + start?: number, + indent?: number, + ) => { const listNode = $( - `<${this.tagName} start="${start || 1}">${nodes.join('')}`, + `<${this.tagName}>${nodes.join('')}`, ); + if (start) { + listNode.attributes('start', start); + } + if (indent) { + listNode.attributes(this.editor.list.INDENT_KEY, indent); + } list.addBr(listNode); return listNode.get()?.outerHTML; }; @@ -147,21 +155,27 @@ export default class extends ListPlugin { let newText = ''; const rows = text.split(/\n|\r\n/); let nodes: Array = []; + let indent = 0; let start: number | undefined = undefined; rows.forEach((row) => { - const match = /^(\d{1,9}\.)/.exec(row); + const match = /^(\s*)(\d{1,9}\.)/.exec(row); if (match) { - const codeLength = match[1].length; + const codeLength = match[2].length; if (start === undefined) - start = parseInt(match[1].substr(0, codeLength - 1), 10); + start = parseInt(match[2].substr(0, codeLength - 1), 10); const content = row.substr( - /^\s+/.test(row.substr(codeLength)) + (/^\s+/.test(row.substr(codeLength)) ? codeLength + 1 - : codeLength, + : codeLength) + match[1].length, ); + if (match[1].length !== indent && nodes.length > 0) { + newText += createList(nodes, undefined, indent); + nodes = []; + indent = Math.ceil(match[1].length / 2); + } nodes.push(`
  • ${content}
  • `); } else if (nodes.length > 0) { - newText += createList(nodes, start) + '\n' + row + '\n'; + newText += createList(nodes, start, indent) + '\n' + row + '\n'; nodes = []; start = undefined; } else { @@ -169,7 +183,7 @@ export default class extends ListPlugin { } }); if (nodes.length > 0) { - newText += createList(nodes, start) + '\n'; + newText += createList(nodes, start, indent) + '\n'; } node.text(newText); } diff --git a/plugins/tasklist/src/index.ts b/plugins/tasklist/src/index.ts index 2a5fe725..42a81fce 100644 --- a/plugins/tasklist/src/index.ts +++ b/plugins/tasklist/src/index.ts @@ -27,6 +27,7 @@ export default class extends ListPlugin { attributes = { class: '@var0', + 'data-indent': '@var1', }; variable = { @@ -34,6 +35,7 @@ export default class extends ListPlugin { required: true, value: [this.editor.list.CUSTOMZIE_UL_CLASS, 'data-list-task'], }, + '@var1': '@number', }; allowIn = ['blockquote', '$root']; @@ -244,12 +246,15 @@ export default class extends ListPlugin { const { list, card } = this.editor; - const createList = (nodes: Array) => { + const createList = (nodes: Array, indent?: number) => { const listNode = $( `<${this.tagName} class="${ list.CUSTOMZIE_UL_CLASS } data-list-task">${nodes.join('')}`, ); + if (indent) { + listNode.attributes(this.editor.list.INDENT_KEY, indent); + } list.addBr(listNode); return listNode.get()?.outerHTML; }; @@ -257,8 +262,9 @@ export default class extends ListPlugin { let newText = ''; const rows = text.split(/\n|\r\n/); let nodes: Array = []; + let indent = 0; rows.forEach((row) => { - const match = /^(-\s*)?(\[[\sx]{0,1}\])/.exec(row); + const match = /^(\s*)(-\s*)?(\[[\sx]{0,1}\])/.exec(row); if (match && !/(\[(.*)\]\(([\S]+?)\))/.test(row)) { const codeLength = match[0].length; const content = row.substr( @@ -275,20 +281,25 @@ export default class extends ListPlugin { }, ); tempNode.remove(); + if (match[1].length !== indent && nodes.length > 0) { + newText += createList(nodes, indent); + nodes = []; + indent = Math.ceil(match[1].length / 2); + } nodes.push( `
  • ${ cardNode.get()?.outerHTML }${content}
  • `, ); } else if (nodes.length > 0) { - newText += createList(nodes) + '\n' + row + '\n'; + newText += createList(nodes, indent) + '\n' + row + '\n'; nodes = []; } else { newText += row + '\n'; } }); if (nodes.length > 0) { - newText += createList(nodes) + '\n'; + newText += createList(nodes, indent) + '\n'; } node.text(newText); } diff --git a/plugins/unorderedlist/src/index.ts b/plugins/unorderedlist/src/index.ts index 069e1c90..eb0c26ff 100644 --- a/plugins/unorderedlist/src/index.ts +++ b/plugins/unorderedlist/src/index.ts @@ -120,7 +120,7 @@ export default class extends ListPlugin { const text = node.text(); if (!text) return; - const reg = /(^|\r\n|\n)([\*\-\+]{1,}\s+)/g; + const reg = /(^|\r\n|\n)\s*([\*\-\+]{1,}\s+)/g; const match = reg.exec(text); return { reg, @@ -137,12 +137,20 @@ export default class extends ListPlugin { const { list } = this.editor; - const createList = (nodes: Array, start?: number) => { + const createList = ( + nodes: Array, + start?: number, + indent?: number, + ) => { const listNode = $( - `<${this.tagName} start="${start || 1}">${nodes.join('')}`, + `<${this.tagName}>${nodes.join('')}`, ); + if (start) { + listNode.attributes('start', start); + } + if (indent) { + listNode.attributes(this.editor.list.INDENT_KEY, indent); + } list.addBr(listNode); return listNode.get()?.outerHTML; }; @@ -150,21 +158,28 @@ export default class extends ListPlugin { let newText = ''; const rows = text.split(/\n|\r\n/); let nodes: Array = []; + let indent = 0; rows.forEach((row) => { - const match = /^([\*\-\+]{1,}\s+)/.exec(row); + const match = /^(\s*)([\*\-\+]{1,}\s+)/.exec(row); if (match) { - const codeLength = match[1].length; - const content = row.substr(codeLength); + const codeLength = match[2].length; + const content = row.substr(codeLength + match[1].length); + if (match[1].length !== indent && nodes.length > 0) { + newText += createList(nodes, undefined, indent); + nodes = []; + indent = Math.ceil(match[1].length / 2); + } nodes.push(`
  • ${content}
  • `); } else if (nodes.length > 0) { - newText += createList(nodes) + '\n' + row + '\n'; + newText += + createList(nodes, undefined, indent) + '\n' + row + '\n'; nodes = []; } else { newText += row + '\n'; } }); if (nodes.length > 0) { - newText += createList(nodes) + '\n'; + newText += createList(nodes, undefined, indent) + '\n'; } node.text(newText); }