perf: rich-text editor perf

This commit is contained in:
yuguangzhou
2024-07-01 23:28:01 +08:00
parent 30320e54ec
commit cb558a1999
8 changed files with 186 additions and 85 deletions
+1
View File
@@ -13,6 +13,7 @@
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.1.2",
"@tiptap/extension-color": "^2.4.0",
"@tiptap/extension-list-item": "^2.4.0",
"@tiptap/extension-text-align": "^2.4.0",
@@ -41,7 +41,7 @@ const BasicInfo = () => {
<Input id="email" className="w-[200px] flex-1" />
</div>
<div className="flex items-center flex-[48%]">
<Label htmlFor="wechat" className="w-[80px]">
<Label htmlFor="birthday" className="w-[80px]">
</Label>
<Popover open={isDateOpen}>
@@ -49,6 +49,7 @@ const BasicInfo = () => {
<div className="relative flex-1 w-[200px]">
<CalendarIcon className="left-[10px] top-[12px] absolute h-4 w-4" />
<Input
id="birthday"
className="flex-1 pl-[36px] "
value={date ? format(date, "PPP") : "Pick a date"}
onClick={() => setIsDateOpen(true)}
+2 -3
View File
@@ -29,15 +29,14 @@ const ColorBar = ({ setCurrentColor }: IProps) => {
return (
<div
key={index}
className="shrink-0"
className="shrink-0 cursor-pointer hover:scale-[1.3] transition-all"
onClick={setCurrentColor(color)}
style={{
width: "20px",
height: "20px",
backgroundColor: color,
margin: "5px",
border: "1px solid #000000",
borderRadius: "5px"
borderRadius: "50%"
}}
></div>
);
@@ -0,0 +1,24 @@
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger
} from "@/components/ui/tooltip";
interface IProps {
children: React.ReactNode;
title: string;
}
function CustomTooltip({ children, title }: IProps) {
return (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipContent>{title}</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
export default CustomTooltip;
+68 -81
View File
@@ -19,6 +19,7 @@ import {
List,
ListOrdered,
MessageSquareQuote,
Palette,
Pilcrow,
Redo,
Strikethrough,
@@ -30,16 +31,18 @@ import ListItem from "@tiptap/extension-list-item";
import TextStyle from "@tiptap/extension-text-style";
import { EditorProvider, useCurrentEditor } from "@tiptap/react";
import TextAlign from "@tiptap/extension-text-align";
import StarterKit from "@tiptap/starter-kit";
import {
Popover,
PopoverContent,
PopoverTrigger
} from "@/components/ui/popover";
import "../styles/tiptap.scss";
import ColorBar from "./ColorBar";
import CustomTooltip from "./CustomTooltip";
import "../styles/tiptap.scss";
const MenuBar = () => {
const { editor } = useCurrentEditor();
@@ -53,58 +56,33 @@ const MenuBar = () => {
return (
<div className="control-group">
<div className="button-group">
{/* <button
onClick={() => editor.chain().focus().toggleBold().run()}
disabled={!editor.can().chain().focus().toggleBold().run()}
className={editor.isActive("bold") ? "is-active" : ""}
>
Bold
</button> */}
<Bold
onClick={() => editor.chain().focus().toggleBold().run()}
className={editor.isActive("bold") ? "is-active" : ""}
></Bold>
{/* <button
onClick={() => editor.chain().focus().toggleItalic().run()}
disabled={!editor.can().chain().focus().toggleItalic().run()}
className={editor.isActive("italic") ? "is-active" : ""}
>
Italic
</button> */}
<Italic
onClick={() => editor.chain().focus().toggleItalic().run()}
className={editor.isActive("italic") ? "is-active" : ""}
></Italic>
{/* <button
onClick={() => editor.chain().focus().toggleStrike().run()}
disabled={!editor.can().chain().focus().toggleStrike().run()}
className={editor.isActive("strike") ? "is-active" : ""}
>
Strike
</button> */}
<Strikethrough
onClick={() => editor.chain().focus().toggleStrike().run()}
className={editor.isActive("strike") ? "is-active" : ""}
></Strikethrough>
{/* <button
onClick={() => editor.chain().focus().toggleCode().run()}
disabled={!editor.can().chain().focus().toggleCode().run()}
className={editor.isActive("code") ? "is-active" : ""}
>
Code
</button> */}
<CodeXml
onClick={() => editor.chain().focus().toggleCode().run()}
className={editor.isActive("code") ? "is-active" : ""}
></CodeXml>
<Eraser
onClick={() => editor.chain().focus().clearNodes().run()}
></Eraser>
{/*
<Pilcrow
onClick={() => editor.chain().focus().setParagraph().run()}
className={editor.isActive("paragraph") ? "is-active" : ""}
></Pilcrow> */}
<CustomTooltip title="加粗">
<Bold
onClick={() => editor.chain().focus().toggleBold().run()}
className={editor.isActive("bold") ? "is-active" : ""}
></Bold>
</CustomTooltip>
<CustomTooltip title="斜体">
<Italic
onClick={() => editor.chain().focus().toggleItalic().run()}
className={editor.isActive("italic") ? "is-active" : ""}
></Italic>
</CustomTooltip>
<CustomTooltip title="中划线">
<Strikethrough
onClick={() => editor.chain().focus().toggleStrike().run()}
className={editor.isActive("strike") ? "is-active" : ""}
></Strikethrough>
</CustomTooltip>
<CustomTooltip title="行内代码">
<CodeXml
onClick={() => editor.chain().focus().toggleCode().run()}
className={editor.isActive("code") ? "is-active" : ""}
></CodeXml>
</CustomTooltip>
<Heading1
onClick={() =>
@@ -165,11 +143,12 @@ const MenuBar = () => {
onClick={() => editor.chain().focus().toggleOrderedList().run()}
className={editor.isActive("orderedList") ? "is-active" : ""}
></ListOrdered>
<FileCode2
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
className={editor.isActive("codeBlock") ? "is-active" : ""}
></FileCode2>
<CustomTooltip title="代码块">
<FileCode2
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
className={editor.isActive("codeBlock") ? "is-active" : ""}
></FileCode2>
</CustomTooltip>
<MessageSquareQuote
onClick={() => editor.chain().focus().toggleBlockquote().run()}
@@ -194,31 +173,39 @@ const MenuBar = () => {
Redo
</button> */}
<Redo onClick={() => editor.chain().focus().undo().run()}></Redo>
<AlignLeft
onClick={() => editor.chain().focus().setTextAlign("left").run()}
className={editor.isActive({ textAlign: "left" }) ? "is-active" : ""}
></AlignLeft>
<AlignCenter
onClick={() => editor.chain().focus().setTextAlign("center").run()}
className={
editor.isActive({ textAlign: "center" }) ? "is-active" : ""
}
></AlignCenter>
<AlignRight
onClick={() => editor.chain().focus().setTextAlign("right").run()}
className={editor.isActive({ textAlign: "right" }) ? "is-active" : ""}
></AlignRight>
<CustomTooltip title="居左">
<AlignLeft
onClick={() => editor.chain().focus().setTextAlign("left").run()}
className={
editor.isActive({ textAlign: "left" }) ? "is-active" : ""
}
></AlignLeft>
</CustomTooltip>
<CustomTooltip title="居中">
<AlignCenter
onClick={() => editor.chain().focus().setTextAlign("center").run()}
className={
editor.isActive({ textAlign: "center" }) ? "is-active" : ""
}
></AlignCenter>
</CustomTooltip>
<CustomTooltip title="居右">
<AlignRight
onClick={() => editor.chain().focus().setTextAlign("right").run()}
className={
editor.isActive({ textAlign: "right" }) ? "is-active" : ""
}
></AlignRight>
</CustomTooltip>
<Popover>
<PopoverTrigger>
<Baseline></Baseline>
<CustomTooltip title="文本颜色">
<Palette></Palette>
</CustomTooltip>
</PopoverTrigger>
<PopoverContent className="w-80">
{/* <div onClick={() => editor.chain().focus().setColor("blue").run()}>
蓝色
</div>
<div onClick={() => editor.chain().focus().setColor("red").run()}>
红色
</div> */}
<PopoverContent className="w-80 bg-[#21242a]">
<ColorBar setCurrentColor={setCurrentColor}></ColorBar>
</PopoverContent>
</Popover>
@@ -0,0 +1,30 @@
"use client"
import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import { cn } from "@/lib/utils"
const TooltipProvider = TooltipPrimitive.Provider
const Tooltip = TooltipPrimitive.Root
const TooltipTrigger = TooltipPrimitive.Trigger
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
))
TooltipContent.displayName = TooltipPrimitive.Content.displayName
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
+5
View File
@@ -114,6 +114,7 @@
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
padding: 4px;
.lucide {
padding: 4px 6px;
@@ -125,6 +126,10 @@
font-weight: 700;
color: #2ec4b6;
}
&:hover {
border-radius: 4px;
background-color: #e26d5c;
}
}
}
}
+54
View File
@@ -104,6 +104,9 @@ importers:
'@radix-ui/react-tabs':
specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-tooltip':
specifier: ^1.1.2
version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1)
'@tiptap/extension-color':
specifier: ^2.4.0
version: 2.4.0(@tiptap/core@2.4.0)(@tiptap/extension-text-style@2.4.0)
@@ -1750,6 +1753,37 @@ packages:
react-dom: 18.3.1(react@18.3.1)
dev: false
/@radix-ui/react-tooltip@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-9XRsLwe6Yb9B/tlnYCPVUd/TFS4J7HuOZW345DCeC6vKIxQGMZdx21RK4VoZauPD5frgkXTYVS5y90L+3YBn4w==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@radix-ui/primitive': 1.1.0
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.2)(react@18.3.1)
'@radix-ui/react-context': 1.1.0(@types/react@18.3.2)(react@18.3.1)
'@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-id': 1.1.0(@types/react@18.3.2)(react@18.3.1)
'@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1)
'@radix-ui/react-slot': 1.1.0(@types/react@18.3.2)(react@18.3.1)
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.2)(react@18.3.1)
'@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1)
'@types/react': 18.3.2
'@types/react-dom': 18.3.0
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
dev: false
/@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.2)(react@18.3.1):
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
peerDependencies:
@@ -1875,6 +1909,26 @@ packages:
react: 18.3.1
dev: false
/@radix-ui/react-visually-hidden@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1):
resolution: {integrity: sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1)(react@18.3.1)
'@types/react': 18.3.2
'@types/react-dom': 18.3.0
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
dev: false
/@radix-ui/rect@1.1.0:
resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
dev: false