From 2f7c4e7e038181fc9fdb4aa999da29f670866b5e Mon Sep 17 00:00:00 2001 From: Konstantin Krivlenia Date: Tue, 16 Jul 2019 18:27:44 +0300 Subject: [PATCH] Add a button for image upload (#372) * #371 add image icon for toolbar * #371 add file upload handler * #371 upload image from clipboard * #371 change title and decrease size for upload button * #371 allow upload few files * #371 prevent paste text after file was uploaded in firefox --- .../input__markdown-toolbar.scss | 8 +++++ frontend/app/components/input/input.tsx | 17 ++++++++++- .../markdown-toolbar-icons/image-icon.tsx | 13 ++++++++ .../app/components/input/markdown-toolbar.tsx | 30 +++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 frontend/app/components/input/markdown-toolbar-icons/image-icon.tsx diff --git a/frontend/app/components/input/__markdown-toolbar/input__markdown-toolbar.scss b/frontend/app/components/input/__markdown-toolbar/input__markdown-toolbar.scss index 1122ca4e..fd58dca5 100644 --- a/frontend/app/components/input/__markdown-toolbar/input__markdown-toolbar.scss +++ b/frontend/app/components/input/__markdown-toolbar/input__markdown-toolbar.scss @@ -3,6 +3,14 @@ float: right; } +.input__toolbar-file-input { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); +} + .input__toolbar-item { background: none; border: 0; diff --git a/frontend/app/components/input/input.tsx b/frontend/app/components/input/input.tsx index d17b6189..8478e8a9 100644 --- a/frontend/app/components/input/input.tsx +++ b/frontend/app/components/input/input.tsx @@ -92,6 +92,7 @@ export class Input extends Component { this.appendError = this.appendError.bind(this); this.uploadImage = this.uploadImage.bind(this); this.uploadImages = this.uploadImages.bind(this); + this.onPaste = this.onPaste.bind(this); } componentWillReceiveProps(nextProps: Props) { @@ -135,6 +136,15 @@ export class Input extends Component { }); } + async onPaste(e: ClipboardEvent) { + if (!(e.clipboardData && e.clipboardData.files.length > 0)) { + return; + } + e.preventDefault(); + const files = Array.from(e.clipboardData.files); + await this.uploadImages(files); + } + send(e: Event) { const text = this.state.text; const props = this.props; @@ -354,11 +364,16 @@ export class Input extends Component { onDrop={this.onDrop} >
- +
(this.textAreaRef = ref)} className="input__field" placeholder="Your comment here" diff --git a/frontend/app/components/input/markdown-toolbar-icons/image-icon.tsx b/frontend/app/components/input/markdown-toolbar-icons/image-icon.tsx new file mode 100644 index 00000000..d1766ab1 --- /dev/null +++ b/frontend/app/components/input/markdown-toolbar-icons/image-icon.tsx @@ -0,0 +1,13 @@ +/** @jsx h */ +import { h } from 'preact'; + +export default function ImageIcon() { + return ( + + ); +} diff --git a/frontend/app/components/input/markdown-toolbar.tsx b/frontend/app/components/input/markdown-toolbar.tsx index a50c9ba5..9c29ebe7 100644 --- a/frontend/app/components/input/markdown-toolbar.tsx +++ b/frontend/app/components/input/markdown-toolbar.tsx @@ -7,11 +7,23 @@ import ItalicIcon from './markdown-toolbar-icons/italic-icon'; import QuoteIcon from './markdown-toolbar-icons/quote-icon'; import CodeIcon from './markdown-toolbar-icons/code-icon'; import LinkIcon from './markdown-toolbar-icons/link-icon'; +import ImageIcon from './markdown-toolbar-icons/image-icon'; import UnorderedListIcon from './markdown-toolbar-icons/unordered-list-icon'; import OrderedListIcon from './markdown-toolbar-icons/ordered-list-icon'; interface Props { textareaId: string; + uploadImages: (files: File[]) => Promise; + allowUpload: boolean; +} + +interface FileEventTarget extends EventTarget { + readonly files: FileList | null; + value: string | null; +} + +interface FileInputEvent extends Event { + readonly currentTarget: FileEventTarget | null; } const boldLabel = 'Add bold text '; @@ -22,8 +34,20 @@ const codeLabel = 'Insert a code'; const linkLabel = 'Add a link '; const unorderedListLabel = 'Add a bulleted list'; const orderedListLabel = 'Add a numbered list'; +const attachImageLabel = 'Attach the image, drag & drop or paste from clipboard'; export default class MarkdownToolbar extends Component { + constructor(props: Props) { + super(props); + this.uploadImages = this.uploadImages.bind(this); + } + async uploadImages(e: Event) { + const currentTarget = (e as FileInputEvent).currentTarget; + if (!(this.props.allowUpload && currentTarget && currentTarget.files && currentTarget.files.length !== 0)) return; + const files = Array.from(currentTarget.files); + await this.props.uploadImages(files); + currentTarget.value = null; + } render(props: RenderableProps) { return ( @@ -48,6 +72,12 @@ export default class MarkdownToolbar extends Component { + {this.props.allowUpload ? ( + + ) : null}