Skip to content

Commit

Permalink
feat: header button menu
Browse files Browse the repository at this point in the history
  • Loading branch information
wangfupeng1988 committed Jul 2, 2021
1 parent c96bc83 commit 6413135
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 57 deletions.
65 changes: 65 additions & 0 deletions packages/basic-modules/src/modules/header/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* @description header helper
* @author wangfupeng
*/

import { Editor, Transforms } from 'slate'
import { IDomEditor, DomEditor } from '@wangeditor/core'

/**
* 获取 node type('header1' 'header2' 等),未匹配则返回 'paragraph'
*/
export function getHeaderType(editor: IDomEditor): string {
const [match] = Editor.nodes(editor, {
match: n => {
const type = DomEditor.getNodeType(n)
return type.startsWith('header') // 匹配 node.type 是 header 开头的 node
},
universal: true,
})

// 未匹配到 header
if (match == null) return 'paragraph'

// 匹配到 header
const [n] = match

return DomEditor.getNodeType(n)
}

export function isMenuDisabled(editor: IDomEditor): boolean {
if (editor.selection == null) return true

const [nodeEntry] = Editor.nodes(editor, {
match: n => {
const type = DomEditor.getNodeType(n)

// 只可用于 p 和 header
if (type === 'paragraph') return true
if (type.startsWith('header')) return true

return false
},
universal: true,
mode: 'highest', // 匹配最高层级
})

// 匹配到 p header ,不禁用
if (nodeEntry) {
return false
}
// 未匹配到 p header ,则禁用
return true
}

/**
* 设置 node type ('header1' 'header2' 'paragraph' 等)
*/
export function setHeaderType(editor: IDomEditor, type: string) {
if (!type) return

// 执行命令
Transforms.setNodes(editor, {
type: type,
})
}
14 changes: 12 additions & 2 deletions packages/basic-modules/src/modules/header/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@

import { IModuleConf } from '@wangeditor/core'
import { renderHeader1Conf, renderHeader2Conf, renderHeader3Conf } from './render-elem'
import { headerMenuConf } from './menu/index'
import {
HeaderSelectMenuConf,
Header1ButtonMenuConf,
Header2ButtonMenuConf,
Header3ButtonMenuConf,
} from './menu/index'
import { header1ToHtmlConf, header2ToHtmlConf, header3ToHtmlConf } from './elem-to-html'
import withHeader from './plugin'

