Commit 76fb932d by Яков

fix crash mobile

parent b68463ab
{ {
"name": "react-ag-qeditor", "name": "react-ag-qeditor",
"version": "1.0.17", "version": "1.0.20",
"description": "WYSIWYG html editor", "description": "WYSIWYG html editor",
"author": "atma", "author": "atma",
"license": "MIT", "license": "MIT",
...@@ -74,6 +74,7 @@ ...@@ -74,6 +74,7 @@
"prosemirror-state": "1.4.0", "prosemirror-state": "1.4.0",
"rc-upload": "^4.3.3", "rc-upload": "^4.3.3",
"react": "^17.0.2", "react": "^17.0.2",
"react-device-detect": "^2.2.3",
"react-media-recorder": "^1.6.6", "react-media-recorder": "^1.6.6",
"react-stopwatch": "^2.0.4", "react-stopwatch": "^2.0.4",
"react-webcam": "^7.0.1", "react-webcam": "^7.0.1",
......
...@@ -30,9 +30,17 @@ import axios from "axios"; ...@@ -30,9 +30,17 @@ import axios from "axios";
import ReactStopwatch from 'react-stopwatch'; import ReactStopwatch from 'react-stopwatch';
import Audio from "./extensions/Audio"; import Audio from "./extensions/Audio";
import { isMobile } from 'react-device-detect';
const initialBubbleItems = ['bold', 'italic', 'underline', 'strike', '|', 'colorText', 'highlight']; const initialBubbleItems = ['bold', 'italic', 'underline', 'strike', '|', 'colorText', 'highlight'];
const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", errorMessage: ""}, toolsOptions = { type: 'all' } }) => { const QEditor = ({
value,
onChange = () => {},
style,
uploadOptions = {url: "", errorMessage: ""},
toolsOptions = {type: 'all'}
}) => {
global.uploadUrl = uploadOptions.url; global.uploadUrl = uploadOptions.url;
const [innerModalType, setInnerModalType] = useState(null); const [innerModalType, setInnerModalType] = useState(null);
...@@ -46,13 +54,23 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -46,13 +54,23 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
const [focusFromTo, setFocusFromTo] = useState(null); const [focusFromTo, setFocusFromTo] = useState(null);
const [oldFocusFromTo, setOldFocusFromTo] = useState(null); const [oldFocusFromTo, setOldFocusFromTo] = useState(null);
const [isUploading, setIsUploading] = useState(false); const [isUploading, setIsUploading] = useState(false);
const [recordType, setRecordType] = useState({screen: true}) const [recordType, setRecordType] = useState({video: true})
const getRgb = (hex) => { const getRgb = (hex) => {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? `rgb(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)})` : null; return result ? `rgb(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)})` : null;
} }
const { status, startRecording, stopRecording, mediaBlobUrl, previewStream, muteAudio, unMuteAudio, isAudioMuted, clearBlobUrl } = useReactMediaRecorder(recordType); const {
status,
startRecording,
stopRecording,
mediaBlobUrl,
previewStream,
muteAudio,
unMuteAudio,
isAudioMuted,
clearBlobUrl
} = useReactMediaRecorder(recordType);
const videoRef = useRef(null); const videoRef = useRef(null);
...@@ -63,7 +81,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -63,7 +81,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
}, [previewStream]); }, [previewStream]);
useEffect(() => { useEffect(() => {
if(focusFromTo !== oldFocusFromTo){ if (focusFromTo !== oldFocusFromTo) {
setColorsSelected(null) setColorsSelected(null)
setOldFocusFromTo(focusFromTo); setOldFocusFromTo(focusFromTo);
} }
...@@ -121,15 +139,13 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -121,15 +139,13 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
// empty // empty
if (url === '') { if (url === '') {
editor.chain().focus().extendMarkRange('link').unsetLink() editor.chain().focus().extendMarkRange('link').unsetLink().run();
.run();
return return
} }
// update link // update link
editor.chain().focus().extendMarkRange('link').setLink({ href: url, target: '_blank' }) editor.chain().focus().extendMarkRange('link').setLink({href: url, target: '_blank'}).run();
.run();
} }
}, },
file: { file: {
...@@ -150,15 +166,15 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -150,15 +166,15 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
}, },
h2: { h2: {
title: 'Заголовок 2', title: 'Заголовок 2',
onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run() onClick: () => editor.chain().focus().toggleHeading({level: 2}).run()
}, },
h3: { h3: {
title: 'Заголовок 3', title: 'Заголовок 3',
onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run() onClick: () => editor.chain().focus().toggleHeading({level: 3}).run()
}, },
h4: { h4: {
title: 'Заголовок 4', title: 'Заголовок 4',
onClick: () => editor.chain().focus().toggleHeading({ level: 4 }).run() onClick: () => editor.chain().focus().toggleHeading({level: 4}).run()
}, },
paragraph: { paragraph: {
title: 'Обычный', title: 'Обычный',
...@@ -239,7 +255,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -239,7 +255,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
}, },
insertTable: { insertTable: {
title: 'Вставить таблицу', title: 'Вставить таблицу',
onClick: () => editor.chain().focus().insertTable({ rows: 2, cols: 2 }).run() onClick: () => editor.chain().focus().insertTable({rows: 2, cols: 2}).run()
}, },
deleteTable: { deleteTable: {
title: 'Удалить таблицу', title: 'Удалить таблицу',
...@@ -307,7 +323,11 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -307,7 +323,11 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
screencust: { screencust: {
title: 'Записать экран', title: 'Записать экран',
onClick: () => { onClick: () => {
setRecordType({screen: true}) if (isMobile) {
setRecordType({video: true})
} else {
setRecordType({screen: true})
}
clearBlobUrl() clearBlobUrl()
modalOpener('screencust', 'Записать экран') modalOpener('screencust', 'Записать экран')
} }
...@@ -372,10 +392,10 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -372,10 +392,10 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
], ],
content: value, content: value,
onUpdate: ({editor}) => onChange(editor.getHTML()), onUpdate: ({editor}) => onChange(editor.getHTML()),
onFocus: ({editor})=>{ onFocus: ({editor}) => {
let wrap = editor.options.element.closest('.atma-editor-wrap'); let wrap = editor.options.element.closest('.atma-editor-wrap');
wrap.querySelectorAll('.atma-editor-toolbar-s').forEach(function(s) { wrap.querySelectorAll('.atma-editor-toolbar-s').forEach(function (s) {
s.classList.remove('show'); s.classList.remove('show');
}); });
...@@ -391,7 +411,8 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -391,7 +411,8 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
<div className={'atma-editor-modal-action'}> <div className={'atma-editor-modal-action'}>
{ {
buttons.map((btn, i) => ( buttons.map((btn, i) => (
<button disabled={ btn.disabled } type={'button'} key={'mAction' + i} className={'atma-editor-btn' + btn.className} <button disabled={btn.disabled} type={'button'} key={'mAction' + i}
className={'atma-editor-btn' + btn.className}
onClick={btn.onClick}>{btn.title}</button> onClick={btn.onClick}>{btn.title}</button>
)) ))
} }
...@@ -399,34 +420,34 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -399,34 +420,34 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
) )
} }
const getUploader = ({ accept = '*', ...o }) => { const getUploader = ({accept = '*', ...o}) => {
let url = uploadOptions.url; let url = uploadOptions.url;
if(o.afterParams && o.afterParams.length > 0){ if (o.afterParams && o.afterParams.length > 0) {
if(uploadOptions.url.indexOf('?') !== -1){ if (uploadOptions.url.indexOf('?') !== -1) {
url = uploadOptions.url + '&' + o.afterParams.join('&'); url = uploadOptions.url + '&' + o.afterParams.join('&');
}else{ } else {
url = uploadOptions.url + '?' + o.afterParams.join('&'); url = uploadOptions.url + '?' + o.afterParams.join('&');
} }
} }
return <Uploader return <Uploader
key={uploaderUid} key={uploaderUid}
accept={ accept } accept={accept}
action={ url } action={url}
errorMessage={ uploadOptions.errorMessage } errorMessage={uploadOptions.errorMessage}
onSuccess={(file) => { onSuccess={(file) => {
let _uploadedPaths = [...uploadedPaths]; let _uploadedPaths = [...uploadedPaths];
_uploadedPaths.push(file); _uploadedPaths.push(file);
setUploadedPaths(_uploadedPaths) setUploadedPaths(_uploadedPaths)
}} }}
onDelete={(deleteFile)=>{ onDelete={(deleteFile) => {
let deleteIdx = null; let deleteIdx = null;
let _uploadedPaths = [...uploadedPaths]; let _uploadedPaths = [...uploadedPaths];
_uploadedPaths.map((f, i)=>{ _uploadedPaths.map((f, i) => {
if(f.uid === deleteFile.uid){ if (f.uid === deleteFile.uid) {
deleteIdx = i; deleteIdx = i;
} }
}); });
...@@ -434,7 +455,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -434,7 +455,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
setUploadedPaths(_uploadedPaths) setUploadedPaths(_uploadedPaths)
}} }}
multiple={true} multiple={true}
modalType={ innerModalType } modalType={innerModalType}
/> />
} }
...@@ -465,8 +486,9 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -465,8 +486,9 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
case 'iframe': case 'iframe':
return ( return (
<Fragment> <Fragment>
<input type="text" value={embedContent} placeholder={'https://'} onInput={(e) => setEmbedContent(e.target.value) <input type="text" value={embedContent} placeholder={'https://'}
}/> onInput={(e) => setEmbedContent(e.target.value)
}/>
<ul className={'atma-editor-soc-video'}> <ul className={'atma-editor-soc-video'}>
<li className={'youtube'}/> <li className={'youtube'}/>
<li className={'vimeo'}/> <li className={'vimeo'}/>
...@@ -478,43 +500,58 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -478,43 +500,58 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
) )
case 'video': case 'video':
return ( return (
<Fragment>{ getUploader({ accept: 'video/*' }) }</Fragment> <Fragment>{getUploader({accept: 'video/*'})}</Fragment>
) )
case 'image': case 'image':
return ( return (
<Fragment>{ getUploader({ accept: 'image/*' }) }</Fragment> <Fragment>{getUploader({accept: 'image/*'})}</Fragment>
) )
case 'file': case 'file':
return ( return (
<Fragment>{ getUploader({ accept: '*', afterParams: ['no_convert=1'] }) }</Fragment> <Fragment>{getUploader({accept: '*', afterParams: ['no_convert=1']})}</Fragment>
) )
case 'voicemessage': case 'voicemessage':
return ( return (
<> <>
<Fragment> <Fragment>
<div className={"audio-player"}> {
<div className={"audio-player-start audio-player-margin"}> isMobile &&
<>
<div className={"webwrap"}>
<div>Аудиозапись с мобильного устройства недоступна, <br/> запишите стандартными
функциями устройства и воспользуйтесь кнопкой «Прикрепить файл»
</div>
</div>
</>
}
{
! isMobile &&
<div className={"audio-player"}>
<div className={"audio-player-start audio-player-margin"}>
{
status === 'recording' && ! mediaBlobUrl ?
<div onClick={stopRecording}
className={"audio-player-center-recording"}/> :
<div onClick={startRecording} className={"audio-player-center-start"}/>
}
</div>
<div className={"audio-player-voice audio-player-margin"}/>
{ {
status === 'recording' && !mediaBlobUrl ? status === 'recording' && ! mediaBlobUrl ?
<div onClick={stopRecording} className={"audio-player-center-recording"}/> : <ReactStopwatch
<div onClick={startRecording} className={"audio-player-center-start"}/> seconds={0}
minutes={0}
hours={0}
render={({formatted}) => {
return (
<span
className={"audio-player-timer audio-player-margin"}>{formatted}</span>
)
}}
/> : <span className={"audio-player-timer audio-player-margin"}/>
} }
</div> </div>
<div className={"audio-player-voice audio-player-margin"}/> }
{
status === 'recording' && ! mediaBlobUrl ?
<ReactStopwatch
seconds={0}
minutes={0}
hours={0}
render={({formatted}) => {
return (
<span className={"audio-player-timer audio-player-margin"}>{formatted}</span>
)
}}
/> : <span className={"audio-player-timer audio-player-margin"} />
}
</div>
</Fragment> </Fragment>
</> </>
) )
...@@ -522,110 +559,152 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -522,110 +559,152 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
return ( return (
<> <>
<Fragment> <Fragment>
<div className={"webwrap"}> {
<div className={"webwrap-content"}> isMobile &&
{ <>
mediaBlobUrl ? <div className={"webwrap"}>
<video className={"webwrap-video"} id={"id-video"} src={mediaBlobUrl} controls /> : status === "recording" && <div>Запись экрана с мобильного устройства недоступна, <br/> запишите
<video className={"webwrap-video"} ref={videoRef} src={previewStream} autoPlay controls={false}/> стандартными функциями устройства и воспользуйтесь кнопкой «Загрузить видео»
} </div>
{ </div>
status === 'recording' && !mediaBlobUrl ? </>
<ReactStopwatch }
seconds={0} {
minutes={0} ! isMobile &&
hours={0} <>
render={({ formatted }) => { <div className={"webwrap"}>
return ( <div className={"webwrap-content"}>
<span className={"webwrap-timer"}> {
mediaBlobUrl ?
<video className={"webwrap-video"} id={"id-video"}
src={mediaBlobUrl} controls/> : status === "recording" &&
<video className={"webwrap-video"} ref={videoRef}
src={previewStream} autoPlay controls={false}/>
}
{
status === 'recording' && ! mediaBlobUrl ?
<ReactStopwatch
seconds={0}
minutes={0}
hours={0}
render={({formatted}) => {
return (
<span className={"webwrap-timer"}>
{formatted} {formatted}
</span> </span>
) )
}} }}
/> : <span className={"webwrap-timer"}>00:00:00</span> /> : <span className={"webwrap-timer"}>00:00:00</span>
} }
{ {
!mediaBlobUrl && ! mediaBlobUrl &&
<div className={"webwrap-start-border"}> <div className={"webwrap-start-border"}>
<button onClick={status === 'recording' ? stopRecording : startRecording} className={status === 'recording' ? "webwrap-record-center" : "webwrap-start-center"} /> <button
onClick={status === 'recording' ? stopRecording : startRecording}
className={status === 'recording' ? "webwrap-record-center" : "webwrap-start-center"}/>
</div>
}
</div> </div>
}
</div>
</div>
<div className={"web-bottom-elements"}>
{ mediaBlobUrl &&
<div onClick={clearBlobUrl} className={"web-button-wrap"}>
<div className={"web-button-rerecord"}/>
<span className={"web-button-rerecord-text"}>Перезаписать</span>
</div> </div>
} <div className={"web-bottom-elements"}>
{ {mediaBlobUrl &&
!mediaBlobUrl && <div onClick={clearBlobUrl} className={"web-button-wrap"}>
<div className={"web-button-spacer"}/> <div className={"web-button-rerecord"}/>
} <span className={"web-button-rerecord-text"}>Перезаписать</span>
{ </div>
!mediaBlobUrl && }
<div onClick={isAudioMuted ? unMuteAudio : muteAudio} className={isAudioMuted ? "web-button-unmute" : "web-button-mute"}/> {
} ! mediaBlobUrl &&
<div className={"web-button-spacer"}/> <div className={"web-button-spacer"}/>
</div> }
{
! mediaBlobUrl &&
<div onClick={isAudioMuted ? unMuteAudio : muteAudio}
className={isAudioMuted ? "web-button-unmute" : "web-button-mute"}/>
}
<div className={"web-button-spacer"}/>
</div>
</>
}
</Fragment> </Fragment>
</> </>
) )
case 'webcamera': case 'webcamera':
return ( return (
<> <>
<Fragment> <Fragment>
<div className={"webwrap"}>
<div className={"webwrap-content"}>
{
mediaBlobUrl ?
<video className={"webwrap-video"} id={"id-video"} src={mediaBlobUrl} controls /> : status === "recording" &&
<video className={"webwrap-video"} ref={videoRef} src={previewStream} autoPlay controls={false}/>
}
{
status === 'recording' && !mediaBlobUrl ?
<ReactStopwatch
seconds={0}
minutes={0}
hours={0}
render={({ formatted }) => {
return (
<span className={"webwrap-timer"}>
{formatted}
</span>
)
}}
/> : <span className={"webwrap-timer"}>00:00:00</span>
}
{
!mediaBlobUrl &&
<div className={"webwrap-start-border"}>
<button onClick={status === 'recording' ? stopRecording : startRecording} className={status === 'recording' ? "webwrap-record-center" : "webwrap-start-center"} />
</div>
}
</div>
</div>
<div className={"web-bottom-elements"}>
{ mediaBlobUrl &&
<div onClick={clearBlobUrl} className={"web-button-wrap"}>
<div className={"web-button-rerecord"}/>
<span className={"web-button-rerecord-text"}>Перезаписать</span>
</div>
}
{ {
!mediaBlobUrl && isMobile &&
<div className={"web-button-spacer"}/> <>
<div className={"webwrap"}>
<div>Видеозапись с мобильного устройства недоступна, <br/> запишите стандартными
функциями устройства и воспользуйтесь кнопкой «Загрузить видео»
</div>
</div>
</>
} }
{ {
!mediaBlobUrl && ! isMobile &&
<div onClick={isAudioMuted ? unMuteAudio : muteAudio} className={isAudioMuted ? "web-button-unmute" : "web-button-mute"}/> <>
<div className={"webwrap"}>
<div className={"webwrap-content"}>
{
mediaBlobUrl ?
<video className={"webwrap-video"} id={"id-video"}
src={mediaBlobUrl}
controls/> : status === "recording" &&
<video className={"webwrap-video"} ref={videoRef}
src={previewStream}
autoPlay controls={false}/>
}
{
status === 'recording' && ! mediaBlobUrl ?
<ReactStopwatch
seconds={0}
minutes={0}
hours={0}
render={({formatted}) => {
return (
<span className={"webwrap-timer"}>
{formatted}
</span>
)
}}
/> : <span className={"webwrap-timer"}>00:00:00</span>
}
{
! mediaBlobUrl &&
<div className={"webwrap-start-border"}>
<button
onClick={status === 'recording' ? stopRecording : startRecording}
className={status === 'recording' ? "webwrap-record-center" : "webwrap-start-center"}/>
</div>
}
</div>
</div>
<div className={"web-bottom-elements"}>
{mediaBlobUrl &&
<div onClick={clearBlobUrl} className={"web-button-wrap"}>
<div className={"web-button-rerecord"}/>
<span className={"web-button-rerecord-text"}>Перезаписать</span>
</div>
}
{
! mediaBlobUrl &&
<div className={"web-button-spacer"}/>
}
{
! mediaBlobUrl &&
<div onClick={isAudioMuted ? unMuteAudio : muteAudio}
className={isAudioMuted ? "web-button-unmute" : "web-button-mute"}/>
}
<div className={"web-button-spacer"}/>
</div>
</>
} }
<div className={"web-button-spacer"}/> </Fragment>
</div> </>
</Fragment> )
</>
)
default: default:
return <div>Пусто</div> return <div>Пусто</div>
} }
...@@ -634,33 +713,33 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -634,33 +713,33 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
const isDisabledAction = () => { const isDisabledAction = () => {
let isDisabled = false; let isDisabled = false;
switch(innerModalType){ switch (innerModalType) {
case 'video': case 'video':
case 'image': case 'image':
if(uploadOptions.url === null || uploadedPaths.length === 0){ if (uploadOptions.url === null || uploadedPaths.length === 0) {
isDisabled = true; isDisabled = true;
} }
break; break;
case 'screencust': case 'screencust':
if(status === 'recording' || isUploading || !mediaBlobUrl){ if (status === 'recording' || isUploading || ! mediaBlobUrl) {
isDisabled = true; isDisabled = true;
} }
break; break;
case 'voicemessage': case 'voicemessage':
if(status === 'recording' || isUploading || !mediaBlobUrl){ if (status === 'recording' || isUploading || ! mediaBlobUrl) {
isDisabled = true; isDisabled = true;
} }
break; break;
case 'webcamera': case 'webcamera':
if(status === 'recording' || isUploading || !mediaBlobUrl){ if (status === 'recording' || isUploading || ! mediaBlobUrl) {
isDisabled = true; isDisabled = true;
} }
break; break;
case 'iframe': case 'iframe':
try{ try {
let url = new URL(embedContent); let url = new URL(embedContent);
switch (url.hostname){ switch (url.hostname) {
case 'rutube.ru': case 'rutube.ru':
case 'www.rutube.ru': case 'www.rutube.ru':
case 'vimeo.com': case 'vimeo.com':
...@@ -674,7 +753,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -674,7 +753,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
isDisabled = true; isDisabled = true;
} }
}catch (error){ } catch (error) {
isDisabled = true; isDisabled = true;
} }
break; break;
...@@ -683,7 +762,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -683,7 +762,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
return isDisabled; return isDisabled;
} }
if (!editor) { if ( ! editor) {
return null return null
} }
...@@ -702,48 +781,50 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -702,48 +781,50 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
<BubbleMenu typpyOptions={{followCursor: true,}} editor={editor} shouldShow={({...o}) => { <BubbleMenu typpyOptions={{followCursor: true,}} editor={editor} shouldShow={({...o}) => {
let items = []; let items = [];
if(o.from !== o.to && editor.isActive('paragraph') && editor.isActive('image') === false && document.querySelectorAll('.selectedCell').length === 0){ if (o.from !== o.to && editor.isActive('paragraph') && editor.isActive('image') === false && document.querySelectorAll('.selectedCell').length === 0) {
items = initialBubbleItems; items = initialBubbleItems;
} }
if(editor.isActive('image') === true){ if (editor.isActive('image') === true) {
items = ['alignLeft', 'alignCenter', 'alignRight']; items = ['alignLeft', 'alignCenter', 'alignRight'];
} }
setFocusFromTo([o.from, o.to].join(':')); setFocusFromTo([o.from, o.to].join(':'));
if(items.length > 0){ if (items.length > 0) {
setBubbleItems(items); setBubbleItems(items);
return true; return true;
} }
}} tippyOptions={{ duration: 100 }}> }} tippyOptions={{duration: 100}}>
<div className={"atma-editor-bubble"} onClick={e => e.stopPropagation()}> <div className={"atma-editor-bubble"} onClick={e => e.stopPropagation()}>
{ {
colorsSelected !== null ? colorsSelected !== null ?
colors[colorsSelected].map((itemColor, i) => { colors[colorsSelected].map((itemColor, i) => {
return ( <div key={'colors' + colorsSelected + i} className={'qcolors' + (itemColor === 'none' ? ' unset' : '')} style={{ background: itemColor}} onClick={()=>{ return (<div key={'colors' + colorsSelected + i}
className={'qcolors' + (itemColor === 'none' ? ' unset' : '')}
style={{background: itemColor}} onClick={() => {
if(itemColor === 'none'){ if (itemColor === 'none') {
colorsSelected === 'color' ? colorsSelected === 'color' ?
editor.chain().focus().unsetHighlight().unsetColor().run() : editor.chain().focus().unsetHighlight().unsetColor().run() :
editor.chain().focus().unsetColor().unsetHighlight().run(); editor.chain().focus().unsetColor().unsetHighlight().run();
}else{ } else {
colorsSelected === 'color' ? colorsSelected === 'color' ?
editor.chain().focus().unsetHighlight().setColor(itemColor).run() : editor.chain().focus().unsetHighlight().setColor(itemColor).run() :
editor.chain().focus().unsetColor().toggleHighlight({ color: itemColor }).run(); editor.chain().focus().unsetColor().toggleHighlight({color: itemColor}).run();
} }
setColorsSelected(null); setColorsSelected(null);
}} /> ) }}/>)
}) : bubbleItems.map((type, i) => { }) : bubbleItems.map((type, i) => {
if(type === '|'){ if (type === '|') {
return ( <div key={'bubbleSeparator' + i} className={'qseparator'} /> ) return (<div key={'bubbleSeparator' + i} className={'qseparator'}/>)
}else{ } else {
return ( return (
<div <div
key={ 'bubbleItems' + i } key={'bubbleItems' + i}
className={'qicon q' + type + (editor.isActive(type) ? ' active' : '')} className={'qicon q' + type + (editor.isActive(type) ? ' active' : '')}
title={ toolsLib[type] ? toolsLib[type].title : '' } title={toolsLib[type] ? toolsLib[type].title : ''}
onClick={ toolsLib[type].onClick } onClick={toolsLib[type].onClick}
/> />
) )
} }
...@@ -876,8 +957,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er ...@@ -876,8 +957,7 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions = {url: "", er
uploadedPaths.map((file, i) => { uploadedPaths.map((file, i) => {
let exp = file.path.split('.'); let exp = file.path.split('.');
exp = exp[exp.length - 1] exp = exp[exp.length - 1]
editor.chain().focus() editor.chain().focus().insertContent(`<a href="${file.path}" target="_blank" download="${file.name}.${exp}" data-size="${file.size}">${file.name}</a>`).run();
.insertContent(`<a href="${file.path}" target="_blank" download="${file.name}.${exp}" data-size="${file.size}">${file.name}</a>`).run();
}); });
break break
} }
......
...@@ -10193,6 +10193,13 @@ react-dev-utils@^10.2.1: ...@@ -10193,6 +10193,13 @@ react-dev-utils@^10.2.1:
strip-ansi "6.0.0" strip-ansi "6.0.0"
text-table "0.2.0" text-table "0.2.0"
react-device-detect@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.3.tgz#97a7ae767cdd004e7c3578260f48cf70c036e7ca"
integrity sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==
dependencies:
ua-parser-js "^1.0.33"
react-dom@^17.0.2: react-dom@^17.0.2:
version "17.0.2" version "17.0.2"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
...@@ -12054,6 +12061,11 @@ typescript@^3.8.3: ...@@ -12054,6 +12061,11 @@ typescript@^3.8.3:
resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz" resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz"
integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
ua-parser-js@^1.0.33:
version "1.0.33"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4"
integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==
unbox-primitive@^1.0.2: unbox-primitive@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment