fix:标题,图片设置。

This commit is contained in:
shixiaohua 2024-04-30 17:10:52 +08:00
parent 3f97149a73
commit 72310d3278
11 changed files with 418 additions and 84 deletions

View File

@ -58,9 +58,7 @@
"electron-store": "^8.1.0", "electron-store": "^8.1.0",
"md5": "^2.3.0", "md5": "^2.3.0",
"mousetrap": "^1.6.5", "mousetrap": "^1.6.5",
"node-sass": "^7.0.3", "redux-persist": "^6.0.0"
"redux-persist": "^6.0.0",
"redux-persist-electron-storage": "^2.1.0"
}, },
"browser": { "browser": {
"fs": false "fs": false

View File

@ -1,5 +1,5 @@
import {useEffect, useLayoutEffect} from 'react'; import {useEffect, useLayoutEffect} from 'react';
import {CAN_USE_DOM} from './canUseDOM'; import {CAN_USE_DOM} from '@/pages/Note/Hlexical/context/shared/canUseDOM';
const useLayoutEffectImpl = CAN_USE_DOM const useLayoutEffectImpl = CAN_USE_DOM
? useLayoutEffect ? useLayoutEffect

View File

@ -1,7 +1,7 @@
import {useCallback, useMemo, useState} from 'react'; import {useCallback, useMemo, useState} from 'react';
import * as React from 'react'; import * as React from 'react';
import Modal from '../plugins/Input/Modal'; import Modal from '@/pages/Note/Hlexical/plugins/Input/Modal';
export default function useModal(){ export default function useModal(){
const [modalContent, setModalContent] = useState(null); const [modalContent, setModalContent] = useState(null);

View File

@ -34,6 +34,7 @@ import ContextMenuPlugin from "@/pages/Note/Hlexical/plugins/ContextMenuPlugin"
import {Spin} from "antd"; import {Spin} from "antd";
import {useState} from "react"; import {useState} from "react";
import DragDropPaste from "@/pages/Note/Hlexical/plugins/DragDropPastePlugin"; import DragDropPaste from "@/pages/Note/Hlexical/plugins/DragDropPastePlugin";
import TreeViewPlugin from "@/pages/Note/Hlexical/plugins/TreeViewPlugin";
function Placeholder() { function Placeholder() {
return <div className="editor-placeholder">记录一些灵感吧</div>; return <div className="editor-placeholder">记录一些灵感吧</div>;
} }
@ -94,7 +95,7 @@ export default function Hlexical(props) {
{/*markdown 快捷键*/} {/*markdown 快捷键*/}
<MarkdownShortcutPlugin transformers={TRANSFORMERS}/> <MarkdownShortcutPlugin transformers={TRANSFORMERS}/>
{/*图片加载*/} {/*图片加载*/}
<ImagesPlugin/> <ImagesPlugin captionsEnabled={true}/>
<InlineImagePlugin/> <InlineImagePlugin/>
{/*分割线 */} {/*分割线 */}

View File

@ -225,22 +225,60 @@ h1 {
} }
.editor-heading-h1 { .editor-heading-h1 {
font-size: 24px; font-size: 50px;
color: rgb(5, 5, 5); color: rgb(5, 5, 5);
font-weight: 400; font-weight: 400;
margin: 0; margin: 0;
margin-bottom: 12px; margin-bottom: 25px;
padding: 0; padding: 0;
} }
.editor-heading-h2 { .editor-heading-h2 {
font-size: 15px; font-size: 45px;
color: rgb(101, 103, 107); //color: rgb(101, 103, 107);
font-weight: 700; //font-weight: 400;
color: rgb(5, 5, 5);
font-weight: 400;
margin: 0; margin: 0;
margin-top: 10px; margin-top: 25px;
padding: 0; padding: 0;
text-transform: uppercase; //text-transform: uppercase;
}
.editor-heading-h3 {
font-size: 40px;
color: rgb(5, 5, 5);
font-weight: 400;
margin: 0;
margin-top: 20px;
padding: 0;
//text-transform: uppercase;
}
.editor-heading-h4 {
font-size: 35px;
color: rgb(5, 5, 5);
font-weight: 400;
margin: 0;
margin-top: 20px;
padding: 0;
//text-transform: uppercase;
}
.editor-heading-h5 {
font-size: 30px;
color: rgb(5, 5, 5);
font-weight: 400;
margin: 0;
margin-top: 15px;
padding: 0;
//text-transform: uppercase;
}
.editor-heading-h6 {
font-size: 25px;
color: rgb(5, 5, 5);
font-weight: 400;
margin: 0;
margin-top: 15px;
padding: 0;
//text-transform: uppercase;
} }
.editor-quote { .editor-quote {

View File

@ -1,4 +1,4 @@
import '../index.less'; import '@/pages/Note/Hlexical/nodes/ImageNode/index.less';
import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin'; import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
import {useCollaborationContext} from '@lexical/react/LexicalCollaborationContext'; import {useCollaborationContext} from '@lexical/react/LexicalCollaborationContext';
import {CollaborationPlugin} from '@lexical/react/LexicalCollaborationPlugin'; import {CollaborationPlugin} from '@lexical/react/LexicalCollaborationPlugin';
@ -30,20 +30,25 @@ import {Suspense, useCallback, useEffect, useRef, useState} from 'react';
// import {createWebsocketProvider} from '../../../createWebsocketProvider'; // import {createWebsocketProvider} from '../../../createWebsocketProvider';
import {useSharedHistoryContext} from '../../../context/SharedHistoryContext'; import {useSharedHistoryContext} from '../../../context/SharedHistoryContext';
import EmojisPlugin from '../../../plugins/EmojisPlugin'; import EmojisPlugin from '@/pages/Note/Hlexical/plugins/EmojisPlugin';
import KeywordsPlugin from '../../../plugins/KeywordsPlugin'; import KeywordsPlugin from '@/pages/Note/Hlexical/plugins/KeywordsPlugin';
import LinkPlugin from '../../../plugins/LinkPlugin'; import LinkPlugin from '@/pages/Note/Hlexical/plugins/LinkPlugin';
// import MentionsPlugin from '../../../plugins/MentionsPlugin'; // import MentionsPlugin from '../../../plugins/MentionsPlugin';
import TreeViewPlugin from '../../../plugins/TreeViewPlugin'; import TreeViewPlugin from '@/pages/Note/Hlexical/plugins/TreeViewPlugin';
import ContentEditable from '../../../plugins/Input/ContentEditable'; import ContentEditable from '@/pages/Note/Hlexical/plugins/Input/ContentEditable';
import ImageResizer from '../../../plugins/Input/ImageResizer'; import ImageResizer from '@/pages/Note/Hlexical/plugins/Input/ImageResizer';
import Placeholder from '../../../plugins/Input/Placeholder'; import Placeholder from '@/pages/Note/Hlexical/plugins/Input/Placeholder';
import {$isImageNode} from '../index';
import {$isImageNode} from '@/pages/Note/Hlexical/nodes/ImageNode';
import {Image as AntImage} from "antd";
/*
* 缓存图片后期使用预览所有图片
*/
const imageCache = new Set(); const imageCache = new Set();
/*
* 添加图片到缓存列表
*/
function useSuspenseImage(src) { function useSuspenseImage(src) {
if (!imageCache.has(src)) { if (!imageCache.has(src)) {
throw new Promise((resolve) => { throw new Promise((resolve) => {
@ -59,16 +64,23 @@ function useSuspenseImage(src) {
function LazyImage({altText, className, imageRef, src, width, height, maxWidth,}) { function LazyImage({altText, className, imageRef, src, width, height, maxWidth,}) {
useSuspenseImage(src); useSuspenseImage(src);
return (<img const [editor] = useLexicalComposerContext();
className={className || undefined} return editor.isEditable()?(<img
className={className || undefined}
src={src}
alt={altText}
ref={imageRef}
style={{
height, maxWidth, width,
}}
draggable="false"
/>):(<AntImage
src={src} src={src}
alt={altText} alt={altText}
height={height}
width={width}
ref={imageRef} ref={imageRef}
style={{ />)
height, maxWidth, width,
}}
draggable="false"
/>);
} }
export default function ImageComponent({ export default function ImageComponent({
@ -87,6 +99,7 @@ export default function ImageComponent({
const buttonRef = useRef(null); const buttonRef = useRef(null);
const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey); const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
const [isResizing, setIsResizing] = useState(false); const [isResizing, setIsResizing] = useState(false);
//
const {isCollabActive} = useCollaborationContext(); const {isCollabActive} = useCollaborationContext();
const [editor] = useLexicalComposerContext(); const [editor] = useLexicalComposerContext();
const [selection, setSelection] = useState(null); const [selection, setSelection] = useState(null);
@ -139,6 +152,7 @@ export default function ImageComponent({
}, [caption, editor, setSelected],); }, [caption, editor, setSelected],);
useEffect(() => { useEffect(() => {
//
let isMounted = true; let isMounted = true;
const unregister = mergeRegister( const unregister = mergeRegister(
editor.registerUpdateListener(({editorState}) => { editor.registerUpdateListener(({editorState}) => {
@ -152,12 +166,15 @@ export default function ImageComponent({
}, COMMAND_PRIORITY_LOW,), }, COMMAND_PRIORITY_LOW,),
editor.registerCommand(CLICK_COMMAND, (payload) => { editor.registerCommand(CLICK_COMMAND, (payload) => {
const event = payload; const event = payload;
console.log("点击图片:",event,isResizing)
if (isResizing) { if (isResizing) {
return true; return true;
} }
if (event.target === imageRef.current) { if (event.target === imageRef.current) {
if (event.shiftKey) { if (event.ctrlKey){
// ctrl
}else if (event.shiftKey) {
// shift
setSelected(!isSelected); setSelected(!isSelected);
} else { } else {
clearSelection(); clearSelection();
@ -168,6 +185,7 @@ export default function ImageComponent({
return false; return false;
}, COMMAND_PRIORITY_LOW,), }, COMMAND_PRIORITY_LOW,),
//
editor.registerCommand(DRAGSTART_COMMAND, (event) => { editor.registerCommand(DRAGSTART_COMMAND, (event) => {
if (event.target === imageRef.current) { if (event.target === imageRef.current) {
// TODO This is just a temporary workaround for FF to behave like other browsers. // TODO This is just a temporary workaround for FF to behave like other browsers.
@ -191,6 +209,7 @@ export default function ImageComponent({
editor.update(() => { editor.update(() => {
const node = $getNodeByKey(nodeKey); const node = $getNodeByKey(nodeKey);
if ($isImageNode(node)) { if ($isImageNode(node)) {
//
node.setShowCaption(true); node.setShowCaption(true);
} }
}); });

View File

@ -3,7 +3,7 @@ import * as React from 'react';
import {Suspense} from 'react'; import {Suspense} from 'react';
const ImageComponent = React.lazy( const ImageComponent = React.lazy(
() => import('./ImageComponent'), () => import('@/pages/Note/Hlexical/nodes/ImageNode/ImageComponent'),
); );
function convertImageElement(domNode) { function convertImageElement(domNode) {
@ -15,17 +15,18 @@ function convertImageElement(domNode) {
return null; return null;
} }
export class ImageNode extends DecoratorNode { export class ImageNode extends DecoratorNode {
//
__src; __src;
__altText; __altText;
//
__width; __width;
__height; __height;
__maxWidth; __maxWidth;
//
__showCaption; __showCaption;
__caption; __caption;
__captionsEnabled; __captionsEnabled;
static getType() { static getType() {
return 'image'; return 'image';
} }
@ -154,6 +155,7 @@ export class ImageNode extends DecoratorNode {
} }
decorate() { decorate() {
console.log('ImageNode.decorate()')
return ( return (
<Suspense fallback={null}> <Suspense fallback={null}>
<ImageComponent <ImageComponent
@ -176,7 +178,7 @@ export class ImageNode extends DecoratorNode {
export function $createImageNode({ export function $createImageNode({
altText, altText,
height, height,
maxWidth = 500, maxWidth = 600,
captionsEnabled, captionsEnabled,
src, src,
width, width,

View File

@ -41,3 +41,236 @@
.image-control-wrapper--resizing { .image-control-wrapper--resizing {
touch-action: none; touch-action: none;
} }
.editor-container span.editor-image {
cursor: default;
display: inline-block;
position: relative;
user-select: none;
}
.editor-container .editor-image img {
max-width: 200%;
cursor: default;
}
.editor-container .editor-image img.focused {
outline: 2px solid rgb(60, 132, 244);
user-select: none;
}
.editor-container .editor-image img.focused.draggable {
cursor: grab;
}
.editor-container .editor-image img.focused.draggable:active {
cursor: grabbing;
}
.editor-container .editor-image .image-caption-container .tree-view-output {
margin: 0;
border-radius: 0;
}
.editor-container .editor-image .image-caption-container {
display: block;
position: absolute;
bottom: 4px;
left: 0;
right: 0;
padding: 0;
margin: 0;
border-top: 1px solid #fff;
background-color: rgba(255, 255, 255, 0.9);
min-width: 100px;
color: #000;
overflow: hidden;
}
.editor-container .editor-image .image-caption-button {
display: block;
position: absolute;
bottom: 20px;
left: 0;
right: 0;
width: 30%;
padding: 10px;
margin: 0 auto;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.5);
min-width: 100px;
color: #fff;
cursor: pointer;
user-select: none;
}
.editor-container .editor-image .image-caption-button:hover {
background-color: rgba(60, 132, 244, 0.5);
}
.editor-container .editor-image .image-edit-button {
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 5px;
background-image: url(../../images/icons/pencil-fill.svg);
background-size: 16px;
background-position: center;
background-repeat: no-repeat;
width: 35px;
height: 35px;
vertical-align: -0.25em;
position: absolute;
right: 4px;
top: 4px;
cursor: pointer;
user-select: none;
}
.editor-container .editor-image .image-edit-button:hover {
background-color: rgba(60, 132, 244, 0.1);
}
.editor-container .editor-image .image-resizer {
display: block;
width: 7px;
height: 7px;
position: absolute;
background-color: rgb(60, 132, 244);
border: 1px solid #fff;
}
.editor-container .editor-image .image-resizer.image-resizer-n {
top: -6px;
left: 48%;
cursor: n-resize;
}
.editor-container .editor-image .image-resizer.image-resizer-ne {
top: -6px;
right: -6px;
cursor: ne-resize;
}
.editor-container .editor-image .image-resizer.image-resizer-e {
bottom: 48%;
right: -6px;
cursor: e-resize;
}
.editor-container .editor-image .image-resizer.image-resizer-se {
bottom: -2px;
right: -6px;
cursor: nwse-resize;
}
.editor-container .editor-image .image-resizer.image-resizer-s {
bottom: -2px;
left: 48%;
cursor: s-resize;
}
.editor-container .editor-image .image-resizer.image-resizer-sw {
bottom: -2px;
left: -6px;
cursor: sw-resize;
}
.editor-container .editor-image .image-resizer.image-resizer-w {
bottom: 48%;
left: -6px;
cursor: w-resize;
}
.editor-container .editor-image .image-resizer.image-resizer-nw {
top: -6px;
left: -6px;
cursor: nw-resize;
}
.editor-container span.inline-editor-image {
cursor: default;
display: inline-block;
position: relative;
z-index: 1;
}
.editor-container .inline-editor-image img {
max-width: 200%;
cursor: default;
}
.editor-container .inline-editor-image img.focused {
outline: 2px solid rgb(60, 132, 244);
}
.editor-container .inline-editor-image img.focused.draggable {
cursor: grab;
}
.editor-container .inline-editor-image img.focused.draggable:active {
cursor: grabbing;
}
.editor-container .inline-editor-image .image-caption-container .tree-view-output {
margin: 0;
border-radius: 0;
}
.editor-container .inline-editor-image.position-full {
margin: 1em 0 1em 0;
}
.editor-container .inline-editor-image.position-left {
float: left;
width: 50%;
margin: 1em 1em 0 0;
}
.editor-container .inline-editor-image.position-right {
float: right;
width: 50%;
margin: 1em 0 0 1em;
}
.editor-container .inline-editor-image .image-edit-button {
display: block;
position: absolute;
top: 12px;
right: 12px;
padding: 6px 8px;
margin: 0 auto;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.5);
min-width: 60px;
color: #fff;
cursor: pointer;
user-select: none;
}
.editor-container .inline-editor-image .image-edit-button:hover {
background-color: rgba(60, 132, 244, 0.5);
}
.editor-container .inline-editor-image .image-caption-container {
display: block;
background-color: #f4f4f4;
min-width: 100%;
color: #000;
overflow: hidden;
}
.editor-container .editor-image img.focused.draggable {
cursor: grab;
}
.editor-container .editor-image img.focused.draggable:active {
cursor: grabbing;
}
.editor-container .inline-editor-image img.focused.draggable {
cursor: grab;
}
.editor-container .inline-editor-image img.focused.draggable:active {
cursor: grabbing;
}

View File

@ -30,33 +30,34 @@ import {DialogActions, DialogButtonsList} from '../Input/Dialog';
import FileInput from '../Input/FileInput'; import FileInput from '../Input/FileInput';
import TextInput from '../Input/TextInput'; import TextInput from '../Input/TextInput';
import {Button} from "antd"; import {Button} from "antd";
export const INSERT_IMAGE_COMMAND = export const INSERT_IMAGE_COMMAND =
createCommand('INSERT_IMAGE_COMMAND'); createCommand('INSERT_IMAGE_COMMAND');
/**
* 插入路径图片对话框
* @param onClick
* @returns {Element}
* @constructor
*/
export function InsertImageUriDialogBody({ export function InsertImageUriDialogBody({
onClick, onClick,
}) { }) {
const [src, setSrc] = useState(''); const [src, setSrc] = useState('');
const [altText, setAltText] = useState(''); const [altText, setAltText] = useState('');
const [resizable,setResizable] = useState(true);
const isDisabled = src === ''; const isDisabled = src === '';
return ( return (
<> <>
<TextInput <TextInput
label="Image URL" label="文件url"
placeholder="i.e. https://source.unsplash.com/random" placeholder="例如:https://tenfei03.cfp.cn/creative/vcg/800/new/VCG41175510742.jpg"
onChange={setSrc} onChange={setSrc}
value={src} value={src}
data-test-id="image-modal-url-input" data-test-id="image-modal-url-input"
/> />
<TextInput <TextInput
label="Alt Text" label="图片描述"
placeholder="Random unsplash image" placeholder="例如:风景"
onChange={setAltText} onChange={setAltText}
value={altText} value={altText}
data-test-id="image-modal-alt-text-input" data-test-id="image-modal-alt-text-input"
@ -65,7 +66,7 @@ export function InsertImageUriDialogBody({
<Button <Button
data-test-id="image-modal-confirm-btn" data-test-id="image-modal-confirm-btn"
disabled={isDisabled} disabled={isDisabled}
onClick={() => onClick({altText, src})}> onClick={() => onClick({altText, src,resizable})}>
确认 确认
</Button> </Button>
</DialogActions> </DialogActions>
@ -73,14 +74,18 @@ export function InsertImageUriDialogBody({
); );
} }
/**
* 插入上传文件对话框
* @param onClick
* @returns {Element}
* @constructor
*/
export function InsertImageUploadedDialogBody({ export function InsertImageUploadedDialogBody({
onClick, onClick,
}) { }) {
const [src, setSrc] = useState(''); const [src, setSrc] = useState('');
const [altText, setAltText] = useState(''); const [altText, setAltText] = useState('');
const isDisabled = src === ''; const isDisabled = src === '';
const loadImage = (files) => { const loadImage = (files) => {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = function () { reader.onload = function () {
@ -121,6 +126,13 @@ export function InsertImageUploadedDialogBody({
); );
} }
/**
* 插入文件对话框
* @param activeEditor
* @param onClose
* @returns {Element}
* @constructor
*/
export function InsertImageDialog({ export function InsertImageDialog({
activeEditor, activeEditor,
onClose, onClose,
@ -166,6 +178,12 @@ export function InsertImageDialog({
); );
} }
/**
* 图片插件
* @param captionsEnabled
* @returns {null}
* @constructor
*/
export default function ImagesPlugin({captionsEnabled,}){ export default function ImagesPlugin({captionsEnabled,}){
const [editor] = useLexicalComposerContext(); const [editor] = useLexicalComposerContext();
@ -214,7 +232,10 @@ export default function ImagesPlugin({captionsEnabled,}){
return null; return null;
} }
/**
* 拖拽图标
* @type {string}
*/
const TRANSPARENT_IMAGE = const TRANSPARENT_IMAGE =
'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
const img = document.createElement('img'); const img = document.createElement('img');

View File

@ -39,49 +39,41 @@ import {
getDefaultCodeLanguage, getDefaultCodeLanguage,
getCodeLanguages getCodeLanguages
} from "@lexical/code"; } from "@lexical/code";
import DropDown, {DropDownItem} from "./Input/DropDown"; import DropDown, {DropDownItem} from "@/pages/Note/Hlexical/plugins/Input/DropDown";
import {InsertImageDialog} from "./ImagesPlugin"; import {InsertImageDialog} from "@/pages/Note/Hlexical/plugins/ImagesPlugin";
import useModal from "../hook/userModal"; import useModal from "@/pages/Note/Hlexical/hook/userModal";
import {InsertTableDialog} from "./TablePlugin"; import {InsertTableDialog} from "@/pages/Note/Hlexical/plugins/TablePlugin";
import {INSERT_HORIZONTAL_RULE_COMMAND} from "@lexical/react/LexicalHorizontalRuleNode"; import {INSERT_HORIZONTAL_RULE_COMMAND} from "@lexical/react/LexicalHorizontalRuleNode";
import {InsertInlineImageDialog} from "./InlineImagePlugin"; import {InsertInlineImageDialog} from "@/pages/Note/Hlexical/plugins/InlineImagePlugin";
import {INSERT_EXCALIDRAW_COMMAND} from "./ExcalidrawPlugin"; import {INSERT_EXCALIDRAW_COMMAND} from "@/pages/Note/Hlexical/plugins/ExcalidrawPlugin";
const LowPriority = 1; const LowPriority = 1;
// 支持的块类型
const supportedBlockTypes = new Set([ const supportedBlockTypes = new Set(["paragraph", "quote", "code", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol"
"paragraph",
"quote",
"code",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"ul",
"ol"
]); ]);
// 块类型对应的名称
const blockTypeToBlockName = { const blockTypeToBlockName = {code: "代码块",
code: "代码块", h1: "一级标题", h2: "二级标题", h3: "三级标题", h4: "四级标题", h5: "五级标题", h6: "六级标题",
h1: "一级标题", ol: "有序序列", ul: "无序序列",
h2: "二级标题",
h3: "三级标题",
h4: "四级标题",
h5: "五级标题",
h6: "六级标题",
ol: "有序序列",
paragraph: "普通文本", paragraph: "普通文本",
quote: "引用", quote: "引用",
ul: "无序序列"
}; };
/**
* 工具栏分割符
* @returns {JSX.Element}
* @constructor
*/
function Divider() { function Divider() {
return <div className="divider"/>; return <div className="divider"/>;
} }
/**
* 位置计算
* @param editor
* @param rect
*/
function positionEditorElement(editor, rect) { function positionEditorElement(editor, rect) {
if (rect === null) { if (rect === null) {
editor.style.opacity = "0"; editor.style.opacity = "0";
@ -96,6 +88,12 @@ function positionEditorElement(editor, rect) {
} }
} }
/**
* 浮动连接设置
* @param editor
* @returns {JSX.Element}
* @constructor
*/
function FloatingLinkEditor({editor}) { function FloatingLinkEditor({editor}) {
const editorRef = useRef(null); const editorRef = useRef(null);
const inputRef = useRef(null); const inputRef = useRef(null);
@ -236,6 +234,15 @@ function FloatingLinkEditor({editor}) {
); );
} }
/**
* 下拉选择
* @param onChange
* @param className
* @param options
* @param value
* @returns {JSX.Element}
* @constructor
*/
function Select({onChange, className, options, value}) { function Select({onChange, className, options, value}) {
return ( return (
<select className={className} onChange={onChange} value={value}> <select className={className} onChange={onChange} value={value}>
@ -252,6 +259,7 @@ function Select({onChange, className, options, value}) {
function getSelectedNode(selection) { function getSelectedNode(selection) {
const anchor = selection.anchor; const anchor = selection.anchor;
const focus = selection.focus; const focus = selection.focus;
console.log('selection',anchor,focus)
const anchorNode = selection.anchor.getNode(); const anchorNode = selection.anchor.getNode();
const focusNode = selection.focus.getNode(); const focusNode = selection.focus.getNode();
if (anchorNode === focusNode) { if (anchorNode === focusNode) {
@ -449,6 +457,7 @@ export default function ToolbarPlugin() {
const [isUnderline, setIsUnderline] = useState(false); const [isUnderline, setIsUnderline] = useState(false);
const [isStrikethrough, setIsStrikethrough] = useState(false); const [isStrikethrough, setIsStrikethrough] = useState(false);
const [isCode, setIsCode] = useState(false); const [isCode, setIsCode] = useState(false);
const [isEditable, setIsEditable] = useState(() => editor.isEditable());
const updateToolbar = useCallback(() => { const updateToolbar = useCallback(() => {
const selection = $getSelection(); const selection = $getSelection();
@ -555,7 +564,18 @@ export default function ToolbarPlugin() {
return ( return (
<div className="toolbar" ref={toolbarRef}> <div className="toolbar" ref={toolbarRef}>
<button <button
disabled={!canUndo} onClick={() => {
editor.setEditable(!isEditable)
setIsEditable(!isEditable)
}}
className={"toolbar-item spaced " + (isBold ? "active" : "")}
aria-label="Format Bold"
>
{isEditable?'阅读':'编辑'}
</button>
<Divider/>
<button
disabled={!canUndo||!isEditable}
onClick={() => { onClick={() => {
editor.dispatchCommand(UNDO_COMMAND); editor.dispatchCommand(UNDO_COMMAND);
}} }}
@ -565,7 +585,7 @@ export default function ToolbarPlugin() {
<i className="format undo"/> <i className="format undo"/>
</button> </button>
<button <button
disabled={!canRedo} disabled={!canRedo||!isEditable}
onClick={() => { onClick={() => {
editor.dispatchCommand(REDO_COMMAND); editor.dispatchCommand(REDO_COMMAND);
}} }}
@ -578,6 +598,7 @@ export default function ToolbarPlugin() {
{supportedBlockTypes.has(blockType) && ( {supportedBlockTypes.has(blockType) && (
<> <>
<button <button
disabled={!isEditable}
className="toolbar-item block-controls" className="toolbar-item block-controls"
onClick={() => onClick={() =>
setShowBlockOptionsDropDown(!showBlockOptionsDropDown) setShowBlockOptionsDropDown(!showBlockOptionsDropDown)

View File

@ -9,7 +9,8 @@ const firstTheme = {
h2: "editor-heading-h2", h2: "editor-heading-h2",
h3: "editor-heading-h3", h3: "editor-heading-h3",
h4: "editor-heading-h4", h4: "editor-heading-h4",
h5: "editor-heading-h5" h5: "editor-heading-h5",
h6: "editor-heading-h6"
}, },
list: { list: {
nested: { nested: {