Commit e3e93b15 by Яков

update

parent 269ad207
...@@ -7,15 +7,7 @@ const App = () => { ...@@ -7,15 +7,7 @@ const App = () => {
return <div style={{padding:40}}> return <div style={{padding:40}}>
<QEditor <QEditor
// value={`<iframe src="https://cdn.atmaguru.online/2/atmacompany/8/b/8bTfGoWtAuv5waVabQRTtWaNOrve5uv8UBXFbGOH9cowQ1K56dYi7TFz6h5jUfzr.pdf" width="100%" /><iframe src="https://www.youtube.com/embed/YmZGP7YP8c4" frameborder="0" allowfullscreen="true"></iframe><video src="https://cdn.atmaguru.online/2/demo/V/k/VkrEXjkxnutXLgcJPt5CLXNgEj4RaL9Zk4SQhIMUjOeIRpu0dSKtQCIMl49pJM6N.webm" controls="true"></video><p>Так исторически сложилось, что взрослым людям стараются дать максимум материалов: часовые лекции, объемные массивы текста и должностных инструкций. Сотрудник изучает огромный объем информации. Пытается его запомнить, а потом в конце курса сдать большой аттестационный экзамен. Вы не учитывете при этом, что мозг взрослого человека перегружен, ему нужно выполнять обязанности по работе, думать о домашних делах, его постоянно отвлекают менеджеры и коллеги по работе… Единственный правильный способ — это давать информацию небольшими кусочками и после каждой порции проверять усвоена она или нет.</p><p></p><p>что-то новое о компании<br><a href="https://cdn.atmaguru.online/1/demo/T/G/TGvSAoLawONkteJ47yyNfmsC8zNe3ZRG4iO0ZfAjmvOIZkm20BWp8KdWCH5p1Rrx.gif" target="_blank" download="Редактор.gif" data-size="37 Мб">РСкачать книгу</a> <br></p>`} // value={`<iframe src="https://cdn.atmaguru.online/2/atmacompany/8/b/8bTfGoWtAuv5waVabQRTtWaNOrve5uv8UBXFbGOH9cowQ1K56dYi7TFz6h5jUfzr.pdf" width="100%" /><iframe src="https://www.youtube.com/embed/YmZGP7YP8c4" frameborder="0" allowfullscreen="true"></iframe><video src="https://cdn.atmaguru.online/2/demo/V/k/VkrEXjkxnutXLgcJPt5CLXNgEj4RaL9Zk4SQhIMUjOeIRpu0dSKtQCIMl49pJM6N.webm" controls="true"></video><p>Так исторически сложилось, что взрослым людям стараются дать максимум материалов: часовые лекции, объемные массивы текста и должностных инструкций. Сотрудник изучает огромный объем информации. Пытается его запомнить, а потом в конце курса сдать большой аттестационный экзамен. Вы не учитывете при этом, что мозг взрослого человека перегружен, ему нужно выполнять обязанности по работе, думать о домашних делах, его постоянно отвлекают менеджеры и коллеги по работе… Единственный правильный способ — это давать информацию небольшими кусочками и после каждой порции проверять усвоена она или нет.</p><p></p><p>что-то новое о компании<br><a href="https://cdn.atmaguru.online/1/demo/T/G/TGvSAoLawONkteJ47yyNfmsC8zNe3ZRG4iO0ZfAjmvOIZkm20BWp8KdWCH5p1Rrx.gif" target="_blank" download="Редактор.gif" data-size="37 Мб">РСкачать книгу</a> <br></p>`}
value={'<p style="text-align: left"><span><span data-type="resizable-image" data-image-wrapper="true" style="\n' + value={"<p style=\"text-align: left\"><a target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://telemost.yandex.ru/j/5911922929\">дшдлодлод</a></p><table data-bordered=\"true\" class=\"\" style=\"min-width: 75px\"><colgroup><col style=\"min-width: 25px\"><col style=\"min-width: 25px\"><col style=\"min-width: 25px\"></colgroup><tbody><tr><th colspan=\"1\" rowspan=\"1\"><p style=\"text-align: left\">sdfsdf</p></th><th colspan=\"1\" rowspan=\"1\"><p style=\"text-align: left\"></p></th><th colspan=\"1\" rowspan=\"1\"><p style=\"text-align: left\">sdfsdf</p></th></tr><tr><td colspan=\"1\" rowspan=\"1\"><p style=\"text-align: left\">sdfsdf</p></td><td colspan=\"1\" rowspan=\"1\"><p style=\"text-align: left\"></p></td><td colspan=\"1\" rowspan=\"1\"><p style=\"text-align: left\">sdfsdf</p></td></tr></tbody></table><p style=\"text-align: left\"></p>"}
' display: inline-block;\n' +
' float: left;\n' +
' margin: 0 1rem 1rem 0;\n' +
' shape-outside: none;\n' +
' vertical-align: top;\n' +
' position: relative;\n' +
' z-index: 1;\n' +
' " data-align="left"><img src="https://cdn.atmaguru.online/2/demo/3/k/3kL9yGrVaNZibUZzoeIcSmPuLyK8mqr6EptFPU8HQJ5sCeFbYey3y8mAfoaK6f3H.png" width="289" height="302" data-align="left"></span>​</span>​ываыва</p><p style="text-align: left">ываыва</p><p style="text-align: left">ываыва</p><p style="text-align: left"></p>'}
onChange={(value)=>{ onChange={(value)=>{
console.log(value); console.log(value);
}} }}
......
...@@ -8,7 +8,6 @@ import './index.scss' ...@@ -8,7 +8,6 @@ import './index.scss'
import { useEditor, EditorContent, BubbleMenu } from '@tiptap/react' import { useEditor, EditorContent, BubbleMenu } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline' import Underline from '@tiptap/extension-underline'
import Table from '@tiptap/extension-table'
import TableCell from '@tiptap/extension-table-cell' import TableCell from '@tiptap/extension-table-cell'
import TableRow from '@tiptap/extension-table-row' import TableRow from '@tiptap/extension-table-row'
import TableHeader from '@tiptap/extension-table-header' import TableHeader from '@tiptap/extension-table-header'
...@@ -34,6 +33,8 @@ import { useReactMediaRecorder } from 'react-media-recorder' ...@@ -34,6 +33,8 @@ import { useReactMediaRecorder } from 'react-media-recorder'
import axios from 'axios' 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 TableExtension from './extensions/TableExtension'
import ToggleBlock from './extensions/ToggleBlock'
// import Image from '@tiptap/extension-image' // import Image from '@tiptap/extension-image'
// import ImageResize from 'tiptap-extension-resize-image'; // import ImageResize from 'tiptap-extension-resize-image';
...@@ -445,7 +446,45 @@ const QEditor = ({ ...@@ -445,7 +446,45 @@ const QEditor = ({
clearBlobUrl() clearBlobUrl()
modalOpener('screencust', 'Записать экран') modalOpener('screencust', 'Записать экран')
} }
} },
insertToggleBlock: {
title: 'Переключаемый блок',
onClick: () =>
editor.commands.insertContent([
{
type: 'toggleBlock',
attrs: {
title: 'Заголовок',
open: true,
},
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'Введите подробности...',
},
],
},
],
},
{
type: 'paragraph',
content: [],
},
]),
},
toggleTableBorders: {
title: 'Показать/скрыть границы таблицы',
onClick: () => {
const current = editor.getAttributes('table')?.bordered;
const newValue = !current;
editor.commands.updateAttributes('table', { bordered: newValue });
}
},
// katex: { // katex: {
// title: 'Вставить формулу', // title: 'Вставить формулу',
// onClick: () => { // onClick: () => {
...@@ -471,10 +510,7 @@ const QEditor = ({ ...@@ -471,10 +510,7 @@ const QEditor = ({
}), }),
Video, Video,
Iframe, Iframe,
Table.configure({ TableExtension,
resizable: true,
allowTableNodeSelection: true
}),
TableRow, TableRow,
TableHeader, TableHeader,
TableCell.extend({ TableCell.extend({
...@@ -512,6 +548,7 @@ const QEditor = ({ ...@@ -512,6 +548,7 @@ const QEditor = ({
Audio, Audio,
Superscript, Superscript,
Subscript, Subscript,
ToggleBlock,
DragAndDrop.configure({ DragAndDrop.configure({
uploadUrl: uploadOptions.url, uploadUrl: uploadOptions.url,
allowedFileTypes: [ allowedFileTypes: [
...@@ -540,6 +577,7 @@ const QEditor = ({ ...@@ -540,6 +577,7 @@ const QEditor = ({
} }
}) })
const buildActionsModal = (buttons = []) => { const buildActionsModal = (buttons = []) => {
if (buttons.length === 0) { if (buttons.length === 0) {
return null return null
...@@ -1186,6 +1224,7 @@ const QEditor = ({ ...@@ -1186,6 +1224,7 @@ const QEditor = ({
} }
] ]
return ( return (
<div className='atma-editor-wrap' style={style}> <div className='atma-editor-wrap' style={style}>
<div className='atma-editor'> <div className='atma-editor'>
......
...@@ -51,6 +51,7 @@ const toolsInit = { ...@@ -51,6 +51,7 @@ const toolsInit = {
'iframe_pdf', 'iframe_pdf',
'audio', 'audio',
'iframe_custom', 'iframe_custom',
'insertToggleBlock'
] ]
}, },
{ {
...@@ -78,7 +79,9 @@ const toolsInit = { ...@@ -78,7 +79,9 @@ const toolsInit = {
'toggleHeaderCell', 'toggleHeaderCell',
'|', '|',
'mergeOrSplit', 'mergeOrSplit',
'deleteTable' 'deleteTable',
'|',
'toggleTableBorders'
] ]
}, },
{ {
...@@ -171,6 +174,7 @@ const ToolBar = ({ editor, toolsLib = [], toolsOptions }) => { ...@@ -171,6 +174,7 @@ const ToolBar = ({ editor, toolsLib = [], toolsOptions }) => {
case 'toggleHeaderCell': case 'toggleHeaderCell':
case 'mergeOrSplit': case 'mergeOrSplit':
case 'deleteTable': case 'deleteTable':
case 'toggleTableBorders':
if(!editor.isActive('table')){ if(!editor.isActive('table')){
return true; return true;
} }
......
import { Table } from '@tiptap/extension-table';
const CustomTableView = ({ node }) => {
const dom = document.createElement('div');
dom.classList.add('tableWrapper');
const content = document.createElement('table');
// выставляем data-no-border при инициализации
if (node.attrs.bordered === false) {
dom.setAttribute('data-no-border', 'true');
}
dom.appendChild(content);
return {
dom,
contentDOM: content,
update(updatedNode) {
const bordered = updatedNode.attrs.bordered !== false;
if (!bordered) {
dom.setAttribute('data-no-border', 'true');
} else {
dom.removeAttribute('data-no-border');
}
return true;
}
};
};
const TableExtension = Table.extend({
addAttributes() {
return {
bordered: {
default: true,
parseHTML: element => element.getAttribute('data-bordered') !== 'false',
renderHTML: attributes => ({
'data-bordered': attributes.bordered ? 'true' : 'false',
class: attributes.bordered ? '' : 'no-border',
}),
},
};
},
addNodeView() {
return CustomTableView;
},
}).configure({
resizable: true,
allowTableNodeSelection: true,
});
export default TableExtension;
import { Node } from '@tiptap/core'
import { ReactNodeViewRenderer, NodeViewWrapper, NodeViewContent } from '@tiptap/react'
import React, { useEffect, useRef, useState } from 'react'
// React компонент NodeView
export const ToggleBlockComponent = ({ node, updateAttributes }) => {
const open = node.attrs.open
const title = node.attrs.title
const measurerRef = useRef(null)
const [inputWidth, setInputWidth] = useState('100px')
const toggle = () => {
updateAttributes({ open: !open })
}
useEffect(() => {
if (measurerRef.current) {
const width = measurerRef.current.offsetWidth + 20 // небольшой запас
setInputWidth(`${width}px`)
}
}, [title])
return (
<NodeViewWrapper className="toggle-block" data-open={open}>
<div className="toggle-block-inner">
<div className="toggle-header-wrapper">
<span
className="toggle-header-measurer"
ref={measurerRef}
aria-hidden="true"
>
{title || 'Заголовок'}
</span>
<button className="toggle-button" onClick={toggle}>
{open ? '▼' : '▶'}
</button>
<input
type="text"
className="toggle-header"
value={title}
onChange={(e) => updateAttributes({ title: e.target.value })}
placeholder="Заголовок..."
style={{ width: inputWidth }}
/>
</div>
</div>
<div
className="toggle-body"
style={{
maxHeight: open ? '1000px' : '0',
overflow: 'hidden',
transition: 'max-height 0.3s ease',
}}
>
<div className="toggle-body-wrapper">
<NodeViewContent className="toggle-content" />
</div>
</div>
</NodeViewWrapper>
)
}
// Расширение узла ToggleBlock
const ToggleBlock = Node.create({
name: 'toggleBlock',
group: 'block',
content: 'block+',
addAttributes() {
return {
title: { default: 'Заголовок' },
open: { default: false },
}
},
parseHTML() {
return [{ tag: 'div.toggle-block' }]
},
renderHTML({ HTMLAttributes }) {
return [
'div',
{
class: 'toggle-block',
'data-open': HTMLAttributes.open ? 'true' : 'false',
},
[
'div',
{ class: 'toggle-block-inner' },
['span', { class: 'toggle-button' }, HTMLAttributes.open ? '▼' : '▶'],
['span', { class: 'toggle-header' }, HTMLAttributes.title || 'Заголовок'],
],
['div', { class: 'toggle-body' }, ['div', { class: 'toggle-content' }, 0]],
]
},
addNodeView() {
return ReactNodeViewRenderer(ToggleBlockComponent)
},
})
export default ToggleBlock
...@@ -1053,6 +1053,12 @@ body{ ...@@ -1053,6 +1053,12 @@ body{
&.qsubscript { &.qsubscript {
background-image: url("data:image/svg+xml,%3Csvg fill='%23000000' width='14' height='14' viewBox='0 0 14 14' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.95438741,4.5 L8.7850203,7.30626765 C9.17584914,7.69373235 9.17584914,8.32193676 8.7850203,8.70940147 C8.39419145,9.09686618 7.7605327,9.09686618 7.36970385,8.70940147 L4.53907097,5.90313382 L1.70843808,8.70940147 C1.31760923,9.09686618 0.683950477,9.09686618 0.293121633,8.70940147 C-0.097707211,8.32193676 -0.097707211,7.69373235 0.293121633,7.30626765 L3.12375452,4.5 L0.293121633,1.69373235 C-0.097707211,1.30626765 -0.097707211,0.678063236 0.293121633,0.29059853 C0.683950477,-0.0968661766 1.31760923,-0.0968661766 1.70843808,0.29059853 L4.53907097,3.09686618 L7.36970385,0.29059853 C7.7605327,-0.0968661766 8.39419145,-0.0968661766 8.7850203,0.29059853 C9.17584914,0.678063236 9.17584914,1.30626765 8.7850203,1.69373235 L5.95438741,4.5 Z M14,12.3519235 L14,14 L9.07814193,14 L9.07814193,12.9855469 L9.17911195,12.740495 L10.9800051,10.826951 C11.4133348,10.392144 11.77981,10.0571893 11.9106676,9.81557985 C12.0353516,9.58536908 12.0941418,9.38912772 12.0941418,9.22851562 C12.0941418,9.08820171 12.0529006,8.98652895 11.9660329,8.90193322 C11.8784905,8.81668046 11.7672384,8.77460938 11.6114715,8.77460938 C11.4636071,8.77460938 11.3523966,8.82385579 11.2538919,8.93290599 C11.1476773,9.05049131 11.0872658,9.22080769 11.0789418,9.45806499 L11.067089,9.79589844 L9.2269351,9.79589844 L9.24400536,9.42973922 C9.27824372,8.69532221 9.51816978,8.09947682 9.96736119,7.66058259 C10.4175505,7.22071332 10.9973662,7 11.6859406,7 C12.1131716,7 12.4979375,7.09112961 12.8348078,7.27522069 C13.1746136,7.46091603 13.4472171,7.72952508 13.6486043,8.07526694 C13.8483691,8.41822347 13.9503539,8.77415268 13.9503539,9.13828125 C13.9503539,9.55406894 13.7188967,9.98681416 13.4907809,10.4451899 C13.2667292,10.8953993 12.9524998,11.3208384 12.4178891,12.0000117 L12.3003917,12.1228516 L12.0445871,12.3519235 L14,12.3519235 Z'/%3E%3C/svg%3E"); background-image: url("data:image/svg+xml,%3Csvg fill='%23000000' width='14' height='14' viewBox='0 0 14 14' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.95438741,4.5 L8.7850203,7.30626765 C9.17584914,7.69373235 9.17584914,8.32193676 8.7850203,8.70940147 C8.39419145,9.09686618 7.7605327,9.09686618 7.36970385,8.70940147 L4.53907097,5.90313382 L1.70843808,8.70940147 C1.31760923,9.09686618 0.683950477,9.09686618 0.293121633,8.70940147 C-0.097707211,8.32193676 -0.097707211,7.69373235 0.293121633,7.30626765 L3.12375452,4.5 L0.293121633,1.69373235 C-0.097707211,1.30626765 -0.097707211,0.678063236 0.293121633,0.29059853 C0.683950477,-0.0968661766 1.31760923,-0.0968661766 1.70843808,0.29059853 L4.53907097,3.09686618 L7.36970385,0.29059853 C7.7605327,-0.0968661766 8.39419145,-0.0968661766 8.7850203,0.29059853 C9.17584914,0.678063236 9.17584914,1.30626765 8.7850203,1.69373235 L5.95438741,4.5 Z M14,12.3519235 L14,14 L9.07814193,14 L9.07814193,12.9855469 L9.17911195,12.740495 L10.9800051,10.826951 C11.4133348,10.392144 11.77981,10.0571893 11.9106676,9.81557985 C12.0353516,9.58536908 12.0941418,9.38912772 12.0941418,9.22851562 C12.0941418,9.08820171 12.0529006,8.98652895 11.9660329,8.90193322 C11.8784905,8.81668046 11.7672384,8.77460938 11.6114715,8.77460938 C11.4636071,8.77460938 11.3523966,8.82385579 11.2538919,8.93290599 C11.1476773,9.05049131 11.0872658,9.22080769 11.0789418,9.45806499 L11.067089,9.79589844 L9.2269351,9.79589844 L9.24400536,9.42973922 C9.27824372,8.69532221 9.51816978,8.09947682 9.96736119,7.66058259 C10.4175505,7.22071332 10.9973662,7 11.6859406,7 C12.1131716,7 12.4979375,7.09112961 12.8348078,7.27522069 C13.1746136,7.46091603 13.4472171,7.72952508 13.6486043,8.07526694 C13.8483691,8.41822347 13.9503539,8.77415268 13.9503539,9.13828125 C13.9503539,9.55406894 13.7188967,9.98681416 13.4907809,10.4451899 C13.2667292,10.8953993 12.9524998,11.3208384 12.4178891,12.0000117 L12.3003917,12.1228516 L12.0445871,12.3519235 L14,12.3519235 Z'/%3E%3C/svg%3E");
} }
&.qtoggleTableBorders {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+CiAgPHJlY3QgeD0iMyIgeT0iMyIgd2lkdGg9IjciIGhlaWdodD0iNyIgLz4KICA8cmVjdCB4PSIxNCIgeT0iMyIgd2lkdGg9IjciIGhlaWdodD0iNyIgLz4KICA8cmVjdCB4PSIzIiB5PSIxNCIgd2lkdGg9IjciIGhlaWdodD0iNyIgLz4KICA8cmVjdCB4PSIxNCIgeT0iMTQiIHdpZHRoPSI3IiBoZWlnaHQ9IjciIC8+CiAgPHBhdGggZD0iTTMgMTJoMTgiIC8+CiAgPHBhdGggZD0iTTEyIDN2MTgiIC8+Cjwvc3ZnPgo=");
}
&.qinsertToggleBlock {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+CiAgPHJlY3QgeD0iMyIgeT0iNCIgd2lkdGg9IjE4IiBoZWlnaHQ9IjMiIHJ4PSIxIiAvPgogIDxyZWN0IHg9IjMiIHk9IjEwIiB3aWR0aD0iMTgiIGhlaWdodD0iMyIgcng9IjEiIC8+CiAgPHJlY3QgeD0iMyIgeT0iMTYiIHdpZHRoPSIxMiIgaGVpZ2h0PSIzIiByeD0iMSIgLz4KICA8cG9seWxpbmUgcG9pbnRzPSIxOCwxNiAyMSwxOC41IDE4LDIxIiAvPgo8L3N2Zz4K");
}
} }
.qcolors{ .qcolors{
...@@ -1126,3 +1132,62 @@ body{ ...@@ -1126,3 +1132,62 @@ body{
opacity: 1; opacity: 1;
} }
} }
/* внутри редактора */
.atma-editor-content .tableWrapper:has(table[data-bordered='false']) td,
.atma-editor-content .tableWrapper:has(table[data-bordered='false']) th {
border: none !important;
}
.tableWrapper[data-no-border='true'] table,
.tableWrapper[data-no-border='true'] td,
.tableWrapper[data-no-border='true'] th {
border: none !important;
}
.toggle-body {
overflow: hidden;
transition: max-height 0.3s ease;
}
.toggle-block[data-open="false"] .toggle-body {
max-height: 0;
padding: 0;
border: none;
}
.toggle-block[data-open="true"] .toggle-body {
max-height: 1000px;
border: 1px dashed #aaa;
border-radius: 6px;
padding: 10px;
background: #fafafa;
}
.toggle-block {
margin-bottom: 12px;
}
.toggle-header-wrapper {
position: relative;
display: inline-block;
margin-bottom: 5px;
}
.toggle-header-measurer {
visibility: hidden;
white-space: pre;
position: absolute;
top: 0;
left: 0;
font: inherit;
padding: 0;
border: 0;
}
.toggle-header {
font: inherit;
padding: 2px 6px;
border: 1px solid #ccc;
border-radius: 4px;
margin-left: 5px;
}
.toggle-button {
height: 25px;
vertical-align: top;
}
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