feat:添加分割线

This commit is contained in:
shixiaohua 2024-02-27 09:47:34 +08:00
parent 4a995d69a5
commit 36f2cb43e0
8 changed files with 398 additions and 252 deletions

View File

@ -1,13 +1,13 @@
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 "antd";
import Modal from '../plugins/Input/Modal';
export default function useModal(){ export default function useModal(){
const [modalContent, setModalContent] = useState(null); const [modalContent, setModalContent] = useState(null);
const [isOpen,setIsOpen]=useState(true)
const onClose = useCallback(() => { const onClose = useCallback(() => {
setModalContent(null); setModalContent(null);
setIsOpen(false)
}, []); }, []);
const modal = useMemo(() => { const modal = useMemo(() => {
@ -15,21 +15,19 @@ export default function useModal(){
return null; return null;
} }
const {title, content, closeOnClickOutside} = modalContent; const {title, content, closeOnClickOutside} = modalContent;
return (<div> console.log("modalContent !== null",modalContent)
return (
<Modal <Modal
open={isOpen} onClose={onClose}
//onOk={handleOk}
onCancel={onClose}
title={title} title={title}
// closeOnClickOutside={closeOnClickOutside} closeOnClickOutside={closeOnClickOutside}>
>
{content} {content}
</Modal></div> </Modal>
); );
}, [modalContent, onClose,isOpen]); }, [modalContent, onClose]);
const showModal = useCallback(( const showModal = useCallback(
(
title, title,
getContent, getContent,
closeOnClickOutside = false, closeOnClickOutside = false,

View File

@ -20,11 +20,13 @@ import AutoLinkPlugin from "./plugins/AutoLinkPlugin";
import ListMaxIndentLevelPlugin from "./plugins/ListMaxIndentLevelPlugin"; import ListMaxIndentLevelPlugin from "./plugins/ListMaxIndentLevelPlugin";
import CodeHighlightPlugin from "./plugins/CodeHighlightPlugin"; import CodeHighlightPlugin from "./plugins/CodeHighlightPlugin";
import ImportFilePlugin from "./plugins/ImportFilePlugin"; import ImportFilePlugin from "./plugins/ImportFilePlugin";
import {TablePlugin} from "@lexical/react/LexicalTablePlugin";
import SaveFilePlugin from "./plugins/SaveFilePlugin"; import SaveFilePlugin from "./plugins/SaveFilePlugin";
import {TabIndentationPlugin} from "@lexical/react/LexicalTabIndentationPlugin"; import {TabIndentationPlugin} from "@lexical/react/LexicalTabIndentationPlugin";
import UsefulNodes from "./nodes/UsefulNodes"; import UsefulNodes from "./nodes/UsefulNodes";
import ImagesPlugin from "./plugins/ImagesPlugin"; import ImagesPlugin from "./plugins/ImagesPlugin";
import {TablePlugin} from "./plugins/TablePlugin";
import {HorizontalRulePlugin} from "@lexical/react/LexicalHorizontalRulePlugin"
function Placeholder() { function Placeholder() {
return <div className="editor-placeholder">Enter some rich text...</div>; return <div className="editor-placeholder">Enter some rich text...</div>;
} }
@ -74,9 +76,12 @@ export default function Hlexical(props) {
<MarkdownShortcutPlugin transformers={TRANSFORMERS}/> <MarkdownShortcutPlugin transformers={TRANSFORMERS}/>
{/*图片加载*/} {/*图片加载*/}
<ImagesPlugin/> <ImagesPlugin/>
{/*分割线 */}
<HorizontalRulePlugin />
{/*页分割线*/}
{/*目录加载*/} {/*目录加载*/}
{/* 表格加载 */} {/* 表格加载 */}
{/*<TableOfContentsPlugin />*/} {/*<TableOfContentsPlugin />*/}
{/*<LexicalTableOfContents>*/} {/*<LexicalTableOfContents>*/}
{/* {(tableOfContentsArray) => {*/} {/* {(tableOfContentsArray) => {*/}

View File

@ -4,6 +4,7 @@ import {ListItemNode, ListNode} from "@lexical/list";
import {CodeHighlightNode, CodeNode, $createCodeNode, $isCodeNode} from "@lexical/code"; import {CodeHighlightNode, CodeNode, $createCodeNode, $isCodeNode} from "@lexical/code";
import {AutoLinkNode, LinkNode} from "@lexical/link"; import {AutoLinkNode, LinkNode} from "@lexical/link";
import {ImageNode} from "./ImageNode"; import {ImageNode} from "./ImageNode";
import {HorizontalRuleNode} from "@lexical/react/LexicalHorizontalRuleNode";
const UsefulNodes=[ const UsefulNodes=[
HeadingNode, HeadingNode,
@ -17,6 +18,7 @@ const UsefulNodes=[
TableRowNode, TableRowNode,
AutoLinkNode, AutoLinkNode,
ImageNode, ImageNode,
LinkNode LinkNode,
HorizontalRuleNode,
] ]
export default UsefulNodes; export default UsefulNodes;

View File

@ -0,0 +1,89 @@
import './index.less';
import * as React from 'react';
import {useEffect, useRef} from 'react';
import {createPortal} from 'react-dom';
function PortalImpl({ onClose,
children,
title,
closeOnClickOutside,
}) {
const modalRef = useRef(null);
useEffect(() => {
console.log("PortalImpl")
if (modalRef.current !== null) {
modalRef.current.focus();
}
}, []);
useEffect(() => {
let modalOverlayElement= null;
const handler = (event) => {
if (event.keyCode === 27) {
onClose();
}
};
const clickOutsideHandler = (event) => {
const target = event.target;
if (
modalRef.current !== null &&
!modalRef.current.contains(target) &&
closeOnClickOutside
) {
onClose();
}
};
const modelElement = modalRef.current;
if (modelElement !== null) {
modalOverlayElement = modelElement.parentElement;
if (modalOverlayElement !== null) {
modalOverlayElement.addEventListener('click', clickOutsideHandler);
}
}
window.addEventListener('keydown', handler);
return () => {
window.removeEventListener('keydown', handler);
if (modalOverlayElement !== null) {
modalOverlayElement?.removeEventListener('click', clickOutsideHandler);
}
};
}, [closeOnClickOutside, onClose]);
return (
<div className="Modal__overlay" role="dialog">
<div className="Modal__modal" tabIndex={-1} ref={modalRef}>
<h2 className="Modal__title">{title}</h2>
<button
className="Modal__closeButton"
aria-label="Close modal"
type="button"
onClick={onClose}>
X
</button>
<div className="Modal__content">{children}</div>
</div>
</div>
);
}
export default function Modal({
onClose,
children,
title,
closeOnClickOutside = false,
}){
console.log("createPortal")
return createPortal(
<PortalImpl
onClose={onClose}
title={title}
closeOnClickOutside={closeOnClickOutside}>
{children}
</PortalImpl>,
document.body,
);
}

View File

@ -0,0 +1,53 @@
.Modal__overlay {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
flex-direction: column;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
background-color: rgba(40, 40, 40, 0.6);
flex-grow: 0px;
flex-shrink: 1px;
z-index: 100;
}
.Modal__modal {
padding: 20px;
min-height: 100px;
min-width: 300px;
display: flex;
flex-grow: 0px;
background-color: #fff;
flex-direction: column;
position: relative;
box-shadow: 0 0 20px 0 #444;
border-radius: 10px;
}
.Modal__title {
color: #444;
margin: 0px;
padding-bottom: 10px;
border-bottom: 1px solid #ccc;
}
.Modal__closeButton {
border: 0px;
position: absolute;
right: 20px;
border-radius: 20px;
justify-content: center;
align-items: center;
display: flex;
width: 30px;
height: 30px;
text-align: center;
cursor: pointer;
background-color: #eee;
}
.Modal__closeButton:hover {
background-color: #ddd;
}
.Modal__content {
padding-top: 20px;
}

View File

@ -102,53 +102,53 @@ export function InsertTableDialog({activeEditor,
); );
} }
export function InsertNewTableDialog({activeEditor, onClose, // export function InsertNewTableDialog({activeEditor, onClose,
}){ // }){
const [rows, setRows] = useState(''); // const [rows, setRows] = useState('');
const [columns, setColumns] = useState(''); // const [columns, setColumns] = useState('');
const [isDisabled, setIsDisabled] = useState(true); // const [isDisabled, setIsDisabled] = useState(true);
//
useEffect(() => { // useEffect(() => {
const row = Number(rows); // const row = Number(rows);
const column = Number(columns); // const column = Number(columns);
if (row && row > 0 && row <= 500 && column && column > 0 && column <= 50) { // if (row && row > 0 && row <= 500 && column && column > 0 && column <= 50) {
setIsDisabled(false); // setIsDisabled(false);
} else { // } else {
setIsDisabled(true); // setIsDisabled(true);
} // }
}, [rows, columns]); // }, [rows, columns]);
//
const onClick = () => { // const onClick = () => {
activeEditor.dispatchCommand(INSERT_NEW_TABLE_COMMAND, {columns, rows}); // activeEditor.dispatchCommand(INSERT_NEW_TABLE_COMMAND, {columns, rows});
onClose(); // onClose();
}; // };
//
return ( // return (
<> // <>
<TextInput // <TextInput
placeholder={'# of rows (1-500)'} // placeholder={'# of rows (1-500)'}
label="Rows" // label="Rows"
onChange={setRows} // onChange={setRows}
value={rows} // value={rows}
data-test-id="table-modal-rows" // data-test-id="table-modal-rows"
type="number" // type="number"
/> // />
<TextInput // <TextInput
placeholder={'# of columns (1-50)'} // placeholder={'# of columns (1-50)'}
label="Columns" // label="Columns"
onChange={setColumns} // onChange={setColumns}
value={columns} // value={columns}
data-test-id="table-modal-columns" // data-test-id="table-modal-columns"
type="number" // type="number"
/> // />
<DialogActions data-test-id="table-model-confirm-insert"> // <DialogActions data-test-id="table-model-confirm-insert">
<Button disabled={isDisabled} onClick={onClick}> // <Button disabled={isDisabled} onClick={onClick}>
Confirm // Confirm
</Button> // </Button>
</DialogActions> // </DialogActions>
</> // </>
); // );
} // }
export function TablePlugin({ export function TablePlugin({
cellEditorConfig, cellEditorConfig,
@ -165,7 +165,7 @@ export function TablePlugin({
cellContext.set(cellEditorConfig, children); cellContext.set(cellEditorConfig, children);
return editor.registerCommand( return editor.registerCommand(
INSERT_NEW_TABLE_COMMAND, INSERT_TABLE_COMMAND,
({columns, rows, includeHeaders}) => { ({columns, rows, includeHeaders}) => {
const tableNode = $createTableNodeWithDimensions( const tableNode = $createTableNodeWithDimensions(
Number(rows), Number(rows),

View File

@ -44,7 +44,7 @@ import DropDown, {DropDownItem} from "./Input/DropDown";
import {InsertImageDialog} from "./ImagesPlugin"; import {InsertImageDialog} from "./ImagesPlugin";
import useModal from "../hook/userModal"; import useModal from "../hook/userModal";
import {InsertTableDialog} from "./TablePlugin"; import {InsertTableDialog} from "./TablePlugin";
import {INSERT_HORIZONTAL_RULE_COMMAND} from "@lexical/react/LexicalHorizontalRuleNode.prod"; import {INSERT_HORIZONTAL_RULE_COMMAND} from "@lexical/react/LexicalHorizontalRuleNode";
const LowPriority = 1; const LowPriority = 1;
@ -701,11 +701,10 @@ export default function ToolbarPlugin() {
> >
<i className="format justify-align"/> <i className="format justify-align"/>
</button> </button>
{" "}
<Divider/> <Divider/>
<DropDown <DropDown
buttonClassName="toolbar-item spaced" buttonClassName="toolbar-item spaced"
buttonLabel="Insert" buttonLabel="插入"
buttonAriaLabel="Insert specialized editor node" buttonAriaLabel="Insert specialized editor node"
buttonIconClassName="icon plus"> buttonIconClassName="icon plus">
<DropDownItem <DropDownItem
@ -717,16 +716,16 @@ export default function ToolbarPlugin() {
}} }}
className="item"> className="item">
<i className="icon horizontal-rule"/> <i className="icon horizontal-rule"/>
<span className="text">Horizontal Rule</span> <span className="text">分割线</span>
</DropDownItem>
<DropDownItem
onClick={() => {
activeEditor.dispatchCommand(INSERT_PAGE_BREAK, undefined);
}}
className="item">
<i className="icon page-break"/>
<span className="text">Page Break</span>
</DropDownItem> </DropDownItem>
{/*<DropDownItem*/}
{/* onClick={() => {*/}
{/* activeEditor.dispatchCommand(INSERT_PAGE_BREAK, undefined);*/}
{/* }}*/}
{/* className="item">*/}
{/* <i className="icon page-break"/>*/}
{/* <span className="text">页分割线</span>*/}
{/*</DropDownItem>*/}
<DropDownItem <DropDownItem
onClick={() => { onClick={() => {
showModal('Insert Image', (onClose) => ( showModal('Insert Image', (onClose) => (
@ -738,7 +737,7 @@ export default function ToolbarPlugin() {
}} }}
className="item"> className="item">
<i className="icon image"/> <i className="icon image"/>
<span className="text">Image</span> <span className="text">图片</span>
</DropDownItem> </DropDownItem>
<DropDownItem <DropDownItem
onClick={() => { onClick={() => {
@ -751,7 +750,7 @@ export default function ToolbarPlugin() {
}} }}
className="item"> className="item">
<i className="icon image"/> <i className="icon image"/>
<span className="text">Inline Image</span> <span className="text">行内图片</span>
</DropDownItem> </DropDownItem>
<DropDownItem <DropDownItem
onClick={() => onClick={() =>
@ -762,7 +761,7 @@ export default function ToolbarPlugin() {
} }
className="item"> className="item">
<i className="icon gif"/> <i className="icon gif"/>
<span className="text">GIF</span> <span className="text">动态图片</span>
</DropDownItem> </DropDownItem>
<DropDownItem <DropDownItem
onClick={() => { onClick={() => {
@ -773,7 +772,7 @@ export default function ToolbarPlugin() {
}} }}
className="item"> className="item">
<i className="icon diagram-2"/> <i className="icon diagram-2"/>
<span className="text">Excalidraw</span> <span className="text">画图</span>
</DropDownItem> </DropDownItem>
<DropDownItem <DropDownItem
onClick={() => { onClick={() => {
@ -789,7 +788,7 @@ export default function ToolbarPlugin() {
<span className="text">表格</span> <span className="text">表格</span>
</DropDownItem> </DropDownItem>
</DropDown> </DropDown>
{modal}
</> </>
)} )}
</div> </div>