deps: 更新 froala 版本,去除 jQuery 依赖;修复 rootClose 不生效问题 (#2640)

* deps: 更新 froala 版本,去除 jQuery 依赖;修复 rootClose 不生效问题

* 修测试用例报错

* 修复 nestedselect 弹不出问题
This commit is contained in:
吴多益 2021-10-08 10:36:37 +08:00 committed by GitHub
parent 4f0ef7f04f
commit 19ea049a54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 180 additions and 566 deletions

View File

@ -62,7 +62,7 @@ test('Renderer:FormItem:validateApi:success', async () => {
await wait(100);
expect(notify).toHaveBeenCalledWith(
'error',
'依赖的部分字段没有通过验证\na: 这是必填项'
'依赖的部分字段没有通过验证\n这是必填项'
);
const input = container.querySelector('input[name=a]');
@ -134,7 +134,7 @@ test('Renderer:FormItem:validateApi:failed', async () => {
await wait(100);
expect(notify).toHaveBeenCalledWith(
'error',
'依赖的部分字段没有通过验证\na: 这是必填项'
'依赖的部分字段没有通过验证\n这是必填项'
);
const input = container.querySelector('input[name=a]');

View File

@ -108,7 +108,7 @@ test('Renderer:Form:valdiate', async () => {
await wait(100);
expect(notify).toHaveBeenCalledWith(
'error',
'依赖的部分字段没有通过验证\na: 这是必填项'
'依赖的部分字段没有通过验证\n这是必填项'
);
const input = container.querySelector('input[name=a]');
@ -233,7 +233,7 @@ test('Renderer:Form:onValidate', async () => {
await wait(100);
expect(notify).toHaveBeenCalledWith(
'error',
'依赖的部分字段没有通过验证\na: a is wrong\nb: b is wrong\nb: b is wrong 2'
'依赖的部分字段没有通过验证\na is wrong\nb is wrong\nb is wrong 2'
);
fireEvent.click(getByText('Submit'));

View File

@ -117,25 +117,22 @@ froala 可以通过设置 buttons 参数来控制显示哪些按钮,默认是
```json
[
"undo",
"redo",
"paragraphFormat",
"quote",
"color",
"|",
"textColor",
"backgroundColor",
"bold",
"italic",
"underline",
"strikeThrough",
"|",
"formatOL",
"formatUL",
"align",
"|",
"quote",
"insertLink",
"insertImage",
"insertEmotion",
"insertTable",
"|",
"undo",
"redo",
"html"
]
```

View File

@ -547,7 +547,7 @@ export const components = [
)
},
{
label: 'InpuRichText 富文本编辑器',
label: 'InputRichText 富文本编辑器',
path: '/zh-CN/components/form/input-rich-text',
getComponent: () =>
import('../../docs/zh-CN/components/form/input-rich-text.md').then(

View File

@ -5,7 +5,6 @@
const mapping: {
[propName: string]: any;
} = {
'jquery': __moduleId('jquery'),
'react': __moduleId('react'),
'react-dom': __moduleId('react-dom'),
'immutability-helper': __moduleId('immutability-helper'),

View File

@ -491,8 +491,8 @@ if (fis.project.currentMedia() === 'publish') {
'!mpegts.js/**',
'!hls.js/**',
'!froala-editor/**',
'!react-froala-wysiwyg/**',
'!tinymce/**',
'!jquery/**',
'!zrender/**',
'!echarts/**',
'!echarts-stat/**',
@ -525,7 +525,7 @@ if (fis.project.currentMedia() === 'publish') {
'rich-text.js': [
'src/components/RichText.tsx',
'froala-editor/**',
'jquery/**'
'react-froala-wysiwyg/**'
],
'tinymce.js': ['src/components/Tinymce.tsx', 'tinymce/**'],
@ -565,8 +565,8 @@ if (fis.project.currentMedia() === 'publish') {
'!mpegts.js/**',
'!hls.js/**',
'!froala-editor/**',
'!react-froala-wysiwyg/**',
'!src/components/RichText.tsx',
'!jquery/**',
'!zrender/**',
'!echarts/**',
'!papaparse/**',
@ -786,8 +786,8 @@ if (fis.project.currentMedia() === 'publish') {
'!mpegts.js/**',
'!hls.js/**',
'!froala-editor/**',
'!react-froala-wysiwyg/**',
'!tinymce/**',
'!jquery/**',
'!zrender/**',
'!echarts/**',
'!echarts-stat/**',
@ -820,7 +820,7 @@ if (fis.project.currentMedia() === 'publish') {
'pkg/rich-text.js': [
'src/components/RichText.js',
'froala-editor/**',
'jquery/**'
'react-froala-wysiwyg/**'
],
'pkg/tinymce.js': ['src/components/Tinymce.tsx', 'tinymce/**'],
@ -873,8 +873,8 @@ if (fis.project.currentMedia() === 'publish') {
'!mpegts.js/**',
'!hls.js/**',
'!froala-editor/**',
'!react-froala-wysiwyg/**',
'!src/components/RichText.tsx',
'!jquery/**',
'!zrender/**',
'!echarts/**',
'!papaparse/**',

View File

@ -48,12 +48,11 @@
"echarts-stat": "^1.2.0",
"exceljs": "^4.3.0",
"file-saver": "^2.0.2",
"froala-editor": "2.9.6",
"froala-editor": "3.1.1",
"hls.js": "1.0.10",
"hoist-non-react-statics": "^3.3.2",
"hotkeys-js": "^3.8.7",
"immutability-helper": "^3.1.1",
"jquery": "^3.2.1",
"keycode": "^2.1.9",
"lodash": "^4.17.15",
"markdown-it": "^12.0.6",
@ -77,6 +76,7 @@
"react-datetime": "2.16.2",
"react-dom": "^16.8.6",
"react-dropzone": "^11.4.2",
"react-froala-wysiwyg": "3.1.1",
"react-input-range": "1.3.0",
"react-json-view": "1.21.3",
"react-overlays": "5.1.1",

View File

@ -29,7 +29,11 @@
max-height: 400px;
box-shadow: none;
overflow: auto;
border-top: 1px solid var(--Form-input-borderColor);
border: none;
}
.second-toolbar {
border: none;
}
.fr-toolbar .fr-command.fr-btn,
@ -67,11 +71,6 @@
&.is-focused {
border: var(--Form-input-borderWidth) solid
var(--Form-input-onFocused-borderColor);
.fr-box.fr-basic.fr-top .fr-wrapper {
border-top: var(--Form-input-borderWidth) solid
var(--Form-input-onFocused-borderColor);
}
}
.fr-box.fr-basic .fr-element {

View File

@ -290,7 +290,13 @@ export default class Overlay extends React.Component<
// This goes after everything else because it adds a wrapping div.
if (rootClose) {
child = <RootClose onRootClose={props.onHide}>{child}</RootClose>;
child = (
<RootClose onRootClose={props.onHide}>
{(ref: any) => {
return <div ref={ref}>{child}</div>;
}}
</RootClose>
);
}
// @ts-ignore

View File

@ -5,454 +5,92 @@
*/
import React from 'react';
import $ from 'jquery';
// Require Editor JS files.
// import 'froala-editor/js/froala_editor.pkgd.min.js';
[
require('froala-editor/js/froala_editor.min.js'),
require('froala-editor/js/plugins/align.min'),
require('froala-editor/js/plugins/char_counter.min'),
require('froala-editor/js/plugins/code_beautifier.min'),
require('froala-editor/js/plugins/code_view.min'),
require('froala-editor/js/plugins/colors.min'),
require('froala-editor/js/plugins/draggable.min'),
require('froala-editor/js/plugins/emoticons.min'),
require('froala-editor/js/plugins/entities.min'),
// require('froala-editor/js/plugins/file.min'),
require('froala-editor/js/plugins/font_family.min'),
require('froala-editor/js/plugins/font_size.min'),
require('froala-editor/js/plugins/forms.min'),
require('froala-editor/js/plugins/fullscreen.min'),
require('froala-editor/js/plugins/help.min'),
require('froala-editor/js/plugins/image.min'),
require('froala-editor/js/plugins/image_manager.min'),
require('froala-editor/js/plugins/inline_class.min'),
require('froala-editor/js/plugins/inline_style.min'),
require('froala-editor/js/plugins/line_breaker.min'),
require('froala-editor/js/plugins/line_height.min'),
require('froala-editor/js/plugins/link.min'),
require('froala-editor/js/plugins/lists.min'),
require('froala-editor/js/plugins/paragraph_format.min'),
require('froala-editor/js/plugins/paragraph_style.min'),
require('froala-editor/js/plugins/print.min'),
require('froala-editor/js/plugins/quick_insert.min'),
require('froala-editor/js/plugins/quote.min'),
require('froala-editor/js/plugins/save.min'),
require('froala-editor/js/plugins/special_characters.min'),
require('froala-editor/js/plugins/table.min'),
require('froala-editor/js/plugins/url.min'),
require('froala-editor/js/plugins/video.min'),
require('froala-editor/js/plugins/word_paste.min')
].forEach(init => init());
// @ts-ignore
import FroalaEditorComponent from 'react-froala-wysiwyg';
// @ts-ignore
import Froala from 'froala-editor/js/froala_editor.min.js';
import 'froala-editor/js/plugins/align.min';
import 'froala-editor/js/plugins/colors.min';
import 'froala-editor/js/plugins/char_counter.min';
import 'froala-editor/js/plugins/code_view.min';
import 'froala-editor/js/plugins/draggable.min';
import 'froala-editor/js/plugins/entities.min';
import 'froala-editor/js/plugins/font_family.min';
import 'froala-editor/js/plugins/font_size.min';
import 'froala-editor/js/plugins/forms.min';
import 'froala-editor/js/plugins/fullscreen.min';
import 'froala-editor/js/plugins/help.min';
import 'froala-editor/js/plugins/image.min';
import 'froala-editor/js/plugins/inline_class.min';
import 'froala-editor/js/plugins/inline_style.min';
import 'froala-editor/js/plugins/line_breaker.min';
import 'froala-editor/js/plugins/line_height.min';
import 'froala-editor/js/plugins/link.min';
import 'froala-editor/js/plugins/lists.min';
import 'froala-editor/js/plugins/paragraph_format.min';
import 'froala-editor/js/plugins/paragraph_style.min';
import 'froala-editor/js/plugins/print.min';
import 'froala-editor/js/plugins/quick_insert.min';
import 'froala-editor/js/plugins/quote.min';
import 'froala-editor/js/plugins/save.min';
import 'froala-editor/js/plugins/special_characters.min';
import 'froala-editor/js/plugins/table.min';
import 'froala-editor/js/plugins/url.min';
import 'froala-editor/js/plugins/video.min';
import 'froala-editor/js/plugins/word_paste.min';
import 'froala-editor/js/languages/zh_cn.js';
// Require Editor CSS files.
import 'froala-editor/css/froala_style.min.css';
import 'froala-editor/css/froala_editor.pkgd.min.css';
import {resizeSensor} from '../utils/resize-sensor';
export default class FroalaEditor extends React.Component<any, any> {
listeningEvents: Array<any> = [];
$element: any = null;
$editor: any = null;
config: any = {
immediateReactModelUpdate: false,
reactIgnoreAttrs: null
};
editorInitialized: boolean = false;
oldModel: any = null;
constructor(props: any) {
super(props);
this.textareaRef = this.textareaRef.bind(this);
Froala.VIDEO_PROVIDERS = [
{
test_regex: /.*/,
url_regex: '',
url_text: '',
html: '<span class="fr-video fr-dvb fr-draggable" contenteditable="false" draggable="true"><video class="fr-draggable" controls="" data-msg="ok" data-status="0" src="{url}" style="width: 600px;"></video></span>'
}
];
this.state = {
model: this.props.model
};
this.handleModelChange = this.handleModelChange.bind(this);
}
componentDidUpdate() {
if (JSON.stringify(this.oldModel) == JSON.stringify(this.props.model)) {
return;
}
this.setContent();
this.handleModelChange(this.props.model);
}
textareaRef(ref: any) {
ref ? this.createEditor(ref) : this.destroyEditor();
}
createEditor(ref: any) {
if (this.editorInitialized) {
return;
}
this.config = this.props.config || this.config;
this.$element = $(ref);
this.setContent(true);
this.registerEvents();
resizeSensor(ref.parentElement, () => {
$(ref).prev('.fr-box').find('.fr-toolbar').css('width', '');
});
this.$editor = this.$element
.froalaEditor(this.config)
.data('froala.editor').$el;
this.initListeners();
this.editorInitialized = true;
}
setContent(firstTime: boolean = false) {
if (!this.editorInitialized && !firstTime) {
return;
}
if (this.props.model || this.props.model == '') {
this.oldModel = this.props.model;
this.setNormalTagContent(firstTime);
}
}
setNormalTagContent(firstTime: boolean) {
let self = this;
function htmlSet() {
self.$element.froalaEditor('html.set', self.props.model || '', true);
//This will reset the undo stack everytime the model changes externally. Can we fix this?
self.$element.froalaEditor('undo.reset');
self.$element.froalaEditor('undo.saveStep');
}
if (firstTime) {
this.registerEvent(this.$element, 'froalaEditor.initialized', htmlSet);
} else {
htmlSet();
}
}
getEditor() {
if (this.$element) {
return this.$element.froalaEditor.bind(this.$element);
}
return null;
}
updateModel() {
handleModelChange(model: string) {
if (!this.props.onModelChange) {
return;
}
let modelContent = '';
let returnedHtml = this.$element.froalaEditor('html.get');
if (typeof returnedHtml === 'string') {
modelContent = returnedHtml;
}
this.oldModel = modelContent;
this.props.onModelChange(modelContent);
}
initListeners() {
let self = this;
// bind contentChange and keyup event to froalaModel
this.registerEvent(
this.$element,
'froalaEditor.contentChanged',
function () {
self.updateModel();
}
);
if (this.config.immediateReactModelUpdate) {
this.registerEvent(this.$editor, 'keyup', function () {
self.updateModel();
});
}
}
// register event on jquery editor element
registerEvent(element: any, eventName: any, callback: any) {
if (!element || !eventName || !callback) {
return;
}
this.listeningEvents.push(eventName);
element.on(eventName, callback);
}
registerEvents() {
let events = this.config.events;
if (!events) {
return;
}
for (let event in events) {
if (events.hasOwnProperty(event)) {
this.registerEvent(this.$element, event, events[event]);
}
}
}
destroyEditor() {
if (this.$element) {
this.listeningEvents && this.$element.off(this.listeningEvents.join(' '));
this.$editor.off('keyup');
this.$element.froalaEditor('destroy');
this.listeningEvents.length = 0;
this.$element = null;
this.editorInitialized = false;
}
this.oldModel = model;
this.props.onModelChange(model);
this.setState({
model: model
});
}
render() {
return <textarea ref={this.textareaRef} />;
return (
<FroalaEditorComponent
tag="textarea"
config={this.props.config}
model={this.state.model}
onModelChange={this.handleModelChange}
/>
);
}
}
// 不限制视频插入。
($ as any).FE.VIDEO_PROVIDERS = [
{
test_regex: /.*/,
url_regex: '',
url_text: '',
html:
'<span class="fr-video fr-dvb fr-draggable" contenteditable="false" draggable="true"><video class="fr-draggable" controls="" data-msg="ok" data-status="0" src="{url}" style="width: 600px;"></video></span>'
}
];
($ as any).FE.LANGUAGE['zh_cn'] = {
translation: {
// Place holder
'Type something': '\u8f93\u5165\u4e00\u4e9b\u5185\u5bb9',
// Basic formatting
'Bold': '\u7c97\u4f53',
'Italic': '\u659c\u4f53',
'Underline': '\u4e0b\u5212\u7ebf',
'Strikethrough': '\u5220\u9664\u7ebf',
// Main buttons
'Insert': '\u63d2\u5165',
'Delete': '\u5220\u9664',
'Cancel': '\u53d6\u6d88',
'OK': '\u786e\u5b9a',
'Back': '\u80cc\u90e8',
'Remove': '\u53bb\u6389',
'More': '\u66f4\u591a',
'Update': '\u66f4\u65b0',
'Style': '\u98ce\u683c',
// Font
'Font Family': '\u5b57\u4f53',
'Font Size': '\u5b57\u53f7',
// Colors
'Colors': '\u989c\u8272',
'Background': '\u80cc\u666f',
'Text': '\u6587\u5b57',
// Paragraphs
'Paragraph Format': '\u683c\u5f0f',
'Normal': '\u6b63\u5e38',
'Code': '\u4ee3\u7801',
'Heading 1': '\u6807\u98981',
'Heading 2': '\u6807\u98982',
'Heading 3': '\u6807\u98983',
'Heading 4': '\u6807\u98984',
// Style
'Paragraph Style': '\u6bb5\u843d\u6837\u5f0f',
'Inline Style': '\u5185\u8054\u6837\u5f0f',
// Alignment
'Align': '\u5bf9\u9f50\u65b9\u5f0f',
'Align Left': '\u5de6\u5bf9\u9f50',
'Align Center': '\u5c45\u4e2d',
'Align Right': '\u53f3\u5bf9\u9f50',
'Align Justify': '\u4e24\u7aef\u5bf9\u9f50',
'None': '\u65e0',
// Lists
'Ordered List': '\u7f16\u53f7\u5217\u8868',
'Unordered List': '\u9879\u76ee\u7b26\u53f7',
// Indent
'Decrease Indent': '\u51cf\u5c11\u7f29\u8fdb',
'Increase Indent': '\u589e\u52a0\u7f29\u8fdb',
// Links
'Insert Link': '\u63d2\u5165\u94fe\u63a5',
'Open in new tab': '\u5f00\u542f\u5728\u65b0\u6807\u7b7e\u9875',
'Open Link': '\u6253\u5f00\u94fe\u63a5',
'Edit Link': '\u7f16\u8f91\u94fe\u63a5',
'Unlink': '\u5220\u9664\u94fe\u63a5',
'Choose Link': '\u9009\u62e9\u94fe\u63a5',
// Images
'Insert Image': '\u63d2\u5165\u56fe\u7247',
'Upload Image': '\u4e0a\u4f20\u56fe\u7247',
'By URL': '\u901a\u8fc7\u7f51\u5740',
'Browse': '\u6d4f\u89c8',
'Drop image': '\u56fe\u50cf\u62d6\u653e',
'or click': '\u6216\u70b9\u51fb',
'Manage Images': '\u7ba1\u7406\u56fe\u50cf',
'Loading': '\u8f7d\u5165\u4e2d',
'Deleting': '\u5220\u9664',
'Tags': '\u6807\u7b7e',
'Are you sure? Image will be deleted.':
'\u4f60\u786e\u5b9a\u5417\uff1f\u56fe\u50cf\u5c06\u88ab\u5220\u9664\u3002',
'Replace': '\u66f4\u6362',
'Uploading': '\u4e0a\u4f20',
'Loading image': '\u5bfc\u5165\u56fe\u50cf',
'Display': '\u663e\u793a',
'Inline': '\u6392\u961f',
'Break Text': '\u65ad\u5f00\u6587\u672c',
'Alternate Text': '\u5907\u7528\u6587\u672c',
'Change Size': '\u5c3a\u5bf8\u53d8\u5316',
'Width': '\u5bbd\u5ea6',
'Height': '\u9ad8\u5ea6',
'Something went wrong. Please try again.':
'\u51fa\u4e86\u4e9b\u95ee\u9898\u3002 \u8bf7\u518d\u8bd5\u4e00\u6b21\u3002',
// Video
'Insert Video': '\u63d2\u5165\u89c6\u9891',
'Embedded Code': '\u5d4c\u5165\u5f0f\u4ee3\u7801',
// Tables
'Insert Table': '\u63d2\u5165\u8868\u683c',
'Table Header': '\u8868\u5934',
'Remove Table': '\u5220\u9664\u8868',
'Table Style': '\u8868\u683c\u6837\u5f0f',
'Horizontal Align': '\u6c34\u5e73\u5bf9\u9f50\u65b9\u5f0f',
'Row': '\u884c',
'Insert row above': '\u5728\u4e0a\u65b9\u63d2\u5165',
'Insert row below': '\u5728\u4e0b\u65b9\u63d2\u5165',
'Delete row': '\u5220\u9664\u884c',
'Column': '\u5217',
'Insert column before': '\u5728\u5de6\u4fa7\u63d2\u5165',
'Insert column after': '\u5728\u53f3\u4fa7\u63d2\u5165',
'Delete column': '\u5220\u9664\u5217',
'Cell': '\u5355\u5143\u683c',
'Merge cells': '\u5408\u5e76\u5355\u5143\u683c',
'Horizontal split': '\u6c34\u5e73\u5206\u5272',
'Vertical split': '\u5782\u76f4\u5206\u5272',
'Cell Background': '\u5355\u5143\u683c\u80cc\u666f',
'Vertical Align': '\u5782\u76f4\u5bf9\u9f50\u65b9\u5f0f',
'Top': '\u6700\u4f73',
'Middle': '\u4e2d\u95f4',
'Bottom': '\u5e95\u90e8',
'Align Top': '\u9876\u90e8\u5bf9\u9f50',
'Align Middle': '\u4e2d\u95f4\u5bf9\u9f50',
'Align Bottom': '\u5e95\u90e8\u5bf9\u9f50',
'Cell Style': '\u5355\u5143\u683c\u6837\u5f0f',
// Files
'Upload File': '\u4e0a\u4f20\u6587\u4ef6',
'Drop file': '\u6587\u4ef6\u62d6\u653e',
// Emoticons
'Emoticons': '\u8868\u60c5',
'Grinning face': '\u8138\u4e0a\u7b11\u563b\u563b',
'Grinning face with smiling eyes': '',
'Face with tears of joy':
'\u7b11\u563b\u563b\u7684\u8138\uff0c\u542b\u7b11\u7684\u773c\u775b',
'Smiling face with open mouth': '\u7b11\u8138\u5f20\u5f00\u5634',
'Smiling face with open mouth and smiling eyes':
'\u7b11\u8138\u5f20\u5f00\u5634\u5fae\u7b11\u7684\u773c\u775b',
'Smiling face with open mouth and cold sweat':
'\u7b11\u8138\u5f20\u5f00\u5634\uff0c\u4e00\u8eab\u51b7\u6c57',
'Smiling face with open mouth and tightly-closed eyes':
'\u7b11\u8138\u5f20\u5f00\u5634\uff0c\u7d27\u7d27\u95ed\u7740\u773c\u775b',
'Smiling face with halo': '\u7b11\u8138\u6655',
'Smiling face with horns': '\u5fae\u7b11\u7684\u8138\u89d2',
'Winking face': '\u7728\u773c\u8868\u60c5',
'Smiling face with smiling eyes':
'\u9762\u5e26\u5fae\u7b11\u7684\u773c\u775b',
'Face savoring delicious food':
'\u9762\u5bf9\u54c1\u5c1d\u7f8e\u5473\u7684\u98df\u7269',
'Relieved face': '\u9762\u5bf9\u5982\u91ca\u91cd\u8d1f',
'Smiling face with heart-shaped eyes':
'\u5fae\u7b11\u7684\u8138\uff0c\u5fc3\u810f\u5f62\u7684\u773c\u775b',
'Smiling face with sunglasses': '\u7b11\u8138\u592a\u9633\u955c',
'Smirking face': '\u9762\u5bf9\u9762\u5e26\u7b11\u5bb9',
'Neutral face': '\u4e2d\u6027\u9762',
'Expressionless face': '\u9762\u65e0\u8868\u60c5',
'Unamused face': '\u4e00\u8138\u4e0d\u5feb\u7684\u8138',
'Face with cold sweat': '\u9762\u5bf9\u51b7\u6c57',
'Pensive face': '\u6c89\u601d\u7684\u8138',
'Confused face': '\u9762\u5bf9\u56f0\u60d1',
'Confounded face': '\u8be5\u6b7b\u7684\u8138',
'Kissing face': '\u9762\u5bf9\u63a5\u543b',
'Face throwing a kiss': '\u9762\u5bf9\u6295\u63b7\u4e00\u4e2a\u543b',
'Kissing face with smiling eyes':
'\u63a5\u543b\u8138\uff0c\u542b\u7b11\u7684\u773c\u775b',
'Kissing face with closed eyes':
'\u63a5\u543b\u7684\u8138\u95ed\u7740\u773c\u775b',
'Face with stuck out tongue': '\u9762\u5bf9\u4f38\u51fa\u820c\u5934',
'Face with stuck out tongue and winking eye':
'\u9762\u5bf9\u4f38\u51fa\u820c\u5934\u548c\u7728\u52a8\u7684\u773c\u775b',
'Face with stuck out tongue and tightly-closed eyes':
'\u9762\u5bf9\u4f38\u51fa\u820c\u5934\u548c\u7d27\u95ed\u7684\u773c\u775b',
'Disappointed face': '\u9762\u5bf9\u5931\u671b',
'Worried face': '\u9762\u5bf9\u62c5\u5fc3',
'Angry face': '\u6124\u6012\u7684\u8138',
'Pouting face': '\u9762\u5bf9\u5658\u5634',
'Crying face': '\u54ed\u6ce3\u7684\u8138',
'Persevering face': '\u600e\u5948\u8138',
'Face with look of triumph': '\u9762\u5e26\u770b\u7684\u80dc\u5229',
'Disappointed but relieved face':
'\u5931\u671b\uff0c\u4f46\u8138\u4e0a\u91ca\u7136',
'Frowning face with open mouth':
'\u9762\u5bf9\u76b1\u7740\u7709\u5934\u5f20\u53e3',
'Anguished face': '\u9762\u5bf9\u75db\u82e6',
'Fearful face': '\u53ef\u6015\u7684\u8138',
'Weary face': '\u9762\u5bf9\u538c\u5026',
'Sleepy face': '\u9762\u5bf9\u56f0',
'Tired face': '\u75b2\u60eb\u7684\u8138',
'Grimacing face': '\u72f0\u72de\u7684\u8138',
'Loudly crying face': '\u5927\u58f0\u54ed\u8138',
'Face with open mouth': '\u9762\u5bf9\u5f20\u5f00\u5634',
'Hushed face': '\u5b89\u9759\u7684\u8138',
'Face with open mouth and cold sweat': '',
'Face screaming in fear':
'\u9762\u5bf9\u5f20\u5f00\u5634\uff0c\u4e00\u8eab\u51b7\u6c57',
'Astonished face': '\u9762\u5bf9\u60ca\u8bb6',
'Flushed face': '\u7ea2\u6251\u6251\u7684\u8138\u86cb',
'Sleeping face': '\u719f\u7761\u7684\u8138',
'Dizzy face': '\u9762\u5bf9\u7729',
'Face without mouth': '\u8138\u4e0a\u6ca1\u6709\u5634',
'Face with medical mask': '\u9762\u5bf9\u533b\u7597\u53e3\u7f69',
// Line breaker
'Break': '\u7834',
// Math
'Subscript': '\u4e0b\u6807',
'Superscript': '\u4e0a\u6807',
// Full screen
'Fullscreen': '\u5168\u5c4f',
// Horizontal line
'Insert Horizontal Line': '\u63d2\u5165\u6c34\u5e73\u7ebf',
// Clear formatting
'Clear Formatting': '\u683c\u5f0f\u5316\u5220\u9664',
// Undo, redo
'Undo': '\u64a4\u6d88',
'Redo': '\u91cd\u590d',
// Select all
'Select All': '\u5168\u9009',
// Code view
'Code View': '\u4ee3\u7801\u89c6\u56fe',
// Quote
'Quote': '\u5f15\u7528',
'Increase': '\u589e\u52a0\u5f15\u7528',
'Decrease': '\u5220\u9664\u5f15\u7528',
// Quick Insert
'Quick Insert': '\u5feb\u63d2'
},
direction: 'ltr'
};

View File

@ -178,35 +178,50 @@ export default class DropDownButton extends React.Component<
disabled={!this.state.isOpened}
onRootClose={closeOnOutside !== false ? this.close : noop}
>
<ul
className={cx('DropDown-menu')}
onClick={closeOnClick ? this.close : noop}
>
{children
? children
: Array.isArray(buttons)
? buttons.map((button, index) => {
if (typeof button !== 'string' && !isVisible(button, data)) {
return null;
} else if (button === 'divider' || button.type === 'divider') {
return <li key={index} className={cx('DropDown-divider')} />;
}
{(ref: any) => {
return (
<ul
className={cx('DropDown-menu')}
onClick={closeOnClick ? this.close : noop}
ref={ref}
>
{children
? children
: Array.isArray(buttons)
? buttons.map((button, index) => {
if (
typeof button !== 'string' &&
!isVisible(button, data)
) {
return null;
} else if (
button === 'divider' ||
button.type === 'divider'
) {
return (
<li key={index} className={cx('DropDown-divider')} />
);
}
return (
<li
key={index}
className={isDisabled(button, data) ? 'is-disabled' : ''}
>
{render(`button/${index}`, {
type: 'button',
...(button as any),
isMenuItem: true
})}
</li>
);
})
: null}
</ul>
return (
<li
key={index}
className={
isDisabled(button, data) ? 'is-disabled' : ''
}
>
{render(`button/${index}`, {
type: 'button',
...(button as any),
isMenuItem: true
})}
</li>
);
})
: null}
</ul>
);
}}
</RootClose>
);

View File

@ -50,73 +50,23 @@ export default class RichTextControl extends React.Component<
videoReceiver: '/api/upload/video',
placeholder: 'placeholder.enter',
options: {
toolbarButtonsSM: [
'paragraphFormat',
'quote',
'color',
'|',
'bold',
'italic',
'underline',
'strikeThrough',
'|',
'formatOL',
'formatUL',
'align',
'|',
'insertLink',
'insertImage',
'insertEmotion',
'insertTable',
'|',
'undo',
'redo',
'html'
],
toolbarButtonsMD: [
'paragraphFormat',
'quote',
'color',
'|',
'bold',
'italic',
'underline',
'strikeThrough',
'|',
'formatOL',
'formatUL',
'align',
'|',
'insertLink',
'insertImage',
'insertEmotion',
'insertTable',
'|',
'undo',
'redo',
'html'
],
toolbarButtons: [
'undo',
'redo',
'paragraphFormat',
'quote',
'color',
'|',
'textColor',
'backgroundColor',
'bold',
'italic',
'underline',
'strikeThrough',
'|',
'formatOL',
'formatUL',
'align',
'|',
'quote',
'insertLink',
'insertImage',
'insertEmotion',
'insertTable',
'|',
'undo',
'redo',
'html'
]
}
@ -157,6 +107,7 @@ export default class RichTextControl extends React.Component<
]
: [],
key: props.env.richTextToken,
attribution: false,
...props.options,
editorClass: props.editorClass,
placeholderText: props.translate(props.placeholder),
@ -170,17 +121,14 @@ export default class RichTextControl extends React.Component<
},
events: {
...(props.options && props.options.events),
'froalaEditor.focus': this.handleFocus,
'froalaEditor.blur': this.handleBlur
focus: this.handleFocus,
blur: this.handleBlur
},
language:
!this.props.locale || this.props.locale === 'zh-CN' ? 'zh_cn' : ''
};
if (props.buttons) {
this.config.toolbarButtonsSM = props.buttons;
this.config.toolbarButtonsMD = props.buttons;
this.config.toolbarButtonsXS = props.buttons;
this.config.toolbarButtons = props.buttons;
}
} else {

View File

@ -583,20 +583,27 @@ export default class NestedSelectControl extends React.Component<
let body = (
<RootClose disabled={!this.state.isOpened} onRootClose={this.close}>
<div className={cx('NestedSelect-menuOuter')}>
{options.length ? (
this.renderOptions()
) : (
<div className={cx('NestedSelect-noResult')}>{noResultsText}</div>
)}
</div>
{(ref: any) => {
return (
<div className={cx('NestedSelect-menuOuter')} ref={ref}>
{options.length ? (
this.renderOptions()
) : (
<div className={cx('NestedSelect-noResult')}>
{noResultsText}
</div>
)}
</div>
);
}}
</RootClose>
);
return (
<Overlay
container={popOverContainer || this.getTarget}
target={this.getTarget}
container={popOverContainer || findDOMNode(this)?.parentElement}
placement={'auto'}
show
>
<PopOver className={cx('NestedSelect-popover')}>{body}</PopOver>

View File

@ -270,21 +270,26 @@ export const HocPopOver =
disabled={!this.state.isOpened}
onRootClose={this.closePopOver}
>
<div
className={cx(`PopOverAble--fixed PopOverAble--${position}`)}
onMouseLeave={
(popOver as SchemaPopOverObject)?.trigger === 'hover'
? this.closePopOver
: undefined
}
onMouseEnter={
(popOver as SchemaPopOverObject)?.trigger === 'hover'
? this.clearCloseTimer
: undefined
}
>
{content}
</div>
{(ref: any) => {
return (
<div
className={cx(`PopOverAble--fixed PopOverAble--${position}`)}
onMouseLeave={
(popOver as SchemaPopOverObject)?.trigger === 'hover'
? this.closePopOver
: undefined
}
onMouseEnter={
(popOver as SchemaPopOverObject)?.trigger === 'hover'
? this.clearCloseTimer
: undefined
}
ref={ref}
>
{content}
</div>
);
}}
</RootClose>
) : (
<Overlay

View File

@ -467,7 +467,7 @@ export const FormStore = ServiceStore.named('FormStore')
!env.hideValidateFailedDetail &&
self.items.forEach(item => {
item.errorData.forEach(errorData => {
msg = `${msg}\n${item.name}: ${errorData.msg}`;
msg = `${msg}\n${errorData.msg}`;
});
});