feat: redesign some editor blocks

This commit is contained in:
swve 2023-08-14 14:10:49 +02:00
parent 3cca78d6e1
commit 7a6d86e598
11 changed files with 131 additions and 183 deletions

View file

@ -2,16 +2,13 @@
import React from "react";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Collaboration from "@tiptap/extension-collaboration";
import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
import { AuthContext } from "../../Security/AuthProvider";
import learnhouseIcon from "public/learnhouse_icon.png";
import { ToolbarButtons } from "./Toolbar/ToolbarButtons";
import { motion, AnimatePresence } from "framer-motion";
import { motion } from "framer-motion";
import Image from "next/image";
import styled from "styled-components";
import { getBackendUrl, getUriWithOrg } from "@services/config/config";
import { DividerVerticalIcon, EyeOpenIcon, SlashIcon } from "@radix-ui/react-icons";
import { DividerVerticalIcon, SlashIcon } from "@radix-ui/react-icons";
import Avvvatars from "avvvatars-react";
// extensions
import InfoCallout from "./Extensions/Callout/Info/InfoCallout";
@ -19,7 +16,7 @@ import WarningCallout from "./Extensions/Callout/Warning/WarningCallout";
import ImageBlock from "./Extensions/Image/ImageBlock";
import Youtube from "@tiptap/extension-youtube";
import VideoBlock from "./Extensions/Video/VideoBlock";
import { Eye, Save } from "lucide-react";
import { Eye } from "lucide-react";
import MathEquationBlock from "./Extensions/MathEquation/MathEquationBlock";
import PDFBlock from "./Extensions/PDF/PDFBlock";
import QuizBlock from "./Extensions/Quiz/QuizBlock";
@ -120,7 +117,7 @@ function Editor(props: Editor) {
<Link href="/">
<EditorInfoLearnHouseLogo width={25} height={25} src={learnhouseIcon} alt="" />
</Link>
<Link target="_blank" href={`/course/${course_id}`}>
<Link target="_blank" href={`/course/${course_id}/edit`}>
<EditorInfoThumbnail src={`${getCourseThumbnailMediaDirectory(props.course.course.org_id,props.course.course.course_id,props.course.course.thumbnail)}`} alt=""></EditorInfoThumbnail>
</Link>
<EditorInfoDocName>

View file

@ -1,7 +1,6 @@
'use client';
import { default as React, } from "react";
import * as Y from "yjs";
import { WebrtcProvider } from "y-webrtc";
import Editor from "./Editor";
import { updateActivity } from "@services/courses/activities";
import { toast } from "react-hot-toast";

View file

@ -6,7 +6,7 @@ import styled from "styled-components";
function InfoCalloutComponent(props: any) {
return (
<NodeViewWrapper>
<InfoCalloutWrapper contentEditable={props.extension.options.editable}>
<InfoCalloutWrapper className="flex space-x-2 items-center bg-blue-200 rounded-lg text-blue-900 px-3 shadow-inner" contentEditable={props.extension.options.editable}>
<AlertCircle /> <NodeViewContent contentEditable={props.extension.options.editable} className="content" />
</InfoCalloutWrapper>
</NodeViewWrapper>
@ -14,16 +14,6 @@ function InfoCalloutComponent(props: any) {
}
const InfoCalloutWrapper = styled.div`
display: flex;
flex-direction: row;
color: #1f3a8a;
background-color: #dbe9fe;
border: 1px solid #c1d9fb;
border-radius: 16px;
margin: 1rem 0;
align-items: center;
padding-left: 15px;
svg{
padding: 3px;
}

View file

@ -6,23 +6,15 @@ import styled from "styled-components";
function WarningCalloutComponent(props: any) {
return (
<NodeViewWrapper>
<CalloutWrapper contentEditable={props.extension.options.editable}>
<AlertTriangle/> <NodeViewContent contentEditable={props.extension.options.editable} className="content" />
<CalloutWrapper className="flex space-x-2 items-center bg-yellow-200 rounded-lg text-yellow-900 px-3 shadow-inner" contentEditable={props.extension.options.editable}>
<AlertTriangle /> <NodeViewContent contentEditable={props.extension.options.editable} className="content" />
</CalloutWrapper>
</NodeViewWrapper>
);
}
const CalloutWrapper = 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;
svg {
padding: 3px;

View file

@ -2,11 +2,9 @@ import { NodeViewWrapper } from "@tiptap/react";
import React from "react";
import styled from "styled-components";
import { Resizable } from 're-resizable';
import * as AspectRatio from '@radix-ui/react-aspect-ratio';
import { AlertCircle, AlertTriangle, Image, ImagePlus, Info } from "lucide-react";
import { getImageFile, uploadNewImageFile } from "../../../../../services/blocks/Image/images";
import { getBackendUrl } from "../../../../../services/config/config";
import { AlertTriangle, Image, Loader } from "lucide-react";
import { uploadNewImageFile } from "../../../../../services/blocks/Image/images";
import { UploadIcon } from "@radix-ui/react-icons";
import { getActivityBlockMediaDirectory } from "@services/media/media";
function ImageBlockComponent(props: any) {
@ -34,33 +32,30 @@ function ImageBlockComponent(props: any) {
return (
<NodeViewWrapper className="block-image">
{!blockObject && (
<BlockImageWrapper contentEditable={props.extension.options.editable}>
<div>
<Image color="#e1e0e0" size={50} />
<br />
</div>
<input onChange={handleImageChange} type="file" name="" id="" />
<br />
<button onClick={handleSubmit}>Submit</button>
{!blockObject && props.extension.options.editable && (
<BlockImageWrapper className="flex items-center space-x-3 py-7 bg-gray-50 rounded-xl text-gray-900 px-3 border-dashed border-gray-150 border-2" contentEditable={props.extension.options.editable}>
{isLoading ? (
<Loader className="animate-spin animate-pulse text-gray-200" size={50} />
) : (
<>
<div>
<Image className="text-gray-200" size={50} />
</div>
<input className="p-3 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 hover:file:cursor-pointer file:bg-gray-200 cursor-pointer file:text-gray-500" onChange={handleImageChange} type="file" name="" id="" />
<button className='p-2 px-3 bg-gray-200 rounded-lg text-gray-500 hover:bg-gray-300 transition space-x-2 items-center flex' onClick={handleSubmit}><UploadIcon></UploadIcon><p>Submit</p></button>
</>
)}
</BlockImageWrapper>
)}
{blockObject && (
<Resizable defaultSize={{ width: imageSize.width, height: "100%" }}
handleStyles={{
right: { width: '10px', height: '100%', cursor: 'col-resize' },
top: { width: 0 },
bottom: { width: 0 },
left: { width: 0 },
topRight: { width: 0 },
bottomRight: { width: 0 },
bottomLeft: { width: 0 },
topLeft: { width: 0 },
right: { position: 'unset', width: 7, height: 30, borderRadius: 20, cursor: 'col-resize', backgroundColor: 'black', opacity: '0.3', margin: 'auto', marginLeft:5 },
}}
style={{ margin: "auto" }}
maxWidth={850}
minWidth={400}
style={{ margin: "auto", display: "flex", justifyContent: "center", alignItems: "center", height: "100%" }}
maxWidth={1000}
minWidth={200}
onResizeStop={(e, direction, ref, d) => {
props.updateAttributes({
size: {
@ -73,21 +68,17 @@ function ImageBlockComponent(props: any) {
}}
>
<BlockImage>
<AspectRatio.Root ratio={16 / 9}>
<img
src={`${getActivityBlockMediaDirectory(props.extension.options.activity.org_id,
props.extension.options.activity.course_id,
props.extension.options.activity.activity_id,
blockObject.block_id,
blockObject ? fileId : ' ', 'imageBlock')}`}
alt=""
/>
{blockObject.block_id}
</AspectRatio.Root>
<img
src={`${getActivityBlockMediaDirectory(props.extension.options.activity.org_id,
props.extension.options.activity.course_id,
props.extension.options.activity.activity_id,
blockObject.block_id,
blockObject ? fileId : ' ', 'imageBlock')}`}
alt=""
className="rounded-lg shadow "
/>
</BlockImage>
</Resizable>
)}
{isLoading && (
@ -102,19 +93,11 @@ function ImageBlockComponent(props: any) {
export default ImageBlockComponent;
const BlockImageWrapper = styled.div`
display: flex;
flex-direction: column;
background: #f9f9f9;
border-radius: 3px;
padding: 30px;
min-height: 74px;
border: ${(props) => (props.contentEditable ? "2px dashed #713f1117" : "none")};
// center
align-items: center;
justify-content: center;
text-align: center;
font-size: 14px;
`;
const BlockImage = styled.div`
@ -128,11 +111,5 @@ const BlockImage = styled.div`
font-size: 14px;
img{
object-fit: "cover";
width: 100%;
height: 100%;
border-radius: 6px;
}
`;

View file

@ -22,26 +22,18 @@ function MathEquationBlockComponent(props: any) {
props.updateAttributes({
math_equation: equation,
});
setIsEditing(false);
//setIsEditing(false);
};
return (
<NodeViewWrapper className="block-math-equation">
<MathEqWrapper>
{isEditable && (
<MathEqTopMenu>
<button onClick={() => setIsEditing(true)}>
<Edit size={15}></Edit>
</button>
<span className="pl-2">Edit</span>
</MathEqTopMenu>
)}
<MathEqWrapper className="flex flex-col space-y-2 bg-gray-50 shadow-inner rounded-lg py-7 px-5">
<BlockMath>{equation}</BlockMath>
{isEditing && isEditable && (
<>
<EditBar>
<input value={equation} onChange={handleEquationChange} placeholder="Insert a Math Equation (LaTeX) " type="text" />
<button onClick={() => saveEquation()}>
<button className="opacity-1" onClick={() => saveEquation()}>
<Save size={15}></Save>
</button>
</EditBar>
@ -57,27 +49,6 @@ function MathEquationBlockComponent(props: any) {
export default MathEquationBlockComponent;
const MathEqWrapper = styled.div`
display: flex;
flex-direction: column;
background: #f9f9f9a2;
border-radius: 8px;
margin: 20px;
padding: 20px;
min-height: 74px;
border: ${(props) => (props.contentEditable ? "2px dashed #713f1117" : "none")};
`;
const MathEqTopMenu = styled.div`
display: flex;
justify-content: flex-start;
button {
margin-left: 10px;
cursor: pointer;
border: none;
background: none;
font-size: 14px;
color: #494949;
}
`;
const EditBar = styled.div`

View file

@ -1,9 +1,10 @@
import { NodeViewWrapper } from "@tiptap/react";
import React from "react";
import styled from "styled-components";
import { AlertCircle, AlertTriangle, FileText, Image, ImagePlus, Info } from "lucide-react";
import { AlertCircle, AlertTriangle, FileText, Image, ImagePlus, Info, Loader } from "lucide-react";
import { getPDFFile, uploadNewPDFFile } from "../../../../../services/blocks/Pdf/pdf";
import { getBackendUrl } from "../../../../../services/config/config";
import { UploadIcon } from "@radix-ui/react-icons";
import { getActivityBlockMediaDirectory } from "@services/media/media";
function PDFBlockComponent(props: any) {
@ -30,19 +31,24 @@ function PDFBlockComponent(props: any) {
return (
<NodeViewWrapper className="block-pdf">
{!blockObject && (
<BlockPDFWrapper contentEditable={props.extension.options.editable}>
<div>
<FileText color="#e1e0e0" size={50} />
<br />
</div>
<input onChange={handlePDFChange} type="file" name="" id="" />
<br />
<button onClick={handleSubmit}>Submit</button>
<BlockPDFWrapper className="flex items-center space-x-3 py-7 bg-gray-50 rounded-xl text-gray-900 px-3 border-dashed border-gray-150 border-2" contentEditable={props.extension.options.editable}>
{isLoading ? (
<Loader className="animate-spin animate-pulse text-gray-200" size={50} />
) : (
<>
<div>
<FileText className="text-gray-200" size={50} />
</div>
<input className="p-3 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 hover:file:cursor-pointer file:bg-gray-200 cursor-pointer file:text-gray-500" onChange={handlePDFChange} type="file" name="" id="" />
<button className='p-2 px-3 bg-gray-200 rounded-lg text-gray-500 hover:bg-gray-300 transition space-x-2 items-center flex' onClick={handleSubmit}><UploadIcon></UploadIcon><p>Submit</p></button>
</>
)}
</BlockPDFWrapper>
)}
{blockObject && (
<BlockPDF>
<iframe
className="shadow rounded-lg h-96 w-full object-scale-down bg-black"
src={`${getActivityBlockMediaDirectory(props.extension.options.activity.org_id,
props.extension.options.activity.course_id,
props.extension.options.activity.activity_id,
@ -63,14 +69,7 @@ function PDFBlockComponent(props: any) {
export default PDFBlockComponent;
const BlockPDFWrapper = styled.div`
display: flex;
flex-direction: column;
background: #f9f9f9;
border-radius: 3px;
padding: 30px;
min-height: 74px;
border: ${(props) => (props.contentEditable ? "2px dashed #713f1117" : "none")};
// center
align-items: center;
justify-content: center;

View file

@ -1,10 +1,11 @@
import { NodeViewWrapper } from "@tiptap/react";
import { AlertTriangle, Image, Video } from "lucide-react";
import { AlertTriangle, Image, Loader, Video } from "lucide-react";
import React from "react";
import styled from "styled-components";
import { getBackendUrl } from "../../../../../services/config/config";
import { uploadNewVideoFile } from "../../../../../services/blocks/Video/video";
import { getActivityBlockMediaDirectory } from "@services/media/media";
import { UploadIcon } from "@radix-ui/react-icons";
function VideoBlockComponents(props: any) {
const [video, setVideo] = React.useState(null);
@ -30,20 +31,25 @@ function VideoBlockComponents(props: any) {
return (
<NodeViewWrapper className="block-video">
{!blockObject && (
<BlockVideoWrapper contentEditable={props.extension.options.editable}>
<div>
<Video color="#e1e0e0" size={50} />
<br />
</div>
<input onChange={handleVideoChange} type="file" name="" id="" />
<br />
<button onClick={handleSubmit}>Submit</button>
<BlockVideoWrapper className="flex items-center space-x-3 py-7 bg-gray-50 rounded-xl text-gray-900 px-3 border-dashed border-gray-150 border-2" contentEditable={props.extension.options.editable}>
{isLoading ? (
<Loader className="animate-spin animate-pulse text-gray-200" size={50} />
) : (
<>
<div>
<Video className="text-gray-200" size={50} />
</div>
<input className="p-3 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 hover:file:cursor-pointer file:bg-gray-200 cursor-pointer file:text-gray-500" onChange={handleVideoChange} type="file" name="" id="" />
<button className='p-2 px-3 bg-gray-200 rounded-lg text-gray-500 hover:bg-gray-300 transition space-x-2 items-center flex' onClick={handleSubmit}><UploadIcon></UploadIcon><p>Submit</p></button>
</>
)}
</BlockVideoWrapper>
)}
{blockObject && (
<BlockVideo>
<video
controls
className="rounded-lg shadow h-96 w-full object-scale-down bg-black"
src={`${getActivityBlockMediaDirectory(props.extension.options.activity.org_id,
props.extension.options.activity.course_id,
props.extension.options.activity.activity_id,
@ -52,22 +58,13 @@ function VideoBlockComponents(props: any) {
></video>
</BlockVideo>
)}
{isLoading && (
<div>
<AlertTriangle color="#e1e0e0" size={50} />
</div>
)}
</NodeViewWrapper>
);
}
const BlockVideoWrapper = styled.div`
display: flex;
flex-direction: column;
background: #f9f9f9;
border-radius: 3px;
padding: 30px;
min-height: 74px;
border: ${(props) => (props.contentEditable ? "2px dashed #713f1117" : "none")};
//border: ${(props) => (props.contentEditable ? "2px dashed #713f1117" : "none")};
// center
align-items: center;
@ -79,12 +76,5 @@ const BlockVideoWrapper = styled.div`
const BlockVideo = styled.div`
display: flex;
flex-direction: column;
video {
width: 100%;
border-radius: 6px;
height: 300px;
// cover
object-fit: cover;
}
`;
export default VideoBlockComponents;

View file

@ -1,4 +1,3 @@
import React from "react";
import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { styled } from "styled-components";
@ -63,9 +62,54 @@ function Canva(props: Editor) {
}
const CanvaWrapper = styled.div`
padding-top: 20px;
width: 100%;
margin: 0 auto;
// disable chrome outline
.ProseMirror {
h1 {
font-size: 30px;
font-weight: 600;
margin-bottom: 10px;
}
h2 {
font-size: 25px;
font-weight: 600;
margin-bottom: 10px;
}
h3 {
font-size: 20px;
font-weight: 600;
margin-bottom: 10px;
}
h4 {
font-size: 18px;
font-weight: 600;
margin-top: 10px;
margin-bottom: 10px;
}
h5 {
font-size: 16px;
font-weight: 600;
margin-top: 10px;
margin-bottom: 10px;
}
&:focus {
outline: none !important;
outline-style: none !important;
box-shadow: none !important;
}
}
`;
export default Canva;

View file

@ -1,10 +1,8 @@
import Link from "next/link";
import React from "react";
import { Draggable } from "react-beautiful-dnd";
import { EyeOpenIcon, Pencil2Icon } from '@radix-ui/react-icons'
import styled from "styled-components";
import { getUriWithOrg } from "@services/config/config";
import { FileText, Video, Sparkles } from "lucide-react";
import { FileText, Video, Sparkles, File } from "lucide-react";
function Activity(props: any) {
@ -12,14 +10,14 @@ function Activity(props: any) {
<Draggable key={props.activity.id} draggableId={props.activity.id} index={props.index}>
{(provided) => (
<div
className="flex flex-row py-2 my-2 rounded-md bg-gray-50 text-gray-500 hover:bg-gray-100 hover:scale-105 hover:shadow space-x-2 w-auto items-center ring-1 ring-inset ring-gray-400/10 shadow-sm transition-all delay-100 duration-75 ease-linear" key={props.activity.id} {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
<div className="px-3 text-gray-300 space-x-1" >
{props.activity.type === "video" && <Video size={16} />}
{props.activity.type === "documentpdf" && <FileText size={16} />}
{props.activity.type === "dynamic" && <Sparkles size={16} />}
className="flex flex-row py-2 my-2 rounded-md bg-gray-50 text-gray-500 hover:bg-gray-100 hover:scale-102 hover:shadow space-x-2 w-auto items-center ring-1 ring-inset ring-gray-400/10 shadow-sm transition-all delay-100 duration-75 ease-linear" key={props.activity.id} {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
<div className="px-3 text-gray-300 space-x-1 w-28" >
{props.activity.type === "video" && <><div className="flex space-x-2 items-center"><Video size={16} /> <div className="text-xs bg-gray-200 text-gray-400 font-bold px-2 py-1 rounded-full">Video</div> </div></>}
{props.activity.type === "documentpdf" && <><div className="flex space-x-2 items-center"><FileText size={16} /> <div className="text-xs bg-gray-200 text-gray-400 font-bold px-2 py-1 rounded-full">Document</div> </div></>}
{props.activity.type === "dynamic" && <><div className="flex space-x-2 items-center"><Sparkles size={16} /> <div className="text-xs bg-gray-200 text-gray-400 font-bold px-2 py-1 rounded-full">Dynamic</div> </div></>}
</div>
<div className="grow justify-end text-center items-center">
<p className="first-letter:uppercase">{props.activity.name} </p>
<div className="grow items-center space-x-1 flex">
<p className="first-letter:uppercase"> {props.activity.name} </p>
</div>
<div className="flex flex-row space-x-1 px-3"><Link
href={getUriWithOrg(props.orgslug, "") + `/course/${props.courseid}/activity/${props.activity.id.replace("activity_", "")}`}