aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/Import & Export/DirectoryImportBox.tsx
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
committerSophie Zhang <sophie_zhang@brown.edu>2024-01-25 11:35:26 -0500
commitf3dab2a56db5e4a6a3dca58185d94e1ff7d1dc32 (patch)
treea7bc895266b53bb620dbd2dd71bad2e83b555446 /src/client/util/Import & Export/DirectoryImportBox.tsx
parentb5c5410b4af5d2c68d2107d3f064f6e3ec4ac3f2 (diff)
parent136f3d9f349d54e8bdd73b6380ea47c19e5edebf (diff)
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/util/Import & Export/DirectoryImportBox.tsx')
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx819
1 files changed, 409 insertions, 410 deletions
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 1a4c2450e..398ba3c04 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -1,440 +1,439 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { BatchedArray } from 'array-batcher';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import { extname } from 'path';
-import Measure, { ContentRect } from 'react-measure';
-import { Doc, DocListCast, DocListCastAsync, Opt } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
-import { List } from '../../../fields/List';
-import { listSpec } from '../../../fields/Schema';
-import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { BoolCast, Cast, NumCast } from '../../../fields/Types';
-import { AcceptableMedia, Upload } from '../../../server/SharedMediaTypes';
-import { Utils } from '../../../Utils';
-import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
-import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { Networking } from '../../Network';
-import { FieldView, FieldViewProps } from '../../views/nodes/FieldView';
-import { DocumentManager } from '../DocumentManager';
-import './DirectoryImportBox.scss';
-import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from './ImportMetadataEntry';
-import React = require('react');
-import _ = require('lodash');
+// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// import { BatchedArray } from 'array-batcher';
+// import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+// import { observer } from 'mobx-react';
+// import { extname } from 'path';
+// import Measure, { ContentRect } from 'react-measure';
+// import { Doc, DocListCast, DocListCastAsync, Opt } from '../../../fields/Doc';
+// import { Id } from '../../../fields/FieldSymbols';
+// import { List } from '../../../fields/List';
+// import { listSpec } from '../../../fields/Schema';
+// import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
+// import { BoolCast, Cast, NumCast } from '../../../fields/Types';
+// import { AcceptableMedia, Upload } from '../../../server/SharedMediaTypes';
+// import { Utils } from '../../../Utils';
+// import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
+// import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
+// import { DocumentType } from '../../documents/DocumentTypes';
+// import { Networking } from '../../Network';
+// import { FieldView, FieldViewProps } from '../../views/nodes/FieldView';
+// import { DocumentManager } from '../DocumentManager';
+// import './DirectoryImportBox.scss';
+// import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from './ImportMetadataEntry';
+// import * as React from 'react';
-const unsupported = ['text/html', 'text/plain'];
+// const unsupported = ['text/html', 'text/plain'];
-@observer
-export class DirectoryImportBox extends React.Component<FieldViewProps> {
- private selector = React.createRef<HTMLInputElement>();
- @observable private top = 0;
- @observable private left = 0;
- private dimensions = 50;
- @observable private phase = '';
- private disposer: Opt<IReactionDisposer>;
+// @observer
+// export class DirectoryImportBox extends React.Component<FieldViewProps> {
+// private selector = React.createRef<HTMLInputElement>();
+// @observable private top = 0;
+// @observable private left = 0;
+// private dimensions = 50;
+// @observable private phase = '';
+// private disposer: Opt<IReactionDisposer>;
- @observable private entries: ImportMetadataEntry[] = [];
+// @observable private entries: ImportMetadataEntry[] = [];
- @observable private quota = 1;
- @observable private completed = 0;
+// @observable private quota = 1;
+// @observable private completed = 0;
- @observable private uploading = false;
- @observable private removeHover = false;
+// @observable private uploading = false;
+// @observable private removeHover = false;
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(DirectoryImportBox, fieldKey);
- }
+// public static LayoutString(fieldKey: string) {
+// return FieldView.LayoutString(DirectoryImportBox, fieldKey);
+// }
- constructor(props: FieldViewProps) {
- super(props);
- const doc = this.props.Document;
- this.editingMetadata = this.editingMetadata || false;
- this.persistent = this.persistent || false;
- !Cast(doc.data, listSpec(Doc)) && (doc.data = new List<Doc>());
- }
+// constructor(props: FieldViewProps) {
+// super(props);
+// const doc = this.props.Document;
+// this.editingMetadata = this.editingMetadata || false;
+// this.persistent = this.persistent || false;
+// !Cast(doc.data, listSpec(Doc)) && (doc.data = new List<Doc>());
+// }
- @computed
- private get editingMetadata() {
- return BoolCast(this.props.Document.editingMetadata);
- }
+// @computed
+// private get editingMetadata() {
+// return BoolCast(this.props.Document.editingMetadata);
+// }
- private set editingMetadata(value: boolean) {
- this.props.Document.editingMetadata = value;
- }
+// private set editingMetadata(value: boolean) {
+// this.props.Document.editingMetadata = value;
+// }
- @computed
- private get persistent() {
- return BoolCast(this.props.Document.persistent);
- }
+// @computed
+// private get persistent() {
+// return BoolCast(this.props.Document.persistent);
+// }
- private set persistent(value: boolean) {
- this.props.Document.persistent = value;
- }
+// private set persistent(value: boolean) {
+// this.props.Document.persistent = value;
+// }
- handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
- runInAction(() => {
- this.uploading = true;
- this.phase = 'Initializing download...';
- });
+// handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
+// runInAction(() => {
+// this.uploading = true;
+// this.phase = 'Initializing download...';
+// });
- const docs: Doc[] = [];
+// const docs: Doc[] = [];
- const files = e.target.files;
- if (!files || files.length === 0) return;
+// const files = e.target.files;
+// if (!files || files.length === 0) return;
- const directory = (files.item(0) as any).webkitRelativePath.split('/', 1)[0];
+// const directory = (files.item(0) as any).webkitRelativePath.split('/', 1)[0];
- const validated: File[] = [];
- for (let i = 0; i < files.length; i++) {
- const file = files.item(i);
- if (file && !unsupported.includes(file.type)) {
- const ext = extname(file.name).toLowerCase();
- if (AcceptableMedia.imageFormats.includes(ext)) {
- validated.push(file);
- }
- }
- }
+// const validated: File[] = [];
+// for (let i = 0; i < files.length; i++) {
+// const file = files.item(i);
+// if (file && !unsupported.includes(file.type)) {
+// const ext = extname(file.name).toLowerCase();
+// if (AcceptableMedia.imageFormats.includes(ext)) {
+// validated.push(file);
+// }
+// }
+// }
- runInAction(() => {
- this.quota = validated.length;
- this.completed = 0;
- });
+// runInAction(() => {
+// this.quota = validated.length;
+// this.completed = 0;
+// });
- const sizes: number[] = [];
- const modifiedDates: number[] = [];
+// const sizes: number[] = [];
+// const modifiedDates: number[] = [];
- runInAction(() => (this.phase = `Internal: uploading ${this.quota - this.completed} files to Dash...`));
+// runInAction(() => (this.phase = `Internal: uploading ${this.quota - this.completed} files to Dash...`));
- const batched = BatchedArray.from(validated, { batchSize: 15 });
- const uploads = await batched.batchedMapAsync<Upload.FileResponse<Upload.ImageInformation>>(async (batch, collector) => {
- batch.forEach(file => {
- sizes.push(file.size);
- modifiedDates.push(file.lastModified);
- });
- collector.push(...(await Networking.UploadFilesToServer<Upload.ImageInformation>(batch.map(file =>({file})))));
- runInAction(() => (this.completed += batch.length));
- });
+// const batched = BatchedArray.from(validated, { batchSize: 15 });
+// const uploads = await batched.batchedMapAsync<Upload.FileResponse<Upload.ImageInformation>>(async (batch, collector) => {
+// batch.forEach(file => {
+// sizes.push(file.size);
+// modifiedDates.push(file.lastModified);
+// });
+// collector.push(...(await Networking.UploadFilesToServer<Upload.ImageInformation>(batch.map(file => ({ file })))));
+// runInAction(() => (this.completed += batch.length));
+// });
- await Promise.all(
- uploads.map(async response => {
- const {
- source: { type },
- result,
- } = response;
- if (result instanceof Error) {
- return;
- }
- const { accessPaths, exifData } = result;
- const path = Utils.prepend(accessPaths.agnostic.client);
- const document = type && (await DocUtils.DocumentFromType(type, path, { _width: 300 }));
- const { data, error } = exifData;
- if (document) {
- Doc.GetProto(document).exif = error || Doc.Get.FromJson({ data });
- docs.push(document);
- }
- })
- );
+// await Promise.all(
+// uploads.map(async response => {
+// const {
+// source: { mimetype },
+// result,
+// } = response;
+// if (result instanceof Error) {
+// return;
+// }
+// const { accessPaths, exifData } = result;
+// const path = Utils.prepend(accessPaths.agnostic.client);
+// const document = mimetype && (await DocUtils.DocumentFromType(mimetype, path, { _width: 300 }));
+// const { data, error } = exifData;
+// if (document) {
+// Doc.GetProto(document).exif = error || Doc.Get.FromJson({ data });
+// docs.push(document);
+// }
+// })
+// );
- for (let i = 0; i < docs.length; i++) {
- const doc = docs[i];
- doc.size = sizes[i];
- doc.modified = modifiedDates[i];
- this.entries.forEach(entry => {
- const target = entry.onDataDoc ? Doc.GetProto(doc) : doc;
- target[entry.key] = entry.value;
- });
- }
+// for (let i = 0; i < docs.length; i++) {
+// const doc = docs[i];
+// doc.size = sizes[i];
+// doc.modified = modifiedDates[i];
+// this.entries.forEach(entry => {
+// const target = entry.onDataDoc ? Doc.GetProto(doc) : doc;
+// target[entry.key] = entry.value;
+// });
+// }
- const doc = this.props.Document;
- const height: number = NumCast(doc.height) || 0;
- const offset: number = this.persistent ? (height === 0 ? 0 : height + 30) : 0;
- const options: DocumentOptions = {
- title: `Import of ${directory}`,
- _width: 1105,
- _height: 500,
- _chromeHidden: true,
- x: NumCast(doc.x),
- y: NumCast(doc.y) + offset,
- };
- const parent = this.props.DocumentView?.().props.docViewPath().lastElement();
- if (parent?.rootDoc.type === DocumentType.COL) {
- let importContainer: Doc;
- if (docs.length < 50) {
- importContainer = Docs.Create.MasonryDocument(docs, options);
- } else {
- const headers = [new SchemaHeaderField('title'), new SchemaHeaderField('size')];
- importContainer = Docs.Create.SchemaDocument(headers, docs, options);
- }
- runInAction(() => (this.phase = 'External: uploading files to Google Photos...'));
- await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
- Doc.AddDocToList(Doc.GetProto(parent.props.Document), 'data', importContainer);
- !this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
- DocumentManager.Instance.showDocument(importContainer, { willZoomCentered: true });
- }
+// const doc = this.props.Document;
+// const height: number = NumCast(doc.height) || 0;
+// const offset: number = this.persistent ? (height === 0 ? 0 : height + 30) : 0;
+// const options: DocumentOptions = {
+// title: `Import of ${directory}`,
+// _width: 1105,
+// _height: 500,
+// _chromeHidden: true,
+// x: NumCast(doc.x),
+// y: NumCast(doc.y) + offset,
+// };
+// const parent = this.props.DocumentView?.().containerViewPath().lastElement();
+// if (parent?.Document.type === DocumentType.COL) {
+// let importContainer: Doc;
+// if (docs.length < 50) {
+// importContainer = Docs.Create.MasonryDocument(docs, options);
+// } else {
+// const headers = [new SchemaHeaderField('title'), new SchemaHeaderField('size')];
+// importContainer = Docs.Create.SchemaDocument(headers, docs, options);
+// }
+// runInAction(() => (this.phase = 'External: uploading files to Google Photos...'));
+// await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
+// Doc.AddDocToList(Doc.GetProto(parent.props.Document), 'data', importContainer);
+// !this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
+// DocumentManager.Instance.showDocument(importContainer, { willZoomCentered: true });
+// }
- runInAction(() => {
- this.uploading = false;
- this.quota = 1;
- this.completed = 0;
- });
- };
+// runInAction(() => {
+// this.uploading = false;
+// this.quota = 1;
+// this.completed = 0;
+// });
+// };
- componentDidMount() {
- this.selector.current!.setAttribute('directory', '');
- this.selector.current!.setAttribute('webkitdirectory', '');
- this.disposer = reaction(
- () => this.completed,
- completed => runInAction(() => (this.phase = `Internal: uploading ${this.quota - completed} files to Dash...`))
- );
- }
+// componentDidMount() {
+// this.selector.current!.setAttribute('directory', '');
+// this.selector.current!.setAttribute('webkitdirectory', '');
+// this.disposer = reaction(
+// () => this.completed,
+// completed => runInAction(() => (this.phase = `Internal: uploading ${this.quota - completed} files to Dash...`))
+// );
+// }
- componentWillUnmount() {
- this.disposer && this.disposer();
- }
+// componentWillUnmount() {
+// this.disposer && this.disposer();
+// }
- @action
- preserveCentering = (rect: ContentRect) => {
- const bounds = rect.offset!;
- if (bounds.width === 0 || bounds.height === 0) {
- return;
- }
- const offset = this.dimensions / 2;
- this.left = bounds.width / 2 - offset;
- this.top = bounds.height / 2 - offset;
- };
+// @action
+// preserveCentering = (rect: ContentRect) => {
+// const bounds = rect.offset!;
+// if (bounds.width === 0 || bounds.height === 0) {
+// return;
+// }
+// const offset = this.dimensions / 2;
+// this.left = bounds.width / 2 - offset;
+// this.top = bounds.height / 2 - offset;
+// };
- @action
- addMetadataEntry = async () => {
- const entryDoc = new Doc();
- entryDoc.checked = false;
- entryDoc.key = keyPlaceholder;
- entryDoc.value = valuePlaceholder;
- Doc.AddDocToList(this.props.Document, 'data', entryDoc);
- };
+// @action
+// addMetadataEntry = async () => {
+// const entryDoc = new Doc();
+// entryDoc.checked = false;
+// entryDoc.key = keyPlaceholder;
+// entryDoc.value = valuePlaceholder;
+// Doc.AddDocToList(this.props.Document, 'data', entryDoc);
+// };
- @action
- remove = async (entry: ImportMetadataEntry) => {
- const metadata = await DocListCastAsync(this.props.Document.data);
- if (metadata) {
- let index = this.entries.indexOf(entry);
- if (index !== -1) {
- runInAction(() => this.entries.splice(index, 1));
- index = metadata.indexOf(entry.props.Document);
- if (index !== -1) {
- metadata.splice(index, 1);
- }
- }
- }
- };
+// @action
+// remove = async (entry: ImportMetadataEntry) => {
+// const metadata = await DocListCastAsync(this.props.Document.data);
+// if (metadata) {
+// let index = this.entries.indexOf(entry);
+// if (index !== -1) {
+// runInAction(() => this.entries.splice(index, 1));
+// index = metadata.indexOf(entry.props.Document);
+// if (index !== -1) {
+// metadata.splice(index, 1);
+// }
+// }
+// }
+// };
- render() {
- const dimensions = 50;
- const entries = DocListCast(this.props.Document.data);
- const isEditing = this.editingMetadata;
- const completed = this.completed;
- const quota = this.quota;
- const uploading = this.uploading;
- const showRemoveLabel = this.removeHover;
- const persistent = this.persistent;
- let percent = `${(completed / quota) * 100}`;
- percent = percent.split('.')[0];
- percent = percent.startsWith('100') ? '99' : percent;
- const marginOffset = (percent.length === 1 ? 5 : 0) - 1.6;
- const message = <span className={'phase'}>{this.phase}</span>;
- const centerPiece = this.phase.includes('Google Photos') ? (
- <img
- src={'/assets/google_photos.png'}
- style={{
- transition: '0.4s opacity ease',
- width: 30,
- height: 30,
- opacity: uploading ? 1 : 0,
- pointerEvents: 'none',
- position: 'absolute',
- left: 12,
- top: this.top + 10,
- fontSize: 18,
- color: 'white',
- marginLeft: this.left + marginOffset,
- }}
- />
- ) : (
- <div
- style={{
- transition: '0.4s opacity ease',
- opacity: uploading ? 1 : 0,
- pointerEvents: 'none',
- position: 'absolute',
- left: 10,
- top: this.top + 12.3,
- fontSize: 18,
- color: 'white',
- marginLeft: this.left + marginOffset,
- }}>
- {percent}%
- </div>
- );
- return (
- <Measure offset onResize={this.preserveCentering}>
- {({ measureRef }) => (
- <div ref={measureRef} style={{ width: '100%', height: '100%', pointerEvents: 'all' }}>
- {message}
- <input
- id={'selector'}
- ref={this.selector}
- onChange={this.handleSelection}
- type="file"
- style={{
- position: 'absolute',
- display: 'none',
- }}
- />
- <label
- htmlFor={'selector'}
- style={{
- opacity: isEditing ? 0 : 1,
- pointerEvents: isEditing ? 'none' : 'all',
- transition: '0.4s ease opacity',
- }}>
- <div
- style={{
- width: dimensions,
- height: dimensions,
- borderRadius: '50%',
- background: 'black',
- position: 'absolute',
- left: this.left,
- top: this.top,
- }}
- />
- <div
- style={{
- position: 'absolute',
- left: this.left + 8,
- top: this.top + 10,
- opacity: uploading ? 0 : 1,
- transition: '0.4s opacity ease',
- }}>
- <FontAwesomeIcon icon={'cloud-upload-alt'} color="#FFFFFF" size={'2x'} />
- </div>
- <img
- style={{
- width: 80,
- height: 80,
- transition: '0.4s opacity ease',
- opacity: uploading ? 0.7 : 0,
- position: 'absolute',
- top: this.top - 15,
- left: this.left - 15,
- }}
- src={'/assets/loading.gif'}></img>
- </label>
- <input
- type={'checkbox'}
- onChange={e => runInAction(() => (this.persistent = e.target.checked))}
- style={{
- margin: 0,
- position: 'absolute',
- left: 10,
- bottom: 10,
- opacity: isEditing || uploading ? 0 : 1,
- transition: '0.4s opacity ease',
- pointerEvents: isEditing || uploading ? 'none' : 'all',
- }}
- checked={this.persistent}
- onPointerEnter={action(() => (this.removeHover = true))}
- onPointerLeave={action(() => (this.removeHover = false))}
- />
- <p
- style={{
- position: 'absolute',
- left: 27,
- bottom: 8.4,
- fontSize: 12,
- opacity: showRemoveLabel ? 1 : 0,
- transition: '0.4s opacity ease',
- }}>
- Template will be <span style={{ textDecoration: 'underline', textDecorationColor: persistent ? 'green' : 'red', color: persistent ? 'green' : 'red' }}>{persistent ? 'kept' : 'removed'}</span> after upload
- </p>
- {centerPiece}
- <div
- style={{
- position: 'absolute',
- top: 10,
- right: 10,
- borderRadius: '50%',
- width: 25,
- height: 25,
- background: 'black',
- pointerEvents: uploading ? 'none' : 'all',
- opacity: uploading ? 0 : 1,
- transition: '0.4s opacity ease',
- }}
- title={isEditing ? 'Back to Upload' : 'Add Metadata'}
- onClick={action(() => (this.editingMetadata = !this.editingMetadata))}
- />
- <FontAwesomeIcon
- style={{
- pointerEvents: 'none',
- position: 'absolute',
- right: isEditing ? 14 : 15,
- top: isEditing ? 15.4 : 16,
- opacity: uploading ? 0 : 1,
- transition: '0.4s opacity ease',
- }}
- icon={isEditing ? 'cloud-upload-alt' : 'tag'}
- color="#FFFFFF"
- size={'1x'}
- />
- <div
- style={{
- transition: '0.4s ease opacity',
- width: '100%',
- height: '100%',
- pointerEvents: isEditing ? 'all' : 'none',
- opacity: isEditing ? 1 : 0,
- overflowY: 'scroll',
- }}>
- <div
- style={{
- borderRadius: '50%',
- width: 25,
- height: 25,
- marginLeft: 10,
- position: 'absolute',
- right: 41,
- top: 10,
- }}
- title={'Add Metadata Entry'}
- onClick={this.addMetadataEntry}>
- <FontAwesomeIcon
- style={{
- pointerEvents: 'none',
- marginLeft: 6.4,
- marginTop: 5.2,
- }}
- icon={'plus'}
- size={'1x'}
- />
- </div>
- <p style={{ paddingLeft: 10, paddingTop: 8, paddingBottom: 7 }}>Add metadata to your import...</p>
- <hr style={{ margin: '6px 10px 12px 10px' }} />
- {entries.map(doc => (
- <ImportMetadataEntry
- Document={doc}
- key={doc[Id]}
- remove={this.remove}
- ref={el => {
- if (el) this.entries.push(el);
- }}
- next={this.addMetadataEntry}
- />
- ))}
- </div>
- </div>
- )}
- </Measure>
- );
- }
-}
+// render() {
+// const dimensions = 50;
+// const entries = DocListCast(this.props.Document.data);
+// const isEditing = this.editingMetadata;
+// const completed = this.completed;
+// const quota = this.quota;
+// const uploading = this.uploading;
+// const showRemoveLabel = this.removeHover;
+// const persistent = this.persistent;
+// let percent = `${(completed / quota) * 100}`;
+// percent = percent.split('.')[0];
+// percent = percent.startsWith('100') ? '99' : percent;
+// const marginOffset = (percent.length === 1 ? 5 : 0) - 1.6;
+// const message = <span className={'phase'}>{this.phase}</span>;
+// const centerPiece = this.phase.includes('Google Photos') ? (
+// <img
+// src={'/assets/google_photos.png'}
+// style={{
+// transition: '0.4s opacity ease',
+// width: 30,
+// height: 30,
+// opacity: uploading ? 1 : 0,
+// pointerEvents: 'none',
+// position: 'absolute',
+// left: 12,
+// top: this.top + 10,
+// fontSize: 18,
+// color: 'white',
+// marginLeft: this.left + marginOffset,
+// }}
+// />
+// ) : (
+// <div
+// style={{
+// transition: '0.4s opacity ease',
+// opacity: uploading ? 1 : 0,
+// pointerEvents: 'none',
+// position: 'absolute',
+// left: 10,
+// top: this.top + 12.3,
+// fontSize: 18,
+// color: 'white',
+// marginLeft: this.left + marginOffset,
+// }}>
+// {percent}%
+// </div>
+// );
+// return (
+// <Measure offset onResize={this.preserveCentering}>
+// {({ measureRef }) => (
+// <div ref={measureRef} style={{ width: '100%', height: '100%', pointerEvents: 'all' }}>
+// {message}
+// <input
+// id={'selector'}
+// ref={this.selector}
+// onChange={this.handleSelection}
+// type="file"
+// style={{
+// position: 'absolute',
+// display: 'none',
+// }}
+// />
+// <label
+// htmlFor={'selector'}
+// style={{
+// opacity: isEditing ? 0 : 1,
+// pointerEvents: isEditing ? 'none' : 'all',
+// transition: '0.4s ease opacity',
+// }}>
+// <div
+// style={{
+// width: dimensions,
+// height: dimensions,
+// borderRadius: '50%',
+// background: 'black',
+// position: 'absolute',
+// left: this.left,
+// top: this.top,
+// }}
+// />
+// <div
+// style={{
+// position: 'absolute',
+// left: this.left + 8,
+// top: this.top + 10,
+// opacity: uploading ? 0 : 1,
+// transition: '0.4s opacity ease',
+// }}>
+// <FontAwesomeIcon icon={'cloud-upload-alt'} color="#FFFFFF" size={'2x'} />
+// </div>
+// <img
+// style={{
+// width: 80,
+// height: 80,
+// transition: '0.4s opacity ease',
+// opacity: uploading ? 0.7 : 0,
+// position: 'absolute',
+// top: this.top - 15,
+// left: this.left - 15,
+// }}
+// src={'/assets/loading.gif'}></img>
+// </label>
+// <input
+// type={'checkbox'}
+// onChange={e => runInAction(() => (this.persistent = e.target.checked))}
+// style={{
+// margin: 0,
+// position: 'absolute',
+// left: 10,
+// bottom: 10,
+// opacity: isEditing || uploading ? 0 : 1,
+// transition: '0.4s opacity ease',
+// pointerEvents: isEditing || uploading ? 'none' : 'all',
+// }}
+// checked={this.persistent}
+// onPointerEnter={action(() => (this.removeHover = true))}
+// onPointerLeave={action(() => (this.removeHover = false))}
+// />
+// <p
+// style={{
+// position: 'absolute',
+// left: 27,
+// bottom: 8.4,
+// fontSize: 12,
+// opacity: showRemoveLabel ? 1 : 0,
+// transition: '0.4s opacity ease',
+// }}>
+// Template will be <span style={{ textDecoration: 'underline', textDecorationColor: persistent ? 'green' : 'red', color: persistent ? 'green' : 'red' }}>{persistent ? 'kept' : 'removed'}</span> after upload
+// </p>
+// {centerPiece}
+// <div
+// style={{
+// position: 'absolute',
+// top: 10,
+// right: 10,
+// borderRadius: '50%',
+// width: 25,
+// height: 25,
+// background: 'black',
+// pointerEvents: uploading ? 'none' : 'all',
+// opacity: uploading ? 0 : 1,
+// transition: '0.4s opacity ease',
+// }}
+// title={isEditing ? 'Back to Upload' : 'Add Metadata'}
+// onClick={action(() => (this.editingMetadata = !this.editingMetadata))}
+// />
+// <FontAwesomeIcon
+// style={{
+// pointerEvents: 'none',
+// position: 'absolute',
+// right: isEditing ? 14 : 15,
+// top: isEditing ? 15.4 : 16,
+// opacity: uploading ? 0 : 1,
+// transition: '0.4s opacity ease',
+// }}
+// icon={isEditing ? 'cloud-upload-alt' : 'tag'}
+// color="#FFFFFF"
+// size={'1x'}
+// />
+// <div
+// style={{
+// transition: '0.4s ease opacity',
+// width: '100%',
+// height: '100%',
+// pointerEvents: isEditing ? 'all' : 'none',
+// opacity: isEditing ? 1 : 0,
+// overflowY: 'scroll',
+// }}>
+// <div
+// style={{
+// borderRadius: '50%',
+// width: 25,
+// height: 25,
+// marginLeft: 10,
+// position: 'absolute',
+// right: 41,
+// top: 10,
+// }}
+// title={'Add Metadata Entry'}
+// onClick={this.addMetadataEntry}>
+// <FontAwesomeIcon
+// style={{
+// pointerEvents: 'none',
+// marginLeft: 6.4,
+// marginTop: 5.2,
+// }}
+// icon={'plus'}
+// size={'1x'}
+// />
+// </div>
+// <p style={{ paddingLeft: 10, paddingTop: 8, paddingBottom: 7 }}>Add metadata to your import...</p>
+// <hr style={{ margin: '6px 10px 12px 10px' }} />
+// {entries.map(doc => (
+// <ImportMetadataEntry
+// Document={doc}
+// key={doc[Id]}
+// remove={this.remove}
+// ref={el => {
+// if (el) this.entries.push(el);
+// }}
+// next={this.addMetadataEntry}
+// />
+// ))}
+// </div>
+// </div>
+// )}
+// </Measure>
+// );
+// }
+// }