toolbar: convert OpenFileButton to function

This commit is contained in:
David Lechner
2021-12-27 12:38:50 -06:00
parent f3e179683e
commit 8cd014c2ab
+93 -105
View File
@@ -2,9 +2,9 @@
// Copyright (c) 2020-2021 The Pybricks Authors
import { Button, Intent, Position, Spinner, Tooltip } from '@blueprintjs/core';
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
import { useI18n } from '@shopify/react-i18n';
import React from 'react';
import Dropzone, { FileRejection } from 'react-dropzone';
import Dropzone from 'react-dropzone';
import { tooltipDelay } from '../app/constants';
import { TooltipId } from './i18n';
import en from './i18n.en.json';
@@ -32,114 +32,102 @@ export interface OpenFileButtonProps {
readonly onClick?: () => void;
}
type Props = OpenFileButtonProps & WithI18nProps;
/**
* Button that opens a file chooser dialog or accepts files dropped on it.
*/
class OpenFileButton extends React.Component<Props> {
constructor(props: Props) {
super(props);
this.onDropAccepted = this.onDropAccepted.bind(this);
this.onDropRejected = this.onDropRejected.bind(this);
}
const OpenFileButton: React.FC<OpenFileButtonProps> = (props) => {
const [i18n] = useI18n({
id: 'openFileButton',
translations: { en },
fallback: en,
});
private onDropAccepted(acceptedFiles: File[]): void {
// should only be one file since multiple={false}
acceptedFiles.forEach((f) => {
const reader = new FileReader();
return (
<Dropzone
accept={props.fileExtension}
multiple={false}
noClick={props.onClick !== undefined}
noKeyboard={props.onClick !== undefined}
onDropAccepted={(acceptedFiles) => {
// should only be one file since multiple={false}
acceptedFiles.forEach((f) => {
const reader = new FileReader();
reader.onabort = (): void => console.error('file reading was aborted');
reader.onerror = (): void => console.error('file reading has failed');
reader.onload = (): void => {
const binaryStr = reader.result;
if (binaryStr === null) {
throw Error('Unexpected null binaryStr');
}
if (typeof binaryStr === 'string') {
throw Error('Unexpected string binaryStr');
}
this.props.onFile(binaryStr);
};
reader.readAsArrayBuffer(f);
});
}
private onDropRejected(fileRejections: FileRejection[]): void {
// should only be one file since multiple={false}
fileRejections.forEach((r) => {
this.props.onReject(r.file);
});
}
render(): JSX.Element {
return (
<Dropzone
onDropAccepted={this.onDropAccepted}
onDropRejected={this.onDropRejected}
accept={this.props.fileExtension}
multiple={false}
noClick={this.props.onClick !== undefined}
noKeyboard={this.props.onClick !== undefined}
>
{({ getRootProps, getInputProps }): JSX.Element => (
<Tooltip
content={this.props.i18n.translate(
this.props.tooltip,
this.props.tooltip === TooltipId.FlashProgress
? {
percent:
this.props.progress === undefined
? ''
: this.props.i18n.formatPercentage(
this.props.progress,
),
}
: undefined,
)}
position={Position.BOTTOM}
hoverOpenDelay={tooltipDelay}
reader.onabort = (): void =>
console.error('file reading was aborted');
reader.onerror = (): void =>
console.error('file reading has failed');
reader.onload = (): void => {
const binaryStr = reader.result;
if (binaryStr === null) {
throw Error('Unexpected null binaryStr');
}
if (typeof binaryStr === 'string') {
throw Error('Unexpected string binaryStr');
}
props.onFile(binaryStr);
};
reader.readAsArrayBuffer(f);
});
}}
onDropRejected={(fileRejections) => {
// should only be one file since multiple={false}
fileRejections.forEach((r) => {
props.onReject(r.file);
});
}}
>
{({ getRootProps, getInputProps }): JSX.Element => (
<Tooltip
content={i18n.translate(
props.tooltip,
props.tooltip === TooltipId.FlashProgress
? {
percent:
props.progress === undefined
? ''
: i18n.formatPercentage(props.progress),
}
: undefined,
)}
position={Position.BOTTOM}
hoverOpenDelay={tooltipDelay}
>
<div
{...getRootProps()}
tabIndex={-1}
className="pb-open-file-button-root"
>
<div
{...getRootProps()}
tabIndex={-1}
className="pb-open-file-button-root"
<Button
intent={Intent.PRIMARY}
disabled={props.enabled === false}
className="no-box-shadow"
style={
props.enabled === false
? { pointerEvents: 'none' }
: undefined
}
onMouseDown={(e) => e.preventDefault()} // prevent focus
// onClick={this.props.onClick}
// breaks Dropzone when this.props.onClick is undefined
// so we have to do it the long way
{...(props.onClick ? { onClick: props.onClick } : {})}
>
<Button
intent={Intent.PRIMARY}
disabled={this.props.enabled === false}
className="no-box-shadow"
style={
this.props.enabled === false
? { pointerEvents: 'none' }
: undefined
}
onMouseDown={(e) => e.preventDefault()} // prevent focus
// onClick={this.props.onClick}
// breaks Dropzone when this.props.onClick is undefined
// so we have to do it the long way
{...(this.props.onClick
? { onClick: this.props.onClick }
: {})}
>
<input {...getInputProps()} />
{this.props.showProgress ? (
<Spinner
value={this.props.progress}
intent={Intent.PRIMARY}
/>
) : (
<img src={this.props.icon} alt={this.props.id} />
)}
</Button>
</div>
</Tooltip>
)}
</Dropzone>
);
}
}
<input {...getInputProps()} />
{props.showProgress ? (
<Spinner
value={props.progress}
intent={Intent.PRIMARY}
/>
) : (
<img src={props.icon} alt={props.id} />
)}
</Button>
</div>
</Tooltip>
)}
</Dropzone>
);
};
export default withI18n({ id: 'openFileButton', fallback: en, translations: { en } })(
OpenFileButton,
);
export default OpenFileButton;