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:
committed by
Umputun
parent
17f61ee339
commit
2f7c4e7e03
@@ -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;
|
||||
|
||||
@@ -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}>
|
||||
|
||||
Reference in New Issue
Block a user