feat: adding video to text editor

This commit is contained in:
MTG2000
2022-05-12 19:16:37 +03:00
parent 5d7a997ba7
commit 3add019def
11 changed files with 201 additions and 23 deletions

View File

@@ -0,0 +1,17 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import InsertVideoModal from './InsertVideoModal';
import { ModalsDecorator } from 'src/utils/storybook/decorators';
export default {
title: 'Shared/Inputs/Text Editor/Insert Video Modal',
component: InsertVideoModal,
decorators: [ModalsDecorator]
} as ComponentMeta<typeof InsertVideoModal>;
const Template: ComponentStory<typeof InsertVideoModal> = (args) => <InsertVideoModal {...args} />;
export const Default = Template.bind({});

View File

@@ -0,0 +1,70 @@
import React, { FormEvent, useState } from 'react'
import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'
import { motion } from 'framer-motion'
import { IoClose } from 'react-icons/io5'
import Button from 'src/Components/Button/Button'
import { useAppDispatch } from 'src/utils/hooks'
import { PayloadAction } from '@reduxjs/toolkit'
interface Props extends ModalCard {
callbackAction: PayloadAction<{ src: string, alt?: string }>
}
export default function InsertVideoModal({ onClose, direction, callbackAction, ...props }: Props) {
const [idInput, setIdInput] = useState("")
const [altInput, setAltInput] = useState("")
const dispatch = useAppDispatch();
const handleSubmit = (e: FormEvent) => {
e.preventDefault()
if (idInput.length > 10) {
// onInsert({ src: idInput, alt: altInput })
const action = Object.assign({}, callbackAction);
action.payload = { src: idInput, alt: altInput }
dispatch(action)
onClose?.();
}
}
return (
<motion.div
custom={direction}
variants={modalCardVariants}
initial='initial'
animate="animate"
exit='exit'
className="modal-card max-w-[660px] p-24 rounded-xl relative"
>
<IoClose className='absolute text-body2 top-24 right-24 hover:cursor-pointer' onClick={onClose} />
<h2 className='text-h5 font-bold'>Insert Youtube Video</h2>
<form onSubmit={handleSubmit}>
<div className="grid gap-16 mt-32">
<div className='md:col-span-2'>
<p className="text-body5">
Video Id
</p>
<div className="input-wrapper mt-8 relative">
<input
type='text'
className="input-text"
value={idInput}
onChange={e => setIdInput(e.target.value)}
placeholder='Zi7sRMcJT-o'
/>
</div>
</div>
</div>
<div className="flex gap-16 justify-end mt-32">
<Button onClick={onClose}>
Cancel
</Button>
<Button type='submit' color='primary' >
Insert
</Button>
</div>
</form>
</motion.div>
)
}

View File

