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
This commit is contained in:
Konstantin Krivlenia
2019-07-16 10:27:44 -05:00
committed by Umputun
parent 17f61ee339
commit 2f7c4e7e03
4 changed files with 67 additions and 1 deletions
@@ -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;
+16 -1
View File
@@ -92,6 +92,7 @@ export class Input extends Component<Props, State> {
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<Props, State> {
});
}
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<Props, State> {
onDrop={this.onDrop}
>
<div className="input__control-panel">
<MarkdownToolbar textareaId={this.textareaId} />
<MarkdownToolbar
allowUpload={Boolean(this.props.uploadImage)}
uploadImages={this.uploadImages}
textareaId={this.textareaId}
/>
</div>
<div className="input__field-wrapper">
<TextareaAutosize
id={this.textareaId}
onPaste={this.onPaste}
ref={ref => (this.textAreaRef = ref)}
className="input__field"
placeholder="Your comment here"
@@ -0,0 +1,13 @@
/** @jsx h */
import { h } from 'preact';
export default function ImageIcon() {
return (
<svg className="input__toolbar-icon" width="11.25" height="15" viewBox="0 0 384 512" aria-hidden="true">
<path
fill-rule="evenodd"
d="M369.9 97.9L286 14C277 5 264.8-.1 252.1-.1H48C21.5 0 0 21.5 0 48v416c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48V131.9c0-12.7-5.1-25-14.1-34zM332.1 128H256V51.9l76.1 76.1zM48 464V48h160v104c0 13.3 10.7 24 24 24h104v288H48zm32-48h224V288l-23.5-23.5c-4.7-4.7-12.3-4.7-17 0L176 352l-39.5-39.5c-4.7-4.7-12.3-4.7-17 0L80 352v64zm48-240c-26.5 0-48 21.5-48 48s21.5 48 48 48 48-21.5 48-48-21.5-48-48-48z"
/>
</svg>
);
}
@@ -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<void>;
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 <cmd-b>';
@@ -22,8 +34,20 @@ const codeLabel = 'Insert a code';
const linkLabel = 'Add a link <cmd-k>';
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<Props> {
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<Props>) {
return (
<markdown-toolbar className="input__toolbar" for={props.textareaId}>
@@ -48,6 +72,12 @@ export default class MarkdownToolbar extends Component<Props> {
<md-link className="input__toolbar-item" title={linkLabel} aria-label={linkLabel}>
<LinkIcon />
</md-link>
{this.props.allowUpload ? (
<label className="input__toolbar-item" title={attachImageLabel} aria-label={attachImageLabel}>
<input multiple class="input__toolbar-file-input" type="file" onChange={this.uploadImages} />
<ImageIcon />
</label>
) : null}
</div>
<div className="input__toolbar-group">
<md-unordered-list className="input__toolbar-item" title={unorderedListLabel} aria-label={unorderedListLabel}>