-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bfb3014
commit fe6c083
Showing
12 changed files
with
309 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* @description list module entry | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { IModuleConf } from '@wangeditor/core' | ||
import { renderBulletedListConf, renderNumberedListConf, renderListItemConf } from './render-elem' | ||
import { bulletedListMenuConf, numberedListMenuConf } from './menu/index' | ||
import withList from './plugin' | ||
|
||
const bold: IModuleConf = { | ||
renderElems: [renderBulletedListConf, renderNumberedListConf, renderListItemConf], | ||
menus: [bulletedListMenuConf, numberedListMenuConf], | ||
editorPlugin: withList, | ||
} | ||
|
||
export default bold |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/** | ||
* @description base menu | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { Editor, Node, Transforms } from 'slate' | ||
import { IButtonMenu, IDomEditor } from '@wangeditor/core' | ||
import { getSelectedNodeByType } from '../../_helpers/node' | ||
|
||
function checkList(n: Node): boolean { | ||
// @ts-ignore | ||
return ['bulleted-list', 'numbered-list'].includes(n.type) | ||
} | ||
|
||
abstract class BaseMenu implements IButtonMenu { | ||
abstract type: string // 'bulleted-list' / 'numbered-list' | ||
abstract title: string | ||
abstract iconSvg: string | ||
tag = 'button' | ||
|
||
private getListNode(editor: IDomEditor): Node | null { | ||
const { type } = this | ||
return getSelectedNodeByType(editor, type) | ||
} | ||
|
||
getValue(editor: IDomEditor): string | boolean { | ||
return '' | ||
} | ||
|
||
isActive(editor: IDomEditor): boolean { | ||
const node = this.getListNode(editor) | ||
return !!node | ||
} | ||
|
||
isDisabled(editor: IDomEditor): boolean { | ||
if (editor.selection == null) return true | ||
|
||
const [nodeEntry] = Editor.nodes(editor, { | ||
// @ts-ignore | ||
match: n => { | ||
// @ts-ignore | ||
const { type = '' } = n | ||
|
||
if (type === 'pre') return true // 代码块 | ||
if (Editor.isVoid(editor, n)) return true // void node | ||
if (type === 'table') return true // table | ||
|
||
return false | ||
}, | ||
universal: true, | ||
}) | ||
|
||
// 命中,则禁用 | ||
if (nodeEntry) return true | ||
return false | ||
} | ||
|
||
/** | ||
* 获取当前选区匹配的 type 'bulleted-list' / 'numbered-list' | ||
* @param editor editor | ||
*/ | ||
private getMatchListType(editor: IDomEditor): string { | ||
const [nodeEntry] = Editor.nodes(editor, { | ||
match: n => checkList(n), | ||
universal: true, | ||
}) | ||
if (nodeEntry == null) return '' | ||
const [n] = nodeEntry | ||
// @ts-ignore | ||
return n.type | ||
} | ||
|
||
exec(editor: IDomEditor, value: string | boolean): void { | ||
const { type } = this | ||
const active = this.isActive(editor) | ||
|
||
// 移除 ul ol 的父节点 | ||
Transforms.unwrapNodes(editor, { | ||
match: n => checkList(n), | ||
split: true, | ||
}) | ||
// 设置当前节点 type | ||
Transforms.setNodes(editor, { | ||
// @ts-ignore | ||
type: active ? 'paragraph' : 'list-item', | ||
}) | ||
|
||
const listNode = { type, children: [] } | ||
if (!active) { | ||
// 非 list 设置为 list ,则外层再包裹一个 listNode | ||
Transforms.wrapNodes(editor, listNode) | ||
} | ||
const matchType = this.getMatchListType(editor) | ||
if (matchType != '' && matchType !== type) { | ||
// list 却换 type(如 ul 切换为 ol),则外层再包裹一个 listNode | ||
Transforms.wrapNodes(editor, listNode) | ||
} | ||
} | ||
} | ||
|
||
export default BaseMenu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* @description bulleted list menu | ||
* @author wangfupeng | ||
*/ | ||
|
||
import BaseMenu from './BaseMenu' | ||
import { BULLETED_LIST_SVG } from '../../_helpers/icon-svg' | ||
|
||
class BulletedListMenu extends BaseMenu { | ||
type = 'bulleted-list' | ||
title = '无序列表' | ||
iconSvg = BULLETED_LIST_SVG | ||
} | ||
|
||
export default BulletedListMenu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* @description numbered list menu | ||
* @author wangfupeng | ||
*/ | ||
|
||
import BaseMenu from './BaseMenu' | ||
import { NUMBERED_LIST_SVG } from '../../_helpers/icon-svg' | ||
|
||
class NumberedListMenu extends BaseMenu { | ||
type = 'numbered-list' | ||
title = '有序列表' | ||
iconSvg = NUMBERED_LIST_SVG | ||
} | ||
|
||
export default NumberedListMenu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* @description menu entry | ||
* @author wangfupeng | ||
*/ | ||
|
||
import BulletedListMenu from './BulletedListMenu' | ||
import NumberedListMenu from './NumberedListMenu' | ||
|
||
export const bulletedListMenuConf = { | ||
key: 'bulletedList', | ||
factory() { | ||
return new BulletedListMenu() | ||
}, | ||
} | ||
|
||
export const numberedListMenuConf = { | ||
key: 'numberedList', | ||
factory() { | ||
return new NumberedListMenu() | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/** | ||
* @description editor 插件,重写 editor API | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { Editor, Transforms, Node } from 'slate' | ||
import { IDomEditor, DomEditor } from '@wangeditor/core' | ||
|
||
function withList<T extends IDomEditor>(editor: T): T { | ||
const { insertBreak } = editor | ||
const newEditor = editor | ||
|
||
// 重写 insertBreak | ||
newEditor.insertBreak = () => { | ||
// 匹配 list-item | ||
const [nodeEntry] = Editor.nodes(newEditor, { | ||
// @ts-ignore | ||
match: n => n.type === 'list-item', | ||
universal: true, | ||
}) | ||
|
||
if (nodeEntry == null) { | ||
// 未匹配到 list-item | ||
insertBreak() | ||
return | ||
} | ||
|
||
const [n] = nodeEntry | ||
const listNode = DomEditor.getParentNode(newEditor, n) // 获取 list-item 的父节点,即 list 节点 | ||
const children = listNode?.children || [] | ||
const childrenLength = children.length | ||
if (n === children[childrenLength - 1]) { | ||
// 当前 list-item 是 list 的最后一个 child | ||
const str = Node.string(n) | ||
if (str === '') { | ||
// 当前 list-item 无内容。则删除这个空白 list-item,并跳出 list ,插入一个空行 | ||
Transforms.removeNodes(newEditor, { | ||
// @ts-ignore | ||
match: n => n.type === 'list-item', | ||
}) | ||
|
||
const p = { type: 'paragraph', children: [{ text: '' }] } | ||
Transforms.insertNodes(newEditor, p, { | ||
mode: 'highest', // 在最高层级插入,否则会插入到 list 下面 | ||
}) | ||
|
||
return // 阻止默认的 insertBreak ,重要!!! | ||
} | ||
} | ||
|
||
// 其他情况,执行默认的 insertBreak() | ||
insertBreak() | ||
} | ||
|
||
// 返回 editor ,重要! | ||
return newEditor | ||
} | ||
|
||
export default withList |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/** | ||
* @description register formats | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { Element as SlateElement } from 'slate' | ||
import { jsx, VNode } from 'snabbdom' | ||
import { IDomEditor } from '@wangeditor/core' | ||
|
||
function genTag(type: string): string { | ||
if (type === 'bulleted-list') return 'ul' | ||
if (type === 'numbered-list') return 'ol' | ||
if (type === 'list-item') return 'li' | ||
throw new Error(`list type '${type}' is invalid`) | ||
} | ||
|
||
function genRenderFn(type: string) { | ||
/** | ||
* render header elem | ||
* @param elemNode slate elem | ||
* @param children children | ||
* @param editor editor | ||
* @returns vnode | ||
*/ | ||
function renderHeader( | ||
elemNode: SlateElement, | ||
children: VNode[] | null, | ||
editor: IDomEditor | ||
): VNode { | ||
const Tag = genTag(type) | ||
const vnode = <Tag>{children}</Tag> | ||
return vnode | ||
} | ||
|
||
return renderHeader | ||
} | ||
|
||
const renderBulletedListConf = { | ||
type: 'bulleted-list', // 和 elemNode.type 一致 | ||
renderFn: genRenderFn('bulleted-list'), | ||
} | ||
const renderNumberedListConf = { | ||
type: 'numbered-list', | ||
renderFn: genRenderFn('numbered-list'), | ||
} | ||
const renderListItemConf = { | ||
type: 'list-item', | ||
renderFn: genRenderFn('list-item'), | ||
} | ||
|
||
export { renderBulletedListConf, renderNumberedListConf, renderListItemConf } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,4 +45,8 @@ | |
padding: 3px 5px; | ||
width: 50px; | ||
} | ||
|
||
ul, ol { | ||
padding-left: 20px; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters