Skip to content

Commit 1bc3a1e

Browse files
authored
feat(fluent-editor): add before-editor-init props (#2670)
* feat(fluent-editor): add before-editor-init props * docs(fluent-editor): add before-editor-init api/demo docs * chore: add meta to before-editor-init * docs(fluent-editor): add options demo
1 parent e54f637 commit 1bc3a1e

File tree

8 files changed

+196
-17
lines changed

8 files changed

+196
-17
lines changed

examples/sites/demos/apis/fluent-editor.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ export default {
55
name: 'fluent-editor',
66
type: 'component',
77
props: [
8+
{
9+
name: 'before-editor-init',
10+
type: '(FluentEditor) => void',
11+
defaultValue: '',
12+
meta: {
13+
stable: '3.21.0'
14+
},
15+
desc: {
16+
'zh-CN': 'FluentEditor 初始化之前执行的钩子,用于注册自定义 FluentEditor 模块和格式。',
17+
'en-US': ''
18+
},
19+
pcDemo: 'before-editor-init'
20+
},
821
{
922
name: 'data-type',
1023
type: 'boolean',
@@ -64,7 +77,7 @@ export default {
6477
{
6578
name: 'options',
6679
type: 'object',
67-
defaultValue: "",
80+
defaultValue: '',
6881
desc: {
6982
'zh-CN': '编辑器配置项,参考 Quill 文档:https://quilljs.com/docs/configuration#options',
7083
'en-US': ''
@@ -75,7 +88,7 @@ export default {
7588
{
7689
name: 'zIndex',
7790
type: 'number',
78-
defaultValue: "",
91+
defaultValue: '',
7992
desc: {
8093
'zh-CN': '编辑器的 z-index',
8194
'en-US': ''
@@ -105,6 +118,6 @@ interface IImageUploadOptions {
105118
fail: (serverError: string) => void // 上传失败回调信息
106119
}
107120
`
108-
},
121+
}
109122
]
110123
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<template>
2+
<div>
3+
<tiny-fluent-editor
4+
ref="fluentEditorRef"
5+
v-model="content"
6+
:before-editor-init="beforeEditorInit"
7+
:options="editorOptions"
8+
></tiny-fluent-editor>
9+
内容:<br />
10+
{{ content }}
11+
</div>
12+
</template>
13+
14+
<script setup>
15+
import { ref, onMounted } from 'vue'
16+
import { TinyFluentEditor } from '@opentiny/vue'
17+
18+
const content = ref('{"ops":[{"insert":"Hello "},{"attributes":{"bold":true},"insert":"FluentEditor"},{"insert":"!"}]}')
19+
20+
const fluentEditorRef = ref()
21+
22+
const beforeEditorInit = (FluentEditor) => {
23+
const goodIcon = `<svg t="1734490908682" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5918" width="200" height="200"><path d="M881.066667 394.666667c-21.333333-23.466667-51.2-36.266667-81.066667-36.266667H618.666667v-117.333333c0-44.8-29.866667-85.333333-87.466667-117.333334-17.066667-10.666667-38.4-12.8-57.6-8.533333-19.2 4.266667-36.266667 17.066667-46.933333 34.133333-2.133333 2.133333-2.133333 4.266667-4.266667 6.4l-125.866667 281.6H204.8c-59.733333 0-108.8 46.933333-108.8 106.666667v258.133333c0 57.6 49.066667 106.666667 108.8 106.666667h544c53.333333 0 98.133333-38.4 106.666667-89.6l51.2-337.066667c4.266667-34.133333-6.4-64-25.6-87.466666z m-593.066667 448H204.8c-25.6 0-44.8-19.2-44.8-42.666667v-256c0-23.466667 19.2-42.666667 44.8-42.666667h83.2v341.333334z m554.666667-373.333334L789.333333 806.4c-4.266667 21.333333-21.333333 36.266667-42.666666 36.266667H352V471.466667l130.133333-290.133334c2.133333-4.266667 4.266667-4.266667 6.4-4.266666 2.133333 0 4.266667 0 8.533334 2.133333 25.6 14.933333 55.466667 38.4 55.466666 64v149.333333c0 17.066667 14.933333 32 32 32h213.333334c12.8 0 25.6 4.266667 34.133333 14.933334 8.533333 6.4 12.8 19.2 10.666667 29.866666z" fill="#666666" p-id="5919"></path></svg>`
24+
const badIcon = `<svg t="1734491308472" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3380" width="200" height="200"><path d="M904.533333 522.666667L853.333333 185.6c-8.533333-51.2-55.466667-89.6-106.666666-89.6H204.8c-59.733333 0-108.8 46.933333-108.8 106.666667v258.133333c0 57.6 49.066667 106.666667 108.8 106.666667h91.733333l125.866667 281.6c2.133333 2.133333 2.133333 4.266667 4.266667 6.4 14.933333 23.466667 38.4 36.266667 64 36.266666 12.8 0 25.6-4.266667 38.4-10.666666 57.6-34.133333 87.466667-72.533333 87.466666-117.333334v-117.333333h183.466667c32 0 59.733333-12.8 81.066667-36.266667 19.2-25.6 29.866667-55.466667 23.466666-87.466666z m-616.533333-21.333334H204.8c-25.6 0-44.8-19.2-44.8-42.666666v-256c0-23.466667 19.2-42.666667 44.8-42.666667h83.2v341.333333zM832 567.466667c-8.533333 8.533333-21.333333 14.933333-34.133333 14.933333h-213.333334c-17.066667 0-32 14.933333-32 32v149.333333c0 25.6-29.866667 49.066667-55.466666 64-4.266667 2.133333-10.666667 2.133333-14.933334-4.266666L352 533.333333V160H746.666667c21.333333 0 40.533333 14.933333 42.666666 36.266667L842.666667 533.333333c2.133333 10.666667-2.133333 23.466667-10.666667 34.133334z" fill="#666666" p-id="3381"></path></svg>`
25+
const icons = FluentEditor.import('ui/icons')
26+
icons.good = goodIcon
27+
icons.bad = badIcon
28+
29+
const Parchment = FluentEditor.import('parchment')
30+
31+
const GoodStyle = new Parchment.StyleAttributor('good', 'color', {
32+
scope: Parchment.Scope.INLINE
33+
})
34+
35+
const BadStyle = new Parchment.StyleAttributor('bad', 'color', {
36+
scope: Parchment.Scope.INLINE
37+
})
38+
39+
FluentEditor.register('formats/good', GoodStyle)
40+
FluentEditor.register('formats/bad', BadStyle)
41+
}
42+
43+
const editorOptions = {
44+
modules: {
45+
// 工具栏
46+
toolbar: [
47+
['undo', 'redo', 'clean', 'format-painter'],
48+
['bold', 'italic', 'underline', 'strike'],
49+
[{ list: 'bullet' }, { list: 'ordered' }],
50+
[{ align: '' }, { align: 'center' }, { align: 'right' }],
51+
['better-table'],
52+
['fullscreen'],
53+
['good', 'bad']
54+
]
55+
}
56+
}
57+
58+
onMounted(() => {
59+
const fluentEditor = fluentEditorRef.value.state.quill
60+
const toolbar = fluentEditor.getModule('toolbar')
61+
62+
toolbar.addHandler('good', function (value) {
63+
this.quill.format('good', value ? '#5cb300' : '')
64+
})
65+
66+
toolbar.addHandler('bad', function (value) {
67+
this.quill.format('bad', value ? '#f23030' : '')
68+
})
69+
})
70+
</script>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<template>
2+
<div>
3+
<tiny-fluent-editor
4+
ref="fluentEditorRef"
5+
v-model="content"
6+
:before-editor-init="beforeEditorInit"
7+
:options="editorOptions"
8+
></tiny-fluent-editor>
9+
内容:<br />
10+
{{ content }}
11+
</div>
12+
</template>
13+
14+
<script>
15+
import { TinyFluentEditor } from '@opentiny/vue'
16+
17+
export default {
18+
components: {
19+
TinyFluentEditor
20+
},
21+
data() {
22+
return {
23+
content: '{"ops":[{"insert":"Hello "},{"attributes":{"bold":true},"insert":"FluentEditor"},{"insert":"!"}]}',
24+
editorOptions: {
25+
modules: {
26+
// 工具栏
27+
toolbar: [
28+
['undo', 'redo', 'clean', 'format-painter'],
29+
['bold', 'italic', 'underline', 'strike'],
30+
[{ list: 'bullet' }, { list: 'ordered' }],
31+
[{ align: '' }, { align: 'center' }, { align: 'right' }],
32+
['better-table'],
33+
['fullscreen'],
34+
['good', 'bad']
35+
]
36+
}
37+
}
38+
}
39+
},
40+
methods: {
41+
beforeEditorInit(FluentEditor) {
42+
const goodIcon = `<svg t="1734490908682" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5918" width="200" height="200"><path d="M881.066667 394.666667c-21.333333-23.466667-51.2-36.266667-81.066667-36.266667H618.666667v-117.333333c0-44.8-29.866667-85.333333-87.466667-117.333334-17.066667-10.666667-38.4-12.8-57.6-8.533333-19.2 4.266667-36.266667 17.066667-46.933333 34.133333-2.133333 2.133333-2.133333 4.266667-4.266667 6.4l-125.866667 281.6H204.8c-59.733333 0-108.8 46.933333-108.8 106.666667v258.133333c0 57.6 49.066667 106.666667 108.8 106.666667h544c53.333333 0 98.133333-38.4 106.666667-89.6l51.2-337.066667c4.266667-34.133333-6.4-64-25.6-87.466666z m-593.066667 448H204.8c-25.6 0-44.8-19.2-44.8-42.666667v-256c0-23.466667 19.2-42.666667 44.8-42.666667h83.2v341.333334z m554.666667-373.333334L789.333333 806.4c-4.266667 21.333333-21.333333 36.266667-42.666666 36.266667H352V471.466667l130.133333-290.133334c2.133333-4.266667 4.266667-4.266667 6.4-4.266666 2.133333 0 4.266667 0 8.533334 2.133333 25.6 14.933333 55.466667 38.4 55.466666 64v149.333333c0 17.066667 14.933333 32 32 32h213.333334c12.8 0 25.6 4.266667 34.133333 14.933334 8.533333 6.4 12.8 19.2 10.666667 29.866666z" fill="#666666" p-id="5919"></path></svg>`
43+
const badIcon = `<svg t="1734491308472" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3380" width="200" height="200"><path d="M904.533333 522.666667L853.333333 185.6c-8.533333-51.2-55.466667-89.6-106.666666-89.6H204.8c-59.733333 0-108.8 46.933333-108.8 106.666667v258.133333c0 57.6 49.066667 106.666667 108.8 106.666667h91.733333l125.866667 281.6c2.133333 2.133333 2.133333 4.266667 4.266667 6.4 14.933333 23.466667 38.4 36.266667 64 36.266666 12.8 0 25.6-4.266667 38.4-10.666666 57.6-34.133333 87.466667-72.533333 87.466666-117.333334v-117.333333h183.466667c32 0 59.733333-12.8 81.066667-36.266667 19.2-25.6 29.866667-55.466667 23.466666-87.466666z m-616.533333-21.333334H204.8c-25.6 0-44.8-19.2-44.8-42.666666v-256c0-23.466667 19.2-42.666667 44.8-42.666667h83.2v341.333333zM832 567.466667c-8.533333 8.533333-21.333333 14.933333-34.133333 14.933333h-213.333334c-17.066667 0-32 14.933333-32 32v149.333333c0 25.6-29.866667 49.066667-55.466666 64-4.266667 2.133333-10.666667 2.133333-14.933334-4.266666L352 533.333333V160H746.666667c21.333333 0 40.533333 14.933333 42.666666 36.266667L842.666667 533.333333c2.133333 10.666667-2.133333 23.466667-10.666667 34.133334z" fill="#666666" p-id="3381"></path></svg>`
44+
const icons = FluentEditor.import('ui/icons')
45+
icons.good = goodIcon
46+
icons.bad = badIcon
47+
48+
const Parchment = FluentEditor.import('parchment')
49+
50+
const GoodStyle = new Parchment.StyleAttributor('good', 'color', {
51+
scope: Parchment.Scope.INLINE
52+
})
53+
54+
const BadStyle = new Parchment.StyleAttributor('bad', 'color', {
55+
scope: Parchment.Scope.INLINE
56+
})
57+
58+
FluentEditor.register('formats/good', GoodStyle)
59+
FluentEditor.register('formats/bad', BadStyle)
60+
}
61+
},
62+
mounted() {
63+
const fluentEditor = this.$refs.fluentEditorRef.state.quill
64+
const toolbar = fluentEditor.getModule('toolbar')
65+
66+
toolbar.addHandler('good', function (value) {
67+
this.quill.format('good', value ? '#5cb300' : '')
68+
})
69+
70+
toolbar.addHandler('bad', function (value) {
71+
this.quill.format('bad', value ? '#f23030' : '')
72+
})
73+
}
74+
}
75+
</script>

examples/sites/demos/pc/app/fluent-editor/webdoc/fluent-editor.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ export default {
6363
'en-US': ''
6464
},
6565
codeFiles: ['data-switch.vue']
66+
},
67+
{
68+
demoId: 'before-editor-init',
69+
name: {
70+
'zh-CN': '初始化前的钩子',
71+
'en-US': ''
72+
},
73+
desc: {
74+
'zh-CN':
75+
'<p>通过 <code>before-editor-init</code> 设置 FluentEditor 初始化前的钩子函数,主要用于注册 FluentEditor 自定义格式和模块。<br>这个示例增加了两个新的格式:good / bad,并在工具栏增加了对应的图标用于设置这两种格式。<br>选中一段文本,点击点赞图标,会将文本色设置成绿色;点击点踩图标,会将文本色设置成红色。</p>',
76+
'en-US': ''
77+
},
78+
codeFiles: ['before-editor-init.vue']
6679
}
6780
]
6881
}

packages/renderless/src/fluent-editor/index.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export const init =
4949
state.innerOptions.modules.toolbar = simpleToolbar
5050
}
5151

52+
props.beforeEditorInit?.(FluentEditor)
53+
5254
const quill = new FluentEditor(vm.$refs.editor, state.innerOptions)
5355
quill.emitter.on('file-change', api.fileOperationToSev)
5456
state.quill = Object.freeze(quill)
@@ -194,8 +196,8 @@ export const handleCompositionend =
194196
state.quill.root.classList.add('ql-blank')
195197
}
196198
} else {
197-
let data = state.quill.container.innerHTML,
198-
range = state.quill.getSelection(true)
199+
let data = state.quill.container.innerHTML
200+
let range = state.quill.getSelection(true)
199201
const [mentionItem, offset] = state.quill.getLeaf(range.index)
200202

201203
if (mentionItem.statics.blotName === 'break' || (mentionItem.statics.blotName === 'text' && offset === 0)) {
@@ -204,8 +206,8 @@ export const handleCompositionend =
204206
if (mentionItem.statics.blotName === 'break') {
205207
state.quill.setSelection(range.index + event.data.length)
206208
} else {
207-
let pattern = /[\u4E00-\u9FA5\uf900-\ufa2d]/,
208-
flag
209+
let pattern = /[\u4E00-\u9FA5\uF900-\uFA2D]/
210+
let flag
209211

210212
if (pattern.test(event.data)) {
211213
flag = true
@@ -298,8 +300,8 @@ export const inputFileHandler =
298300
fileInput.setAttribute('accept', mimeTypes)
299301

300302
if (
301-
(UploaderDfls.enableMultiUpload['file'] && type === 'file') ||
302-
(UploaderDfls.enableMultiUpload['image'] && type === 'image')
303+
(UploaderDfls.enableMultiUpload.file && type === 'file') ||
304+
(UploaderDfls.enableMultiUpload.image && type === 'image')
303305
) {
304306
fileInput.setAttribute('multiple', '')
305307
}
@@ -343,12 +345,12 @@ export const uploaderDflsHandler =
343345
export const handleUploadFile =
344346
({ api, UploaderDfls }) =>
345347
(range, files, hasRejectedFile) => {
346-
const fileEnableMultiUpload = UploaderDfls.enableMultiUpload === true || UploaderDfls.enableMultiUpload['file']
348+
const fileEnableMultiUpload = UploaderDfls.enableMultiUpload === true || UploaderDfls.enableMultiUpload.file
347349

348350
api.fileOperationToSev({
349351
operation: 'upload',
350352
data: fileEnableMultiUpload ? { files } : { file: files[0] },
351-
hasRejectedFile: hasRejectedFile,
353+
hasRejectedFile,
352354
callback: (res) => {
353355
if (!res) {
354356
return
@@ -490,11 +492,11 @@ export const handleUploadImage =
490492
(range, { file, files }, hasRejectedImage) => {
491493
if (state.quill.options.uploadOption.imageUploadToServer) {
492494
const index = state.promisesData.length
493-
const imageEnableMultiUpload = UploaderDfls.enableMultiUpload['image']
495+
const imageEnableMultiUpload = UploaderDfls.enableMultiUpload.image
494496
const result = {
495497
file,
496498
data: { files: [file] },
497-
hasRejectedImage: hasRejectedImage,
499+
hasRejectedImage,
498500
callback: (res) => {
499501
if (!res) {
500502
return
@@ -526,7 +528,7 @@ export const handleUploadImage =
526528
}
527529

528530
if (imageEnableMultiUpload) {
529-
result['data'] = { files }
531+
result.data = { files }
530532
}
531533

532534
state.promisesData.push({
@@ -863,7 +865,7 @@ export const handleDblclick =
863865
props.picPreview &&
864866
e &&
865867
e.type === 'dblclick' &&
866-
[...e.target.classList].indexOf('blot-formatter__overlay') > -1 &&
868+
[...e.target.classList].includes('blot-formatter__overlay') &&
867869
e.target.dataset.image
868870
) {
869871
api.doPreview(e.target)

packages/vue/src/fluent-editor/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ export const fluentEditorProps = {
6565
imagePasteFailCallback: {
6666
type: Function,
6767
default: () => {}
68+
},
69+
beforeEditorInit: {
70+
type: Function,
71+
default: () => {}
6872
}
6973
}
7074

packages/vue/src/fluent-editor/src/mobile-first.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ export default defineComponent({
102102
'dataType',
103103
'dataUpgrade',
104104
'zIndex',
105-
'imagePasteFailCallback'
105+
'imagePasteFailCallback',
106+
'beforeEditorInit'
106107
],
107108
setup(props, context) {
108109
return setup({

packages/vue/src/fluent-editor/src/pc.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ export default defineComponent({
103103
'dataType',
104104
'dataUpgrade',
105105
'zIndex',
106-
'imagePasteFailCallback'
106+
'imagePasteFailCallback',
107+
'beforeEditorInit'
107108
],
108109
setup(props, context): any {
109110
return setup({

0 commit comments

Comments
 (0)