const bold: Partial<IModuleConf> = {
renderElems: [renderHeader1Conf, renderHeader2Conf, renderHeader3Conf],
elemsToHtml: [header1ToHtmlConf, header2ToHtmlConf, header3ToHtmlConf],
menus: [headerMenuConf],
menus: [
HeaderSelectMenuConf,
Header1ButtonMenuConf,
Header2ButtonMenuConf,
Header3ButtonMenuConf,
],
editorPlugin: withHeader,
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @description header1 button menu
* @author wangfupeng
*/

import HeaderButtonMenuBase from './HeaderButtonMenuBase'

class Header1ButtonMenu extends HeaderButtonMenuBase {
title = 'H1'
type = 'header1'
}

export default Header1ButtonMenu
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @description header2 button menu
* @author wangfupeng
*/

import HeaderButtonMenuBase from './HeaderButtonMenuBase'

class Header2ButtonMenu extends HeaderButtonMenuBase {
title = 'H2'
type = 'header2'
}

export default Header2ButtonMenu
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @description header3 button menu
* @author wangfupeng
*/

import HeaderButtonMenuBase from './HeaderButtonMenuBase'

class Header3ButtonMenu extends HeaderButtonMenuBase {
title = 'H3'
type = 'header3'
}

export default Header3ButtonMenu
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @description button menu base
* @author wangfupeng
*/

import { IButtonMenu, IDomEditor } from '@wangeditor/core'
import { getHeaderType, isMenuDisabled, setHeaderType } from '../helper'

abstract class HeaderButtonMenuBase implements IButtonMenu {
abstract readonly title: string
abstract readonly type: string // 'header1' 'header2' 等
readonly tag = 'button'

/**
* 获取选中节点的 node.type
* @param editor editor
*/
getValue(editor: IDomEditor): string | boolean {
return getHeaderType(editor)
}

isActive(editor: IDomEditor): boolean {
return this.getValue(editor) === this.type
}

isDisabled(editor: IDomEditor): boolean {
return isMenuDisabled(editor)
}

exec(editor: IDomEditor, value: string | boolean) {
const { type } = this
let newType
if (value === type) {
// 选中的 node.type 和当前 type 一样,则取消
newType = 'paragraph'
} else {
// 否则,则设置
newType = type
}

setHeaderType(editor, newType)
}
}

export default HeaderButtonMenuBase
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* @author wangfupeng
*/

import { Editor, Transforms } from 'slate'
import { ISelectMenu, IDomEditor, IOption, DomEditor } from '@wangeditor/core'
import { ISelectMenu, IDomEditor, IOption } from '@wangeditor/core'
import { HEADER_SVG } from '../../../constants/icon-svg'
import { getHeaderType, isMenuDisabled, setHeaderType } from '../helper'

class HeaderMenu implements ISelectMenu {
class HeaderSelectMenu implements ISelectMenu {
readonly title = '标题'
readonly iconSvg = HEADER_SVG
readonly tag = 'select'
Expand Down Expand Up @@ -54,49 +54,15 @@ class HeaderMenu implements ISelectMenu {
}

/**
* 获取 node.type
* 获取选中节点的 node.type
* @param editor editor
*/
getValue(editor: IDomEditor): string | boolean {
const [match] = Editor.nodes(editor, {
match: n => {
const type = DomEditor.getNodeType(n)
return type.startsWith('header') // 匹配 node.type 是 header 开头的 node
},
universal: true,
})

// 未匹配到 header
if (match == null) return 'paragraph'

// 匹配到 header
const [n] = match

return DomEditor.getNodeType(n)
return getHeaderType(editor)
}
isDisabled(editor: IDomEditor): boolean {
if (editor.selection == null) return true

const [nodeEntry] = Editor.nodes(editor, {
match: n => {
const type = DomEditor.getNodeType(n)

// 只可用于 p 和 header
if (type === 'paragraph') return true
if (type.startsWith('header')) return true

return false
},
universal: true,
mode: 'highest', // 匹配最高层级
})

// 匹配到 p header ,不禁用
if (nodeEntry) {
return false
}
// 未匹配到 p header ,则禁用
return true
isDisabled(editor: IDomEditor): boolean {
return isMenuDisabled(editor)
}

/**
Expand All @@ -105,13 +71,9 @@ class HeaderMenu implements ISelectMenu {
* @param value node.type
*/
exec(editor: IDomEditor, value: string | boolean) {
if (!value) return

// 执行命令
Transforms.setNodes(editor, {
type: value.toString(),
})
//【注意】value 是 select change 时获取的,并不是 this.getValue 的值
setHeaderType(editor, value.toString())
}
}

export default HeaderMenu
export default HeaderSelectMenu
32 changes: 28 additions & 4 deletions packages/basic-modules/src/modules/header/menu/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,35 @@
* @author wangfupeng
*/

import HeaderMenu from './HeaderMenu'
import HeaderSelectMenu from './HeaderSelectMenu'
import Header1ButtonMenu from './Header1ButtonMenu'
import Header2ButtonMenu from './Header2ButtonMenu'
import Header3ButtonMenu from './Header3ButtonMenu'

export const headerMenuConf = {
key: 'header',
export const HeaderSelectMenuConf = {
key: 'headerSelect',
factory() {
return new HeaderMenu()
return new HeaderSelectMenu()
},
}

export const Header1ButtonMenuConf = {
key: 'header1',
factory() {
return new Header1ButtonMenu()
},
}

export const Header2ButtonMenuConf = {
key: 'header2',
factory() {
return new Header2ButtonMenu()
},
}

export const Header3ButtonMenuConf = {
key: 'header3',
factory() {
return new Header3ButtonMenu()
},
}
18 changes: 16 additions & 2 deletions packages/editor/src/editor-config/hoverbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,26 @@ export function genHoverbarKeys() {
let parentType = ''
const [parent] = Editor.parent(editor, selection, { edge: 'start' })
if (Element.isElement(parent)) parentType = parent.type
if (parentType === 'code' || parentType === 'pre') return false // code-block 不允许
if (parentType === 'code' || parentType === 'pre' || parentType.startsWith('header')) {
// code-block header 禁止
return false
}

if (Text.isText(n)) return true // 匹配 text node
return false
},
menuKeys: ['header', '|', 'bold', 'underline', 'through', '|', 'color'],
menuKeys: [
'headerSelect',
'|',
'bold',
'underline',
'through',
'|',
'color',
'bgColor',
'|',
'insertLink',
],
},
{
desc: '选中链接 selected link',
Expand Down
5 changes: 4 additions & 1 deletion packages/editor/src/editor-config/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { INDENT_RIGHT_SVG, JUSTIFY_LEFT_SVG, IMAGE_SVG, MORE_SVG } from '../cons

export function genToolbarKeys() {
return [
'header',
'headerSelect',
// 'header1',
// 'header2',
// 'header3',
'blockquote',
'|',
'bold',
Expand Down

0 comments on commit 6413135

Please sign in to comment.