mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-04 15:04:24 +01:00
Merge branch 'dev' into feature/linking-accounts
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import InsertLinkModal from './InsertLinkModal';
|
||||
|
||||
import { ModalsDecorator } from 'src/utils/storybook/decorators';
|
||||
|
||||
export default {
|
||||
title: 'Shared/Inputs/Text Editor/Insert Link Modal',
|
||||
component: InsertLinkModal,
|
||||
|
||||
decorators: [ModalsDecorator]
|
||||
} as ComponentMeta<typeof InsertLinkModal>;
|
||||
|
||||
const Template: ComponentStory<typeof InsertLinkModal> = (args) => <InsertLinkModal {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
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'
|
||||
import * as yup from "yup";
|
||||
import { SubmitHandler, useForm } from "react-hook-form"
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
|
||||
interface Props extends ModalCard {
|
||||
callbackAction: PayloadAction<{ href: string, text: string }>
|
||||
}
|
||||
|
||||
const schema = yup.object({
|
||||
text: yup.string().trim().required().min(2, 'Link text should be at least 2 characters'),
|
||||
href: yup.string().trim().required().url(),
|
||||
|
||||
}).required();
|
||||
|
||||
|
||||
export interface IFormInputs {
|
||||
text: string,
|
||||
href: string,
|
||||
}
|
||||
|
||||
|
||||
export default function InsertLinkModal({ onClose, direction, callbackAction, ...props }: Props) {
|
||||
|
||||
const { register, formState: { errors }, handleSubmit } = useForm<IFormInputs>({
|
||||
defaultValues: {
|
||||
href: '',
|
||||
text: '',
|
||||
},
|
||||
resolver: yupResolver(schema),
|
||||
mode: 'onBlur',
|
||||
});
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const onSubmit: SubmitHandler<IFormInputs> = data => {
|
||||
const action = Object.assign({}, callbackAction);
|
||||
action.payload = { text: data.text, href: data.href }
|
||||
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 Link</h2>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="grid md:grid-cols-2 gap-16 mt-32">
|
||||
<div>
|
||||
<p className="text-body5">
|
||||
Link Text
|
||||
</p>
|
||||
<div className="input-wrapper mt-8 relative">
|
||||
<input
|
||||
type='text'
|
||||
className="input-text"
|
||||
placeholder='My Website'
|
||||
{...register('text')}
|
||||
/>
|
||||
</div>
|
||||
{errors.text && <p className="input-error">
|
||||
{errors.text.message}
|
||||
</p>}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-body5">
|
||||
Link URL
|
||||
</p>
|
||||
<div className="input-wrapper mt-8 relative">
|
||||
<input
|
||||
type='text'
|
||||
className="input-text"
|
||||
placeholder='https://www.website.com'
|
||||
{...register('href')}
|
||||
/>
|
||||
</div>
|
||||
{errors.href && <p className="input-error">
|
||||
{errors.href.message}
|
||||
</p>}
|
||||
</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>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
import { lazyModal } from 'src/utils/helperFunctions';
|
||||
|
||||
export const { LazyComponent: InsertLinkModal } = lazyModal(() => import('./InsertLinkModal'))
|
||||
68
src/Components/Inputs/TextEditor/ToolButton/LinkToolBtn.tsx
Normal file
68
src/Components/Inputs/TextEditor/ToolButton/LinkToolBtn.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
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_LINK_ACTION = createAction<{ href: string, text: string }>('LINK_INSERTED_IN_EDITOR')({ href: '', text: '' })
|
||||
|
||||
export default function LinkToolButton({ classes }: Props) {
|
||||
|
||||
const commands = useCommands();
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const onInsertLink = useCallback(({ payload: { href, text } }: typeof INSERT_LINK_ACTION) => {
|
||||
commands.insertMarkdown(`[${text}](${href})`)
|
||||
}, [commands])
|
||||
|
||||
useReduxEffect(onInsertLink, INSERT_LINK_ACTION.type)
|
||||
|
||||
|
||||
|
||||
const { tip, Icon } = cmdToBtn['link'];
|
||||
const onClick = () => {
|
||||
dispatch(openModal({
|
||||
Modal: "InsertLinkModal",
|
||||
props: {
|
||||
callbackAction: {
|
||||
type: INSERT_LINK_ACTION.type,
|
||||
payload: {
|
||||
href: "",
|
||||
text: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
data-tip={tip}
|
||||
className={`
|
||||
${classes.button}
|
||||
${classes.enabled}
|
||||
`}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon className={classes.icon} />
|
||||
</button>
|
||||
)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import HeadingsToolButton from './HeadingsToolBtn';
|
||||
import DefaultToolButton from './DefaultToolBtn';
|
||||
import { Command, isCommand } from './helpers';
|
||||
import VideoToolButton from './VideoToolBtn';
|
||||
import LinkToolButton from './LinkToolBtn';
|
||||
|
||||
interface Props {
|
||||
cmd: Command
|
||||
@@ -43,6 +44,10 @@ export default function ToolButton({ cmd,
|
||||
if (cmd === 'img')
|
||||
return <ImageToolButton classes={classes} />
|
||||
|
||||
|
||||
if (cmd === 'link')
|
||||
return <LinkToolButton classes={classes} />
|
||||
|
||||
return <DefaultToolButton classes={classes} cmd={cmd} />
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { FiBold, FiItalic, FiType, FiUnderline, FiAlignCenter, FiAlignLeft, FiAlignRight, FiCode } from 'react-icons/fi'
|
||||
import { FaListOl, FaListUl, FaUndo, FaRedo, FaImage, FaYoutube, FaQuoteLeft } from 'react-icons/fa'
|
||||
import { FaListOl, FaListUl, FaUndo, FaRedo, FaImage, FaYoutube, FaQuoteLeft, FaLink } from 'react-icons/fa'
|
||||
import { BiCodeCurly } from 'react-icons/bi';
|
||||
|
||||
|
||||
@@ -97,6 +97,12 @@ export const cmdToBtn = {
|
||||
tip: "Insert Image",
|
||||
Icon: FaImage,
|
||||
},
|
||||
link: {
|
||||
cmd: 'insertLink',
|
||||
activeCmd: 'link',
|
||||
tip: "Insert Link",
|
||||
Icon: FaLink,
|
||||
},
|
||||
|
||||
youtube: {
|
||||
cmd: 'addYouTubeVideo',
|
||||
|
||||
@@ -17,9 +17,10 @@ export default function Toolbar() {
|
||||
{/* <TextEditorComponents.ToolButton cmd='leftAlign' />
|
||||
<TextEditorComponents.ToolButton cmd='centerAlign' />
|
||||
<TextEditorComponents.ToolButton cmd='rightAlign' /> */}
|
||||
<TextEditorComponents.ToolButton cmd='blockquote' />
|
||||
<TextEditorComponents.ToolButton cmd='link' />
|
||||
<TextEditorComponents.ToolButton cmd='code' />
|
||||
<TextEditorComponents.ToolButton cmd='codeBlock' />
|
||||
<TextEditorComponents.ToolButton cmd='blockquote' />
|
||||
<TextEditorComponents.ToolButton cmd='bulletList' />
|
||||
<TextEditorComponents.ToolButton cmd='orderedList' />
|
||||
<TextEditorComponents.ToolButton cmd='img' />
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ProjectDetailsCard } from "src/features/Projects/pages/ProjectPage/Proj
|
||||
import VoteCard from "src/features/Projects/pages/ProjectPage/VoteCard/VoteCard";
|
||||
import { InsertImageModal } from 'src/Components/Inputs/TextEditor/InsertImageModal'
|
||||
import { InsertVideoModal } from 'src/Components/Inputs/TextEditor/InsertVideoModal'
|
||||
import { InsertLinkModal } from 'src/Components/Inputs/TextEditor/InsertLinkModal'
|
||||
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 { ConfirmModal } from "src/Components/Modals/ConfirmModal";
|
||||
@@ -38,6 +39,7 @@ export const ALL_MODALS = {
|
||||
// Text Editor Modals
|
||||
InsertImageModal,
|
||||
InsertVideoModal,
|
||||
InsertLinkModal,
|
||||
}
|
||||
|
||||
type ExcludeBaseModalProps<U> = Omit<U, keyof ModalCard>
|
||||
|
||||
Reference in New Issue
Block a user