-
-
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
5262634
commit c1faa1c
Showing
15 changed files
with
381 additions
and
19 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.w-e-textarea-video-container { | ||
text-align: center; | ||
border: 1px solid #d9d9d9; | ||
padding: 5px 0; | ||
} |
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
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 video module | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { IModuleConf } from '@wangeditor/core' | ||
import withVideo from './plugin' | ||
import { renderVideoConf } from './render-elem' | ||
import { insertVideoMenuConf, deleteVideoMenuConf } from './menu/index' | ||
|
||
const video: IModuleConf = { | ||
renderElems: [renderVideoConf], | ||
menus: [insertVideoMenuConf, deleteVideoMenuConf], | ||
editorPlugin: withVideo, | ||
} | ||
|
||
export default video |
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 delete video menu | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { Transforms } from 'slate' | ||
import { IButtonMenu, IDomEditor } from '@wangeditor/core' | ||
import { checkNodeType, getSelectedNodeByType } from '../../_helpers/node' | ||
import { TRASH_SVG } from '../../_helpers/icon-svg' | ||
|
||
class DeleteVideoMenu implements IButtonMenu { | ||
title = '删除视频' | ||
iconSvg = TRASH_SVG | ||
tag = 'button' | ||
|
||
getValue(editor: IDomEditor): string | boolean { | ||
// 无需获取 val | ||
return '' | ||
} | ||
|
||
isActive(editor: IDomEditor): boolean { | ||
const videoNode = getSelectedNodeByType(editor, 'video') | ||
if (videoNode) { | ||
// 选区处于 video node | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
isDisabled(editor: IDomEditor): boolean { | ||
if (editor.selection == null) return true | ||
|
||
const videoNode = getSelectedNodeByType(editor, 'video') | ||
if (videoNode == null) { | ||
// 选区未处于 video node ,则禁用 | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
exec(editor: IDomEditor, value: string | boolean) { | ||
if (this.isDisabled(editor)) return | ||
|
||
// 删除视频 | ||
Transforms.removeNodes(editor, { | ||
match: n => checkNodeType(n, 'video'), | ||
}) | ||
} | ||
} | ||
|
||
export default DeleteVideoMenu |
127 changes: 127 additions & 0 deletions
127
packages/basic/src/modules/video/menu/InsertVideoMenu.ts
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,127 @@ | ||
/** | ||
* @description insert video menu | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { Editor, Transforms, Range, Node } from 'slate' | ||
import { IModalMenu, IDomEditor, DomEditor, hideAllPanelsAndModals } from '@wangeditor/core' | ||
import $, { Dom7Array } from '../../../utils/dom' | ||
import { genRandomStr } from '../../../utils/util' | ||
import { genModalInputElems, genModalButtonElems } from '../../_helpers/menu' | ||
import { VIDEO_SVG } from '../../_helpers/icon-svg' | ||
|
||
/** | ||
* 生成唯一的 DOM ID | ||
*/ | ||
function genDomID(): string { | ||
return genRandomStr('w-e-insert-video') | ||
} | ||
|
||
class InsertVideoMenu implements IModalMenu { | ||
title = '插入视频' | ||
iconSvg = VIDEO_SVG | ||
tag = 'button' | ||
showModal = true // 点击 button 时显示 modal | ||
modalWidth = 300 | ||
private $content: Dom7Array | null = null | ||
private srcInputId = genDomID() | ||
private buttonId = genDomID() | ||
|
||
getValue(editor: IDomEditor): string | boolean { | ||
// 插入菜单,不需要 value | ||
return '' | ||
} | ||
|
||
isActive(editor: IDomEditor): boolean { | ||
// 任何时候,都不用激活 menu | ||
return false | ||
} | ||
|
||
exec(editor: IDomEditor, value: string | boolean) { | ||
// 点击菜单时,弹出 modal 之前,不需要执行其他代码 | ||
// 此处空着即可 | ||
} | ||
|
||
isDisabled(editor: IDomEditor): boolean { | ||
const { selection } = editor | ||
if (selection == null) return true | ||
if (!Range.isCollapsed(selection)) return true // 选区非折叠,禁用 | ||
|
||
return false | ||
} | ||
|
||
getModalPositionNode(editor: IDomEditor): Node | null { | ||
return null // modal 依据选区定位 | ||
} | ||
|
||
getModalContentElem(editor: IDomEditor): Dom7Array { | ||
const { srcInputId, buttonId } = this | ||
|
||
// 获取 input button elem | ||
const [$srcContainer, $inputSrc] = genModalInputElems( | ||
'视频地址', | ||
srcInputId, | ||
'mp4 网址,或第三方 <iframe>...' | ||
) | ||
const [$buttonContainer] = genModalButtonElems(buttonId, '确定') | ||
|
||
if (this.$content == null) { | ||
// 第一次渲染 | ||
const $content = $('<div></div>') | ||
|
||
// 绑定事件(第一次渲染时绑定,不要重复绑定) | ||
$content.on('click', `#${buttonId}`, e => { | ||
e.preventDefault() | ||
const src = $(`#${srcInputId}`).val().trim() | ||
this.insertVideo(editor, src) | ||
}) | ||
|
||
// 记录属性,重要 | ||
this.$content = $content | ||
} | ||
|
||
const $content = this.$content | ||
$content.html('') // 先清空内容 | ||
|
||
// append inputs and button | ||
$content.append($srcContainer) | ||
$content.append($buttonContainer) | ||
|
||
// 设置 input val | ||
$inputSrc.val('') | ||
|
||
// focus 一个 input(异步,此时 DOM 尚未渲染) | ||
setTimeout(() => { | ||
$(`#${srcInputId}`).focus() | ||
}) | ||
|
||
return $content | ||
} | ||
|
||
private insertVideo(editor: IDomEditor, src: string) { | ||
if (!src) { | ||
hideAllPanelsAndModals() // 隐藏 modal | ||
return | ||
} | ||
|
||
// 还原选区 | ||
DomEditor.restoreSelection(editor) | ||
|
||
if (this.isDisabled(editor)) return | ||
|
||
// 新建一个 video node | ||
const video = { | ||
type: 'video', | ||
src, | ||
children: [{ text: '' }], // 【注意】void node 需要一个空 text 作为 children | ||
} | ||
|
||
// 插入图片 | ||
Transforms.insertNodes(editor, video) | ||
|
||
// 隐藏 modal | ||
hideAllPanelsAndModals() | ||
} | ||
} | ||
|
||
export default InsertVideoMenu |
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 video menu | ||
* @author wangfupeng | ||
*/ | ||
|
||
import InsertVideoMenu from './InsertVideoMenu' | ||
import DeleteVideoMenu from './DeleteVideoMenu' | ||
|
||
export const insertVideoMenuConf = { | ||
key: 'insertVideo', | ||
factory() { | ||
return new InsertVideoMenu() | ||
}, | ||
} | ||
|
||
export const deleteVideoMenuConf = { | ||
key: 'deleteVideo', | ||
factory() { | ||
return new DeleteVideoMenu() | ||
}, | ||
} |
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,54 @@ | ||
/** | ||
* @description editor 插件,重写 editor API | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { Node, Transforms } from 'slate' | ||
import { IDomEditor } from '@wangeditor/core' | ||
|
||
function withVideo<T extends IDomEditor>(editor: T): T { | ||
const { isVoid, normalizeNode } = editor | ||
const newEditor = editor | ||
|
||
// 重写 isVoid | ||
newEditor.isVoid = elem => { | ||
// @ts-ignore | ||
const { type } = elem | ||
|
||
if (type === 'video') { | ||
return true | ||
} | ||
|
||
return isVoid(elem) | ||
} | ||
|
||
// 重写 normalizeNode | ||
newEditor.normalizeNode = ([node, path]) => { | ||
// @ts-ignore | ||
const { type } = node | ||
|
||
// ----------------- video 后面必须跟一个 p 或 header ----------------- | ||
if (type === 'video') { | ||
const topLevelNodes = newEditor.children || [] | ||
const nextNode = topLevelNodes[path[0] + 1] || {} | ||
// @ts-ignore | ||
const { type: nextNodeType = '' } = nextNode | ||
if (nextNodeType !== 'paragraph' || nextNodeType.startsWith('header')) { | ||
// video node 后面不是 p 或 header ,则插入一个空 p | ||
const p = { type: 'paragraph', children: [{ text: '' }] } | ||
const insertPath = [path[0] + 1] | ||
Transforms.insertNodes(newEditor, p, { | ||
at: insertPath, // 在表格后面插入 | ||
}) | ||
} | ||
} | ||
|
||
// 执行默认的 normalizeNode ,重要!!! | ||
return normalizeNode([node, path]) | ||
} | ||
|
||
// 返回 editor ,重要! | ||
return newEditor | ||
} | ||
|
||
export default withVideo |
Oops, something went wrong.