mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: add info callout custom extension
This commit is contained in:
parent
e6ebd195d7
commit
fe8fdd1769
10 changed files with 163 additions and 59 deletions
32
front/components/Canva/Canva.tsx
Normal file
32
front/components/Canva/Canva.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import React from "react";
|
||||
import { useEditor, EditorContent } from "@tiptap/react";
|
||||
import StarterKit from "@tiptap/starter-kit";
|
||||
// Custom Extensions
|
||||
import InfoCallout from "../Editor/Extensions/Callout/Info/InfoCallout";
|
||||
|
||||
interface Editor {
|
||||
content: string;
|
||||
element: any;
|
||||
//course: any;
|
||||
}
|
||||
|
||||
function Canva(props: Editor) {
|
||||
const isEditable = false;
|
||||
const editor: any = useEditor({
|
||||
editable: isEditable,
|
||||
extensions: [
|
||||
StarterKit,
|
||||
|
||||
// Custom Extensions
|
||||
InfoCallout.configure({
|
||||
editable: isEditable,
|
||||
}),
|
||||
],
|
||||
|
||||
content: props.content,
|
||||
});
|
||||
|
||||
return <EditorContent editor={editor} />;
|
||||
}
|
||||
|
||||
export default Canva;
|
||||
|
|
@ -10,8 +10,10 @@ import { motion, AnimatePresence } from "framer-motion";
|
|||
import Image from "next/image";
|
||||
import styled from "styled-components";
|
||||
import { getBackendUrl } from "../../services/config";
|
||||
import { GlobeIcon, PaperPlaneIcon, SlashIcon } from "@radix-ui/react-icons";
|
||||
import { SlashIcon } from "@radix-ui/react-icons";
|
||||
import Avvvatars from "avvvatars-react";
|
||||
// extensions
|
||||
import InfoCallout from "./Extensions/Callout/Info/InfoCallout";
|
||||
|
||||
interface Editor {
|
||||
content: string;
|
||||
|
|
@ -26,23 +28,27 @@ function Editor(props: Editor) {
|
|||
const auth: any = React.useContext(AuthContext);
|
||||
|
||||
const editor: any = useEditor({
|
||||
editable: true,
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
// The Collaboration extension comes with its own history handling
|
||||
history: false,
|
||||
// history: false,
|
||||
}),
|
||||
InfoCallout.configure({
|
||||
editable: true,
|
||||
}),
|
||||
// Register the document with Tiptap
|
||||
Collaboration.configure({
|
||||
document: props.ydoc,
|
||||
}),
|
||||
// Collaboration.configure({
|
||||
// document: props.ydoc,
|
||||
// }),
|
||||
// Register the collaboration cursor extension
|
||||
CollaborationCursor.configure({
|
||||
provider: props.provider,
|
||||
user: {
|
||||
name: auth.userInfo.username,
|
||||
color: "#f783ac",
|
||||
},
|
||||
}),
|
||||
// CollaborationCursor.configure({
|
||||
// provider: props.provider,
|
||||
// user: {
|
||||
// name: auth.userInfo.username,
|
||||
// color: "#f783ac",
|
||||
// },
|
||||
// }),
|
||||
],
|
||||
|
||||
content: props.content,
|
||||
|
|
@ -65,15 +71,13 @@ function Editor(props: Editor) {
|
|||
<EditorTop>
|
||||
<EditorDocSection>
|
||||
<EditorInfoWrapper>
|
||||
<EditorInfoLearnHouseLogo width={23} height={23} src={learnhouseIcon} alt="" />
|
||||
<EditorInfoLearnHouseLogo width={25} height={25} src={learnhouseIcon} alt="" />
|
||||
<EditorInfoThumbnail src={`${getBackendUrl()}content/uploads/img/${props.course.course.thumbnail}`} alt=""></EditorInfoThumbnail>
|
||||
<EditorInfoDocName>
|
||||
{" "}
|
||||
<b>{props.course.course.name}</b> <SlashIcon /> {props.element.name}{" "}
|
||||
</EditorInfoDocName>
|
||||
<EditorSaveButton onClick={() => props.setContent(editor.getJSON())}>
|
||||
Save
|
||||
</EditorSaveButton>
|
||||
<EditorSaveButton onClick={() => props.setContent(editor.getJSON())}>Save</EditorSaveButton>
|
||||
</EditorInfoWrapper>
|
||||
<EditorButtonsWrapper>
|
||||
<ToolbarButtons editor={editor} />
|
||||
|
|
@ -90,7 +94,6 @@ function Editor(props: Editor) {
|
|||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.99 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
key="modal"
|
||||
transition={{
|
||||
type: "spring",
|
||||
stiffness: 360,
|
||||
|
|
@ -106,6 +109,7 @@ function Editor(props: Editor) {
|
|||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
const Page = styled.div`
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
|
|
@ -113,13 +117,13 @@ const Page = styled.div`
|
|||
min-width: 100vw;
|
||||
padding-top: 30px;
|
||||
|
||||
// dots background
|
||||
// dots background
|
||||
background-image: radial-gradient(#4744446b 1px, transparent 1px), radial-gradient(#4744446b 1px, transparent 1px);
|
||||
background-position: 0 0, 25px 25px;
|
||||
background-size: 50px 50px;
|
||||
background-attachment: fixed;
|
||||
background-repeat: repeat;
|
||||
`
|
||||
`;
|
||||
|
||||
const EditorTop = styled.div`
|
||||
background-color: #ffffffb8;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
import { mergeAttributes, Node } from "@tiptap/core";
|
||||
import { ReactNodeViewRenderer } from "@tiptap/react";
|
||||
|
||||
import InfoCalloutComponent from "./InfoCalloutComponent";
|
||||
|
||||
export default Node.create({
|
||||
name: "calloutInfo",
|
||||
group: "block",
|
||||
draggable: true,
|
||||
content: "inline*",
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: "callout-info",
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ["callout-info", mergeAttributes(HTMLAttributes), 0];
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(InfoCalloutComponent);
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { NodeViewContent, NodeViewWrapper } from "@tiptap/react";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
function InfoCalloutComponent(props: any) {
|
||||
return (
|
||||
<NodeViewWrapper>
|
||||
<InfoCalloutWrapper contentEditable={props.extension.options.editable}>
|
||||
<div>⚠️ </div> <NodeViewContent contentEditable={props.extension.options.editable} className="content" />
|
||||
</InfoCalloutWrapper>
|
||||
</NodeViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
const InfoCalloutWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background: #fefce8;
|
||||
color: #713f11;
|
||||
border: 1px solid #fff103;
|
||||
border-radius: 16px;
|
||||
margin: 1rem 0;
|
||||
align-items: center;
|
||||
padding-left: 15px;
|
||||
|
||||
|
||||
.content {
|
||||
margin: 5px;
|
||||
padding: 0.5rem;
|
||||
border: ${(props) => (props.contentEditable ? "2px dashed #713f1117" : "none")};
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const DragHandle = styled.div`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1rem;
|
||||
height: 100%;
|
||||
cursor: move;
|
||||
z-index: 1;
|
||||
`;
|
||||
|
||||
export default InfoCalloutComponent;
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import styled from "styled-components";
|
||||
import { FontBoldIcon, FontItalicIcon, StrikethroughIcon, ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons";
|
||||
import { FontBoldIcon, FontItalicIcon, StrikethroughIcon, ArrowLeftIcon, ArrowRightIcon, OpacityIcon } from "@radix-ui/react-icons";
|
||||
import { AlertTriangle, Info } from "lucide-react";
|
||||
|
||||
export const ToolbarButtons = ({ editor }: any) => {
|
||||
if (!editor) {
|
||||
|
|
@ -31,6 +32,10 @@ export const ToolbarButtons = ({ editor }: any) => {
|
|||
<option value="5">Heading 5</option>
|
||||
<option value="6">Heading 6</option>
|
||||
</ToolSelect>
|
||||
{/* TODO: fix this : toggling only works one-way */}
|
||||
<ToolBtn onClick={() => editor.chain().focus().toggleNode('calloutInfo').run()} >
|
||||
<AlertTriangle size={15} />
|
||||
</ToolBtn>
|
||||
</ToolButtonsWrapper>
|
||||
);
|
||||
};
|
||||
|
|
@ -49,10 +54,13 @@ const ToolBtn = styled.div`
|
|||
width: 25px;
|
||||
height: 25px;
|
||||
padding: 5px;
|
||||
font-size: 5px;
|
||||
margin-right: 5px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
svg{
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background: rgba(176, 176, 176, 0.5);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue