diff options
author | Sam Wilkins <samwilkins333@gmail.com> | 2019-07-05 17:31:20 -0400 |
---|---|---|
committer | Sam Wilkins <samwilkins333@gmail.com> | 2019-07-05 17:31:20 -0400 |
commit | 12788cbe0586d0349f70b73d1b6f6a481b4bf2cf (patch) | |
tree | b7c7c55edcf76b5fd99b3fa20069d41aedb13858 | |
parent | aedd283fb9f9eff4145e27658bc6647982256032 (diff) |
first pass at implementation of directory import
-rw-r--r-- | deploy/assets/loading.gif | bin | 0 -> 114208 bytes | |||
-rw-r--r-- | src/client/util/Import & Export/DirectoryImportBox.scss | 0 | ||||
-rw-r--r-- | src/client/util/Import & Export/DirectoryImportBox.tsx | 194 | ||||
-rw-r--r-- | src/client/util/Import & Export/KeyValue.tsx | 79 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 2 |
5 files changed, 217 insertions, 58 deletions
diff --git a/deploy/assets/loading.gif b/deploy/assets/loading.gif Binary files differnew file mode 100644 index 000000000..0652bf021 --- /dev/null +++ b/deploy/assets/loading.gif diff --git a/src/client/util/Import & Export/DirectoryImportBox.scss b/src/client/util/Import & Export/DirectoryImportBox.scss deleted file mode 100644 index e69de29bb..000000000 --- a/src/client/util/Import & Export/DirectoryImportBox.scss +++ /dev/null diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 2d77f6ae6..cbc8c6fdc 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -3,44 +3,46 @@ import React = require("react"); import { Doc } from "../../../new_fields/Doc"; import { DocServer } from "../../DocServer"; import { RouteStore } from "../../../server/RouteStore"; -import { action, observable, runInAction } from "mobx"; +import { action, observable, autorun, runInAction } from "mobx"; import { FieldViewProps, FieldView } from "../../views/nodes/FieldView"; import Measure, { ContentRect } from "react-measure"; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faArrowUp, faTag, faFileExcel } from '@fortawesome/free-solid-svg-icons'; +import { faArrowUp, faTag, faPlus } from '@fortawesome/free-solid-svg-icons'; import { Docs, DocumentOptions } from "../../documents/Documents"; -import { EditableView } from "../../views/EditableView"; +import { observer } from "mobx-react"; +import KeyValue from "./KeyValue"; +import { Utils } from "../../../Utils"; +import { doesNotReject } from "assert"; +import { remove } from "typescript-collections/dist/lib/arrays"; +@observer export default class DirectoryImportBox extends React.Component<FieldViewProps> { private selector = React.createRef<HTMLInputElement>(); @observable private top = 0; @observable private left = 0; private dimensions = 50; - @observable private key = "Key"; - @observable private value = "Value"; + @observable private editingMetadata = false; + @observable private metadata_guids: string[] = []; + @observable private entries: KeyValue[] = []; + + @observable private quota = 1; + @observable private remaining = 1; + + @observable private uploadBegun = false; public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); } constructor(props: FieldViewProps) { super(props); - library.add(faArrowUp, faTag); - } - - updateKey = (newKey: string) => { - runInAction(() => this.key = newKey); - console.log("KEY ", this.key); - return true; - } - - updateValue = (newValue: string) => { - runInAction(() => this.value = newValue); - console.log("VALUE ", this.value); - return true; + library.add(faArrowUp, faTag, faPlus); } + @action handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => { + this.uploadBegun = true; + let promises: Promise<void>[] = []; let docs: Doc[] = []; @@ -49,9 +51,15 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> let directory = (files.item(0) as any).webkitRelativePath.split("/", 1); + let validated: File[] = []; for (let i = 0; i < files.length; i++) { - let uploaded_file = files.item(i); + let file = files.item(i); + file && validated.push(file); + } + + this.quota = validated.length; + for (let uploaded_file of validated) { if (!uploaded_file) { continue; } @@ -61,15 +69,18 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> let dropFileName = uploaded_file ? uploaded_file.name : "-empty-"; let type = uploaded_file.type; + this.remaining++; + let prom = fetch(DocServer.prepend(RouteStore.upload), { method: 'POST', body: formData }).then(async (res: Response) => { (await res.json()).map(action((file: any) => { let path = DocServer.prepend(file); - console.log(path); let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName }); - docPromise.then(doc => doc && docs.push(doc)); + docPromise.then(doc => { + doc && docs.push(doc) && runInAction(() => this.remaining--); + }); })); }); promises.push(prom); @@ -77,11 +88,14 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> await Promise.all(promises); + docs.forEach(doc => this.entries.forEach(entry => doc[entry.key] = entry.value)); + let doc = this.props.Document; let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") }; let parent = this.props.ContainingCollectionView; if (parent) { let importContainer = Docs.StackingDocument(docs, options); + importContainer.singleColumn = false; Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer); this.props.removeDocument && this.props.removeDocument(doc); } @@ -103,9 +117,29 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> this.top = bounds.height / 2 - offset; } + @action + addMetadataEntry = () => { + this.metadata_guids.push(Utils.GenerateGuid()); + } + + @action + remove = (entry: KeyValue) => { + let index = this.entries.indexOf(entry); + let key = entry.key; + this.entries.splice(index, 1); + this.metadata_guids.splice(this.metadata_guids.indexOf(key), 1); + } + render() { let dimensions = 50; - let keyValueStyle = { paddingLeft: 5, width: "50%" }; + let guids = this.metadata_guids.map(el => el); + let isEditing = this.editingMetadata; + let remaining = this.remaining; + let quota = this.quota; + let percent = `${100 - (remaining / quota * 100)}`; + let uploadBegun = this.uploadBegun; + percent = percent.split(".")[0]; + percent = percent.startsWith("100") ? "99" : percent; return ( <Measure offset onResize={this.preserveCentering}> {({ measureRef }) => @@ -113,14 +147,20 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> <input id={"selector"} ref={this.selector} - name={"selector"} onChange={this.handleSelection} type="file" style={{ position: "absolute", display: "none" }} /> - <label htmlFor={"selector"}> + <label + htmlFor={"selector"} + style={{ + opacity: isEditing ? 0 : 1, + pointerEvents: isEditing ? "none" : "all", + transition: "0.4s ease opacity" + }} + > <div style={{ width: dimensions, height: dimensions, @@ -132,46 +172,86 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps> }} /> <div style={{ position: "absolute", - left: this.left + 12.5, - top: this.top + 11 + left: this.left + 12.6, + top: this.top + 11, + opacity: uploadBegun ? 0 : 1, + transition: "0.4s opacity ease" }}> <FontAwesomeIcon icon={faArrowUp} color="#FFFFFF" size={"2x"} /> </div> </label> - <div style={{ - position: "absolute", - top: 5, - right: 5, - borderRadius: "50%", - width: 25, - height: 25, - background: "black" - }} /> - <div style={{ - position: "absolute", - right: 9.5, - top: 11 - }}> - <FontAwesomeIcon icon={faTag} color="#FFFFFF" size={"1x"} /> - </div> - <div style={{ display: "flex", flexDirection: "row", borderBottom: "1px solid black", paddingBottom: 5 }} > - <div className={"key_container"} style={keyValueStyle}> - <EditableView - contents={this.key} - SetValue={this.updateKey} - GetValue={() => this.key} - oneLine={true} - /> - </div> - <div className={"value_container"} style={keyValueStyle}> - <EditableView - contents={this.value} - SetValue={this.updateValue} - GetValue={() => this.value} - oneLine={true} + <div + style={{ + transition: "0.4s opacity ease", + opacity: uploadBegun ? 1 : 0, + pointerEvents: "none", + position: "absolute", + left: 10, + top: this.top + 12.3, + fontSize: 18, + color: "white", + marginLeft: this.left - 1.6 + }}>{percent}%</div> + <div + style={{ + position: "absolute", + top: 10, + right: 10, + borderRadius: "50%", + width: 25, + height: 25, + background: "black" + }} + onClick={action(() => this.editingMetadata = !this.editingMetadata)} + /> + <FontAwesomeIcon + style={{ + pointerEvents: "none", + position: "absolute", + right: isEditing ? 16.3 : 14.5, + top: isEditing ? 15.4 : 16 + }} + icon={isEditing ? faArrowUp : faTag} + 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 + }} + onClick={this.addMetadataEntry} + > + <FontAwesomeIcon + style={{ + pointerEvents: "none", + marginLeft: 6.4, + marginTop: 5.2 + }} + icon={faPlus} + size={"1x"} /> </div> + <p style={{ paddingLeft: 10, paddingTop: 8, paddingBottom: 7 }} >Add metadata to your import...</p> + <hr style={{ margin: "6px 10px 12px 10px" }} /> + {guids.map(guid => <KeyValue remove={this.remove} key={guid} ref={(el) => { if (el) this.entries.push(el); }} />)} </div> + {/* <img style={{ width: 30, height: 30 }} src={"./loading.gif"}></img> */} </div> } </Measure> diff --git a/src/client/util/Import & Export/KeyValue.tsx b/src/client/util/Import & Export/KeyValue.tsx new file mode 100644 index 000000000..15ef25f89 --- /dev/null +++ b/src/client/util/Import & Export/KeyValue.tsx @@ -0,0 +1,79 @@ +import React = require("react"); +import { observer } from "mobx-react"; +import { EditableView } from "../../views/EditableView"; +import { observable, action } from "mobx"; + +interface KeyValueProps { + remove: (self: KeyValue) => void; +} + +@observer +export default class KeyValue extends React.Component<KeyValueProps> { + @observable public key = "Key"; + @observable public value = "Value"; + + @action + updateKey = (newKey: string) => { + this.key = newKey; + return true; + } + + @action + updateValue = (newValue: string) => { + this.value = newValue; + return true; + } + + render() { + let keyValueStyle = { paddingLeft: 10, width: "50%" }; + let keySpecified = (this.key.length > 0 && this.key !== "Key"); + return ( + <div + style={{ + display: "flex", + flexDirection: "row", + paddingBottom: 5, + paddingRight: 5, + justifyContent: "center", + alignItems: "center", + alignContent: "center" + }} + onClick={() => this.props.remove(this)} + > + <input type="checkbox" /> + <div className={"key_container"} style={keyValueStyle}> + <EditableView + contents={this.key} + SetValue={this.updateKey} + GetValue={() => this.key} + oneLine={true} + /> + </div> + <div + className={"value_container"} + style={{ + opacity: keySpecified ? 1 : 0.5, + pointerEvents: keySpecified ? "all" : "none", + ...keyValueStyle + }}> + <EditableView + contents={this.value} + SetValue={this.updateValue} + GetValue={() => this.value} + oneLine={true} + /> + </div> + <div style={{ + borderRadius: "50%", + width: 10, + height: 10, + background: "red", + marginLeft: 15, + marginRight: 15 + }} /> + + </div> + ); + } + +}
\ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 3d059b2f3..60decff25 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -351,7 +351,7 @@ export class MainView extends React.Component { let addColNode = action(() => Docs.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); let addTreeNode = action(() => CurrentUserUtils.UserDocument); let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); - let addImportCollectionNode = action(() => Docs.DirectoryImportDocument({ title: "Directory Import", width: 150, height: 150 })); + let addImportCollectionNode = action(() => Docs.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })); let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [ [React.createRef<HTMLDivElement>(), "image", "Add Image", addImageNode], |