From 95c894f81e1b0cddc25dbb3fba6be7cd8608db10 Mon Sep 17 00:00:00 2001 From: Hamza Alam Date: Sun, 28 Feb 2021 14:57:04 +0500 Subject: [PATCH 1/4] image link paste upload --- src/Editor.tsx | 3 ++- src/Helpers/ImageHelper.tsx | 33 +++++++++++++++++++++++++++++++++ src/ToolBar/ToolBar.tsx | 3 +++ src/assets/image.svg | 1 + src/plugins/Element.tsx | 11 ++++++++++- src/plugins/ElementStyle.ts | 6 ++++++ src/plugins/withImages.tsx | 11 +++++++++++ 7 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/Helpers/ImageHelper.tsx create mode 100644 src/assets/image.svg create mode 100644 src/plugins/withImages.tsx diff --git a/src/Editor.tsx b/src/Editor.tsx index 39bdfdc..3bbddea 100644 --- a/src/Editor.tsx +++ b/src/Editor.tsx @@ -12,6 +12,7 @@ import { withLinks } from './Helpers/LinkHelper'; import Element from './plugins/Element'; import Leaf from './plugins/Leaf'; import withBlockID from './plugins/withBlockID'; +import withImages from './plugins/withImages'; import serialize from './serialize/index'; import deserialize from './deserialize/index'; import { ADD, UPDATE, DELETE } from './constant/operations'; @@ -40,7 +41,7 @@ const Editor: (props: Props) => any = ({ isHoveringToolBar = false, }) => { const [editorData, setData] = useState(EMPTY_NODE); - const withPlugins = [withReact, withHistory, withLinks, withBlockID] as const; + const withPlugins = [withReact, withHistory, withLinks, withBlockID, withImages] as const; const editor: any = useMemo(() => pipe(createEditor(), ...withPlugins), []); const renderElement = useCallback((props) => , []); const renderLeaf = useCallback((props) => , []); diff --git a/src/Helpers/ImageHelper.tsx b/src/Helpers/ImageHelper.tsx new file mode 100644 index 0000000..4089d5f --- /dev/null +++ b/src/Helpers/ImageHelper.tsx @@ -0,0 +1,33 @@ +import React, { FC, SVGProps } from 'react'; +import { Transforms } from 'slate'; +import { ReactEditor, useSlate } from 'slate-react'; + +import { Button, Icon } from './Helper'; + +export const insertImage = (editor: ReactEditor, url: string) => { + const text = { text: '' }; + const image = { type: 'image', url, children: [text] }; + Transforms.insertNodes(editor, image); +}; + +interface InsertImageButtonProps { + icon: FC>; + iconColor?: string; +} +export const InsertImageButton: (props: InsertImageButtonProps) => JSX.Element = ({ icon, iconColor = '#ffffff' }) => { + const editor = useSlate(); + return ( + + ); +}; diff --git a/src/ToolBar/ToolBar.tsx b/src/ToolBar/ToolBar.tsx index bcf5e3c..a3632ec 100644 --- a/src/ToolBar/ToolBar.tsx +++ b/src/ToolBar/ToolBar.tsx @@ -7,6 +7,7 @@ import { BlockButton } from '../Helpers/BlockHelper'; import { MarkButton } from '../Helpers/MarkHelper'; import { LinkButton, insertLink } from '../Helpers/LinkHelper'; import { Menu, Portal, LinkInput } from '../Helpers/Helper'; +import { InsertImageButton } from '../Helpers/ImageHelper'; // icons import { ReactComponent as Bold } from '../assets/bold.svg'; @@ -18,6 +19,7 @@ import { ReactComponent as Underline } from '../assets/underline.svg'; import { ReactComponent as H1 } from '../assets/h1.svg'; import { ReactComponent as H2 } from '../assets/h2.svg'; import { ReactComponent as CLEAR_FORMAT } from '../assets/clear_format.svg'; +import { ReactComponent as Image } from '../assets/image.svg'; const ICON_COLOR = '#b5b9c6'; export const FixedMenu: any = React.forwardRef(({ ...props }, ref: React.Ref) => ( @@ -113,6 +115,7 @@ export const ToolBar: React.FC = () => { + diff --git a/src/assets/image.svg b/src/assets/image.svg new file mode 100644 index 0000000..b711525 --- /dev/null +++ b/src/assets/image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/plugins/Element.tsx b/src/plugins/Element.tsx index def317f..98f0a75 100644 --- a/src/plugins/Element.tsx +++ b/src/plugins/Element.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { RenderElementProps } from 'slate-react'; -import { Paragraph, CodeBlock, BlockQuote, Link, LinkContainer, Triangle, Rectangle } from './ElementStyle'; +import { Paragraph, CodeBlock, BlockQuote, Link, Image, LinkContainer, Triangle, Rectangle } from './ElementStyle'; import getShortLink from '../utils/getShortLink'; const Element: React.FC = ({ attributes, children, element }: any) => { @@ -31,6 +31,15 @@ const Element: React.FC = ({ attributes, children, element } ); + case `image`: + return ( +
+
+ +
+ {children} +
+ ); default: return

{children}

