-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(plugin-mention): add mention plugin
* feat(plugin-mention): add mention plugin * feat(changeset): add changeset for mention plugin patch * chore: update yarn.lock
- Loading branch information
Showing
18 changed files
with
555 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@wangeditor-next/plugin-mention': patch | ||
--- | ||
|
||
feat(plugin-mention): add mention plugin |
Empty file.
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,90 @@ | ||
# wangEditor mention plugin | ||
|
||
[中文文档](./README.md) | ||
|
||
## Introduction | ||
|
||
[wangeditor-next](https://github.com/cycleccc/wangEditor-next) mention plugin, like `@James`. | ||
|
||
 | ||
|
||
## Installation | ||
|
||
```shell | ||
yarn add @wangeditor-next/plugin-mention | ||
``` | ||
|
||
## Usage | ||
|
||
[Vue demo source code](https://github.com/wangfupeng1988/vue2-wangeditor-demo/blob/master/src/components/MyEditorWithMention.vue) | ||
|
||
### Use in editor | ||
|
||
```ts | ||
import { IDomEditor, Boot, IEditorConfig } from '@wangeditor-next/editor' | ||
import mentionModule, { MentionElement } from '@wangeditor-next/plugin-mention' | ||
|
||
// Register | ||
// You should register this before create editor, and register only once (not repeatedly). | ||
Boot.registerModule(mentionModule) | ||
|
||
// Show your modal | ||
function showModal(editor: IDomEditor) { | ||
// Get cursor's position info, to set modal position | ||
const domSelection = document.getSelection() | ||
const domRange = domSelection.getRangeAt(0) | ||
if (domRange == null) return | ||
const selectionRect = domRange.getBoundingClientRect() | ||
|
||
// Get editor container's position info, maybe help to get right modal position | ||
const containerRect = editor.getEditableContainer().getBoundingClientRect() | ||
|
||
// Show your modal, and set position | ||
// PS: You must implement the modal yourself, use <div> or Vue React component | ||
|
||
|
||
// Insert mention node when emit some event. | ||
function insertMention() { | ||
const mentionNode: MentionElement = { | ||
type: 'mention', // must be 'mention' | ||
value: 'James', // text | ||
info: { x: 1, y: 2 }, // extended info | ||
children: [{ text: '' }], // must have an empty text node in children | ||
} | ||
|
||
editor.restoreSelection() | ||
editor.deleteBackward('character') // delete '@' | ||
editor.insertNode(mentionNode) | ||
editor.move(1) // move curser | ||
} | ||
} | ||
|
||
// hide your modal | ||
function hideModal(editor: IDomEditor) { | ||
// hide your modal | ||
} | ||
|
||
// editor config | ||
const editorConfig: Partial<IEditorConfig> = { | ||
EXTEND_CONF: { | ||
mentionConfig: { | ||
showModal, // required | ||
hideModal, // required | ||
}, | ||
}, | ||
|
||
// others... | ||
} | ||
|
||
// Then create editor and toolbar, you will use `editorConfig` | ||
``` | ||
|
||
### Render HTML | ||
|
||
You will get a mention's HTML format like this. You need to `decodeURIComponent` the value of `data-info`. | ||
|
||
```html | ||
<span data-w-e-type="mention" data-w-e-is-void data-w-e-is-inline data-value="James" data-info="%7B%22x%22%3A10%7D">@James</span> | ||
``` | ||
|
||
|
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,87 @@ | ||
# wangEditor mention 插件 | ||
|
||
[English Documentation](./README-en.md) | ||
|
||
## 介绍 | ||
|
||
[wangeditor-next](https://github.com/cycleccc/wangEditor-next) mention 插件,如 `@张三`。 | ||
|
||
 | ||
|
||
## 安装 | ||
|
||
```shell | ||
yarn add @wangeditor-next/plugin-mention | ||
``` | ||
|
||
## 使用 | ||
|
||
[Vue 示例源码](https://github.com/wangfupeng1988/vue2-wangeditor-demo/blob/master/src/components/MyEditorWithMention.vue) | ||
|
||
### 注册到编辑器 | ||
|
||
```ts | ||
import { IDomEditor, Boot, IEditorConfig } from '@wangeditor-next/editor' | ||
import mentionModule, { MentionElement } from '@wangeditor-next/plugin-mention' | ||
|
||
// 注册。要在创建编辑器之前注册,且只能注册一次,不可重复注册。 | ||
Boot.registerModule(mentionModule) | ||
|
||
// 显示弹框 | ||
function showModal(editor: IDomEditor) { | ||
// 获取光标位置,定位 modal | ||
const domSelection = document.getSelection() | ||
const domRange = domSelection.getRangeAt(0) | ||
if (domRange == null) return | ||
const selectionRect = domRange.getBoundingClientRect() | ||
|
||
// 获取编辑区域 DOM 节点的位置,以辅助定位 | ||
const containerRect = editor.getEditableContainer().getBoundingClientRect() | ||
|
||
// 显示 modal 弹框,并定位 | ||
// PS:modal 需要自定义,如 <div> 或 Vue React 组件 | ||
|
||
|
||
// 当触发某事件(如点击一个按钮)时,插入 mention 节点 | ||
function insertMention() { | ||
const mentionNode: MentionElement = { | ||
type: 'mention', // 必须是 'mention' | ||
value: '张三', // 文本 | ||
info: { x: 1, y: 2 }, // 其他信息,自定义 | ||
children: [{ text: '' }], // 必须有一个空 text 作为 children | ||
} | ||
|
||
editor.restoreSelection() // 恢复选区 | ||
editor.deleteBackward('character') // 删除 '@' | ||
editor.insertNode(mentionNode) // 插入 mention | ||
editor.move(1) // 移动光标 | ||
} | ||
} | ||
|
||
// 隐藏弹框 | ||
function hideModal(editor: IDomEditor) { | ||
// 隐藏 modal | ||
} | ||
|
||
// 编辑器配置 | ||
const editorConfig: Partial<IEditorConfig> = { | ||
EXTEND_CONF: { | ||
mentionConfig: { | ||
showModal, // 必须 | ||
hideModal, // 必须 | ||
}, | ||
}, | ||
|
||
// 其他... | ||
} | ||
|
||
// 创建创建和工具栏,会用到 editorConfig 。具体查看 wangEditor 文档 | ||
``` | ||
|
||
### 显示 HTML | ||
|
||
mention 节点返回的 HTML 格式如下,其中 `data-info` 的值需要 `decodeURIComponent` 解析。 | ||
|
||
```html | ||
<span data-w-e-type="mention" data-w-e-is-void data-w-e-is-inline data-value="张三" data-info="%7B%22x%22%3A10%7D">@张三</span> | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,47 @@ | ||
{ | ||
"name": "@wangeditor-next/plugin-mention", | ||
"version": "1.0.0", | ||
"description": "wangEditor mention plugin", | ||
"author": "cycleccc <[email protected]>", | ||
"type": "module", | ||
"homepage": "https://github.com/wangeditor-next/wangEditor-next#readme", | ||
"license": "MIT", | ||
"types": "dist/plugin-mention/src/index.d.ts", | ||
"main": "dist/index.js", | ||
"module": "dist/index.mjs", | ||
"exports": { | ||
".": { | ||
"types": "./dist/plugin-mention/src/index.d.ts", | ||
"import": "./dist/index.mjs", | ||
"require": "./dist/index.js" | ||
}, | ||
"./dist/css/style.css": "./dist/css/style.css" | ||
}, | ||
"directories": { | ||
"lib": "dist" | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/wangeditor-next/wangEditor-next.git" | ||
}, | ||
"scripts": { | ||
"dev": "cross-env NODE_ENV=development rollup -c rollup.config.js", | ||
"dev-watch": "cross-env NODE_ENV=development rollup -c rollup.config.js -w", | ||
"build": "cross-env NODE_ENV=production rollup -c rollup.config.js", | ||
"dev-size-stats": "cross-env NODE_ENV=development:size_stats rollup -c rollup.config.js", | ||
"size-stats": "cross-env NODE_ENV=production:size_stats rollup -c rollup.config.js" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/wangeditor-next/wangeditor-next/issues" | ||
}, | ||
"peerDependencies": { | ||
"@wangeditor-next/editor": "5.6.31", | ||
"snabbdom": "^3.1.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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { createRollupConfig } from '@wangeditor-next-shared/rollup-config' | ||
|
||
import pkg from './package.json' assert { type: 'json' } | ||
|
||
const name = 'WangEditorMentionPlugin' | ||
|
||
const configList = [] | ||
|
||
// esm | ||
const esmConf = createRollupConfig({ | ||
output: { | ||
file: pkg.module, | ||
format: 'esm', | ||
name, | ||
}, | ||
}) | ||
|
||
configList.push(esmConf) | ||
|
||
// umd | ||
const umdConf = createRollupConfig({ | ||
output: { | ||
file: pkg.main, | ||
format: 'umd', | ||
name, | ||
}, | ||
}) | ||
|
||
configList.push(umdConf) | ||
|
||
export default configList |
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,11 @@ | ||
/** | ||
* @description src entry | ||
* @author wangfupeng | ||
*/ | ||
|
||
import module from './module/index' | ||
|
||
export * from './module/custom-types' | ||
export * from './module/interface' | ||
|
||
export default module |
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,14 @@ | ||
/** | ||
* @description mention element | ||
* @author wangfupeng | ||
*/ | ||
|
||
type EmptyText = { | ||
text: '' | ||
} | ||
export type MentionElement = { | ||
type: 'mention' | ||
value: string | ||
info: any | ||
children: EmptyText[] // void 元素必须有一个空 text | ||
} |
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,24 @@ | ||
/** | ||
* @description elem to html | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { SlateElement } from '@wangeditor-next/editor' | ||
|
||
import { MentionElement } from './custom-types' | ||
|
||
// 生成 html 的函数 | ||
function mentionToHtml(elem: SlateElement, _childrenHtml: string): string { | ||
const { value = '', info = {} } = elem as MentionElement | ||
const infoStr = encodeURIComponent(JSON.stringify(info)) | ||
|
||
return `<span data-w-e-type="mention" data-w-e-is-void data-w-e-is-inline data-value="${value}" data-info="${infoStr}">@${value}</span>` | ||
} | ||
|
||
// 配置 | ||
const conf = { | ||
type: 'mention', // 节点 type ,重要!!! | ||
elemToHtml: mentionToHtml, | ||
} | ||
|
||
export default conf |
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,20 @@ | ||
/** | ||
* @description mention module entry | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { IModuleConf } from '@wangeditor-next/editor' | ||
|
||
import elemToHtmlConf from './elem-to-html' | ||
import parseHtmlConf from './parse-elem-html' | ||
import withMention from './plugin' | ||
import renderElemConf from './render-elem' | ||
|
||
const module: Partial<IModuleConf> = { | ||
editorPlugin: withMention, | ||
renderElems: [renderElemConf], | ||
elemsToHtml: [elemToHtmlConf], | ||
parseElemsHtml: [parseHtmlConf], | ||
} | ||
|
||
export default module |
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,13 @@ | ||
/** | ||
* @description interface | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { IDomEditor } from '@wangeditor-next/editor' | ||
|
||
export interface IExtendConfig { | ||
mentionConfig: { | ||
showModal: (editor: IDomEditor) => void | ||
hideModal: (editor: IDomEditor) => void | ||
} | ||
} |
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,41 @@ | ||
/** | ||
* @description parse elem html | ||
* @author wangfupeng | ||
*/ | ||
|
||
import { IDomEditor, SlateDescendant, SlateElement } from '@wangeditor-next/editor' | ||
|
||
import { DOMElement } from '../utils/dom' | ||
import { MentionElement } from './custom-types' | ||
|
||
function parseHtml( | ||
elem: DOMElement, | ||
_children: SlateDescendant[], | ||
_editor: IDomEditor, | ||
): SlateElement { | ||
// elem HTML 结构 <span data-w-e-type="mention" data-w-e-is-void data-w-e-is-inline data-value="张三" data-info="xxx">@张三</span> | ||
|
||
const value = elem.getAttribute('data-value') || '' | ||
const rawInfo = decodeURIComponent(elem.getAttribute('data-info') || '') | ||
let info: any | ||
|
||
try { | ||
info = JSON.parse(rawInfo) | ||
} catch (ex) { | ||
info = rawInfo | ||
} | ||
|
||
return { | ||
type: 'mention', | ||
value, | ||
info, | ||
children: [{ text: '' }], // void node 必须有一个空白 text | ||
} as MentionElement | ||
} | ||
|
||
const parseHtmlConf = { | ||
selector: 'span[data-w-e-type="mention"]', | ||
parseElemHtml: parseHtml, | ||
} | ||
|
||
export default parseHtmlConf |
Oops, something went wrong.