menuItem
2024年07月05日
一、boldMenu.tsx
import { Editor } from "@tiptap/core";
export type BoldMenuProps = {
editor: Editor;
};
function BoldMenu(props: BoldMenuProps) {
const { editor } = props;
const isEmpty = editor.isEmpty;
const isFocused = editor.isFocused;
const onBold = () => {
if(isEmpty && !isFocused){
console.log("kkk")
return;
}
editor.chain().focus().toggleBold().run();
};
return (
<div className="menu-item-container menu-item__bold-menu">
<div
onClick={onBold}
className={`menu-item bold-menu_item ${
editor.isActive("bold") ? "menu-item-active" : ""
}`}
>
加粗
</div>
</div>
);
}
export default BoldMenu;
二、bulletListMenu.tsx
import { Editor } from "@tiptap/core";
export type BulletListMenuMenuProps = {
editor: Editor;
};
function BulletListMenuMenu(props: BulletListMenuMenuProps) {
const { editor } = props;
const onbulletListMenu = () => {
editor.chain().focus().toggleBulletList().run();
};
return (
<div className="menu-item-container menu-item__bullet-list-menu">
<div
onClick={onbulletListMenu}
className={`menu-item bullet-list-menu_item ${
editor.isActive("bulletList") ? "menu-item-active" : ""
}`}
>
符号列表
</div>
</div>
);
}
export default BulletListMenuMenu;
三、codeMenu.tsx
import { Editor } from "@tiptap/core";
export type CodeMenuProps = {
editor: Editor;
};
function CodeMenu(props: CodeMenuProps) {
const { editor } = props;
const onCode = () => {
editor.chain().focus().toggleCode().run();
};
return (
<div className="menu-item-container menu-item__code-menu">
<div className={`menu-item code-menu_item`} onClick={onCode}>
代码块
</div>
</div>
);
}
export default CodeMenu;
四、doMenu.tsx
import { Editor } from "@tiptap/core";
export type UndoMenuProps = {
editor: Editor;
};
function DoMenu(props: UndoMenuProps) {
const { editor } = props;
const onUndo = () => {
if (!editor.can().undo()) {
return;
}
editor.chain().focus().undo().run();
};
const onRedo = () => {
if (!editor.can().redo()) {
return;
}
editor.chain().focus().redo().run();
};
return (
<div className="menu-item-container menu-item__do-menu">
<div className="do-menu__list">
<div
className={`menu-item do-menu__item ${
!editor.can().undo() ? "menu-item-disabled" : ""
}`}
onClick={onUndo}
>
撤销
</div>
<div
className={`menu-item do-menu__item ${
!editor.can().redo() ? "menu-item-disabled" : ""
}`}
onClick={onRedo}
>
重做
</div>
</div>
</div>
);
}
export default DoMenu;
五、fontColorMenu.tsx
import { Editor } from "@tiptap/react";
type FontColorMenuProps = {
editor: Editor;
};
function FontColorMenu(props: FontColorMenuProps) {
const { editor } = props;
const textStyle = editor.getAttributes("textStyle") || {};
const color = textStyle.color || "#222222";
const onInputChange = (event: any) => {
const value = event.target.value;
editor.chain().focus().setColor(value).run();
editor.commands.focus();
};
return (
<div className="menu-item-container menu-item__font-color-menu">
<label
className="menu-item font-color-menu__label"
htmlFor="font-color-menu__select-panel__uniqueId"
>
字体颜色
<input
hidden
type="color"
value={color}
onChange={onInputChange}
className="font-color-menu__select-panel"
id="font-color-menu__select-panel__uniqueId"
/>
</label>
</div>
);
}
export default FontColorMenu;
六、fontSizeMenu.tsx
import { Editor } from "@tiptap/react";
import { useEffect, useState } from "react";
type FontSizeMenuProps = {
editor: Editor;
};
const sizeList = [12, 14, 15, 16, 17, 18, 20, 24];
const normalizedSizeList = sizeList.map((size) => ({
id: `${size}`,
label: size + "px",
}));
function FontSizeMenu(props: FontSizeMenuProps) {
const { editor } = props;
const [showModal, setShowModal] = useState(false);
const [fontSize, setFontSize] = useState(normalizedSizeList[0].id);
const onSelect = (item: { id: string; label: string }) => {
const size = item.id;
setShowModal(false);
setFontSize(size);
editor.commands.setFontSize(size);
};
useEffect(() => {
editor.on("selectionUpdate", () => {
const fontSize = editor.getAttributes("textStyle").fontSize;
setFontSize(fontSize || normalizedSizeList[0].id);
});
}, []);
return (
<div className="menu-item-container menu-item__font-size-menu">
<div
className="menu-item font-size-menu__trigger"
onClick={() => setShowModal(true)}
>
{fontSize + "px"}
</div>
{showModal && (
<div className="font-size-menu__select-panel">
<div className="font-size-menu__font-size-list">
{normalizedSizeList.map((item) => (
<div
key={item.id}
onClick={() => onSelect(item)}
className={`font-size-menu__font-size-item ${
item.id === fontSize ? "active" : ""
}`}
>
<div style={{ fontSize: item.id + "px" }}>{item.label}</div>
</div>
))}
</div>
</div>
)}
</div>
);
}
export default FontSizeMenu;
七、highlightMenu.tsx
import { Editor } from "@tiptap/react";
type HighlightMenuProps = {
editor: Editor;
};
function HighlightMenu(props: HighlightMenuProps) {
const { editor } = props;
const textStyle = editor.getAttributes("textStyle") || {};
const highlightStyle = editor.getAttributes("highlight") || {};
const color = highlightStyle.color || textStyle.backgroundColor || "#FFFFFF";
const onInputChange = (event: any) => {
const value = event.target.value;
editor.chain().focus().setHighlight({ color: value }).run();
editor.commands.focus();
};
return (
<div className="menu-item-container menu-item__highlight-menu">
<label
className="menu-item highlight-menu__label"
htmlFor="highlight-menu__select-panel__uniqueId"
>
背景颜色
<input
hidden
type="color"
value={color}
onChange={onInputChange}
className="highlight-menu__select-panel"
id="highlight-menu__select-panel__uniqueId"
/>
</label>
</div>
);
}
export default HighlightMenu;
八、hMenu.tsx
import { Editor } from "@tiptap/core";
export type HProps = {
editor: Editor;
};
function HMenu(props: HProps) {
const { editor } = props;
const onClick = (level: any) => {
editor.chain().focus().toggleHeading({ level }).run();
};
return (
<div className="menu-item-container menu-item__h-menu">
<div className="h-menu__list">
<div
onClick={() => onClick(1)}
className={`menu-item h-menu__item ${
editor.isActive("heading", { level: 1 }) ? "menu-item-active" : ""
}`}
>
h1
</div>
<div
className={`menu-item h-menu__item ${
editor.isActive("heading", { level: 2 }) ? "menu-item-active" : ""
}`}
onClick={() => onClick(2)}
>
h2
</div>
</div>
</div>
);
}
export default HMenu;
九、iframeMenu.tsx
import { Editor } from "@tiptap/core";
type IframeMenuProps = {
editor: Editor;
};
function IframeMenu(props: IframeMenuProps) {
const { editor } = props;
const onClick = () => {
const url = window.prompt("Enter Iframe URL");
if (url) {
editor.commands.setIframe(url, { width: "100%" });
}
};
return (
<div className="menu-item-container menu-item__iframe-menu">
<div className={`menu-item iframe-menu_item`} onClick={onClick}>
Iframe
</div>
</div>
);
}
export default IframeMenu;
十、imgMenu.tsx
import { Editor } from "@tiptap/react";
type ImgMenuProps = {
editor: Editor;
};
function ImgMenu(props: ImgMenuProps) {
const { editor } = props;
const onInputChange = (e: any) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
const url = reader.result;
editor.commands.setImage({ src: url as string });
};
};
return (
<div className="menu-item-container menu-item__img-menu">
<label
className="menu-item img-menu__label"
htmlFor="img-menu__upload__input__uniqueId"
>
图片
<input
hidden
type="file"
accept="image/*"
onChange={onInputChange}
className="img-menu__select-panel"
id="img-menu__upload__input__uniqueId"
/>
</label>
</div>
);
}
export default ImgMenu;
十一、italicMenu.tsx
import { Editor } from "@tiptap/core";
export type ItalicMenuProps = {
editor: Editor;
};
function ItalicMenu(props: ItalicMenuProps) {
const { editor } = props;
const onItalic = () => {
editor.chain().focus().toggleItalic().run();
};
return (
<div className="menu-item-container menu-item__italic-menu">
<div
onClick={onItalic}
className={`menu-item italic-menu_item ${
editor.isActive("italic") ? "menu-item-active" : ""
}`}
>
斜体
</div>
</div>
);
}
export default ItalicMenu;
十二、linkMenu.tsx
import { Editor } from "@tiptap/react";
type LinkMenuProps = {
editor: Editor;
};
function LinkMenu(props: LinkMenuProps) {
const { editor } = props;
const onClick = () => {
const selection = editor.state.selection;
const text = editor.state.doc.textBetween(selection.from, selection.to);
if (text) {
const url = window.prompt("URL", "");
if (url === null) {
return;
}
if (url === "") {
editor.chain().focus().extendMarkRange("link").unsetLink().run();
return;
}
editor
.chain()
.focus()
.extendMarkRange("link")
.setLink({ href: url })
.run();
} else {
const text = window.prompt("文本", "");
const url = window.prompt("URL", "");
if (url === null || text === null) {
return;
}
editor
.chain()
.focus()
.addLink({
text,
href: url,
title: text,
})
.run();
}
};
return (
<div className="menu-item-container menu-item__link-menu">
<div className={`menu-item link-menu_item`} onClick={onClick}>
插入链接
</div>
</div>
);
}
export default LinkMenu;
十三、orderedListMenu.tsx
import { Editor } from "@tiptap/core";
export type OrderedListMenuProps = {
editor: Editor;
};
function OrderedListMenu(props: OrderedListMenuProps) {
const { editor } = props;
const onOrderedList = () => {
editor.chain().focus().toggleOrderedList().run();
};
return (
<div className="menu-item-container menu-item__ordered-list-menu">
<div
onClick={onOrderedList}
className={`menu-item ordered-list-menu_item ${
editor.isActive("orderedList") ? "menu-item-active" : ""
}`}
>
序号列表
</div>
</div>
);
}
export default OrderedListMenu;
十四、strikeMenu.tsx
import { Editor } from "@tiptap/core";
export type StrikeMenuProps = {
editor: Editor;
};
function StrikeMenu(props: StrikeMenuProps) {
const { editor } = props;
const onStrike = () => {
editor.chain().focus().toggleStrike().run();
};
return (
<div className="menu-item-container menu-item__strike-menu">
<div
onClick={onStrike}
className={`menu-item strike-menu_item ${
editor.isActive("strike") ? "menu-item-active" : ""
}`}
>
删除线
</div>
</div>
);
}
export default StrikeMenu;
十五、tableMenu.tsx
import { Editor } from "@tiptap/core";
type TableMenuProps = {
editor: Editor;
};
function TableMenu(props: TableMenuProps) {
const { editor } = props;
const onInsertTable = () => {
editor
.chain()
.focus()
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
.run();
};
const onAddColumnBefore = () => {
editor.chain().focus().addColumnBefore().run();
};
const onAddColumnAfter = () => {
editor.chain().focus().addColumnAfter().run();
};
const onRemoveColumn = () => {
editor.chain().focus().deleteColumn().run();
};
const onAddRowBefore = () => {
editor.chain().focus().addRowBefore().run();
};
const onAddRowAfter = () => {
editor.chain().focus().addRowAfter().run();
};
const onRemoveRow = () => {
editor.chain().focus().deleteRow().run();
};
const onRemoveTable = () => {
editor.chain().focus().deleteTable().run();
};
const onMergeCells = () => {
editor.chain().focus().mergeCells().run();
};
const onSplitCell = () => {
editor.chain().focus().splitCell().run();
};
return (
<div className="menu-item-container menu-item__table-menu">
<div className="table-menu__list">
<div className="menu-item table-menu__item" onClick={onInsertTable}>
插入表格
</div>
<div className="menu-item table-menu__item" onClick={onAddColumnBefore}>
之前插入列
</div>
<div className="menu-item table-menu__item" onClick={onAddColumnAfter}>
之后插入列
</div>
<div className="menu-item table-menu__item" onClick={onRemoveColumn}>
删除列
</div>
<div className="menu-item table-menu__item" onClick={onAddRowBefore}>
之前插入行
</div>
<div className="menu-item table-menu__item" onClick={onAddRowAfter}>
之后插入行
</div>
<div className="menu-item table-menu__item" onClick={onRemoveRow}>
删除行
</div>
<div className="menu-item table-menu__item" onClick={onRemoveTable}>
删除表格
</div>
<div className="menu-item table-menu__item" onClick={onMergeCells}>
合并单元格
</div>
<div className="menu-item table-menu__item" onClick={onSplitCell}>
分割单元格
</div>
</div>
</div>
);
}
export default TableMenu;
十六、textAlignMenu.tsx
import { Editor } from "@tiptap/react";
type TextAlignMenuProps = {
editor: Editor;
};
function TextAlignMenu(props: TextAlignMenuProps) {
const { editor } = props;
const onClick = (type: string) => {
editor.chain().focus().setTextAlign(type).run();
};
return (
<div className="menu-item-container menu-item__text-align-menu">
<div className="text-align-menu__list">
<div
onClick={() => onClick("left")}
className={`menu-item text-align-menu__item ${
editor.isActive({ textAlign: "left" }) ? "menu-item-active" : ""
}`}
>
左对齐
</div>
<div
onClick={() => onClick("center")}
className={`menu-item text-align-menu__item ${
editor.isActive({ textAlign: "center" }) ? "menu-item-active" : ""
}`}
>
居中
</div>
<div
onClick={() => onClick("right")}
className={`menu-item text-align-menu__item ${
editor.isActive({ textAlign: "right" }) ? "menu-item-active" : ""
}`}
>
右对齐
</div>
</div>
</div>
);
}
export default TextAlignMenu;
十七、underlineMenu.tsx
import { Editor } from "@tiptap/core";
export type UnderlineMenuProps = {
editor: Editor;
};
function UnderlineMenu(props: UnderlineMenuProps) {
const { editor } = props;
const onUnderline = () => {
editor.chain().focus().toggleUnderline().run();
};
return (
<div className="menu-item-container menu-item__underline-menu">
<div
onClick={onUnderline}
className={`menu-item underline-menu_item ${
editor.isActive("underline") ? "menu-item-active" : ""
}`}
>
下划线
</div>
</div>
);
}
export default UnderlineMenu;
十八、unsetAllMarksMenu.tsx
import { Editor } from "@tiptap/core";
export type UnsetAllMarksMenuProps = {
editor: Editor;
};
function UnsetAllMarksMenu(props: UnsetAllMarksMenuProps) {
const { editor } = props;
const onUnsetAllMarks = () => {
editor.chain().focus().unsetAllMarks().run();
};
return (
<div className="menu-item-container menu-item__unset-all-marks-menu">
<div
onClick={onUnsetAllMarks}
className={`menu-item unset-all-marks-menu_item`}
>
清除全部样式
</div>
</div>
);
}
export default UnsetAllMarksMenu;
十九、videoMenu.tsx
import { Editor } from "@tiptap/react";
type VideoMenuProps = {
editor: Editor;
};
function VideoMenu(props: VideoMenuProps) {
const { editor } = props;
const onInputChange = (e: any) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function (event: any) {
const videoBlob = new Blob([event.target.result], { type: file.type });
const blobUrl = URL.createObjectURL(videoBlob);
editor.commands.setVideo(blobUrl, { height: "auto", width: "100%" });
};
};
return (
<div className="menu-item-container menu-item__video-menu">
<label
className="menu-item img-menu__label"
htmlFor="video-menu__upload__input__uniqueId"
>
视频
<input
hidden
type="file"
accept="video/*"
onChange={onInputChange}
className="video-menu__select-panel"
id="video-menu__upload__input__uniqueId"
/>
</label>
</div>
);
}
export default VideoMenu;
二十、youtubeVideoMenu.tsx
import { Editor } from "@tiptap/core";
export type YoutubeVideoMenuProps = {
editor: Editor;
};
function YoutubeVideoMenu(props: YoutubeVideoMenuProps) {
const { editor } = props;
const onClick = () => {
const url = window.prompt("Enter YouTube URL");
if (url) {
editor.commands.setYoutubeVideo({
src: url,
width: 640,
height: 480,
});
}
};
return (
<div className="menu-item-container menu-item__youtube-video-menu">
<div className={`menu-item youtube-video-menu_item`} onClick={onClick}>
Youtube 视频
</div>
</div>
);
}
export default YoutubeVideoMenu;