; } diff --git a/src/plugins/ElementStyle.ts b/src/plugins/ElementStyle.ts index 18b52c7..8061679 100644 --- a/src/plugins/ElementStyle.ts +++ b/src/plugins/ElementStyle.ts @@ -71,3 +71,9 @@ export const Link = styled.a` text-decoration: underline; color: ${({ theme }) => (theme.colors !== undefined ? theme.colors.link : '#050b21')}; `; + +export const Image = styled.img` + display: block; + max-width: 100%; + max-height: 20em; +`; diff --git a/src/plugins/withImages.tsx b/src/plugins/withImages.tsx new file mode 100644 index 0000000..a8716d0 --- /dev/null +++ b/src/plugins/withImages.tsx @@ -0,0 +1,11 @@ +import { ReactEditor } from 'slate-react'; + +const withImages = (editor: ReactEditor) => { + const { isVoid } = editor; + const localEditor = editor; + localEditor.isVoid = (element) => { + return element.type === 'image' ? true : isVoid(element); + }; + return localEditor; +}; +export default withImages; From 0f86c496943148412650a41c3f5588aa7957d314 Mon Sep 17 00:00:00 2001 From: Hamza Alam Date: Tue, 2 Mar 2021 11:29:55 +0500 Subject: [PATCH 2/4] padding added --- src/plugins/ElementStyle.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/ElementStyle.ts b/src/plugins/ElementStyle.ts index 8061679..2a3eaf0 100644 --- a/src/plugins/ElementStyle.ts +++ b/src/plugins/ElementStyle.ts @@ -76,4 +76,5 @@ export const Image = styled.img` display: block; max-width: 100%; max-height: 20em; + padding: 4px 0px 0px 20px; `; From ed82b62b818c2896ac2ab9775cc253045be4972e Mon Sep 17 00:00:00 2001 From: Hamza Alam Date: Tue, 2 Mar 2021 13:23:02 +0500 Subject: [PATCH 3/4] serialize and deserialize --- example/src/App.tsx | 16 ++++++++++++++++ src/deserialize/index.ts | 4 ++++ src/serialize/index.tsx | 9 ++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 69d2366..48e0dfb 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -110,5 +110,21 @@ const blockInitalValue2 = [ }, }, }, + { + block: { + _id: '5', + type: 'image', + properties: { + document: [ + { + text: '', + properties: [ + 'https://repository-images.githubusercontent.com/125159715/61f2ee00-865a-11e9-8ce5-f7028c561633', + ], + }, + ], + }, + }, + }, ]; export default App; diff --git a/src/deserialize/index.ts b/src/deserialize/index.ts index 8e151aa..06b27ad 100644 --- a/src/deserialize/index.ts +++ b/src/deserialize/index.ts @@ -56,6 +56,10 @@ const deserialization = (blockContentList: any) => { const { block } = blockContent; const children = getChildNodes(block); const type = getNodeType(block.type); + if (type === 'image' && block.properties) { + return { id: block._id, type, children, url: block.properties.document[0].properties }; + } + return { id: block._id, type, children }; }); return deserializedContent; diff --git a/src/serialize/index.tsx b/src/serialize/index.tsx index 2211ca8..54fc60f 100644 --- a/src/serialize/index.tsx +++ b/src/serialize/index.tsx @@ -38,7 +38,12 @@ const checkIdAndReturnBlock = (type: string, document: any, node: any) => { const serializeParagraph = (paragraphNode: any, textType: string) => { const document = paragraphNode.children.map((childNodes: any) => { const text = getParagraphText(childNodes); - const properties = getParagraphProperties(childNodes); + let properties = []; + if (textType === 'image') { + properties.push(paragraphNode.url); + } else { + properties = getParagraphProperties(childNodes); + } return { text, properties }; }); const paragraphBlock = checkIdAndReturnBlock(textType, document, paragraphNode); @@ -64,6 +69,8 @@ const serialize = (slateNodesList: any) => { case 'heading-one': case 'heading-two': return serializeHeading(node, node.type); + case 'image': + return serializeParagraph(node, 'image'); default: return serializeParagraph(node, 'text'); } From 9b164ff1ba9e660a9ef7ca6709cce7f82cace679 Mon Sep 17 00:00:00 2001 From: Hamza Alam Date: Thu, 4 Mar 2021 18:17:22 +0500 Subject: [PATCH 4/4] insert break override --- src/Editor.tsx | 3 ++- src/plugins/withBreak.tsx | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/plugins/withBreak.tsx diff --git a/src/Editor.tsx b/src/Editor.tsx index 3bbddea..ac954a8 100644 --- a/src/Editor.tsx +++ b/src/Editor.tsx @@ -13,6 +13,7 @@ import Element from './plugins/Element'; import Leaf from './plugins/Leaf'; import withBlockID from './plugins/withBlockID'; import withImages from './plugins/withImages'; +import withBreak from './plugins/withBreak'; import serialize from './serialize/index'; import deserialize from './deserialize/index'; import { ADD, UPDATE, DELETE } from './constant/operations'; @@ -41,7 +42,7 @@ const Editor: (props: Props) => any = ({ isHoveringToolBar = false, }) => { const [editorData, setData] = useState(EMPTY_NODE); - const withPlugins = [withReact, withHistory, withLinks, withBlockID, withImages] as const; + const withPlugins = [withReact, withHistory, withLinks, withBlockID, withImages, withBreak] as const; const editor: any = useMemo(() => pipe(createEditor(), ...withPlugins), []); const renderElement = useCallback((props) => , []); const renderLeaf = useCallback((props) => , []); diff --git a/src/plugins/withBreak.tsx b/src/plugins/withBreak.tsx new file mode 100644 index 0000000..f133692 --- /dev/null +++ b/src/plugins/withBreak.tsx @@ -0,0 +1,18 @@ +import { Transforms } from 'slate'; + +const withBreak = (editor: any) => { + const localEditor = editor; + localEditor.insertBreak = () => { + const newLine = { + type: 'paragraph', + children: [ + { + text: '', + }, + ], + }; + Transforms.insertNodes(editor, newLine); + }; + return localEditor; +}; +export default withBreak;