From f6a0ab652b8f1c4f80d7ee82397130d52f669b48 Mon Sep 17 00:00:00 2001 From: Muhammad Umair Date: Wed, 22 Nov 2023 19:16:53 +0500 Subject: [PATCH] Fix #9627 Replace Quill editor with Draftjs editor in Dashboard's Text widget (#9724) --- package.json | 1 + .../settings/CompactRichTextEditor.jsx | 126 ++++++++++++++++++ .../builder/wizard/text/TextOptions.jsx | 84 ++++++++---- web/client/plugins/WidgetsBuilder.jsx | 1 + web/client/themes/default/less/common.less | 20 +++ 5 files changed, 205 insertions(+), 27 deletions(-) create mode 100644 web/client/components/mapviews/settings/CompactRichTextEditor.jsx diff --git a/package.json b/package.json index ff7828f9d7..eeb12ba182 100644 --- a/package.json +++ b/package.json @@ -171,6 +171,7 @@ "draft-js-side-toolbar-plugin": "3.0.1", "draftjs-to-html": "0.8.4", "element-closest": "2.0.2", + "embed-video": "2.0.4", "es6-object-assign": "1.1.0", "es6-promise": "2.3.0", "eventlistener": "0.0.1", diff --git a/web/client/components/mapviews/settings/CompactRichTextEditor.jsx b/web/client/components/mapviews/settings/CompactRichTextEditor.jsx new file mode 100644 index 0000000000..5f3acd59bd --- /dev/null +++ b/web/client/components/mapviews/settings/CompactRichTextEditor.jsx @@ -0,0 +1,126 @@ +/* + * Copyright 2022, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'; +import { Editor } from 'react-draft-wysiwyg'; +import embed from 'embed-video'; +import { DEFAULT_FONT_FAMILIES } from '../../../utils/GeoStoryUtils'; + +export const resizeBase64Image = (src, options) => { + return new Promise((resolve, reject) => { + const { + size, + type = 'image/png', + quality = 0.9 + } = options || {}; + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.onload = () => { + const { naturalWidth, naturalHeight } = img; + const imgResolution = naturalWidth / naturalHeight; + const width = size; + const height = size / imgResolution; + const canvas = document.createElement('canvas'); + canvas.setAttribute('width', width); + canvas.setAttribute('height', height); + const ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0, width, height); + const dataURL = canvas.toDataURL(type, quality); + resolve(dataURL); + }; + img.onerror = (error) => { + reject(error); + }; + img.src = src; + }); +}; + +function CompactRichTextEditor({ + wrapperClassName = 'ms-compact-text-editor', + toolbarOptions, + ...props +}) { + + return ( + new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.addEventListener('load', () => { + resizeBase64Image(reader.result, { + size: 500, + type: 'image/jpeg', + quality: 0.8 + }).then((linkBase64) => { + resolve({ data: { link: linkBase64 } }); + }); + }); + if (file) { + reader.readAsDataURL(file); + } else { + reject(); + } + }), + previewImage: true, + inputAccept: 'image/gif,image/jpeg,image/jpg,image/png,image/svg', + alt: props.alt || { present: false, mandatory: false }, + defaultSize: { + height: 'auto', + width: '100%' + } + }, + fontFamily: { + // Setup fonts via props or use default from GeoStories + options: props.fonts || DEFAULT_FONT_FAMILIES + }, + link: { + inDropdown: false, + showOpenOptionOnHover: true, + defaultTargetOption: '_self', + options: ['link', 'unlink'] + }, + blockType: { + inDropdown: true, + options: ['Normal', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Blockquote', 'Code'] + }, + inline: { + inDropdown: true, + options: ['bold', 'italic', 'underline', 'strikethrough', 'monospace'] + }, + textAlign: { + inDropdown: true + }, + list: { + inDropdown: true + }, + embedded: { + embedCallback: link => { + const detectedSrc = / + ); +} + +export default CompactRichTextEditor; diff --git a/web/client/components/widgets/builder/wizard/text/TextOptions.jsx b/web/client/components/widgets/builder/wizard/text/TextOptions.jsx index 3300abe79a..f88bec1a0c 100644 --- a/web/client/components/widgets/builder/wizard/text/TextOptions.jsx +++ b/web/client/components/widgets/builder/wizard/text/TextOptions.jsx @@ -1,4 +1,4 @@ -/* +/** * Copyright 2018, GeoSolutions Sas. * All rights reserved. * @@ -6,34 +6,64 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; -import { Col, Form, FormControl, FormGroup } from 'react-bootstrap'; -import ReactQuill from '../../../../../libs/quill/react-quill-suspense'; +import React, { useState } from "react"; +import { Col, Form, FormControl, FormGroup } from "react-bootstrap"; +import localizedProps from "../../../../misc/enhancers/localizedProps"; +import { + htmlToDraftJSEditorState, + draftJSEditorStateToHtml +} from "../../../../../utils/EditorUtils"; -import localizedProps from '../../../../misc/enhancers/localizedProps'; +import withDebounceOnCallback from "../../../../misc/enhancers/withDebounceOnCallback"; +import CompactRichTextEditor from "../../../../mapviews/settings/CompactRichTextEditor"; const TitleInput = localizedProps("placeholder")(FormControl); +const DescriptorEditor = withDebounceOnCallback( + "onEditorStateChange", + "editorState" +)(CompactRichTextEditor); -const Editor = localizedProps("placeholder")(ReactQuill); - -export default ({ data = {}, onChange = () => { }}) => ( -
- -
- - - onChange("title", e.target.value)} /> - - -
- - onChange("text", val)} /> -
-); +function TextOptions({ data = {}, onChange = () => {} }) { + const [editorState, setEditorState] = useState( + htmlToDraftJSEditorState(data.text || "") + ); + return ( +
+ +
+ + + + onChange("title", e.target.value) + } + /> + + +
+ + { + const previousHTML = draftJSEditorStateToHtml(editorState); + const newHTML = draftJSEditorStateToHtml(newEditorState); + if (newHTML !== previousHTML) { + onChange( + "text", + draftJSEditorStateToHtml(newEditorState) + ); + setEditorState(newEditorState); + } + }} + // Array of custom or built in fonts can be set via props + // fonts={["Arial", "Impact", "Roman"]} + /> +
+ ); +} +export default TextOptions; diff --git a/web/client/plugins/WidgetsBuilder.jsx b/web/client/plugins/WidgetsBuilder.jsx index cf7f00030b..fb3ae176e8 100644 --- a/web/client/plugins/WidgetsBuilder.jsx +++ b/web/client/plugins/WidgetsBuilder.jsx @@ -81,6 +81,7 @@ class SideBarComponent extends React.Component { size={this.props.dockSize} zIndex={this.props.zIndex} position={this.props.position} + className="widgets-builder" bsStyle="primary" hideHeader style={{...this.props.layout, background: "white"}}> diff --git a/web/client/themes/default/less/common.less b/web/client/themes/default/less/common.less index 8ba2b5ab03..82fb005cf3 100644 --- a/web/client/themes/default/less/common.less +++ b/web/client/themes/default/less/common.less @@ -65,6 +65,26 @@ } } +// forced to avoid conflicts with no-image display for not found at link +.rdw-link-decorator-icon +{ + width: 15px !important; + min-width: 10px !important; + min-height: 15px !important; + position: static !important; + vertical-align: text-top; +} + +.widgets-builder .ms2-border-layout-content { + position: relative; + overflow: auto; +} + +.rdw-link-modal, +.rdw-embedded-modal +{ + height: fit-content !important; +} // ************** // Layout // **************