@@ -75,10 +75,7 @@ WithPreview.args = {
#### heading4
###### heading6
<br/>
<br/>
<br/>
###### heading6
some text with **bold**, _italic,_ underline, [www.link.com](//www.link.com)

View File

@@ -23,6 +23,7 @@ import {
TableExtension,
TrailingNodeExtension,
UnderlineExtension,
IframeExtension,
} from 'remirror/extensions';
import { ExtensionPriority, InvalidContentHandler } from 'remirror';
import { EditorComponent, Remirror, useHelpers, useRemirror } from '@remirror/react';
@@ -77,6 +78,7 @@ export default function TextEditor({ placeholder, initialContent }: Props) {
// new TableExtension(),
new MarkdownExtension({ copyAsMarkdown: false }),
new NodeFormattingExtension(),
new IframeExtension(),
/**
* `HardBreakExtension` allows us to create a newline inside paragraphs.
* e.g. in a list item

View File

@@ -32,7 +32,7 @@ export default function ImageToolButton({ classes }: Props) {
})
}, [commands])
useReduxEffect(onInsertImage, 'INSERT_IMAGE_IN_EDITOR')
useReduxEffect(onInsertImage, INSERT_IMAGE_ACTION.type)
@@ -42,7 +42,7 @@ export default function ImageToolButton({ classes }: Props) {
Modal: "InsertImageModal",
props: {
callbackAction: {
type: 'INSERT_IMAGE_IN_EDITOR',
type: INSERT_IMAGE_ACTION.type,
payload: {
src: "",
alt: ""

View File

@@ -2,6 +2,7 @@ import ImageToolButton from './ImageToolBtn';
import HeadingsToolButton from './HeadingsToolBtn';
import DefaultToolButton from './DefaultToolBtn';
import { Command, isCommand } from './helpers';
import VideoToolButton from './VideoToolBtn';
interface Props {
cmd: Command
@@ -36,6 +37,9 @@ export default function ToolButton({ cmd,
return <HeadingsToolButton classes={classes} />
if (cmd === 'youtube')
return <VideoToolButton classes={classes} />
if (cmd === 'img')
return <ImageToolButton classes={classes} />

View File

@@ -0,0 +1,71 @@
import { useActive, useCommands } from '@remirror/react';
import { useAppDispatch } from 'src/utils/hooks';
import { openModal } from 'src/redux/features/modals.slice';
import { useReduxEffect } from 'src/utils/hooks/useReduxEffect';
import { useCallback } from 'react';
import { createAction } from '@reduxjs/toolkit';
import { cmdToBtn } from './helpers';
interface Props {
classes: {
button: string,
icon: string,
active: string,
enabled: string
disabled: string
}
}
const INSERT_VIDEO_ACTION = createAction<{ src: string }>('VIDEO_INSERTED_IN_EDITOR')({ src: '' })
export default function VideoToolButton({ classes }: Props) {
const commands = useCommands();
const active = useActive();
const dispatch = useAppDispatch()
const onInsertVideo = useCallback(({ payload: { src } }: typeof INSERT_VIDEO_ACTION) => {
commands.addYouTubeVideo({
video: src,
})
}, [commands])
useReduxEffect(onInsertVideo, INSERT_VIDEO_ACTION.type)
const { activeCmd, cmd, tip, Icon } = cmdToBtn['youtube'];
const onClick = () => {
dispatch(openModal({
Modal: "InsertVideoModal",
props: {
callbackAction: {
type: INSERT_VIDEO_ACTION.type,
payload: {
src: "",
}
}
}
}))
}
return (
<button
type='button'
data-tip={tip}
className={`
${classes.button}
${(activeCmd && active[activeCmd]()) && classes.active}
${commands[cmd].enabled({ video: "" }) ? classes.enabled : classes.disabled}
`}
onClick={onClick}
>
<Icon className={classes.icon} />
</button>
)
}

View File

@@ -1,5 +1,5 @@
import { FiBold, FiItalic, FiType, FiUnderline, FiAlignCenter, FiAlignLeft, FiAlignRight, FiCode } from 'react-icons/fi'
import { FaListOl, FaListUl, FaUndo, FaRedo, FaImage } from 'react-icons/fa'
import { FaListOl, FaListUl, FaUndo, FaRedo, FaImage, FaYoutube } from 'react-icons/fa'
import { BiCodeCurly } from 'react-icons/bi';
@@ -92,6 +92,13 @@ export const cmdToBtn = {
Icon: FaImage,
},
youtube: {
cmd: 'addYouTubeVideo',
activeCmd: 'iframe',
tip: "Insert Video",
Icon: FaYoutube,
},
} as const

View File

@@ -19,6 +19,7 @@ export default function Toolbar() {
<ToolButton cmd='bulletList' />
<ToolButton cmd='orderedList' />
<ToolButton cmd='img' />
<ToolButton cmd='youtube' />
</div>

View File

@@ -3,6 +3,7 @@ import { Login_ScanningWalletCard, Login_ExternalWalletCard, Login_NativeWalletC
import { ProjectDetailsCard } from "src/features/Projects/pages/ProjectPage/ProjectDetailsCard";
import VoteCard from "src/features/Projects/pages/ProjectPage/VoteCard/VoteCard";
import InsertImageModal from 'src/Components/Inputs/TextEditor/InsertImageModal/InsertImageModal'
import InsertVideoModal from 'src/Components/Inputs/TextEditor/InsertVideoModal/InsertVideoModal'
import { Claim_FundWithdrawCard, Claim_CopySignatureCard, Claim_GenerateSignatureCard, Claim_SubmittedCard } from "src/features/Projects/pages/ProjectPage/ClaimProject";
import { ModalCard } from "src/Components/Modals/ModalsContainer/ModalsContainer";
import { ComponentProps } from "react";
@@ -28,7 +29,10 @@ export const ALL_MODALS = {
Claim_CopySignatureCard,
Claim_SubmittedCard,
Claim_FundWithdrawCard,
InsertImageModal
// Text Editor Modals
InsertImageModal,
InsertVideoModal,
}
type ExcludeBaseModalProps<U> = Omit<U, keyof ModalCard>

View File

@@ -1,36 +1,41 @@
//
//
// This file is for overriding various libraries native styles
// -----------------------------------------------------------
// Re mirror
// ---------------
.remirror-editor-wrapper ul {
list-style: disc !important;
padding: revert;
margin: revert;
list-style: disc !important;
padding: revert;
margin: revert;
}
.remirror-editor-wrapper ol {
list-style: decimal !important;
padding: revert;
margin: revert;
list-style: decimal !important;
padding: revert;
margin: revert;
}
.remirror-editor-wrapper iframe,
.remirror-theme iframe {
width: calc(min(100%, 550px));
margin: 0 auto;
aspect-ratio: 16/9;
}
// React Modals
// ----------------
.ReactModal__Overlay {
opacity: 0;
transition: opacity 900ms ease-in-out;
opacity: 0;
transition: opacity 900ms ease-in-out;
}
.ReactModal__Overlay--after-open {
opacity: 1;
opacity: 1;
}
.ReactModal__Overlay--before-close {
opacity: 0;
transition-timing-function: ease-in;
transition-duration: 400ms;
opacity: 0;
transition-timing-function: ease-in;
transition-duration: 400ms;
}