Merge pull request #4 from JOYCEQL/feat/workbench

Feat/workbench
This commit is contained in:
qingchen
2024-07-15 23:49:39 +08:00
committed by GitHub
25 changed files with 2342 additions and 11 deletions
+9 -1
View File
@@ -1,7 +1,15 @@
# 魔方简历
## 前端
Next技术栈
### 添加shadcn/ui 组件
```bash
pnpm dlx shadcn-ui@latest add tabs
```
## 后端
Nest技术栈
Nest技术栈
+15
View File
@@ -9,13 +9,27 @@
"lint": "next lint"
},
"dependencies": {
"@radix-ui/react-label": "^2.0.2",
"@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",
"@tiptap/extension-text-style": "^2.4.0",
"@tiptap/pm": "^2.4.0",
"@tiptap/react": "^2.4.0",
"@tiptap/starter-kit": "^2.4.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"lucide-react": "^0.379.0",
"next": "14.2.3",
"react": "^18",
"react-day-picker": "^8.10.1",
"react-dom": "^18",
"react-resizable-panels": "^2.0.20",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7"
},
@@ -26,6 +40,7 @@
"eslint": "^8",
"eslint-config-next": "14.2.3",
"postcss": "^8",
"sass": "^1.77.4",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
-4
View File
@@ -5,10 +5,6 @@ import { CircleArrowRight } from "lucide-react";
import EditButton from "@/components/EditButton";
import IconLogo from "@/assets/images/logo@2x.svg";
export default function Home() {
const goWorkbench = () => {
redirect("/workbench/index");
};
return (
<main className="flex min-h-screen flex-col items-center p-[24px]">
<header className="flex justify-between w-[100%]">
@@ -0,0 +1,80 @@
import { useState } from "react";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Calendar } from "@/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
import { CalendarIcon } from "lucide-react";
import { format } from "date-fns";
const BasicInfo = () => {
const [date, setDate] = useState<Date>();
const [isDateOpen, setIsDateOpen] = useState<boolean>(false);
return (
<div className="flex items-center flex-wrap gap-[16px]">
<div className="flex items-center flex-[48%] ">
<Label htmlFor="name" className="w-[80px]">
</Label>
<Input id="name" className="w-[200px] flex-1" />
</div>
<div className="flex items-center flex-[48%]">
<Label htmlFor="phone" className="w-[80px]">
</Label>
<Input id="phone" className="w-[200px] flex-1" />
</div>
<div className="flex items-center flex-[48%]">
<Label htmlFor="wechat" className="w-[80px]">
</Label>
<Input id="wechat" className="w-[200px] flex-1" />
</div>
<div className="flex items-center flex-[48%]">
<Label htmlFor="email" className="w-[80px]">
</Label>
<Input id="email" className="w-[200px] flex-1" />
</div>
<div className="flex items-center flex-[48%]">
<Label htmlFor="birthday" className="w-[80px]">
</Label>
<Popover open={isDateOpen}>
<PopoverTrigger asChild>
<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)}
></Input>
</div>
</PopoverTrigger>
<PopoverContent
className="w-auto p-0"
onFocusOutside={() => setIsDateOpen(false)}
onPointerDownOutside={() => setIsDateOpen(false)}
>
<Calendar
mode="single"
selected={date}
onSelect={(val) => {
setIsDateOpen(false);
setDate(val);
}}
initialFocus
/>
</PopoverContent>
</Popover>
</div>
</div>
);
};
export default BasicInfo;
@@ -0,0 +1,5 @@
const Cert = () => {
return <div></div>;
};
export default Cert;
@@ -0,0 +1,91 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useState } from "react";
import BasicInfo from "../BasicInfo";
import Skills from "../Skills";
import Project from "../Project";
import Empolyment from "../Empolyment";
import Education from "../Education";
import Cert from "../Cert";
import {
UserRound,
PencilRuler,
FileJson,
Network,
GraduationCap,
BookOpen
} from "lucide-react";
const Editor = () => {
const [activeTab, setActiveTab] = useState("basic");
const tabList = [
{
value: "basic",
label: "基本信息",
icon: <UserRound size={16} />
},
{
value: "skills",
label: "专业技能",
icon: <PencilRuler size={16} />
},
{
value: "project",
label: "项目经历",
icon: <FileJson size={16} />
},
{
value: "empolyment",
label: "工作经历",
icon: <Network size={16} />
},
{
value: "education",
label: "教育经历",
icon: <GraduationCap size={16} />
},
{
value: "cert",
label: "技能证书",
icon: <BookOpen size={16} />
}
];
return (
<div className="flex flex-1 p-[12px] h-[100vh]">
<div className="w-[72px] shrink-0 mr-[10px] bg-[#ecedee]">
{tabList.map((item, index) => {
return (
<div
key={index}
className="flex flex-col items-center text-[12px] p-[12px] cursor-pointer hover:bg-[#e3e3e5] hover:rounded-[4px]"
>
<div>{item.icon}</div>
<div>{item.label}</div>
</div>
);
})}
</div>
<div
className="bg-[#fff] p-[12px] rounded-[6px] overflow-auto "
style={{ scrollbarWidth: "none" }}
>
<div className="text-[24px] mb-[10px]">-xx-x年</div>
<div className="mt-[12px]">
<div className="text-[20px] mb-[12px]"></div>
<BasicInfo></BasicInfo>
<div className="text-[20px] mb-[12px]"></div>
<Skills></Skills>
<div className="text-[20px] mb-[12px]"></div>
<Project></Project>
<div className="text-[20px] mb-[12px]"></div>
<Empolyment></Empolyment>
<div className="text-[20px] mb-[12px]"></div>
<Education></Education>
<div className="text-[20px]"></div>
<Cert></Cert>
</div>
</div>
</div>
);
};
export default Editor;
@@ -0,0 +1,5 @@
const Education = () => {
return <div></div>;
};
export default Education;
@@ -0,0 +1,5 @@
const Empolyment = () => {
return <div></div>;
};
export default Empolyment;
@@ -0,0 +1,10 @@
const Preview = () => {
return (
<div className="flex-1 p-[12px]">
{/* 简历编辑表单 */}
</div>
);
};
export default Preview;
@@ -0,0 +1,11 @@
import Tiptap from "@/components/Tiptap";
const Project = () => {
return (
<div>
<Tiptap></Tiptap>
</div>
);
};
export default Project;
@@ -0,0 +1,10 @@
import Tiptap from "@/components/Tiptap";
const Skills = () => {
return (
<div>
<Tiptap></Tiptap>
</div>
);
};
export default Skills;
@@ -0,0 +1,5 @@
.container {
display: flex;
height: 100vh;
background: #ecedee;
}
+24 -1
View File
@@ -1,6 +1,29 @@
"use client";
import Editor from "./compoents/Editor";
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup
} from "@/components/ui/resizable";
import Preview from "./compoents/Preview";
import styles from "./index.module.scss";
const WorkBench = () => {
return <div>WorkBench</div>;
return (
<div className={styles.container}>
<ResizablePanelGroup
direction="horizontal"
className="h-[100vh] rounded-lg border"
>
<ResizablePanel>
<Editor></Editor>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel>
<Preview></Preview>
</ResizablePanel>
</ResizablePanelGroup>
</div>
);
};
export default WorkBench;
+49
View File
@@ -0,0 +1,49 @@
const colorList = [
// 富文本文字顏色值,字符串格式
"#000000",
"#ffffff",
"#ff0000",
"#00ff00",
"#0000ff",
"#ffff00",
"#00ffff",
"#ff00ff",
"#c0c0c0",
"#808080",
"#800000",
"#008000",
"#000080",
"#808000",
"#008080"
];
interface IProps {
setCurrentColor: (color: string) => () => void;
}
const ColorBar = ({ setCurrentColor }: IProps) => {
return (
<div>
<div className="flex flex-wrap">
{colorList.map((color, index) => {
return (
<div
key={index}
className="shrink-0 cursor-pointer hover:scale-[1.3] transition-all"
onClick={setCurrentColor(color)}
style={{
width: "20px",
height: "20px",
backgroundColor: color,
margin: "5px",
borderRadius: "50%"
}}
></div>
);
})}
</div>
</div>
);
};
export default ColorBar;
@@ -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;
+247
View File
@@ -0,0 +1,247 @@
import React from "react";
import {
AlignCenter,
AlignLeft,
AlignRight,
Baseline,
Bold,
CodeXml,
CornerDownLeft,
Eraser,
FileCode2,
Heading1,
Heading2,
Heading3,
Heading4,
Heading5,
Heading6,
Italic,
List,
ListOrdered,
MessageSquareQuote,
Palette,
Pilcrow,
Redo,
Strikethrough,
Undo
} from "lucide-react";
import { Color } from "@tiptap/extension-color";
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 ColorBar from "./ColorBar";
import CustomTooltip from "./CustomTooltip";
import "../styles/tiptap.scss";
const MenuBar = () => {
const { editor } = useCurrentEditor();
const setCurrentColor = (color: string) => () => {
editor?.chain().focus().setColor(color).run();
};
if (!editor) {
return null;
}
return (
<div className="control-group">
<div className="button-group">
<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={() =>
editor.chain().focus().toggleHeading({ level: 1 }).run()
}
className={
editor.isActive("heading", { level: 1 }) ? "is-active" : ""
}
></Heading1>
<Heading2
onClick={() =>
editor.chain().focus().toggleHeading({ level: 2 }).run()
}
className={
editor.isActive("heading", { level: 2 }) ? "is-active" : ""
}
></Heading2>
<Heading3
onClick={() =>
editor.chain().focus().toggleHeading({ level: 3 }).run()
}
className={
editor.isActive("heading", { level: 3 }) ? "is-active" : ""
}
></Heading3>
<Heading4
onClick={() =>
editor.chain().focus().toggleHeading({ level: 4 }).run()
}
className={
editor.isActive("heading", { level: 4 }) ? "is-active" : ""
}
></Heading4>
<Heading5
onClick={() =>
editor.chain().focus().toggleHeading({ level: 5 }).run()
}
className={
editor.isActive("heading", { level: 6 }) ? "is-active" : ""
}
></Heading5>
<Heading6
onClick={() =>
editor.chain().focus().toggleHeading({ level: 5 }).run()
}
className={
editor.isActive("heading", { level: 6 }) ? "is-active" : ""
}
></Heading6>
<List
onClick={() => editor.chain().focus().toggleBulletList().run()}
className={editor.isActive("bulletList") ? "is-active" : ""}
></List>
<ListOrdered
onClick={() => editor.chain().focus().toggleOrderedList().run()}
className={editor.isActive("orderedList") ? "is-active" : ""}
></ListOrdered>
<CustomTooltip title="代码块">
<FileCode2
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
className={editor.isActive("codeBlock") ? "is-active" : ""}
></FileCode2>
</CustomTooltip>
<MessageSquareQuote
onClick={() => editor.chain().focus().toggleBlockquote().run()}
className={editor.isActive("blockquote") ? "is-active" : ""}
></MessageSquareQuote>
<CornerDownLeft
onClick={() => editor.chain().focus().setHardBreak().run()}
></CornerDownLeft>
{/* <button
onClick={() => editor.chain().focus().undo().run()}
disabled={!editor.can().chain().focus().undo().run()}
>
Undo
</button> */}
<Undo onClick={() => editor.chain().focus().undo().run()}></Undo>
{/* <button
onClick={() => editor.chain().focus().redo().run()}
disabled={!editor.can().chain().focus().redo().run()}
>
Redo
</button> */}
<Redo onClick={() => editor.chain().focus().undo().run()}></Redo>
<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>
<CustomTooltip title="文本颜色">
<Palette></Palette>
</CustomTooltip>
</PopoverTrigger>
<PopoverContent className="w-80 bg-[#21242a]">
<ColorBar setCurrentColor={setCurrentColor}></ColorBar>
</PopoverContent>
</Popover>
</div>
</div>
);
};
const extensions = [
Color.configure({ types: [TextStyle.name, ListItem.name] }),
TextStyle,
TextAlign.configure({
types: ["heading", "paragraph"]
}),
StarterKit.configure({
bulletList: {
keepMarks: true,
keepAttributes: false // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
},
orderedList: {
keepMarks: true,
keepAttributes: false // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
}
})
];
const content = ``;
const Tiptap = () => {
return (
<EditorProvider
slotBefore={<MenuBar />}
extensions={extensions}
content={content}
></EditorProvider>
);
};
export default Tiptap;
@@ -0,0 +1,66 @@
"use client"
import * as React from "react"
import { ChevronLeft, ChevronRight } from "lucide-react"
import { DayPicker } from "react-day-picker"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
export type CalendarProps = React.ComponentProps<typeof DayPicker>
function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4",
caption: "flex justify-center pt-1 relative items-center",
caption_label: "text-sm font-medium",
nav: "space-x-1 flex items-center",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-y-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
day: cn(
buttonVariants({ variant: "ghost" }),
"h-9 w-9 p-0 font-normal aria-selected:opacity-100"
),
day_range_end: "day-range-end",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
IconLeft: ({ ...props }) => <ChevronLeft className="h-4 w-4" />,
IconRight: ({ ...props }) => <ChevronRight className="h-4 w-4" />,
}}
{...props}
/>
)
}
Calendar.displayName = "Calendar"
export { Calendar }
+25
View File
@@ -0,0 +1,25 @@
import * as React from "react"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }
+26
View File
@@ -0,0 +1,26 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }
@@ -0,0 +1,31 @@
"use client"
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
const Popover = PopoverPrimitive.Root
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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}
/>
</PopoverPrimitive.Portal>
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent }
@@ -0,0 +1,45 @@
"use client"
import { GripVertical } from "lucide-react"
import * as ResizablePrimitive from "react-resizable-panels"
import { cn } from "@/lib/utils"
const ResizablePanelGroup = ({
className,
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
<ResizablePrimitive.PanelGroup
className={cn(
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
className
)}
{...props}
/>
)
const ResizablePanel = ResizablePrimitive.Panel
const ResizableHandle = ({
withHandle,
className,
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
withHandle?: boolean
}) => (
<ResizablePrimitive.PanelResizeHandle
className={cn(
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
className
)}
{...props}
>
{withHandle && (
<div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
<GripVertical className="h-2.5 w-2.5" />
</div>
)}
</ResizablePrimitive.PanelResizeHandle>
)
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
+55
View File
@@ -0,0 +1,55 @@
"use client"
import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"
import { cn } from "@/lib/utils"
const Tabs = TabsPrimitive.Root
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className
)}
{...props}
/>
))
TabsList.displayName = TabsPrimitive.List.displayName
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className
)}
{...props}
/>
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
))
TabsContent.displayName = TabsPrimitive.Content.displayName
export { Tabs, TabsList, TabsTrigger, TabsContent }
@@ -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 }
+138
View File
@@ -0,0 +1,138 @@
/* Basic editor styles */
.tiptap {
min-height: 300px;
border: 1px solid #d1d5db;
padding: 2px;
border-radius: 0 0 4px 4px;
:first-child {
margin-top: 0;
}
/* List styles */
ul,
ol {
list-style-type: disc;
padding: 0 1rem;
margin: 1.25rem 1rem 1.25rem 0.4rem;
li p {
margin-top: 0.25em;
margin-bottom: 0.25em;
}
}
ol {
list-style-type: decimal;
padding: 0 1rem;
margin: 1.25rem 1rem 1.25rem 0.4rem;
li p {
margin-top: 0.25em;
margin-bottom: 0.25em;
}
}
/* Heading styles */
h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.1;
margin-top: 2.5rem;
text-wrap: pretty;
}
h1,
h2 {
margin-top: 3.5rem;
margin-bottom: 1.5rem;
}
h1 {
font-size: 1.4rem;
}
h2 {
font-size: 1.2rem;
}
h3 {
font-size: 1.1rem;
}
h4,
h5,
h6 {
font-size: 1rem;
}
/* Code and preformatted text styles */
code {
background-color: rgba(27, 31, 35, 0.05);
border-radius: 0.4rem;
color: black;
font-size: 0.85rem;
padding: 0.25em 0.3em;
}
pre {
background: black;
border-radius: 0.5rem;
color: white;
font-family: "JetBrainsMono", monospace;
margin: 1.5rem 0;
padding: 0.75rem 1rem;
code {
background: none;
color: inherit;
font-size: 0.8rem;
padding: 0;
}
}
blockquote {
border-left: 3px solid gray;
margin: 1.5rem 0;
padding-left: 1rem;
}
hr {
border: none;
border-top: 1px solid gray;
margin: 2rem 0;
}
}
.control-group {
border: 1px solid #d1d5db;
border-bottom: none;
background-color: #403d39;
border-radius: 4px 4px 0 0;
overflow: hidden;
.button-group {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
padding: 4px;
.lucide {
padding: 4px 6px;
cursor: pointer;
color: #fff;
width: 28px;
height: 28px;
&.is-active {
font-weight: 700;
color: #2ec4b6;
}
&:hover {
border-radius: 4px;
background-color: #e26d5c;
}
}
}
}
[contenteditable]:focus {
outline: none;
}
+1336 -5
View File
File diff suppressed because it is too large Load Diff